import type { ComputerMode } from "@rakazo/contracts"; import type { Prisma, PrismaClient } from "./client.js"; export type { ComputerMode } from "@rakazo/contracts"; export function parseComputerMode(scope: string): ComputerMode { if (scope === "team" || scope === "dedicated") return scope; throw new Error(`Unknown computer scope: ${scope}`); } export function computerScopeKey(mode: ComputerMode, spaceId: string, botId?: string) { if (mode === "team") return `team:${spaceId}`; if (!botId) throw new Error("Dedicated computers require a bot id"); return `bot:${botId}`; } export function computerHomeKey(mode: ComputerMode, spaceId: string, botId?: string) { if (mode === "team") return `team-${spaceId}`; if (!botId) throw new Error("Dedicated computers require a bot id"); return botId; } type ComputerDb = Pick; type BotDb = Pick; type ExecutionLeaseDb = Pick; export async function listSpaceUserBotIds( prisma: BotDb, spaceId: string, userId: string, ): Promise> { const rows = await prisma.bot.findMany({ where: { spaceId, userId, archivedAt: null }, select: { id: true }, }); return new Set(rows.map((row) => row.id)); } /** Expire leases as fencing tombstones so the next acquire increments fence. */ export async function expireComputerExecutionLeases( prisma: ExecutionLeaseDb, where: Prisma.ComputerExecutionLeaseWhereInput, ): Promise { await prisma.computerExecutionLease.updateMany({ where, data: { expiresAt: new Date(0) }, }); } export async function ensureComputerRecord( prisma: ComputerDb, input: { mode: ComputerMode; spaceId: string; userId: string; botId?: string; kind: string; }, ) { const scopeKey = computerScopeKey(input.mode, input.spaceId, input.botId); return prisma.computer.upsert({ where: { scopeKey }, create: { spaceId: input.spaceId, userId: input.userId, scope: input.mode, scopeKey, homeKey: computerHomeKey(input.mode, input.spaceId, input.botId), kind: input.kind, }, update: {}, }); }