@opengeni/api-router 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +1 -1
- package/dist/{chunk-XSYUDIX3.js → chunk-I2EDWHVF.js} +82 -26
- package/dist/chunk-I2EDWHVF.js.map +1 -0
- package/dist/index.js +1 -1
- package/package.json +10 -10
- package/src/mcp/server.ts +14 -1
- package/src/routes/sessions.ts +83 -31
- package/src/sandbox/viewer.ts +23 -0
- package/dist/chunk-XSYUDIX3.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/app.ts","../src/auth/managed-auth.ts","../src/sandbox/access.ts","../src/mcp/server.ts","../src/mcp/session-view.ts","../src/routes/install.ts","../src/http/auth.ts","../src/routes/capabilities.ts","../src/http/common.ts","../src/routes/codex.ts","../src/routes/documents.ts","../src/mcp/documents.ts","../src/routes/enrollments.ts","../src/sandbox/enrollment.ts","../src/routes/machines.ts","../src/sandbox/machines.ts","../src/routes/environments.ts","../src/routes/files.ts","../src/routes/api-keys.ts","../src/routes/billing.ts","../src/routes/github.ts","../src/routes/packs.ts","../src/routes/scheduled-tasks.ts","../src/routes/sessions.ts","../src/sandbox/channel-a.ts","../src/sandbox/viewer.ts","../src/http/sse.ts","../src/routes/social.ts","../src/routes/workspaces.ts"],"sourcesContent":["import {\n configuredAllowedModels,\n configuredAllowedReasoningEfforts,\n configuredModels,\n} from \"@opengeni/config\";\nimport { ClientConfig } from \"@opengeni/contracts\";\nimport { createDocumentServices, indexDocumentNow, type DocumentServices } from \"@opengeni/documents\";\nimport { createObservability } from \"@opengeni/observability\";\nimport { createObjectStorage } from \"@opengeni/storage\";\nimport { WebStandardStreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\nimport { Hono } from \"hono\";\nimport { cors } from \"hono/cors\";\nimport { HTTPException } from \"hono/http-exception\";\nimport type { ApiRouteDeps, AppDependencies, ObjectStorageDependency, SessionWorkflowClient } from \"@opengeni/core\";\nimport { requireAccessGrant } from \"@opengeni/core\";\nimport { createManagedAuth } from \"./auth/managed-auth\";\nimport { createApiSandboxClient, makeResumeBoxById } from \"./sandbox/access\";\nimport { requireLimit } from \"@opengeni/core\";\nimport { buildOpenGeniMcpServer } from \"./mcp/server\";\nimport { requireAccessKey } from \"./http/auth\";\nimport { registerCapabilityRoutes } from \"./routes/capabilities\";\nimport { registerCodexRoutes } from \"./routes/codex\";\nimport { registerDocumentRoutes } from \"./routes/documents\";\nimport { registerEnrollmentRoutes } from \"./routes/enrollments\";\nimport { registerMachineRoutes } from \"./routes/machines\";\nimport { registerEnvironmentRoutes } from \"./routes/environments\";\nimport { registerFileRoutes } from \"./routes/files\";\nimport { registerApiKeyRoutes } from \"./routes/api-keys\";\nimport { registerBillingRoutes } from \"./routes/billing\";\nimport { registerGitHubRoutes } from \"./routes/github\";\nimport { registerInstallRoutes } from \"./routes/install\";\nimport { registerPackRoutes } from \"./routes/packs\";\nimport { registerScheduledTaskRoutes } from \"./routes/scheduled-tasks\";\nimport { registerSessionRoutes } from \"./routes/sessions\";\nimport { registerSocialRoutes } from \"./routes/social\";\nimport { registerWorkspaceRoutes } from \"./routes/workspaces\";\n\nexport type {\n ApiRouteDeps,\n AppDependencies,\n DocumentIndexClient,\n ObjectStorageDependency,\n SessionWorkflowClient,\n} from \"@opengeni/core\";\nexport {\n mergeResourceRefs,\n mergeToolRefs,\n normalizeResources,\n validateFileResources,\n validateGitHubRepositorySelection,\n validateGitHubRepositorySelectionShape,\n validateToolRefs,\n withDefaultEnabledCapabilityMcpTools,\n} from \"@opengeni/core\";\nexport { workflowIdForSession } from \"@opengeni/core\";\nexport { replaySessionEvents, sseSessionStream } from \"./http/sse\";\n\nexport function createApp(deps: AppDependencies): Hono {\n const managedAuth = deps.managedAuth ?? createManagedAuth(deps.settings, deps.db);\n const objectStorage = createObjectStorage(deps.settings);\n let documentServices: DocumentServices | null = deps.documentServices ?? null;\n const getDocumentServices = () => {\n documentServices ??= createDocumentServices(deps.settings);\n return documentServices;\n };\n const documentIndexer = deps.documentIndexer ?? {\n indexDocument: async ({ accountId, workspaceId, documentId }: { accountId: string; workspaceId: string; documentId: string }) => {\n if (!objectStorage) {\n throw new HTTPException(503, { message: \"object storage is not configured\" });\n }\n return await indexDocumentNow(deps.db, objectStorage, workspaceId, documentId, getDocumentServices(), {\n beforeEmbed: async ({ chunkCount }) => {\n await requireLimit(routeDeps, { accountId, workspaceId, action: \"document:index\", quantity: chunkCount });\n },\n });\n },\n };\n // The API process's own agent-loop-free sandbox client — the API-direct\n // control-plane seam. Constructed from settings (resumes boxes by id\n // in-process) unless a client was injected (tests). resumeBoxById is always\n // concrete for routes; it throws SandboxResumeError when sandboxBackend=none.\n const sandboxClient = deps.sandboxClient ?? createApiSandboxClient(deps.settings);\n const resumeBoxById = deps.resumeBoxById ?? makeResumeBoxById(sandboxClient);\n const routeDeps: ApiRouteDeps = {\n ...deps,\n githubStateSecret: deps.githubStateSecret ?? deps.settings.githubAppManifestStateSecret ?? crypto.randomUUID(),\n managedAuth,\n objectStorage,\n documentIndexer,\n getDocumentServices,\n ...(sandboxClient ? { sandboxClient } : {}),\n resumeBoxById,\n };\n const app = new Hono();\n const observability = deps.observability ?? createObservability(deps.settings, { component: \"api\" });\n\n app.use(\"*\", cors({\n credentials: true,\n origin: (origin) => {\n if (!origin) {\n return null;\n }\n return allowedCorsOrigin(deps.settings.corsAllowOriginRegex, origin) ? origin : null;\n },\n }));\n\n app.use(\"*\", async (c, next) => {\n const url = new URL(c.req.url);\n const route = routeLabel(url.pathname);\n const start = performance.now();\n const span = observability.startSpan(`HTTP ${c.req.method} ${route}`, {\n \"http.request.method\": c.req.method,\n \"url.path\": url.pathname,\n \"opengeni.route\": route,\n });\n try {\n await next();\n const status = c.res.status || 200;\n const durationSeconds = (performance.now() - start) / 1000;\n observability.recordHttpRequest({ method: c.req.method, route, status, durationSeconds });\n span.end({\n attributes: {\n \"http.response.status_code\": status,\n \"opengeni.duration_ms\": Math.round(durationSeconds * 1000),\n },\n });\n observability.info(\"HTTP request completed\", {\n method: c.req.method,\n route,\n status,\n durationMs: Math.round(durationSeconds * 1000),\n traceId: span.traceId,\n spanId: span.spanId,\n });\n } catch (error) {\n const status = httpStatusForError(error);\n const durationSeconds = (performance.now() - start) / 1000;\n observability.recordHttpRequest({ method: c.req.method, route, status, durationSeconds });\n span.end({\n attributes: {\n \"http.response.status_code\": status,\n \"opengeni.duration_ms\": Math.round(durationSeconds * 1000),\n },\n error,\n });\n observability.error(\"HTTP request failed\", {\n method: c.req.method,\n route,\n status,\n durationMs: Math.round(durationSeconds * 1000),\n traceId: span.traceId,\n spanId: span.spanId,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n });\n\n app.use(\"*\", requireAccessKey(deps.settings));\n\n if (managedAuth) {\n app.on([\"GET\", \"POST\"], \"/v1/auth/*\", (c) => managedAuth.handler(c.req.raw));\n }\n\n app.get(\"/healthz\", (c) => c.json({\n service: deps.settings.serviceName,\n environment: deps.settings.environment,\n deploymentRevision: deps.settings.deploymentRevision,\n ok: true,\n }));\n\n app.get(\"/metrics\", (c) => c.text(observability.prometheusMetrics(), 200, {\n \"content-type\": \"text/plain; version=0.0.4; charset=utf-8\",\n }));\n\n app.get(\"/v1/config/client\", (c) => c.json(ClientConfig.parse({\n deploymentRevision: deps.settings.deploymentRevision,\n defaultModel: deps.settings.openaiModel,\n allowedModels: configuredAllowedModels(deps.settings),\n // Provider-grouped model list for the picker. configuredModels() carries the\n // union of the built-in allow-list and every registry provider's models, in\n // selection order (default model first); project each to the client-safe\n // ClientModel shape (ConfiguredModel.providerId → ClientModel.provider).\n models: configuredModels(deps.settings).map((model) => ({\n id: model.id,\n label: model.label,\n provider: model.providerId,\n providerLabel: model.providerLabel,\n api: model.api,\n ...(model.contextWindowTokens === undefined ? {} : { contextWindowTokens: model.contextWindowTokens }),\n })),\n defaultReasoningEffort: deps.settings.openaiReasoningEffort,\n allowedReasoningEfforts: configuredAllowedReasoningEfforts(deps.settings),\n mcpServers: deps.settings.mcpServers.map((server) => ({\n id: server.id,\n name: server.name ?? server.id,\n })),\n fileUploads: {\n enabled: objectStorage !== null,\n maxSizeBytes: objectStorage?.maxSinglePutSizeBytes ?? 5_000_000_000,\n },\n productAccessMode: deps.settings.productAccessMode,\n auth: clientAuthConfig(deps.settings),\n // Channel-A structured services (P4.4) ride exec/readFile/createEditor,\n // available on every real backend; `none` has no box so they are all off.\n // Per-session availability is still negotiated on /stream-capabilities.\n structuredServices: structuredServicesHint(deps.settings.sandboxBackend),\n })));\n\n app.all(\"/v1/workspaces/:workspaceId/mcp\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, routeDeps, workspaceId, \"workspace:read\");\n const transport = new WebStandardStreamableHTTPServerTransport({ enableJsonResponse: true });\n const mcp = buildOpenGeniMcpServer(routeDeps, grant, { requestOrigin: new URL(c.req.url).origin });\n await mcp.connect(transport);\n return await transport.handleRequest(c.req.raw);\n });\n\n registerFileRoutes(app, routeDeps);\n registerApiKeyRoutes(app, routeDeps);\n registerBillingRoutes(app, routeDeps);\n registerDocumentRoutes(app, routeDeps);\n registerGitHubRoutes(app, routeDeps);\n registerInstallRoutes(app, routeDeps);\n registerWorkspaceRoutes(app, routeDeps);\n registerSocialRoutes(app, routeDeps);\n registerCapabilityRoutes(app, routeDeps);\n registerEnrollmentRoutes(app, routeDeps);\n registerMachineRoutes(app, routeDeps);\n registerEnvironmentRoutes(app, routeDeps);\n registerPackRoutes(app, routeDeps);\n registerSessionRoutes(app, routeDeps);\n registerScheduledTaskRoutes(app, routeDeps);\n registerCodexRoutes(app, routeDeps);\n\n return app;\n}\n\nfunction clientAuthConfig(settings: AppDependencies[\"settings\"]) {\n if (settings.productAccessMode === \"managed\") {\n return { mode: \"managedSession\" as const, session: \"cookie\" as const };\n }\n if (settings.productAccessMode === \"configured\") {\n return { mode: \"configuredToken\" as const, headerName: \"authorization\" as const, scheme: \"bearer\" as const };\n }\n if (settings.authRequired) {\n return { mode: \"deploymentKey\" as const, headerName: \"x-opengeni-access-key\" as const };\n }\n return { mode: \"none\" as const };\n}\n\nfunction structuredServicesHint(backend: string): { fileSystem: boolean; git: boolean; terminalEvents: boolean } {\n const hasBox = backend !== \"none\";\n return { fileSystem: hasBox, git: hasBox, terminalEvents: hasBox };\n}\n\nexport function allowedCorsOrigin(pattern: string, origin: string): boolean {\n return new RegExp(`^(?:${pattern})$`).test(origin);\n}\n\nexport function httpStatusForError(error: unknown): number {\n if (error instanceof HTTPException) {\n return error.status;\n }\n return 500;\n}\n\nconst routeLabelPatterns: Array<{ pattern: RegExp; label: string }> = [\n { pattern: /^\\/healthz$/, label: \"/healthz\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/codex\\/connect\\/start$/, label: \"/v1/workspaces/:workspaceId/codex/connect/start\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/codex\\/connect\\/poll$/, label: \"/v1/workspaces/:workspaceId/codex/connect/poll\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/codex\\/status$/, label: \"/v1/workspaces/:workspaceId/codex/status\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/codex\\/usage$/, label: \"/v1/workspaces/:workspaceId/codex/usage\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/codex$/, label: \"/v1/workspaces/:workspaceId/codex\" },\n { pattern: /^\\/metrics$/, label: \"/metrics\" },\n { pattern: /^\\/v1\\/config\\/client$/, label: \"/v1/config/client\" },\n { pattern: /^\\/v1\\/billing$/, label: \"/v1/billing\" },\n { pattern: /^\\/v1\\/billing\\/checkout$/, label: \"/v1/billing/checkout\" },\n { pattern: /^\\/v1\\/billing\\/usage$/, label: \"/v1/billing/usage\" },\n { pattern: /^\\/v1\\/billing\\/entitlements$/, label: \"/v1/billing/entitlements\" },\n { pattern: /^\\/v1\\/webhooks\\/stripe$/, label: \"/v1/webhooks/stripe\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/mcp$/, label: \"/v1/workspaces/:workspaceId/mcp\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/mcp\\/docs$/, label: \"/v1/workspaces/:workspaceId/mcp/docs\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/sessions$/, label: \"/v1/workspaces/:workspaceId/sessions\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/events\\/stream$/, label: \"/v1/workspaces/:workspaceId/sessions/:id/events/stream\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/events$/, label: \"/v1/workspaces/:workspaceId/sessions/:id/events\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/turns\\/reorder$/, label: \"/v1/workspaces/:workspaceId/sessions/:id/turns/reorder\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/turns\\/[^/]+$/, label: \"/v1/workspaces/:workspaceId/sessions/:id/turns/:turnId\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/turns$/, label: \"/v1/workspaces/:workspaceId/sessions/:id/turns\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/stream-capabilities$/, label: \"/v1/workspaces/:workspaceId/sessions/:id/stream-capabilities\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/viewers\\/[^/]+\\/heartbeat$/, label: \"/v1/workspaces/:workspaceId/sessions/:id/viewers/:viewerId/heartbeat\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/viewers\\/[^/]+$/, label: \"/v1/workspaces/:workspaceId/sessions/:id/viewers/:viewerId\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/viewers$/, label: \"/v1/workspaces/:workspaceId/sessions/:id/viewers\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/goal$/, label: \"/v1/workspaces/:workspaceId/sessions/:id/goal\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/sessions\\/[^/]+$/, label: \"/v1/workspaces/:workspaceId/sessions/:id\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/files\\/uploads$/, label: \"/v1/workspaces/:workspaceId/files/uploads\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/files\\/uploads\\/[^/]+\\/complete$/, label: \"/v1/workspaces/:workspaceId/files/uploads/:id/complete\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/files\\/[^/]+\\/download-url$/, label: \"/v1/workspaces/:workspaceId/files/:id/download-url\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/files\\/[^/]+$/, label: \"/v1/workspaces/:workspaceId/files/:id\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/api-keys$/, label: \"/v1/workspaces/:workspaceId/api-keys\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/api-keys\\/[^/]+$/, label: \"/v1/workspaces/:workspaceId/api-keys/:id\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/scheduled-tasks$/, label: \"/v1/workspaces/:workspaceId/scheduled-tasks\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/scheduled-tasks\\/[^/]+\\/pause$/, label: \"/v1/workspaces/:workspaceId/scheduled-tasks/:id/pause\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/scheduled-tasks\\/[^/]+\\/resume$/, label: \"/v1/workspaces/:workspaceId/scheduled-tasks/:id/resume\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/scheduled-tasks\\/[^/]+\\/trigger$/, label: \"/v1/workspaces/:workspaceId/scheduled-tasks/:id/trigger\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/scheduled-tasks\\/[^/]+\\/runs$/, label: \"/v1/workspaces/:workspaceId/scheduled-tasks/:id/runs\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/scheduled-tasks\\/[^/]+$/, label: \"/v1/workspaces/:workspaceId/scheduled-tasks/:id\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/document-bases$/, label: \"/v1/workspaces/:workspaceId/document-bases\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/document-bases\\/[^/]+\\/documents\\/[^/]+\\/reindex$/, label: \"/v1/workspaces/:workspaceId/document-bases/:id/documents/:documentId/reindex\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/document-bases\\/[^/]+\\/documents\\/[^/]+$/, label: \"/v1/workspaces/:workspaceId/document-bases/:id/documents/:documentId\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/document-bases\\/[^/]+\\/documents$/, label: \"/v1/workspaces/:workspaceId/document-bases/:id/documents\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/document-bases\\/[^/]+\\/search$/, label: \"/v1/workspaces/:workspaceId/document-bases/:id/search\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/document-bases\\/[^/]+$/, label: \"/v1/workspaces/:workspaceId/document-bases/:id\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/github\\/app$/, label: \"/v1/workspaces/:workspaceId/github/app\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/github\\/repositories$/, label: \"/v1/workspaces/:workspaceId/github/repositories\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/github\\/repositories\\/sync$/, label: \"/v1/workspaces/:workspaceId/github/repositories/sync\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/github\\/app-manifest$/, label: \"/v1/workspaces/:workspaceId/github/app-manifest\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/capabilities$/, label: \"/v1/workspaces/:workspaceId/capabilities\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/capabilities\\/discovery\\/mcp-registry$/, label: \"/v1/workspaces/:workspaceId/capabilities/discovery/mcp-registry\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/capabilities\\/[^/]+\\/enable$/, label: \"/v1/workspaces/:workspaceId/capabilities/:id/enable\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/capabilities\\/[^/]+\\/disable$/, label: \"/v1/workspaces/:workspaceId/capabilities/:id/disable\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/environments$/, label: \"/v1/workspaces/:workspaceId/environments\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/environments\\/[^/]+\\/variables\\/[^/]+$/, label: \"/v1/workspaces/:workspaceId/environments/:id/variables/:name\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/environments\\/[^/]+$/, label: \"/v1/workspaces/:workspaceId/environments/:id\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/packs$/, label: \"/v1/workspaces/:workspaceId/packs\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/packs\\/installations$/, label: \"/v1/workspaces/:workspaceId/packs/installations\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/packs\\/marketing-social-daily-analysis\\/scheduled-tasks$/, label: \"/v1/workspaces/:workspaceId/packs/marketing-social-daily-analysis/scheduled-tasks\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/packs\\/[^/]+\\/enable$/, label: \"/v1/workspaces/:workspaceId/packs/:id/enable\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/packs\\/[^/]+$/, label: \"/v1/workspaces/:workspaceId/packs/:id\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/social\\/connections$/, label: \"/v1/workspaces/:workspaceId/social/connections\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/social\\/posts$/, label: \"/v1/workspaces/:workspaceId/social/posts\" },\n { pattern: /^\\/v1\\/enrollments\\/device\\/start$/, label: \"/v1/enrollments/device/start\" },\n { pattern: /^\\/v1\\/enrollments\\/device\\/poll$/, label: \"/v1/enrollments/device/poll\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/enrollments\\/device\\/approve$/, label: \"/v1/workspaces/:workspaceId/enrollments/device/approve\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/enrollments\\/[^/]+\\/revoke$/, label: \"/v1/workspaces/:workspaceId/enrollments/:id/revoke\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/enrollments$/, label: \"/v1/workspaces/:workspaceId/enrollments\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/machines\\/[^/]+\\/metrics\\/series$/, label: \"/v1/workspaces/:workspaceId/machines/:enrollmentId/metrics/series\" },\n { pattern: /^\\/v1\\/workspaces\\/[^/]+\\/machines$/, label: \"/v1/workspaces/:workspaceId/machines\" },\n { pattern: /^\\/v1\\/github\\/app-manifest\\/callback$/, label: \"/v1/github/app-manifest/callback\" },\n { pattern: /^\\/v1\\/github\\/setup$/, label: \"/v1/github/setup\" },\n { pattern: /^\\/v1\\/github\\/install\\/callback$/, label: \"/v1/github/install/callback\" },\n { pattern: /^\\/v1\\/github\\/oauth\\/callback$/, label: \"/v1/github/oauth/callback\" },\n];\n\nexport function routeLabel(pathname: string): string {\n const match = routeLabelPatterns.find(({ pattern }) => pattern.test(pathname));\n if (match) {\n return match.label;\n }\n return pathname.startsWith(\"/v1/\") ? \"/v1/unknown\" : \"/unknown\";\n}\n","import type { Settings } from \"@opengeni/config\";\nimport { type ManagedAuth } from \"@opengeni/core\";\nimport type { Database } from \"@opengeni/db\";\nimport { ensureManagedAccessForUser } from \"@opengeni/db\";\nimport { betterAuth } from \"better-auth\";\nimport { createEmailVerificationToken } from \"better-auth/api\";\nimport { Pool } from \"pg\";\nimport { Resend } from \"resend\";\n\n// `ManagedAuth` (the Better Auth `Auth<any>` alias) is owned by @opengeni/core\n// (`managed-auth-type.ts`) — `dependencies.ts`/`access` reference it as a\n// type-only slot. We re-export it from this construction site so existing\n// importers (`app.ts`) keep the same import path.\nexport type { ManagedAuth };\n\nexport function createManagedAuth(settings: Settings, db: Database): ManagedAuth | null {\n if (settings.productAccessMode !== \"managed\") {\n return null;\n }\n const pool = new Pool({ connectionString: settings.databaseUrl });\n return betterAuth({\n appName: \"OpenGeni\",\n baseURL: betterAuthBaseUrl(settings),\n basePath: \"/v1/auth\",\n secret: settings.betterAuthSecret,\n database: pool,\n trustedOrigins: betterAuthTrustedOrigins(settings),\n advanced: {\n useSecureCookies: settings.publicBaseUrl?.startsWith(\"https://\") ?? false,\n ...(settings.betterAuthCookieDomain ? {\n crossSubDomainCookies: {\n enabled: true,\n domain: settings.betterAuthCookieDomain,\n },\n } : {}),\n database: {\n generateId: () => crypto.randomUUID(),\n },\n },\n rateLimit: {\n enabled: true,\n storage: \"database\",\n modelName: \"auth_rate_limits\",\n fields: {\n lastRequest: \"last_request\",\n },\n },\n user: {\n modelName: \"auth_users\",\n fields: {\n emailVerified: \"email_verified\",\n createdAt: \"created_at\",\n updatedAt: \"updated_at\",\n },\n },\n session: {\n modelName: \"auth_sessions\",\n fields: {\n userId: \"user_id\",\n expiresAt: \"expires_at\",\n ipAddress: \"ip_address\",\n userAgent: \"user_agent\",\n createdAt: \"created_at\",\n updatedAt: \"updated_at\",\n },\n },\n account: {\n modelName: \"auth_identities\",\n fields: {\n userId: \"user_id\",\n accountId: \"account_id\",\n providerId: \"provider_id\",\n accessToken: \"access_token\",\n refreshToken: \"refresh_token\",\n idToken: \"id_token\",\n accessTokenExpiresAt: \"access_token_expires_at\",\n refreshTokenExpiresAt: \"refresh_token_expires_at\",\n createdAt: \"created_at\",\n updatedAt: \"updated_at\",\n },\n accountLinking: {\n enabled: false,\n },\n },\n verification: {\n modelName: \"auth_verifications\",\n fields: {\n expiresAt: \"expires_at\",\n createdAt: \"created_at\",\n updatedAt: \"updated_at\",\n },\n storeIdentifier: \"hashed\",\n },\n emailAndPassword: {\n enabled: true,\n requireEmailVerification: true,\n revokeSessionsOnPasswordReset: true,\n onExistingUserSignUp: async ({ user }) => {\n if (!user.emailVerified) {\n const url = await verificationUrl(settings, user.email);\n await sendEmail(settings, {\n to: user.email,\n subject: \"Verify your OpenGeni email\",\n text: `Verify your OpenGeni email: ${url}`,\n html: `<p>Verify your OpenGeni email:</p><p><a href=\"${escapeHtml(url)}\">Verify email</a></p>`,\n });\n }\n },\n sendResetPassword: async ({ user, url }) => {\n await sendEmail(settings, {\n to: user.email,\n subject: \"Reset your OpenGeni password\",\n text: `Reset your OpenGeni password: ${url}`,\n html: `<p>Reset your OpenGeni password:</p><p><a href=\"${escapeHtml(url)}\">Reset password</a></p>`,\n });\n },\n },\n emailVerification: {\n sendOnSignUp: true,\n sendVerificationEmail: async ({ user, url }) => {\n await sendEmail(settings, {\n to: user.email,\n subject: \"Verify your OpenGeni email\",\n text: `Verify your OpenGeni email: ${url}`,\n html: `<p>Verify your OpenGeni email:</p><p><a href=\"${escapeHtml(url)}\">Verify email</a></p>`,\n });\n },\n afterEmailVerification: async (user) => {\n await ensureManagedAccessForUser(db, {\n userId: user.id,\n email: user.email,\n name: user.name,\n });\n },\n },\n databaseHooks: {\n user: {\n create: {\n after: async (user) => {\n await ensureManagedAccessForUser(db, {\n userId: user.id,\n email: user.email,\n name: user.name,\n });\n },\n },\n },\n },\n }) as ManagedAuth;\n}\n\nexport async function managedSessionAccessContext(auth: ManagedAuth, db: Database, headers: Headers) {\n const session = await auth.api.getSession({ headers });\n if (!session?.user) {\n return null;\n }\n return await ensureManagedAccessForUser(db, {\n userId: session.user.id,\n email: session.user.email,\n name: session.user.name,\n });\n}\n\nfunction betterAuthBaseUrl(settings: Settings) {\n const allowedHosts = splitCsv(settings.betterAuthAllowedHosts);\n if (allowedHosts.length === 0) {\n return settings.publicBaseUrl;\n }\n return {\n allowedHosts,\n fallback: settings.publicBaseUrl,\n protocol: \"auto\" as const,\n };\n}\n\nfunction betterAuthTrustedOrigins(settings: Settings): string[] {\n const origins = new Set<string>();\n if (settings.publicBaseUrl) {\n origins.add(new URL(settings.publicBaseUrl).origin);\n }\n for (const origin of splitCsv(settings.betterAuthTrustedOrigins)) {\n origins.add(origin);\n }\n return [...origins];\n}\n\nasync function sendEmail(settings: Settings, input: {\n to: string;\n subject: string;\n text: string;\n html: string;\n}): Promise<void> {\n if (!settings.resendApiKey) {\n if (settings.environment === \"local\" || settings.environment === \"test\") {\n console.warn(`[opengeni] Skipping email to ${input.to}: OPENGENI_RESEND_API_KEY is not configured`);\n return;\n }\n throw new Error(\"OPENGENI_RESEND_API_KEY is required to send managed auth email\");\n }\n const resend = new Resend(settings.resendApiKey);\n const result = await resend.emails.send({\n from: settings.emailFrom,\n to: input.to,\n subject: input.subject,\n text: input.text,\n html: input.html,\n });\n if (result.error) {\n throw new Error(result.error.message);\n }\n}\n\nasync function verificationUrl(settings: Settings, email: string): Promise<string> {\n if (!settings.betterAuthSecret) {\n throw new Error(\"OPENGENI_BETTER_AUTH_SECRET is required to send managed auth verification email\");\n }\n if (!settings.publicBaseUrl) {\n throw new Error(\"OPENGENI_PUBLIC_BASE_URL is required to send managed auth verification email\");\n }\n const token = await createEmailVerificationToken(settings.betterAuthSecret, email);\n const url = new URL(\"/v1/auth/verify-email\", settings.publicBaseUrl);\n url.searchParams.set(\"token\", token);\n url.searchParams.set(\"callbackURL\", \"/\");\n return url.toString();\n}\n\nfunction splitCsv(raw: string): string[] {\n return raw.split(\",\").map((value) => value.trim()).filter(Boolean);\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\");\n}\n","// apps/api/src/sandbox/access.ts — the API-tier sandbox access seam.\n//\n// This is the foundation of the API-DIRECT control plane\n// (docs/design/sandbox-surfacing): the apps/api process constructs its OWN\n// sandbox client and resumes boxes by id IN-PROCESS, so non-turn ops (viewer\n// attach, FS/git reads, tunnel URL mint) never touch Temporal or a worker.\n//\n// IMPORT DISCIPLINE (enforced by apps/api/test/sandbox-access-import-guard.test.ts):\n// apps/api accesses sandbox construction/resume symbols ONLY via the\n// agent-loop-free leaf `@opengeni/runtime/sandbox` — NEVER the bare\n// `@opengeni/runtime` barrel (which pulls the @openai/agents agent loop into\n// the API process). This file is the single chokepoint for that import.\nimport { createSandboxClient } from \"@opengeni/runtime/sandbox\";\nimport type { Settings } from \"@opengeni/config\";\n\n// The structural shapes the API needs from a provider sandbox client now live in\n// @opengeni/core (`sandbox-types.ts`) — `dependencies.ts` (also in core)\n// references them as the `sandboxClient` / `resumeBoxById` provider seams, so\n// core is their single owner. We re-export them from here so existing apps/api\n// importers (and the `@opengeni/runtime/sandbox` value implementation below)\n// keep the same import site. (Mirrors @openai/agents/sandbox's SandboxClient\n// without importing the agent-loop barrel.)\nexport type {\n ApiSandboxSession,\n ApiSandboxClient,\n ResumeBoxByIdInput,\n ResumedSandboxSession,\n} from \"@opengeni/core\";\nimport type {\n ApiSandboxClient,\n ApiSandboxSession,\n ResumeBoxByIdInput,\n ResumedSandboxSession,\n} from \"@opengeni/core\";\n\nexport class SandboxResumeError extends Error {\n constructor(message: string, readonly cause?: unknown) {\n super(message);\n this.name = \"SandboxResumeError\";\n }\n}\n\n/**\n * Construct the API process's own sandbox client from settings, agent-loop-free.\n * Returns undefined when `sandboxBackend=none` (no box to touch). The Modal\n * token + app name are read from settings (already parsed by getSettings and\n * present in the API runtime env), so the client can resume Modal boxes by id.\n */\nexport function createApiSandboxClient(settings: Settings): ApiSandboxClient | undefined {\n const client = createSandboxClient(settings) as ApiSandboxClient | undefined;\n return client;\n}\n\n/**\n * Build the `resumeBoxById` helper bound to the API's sandbox client. Given a\n * backend + a serialized resume_state envelope, it resumes the box and returns\n * a live session for one in-process op. The caller drives exec/readFile and then\n * drops the handle (resume → use → drop); it does NOT own the box.\n */\nexport function makeResumeBoxById(client: ApiSandboxClient | undefined): (input: ResumeBoxByIdInput) => Promise<ResumedSandboxSession> {\n return async ({ backend, resumeState }: ResumeBoxByIdInput): Promise<ResumedSandboxSession> => {\n if (!client) {\n throw new SandboxResumeError(\n \"The API sandbox client is not configured (sandboxBackend=none); cannot resume a box by id.\",\n );\n }\n if (client.backendId !== backend) {\n throw new SandboxResumeError(\n `Resume backend \"${backend}\" does not match the API sandbox client backend \"${client.backendId}\"; a cross-backend resume_state envelope cannot be deserialized.`,\n );\n }\n if (!client.deserializeSessionState || !client.resume) {\n throw new SandboxResumeError(\n `The configured sandbox backend \"${client.backendId}\" does not support resume-by-id (no deserializeSessionState/resume).`,\n );\n }\n let session: ApiSandboxSession;\n try {\n const state = await client.deserializeSessionState(resumeState);\n session = await client.resume(state);\n } catch (error) {\n throw new SandboxResumeError(\n `Failed to resume sandbox box by id on backend \"${backend}\": ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n return session;\n };\n}\n","import {\n CreateScheduledTaskRequest,\n SessionMcpCredentialUpdateInput,\n WorkspaceEnvironmentVariableName,\n type AccessGrant,\n type GitHubRepository,\n type Permission,\n type ResourceRef,\n UpdateScheduledTaskRequest,\n} from \"@opengeni/contracts\";\nimport {\n countWorkspaceEnvironments,\n createWorkspaceEnvironment,\n deleteScheduledTask,\n encryptEnvironmentValue,\n getSession,\n getSessionGoal,\n getWorkspaceEnvironment,\n getWorkspaceEnvironmentByName,\n listGitHubInstallationIdsForWorkspace,\n listScheduledTaskRuns,\n listScheduledTasks,\n listSessionEvents,\n listSessions,\n listSocialConnections,\n listSocialPosts,\n listWorkspaceEnvironments,\n requireFile,\n requireScheduledTask,\n requireSession,\n setSessionGoalStatus,\n setWorkspaceEnvironmentVariable,\n updateScheduledTask,\n updateSessionGoal,\n upsertSessionGoal,\n} from \"@opengeni/db\";\nimport { appendAndPublishEvents } from \"@opengeni/events\";\nimport {\n createGitHubAppInstallationToken,\n createSignedState,\n GitHubAppConfigurationError,\n githubAppMissingSettings,\n listGitHubAppRepositories,\n stateMaxAgeSeconds,\n} from \"@opengeni/github\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport * as z4 from \"zod/v4\";\nimport { hasPermission } from \"@opengeni/core\";\nimport { recordWorkspaceUsage, requireLimit } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\nimport {\n assertAllowedEnvironmentVariableName,\n MAX_ENVIRONMENTS_PER_WORKSPACE,\n MAX_VARIABLES_PER_ENVIRONMENT,\n recordEnvironmentAuditEvent,\n requireEnvironmentEncryption,\n} from \"@opengeni/core\";\nimport {\n createValidatedScheduledTask,\n manualScheduledTaskTriggerUsageKey,\n manualScheduledTaskTriggerWorkflowId,\n scheduledTaskToolsProvided,\n scheduledTaskTriggerToken,\n syncCreatedScheduledTask,\n syncUpdatedScheduledTask,\n validatedScheduledTaskUpdate,\n} from \"@opengeni/core\";\nimport { acceptSessionUserMessage, createSessionForRequest, updateSessionTitle, workflowIdForSession } from \"@opengeni/core\";\nimport {\n buildFleetContextForSession,\n listFleet,\n provisionSandbox,\n runOnSandbox,\n swapActiveSandbox,\n type FleetContext,\n type FleetServices,\n type RunOnOp,\n} from \"@opengeni/core\";\nimport { capEventPage, capSessionDetail } from \"./session-view\";\n\nexport type McpServerOptions = {\n // Origin of the HTTP request that reached the MCP route; last-resort base\n // for links the server mints (github_connect_link) when neither\n // OPENGENI_PUBLIC_BASE_URL nor the manifest base URL is configured.\n requestOrigin?: string | null;\n};\n\nexport function buildOpenGeniMcpServer(deps: ApiRouteDeps, grant: AccessGrant, options: McpServerOptions = {}): McpServer {\n const server = new McpServer({\n name: \"opengeni\",\n version: \"1.0.0\",\n });\n const json = (value: unknown) => ({ content: [{ type: \"text\" as const, text: JSON.stringify(value, null, 2) }] });\n const can = (permission: Permission) => hasPermission(grant.permissions, permission);\n\n // Session-scoped tools key off the worker-asserted sessionId claim (signed\n // into the delegated token by the worker, never agent-controlled).\n const sessionId = typeof grant.metadata?.[\"sessionId\"] === \"string\" ? grant.metadata[\"sessionId\"] as string : null;\n // set_session_title names the agent's OWN session — pure session metadata,\n // not a goal operation — so it is available on every session, gated only on\n // the signed sessionId (NOT goals:manage, and NOT on a goal existing).\n if (sessionId !== null) {\n server.registerTool(\"set_session_title\", {\n description: \"Set this session's display title to a concise 3-7 word summary. Call once early to name the session; calling again replaces it unless a human has manually set the title.\",\n inputSchema: { title: z4.string().min(1).max(200) },\n }, async ({ title }) => {\n const result = await updateSessionTitle(deps, grant.workspaceId, sessionId, title, \"agent\");\n return json({ ok: true, updated: result.updated, title: result.title ?? title });\n });\n }\n // Goal tools require goals:manage (in the default first-party permission set).\n if (sessionId !== null && can(\"goals:manage\")) {\n registerGoalTools(server, deps, grant, sessionId, json);\n }\n\n // Fleet tools (M7 bring-your-own-compute): list / attach / swap / run_on /\n // provision over the session's Modal box + the workspace's enrolled machines.\n // Session-scoped like goals (they steer THIS session's active-sandbox pointer),\n // so they register only when the grant carries the worker-signed sessionId claim\n // (never agent-controlled). Gated on the selfhosted feature flag: the active\n // pointer + swap are only meaningful when bring-your-own-compute is enabled.\n if (sessionId !== null && deps.settings.sandboxSelfhostedEnabled) {\n registerFleetTools(server, deps, grant, sessionId, json);\n }\n\n // Orchestration, environment, and GitHub-connect tools are permission-gated\n // at registration: a grant without the permission does not see the tool.\n // Sandboxed workers reach this server with the first-party delegated\n // permission set (firstPartyMcpPermissions in @opengeni/runtime), which is\n // POWERFUL BY DEFAULT — it carries sessions:*, environments:*, and github:use,\n // so agents can spawn/read sessions, manage workspace environment variables,\n // and mint GitHub install links out of the box. A user DEMOTES a specific\n // session by setting a narrower session.firstPartyMcpPermissions (capped to\n // the creator's own grant); operators still cap what any session can be given.\n registerWorkspaceOrchestrationTools(server, deps, grant, can, json);\n registerEnvironmentTools(server, deps, grant, can, json);\n if (can(\"github:use\")) {\n registerGitHubConnectTool(server, deps, grant, options, json);\n // TOKEN-BROKER (B1): the agent-refreshable git token. Session-scoped (keys off the\n // worker-signed sessionId claim so it mints for THIS session's repos), gated on\n // the same github:use capability as github_connect_link.\n if (sessionId !== null) {\n registerGitHubTokenTool(server, deps, grant, sessionId, json);\n }\n }\n\n server.registerTool(\"files_get_download_url\", {\n description: \"Create a short-lived download URL for a ready file asset.\",\n inputSchema: { fileId: z4.string().uuid() },\n }, async ({ fileId }) => {\n if (!deps.objectStorage) {\n throw new Error(\"object storage is not configured\");\n }\n const file = await requireFile(deps.db, grant.workspaceId, fileId);\n if (file.status !== \"ready\") {\n throw new Error(`file is ${file.status}`);\n }\n const signed = await deps.objectStorage.createGetUrl({ key: file.objectKey });\n return json({\n file: {\n id: file.id,\n filename: file.filename,\n safeFilename: file.safeFilename,\n contentType: file.contentType,\n sizeBytes: file.sizeBytes,\n sha256: file.sha256,\n status: file.status,\n createdAt: file.createdAt,\n updatedAt: file.updatedAt,\n },\n downloadUrl: {\n url: signed.url,\n expiresAt: signed.expiresAt.toISOString(),\n },\n });\n });\n\n server.registerTool(\"github_repositories_list\", {\n description: \"List GitHub App repositories available as scheduled task repository resources. Use the returned resource object in scheduled task agentConfig.resources.\",\n inputSchema: { limit: z4.number().int().positive().optional() },\n }, async ({ limit }) => {\n try {\n const installationIds = await listGitHubInstallationIdsForWorkspace(deps.db, grant.workspaceId);\n const repositories = await listGitHubAppRepositories(deps.settings, { installationIds });\n const visible = typeof limit === \"number\" ? repositories.slice(0, limit) : repositories;\n return json({ repositories: visible.map((repository) => repositoryWithScheduledTaskResource(repository)) });\n } catch (error) {\n if (error instanceof GitHubAppConfigurationError) {\n throw new Error(`GitHub App is not configured: ${error.missing.join(\", \")}`);\n }\n throw error;\n }\n });\n\n server.registerTool(\"social_connections_list\", {\n description: \"List connected social media accounts available to social media analysis packs.\",\n inputSchema: { limit: z4.number().int().positive().optional() },\n }, async ({ limit }) => json({ connections: await listSocialConnections(deps.db, grant.workspaceId, boundedMcpLimit(limit)) }));\n\n server.registerTool(\"social_posts_recent\", {\n description: \"List recent social media posts imported or synced into OpenGeni.\",\n inputSchema: {\n connectionIds: z4.array(z4.string().uuid()).optional(),\n since: z4.string().optional(),\n windowHours: z4.number().int().positive().optional(),\n limit: z4.number().int().positive().optional(),\n },\n }, async ({ connectionIds, since, windowHours, limit }) => {\n const sinceDate = since ? parseMcpDate(since, \"since\") : new Date(Date.now() - (windowHours ?? 24) * 60 * 60 * 1000);\n return json({\n since: sinceDate.toISOString(),\n posts: await listSocialPosts(deps.db, {\n workspaceId: grant.workspaceId,\n ...(connectionIds?.length ? { connectionIds } : {}),\n since: sinceDate,\n limit: boundedMcpLimit(limit),\n }),\n });\n });\n\n server.registerTool(\"social_daily_analysis_context\", {\n description: \"Collect social account and recent post context for a daily marketing analysis run.\",\n inputSchema: {\n connectionIds: z4.array(z4.string().uuid()).optional(),\n documentBaseIds: z4.array(z4.string().uuid()).optional(),\n since: z4.string().optional(),\n windowHours: z4.number().int().positive().optional(),\n limit: z4.number().int().positive().optional(),\n },\n }, async ({ connectionIds, documentBaseIds, since, windowHours, limit }) => {\n const allConnections = await listSocialConnections(deps.db, grant.workspaceId, 500);\n const selectedIds = connectionIds && connectionIds.length > 0 ? new Set(connectionIds) : null;\n const connections = selectedIds\n ? allConnections.filter((connection) => selectedIds.has(connection.id))\n : allConnections.filter((connection) => connection.status === \"connected\");\n if (selectedIds) {\n const foundIds = new Set(connections.map((connection) => connection.id));\n const missing = [...selectedIds].filter((id) => !foundIds.has(id));\n if (missing.length > 0) {\n throw new Error(`Unknown social connection IDs: ${missing.join(\", \")}`);\n }\n }\n const sinceDate = since ? parseMcpDate(since, \"since\") : new Date(Date.now() - (windowHours ?? 24) * 60 * 60 * 1000);\n const posts = connections.length > 0\n ? await listSocialPosts(deps.db, {\n workspaceId: grant.workspaceId,\n connectionIds: connections.map((connection) => connection.id),\n since: sinceDate,\n limit: boundedMcpLimit(limit),\n })\n : [];\n return json({\n generatedAt: new Date().toISOString(),\n window: {\n since: sinceDate.toISOString(),\n until: new Date().toISOString(),\n },\n documentBaseIds: documentBaseIds ?? [],\n connections,\n posts,\n instructions: [\n \"Use docs MCP search tools for the supplied documentBaseIds when brand, campaign, or audience knowledge is needed.\",\n \"Report data gaps explicitly when posts or metrics are missing.\",\n \"Do not infer unpublished metrics or hidden platform data.\",\n ],\n });\n });\n\n server.registerTool(\"scheduled_tasks_list\", {\n description: \"List scheduled tasks.\",\n inputSchema: { limit: z4.number().int().positive().optional() },\n }, async ({ limit }) => json({ tasks: await listScheduledTasks(deps.db, grant.workspaceId, limit ?? 100) }));\n\n server.registerTool(\"scheduled_tasks_get\", {\n description: \"Get one scheduled task.\",\n inputSchema: { id: z4.string().uuid() },\n }, async ({ id }) => json(await requireScheduledTask(deps.db, grant.workspaceId, id)));\n\n server.registerTool(\"scheduled_tasks_create\", {\n description: \"Create a scheduled task.\",\n inputSchema: {\n name: z4.string(),\n schedule: z4.unknown(),\n runMode: z4.string().optional(),\n overlapPolicy: z4.string().optional(),\n agentConfig: z4.unknown(),\n status: z4.string().optional(),\n environmentId: z4.string().uuid().optional(),\n metadata: z4.record(z4.string(), z4.unknown()).optional(),\n },\n }, async (args) => {\n const payload = CreateScheduledTaskRequest.parse(args);\n requireEnvironmentsUseForMcpAttachment(grant, payload.environmentId);\n await requireLimit(deps, { accountId: grant.accountId, workspaceId: grant.workspaceId, action: \"schedule:create\", quantity: 1 });\n const task = await createValidatedScheduledTask({ settings: deps.settings, db: deps.db, objectStorage: deps.objectStorage, grant, payload, toolsProvided: scheduledTaskToolsProvided(args) });\n await syncCreatedScheduledTask({ db: deps.db, workflowClient: deps.workflowClient, task });\n return json(task);\n });\n\n server.registerTool(\"scheduled_tasks_update\", {\n description: \"Update a scheduled task.\",\n inputSchema: {\n id: z4.string().uuid(),\n name: z4.string().optional(),\n schedule: z4.unknown().optional(),\n runMode: z4.string().optional(),\n overlapPolicy: z4.string().optional(),\n agentConfig: z4.unknown().optional(),\n status: z4.string().optional(),\n environmentId: z4.string().uuid().nullable().optional(),\n metadata: z4.record(z4.string(), z4.unknown()).optional(),\n },\n }, async ({ id, ...raw }) => {\n const existing = await requireScheduledTask(deps.db, grant.workspaceId, id);\n const payload = UpdateScheduledTaskRequest.parse(raw);\n requireEnvironmentsUseForMcpAttachment(grant, payload.environmentId);\n const update = await validatedScheduledTaskUpdate({ settings: deps.settings, db: deps.db, objectStorage: deps.objectStorage, grant, existing, payload, toolsProvided: scheduledTaskToolsProvided(raw) });\n const task = await updateScheduledTask(deps.db, grant.workspaceId, id, update);\n await syncUpdatedScheduledTask({ db: deps.db, workflowClient: deps.workflowClient, previous: existing, task });\n return json(task);\n });\n\n server.registerTool(\"scheduled_tasks_pause\", {\n description: \"Pause a scheduled task.\",\n inputSchema: { id: z4.string().uuid() },\n }, async ({ id }) => {\n const existing = await requireScheduledTask(deps.db, grant.workspaceId, id);\n const task = await updateScheduledTask(deps.db, grant.workspaceId, id, { status: \"paused\" });\n await syncUpdatedScheduledTask({ db: deps.db, workflowClient: deps.workflowClient, previous: existing, task });\n return json(task);\n });\n\n server.registerTool(\"scheduled_tasks_resume\", {\n description: \"Resume a scheduled task.\",\n inputSchema: { id: z4.string().uuid() },\n }, async ({ id }) => {\n const existing = await requireScheduledTask(deps.db, grant.workspaceId, id);\n const task = await updateScheduledTask(deps.db, grant.workspaceId, id, { status: \"active\" });\n await syncUpdatedScheduledTask({ db: deps.db, workflowClient: deps.workflowClient, previous: existing, task });\n return json(task);\n });\n\n server.registerTool(\"scheduled_tasks_trigger\", {\n description: \"Trigger a scheduled task immediately. Pass a stable triggerId to make a retried trigger idempotent (one charge, one run).\",\n inputSchema: { id: z4.string().uuid(), triggerId: z4.string().min(1).max(128).optional() },\n }, async ({ id, triggerId }) => {\n const task = await requireScheduledTask(deps.db, grant.workspaceId, id);\n await requireLimit(deps, { accountId: grant.accountId, workspaceId: grant.workspaceId, action: \"agent_run:create\", quantity: 1, model: task.agentConfig.model ?? deps.settings.openaiModel });\n const triggerToken = scheduledTaskTriggerToken(triggerId);\n const agentRunUsageIdempotencyKey = manualScheduledTaskTriggerUsageKey(grant.workspaceId, task.id, triggerToken);\n const triggerWorkflowId = manualScheduledTaskTriggerWorkflowId(task.id, triggerToken);\n await deps.workflowClient.triggerScheduledTask({ task, agentRunUsageIdempotencyKey, triggerWorkflowId });\n await recordWorkspaceUsage(deps, {\n accountId: grant.accountId,\n workspaceId: grant.workspaceId,\n subjectId: grant.subjectId,\n eventType: \"agent_run.created\",\n quantity: 1,\n unit: \"run\",\n sourceResourceType: \"scheduled_task\",\n sourceResourceId: task.id,\n idempotencyKey: agentRunUsageIdempotencyKey,\n });\n return json(task);\n });\n\n server.registerTool(\"scheduled_tasks_delete\", {\n description: \"Delete a scheduled task.\",\n inputSchema: { id: z4.string().uuid() },\n }, async ({ id }) => {\n const task = await requireScheduledTask(deps.db, grant.workspaceId, id);\n await deps.workflowClient.deleteScheduledTaskSchedule({ temporalScheduleId: task.temporalScheduleId });\n await deleteScheduledTask(deps.db, grant.workspaceId, id);\n return json({ ok: true });\n });\n\n server.registerTool(\"scheduled_task_runs_list\", {\n description: \"List runs for a scheduled task.\",\n inputSchema: { taskId: z4.string().uuid(), limit: z4.number().int().positive().optional() },\n }, async ({ taskId, limit }) => json({ runs: await listScheduledTaskRuns(deps.db, grant.workspaceId, taskId, limit ?? 100) }));\n\n return server;\n}\n\nfunction registerGoalTools(\n server: McpServer,\n deps: ApiRouteDeps,\n grant: AccessGrant,\n sessionId: string,\n json: (value: unknown) => { content: Array<{ type: \"text\"; text: string }> },\n): void {\n server.registerTool(\"goal_set\", {\n description: \"Set or replace this session's goal. While a goal is active the session keeps working: idle moments synthesize continuation turns until goal_complete or goal_pause is called. Replacing a goal reactivates it and resets the continuation budget.\",\n inputSchema: {\n text: z4.string().min(1),\n successCriteria: z4.string().min(1).optional(),\n maxAutoContinuations: z4.number().int().positive().optional(),\n },\n }, async ({ text, successCriteria, maxAutoContinuations }) => {\n await requireSession(deps.db, grant.workspaceId, sessionId);\n const { goal, replaced } = await upsertSessionGoal(deps.db, {\n accountId: grant.accountId,\n workspaceId: grant.workspaceId,\n sessionId,\n text,\n successCriteria: successCriteria ?? null,\n maxAutoContinuations: maxAutoContinuations ?? null,\n createdBy: \"agent\",\n });\n await appendAndPublishEvents(deps.db, deps.bus, grant.workspaceId, sessionId, [{\n type: \"goal.set\",\n payload: {\n goalId: goal.id,\n text: goal.text,\n ...(goal.successCriteria ? { successCriteria: goal.successCriteria } : {}),\n version: goal.version,\n actor: \"agent\",\n replaced,\n },\n }]);\n return json(goal);\n });\n\n server.registerTool(\"goal_update\", {\n description: \"Revise the session goal's text or success criteria, or record a progress note. Counts as progress for the no-progress detector; the goal stays active.\",\n inputSchema: {\n text: z4.string().min(1).optional(),\n successCriteria: z4.string().min(1).optional(),\n progressNote: z4.string().min(1).optional(),\n },\n }, async ({ text, successCriteria, progressNote }) => {\n await requireSession(deps.db, grant.workspaceId, sessionId);\n const existing = await getSessionGoal(deps.db, grant.workspaceId, sessionId);\n if (!existing) {\n throw new Error(\"this session has no goal; use goal_set first\");\n }\n if (existing.status === \"completed\") {\n throw new Error(\"session goal is completed; use goal_set to start a new goal\");\n }\n const goal = await updateSessionGoal(deps.db, grant.workspaceId, sessionId, {\n ...(text !== undefined ? { text } : {}),\n ...(successCriteria !== undefined ? { successCriteria } : {}),\n });\n await appendAndPublishEvents(deps.db, deps.bus, grant.workspaceId, sessionId, [{\n type: \"goal.updated\",\n payload: {\n goalId: goal.id,\n text: goal.text,\n ...(goal.successCriteria ? { successCriteria: goal.successCriteria } : {}),\n ...(progressNote ? { progressNote } : {}),\n version: goal.version,\n actor: \"agent\",\n },\n }]);\n return json(goal);\n });\n\n server.registerTool(\"goal_complete\", {\n description: \"Mark the session goal as completed. Requires concrete evidence (what was done and how it satisfies the success criteria). This is the explicit stop signal: no further continuation turns are synthesized.\",\n inputSchema: { evidence: z4.string().min(1) },\n }, async ({ evidence }) => {\n await requireSession(deps.db, grant.workspaceId, sessionId);\n const existing = await getSessionGoal(deps.db, grant.workspaceId, sessionId);\n if (!existing) {\n throw new Error(\"this session has no goal; use goal_set first\");\n }\n const { goal, changed } = await setSessionGoalStatus(deps.db, grant.workspaceId, sessionId, {\n status: \"completed\",\n evidence,\n });\n if (changed) {\n await appendAndPublishEvents(deps.db, deps.bus, grant.workspaceId, sessionId, [{\n type: \"goal.completed\",\n payload: { goalId: goal.id, evidence, version: goal.version },\n }]);\n }\n return json(goal);\n });\n\n server.registerTool(\"goal_pause\", {\n description: \"Pause the session goal with a rationale (blocked, not productive, needs human input). This is the explicit stop signal: no further continuation turns are synthesized until the goal is resumed or replaced.\",\n inputSchema: { rationale: z4.string().min(1) },\n }, async ({ rationale }) => {\n await requireSession(deps.db, grant.workspaceId, sessionId);\n const existing = await getSessionGoal(deps.db, grant.workspaceId, sessionId);\n if (!existing) {\n throw new Error(\"this session has no goal; use goal_set first\");\n }\n const { goal, changed } = await setSessionGoalStatus(deps.db, grant.workspaceId, sessionId, {\n status: \"paused\",\n rationale,\n pausedReason: \"agent\",\n });\n if (changed) {\n await appendAndPublishEvents(deps.db, deps.bus, grant.workspaceId, sessionId, [{\n type: \"goal.paused\",\n payload: {\n goalId: goal.id,\n actor: \"agent\",\n reason: \"agent\",\n rationale,\n autoContinuations: goal.autoContinuations,\n noProgressStreak: goal.noProgressStreak,\n },\n }]);\n }\n return json(goal);\n });\n}\n\ntype JsonResult = (value: unknown) => { content: Array<{ type: \"text\"; text: string }> };\n\n// Fleet tools (M7 bring-your-own-compute). Session-scoped (they steer THIS\n// session's active-sandbox pointer + reach the workspace's enrolled machines),\n// registered only with the worker-signed sessionId claim + the selfhosted flag.\n// The agent uses these to list the fleet (its Modal box + enrolled machines),\n// attach/swap the active sandbox mid-conversation (heterogeneous, single-active,\n// epoch-fenced), run a one-off op on a specific machine without swapping, and\n// surface provisioning (enroll-a-machine) instructions to a human.\nfunction registerFleetTools(\n server: McpServer,\n deps: ApiRouteDeps,\n grant: AccessGrant,\n sessionId: string,\n json: JsonResult,\n): void {\n const services: FleetServices = { db: deps.db, settings: deps.settings, bus: deps.bus };\n\n // Resolve the session's group sandbox (the default/home fleet member) at\n // call-time via the shared helper (same context the user-authenticated swap\n // REST route builds). Throws when the session has no box (backend:none) — the\n // fleet is only meaningful for a session that runs in a sandbox.\n const fleetContext = async (): Promise<FleetContext> =>\n await buildFleetContextForSession(deps, {\n accountId: grant.accountId,\n workspaceId: grant.workspaceId,\n sessionId,\n });\n\n server.registerTool(\"sandboxes_list\", {\n description:\n \"List the sandboxes this session can run on: its own session sandbox (the Modal box) PLUS the workspace's enrolled selfhosted machines, each with liveness (online/reconnecting/offline) and an `active` marker for the currently-routed one. Use before sandbox_attach/sandbox_swap to pick a target. The `id` of any entry is the `target` for attach/swap/run_on.\",\n inputSchema: {},\n }, async () => json(await listFleet(services, await fleetContext())));\n\n server.registerTool(\"sandbox_attach\", {\n description:\n \"Attach this session to a sandbox (make it the active sandbox the agent's next tool calls run on). Heterogeneous: a Modal box or an enrolled selfhosted machine. Validates the target is owned by this workspace and online, then repoints under an epoch fence. Identical mechanic to sandbox_swap; use `target` = a sandboxes_list `id`, or \\\"session\\\"/\\\"default\\\" for this session's own box.\",\n inputSchema: { target: z4.string().min(1) },\n }, async ({ target }) => json(await swapActiveSandbox(services, await fleetContext(), target)));\n\n server.registerTool(\"sandbox_swap\", {\n description:\n \"Swap the active sandbox for this session mid-conversation (the next tool call runs on the new box). Heterogeneous Modal<->selfhosted<->selfhosted, single active at a time, flippable as many times as you like. Validates ownership + liveness, then bumps the active epoch (fencing any in-flight op, which retries against the new box). `target` = a sandboxes_list `id`, or \\\"session\\\"/\\\"default\\\" to swap back to this session's own box.\",\n inputSchema: { target: z4.string().min(1) },\n }, async ({ target }) => json(await swapActiveSandbox(services, await fleetContext(), target)));\n\n server.registerTool(\"run_on\", {\n description:\n \"Run a ONE-OFF op on a SPECIFIC enrolled selfhosted machine WITHOUT changing this session's active sandbox (a side-channel to another machine). Ops: exec (run a command), read (read a file), write (write a file). `target` = a selfhosted sandboxes_list `id`. To make a machine the active sandbox instead, use sandbox_swap.\",\n inputSchema: {\n target: z4.string().min(1),\n op: z4.discriminatedUnion(\"kind\", [\n z4.object({ kind: z4.literal(\"exec\"), cmd: z4.string().min(1), workdir: z4.string().optional() }),\n z4.object({ kind: z4.literal(\"read\"), path: z4.string().min(1) }),\n z4.object({ kind: z4.literal(\"write\"), path: z4.string().min(1), content: z4.string() }),\n ]),\n },\n }, async ({ target, op }) => json(await runOnSandbox(services, await fleetContext(), target, op as RunOnOp)));\n\n server.registerTool(\"sandbox_provision\", {\n description:\n \"Provision a new sandbox for the fleet. kind=selfhosted returns device-flow enrollment instructions to share with a HUMAN (install the agent + enroll their machine with loud whole-machine consent — the agent cannot self-consent). kind=modal creates a named Modal sandbox record (its box materializes on first swap).\",\n inputSchema: {\n kind: z4.enum([\"selfhosted\", \"modal\"]),\n name: z4.string().min(1).max(120).optional(),\n },\n }, async ({ kind, name }) => json(await provisionSandbox(services, await fleetContext(), { kind, ...(name ? { name } : {}) })));\n}\n\n// Workspace orchestration for manager-style agents: sessions are listed,\n// inspected, spawned, and steered with the same domain functions the REST\n// routes use, so limits, validation, and usage metering cannot drift.\nfunction registerWorkspaceOrchestrationTools(\n server: McpServer,\n deps: ApiRouteDeps,\n grant: AccessGrant,\n can: (permission: Permission) => boolean,\n json: JsonResult,\n): void {\n if (can(\"sessions:read\")) {\n server.registerTool(\"sessions_list\", {\n description: \"List sessions in this workspace, newest first.\",\n inputSchema: { limit: z4.number().int().positive().optional() },\n }, async ({ limit }) => json({ sessions: await listSessions(deps.db, grant.workspaceId, boundedMcpLimit(limit)) }));\n\n server.registerTool(\"session_get\", {\n description: \"Get one session: status, goal-bearing metadata, resources, tools, and environment attachment (names/ids only, never variable values). Unbounded agent-set fields (metadata, initial message) are clamped so monitoring another session cannot flood this context.\",\n inputSchema: { sessionId: z4.string().uuid() },\n }, async ({ sessionId }) => {\n const session = await getSession(deps.db, grant.workspaceId, sessionId);\n if (!session) {\n throw new Error(\"session not found\");\n }\n return json(capSessionDetail(session));\n });\n\n server.registerTool(\"session_events\", {\n description: \"Read a session's event timeline (oldest first), to monitor another session's progress. Pass `after` = the highest event `sequence` already seen to page forward; the response's `nextAfter` is that cursor. The response is BYTE-CAPPED for a monitoring glance: fat per-event payloads (a worker's verbatim tool outputs, message/reasoning bodies) are clamped, and an over-budget page is reduced to its head + tail with a marker — page the gap with `after`/`limit`, or read the worker's session notebook, if you need omitted content verbatim. `nextAfter` always advances past every event the page covered, so paging never skips real events.\",\n inputSchema: {\n sessionId: z4.string().uuid(),\n after: z4.number().int().nonnegative().optional(),\n limit: z4.number().int().positive().optional(),\n },\n }, async ({ sessionId, after, limit }) => {\n await requireSession(deps.db, grant.workspaceId, sessionId);\n const events = await listSessionEvents(deps.db, grant.workspaceId, sessionId, after ?? 0, boundedMcpLimit(limit));\n const capped = capEventPage(events);\n return json({\n events: capped.events,\n nextAfter: capped.nextAfter ?? after ?? 0,\n ...(capped.truncated ? { truncated: true } : {}),\n });\n });\n }\n\n if (can(\"sessions:create\")) {\n server.registerTool(\"session_create\", {\n description: \"Spawn a new agent session (a worker) with an initial message and optional goal, resources (e.g. repositories from github_repositories_list), tools, and workspace environment attachment. Environment attachment happens at creation only — it cannot be added to a running session — and requires the environments:use permission. When targetSandboxId names a machine, workingDir sets the working directory (cwd) the spawned session runs under on that machine.\",\n inputSchema: {\n initialMessage: z4.string().min(1),\n // Per-session agent persona/system instructions for the spawned worker\n // (a per-agent-type prompt). Delivered system-level, composed AFTER the\n // workspace persona; never shown in the worker's timeline. Trimmed,\n // non-empty, max 32768 chars (re-validated by the contracts schema).\n instructions: z4.string().min(1).max(32768).optional(),\n goal: z4.unknown().optional(),\n resources: z4.array(z4.unknown()).optional(),\n tools: z4.array(z4.unknown()).optional(),\n // Per-session third-party MCP servers. Credential header values are\n // accepted only at create and never appear in responses/events.\n mcpServers: z4.array(z4.unknown()).optional(),\n environmentId: z4.string().uuid().optional(),\n model: z4.string().min(1).optional(),\n reasoningEffort: z4.string().optional(),\n sandboxBackend: z4.string().optional(),\n // Create-time machine targeting: an enrolled sandbox id (from\n // sandboxes_list) to run the spawned session on. Seeds the active-sandbox\n // pointer at creation so the FIRST turn lands on the chosen machine\n // (race-free). Ownership + liveness are validated in the domain via the\n // same path as sandbox_swap; an unowned/offline/unknown target 422s.\n targetSandboxId: z4.string().uuid().optional(),\n // The working directory (cwd) for a machine target: the path/cwd base the\n // spawned session's agent exec, terminal, and file dock run under. A\n // workspace_root-relative subdir or an absolute machine path. Only valid\n // WITH targetSandboxId (workingDir alone 422s); omitted ⇒ workspace_root.\n workingDir: z4.string().optional(),\n metadata: z4.record(z4.string(), z4.unknown()).optional(),\n // Workspace-scoped CREATE idempotency key: a retried session_create with\n // the same key returns the already-spawned worker instead of a duplicate.\n idempotencyKey: z4.string().min(1).max(200).optional(),\n // First-party MCP token permissions for the spawned session; every\n // permission must be held by this grant (validated in the domain).\n firstPartyMcpPermissions: z4.array(z4.string()).optional(),\n // Shared-sandbox placement (addendum 05 §D). OMIT (default) to SHARE the\n // creator's box — one filesystem/repo/desktop, N independent conversations;\n // this is the SAFE DEFAULT. Pass \"new\" for a fresh isolated box (a different\n // repo set or a genuinely separate filesystem), or {groupId} (a sibling\n // session's `sandboxGroupId` from a prior session_create response) to join\n // that specific sibling's box.\n // Shared state must be compatible: a shared box requires the SAME image\n // (rejected at the lease layer, B3) and — because the box's environment is\n // fixed at creation under the current mechanics — the SAME workspace\n // Environment. The domain layer is env-aware: an inherited default with a\n // different environmentId silently gets its OWN box (the spawn still works),\n // while an explicit shared/{groupId} with a mismatched environment 422s at\n // create. When the Environment is eventually evicted from the box manifest\n // (per-exec, like the git token), the env check dissolves on its own.\n // The description below is what the AGENT sees (this comment is invisible to\n // it); keep the two in sync.\n sandbox: z4.union([\n z4.literal(\"shared\"),\n z4.literal(\"new\"),\n z4.object({ groupId: z4.string().uuid() }),\n ]).describe(\n \"Sandbox placement. OMIT (default) to SHARE the creator's box — one filesystem/repo/desktop, N independent conversations; this is the safe default. If the new session attaches a DIFFERENT environment than the creator's box, the platform automatically gives it its own box (the box environment is fixed at creation), so omitting stays safe. Pass 'new' for a fresh isolated box (different repo set or a genuinely separate filesystem). Pass {groupId} to join a specific sibling's box — requires the same environment (a mismatch is rejected at create) and the same image (a conflicting image is rejected when the box warms).\",\n ).optional(),\n // The parent (manager) session is auto-inferred from the caller's\n // worker-signed sessionId claim, so a spawned worker's completion wakes\n // its manager automatically. There is deliberately no caller-supplied\n // parent parameter: it would let a sessions:create grant target an\n // arbitrary session's wake channel without sessions:control on it.\n },\n }, async (args) => json(await createSessionForRequest(deps, grant, grant.workspaceId, args)));\n }\n\n if (can(\"sessions:control\")) {\n server.registerTool(\"session_send_message\", {\n description: \"Post a user message into an existing session; the session queues a turn and resumes if idle.\",\n inputSchema: {\n sessionId: z4.string().uuid(),\n text: z4.string().min(1),\n // Header-value rotation only. URL/name/tool settings are immutable\n // after create; core enforces mcp_servers:attach on this field.\n mcpCredentialUpdates: z4.array(z4.unknown()).optional(),\n },\n }, async ({ sessionId, text, mcpCredentialUpdates }) => {\n const { accepted, turn } = await acceptSessionUserMessage(deps, grant, grant.workspaceId, sessionId, {\n text,\n toolsProvided: false,\n mcpCredentialUpdates: (mcpCredentialUpdates ?? []).map((update) => SessionMcpCredentialUpdateInput.parse(update)),\n });\n return json({ event: accepted, turnId: turn.id });\n });\n\n server.registerTool(\"session_interrupt\", {\n description:\n \"Interrupt a session in this workspace. mode='stop' (default) cancels the current turn AND pauses the session's active goal so it halts. mode='steer' cancels the current turn WITHOUT pausing the goal, so the session picks up its next queued turn (or, if nothing is queued, continues toward its active goal) — pair it with a preceding session_send_message to redirect a running session. Works whether the target is mid-turn or idle.\",\n inputSchema: {\n sessionId: z4.string().uuid(),\n mode: z4.enum([\"stop\", \"steer\"]).optional(),\n },\n }, async ({ sessionId, mode }) => {\n await requireSession(deps.db, grant.workspaceId, sessionId);\n const appended = await appendAndPublishEvents(deps.db, deps.bus, grant.workspaceId, sessionId, [{\n type: \"user.interrupt\",\n payload: mode === \"steer\" ? { reason: \"steer\" } : {},\n }]);\n const accepted = appended[0];\n if (!accepted) {\n throw new Error(\"failed to append interrupt event\");\n }\n await deps.workflowClient.signalInterrupt({\n accountId: grant.accountId,\n workspaceId: grant.workspaceId,\n sessionId,\n eventId: accepted.id,\n workflowId: workflowIdForSession(sessionId),\n });\n return json({ event: accepted });\n });\n\n server.registerTool(\"set_other_session_title\", {\n description: \"Set another session's display title to a concise 3-7 word summary. The target session must belong to this workspace. Replaces an existing title unless a human has manually set it.\",\n inputSchema: {\n session_id: z4.string().uuid(),\n title: z4.string().min(1).max(200),\n },\n }, async ({ session_id, title }) => {\n await requireSession(deps.db, grant.workspaceId, session_id);\n const result = await updateSessionTitle(deps, grant.workspaceId, session_id, title, \"agent\");\n return json({ ok: true, updated: result.updated, title: result.title ?? title });\n });\n }\n}\n\n// Environment management for manager-style agents. v1 deliberately accepts\n// variable VALUES in plain tool arguments: the calling model is trusted with\n// the secrets it is persisting (see docs/environments.md). Reads stay\n// write-only — responses carry names and metadata, never values.\nfunction registerEnvironmentTools(\n server: McpServer,\n deps: ApiRouteDeps,\n grant: AccessGrant,\n can: (permission: Permission) => boolean,\n json: JsonResult,\n): void {\n if (can(\"environments:use\")) {\n server.registerTool(\"environment_list\", {\n description: \"List workspace environments with variable names and metadata (versions, timestamps). Values are write-only and never returned.\",\n inputSchema: {},\n }, async () => json({ environments: await listWorkspaceEnvironments(deps.db, grant.workspaceId) }));\n }\n\n if (can(\"environments:manage\")) {\n server.registerTool(\"environment_set_variable\", {\n description: \"Set or rotate one variable in a workspace environment. Target by environmentId, or by environmentName (created if it does not exist). The value is encrypted at rest and injected into sandboxes of sessions the environment is attached to; it is never readable back through any API.\",\n inputSchema: {\n environmentId: z4.string().uuid().optional(),\n environmentName: z4.string().min(1).optional(),\n name: z4.string().min(1),\n value: z4.string().min(1).max(32768),\n },\n }, async ({ environmentId, environmentName, name, value }) => {\n const key = requireEnvironmentEncryption(deps.settings);\n const parsedName = WorkspaceEnvironmentVariableName.safeParse(name);\n if (!parsedName.success) {\n throw new Error(\"environment variable names must match ^[A-Z][A-Z0-9_]*$\");\n }\n assertAllowedEnvironmentVariableName(parsedName.data);\n if ((environmentId === undefined) === (environmentName === undefined)) {\n throw new Error(\"provide exactly one of environmentId or environmentName\");\n }\n const trimmedEnvironmentName = environmentName?.trim();\n if (environmentName !== undefined && !trimmedEnvironmentName) {\n throw new Error(\"environment name is required\");\n }\n let created = false;\n let environment = environmentId !== undefined\n ? await getWorkspaceEnvironment(deps.db, grant.workspaceId, environmentId)\n : await getWorkspaceEnvironmentByName(deps.db, grant.workspaceId, trimmedEnvironmentName!);\n if (!environment && environmentId !== undefined) {\n throw new Error(\"environment not found\");\n }\n if (!environment) {\n if (await countWorkspaceEnvironments(deps.db, grant.workspaceId) >= MAX_ENVIRONMENTS_PER_WORKSPACE) {\n throw new Error(`a workspace supports at most ${MAX_ENVIRONMENTS_PER_WORKSPACE} environments`);\n }\n environment = await createWorkspaceEnvironment(deps.db, {\n accountId: grant.accountId,\n workspaceId: grant.workspaceId,\n name: trimmedEnvironmentName!,\n });\n created = true;\n await recordEnvironmentAuditEvent(deps.db, { grant, action: \"environment.created\", environmentId: environment.id });\n }\n const exists = environment.variables.some((variable) => variable.name === parsedName.data);\n if (!exists && environment.variables.length >= MAX_VARIABLES_PER_ENVIRONMENT) {\n throw new Error(`an environment supports at most ${MAX_VARIABLES_PER_ENVIRONMENT} variables`);\n }\n const metadata = await setWorkspaceEnvironmentVariable(deps.db, {\n accountId: grant.accountId,\n workspaceId: grant.workspaceId,\n environmentId: environment.id,\n name: parsedName.data,\n valueEncrypted: encryptEnvironmentValue(key, value),\n });\n await recordEnvironmentAuditEvent(deps.db, { grant, action: \"environment.variable.set\", environmentId: environment.id, variableName: parsedName.data });\n return json({\n environment: { id: environment.id, name: environment.name, created },\n variable: metadata,\n });\n });\n }\n}\n\n// GitHub connect link for manager-style agents: mirrors GET .../github/app but\n// returns a browser entry URL (.../github/connect) that plants the CSRF state\n// cookie before forwarding to GitHub, because an MCP-issued link is opened in\n// a browser that never called the API directly.\nfunction registerGitHubConnectTool(\n server: McpServer,\n deps: ApiRouteDeps,\n grant: AccessGrant,\n options: McpServerOptions,\n json: JsonResult,\n): void {\n server.registerTool(\"github_connect_link\", {\n description: \"Create a workspace-bound GitHub App install link to share with a human. Opening it redirects to GitHub to install the app and select repositories for this workspace; completing the connection requires the person to be signed in to this OpenGeni deployment with github:manage. The link expires.\",\n inputSchema: {},\n }, async () => {\n const { settings } = deps;\n const missing = githubAppMissingSettings(settings);\n const slug = settings.githubAppSlug?.trim() || null;\n if (missing.length > 0 || !slug) {\n return json({ configured: false, appSlug: slug, installUrl: null, missing });\n }\n const base = (settings.publicBaseUrl ?? settings.githubAppManifestBaseUrl ?? options.requestOrigin ?? \"\").replace(/\\/+$/, \"\");\n if (!base) {\n throw new Error(\"github_connect_link requires OPENGENI_PUBLIC_BASE_URL (or OPENGENI_GITHUB_APP_MANIFEST_BASE_URL) so the install link can route through this deployment\");\n }\n const state = createSignedState(deps.githubStateSecret, {\n accountId: grant.accountId,\n workspaceId: grant.workspaceId,\n });\n return json({\n configured: true,\n appSlug: slug,\n installUrl: `${base}/v1/workspaces/${grant.workspaceId}/github/connect?state=${encodeURIComponent(state)}`,\n expiresInSeconds: stateMaxAgeSeconds,\n missing: [],\n });\n });\n}\n\n// TOKEN-BROKER (B1): mint a FRESH short-lived GitHub App installation token for the\n// session's repository resources. The agent calls this to refresh git auth before\n// the current token expires. The MCP server CANNOT write the box, so the tool RETURNS\n// the token as JSON; the agent writes it to the token file (via exec) to refresh\n// GIT_ASKPASS. Same github:use capability gate as github_connect_link.\nfunction registerGitHubTokenTool(\n server: McpServer,\n deps: ApiRouteDeps,\n grant: AccessGrant,\n sessionId: string,\n json: JsonResult,\n): void {\n server.registerTool(\"github_token\", {\n description: \"Mint a fresh short-lived GitHub token for this session's repositories. Write it to $OPENGENI_GIT_TOKEN_FILE (default $HOME/.opengeni/git-token) to refresh git auth before the current token expires.\",\n inputSchema: {},\n }, async () => {\n const session = await requireSession(deps.db, grant.workspaceId, sessionId);\n // Resolve the run-scoped installation + repository ids from THIS session's\n // repository resources (same shape sandboxEnvironmentForRun mints against). Only\n // private GitHub-App repos carry the installation/repository ids.\n const selected = (session.resources ?? []).flatMap((resource) => {\n if (resource.kind !== \"repository\") {\n return [];\n }\n const installationId = resource.githubInstallationId;\n const repositoryId = resource.githubRepositoryId;\n return typeof installationId === \"number\" && installationId > 0\n && typeof repositoryId === \"number\" && repositoryId > 0\n ? [{ installationId, repositoryId }]\n : [];\n });\n if (selected.length === 0) {\n throw new Error(\"this session has no GitHub App repository resources to mint a token for\");\n }\n const installationId = selected[0]!.installationId;\n if (selected.some((item) => item.installationId !== installationId)) {\n throw new Error(\"GitHub App repository resources must belong to one installation\");\n }\n const token = await createGitHubAppInstallationToken(deps.settings, {\n installationId,\n repositoryIds: selected.map((item) => item.repositoryId),\n });\n return json({\n token,\n tokenFile: \"$OPENGENI_GIT_TOKEN_FILE (default $HOME/.opengeni/git-token)\",\n });\n });\n}\n\n// Defense-in-depth for invariant \"agents cannot self-attach\": the worker's\n// first-party delegated token never carries environments:use, so sandboxed\n// agents calling these MCP tools cannot attach a workspace environment.\n// Explicit detach (environmentId: null) is also an attachment change and is\n// blocked the same way.\nfunction requireEnvironmentsUseForMcpAttachment(grant: AccessGrant, environmentId: string | null | undefined): void {\n if (environmentId !== undefined && !hasPermission(grant.permissions, \"environments:use\")) {\n throw new Error(\"missing permission: environments:use\");\n }\n}\n\nfunction repositoryWithScheduledTaskResource(repository: GitHubRepository): GitHubRepository & { resource: ResourceRef } {\n const uri = normalizedRepositoryUri(repository.cloneUrl);\n return {\n ...repository,\n resource: {\n kind: \"repository\",\n uri,\n ref: repository.defaultBranch,\n mountPath: repositoryMountPath(uri),\n ...(repository.private ? { githubInstallationId: repository.installationId, githubRepositoryId: repository.id } : {}),\n },\n };\n}\n\nfunction normalizedRepositoryUri(value: string): string {\n const url = new URL(value);\n const path = url.pathname.replace(/^\\/+|\\/+$/g, \"\").replace(/\\.git$/, \"\");\n return `https://${url.hostname.toLowerCase()}/${path}.git`;\n}\n\nfunction repositoryMountPath(uri: string): string {\n const url = new URL(uri);\n return `repos/${url.pathname.replace(/^\\/+|\\/+$/g, \"\").replace(/\\.git$/, \"\")}`;\n}\n\nfunction boundedMcpLimit(limit: number | undefined): number {\n if (!limit || !Number.isFinite(limit)) {\n return 100;\n }\n return Math.min(500, Math.max(1, Math.floor(limit)));\n}\n\nfunction parseMcpDate(raw: string, label: string): Date {\n const date = new Date(raw);\n if (Number.isNaN(date.getTime())) {\n throw new Error(`${label} must be an ISO date-time`);\n }\n return date;\n}\n","/**\n * Byte/token caps for the cross-session read tools (`session_events`,\n * `session_get`) exposed to manager-style agents over MCP.\n *\n * A long-lived manager session monitors its spawned workers by reading their\n * event timeline. A worker's events carry verbatim model output and, worse,\n * verbatim TOOL OUTPUTS (`agent.toolCall.output.payload.output`) and raw tool\n * call items (`agent.toolCall.created.payload.raw` / `.arguments`). Those are\n * sized for the worker's own context, not the manager's: a single\n * `session_events` page (the DB limit caps event COUNT, not BYTES) can return\n * tens of thousands of characters, and a manager that pages a busy worker piles\n * hundreds of thousands of characters into its own context in one monitoring\n * turn — the exact \"parent ingests child\" blow-up that bricks the manager.\n *\n * The manager rarely needs a worker's full message deltas / tool outputs\n * verbatim; it needs status + recent progress. So these tools cap what they\n * hand back in two stages, both pure and exhaustively testable here:\n *\n * 1. PER-EVENT FIELD TRIM (`capEventPayload` / `capPayloadValue`): walk each\n * event's payload and clamp any over-long string (and any over-large nested\n * object, by serializing then clamping) to a per-field budget, leaving an\n * explicit `…N chars truncated…` marker. Type-agnostic: it targets whatever\n * field is fat (`text`, `output`, `arguments`, `raw`, `delta`, …) without\n * enumerating event types, so a new fat event type is capped automatically.\n *\n * 2. HEAD+TAIL PAGE BUDGET (`capEventPage`): after per-event trim, if the page\n * still exceeds the total token budget, keep a HEAD (oldest, for entry\n * context) and a TAIL (newest, for recent progress) of events and drop the\n * middle, inserting one synthetic marker event that says how many were\n * dropped and how to get them (page with `after`/`limit`, or read the\n * notebook). Pagination semantics are preserved: `nextAfter` is still the\n * real highest `sequence` returned, so the next page starts exactly where\n * this one ended.\n *\n * Worker-side and UI consumers never go through here — they call the DB\n * functions or the REST routes directly. This module only shapes the MCP tool\n * result a manager model reads, and it is intentionally dependency-free (no DB)\n * so the cap logic can be unit-tested in isolation.\n */\n\nimport type { SessionEvent } from \"@opengeni/contracts\";\n\n// ~4 chars per token is the same coarse estimate the runtime compaction path\n// uses; we only need an order-of-magnitude budget, not exact tokenization.\nconst CHARS_PER_TOKEN = 4;\n\nexport function estimateTokensFromChars(chars: number): number {\n return Math.ceil(chars / CHARS_PER_TOKEN);\n}\n\nfunction estimateValueTokens(value: unknown): number {\n return estimateTokensFromChars(safeStringify(value).length);\n}\n\nexport type EventCapConfig = {\n // Per-event cap: max characters any single string field (or serialized\n // nested object) inside an event payload may contribute before it is clamped\n // with a truncation marker.\n perFieldChars: number;\n // Total page cap: max estimated tokens the whole returned event array may\n // occupy. When the per-event-trimmed page still exceeds this, head+tail\n // selection drops the middle.\n pageTokenBudget: number;\n // When head+tail selection kicks in, how many events to keep at each end.\n headEvents: number;\n tailEvents: number;\n};\n\n// ~2k chars (~500 tokens) per fat field keeps a status glance readable without\n// shipping a worker's whole tool output. ~10k-token page budget sits in the\n// 8–12k target band; head/tail of 8 keeps entry context plus recent progress.\nexport const DEFAULT_EVENT_CAP: EventCapConfig = {\n perFieldChars: 2_000,\n pageTokenBudget: 10_000,\n headEvents: 8,\n tailEvents: 8,\n};\n\n// ~6k chars (~1.5k tokens) for a single session detail blob: resources/tools/\n// metadata are normally tiny, but agent-set metadata is unbounded, so clamp it.\nexport const DEFAULT_SESSION_DETAIL_CHARS = 6_000;\n\nfunction safeStringify(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\nfunction truncationMarker(droppedChars: number): string {\n return `…[${droppedChars} chars truncated — page with after/limit on session_events, or read the session notebook for the full content]`;\n}\n\nfunction clampString(value: string, maxChars: number): string {\n if (value.length <= maxChars) {\n return value;\n }\n // Keep a head and a small tail of the field so both the start and the end\n // (often the most diagnostic part of a tool output / error) survive.\n const dropped = value.length - maxChars;\n const headChars = Math.max(0, Math.floor(maxChars * 0.7));\n const tailChars = Math.max(0, maxChars - headChars);\n const head = value.slice(0, headChars);\n const tail = tailChars > 0 ? value.slice(value.length - tailChars) : \"\";\n return `${head}${truncationMarker(dropped)}${tail}`;\n}\n\n/**\n * Recursively clamp any over-budget string or nested value inside a payload.\n * Strings longer than `perFieldChars` are head+tail clamped. Nested objects /\n * arrays whose serialized form exceeds `perFieldChars` are recursed into so the\n * clamp lands on the actual fat leaf; if recursion cannot shrink them enough\n * (e.g. thousands of tiny fields), the whole branch is replaced by its clamped\n * serialization. Plain scalars pass through untouched. A depth guard makes the\n * walk safe against pathological / cyclic structures.\n */\nexport function capPayloadValue(value: unknown, perFieldChars: number, depth = 0): unknown {\n if (typeof value === \"string\") {\n return clampString(value, perFieldChars);\n }\n if (value === null || typeof value !== \"object\") {\n return value;\n }\n // Guard against pathological / cyclic structures: past a reasonable depth,\n // collapse to a clamped serialization.\n if (depth >= 8) {\n return clampString(safeStringify(value), perFieldChars);\n }\n const serializedLength = safeStringify(value).length;\n if (serializedLength <= perFieldChars) {\n return value;\n }\n if (Array.isArray(value)) {\n const mapped = value.map((entry) => capPayloadValue(entry, perFieldChars, depth + 1));\n if (safeStringify(mapped).length <= perFieldChars * 2) {\n return mapped;\n }\n return clampString(safeStringify(value), perFieldChars);\n }\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value as Record<string, unknown>)) {\n out[key] = capPayloadValue(entry, perFieldChars, depth + 1);\n }\n // If recursion still left the object fat (many small fields), fall back to a\n // clamped serialization so the page budget is respected.\n if (safeStringify(out).length <= perFieldChars * 4) {\n return out;\n }\n return clampString(safeStringify(value), perFieldChars);\n}\n\nexport function capEventPayload(event: SessionEvent, perFieldChars: number): SessionEvent {\n const cappedPayload = capPayloadValue(event.payload, perFieldChars);\n if (cappedPayload === event.payload) {\n return event;\n }\n return { ...event, payload: cappedPayload };\n}\n\nexport type CappedEventPage = {\n events: SessionEvent[];\n // The real highest `sequence` among the events the DB returned, so the caller\n // can advance the cursor correctly even when the middle was dropped. Null\n // when the page was empty.\n nextAfter: number | null;\n truncated: boolean;\n};\n\n/**\n * Build a synthetic marker event that stands in for the dropped middle. It is\n * NOT a real persisted event; its `id` is the zero UUID and its sequence sits\n * between the kept head and tail so ordering by sequence stays monotonic. It\n * never participates in pagination (the caller derives `nextAfter` from the\n * real events, not this marker). Typed `session.status.changed` so the\n * synthetic event still validates against the `SessionEvent` contract.\n */\nfunction buildTruncationEvent(\n template: SessionEvent,\n droppedCount: number,\n firstDroppedSequence: number,\n lastDroppedSequence: number,\n markerSequence: number,\n): SessionEvent {\n return {\n id: \"00000000-0000-0000-0000-000000000000\",\n workspaceId: template.workspaceId,\n sessionId: template.sessionId,\n sequence: markerSequence,\n type: \"session.status.changed\",\n payload: {\n _truncated: true,\n note: `${droppedCount} event(s) (sequence ${firstDroppedSequence}–${lastDroppedSequence}) omitted from this monitoring view to keep the response bounded. Page the gap with session_events after=${firstDroppedSequence - 1} limit=… if you need them verbatim, or read the worker's session notebook.`,\n droppedCount,\n omittedSequenceRange: [firstDroppedSequence, lastDroppedSequence],\n },\n occurredAt: template.occurredAt,\n clientEventId: null,\n turnId: null,\n };\n}\n\n/**\n * Apply per-event field trim then, if the page is still over budget, keep a\n * head and a tail of events and drop the middle behind a marker. `events` is\n * assumed oldest-first (as `listSessionEvents` returns).\n */\nexport function capEventPage(events: SessionEvent[], config: EventCapConfig = DEFAULT_EVENT_CAP): CappedEventPage {\n const realLast = events[events.length - 1];\n const nextAfter = realLast ? realLast.sequence : null;\n\n const trimmed = events.map((event) => capEventPayload(event, config.perFieldChars));\n\n let runningTokens = 0;\n let overBudget = false;\n for (const event of trimmed) {\n runningTokens += estimateValueTokens(event);\n if (runningTokens > config.pageTokenBudget) {\n overBudget = true;\n break;\n }\n }\n\n const keepCount = config.headEvents + config.tailEvents;\n if (!overBudget || trimmed.length <= keepCount + 1) {\n return { events: trimmed, nextAfter, truncated: overBudget && trimmed.length > keepCount + 1 };\n }\n\n const head = trimmed.slice(0, config.headEvents);\n const tail = trimmed.slice(trimmed.length - config.tailEvents);\n const droppedStart = config.headEvents;\n const droppedEnd = trimmed.length - config.tailEvents - 1;\n const droppedCount = droppedEnd - droppedStart + 1;\n const firstDroppedSequence = trimmed[droppedStart]!.sequence;\n const lastDroppedSequence = trimmed[droppedEnd]!.sequence;\n // Marker sequence sits between the kept head and tail; reusing the last head\n // sequence keeps the returned page monotonic non-decreasing by sequence.\n const markerSequence = head[head.length - 1]!.sequence;\n const marker = buildTruncationEvent(\n realLast!,\n droppedCount,\n firstDroppedSequence,\n lastDroppedSequence,\n markerSequence,\n );\n\n return {\n events: [...head, marker, ...tail],\n nextAfter,\n truncated: true,\n };\n}\n\n/**\n * Clamp a single session-detail object for `session_get`. Only the unbounded\n * agent-controlled fields (`metadata`, and defensively `initialMessage`) can\n * grow large; everything else is small and structural. Returns a shallow copy\n * with those fields capped when over budget, otherwise the original reference.\n */\nexport function capSessionDetail<T extends { metadata?: unknown; initialMessage?: unknown }>(\n session: T,\n perFieldChars: number = DEFAULT_SESSION_DETAIL_CHARS,\n): T {\n let changed = false;\n const out: T = { ...session };\n if (session.metadata !== undefined) {\n const capped = capPayloadValue(session.metadata, perFieldChars);\n if (capped !== session.metadata) {\n (out as { metadata?: unknown }).metadata = capped;\n changed = true;\n }\n }\n if (typeof session.initialMessage === \"string\" && session.initialMessage.length > perFieldChars) {\n (out as { initialMessage?: unknown }).initialMessage = clampString(session.initialMessage, perFieldChars);\n changed = true;\n }\n return changed ? out : session;\n}\n","import { readFile, stat } from \"node:fs/promises\";\nimport type { Hono } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\n\n// The get.<domain> install-serving routes (dossier §23.1). These are\n// UNAUTHENTICATED (see http/auth.ts isAuthExempt — the `installExemptPaths` set)\n// so a fresh machine with no credentials can `curl -fsSL https://get.<domain>/install.sh`,\n// read it first, then pipe to sh. They serve the IN-REPO committed script bodies\n// (agent/install/*) verbatim — a single branded, audit-greppable trust root.\n//\n// The script BODIES contain NO secrets (POSIX sh, the device-flow captures the\n// loud consent). The release-binary asset routes serve the agent BAKED into THIS\n// control-plane image (the per-SHA Linux musl binary + its `.sha256`/`.minisig`)\n// when present, and otherwise 302-redirect to the matching GitHub Release asset.\n//\n// \"The agent ships inside the control-plane\" (the owned decision): for every\n// deployed env (preview/staging/managed-prod) the API image — already built\n// per-SHA by GitHub Actions from the PR branch — bakes the SIGNED `opengeni-agent`\n// binary matching that EXACT control-plane SHA into agent/install/baked/ (a CI step\n// signs + COPYs; the signing key never enters the Docker build). install.sh then\n// pulls a binary that is guaranteed in lockstep with the API it enrolls against —\n// zero drift, zero new store. GitHub Releases remains the PUBLIC archive + the\n// self-update channel + the install.sh fallback (mac/windows, and any asset this\n// image did not bake), reached by the 302 below.\n\n// The committed install artifacts, resolved relative to this module so the API\n// (run from source under /app via bun) locates the sibling agent/install/ dir at\n// runtime. apps/api/src/routes -> ../../../../agent/install.\nconst INSTALL_DIR = new URL(\"../../../../agent/install/\", import.meta.url);\n\n// The baked release-binary dir (a sibling of the committed scripts). The build's\n// signing step writes the per-SHA Linux musl binaries + their `.sha256`/`.minisig`\n// siblings here; in a plain `docker build` (or a source checkout) it holds only a\n// `.gitkeep`, so every asset falls through to the GitHub-Releases redirect.\nconst BAKED_DIR = new URL(\"baked/\", INSTALL_DIR);\n\n// The static text artifacts served verbatim, with their content types. Each is\n// read once at first request and memoized (committed files; immutable per deploy).\nconst TEXT_ASSETS: Record<string, { file: string; contentType: string }> = {\n \"/install.sh\": { file: \"install.sh\", contentType: \"text/x-shellscript; charset=utf-8\" },\n \"/install.ps1\": { file: \"install.ps1\", contentType: \"text/plain; charset=utf-8\" },\n \"/uninstall.sh\": { file: \"uninstall.sh\", contentType: \"text/x-shellscript; charset=utf-8\" },\n \"/opengeni-agent-minisign.pub\": { file: \"opengeni-agent-minisign.pub\", contentType: \"text/plain; charset=utf-8\" },\n};\n\nconst assetCache = new Map<string, string>();\n\nasync function loadAsset(file: string): Promise<string> {\n const cached = assetCache.get(file);\n if (cached !== undefined) {\n return cached;\n }\n const body = await readFile(new URL(file, INSTALL_DIR), \"utf8\");\n assetCache.set(file, body);\n return body;\n}\n\n// \"The agent ships inside the control-plane\" (cont.): the committed install\n// scripts default their release-asset base URL to the public archive\n// (get.opengeni.ai) so a from-source / standalone copy still works. But a\n// DEPLOYED control plane must serve a script that pulls the agent from ITSELF —\n// the per-SHA binary baked into THIS image, via the /agent/* routes below — so\n// `curl https://<this-host>/install.sh | sh` installs the exact agent that\n// matches the API it enrolls against, with NO dependency on a public CDN (which a\n// private/air-gapped deploy may not even resolve). When a public base URL is\n// configured we therefore rewrite each script's default-base-URL marker line to\n// that origin before serving. The user's OPENGENI_INSTALL_BASE_URL env override\n// still wins at run time (the scripts use `:-default`); only the built-in DEFAULT\n// changes. The marker lines are kept shape-stable in agent/install/install.{sh,ps1}.\nconst DEFAULT_BASE_REWRITES: Record<string, (base: string) => { from: string; to: string }> = {\n \"install.sh\": (base) => ({\n from: 'OPENGENI_INSTALL_DEFAULT_BASE_URL=\"https://get.opengeni.ai\"',\n to: `OPENGENI_INSTALL_DEFAULT_BASE_URL=\"${base}\"`,\n }),\n \"install.ps1\": (base) => ({\n from: \"$OpengeniInstallDefaultBaseUrl = 'https://get.opengeni.ai'\",\n to: `$OpengeniInstallDefaultBaseUrl = '${base}'`,\n }),\n};\n\n// Rewrite a served script's default release-asset base URL to this deployment's\n// own public origin. A no-op when no public base URL is configured (the public\n// archive default stands), when the URL is not absolute http(s) (never serve a\n// script with a broken base), or when the file has no marker (script drift — fail\n// safe to serving it verbatim rather than crashing the install surface).\nfunction rewriteDefaultBaseUrl(file: string, body: string, publicBaseUrl: string | undefined): string {\n if (!publicBaseUrl || !/^https?:\\/\\//.test(publicBaseUrl)) {\n return body;\n }\n const base = publicBaseUrl.replace(/\\/+$/, \"\");\n const rule = DEFAULT_BASE_REWRITES[file]?.(base);\n if (!rule || !body.includes(rule.from)) {\n return body;\n }\n return body.replace(rule.from, rule.to);\n}\n\n// The content type for a baked release asset. The binary is an\n// application/octet-stream download; its `.sha256`/`.minisig` sidecars are short\n// text the install script parses line-by-line.\nfunction bakedContentType(asset: string): string {\n if (asset.endsWith(\".sha256\") || asset.endsWith(\".minisig\")) {\n return \"text/plain; charset=utf-8\";\n }\n return \"application/octet-stream\";\n}\n\n// Read a baked asset's bytes, or `null` when it is not baked into THIS image (the\n// signal to fall through to the GitHub-Releases redirect). The asset name is\n// already validated against ASSET_NAME by the caller (no traversal possible), and\n// BAKED_DIR is a fixed sibling, so the resolved path cannot escape the dir.\nasync function readBaked(asset: string): Promise<ArrayBuffer | null> {\n const url = new URL(asset, BAKED_DIR);\n try {\n const info = await stat(url);\n if (!info.isFile()) {\n return null;\n }\n } catch {\n return null;\n }\n // Return a standalone ArrayBuffer (a valid Response BodyInit). `readFile`'s\n // Buffer may be a view into a larger pooled allocation, so copy out the exact\n // byte range with `.slice` rather than handing over the backing store.\n const buf = await readFile(url);\n return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);\n}\n\n// `/agent/latest/<asset>` and `/agent/v<ver>/<asset>` (+ the `.sha256` / `.minisig`\n// siblings the install script fetches) — see agent/install/install.sh asset_url().\n// `<asset>` and the version segment are constrained so the redirect cannot be used\n// as an open redirector: only the agent asset-name shape + a `v`-prefixed version.\nconst ASSET_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\n// The install script's version path segment is the literal `v<ver>` (e.g. v1.2.3).\nconst VERSION_SEG = /^v[A-Za-z0-9][A-Za-z0-9._-]*$/;\n\nexport function registerInstallRoutes(app: Hono, deps: ApiRouteDeps): void {\n const releasesBase = deps.settings.agentReleasesBaseUrl.replace(/\\/+$/, \"\");\n\n for (const [path, { file, contentType }] of Object.entries(TEXT_ASSETS)) {\n app.get(path, async (c) => {\n // Serve the committed body, but rewrite the install scripts' default\n // asset base URL to THIS deployment's origin so the agent is pulled from\n // the same control plane it enrolls against (see rewriteDefaultBaseUrl).\n const body = rewriteDefaultBaseUrl(file, await loadAsset(file), deps.settings.publicBaseUrl);\n return c.text(body, 200, {\n \"content-type\": contentType,\n // Short cache: the edge serves the latest committed copy; new installs\n // should pick up script fixes promptly, but a brief cache absorbs bursts.\n \"cache-control\": \"public, max-age=300\",\n });\n });\n }\n\n // Serve a release-binary asset: the BAKED per-SHA file if THIS image carries it\n // (the Linux musl binary + sidecars that match the control plane exactly),\n // otherwise 302 to the GitHub Release at `redirectUrl` (mac/windows + any\n // un-baked asset). The baked path makes the agent the API enrolls against\n // identical to the API that serves it — no version skew, no extra hop.\n async function serveAsset(asset: string, redirectUrl: string): Promise<Response> {\n const baked = await readBaked(asset);\n if (baked !== null) {\n return new Response(baked, {\n status: 200,\n headers: {\n \"content-type\": bakedContentType(asset),\n // The baked artifact is immutable for this image SHA: the binary, its\n // checksum, and its signature never change once built. A long cache is\n // safe; new installs land when a new image (new SHA) rolls out.\n \"cache-control\": \"public, max-age=3600\",\n \"x-opengeni-agent-source\": \"baked\",\n },\n });\n }\n // Not baked here → the GitHub Release is the source of truth (the public\n // archive + the documented install.sh fallback). 302 so the client refetches.\n return new Response(null, { status: 302, headers: { location: redirectUrl } });\n }\n\n // `latest` → the BAKED binary if present, else the dedicated moving\n // `agent-latest` GitHub Release. We deliberately do NOT use GitHub's\n // repo-global `releases/latest` alias here: in this monorepo that alias is\n // perpetually shadowed by the frequent changesets package releases (e.g.\n // `@opengeni/contracts@x.y.z`), which carry no agent binaries → 404. The\n // `agent-latest` release is maintained by .github/workflows/agent-release.yml\n // as a moving tag that always points at the newest signed mac/windows\n // binaries (+ checksums, install scripts, minisign pubkey).\n app.get(\"/agent/latest/:asset\", async (c) => {\n const asset = c.req.param(\"asset\");\n if (!ASSET_NAME.test(asset)) {\n throw new HTTPException(400, { message: \"invalid asset name\" });\n }\n return serveAsset(asset, `${releasesBase}/download/agent-latest/${asset}`);\n });\n\n // The version segment is the literal `v<ver>` (e.g. `v1.2.3`) — Hono cannot bind\n // a param glued to a literal prefix, so the whole segment is the param and the\n // `v` prefix is validated/stripped here. The release tag is `agent-v<ver>`.\n app.get(\"/agent/:versionSeg/:asset\", async (c) => {\n const versionSeg = c.req.param(\"versionSeg\");\n const asset = c.req.param(\"asset\");\n // `/agent/latest/<asset>` is handled by the more specific route above; any\n // other version segment must be the `v<ver>` shape.\n if (!VERSION_SEG.test(versionSeg) || !ASSET_NAME.test(asset)) {\n throw new HTTPException(400, { message: \"invalid version or asset name\" });\n }\n return serveAsset(asset, `${releasesBase}/download/agent-${versionSeg}/${asset}`);\n });\n}\n\n// The path prefixes/exact paths the install routes own — exported so the auth\n// middleware can exempt them (they must be reachable with no credentials).\nexport const installExactPaths: ReadonlySet<string> = new Set(Object.keys(TEXT_ASSETS));\n\nexport function isInstallRedirectPath(path: string): boolean {\n return path.startsWith(\"/agent/latest/\") || path.startsWith(\"/agent/v\");\n}\n","import type { Settings } from \"@opengeni/config\";\nimport type { Context, MiddlewareHandler } from \"hono\";\nimport { installExactPaths, isInstallRedirectPath } from \"../routes/install\";\n\nconst githubConnectPathPattern = /^\\/v1\\/workspaces\\/[^/]+\\/github\\/connect$/;\n\nexport function requireAccessKey(settings: Settings): MiddlewareHandler {\n return async (c, next) => {\n // §7.2 P1: requireAccessKey is the coarse NETWORK perimeter, not the\n // per-tenant identity gate (that is resolveAccessContext). When\n // `authRequired:false` it is a NO-OP — the embedded (Path 2) case where the\n // host's own auth is the sole human gate and OpenGeni is mounted behind it.\n // Standalone/separate deployments set `authRequired:true` to keep this ON as\n // the shared-deployment-key perimeter.\n if (!settings.authRequired || isAuthExempt(c, settings)) {\n await next();\n return;\n }\n if (isAuthorized(c, settings.accessKey)) {\n await next();\n return;\n }\n return c.json({ error: \"unauthorized\" }, 401);\n };\n}\n\nfunction isAuthExempt(c: Context, settings: Settings): boolean {\n if (c.req.method === \"OPTIONS\") {\n return true;\n }\n const path = new URL(c.req.url).pathname;\n if (path === \"/v1/config/client\") {\n return true;\n }\n if (path === \"/v1/auth\" || path.startsWith(\"/v1/auth/\")) {\n return true;\n }\n if (path === \"/v1/webhooks/stripe\") {\n return true;\n }\n if (\n path === \"/v1/github/setup\" ||\n path === \"/v1/github/install/callback\" ||\n path === \"/v1/github/oauth/callback\" ||\n path === \"/v1/github/app-manifest/callback\"\n ) {\n return true;\n }\n // Browser entry for MCP-issued GitHub install links: opened in a browser\n // that holds no API credentials, like the callbacks above. The route itself\n // verifies the signed workspace-bound state before doing anything.\n if (githubConnectPathPattern.test(path)) {\n return true;\n }\n // The get.<domain> install-serving routes (install.sh/.ps1/uninstall.sh/\n // minisign pub + the release-binary redirects). Reached by a fresh machine\n // with no credentials; the bodies carry no secrets (dossier §23.1).\n if (installExactPaths.has(path) || isInstallRedirectPath(path)) {\n return true;\n }\n if (settings.authAllowHealth && path === \"/healthz\") {\n return true;\n }\n if (settings.authAllowMetrics && path === \"/metrics\") {\n return true;\n }\n return false;\n}\n\nfunction isAuthorized(c: Context, expected: string | undefined): boolean {\n if (!expected) {\n return false;\n }\n const explicit = c.req.header(\"x-opengeni-access-key\");\n return constantTimeEqual(explicit, expected);\n}\n\nfunction constantTimeEqual(actual: string | undefined, expected: string): boolean {\n if (typeof actual !== \"string\") {\n return false;\n }\n const actualBytes = new TextEncoder().encode(actual);\n const expectedBytes = new TextEncoder().encode(expected);\n if (actualBytes.length !== expectedBytes.length) {\n return false;\n }\n let diff = 0;\n for (let index = 0; index < actualBytes.length; index += 1) {\n diff |= actualBytes[index]! ^ expectedBytes[index]!;\n }\n return diff === 0;\n}\n","import {\n CapabilityCatalogResponse,\n CreateCapabilityCatalogItemRequest,\n DiscoverMcpCapabilitiesResponse,\n EnableCapabilityRequest,\n} from \"@opengeni/contracts\";\nimport type { Hono } from \"hono\";\nimport { requireAccessGrant } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\nimport {\n buildCapabilityCatalog,\n createCatalogItem,\n disableCapability,\n discoverMcpRegistryCapabilities,\n enableCapability,\n officialMcpRegistryUrl,\n} from \"@opengeni/core\";\nimport { boundedLimit } from \"../http/common\";\n\nexport function registerCapabilityRoutes(app: Hono, deps: ApiRouteDeps): void {\n const { db, settings } = deps;\n\n app.get(\"/v1/workspaces/:workspaceId/capabilities\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n return c.json(CapabilityCatalogResponse.parse(await buildCapabilityCatalog({ db, workspaceId, settings })));\n });\n\n app.post(\"/v1/workspaces/:workspaceId/capabilities\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const payload = CreateCapabilityCatalogItemRequest.parse(await c.req.json());\n return c.json(await createCatalogItem({ db, accountId: grant.accountId, workspaceId, payload }), 201);\n });\n\n app.get(\"/v1/workspaces/:workspaceId/capabilities/discovery/mcp-registry\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n const query = c.req.query(\"query\");\n const options: { query?: string; limit?: number } = { limit: boundedLimit(c.req.query(\"limit\")) };\n if (query) {\n options.query = query;\n }\n const items = await discoverMcpRegistryCapabilities(options);\n return c.json(DiscoverMcpCapabilitiesResponse.parse({\n items,\n source: \"official_mcp_registry\",\n sourceUrl: officialMcpRegistryUrl,\n }));\n });\n\n app.post(\"/v1/workspaces/:workspaceId/capabilities/:capabilityId/enable\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const payload = EnableCapabilityRequest.parse(await c.req.json());\n const installation = await enableCapability({\n db,\n grant,\n accountId: grant.accountId,\n workspaceId,\n settings,\n capabilityId: decodeURIComponent(c.req.param(\"capabilityId\")),\n payload,\n });\n return c.json(installation, 201);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/capabilities/:capabilityId/disable\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const installation = await disableCapability({\n db,\n accountId: grant.accountId,\n workspaceId,\n settings,\n capabilityId: decodeURIComponent(c.req.param(\"capabilityId\")),\n });\n return c.json(installation);\n });\n}\n","import { getSession, type Database } from \"@opengeni/db\";\nimport { HTTPException } from \"hono/http-exception\";\n\nexport function boundedLimit(raw: string | undefined): number {\n const limit = Number(raw ?? 100);\n if (!Number.isFinite(limit)) {\n return 100;\n }\n return Math.min(500, Math.max(1, Math.floor(limit)));\n}\n\nexport async function assertSessionExists(db: Database, workspaceId: string, sessionId: string): Promise<void> {\n if (!await getSession(db, workspaceId, sessionId)) {\n throw new HTTPException(404, { message: \"session not found\" });\n }\n}\n","// Codex (ChatGPT) subscription connect / status / usage routes.\n//\n// Connect uses the device-code flow split into two stateless calls: `start`\n// returns a user code + verification URL and an HMAC-signed state carrying the\n// device_auth_id; the client opens the URL, authorizes, then drives `poll` on the\n// returned interval. No browser redirect and no 15-minute server block, so nothing\n// is added to isAuthExempt. Secrets never leave the server: status/usage read the\n// decrypted token only to call the codex backend; the token is never returned.\n\nimport { environmentsEncryptionKeyBytes } from \"@opengeni/config\";\nimport {\n accessTokenExpiry,\n buildCodexUsageWindowFromCache,\n CODEX_CLIENT_VERSION,\n CODEX_FALLBACK_MODEL_SLUGS,\n CODEX_FIVE_HOUR_WINDOW_SECONDS,\n CODEX_MODEL_ID_PREFIX,\n CODEX_PROVIDER_ID,\n CODEX_WEEKLY_WINDOW_SECONDS,\n CodexDeviceError,\n exchangeDeviceCode,\n fetchCodexModels,\n parseIdToken,\n pollDeviceCode,\n startDeviceCode,\n type CodexUsagePayload,\n} from \"@opengeni/codex\";\nimport {\n disconnectAllCodexAccounts,\n disconnectCodexAccount,\n encryptEnvironmentValue,\n ensureCodexRotationSettings,\n fetchCodexUsageForAccount,\n getCodexCredentialStatus,\n getCodexRotationSettings,\n listCodexAccountStatuses,\n loadCodexCredentialForRun,\n renameCodexAccount,\n setActiveCodexCredential,\n updateCodexRotationSettings,\n upsertCodexSubscriptionCredential,\n CODEX_ROTATION_STRATEGIES,\n type CodexAccountStatus,\n type CodexRotationStrategy,\n} from \"@opengeni/db\";\n\n// The picker surfaces codex models under their own \"no credits\" provider group so\n// they read distinctly from the platform provider's same-named model.\nconst CODEX_PROVIDER_LABEL = \"Codex subscription · no credits\";\n\n// The wire shape for one Codex account (metadata only; never the secret column).\n// P2: fiveHour/weekly ride along, built from the CACHED usage columns (zero\n// provider calls, zero decrypts) so the bars render instantly off this read.\nfunction codexAccountJson(row: CodexAccountStatus) {\n return {\n id: row.id,\n chatgptAccountId: row.chatgptAccountId,\n label: row.label,\n email: row.accountEmail,\n plan: row.planType,\n status: row.status,\n active: row.isActive,\n expiresAt: row.expiresAt,\n lastRefreshAt: row.lastRefreshAt,\n lastError: row.lastError,\n fiveHour: buildCodexUsageWindowFromCache(row.primaryUsedPercent, row.primaryResetAt, CODEX_FIVE_HOUR_WINDOW_SECONDS),\n weekly: buildCodexUsageWindowFromCache(row.secondaryUsedPercent, row.secondaryResetAt, CODEX_WEEKLY_WINDOW_SECONDS),\n usageCheckedAt: row.usageCheckedAt,\n // P3 rotation cooldown: when set and in the future, this account is cooling-down.\n exhaustedUntil: row.exhaustedUntil,\n };\n}\n\n// The /codex/usage{,/refresh,/:id} wire wrapper: the rich normalized payload\n// carries its own `status`, surfaced at the top level for back-compat with the\n// existing CodexUsage = { status; usage } shape.\nfunction codexUsageJson(payload: CodexUsagePayload): { status: CodexUsagePayload[\"status\"]; usage: CodexUsagePayload } {\n return { status: payload.status, usage: payload };\n}\n\nfunction codexModelsForPicker(slugs: string[]): Array<{ id: string; label: string; provider: string; providerLabel: string; api: \"responses\" }> {\n return slugs\n .filter((slug) => (/^gpt-5/.test(slug) || slug.includes(\"codex\")) && slug !== \"codex-auto-review\")\n .map((slug) => ({\n id: `${CODEX_MODEL_ID_PREFIX}${slug}`,\n label: slug.replace(/^gpt-/, \"GPT-\"),\n provider: CODEX_PROVIDER_ID,\n providerLabel: CODEX_PROVIDER_LABEL,\n api: \"responses\" as const,\n }));\n}\nimport { createSignedState, readSignedState } from \"@opengeni/github\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\nimport type { Hono } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport { requireAccessGrant } from \"@opengeni/core\";\n\ntype CodexConnectState = { workspaceId?: string; deviceAuthId?: string; userCode?: string; iat?: number };\n\nconst CODEX_DEVICE_EXPIRY_SECONDS = 15 * 60; // the device code expires 15 min after start (spec §1.1)\n\nexport function registerCodexRoutes(app: Hono, deps: ApiRouteDeps): void {\n const { db, settings, githubStateSecret } = deps;\n\n // Begin device-code login: returns the user code + verification URL and a\n // signed state that carries the device_auth_id back to `poll`.\n app.post(\"/v1/workspaces/:workspaceId/codex/connect/start\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n let start: Awaited<ReturnType<typeof startDeviceCode>>;\n try {\n start = await startDeviceCode();\n } catch (error) {\n throw new HTTPException(502, { message: error instanceof CodexDeviceError ? error.message : \"failed to start Codex device login\" });\n }\n const state = createSignedState(githubStateSecret, { workspaceId, deviceAuthId: start.deviceAuthId, userCode: start.userCode });\n return c.json({ userCode: start.userCode, verificationUri: start.verificationUri, intervalSeconds: start.intervalSeconds, state });\n });\n\n // Poll for authorization: pending | expired | connected (persists on success).\n app.post(\"/v1/workspaces/:workspaceId/codex/connect/poll\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const { state } = (await c.req.json()) as { state?: string };\n const payload = (state ? readSignedState(state, githubStateSecret) : null) as unknown as CodexConnectState | null;\n if (!payload || payload.workspaceId !== workspaceId || !payload.deviceAuthId || !payload.userCode) {\n throw new HTTPException(400, { message: \"codex connect state is invalid or expired\" });\n }\n // The device code itself expires 15 minutes after start; surface that to the\n // client (the 1-hour signed-state TTL is longer than the device window).\n if (typeof payload.iat === \"number\" && Date.now() / 1000 - payload.iat > CODEX_DEVICE_EXPIRY_SECONDS) {\n return c.json({ status: \"expired\" });\n }\n\n let poll: Awaited<ReturnType<typeof pollDeviceCode>>;\n try {\n poll = await pollDeviceCode({ deviceAuthId: payload.deviceAuthId, userCode: payload.userCode });\n } catch (error) {\n throw new HTTPException(502, { message: error instanceof CodexDeviceError ? error.message : \"codex device poll failed\" });\n }\n if (poll.status === \"pending\") {\n return c.json({ status: \"pending\" });\n }\n if (poll.status === \"expired\") {\n return c.json({ status: \"expired\" });\n }\n\n let tokens: Awaited<ReturnType<typeof exchangeDeviceCode>>;\n try {\n tokens = await exchangeDeviceCode({ authorizationCode: poll.authorizationCode, codeVerifier: poll.codeVerifier });\n } catch (error) {\n throw new HTTPException(502, { message: error instanceof CodexDeviceError ? error.message : \"codex token exchange failed\" });\n }\n const id = parseIdToken(tokens.idToken);\n const key = environmentsEncryptionKeyBytes(settings);\n if (!key) {\n throw new HTTPException(500, { message: \"OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is not configured\" });\n }\n const upserted = await upsertCodexSubscriptionCredential(db, {\n accountId: grant.accountId,\n workspaceId,\n credentialEncrypted: encryptEnvironmentValue(\n key,\n JSON.stringify({ access_token: tokens.accessToken, refresh_token: tokens.refreshToken, id_token: tokens.idToken }),\n ),\n chatgptAccountId: id.chatgptAccountId,\n scopes: null, // device grant scopes are discovered at runtime, not asserted here\n planType: id.planType,\n isFedramp: id.isFedramp,\n expiresAt: accessTokenExpiry(tokens.accessToken),\n lastRefreshAt: new Date(),\n accountEmail: id.email ?? null,\n label: id.email ?? id.chatgptAccountId ?? null,\n });\n // Ensure the per-workspace rotation-settings row exists, then auto-activate\n // the FIRST account only. Additional new accounts do NOT auto-activate — a\n // manual switch is required (no auto-rotation in P1). A re-connect of the\n // already-active account is a no-op for the pointer.\n await ensureCodexRotationSettings(db, grant.accountId, workspaceId);\n const rotation = await getCodexRotationSettings(db, workspaceId);\n let isActive = rotation?.activeCredentialId === upserted.id;\n if (!isActive && rotation?.activeCredentialId == null) {\n await setActiveCodexCredential(db, workspaceId, upserted.id);\n isActive = true;\n }\n return c.json({ status: \"connected\", plan: id.planType, accountId: upserted.id, isActive });\n });\n\n // Connection health: the cheapest real call is GET /codex/models (a 200 proves\n // the token is accepted). Never runs a generation. Never returns the token.\n app.get(\"/v1/workspaces/:workspaceId/codex/status\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n const status = await getCodexCredentialStatus(db, workspaceId);\n if (!status) {\n return c.json({ connected: false });\n }\n const accounts = await listCodexAccountStatuses(db, workspaceId);\n const activeRow = accounts.find((account) => account.id === status.credentialId) ?? null;\n const activeAccount = activeRow\n ? { id: activeRow.id, label: activeRow.label ?? activeRow.accountEmail ?? activeRow.planType ?? activeRow.chatgptAccountId, chatgptAccountId: activeRow.chatgptAccountId }\n : null;\n let valid = false;\n let models = codexModelsForPicker([...CODEX_FALLBACK_MODEL_SLUGS]); // offline fallback list\n try {\n const cred = status.credentialId ? await loadCodexCredentialForRun(db, settings, workspaceId, status.credentialId) : null;\n if (cred) {\n const live = await fetchCodexModels({\n accessToken: cred.tokens.accessToken,\n chatgptAccountId: cred.chatgptAccountId,\n isFedramp: cred.isFedramp,\n clientVersion: CODEX_CLIENT_VERSION,\n });\n valid = live.ok;\n if (live.ok && live.slugs.length > 0) {\n models = codexModelsForPicker(live.slugs); // prefer the live catalog\n }\n }\n } catch {\n valid = false;\n }\n return c.json({\n connected: status.connected,\n plan: status.planType,\n valid,\n expiresAt: status.expiresAt,\n lastError: status.lastError,\n models, // ClientModel[] the picker surfaces under the \"no credits\" group\n activeAccount, // the account a session runs on when unpinned (label for the indicator)\n accountCount: accounts.length,\n });\n });\n\n // List every connected Codex account (metadata only, never decrypts) + the\n // workspace active pointer + rotation settings. Read access.\n app.get(\"/v1/workspaces/:workspaceId/codex/accounts\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n const [accounts, rotation] = await Promise.all([\n listCodexAccountStatuses(db, workspaceId),\n getCodexRotationSettings(db, workspaceId),\n ]);\n const activeAccountId = rotation?.activeCredentialId ?? null;\n return c.json({\n accounts: accounts.map(codexAccountJson),\n activeAccountId,\n settings: {\n rotationEnabled: rotation?.rotationEnabled ?? false,\n rotationStrategy: rotation?.rotationStrategy ?? \"most_remaining\",\n activeCredentialId: activeAccountId,\n },\n });\n });\n\n // Manually switch the workspace ACTIVE account (the one unpinned sessions use).\n // Pure pointer flip; in-flight turns pick it up on their next token fetch.\n app.post(\"/v1/workspaces/:workspaceId/codex/accounts/:accountId/activate\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const accountId = c.req.param(\"accountId\");\n const activated = await setActiveCodexCredential(db, workspaceId, accountId);\n if (!activated) {\n throw new HTTPException(404, { message: \"codex account not found\" });\n }\n return c.json({ activated: true, accountId });\n });\n\n // P3: update rotation settings (enable auto-rotation + pick the strategy). admin access.\n // ensureCodexRotationSettings guarantees the row exists, then a one-cell patch.\n app.patch(\"/v1/workspaces/:workspaceId/codex/settings\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const body = (await c.req.json().catch(() => ({}))) as { rotationEnabled?: unknown; rotationStrategy?: unknown };\n const patch: { rotationEnabled?: boolean; rotationStrategy?: CodexRotationStrategy } = {};\n if (typeof body.rotationEnabled === \"boolean\") {\n patch.rotationEnabled = body.rotationEnabled;\n }\n if (typeof body.rotationStrategy === \"string\") {\n if (!CODEX_ROTATION_STRATEGIES.includes(body.rotationStrategy as CodexRotationStrategy)) {\n throw new HTTPException(400, { message: \"invalid rotation strategy\" });\n }\n patch.rotationStrategy = body.rotationStrategy as CodexRotationStrategy;\n }\n if (patch.rotationEnabled === undefined && patch.rotationStrategy === undefined) {\n throw new HTTPException(400, { message: \"no settings to update\" });\n }\n await ensureCodexRotationSettings(db, grant.accountId, workspaceId);\n const updated = await updateCodexRotationSettings(db, workspaceId, patch);\n if (!updated) {\n throw new HTTPException(404, { message: \"codex rotation settings not found\" });\n }\n return c.json({\n rotationEnabled: updated.rotationEnabled,\n rotationStrategy: updated.rotationStrategy,\n activeCredentialId: updated.activeCredentialId,\n });\n });\n\n // Rename an account (label only in P1).\n app.patch(\"/v1/workspaces/:workspaceId/codex/accounts/:accountId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const accountId = c.req.param(\"accountId\");\n const body = (await c.req.json()) as { label?: string | null };\n const label = typeof body.label === \"string\" ? body.label : null;\n const renamed = await renameCodexAccount(db, workspaceId, accountId, label);\n if (!renamed) {\n throw new HTTPException(404, { message: \"codex account not found\" });\n }\n const accounts = await listCodexAccountStatuses(db, workspaceId);\n const row = accounts.find((account) => account.id === accountId);\n if (!row) {\n throw new HTTPException(404, { message: \"codex account not found\" });\n }\n return c.json(codexAccountJson(row));\n });\n\n // Disconnect ONE account by id. The accessor re-picks active when the removed\n // row was active (FK ON DELETE SET NULL + re-pick in the same RLS txn).\n app.delete(\"/v1/workspaces/:workspaceId/codex/accounts/:accountId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const accountId = c.req.param(\"accountId\");\n const result = await disconnectCodexAccount(db, workspaceId, accountId);\n return c.json({ disconnected: result.removed, newActiveId: result.newActiveCredentialId });\n });\n\n // Legacy \"disconnect all\" (old workspace-wide behavior), deprecated in favor of\n // the by-id route above.\n app.delete(\"/v1/workspaces/:workspaceId/codex\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const removed = await disconnectAllCodexAccounts(db, workspaceId);\n return c.json({ disconnected: removed > 0 });\n });\n\n // Back-compat: remaining usage / limits for the ACTIVE account only. Repointed\n // through the refreshing wrapper (P2) so it no longer 401s on an idle account's\n // stale access token. Deprecated in favor of the /accounts + /usage/refresh pair.\n app.get(\"/v1/workspaces/:workspaceId/codex/usage\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n const status = await getCodexCredentialStatus(db, workspaceId);\n if (!status?.credentialId) {\n throw new HTTPException(404, { message: \"codex subscription is not connected\" });\n }\n const payload = await fetchCodexUsageForAccount(db, settings, workspaceId, status.credentialId);\n return c.json(codexUsageJson(payload));\n });\n\n // Single-account LIVE usage read (per-row manual refresh): refresh THIS account's\n // bearer, hit /wham/usage, write the cache columns, return the normalized payload.\n app.get(\"/v1/workspaces/:workspaceId/codex/accounts/:accountId/usage\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n const accountId = c.req.param(\"accountId\");\n // Constrain to a real account in this workspace (RLS already scopes, but a 404\n // for an unknown id is friendlier than an opaque needs_relogin payload).\n const accounts = await listCodexAccountStatuses(db, workspaceId);\n if (!accounts.some((account) => account.id === accountId)) {\n throw new HTTPException(404, { message: \"codex account not found\" });\n }\n const payload = await fetchCodexUsageForAccount(db, settings, workspaceId, accountId);\n return c.json(codexUsageJson(payload));\n });\n\n // Batched LIVE refresh across every connected account, keyed by credential id.\n // A small concurrency cap + Promise.allSettled so one account's 401/error/timeout\n // can't sink the batch; each entry is independently statused. Writes the cache\n // columns as a side effect. This is what the \"Refresh\" button and an on-mount\n // staleness check call — NEVER a browser interval.\n app.post(\"/v1/workspaces/:workspaceId/codex/usage/refresh\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n const accounts = await listCodexAccountStatuses(db, workspaceId);\n const usage: Record<string, { status: CodexUsagePayload[\"status\"]; usage: CodexUsagePayload }> = {};\n const queue = [...accounts];\n const CONCURRENCY = 4;\n const worker = async (): Promise<void> => {\n for (;;) {\n const account = queue.shift();\n if (!account) return;\n const settled = await Promise.allSettled([fetchCodexUsageForAccount(db, settings, workspaceId, account.id)]);\n const result = settled[0];\n usage[account.id] = result.status === \"fulfilled\"\n ? codexUsageJson(result.value)\n : { status: \"error\", usage: { status: \"error\", planType: null, fiveHour: null, weekly: null, limitReached: false, fetchedAt: new Date().toISOString() } };\n }\n };\n await Promise.all(Array.from({ length: Math.min(CONCURRENCY, Math.max(1, accounts.length)) }, () => worker()));\n return c.json({ usage });\n });\n}\n","import {\n AddDocumentRequest,\n CreateDocumentBaseRequest,\n Document,\n DocumentBase,\n DocumentSearchRequest,\n} from \"@opengeni/contracts\";\nimport {\n addDocumentToBase,\n createDocumentBase,\n deleteDocumentFromBase,\n getDocument,\n getDocumentBase,\n listDocumentBases,\n listDocuments,\n queueDocumentForReindex,\n searchDocuments,\n} from \"@opengeni/documents\";\nimport { WebStandardStreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\nimport type { Hono } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport { requireAccessGrant } from \"@opengeni/core\";\nimport { recordWorkspaceUsage, requireLimit } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\nimport { buildDocumentsMcpServer } from \"../mcp/documents\";\n\nexport function registerDocumentRoutes(app: Hono, deps: ApiRouteDeps): void {\n const { db, objectStorage, documentIndexer, getDocumentServices } = deps;\n\n app.post(\"/v1/workspaces/:workspaceId/document-bases\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"documents:manage\");\n const payload = CreateDocumentBaseRequest.parse(await c.req.json());\n return c.json(DocumentBase.parse(await createDocumentBase(db, { ...payload, accountId: grant.accountId, workspaceId })), 201);\n });\n\n app.get(\"/v1/workspaces/:workspaceId/document-bases\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"documents:search\");\n return c.json((await listDocumentBases(db, workspaceId)).map((base) => DocumentBase.parse(base)));\n });\n\n app.get(\"/v1/workspaces/:workspaceId/document-bases/:baseId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"documents:search\");\n const base = await getDocumentBase(db, workspaceId, c.req.param(\"baseId\"));\n if (!base) {\n throw new HTTPException(404, { message: \"document base not found\" });\n }\n return c.json(DocumentBase.parse(base));\n });\n\n app.post(\"/v1/workspaces/:workspaceId/document-bases/:baseId/documents\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"documents:manage\");\n if (!objectStorage) {\n throw new HTTPException(503, { message: \"object storage is not configured\" });\n }\n await requireLimit(deps, { accountId: grant.accountId, workspaceId, action: \"document:index\", quantity: 0 });\n const payload = AddDocumentRequest.parse(await c.req.json());\n try {\n const document = await addDocumentToBase(db, { accountId: grant.accountId, workspaceId, baseId: c.req.param(\"baseId\"), fileId: payload.fileId });\n const wasCreated = document.status === \"queued\" && document.chunkCount === 0 && document.error === null;\n const indexed = document.status === \"ready\" ? document : (await documentIndexer.indexDocument({ accountId: grant.accountId, workspaceId, documentId: document.id }) ?? document);\n if (indexed.status === \"ready\") {\n await recordWorkspaceUsage(deps, {\n accountId: grant.accountId,\n workspaceId,\n subjectId: grant.subjectId,\n eventType: \"document.indexed\",\n quantity: indexed.chunkCount,\n unit: \"chunk\",\n sourceResourceType: \"document\",\n sourceResourceId: indexed.id,\n idempotencyKey: `document.indexed:${workspaceId}:${indexed.id}:${indexed.updatedAt}`,\n });\n }\n return c.json(Document.parse(indexed), wasCreated ? 201 : 200);\n } catch (error) {\n throw documentHttpException(error);\n }\n });\n\n app.get(\"/v1/workspaces/:workspaceId/document-bases/:baseId/documents\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"documents:search\");\n return c.json((await listDocuments(db, workspaceId, c.req.param(\"baseId\"))).map((document) => Document.parse(document)));\n });\n\n app.delete(\"/v1/workspaces/:workspaceId/document-bases/:baseId/documents/:documentId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"documents:manage\");\n try {\n await deleteDocumentFromBase(db, {\n accountId: grant.accountId,\n workspaceId,\n baseId: c.req.param(\"baseId\"),\n documentId: c.req.param(\"documentId\"),\n });\n return c.body(null, 204);\n } catch (error) {\n throw documentHttpException(error);\n }\n });\n\n app.post(\"/v1/workspaces/:workspaceId/document-bases/:baseId/documents/:documentId/reindex\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"documents:manage\");\n if (!objectStorage) {\n throw new HTTPException(503, { message: \"object storage is not configured\" });\n }\n await requireLimit(deps, { accountId: grant.accountId, workspaceId, action: \"document:index\", quantity: 0 });\n try {\n const document = await getDocument(db, workspaceId, c.req.param(\"documentId\"));\n if (!document) {\n throw new HTTPException(404, { message: \"document not found\" });\n }\n if (document.status !== \"failed\") {\n throw new HTTPException(422, { message: \"only failed documents can be retried\" });\n }\n if (document.baseId !== c.req.param(\"baseId\")) {\n throw new HTTPException(404, { message: \"document not found\" });\n }\n const queued = await queueDocumentForReindex(db, workspaceId, document.id);\n const indexed = await documentIndexer.indexDocument({ accountId: grant.accountId, workspaceId, documentId: document.id }) ?? queued;\n if (indexed.status === \"ready\") {\n await recordWorkspaceUsage(deps, {\n accountId: grant.accountId,\n workspaceId,\n subjectId: grant.subjectId,\n eventType: \"document.indexed\",\n quantity: indexed.chunkCount,\n unit: \"chunk\",\n sourceResourceType: \"document\",\n sourceResourceId: indexed.id,\n idempotencyKey: `document.indexed:${workspaceId}:${indexed.id}:${indexed.updatedAt}`,\n });\n }\n return c.json(Document.parse(indexed));\n } catch (error) {\n if (error instanceof HTTPException) {\n throw error;\n }\n throw documentHttpException(error);\n }\n });\n\n app.post(\"/v1/workspaces/:workspaceId/document-bases/:baseId/search\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"documents:search\");\n const payload = DocumentSearchRequest.parse(await c.req.json());\n const base = await getDocumentBase(db, workspaceId, c.req.param(\"baseId\"));\n if (!base) {\n throw new HTTPException(404, { message: \"document base not found\" });\n }\n return c.json({\n results: await searchDocuments(db, {\n workspaceId,\n baseIds: [base.id],\n query: payload.query,\n limit: payload.limit,\n }, getDocumentServices()),\n });\n });\n\n app.all(\"/v1/workspaces/:workspaceId/mcp/docs\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"documents:search\");\n const transport = new WebStandardStreamableHTTPServerTransport({ enableJsonResponse: true });\n const server = buildDocumentsMcpServer(db, workspaceId, getDocumentServices());\n await server.connect(transport);\n return await transport.handleRequest(c.req.raw);\n });\n}\n\nfunction documentHttpException(error: unknown): HTTPException {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"not found\")) {\n return new HTTPException(404, { message });\n }\n if (message.includes(\"pending\") || message.includes(\"failed\") || message.includes(\"deleted\")) {\n return new HTTPException(422, { message });\n }\n return new HTTPException(500, { message });\n}\n","import {\n getDocumentChunk,\n listDocumentBases,\n searchDocuments,\n type DocumentServices,\n} from \"@opengeni/documents\";\nimport type { Database } from \"@opengeni/db\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport * as z from \"zod/v4\";\n\nexport function buildDocumentsMcpServer(db: Database, workspaceId: string, documentServices: DocumentServices): McpServer {\n const server = new McpServer({\n name: \"opengeni-documents\",\n version: \"1.0.0\",\n });\n\n server.registerTool(\"list_document_bases\", {\n description: \"List document bases available for retrieval.\",\n inputSchema: {},\n }, async () => ({\n content: [{ type: \"text\", text: JSON.stringify(await listDocumentBases(db, workspaceId)) }],\n }));\n\n server.registerTool(\"search_documents\", {\n description: \"Search indexed documents.\",\n inputSchema: {\n query: z.string(),\n baseIds: z.array(z.string()).optional(),\n limit: z.number().optional(),\n },\n }, async ({ query, baseIds, limit }) => ({\n content: [{\n type: \"text\",\n text: JSON.stringify(await searchDocuments(db, {\n workspaceId,\n query,\n ...(baseIds ? { baseIds } : {}),\n ...(limit ? { limit } : {}),\n }, documentServices)),\n }],\n }));\n\n server.registerTool(\"fetch_document_chunk\", {\n description: \"Fetch one indexed document chunk by id.\",\n inputSchema: {\n chunkId: z.string(),\n },\n }, async ({ chunkId }) => {\n const found = await getDocumentChunk(db, workspaceId, chunkId);\n return {\n content: [{ type: \"text\", text: found ? JSON.stringify(found) : `chunk not found: ${chunkId}` }],\n isError: !found,\n };\n });\n\n return server;\n}\n","// apps/api/src/routes/enrollments.ts — the bring-your-own-compute enrollment\n// device-flow routes (M5; dossier §10.2 + §18). Mirrors the other sandbox route\n// modules (registerSessionRoutes / registerApiKeyRoutes): a thin route over a\n// focused service (../sandbox/enrollment.ts), requireAccessGrant BEFORE any Zod\n// parse on the USER-authenticated routes, explicit HTTPException(400) on a parse\n// failure (never a raw ZodError → 500), and the whole router gated behind\n// sandboxSelfhostedEnabled (default OFF → every route 404s, invisible).\n//\n// AUTH SEAM (the device-flow split):\n// * device/start + device/poll are AGENT-side — user-UNAUTHENTICATED (the agent\n// has no logged-in browser session; it presents only the deployment access key\n// the app.use(\"*\", requireAccessKey) edge already enforces). They are\n// IP-rate-limited here. They DO NOT call requireAccessGrant.\n// * device/approve + GET /enrollments + revoke are USER-authenticated +\n// workspace-gated via requireAccessGrant (enrollments:manage / enrollments:read;\n// workspace:admin is the super-wildcard).\n//\n// The cross-workspace safety of an unauthenticated start: start binds the request\n// to a workspaceId the agent supplies, and ONLY a user holding a grant in THAT\n// workspace can approve it (the approve's user_code lookup is workspace-scoped) — a\n// start can never grant access to a workspace no authorized user later approves in.\n\nimport {\n DeviceEnrollmentApproveRequest,\n DeviceEnrollmentApproveResponse,\n DeviceEnrollmentDenyRequest,\n DeviceEnrollmentDenyResponse,\n DeviceEnrollmentLookupRequest,\n DeviceEnrollmentLookupResponse,\n DeviceEnrollmentPollRequest,\n DeviceEnrollmentStartRequest,\n EnrollmentSummary,\n EnrollTokenExchangeRequest,\n EnrollTokenExchangeResponse,\n ListEnrollmentsResponse,\n MintEnrollTokenRequest,\n MintEnrollTokenResponse,\n RevokeEnrollmentResponse,\n type EnrollmentArch,\n type EnrollmentOs,\n} from \"@opengeni/contracts\";\nimport {\n getWorkspace,\n listEnrollments,\n revokeEnrollment,\n} from \"@opengeni/db\";\nimport type { Context, Hono } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport { requireAccessGrant } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\nimport {\n approveDeviceEnrollment,\n denyDeviceEnrollment,\n exchangeEnrollToken,\n lookupDeviceEnrollment,\n mintEnrollToken,\n pollDeviceEnrollment,\n startDeviceEnrollment,\n toLookupResponse,\n} from \"../sandbox/enrollment\";\n\nexport function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {\n const { settings, db } = deps;\n\n // The whole feature is behind sandboxSelfhostedEnabled. A 404 (not 403) keeps the\n // surface invisible while disabled — it does not exist for this deployment yet.\n function assertSelfhostedEnabled(): void {\n if (!settings.sandboxSelfhostedEnabled) {\n throw new HTTPException(404, { message: \"selfhosted enrollment is not enabled for this deployment\" });\n }\n }\n\n // A tiny in-process IP token-bucket for the UNAUTHENTICATED agent routes (start/\n // poll). The relay tier owns the heavy stream rate-limiting (dossier §10.5); this\n // is the application-tier abuse cap on the device-flow endpoints. Per-IP buckets\n // are pruned lazily. Not a distributed limiter (one replica per bucket) — that is\n // acceptable for a bounded, access-key-gated, short-TTL flow.\n const startLimiter = new TokenBucket({ capacity: 10, refillPerSecond: 0.5 });\n const pollLimiter = new TokenBucket({ capacity: 60, refillPerSecond: 2 });\n // The click-Grant approve-page lookup (authenticated, but capped against a\n // user_code brute force — the lookup resolves a workspace from a short code).\n const lookupLimiter = new TokenBucket({ capacity: 30, refillPerSecond: 1 });\n // The headless token exchange (UNAUTHENTICATED — the token is the auth). Bounded\n // against an enroll-token brute force; the `oget_` HMAC is the real boundary.\n const exchangeLimiter = new TokenBucket({ capacity: 20, refillPerSecond: 0.5 });\n\n function rateLimit(c: Context, limiter: TokenBucket): void {\n const ip = clientIp(c);\n if (!limiter.take(ip)) {\n throw new HTTPException(429, { message: \"too many requests; slow down\" });\n }\n }\n\n // ── POST /enrollments/device/start (agent-side, user-unauthenticated) ───────\n app.post(\"/v1/enrollments/device/start\", async (c) => {\n assertSelfhostedEnabled();\n rateLimit(c, startLimiter);\n const parsed = DeviceEnrollmentStartRequest.safeParse(await c.req.json().catch(() => null));\n if (!parsed.success) {\n throw new HTTPException(400, { message: \"invalid device-start request\" });\n }\n const body = parsed.data;\n // Resolve the (account) for the supplied workspace. An unknown workspace is a\n // 404 (the same shape requireAccessGrant uses for an unknown workspace).\n const workspace = await getWorkspace(db, body.workspaceId);\n if (!workspace) {\n throw new HTTPException(404, { message: \"workspace not found\" });\n }\n const result = await startDeviceEnrollment({ db, settings }, {\n accountId: workspace.accountId,\n workspaceId: workspace.id,\n publicKey: body.publicKey,\n os: body.os as EnrollmentOs,\n arch: body.arch as EnrollmentArch,\n machineName: body.machineName ?? null,\n canOfferDisplay: body.canOfferDisplay,\n requestsScreenControl: body.requestsScreenControl,\n // The approve page is served at the SAME origin as this request.\n verificationOrigin: new URL(c.req.url).origin,\n });\n return c.json(result, 201);\n });\n\n // ── POST /enrollments/device/poll (agent-side, user-unauthenticated) ────────\n app.post(\"/v1/enrollments/device/poll\", async (c) => {\n assertSelfhostedEnabled();\n rateLimit(c, pollLimiter);\n const parsed = DeviceEnrollmentPollRequest.safeParse(await c.req.json().catch(() => null));\n if (!parsed.success) {\n throw new HTTPException(400, { message: \"invalid device-poll request\" });\n }\n const result = await pollDeviceEnrollment({ db, settings }, { deviceCode: parsed.data.deviceCode });\n return c.json(result, 200);\n });\n\n // ── POST /enrollments/device/lookup (user-authed, NO workspace in path) ─────\n // The click-Grant approve page reads machine details for a user_code WITHOUT\n // consuming it. The user_code is globally unique among pending rows; we resolve\n // its workspace, then assert the caller holds enrollments:read in THAT workspace.\n // A failed grant OR no live pending row both → 404 (never reveal cross-workspace\n // existence). Rate-limited against a user_code brute force.\n app.post(\"/v1/enrollments/device/lookup\", async (c) => {\n assertSelfhostedEnabled();\n rateLimit(c, lookupLimiter);\n const parsed = DeviceEnrollmentLookupRequest.safeParse(await c.req.json().catch(() => null));\n if (!parsed.success) {\n throw new HTTPException(400, { message: \"invalid device-lookup request\" });\n }\n const record = await lookupDeviceEnrollment({ db, settings }, { userCode: parsed.data.userCode });\n if (!record) {\n // Unknown / terminal / expired code → 404 (indistinguishable from an\n // unauthorized one below, by design).\n throw new HTTPException(404, { message: \"no pending enrollment for that code\" });\n }\n // Authorize the caller against the RESOLVED workspace. A missing grant throws\n // 403/404 from requireAccessGrant; we normalize that to 404 so a caller cannot\n // distinguish \"code exists in a workspace I can't see\" from \"no such code\".\n try {\n await requireAccessGrant(c, deps, record.workspaceId, \"enrollments:read\");\n } catch {\n throw new HTTPException(404, { message: \"no pending enrollment for that code\" });\n }\n return c.json(DeviceEnrollmentLookupResponse.parse(toLookupResponse(record)), 200);\n });\n\n // ── POST /enrollments/token/exchange (UNAUTHENTICATED — the token is the auth) ─\n // The headless / fleet enroll path. The agent presents the same identity fields\n // it sends to device/start plus the `oget_` enroll token. The token IS the grant\n // (no human approve). On a valid token we perform the SAME finalize as approve and\n // return the IDENTICAL EnrollmentCredentials shape the poll authorized branch\n // returns. Rate-limited against an enroll-token brute force.\n app.post(\"/v1/enrollments/token/exchange\", async (c) => {\n assertSelfhostedEnabled();\n rateLimit(c, exchangeLimiter);\n const parsed = EnrollTokenExchangeRequest.safeParse(await c.req.json().catch(() => null));\n if (!parsed.success) {\n throw new HTTPException(400, { message: \"invalid enroll-token-exchange request\" });\n }\n const body = parsed.data;\n const result = await exchangeEnrollToken({ db, settings }, {\n token: body.token,\n publicKey: body.publicKey,\n os: body.os as EnrollmentOs,\n arch: body.arch as EnrollmentArch,\n machineName: body.machineName ?? null,\n canOfferDisplay: body.canOfferDisplay,\n });\n if (!result.ok) {\n if (result.reason === \"disabled\") {\n // The credential plane is off for this deployment (no signing secret).\n throw new HTTPException(503, { message: \"enrollment credential plane is not configured\" });\n }\n // An invalid / expired / wrong-typ token — the token is the auth, so 401.\n throw new HTTPException(401, { message: \"invalid or expired enroll token\" });\n }\n return c.json(EnrollTokenExchangeResponse.parse({ credentials: result.credentials }), 201);\n });\n\n // ── POST /workspaces/:workspaceId/enrollments/device/approve (user-authed) ──\n // The LOUD CONSENT step. requireAccessGrant BEFORE the Zod parse.\n app.post(\"/v1/workspaces/:workspaceId/enrollments/device/approve\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"enrollments:manage\");\n assertSelfhostedEnabled();\n const parsed = DeviceEnrollmentApproveRequest.safeParse(await c.req.json().catch(() => null));\n if (!parsed.success) {\n throw new HTTPException(400, { message: \"invalid device-approve request\" });\n }\n const body = parsed.data;\n const approved = await approveDeviceEnrollment({ db, settings }, {\n accountId: grant.accountId,\n workspaceId,\n userCode: body.userCode,\n allowScreenControl: body.allowScreenControl,\n // The LOUD consent record: WHO consented (the authenticated subject + label).\n approvedBySubjectId: grant.subjectId,\n approvedBySubjectLabel: grant.subjectLabel ?? null,\n });\n if (!approved) {\n // An unknown / expired / already-terminal user_code in this workspace.\n throw new HTTPException(404, { message: \"no pending enrollment for that code\" });\n }\n return c.json(DeviceEnrollmentApproveResponse.parse({\n approved: true,\n enrollmentId: approved.enrollmentId,\n sandboxId: approved.sandboxId,\n allowScreenControl: approved.allowScreenControl,\n }), 201);\n });\n\n // ── POST /workspaces/:workspaceId/enrollments/device/deny (user-authed) ─────\n // The explicit \"no\" at the approve page. Mirrors approve (enrollments:manage,\n // requireAccessGrant BEFORE the parse). Idempotent: a non-pending code → denied:false.\n app.post(\"/v1/workspaces/:workspaceId/enrollments/device/deny\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"enrollments:manage\");\n assertSelfhostedEnabled();\n const parsed = DeviceEnrollmentDenyRequest.safeParse(await c.req.json().catch(() => null));\n if (!parsed.success) {\n throw new HTTPException(400, { message: \"invalid device-deny request\" });\n }\n const result = await denyDeviceEnrollment({ db, settings }, {\n accountId: grant.accountId,\n workspaceId,\n userCode: parsed.data.userCode,\n });\n return c.json(DeviceEnrollmentDenyResponse.parse({ denied: result.denied }), 200);\n });\n\n // ── POST /workspaces/:workspaceId/enrollments/token (user-authed) ───────────\n // Mint a short-TTL headless enroll token. Mirrors approve (enrollments:manage,\n // requireAccessGrant BEFORE the parse). The accountId comes from the grant. No\n // signing secret → 503/disabled (mirror poll). The token is SECRET — never logged.\n app.post(\"/v1/workspaces/:workspaceId/enrollments/token\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"enrollments:manage\");\n assertSelfhostedEnabled();\n // All fields are optional/defaulted, so an empty POST body is valid (default\n // allowScreenControl=false). Coalesce a missing/empty body to {} before parse.\n const parsed = MintEnrollTokenRequest.safeParse(await c.req.json().catch(() => ({})));\n if (!parsed.success) {\n throw new HTTPException(400, { message: \"invalid mint-enroll-token request\" });\n }\n const minted = await mintEnrollToken({ db, settings }, {\n accountId: grant.accountId,\n workspaceId,\n allowScreenControl: parsed.data.allowScreenControl,\n });\n if (!minted) {\n // The credential plane is off (no signing secret) — mirror poll's disabled path.\n throw new HTTPException(503, { message: \"enrollment credential plane is not configured\" });\n }\n return c.json(MintEnrollTokenResponse.parse(minted), 201);\n });\n\n // ── GET /workspaces/:workspaceId/enrollments (user-authed) ──────────────────\n app.get(\"/v1/workspaces/:workspaceId/enrollments\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"enrollments:read\");\n assertSelfhostedEnabled();\n const statusFilter = c.req.query(\"status\");\n const rows = await listEnrollments(db, workspaceId, statusFilter === \"active\" ? { status: \"active\" } : {});\n return c.json(ListEnrollmentsResponse.parse({\n enrollments: rows.map((row) => EnrollmentSummary.parse({\n id: row.id,\n pubkey: row.pubkey,\n exposure: row.exposure,\n hasDisplay: row.hasDisplay,\n allowScreenControl: row.allowScreenControl,\n status: row.status,\n os: row.os,\n arch: row.arch,\n lastSeenAt: row.lastSeenAt,\n createdAt: row.createdAt,\n revokedAt: row.revokedAt,\n })),\n }));\n });\n\n // ── POST /workspaces/:workspaceId/enrollments/:id/revoke (user-authed) ──────\n app.post(\"/v1/workspaces/:workspaceId/enrollments/:enrollmentId/revoke\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"enrollments:manage\");\n assertSelfhostedEnabled();\n const result = await revokeEnrollment(db, {\n accountId: grant.accountId,\n workspaceId,\n enrollmentId: c.req.param(\"enrollmentId\"),\n });\n return c.json(RevokeEnrollmentResponse.parse(result));\n });\n}\n\n// The remote client IP for the per-IP rate-limit bucket. Honors the proxy's\n// X-Forwarded-For (the first hop) when present, falling back to a constant key when\n// neither is available (the bucket then caps the whole edge — still a useful cap).\nfunction clientIp(c: Context): string {\n const xff = c.req.header(\"x-forwarded-for\");\n if (xff) {\n const first = xff.split(\",\")[0]?.trim();\n if (first) return first;\n }\n return c.req.header(\"x-real-ip\")?.trim() || \"unknown\";\n}\n\n// A minimal per-key token bucket. capacity = burst; refillPerSecond = sustained\n// rate. Buckets are created lazily and reset their tokens by elapsed time on each\n// take, so an idle key fully refills without a background timer.\nclass TokenBucket {\n private readonly capacity: number;\n private readonly refillPerSecond: number;\n private readonly buckets = new Map<string, { tokens: number; updatedAt: number }>();\n\n constructor(options: { capacity: number; refillPerSecond: number }) {\n this.capacity = options.capacity;\n this.refillPerSecond = options.refillPerSecond;\n }\n\n take(key: string, now = Date.now()): boolean {\n const bucket = this.buckets.get(key) ?? { tokens: this.capacity, updatedAt: now };\n const elapsedSeconds = Math.max(0, (now - bucket.updatedAt) / 1000);\n bucket.tokens = Math.min(this.capacity, bucket.tokens + elapsedSeconds * this.refillPerSecond);\n bucket.updatedAt = now;\n // Prune the map opportunistically so it never grows unbounded: a fully-refilled\n // bucket carries no state worth keeping.\n if (bucket.tokens >= this.capacity && this.buckets.size > 10_000) {\n this.buckets.delete(key);\n }\n if (bucket.tokens < 1) {\n this.buckets.set(key, bucket);\n return false;\n }\n bucket.tokens -= 1;\n this.buckets.set(key, bucket);\n return true;\n }\n}\n","// apps/api/src/sandbox/enrollment.ts — the API-DIRECT enrollment device-flow seam\n// (M5 of the bring-your-own-compute mega-PR; dossier §10.2 enrollment + §18 LOUD\n// consent). This is the service layer the routes (routes/enrollments.ts) call — it\n// mirrors the channel-a.ts / viewer.ts split (a thin route over a focused service).\n//\n// THE FLOW (OAuth 2.0 device-authorization, RFC 8628):\n// 1. start (agent-side, user-unauthenticated, rate-limited): the agent presents\n// its ed25519 pubkey + os/arch + requested whole-machine exposure +\n// can-offer-display + requests-screen-control. We mint an unguessable\n// device_code (the poll key) + a short user_code (the user types) and persist a\n// short-TTL, SINGLE-USE pending row. Returns DeviceAuthStart.\n// 2. approve (USER-authenticated, workspace-gated): the LOUD CONSENT step. We\n// record WHO consented WHEN to WHAT (whole-machine mandatory + screen-control\n// per allow_screen_control) and, in one txn, createEnrollment + createSandbox\n// (an enrollments row AND a sandboxes row appear — acceptance #2). Idempotent\n// via the M2 upsert.\n// 3. poll (agent-side, with device_code): pending → {pending}; approved → the\n// EnrollmentCredentials (agent_id + workspace + a SIGNED bearer the agent\n// presents to the control plane + the subject prefix agent.<ws>.<id> + a\n// placeholder for the per-workspace NATS Account creds [infra-deferred]);\n// denied/expired/disabled → the typed state.\n//\n// SECURITY (dossier §18): device_code/user_code are CSPRNG-unguessable + short-TTL +\n// single-use; approve is strictly workspace-gated (the route asserts the grant); the\n// signing secret value is NEVER logged. Rate-limiting of start/poll is enforced at\n// the route. The consent record (who/when/what) lives on the request row.\n//\n// FLAG GATE: the whole feature is behind sandboxSelfhostedEnabled (default OFF) —\n// when off the routes 404 (the surface is invisible) and boot is unaffected.\n\nimport { randomBytes } from \"node:crypto\";\nimport {\n resolveEnrollmentSigningSecret,\n resolveRelayTokenSecret,\n type Settings,\n} from \"@opengeni/config\";\nimport {\n DeviceEnrollmentState,\n signEnrollmentBearer,\n signEnrollToken,\n signRelayToken,\n verifyEnrollToken,\n type DeviceEnrollmentLookupResponse,\n type DeviceEnrollmentPollResponse,\n type DeviceEnrollmentStartResponse,\n type EnrollmentCredentialsResponse,\n type EnrollTokenExchangeResponse,\n type MintEnrollTokenResponse,\n} from \"@opengeni/contracts\";\nimport {\n approveDeviceEnrollmentRequest,\n consumeDeviceEnrollmentRequest,\n createDeviceEnrollmentRequest,\n denyDeviceEnrollmentRequest,\n finalizeEnrollmentByToken,\n getDeviceEnrollmentRequestByDeviceCode,\n getEnrollment,\n getPendingDeviceEnrollmentRequestByUserCode,\n getPendingDeviceEnrollmentRequestByUserCodeGlobal,\n type Database,\n type DeviceEnrollmentRequestRecord,\n type EnrollmentOs,\n} from \"@opengeni/db\";\nimport { relayDialBaseFromSettings } from \"@opengeni/core\";\n\n// The device-flow timing knobs (RFC 8628). Short TTL + a poll interval the agent\n// must honor (the route rate-limits to the same cadence). These mirror the proto's\n// DeviceAuthStartResponse interval/expiry fields.\nexport const DEVICE_CODE_TTL_SECONDS = 600; // 10 minutes\nexport const DEVICE_POLL_INTERVAL_SECONDS = 5;\n// The bearer the agent presents to the NATS auth-callout. A bring-your-own-compute\n// machine is PERSISTENT (unlike an ephemeral Modal box, whose lifetime ~= an agent\n// token's hour), so this is long-lived — 30 days, matching the relay token below —\n// and re-minted on every poll/re-enroll. The old 1-hour value (sized for a Modal\n// box) caused a self-hosted agent to drop PERMANENTLY one hour after connecting: the\n// bearer expired and the auth-callout rejected every reconnect (\"re-enroll may be\n// required\"). A long-lived bearer is safe because the auth-callout RE-CHECKS the\n// enrollment status on every (re)connect (auth-callout.ts) — a revoked machine is\n// denied regardless of bearer life — exactly as the long-lived relay token relies on.\nexport const ENROLLMENT_BEARER_TTL_SECONDS = 30 * 24 * 3600;\n// The relay PRODUCER token (the `ogr_` token; M8b/dossier §10.5) is ENROLLMENT-scoped,\n// NOT per-stream: the agent presents it on every channel registration for the life\n// of its run, and the producer side has no per-viewer epoch fence (that is the\n// VIEWER's `ogs_` token's job). So it is long-lived — 30 days — re-minted on every\n// poll/re-enroll. The relay re-verifies it (authenticity + the channel-key ws+agent\n// scope) on every StreamOpen; a revoked enrollment's machine goes offline at the\n// control plane regardless, so a long-lived relay token cannot reach a dead agent.\nexport const RELAY_TOKEN_TTL_SECONDS = 30 * 24 * 3600;\n// The headless enroll token (`oget_`; design 11 §A2.1) TTL. 1h: long enough to\n// script a fleet rollout, short enough to bound exposure of a workspace-scoped\n// secret that IS the grant (no human approve). Re-mintable by an authorized user.\nexport const ENROLL_TOKEN_TTL_SECONDS = 3600;\n\nexport type EnrollmentServices = {\n db: Database;\n settings: Settings;\n};\n\n/** A workspace-scoped flow START context (the route resolves the workspace the\n * agent's flow binds to from the deployment edge / a workspace hint). */\nexport type DeviceStartInput = {\n accountId: string;\n workspaceId: string;\n publicKey: string;\n os: EnrollmentOs;\n arch: string;\n machineName?: string | null;\n canOfferDisplay: boolean;\n requestsScreenControl: boolean;\n // Where the user goes to approve (same origin as the request).\n verificationOrigin: string;\n};\n\n// A CSPRNG opaque token (URL-safe base64, no padding) for the device_code. 32\n// bytes = 256 bits of entropy — unguessable.\nfunction mintDeviceCode(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\n// A short, human-typeable user_code: 8 chars from an unambiguous alphabet (no\n// 0/O/1/I), grouped XXXX-XXXX. CSPRNG-drawn (rejection-free via modulo over a\n// 32-char alphabet, which divides 256 evenly enough; we draw extra bytes and map).\nconst USER_CODE_ALPHABET = \"ABCDEFGHJKLMNPQRSTUVWXYZ23456789\"; // 32 chars\nfunction mintUserCode(): string {\n const bytes = randomBytes(8);\n let out = \"\";\n for (let i = 0; i < 8; i += 1) {\n out += USER_CODE_ALPHABET[bytes[i]! % USER_CODE_ALPHABET.length];\n }\n return `${out.slice(0, 4)}-${out.slice(4, 8)}`;\n}\n\n/**\n * START a device-flow: persist a short-TTL single-use pending request + return the\n * DeviceAuthStart. Retries the user_code mint on the (astronomically rare) partial-\n * unique collision among live pending rows.\n */\nexport async function startDeviceEnrollment(\n services: EnrollmentServices,\n input: DeviceStartInput,\n): Promise<DeviceEnrollmentStartResponse> {\n const { db } = services;\n const expiresAt = new Date(Date.now() + DEVICE_CODE_TTL_SECONDS * 1000);\n\n let request: DeviceEnrollmentRequestRecord | undefined;\n let lastError: unknown;\n for (let attempt = 0; attempt < 5 && !request; attempt += 1) {\n const deviceCode = mintDeviceCode();\n const userCode = mintUserCode();\n try {\n request = await createDeviceEnrollmentRequest(db, {\n accountId: input.accountId,\n workspaceId: input.workspaceId,\n deviceCode,\n userCode,\n pubkey: input.publicKey,\n os: input.os,\n arch: input.arch,\n machineName: input.machineName ?? null,\n requestedExposure: \"whole-machine\",\n canOfferDisplay: input.canOfferDisplay,\n requestsScreenControl: input.requestsScreenControl,\n expiresAt,\n });\n } catch (error) {\n // A unique-violation on the live user_code (or device_code) → re-mint + retry.\n lastError = error;\n }\n }\n if (!request) {\n throw lastError instanceof Error ? lastError : new Error(\"failed to start device enrollment\");\n }\n\n const base = input.verificationOrigin.replace(/\\/$/, \"\");\n const verificationUri = `${base}/device`;\n const verificationUriComplete = `${verificationUri}?user_code=${encodeURIComponent(request.userCode)}`;\n return {\n deviceCode: request.deviceCode,\n userCode: request.userCode,\n verificationUri,\n verificationUriComplete,\n intervalSeconds: DEVICE_POLL_INTERVAL_SECONDS,\n expiresInSeconds: DEVICE_CODE_TTL_SECONDS,\n };\n}\n\n/** APPROVE a flow by user_code (the LOUD consent step). Returns the resulting\n * enrollment + sandbox ids, or null when no LIVE pending request matches the code\n * in this workspace (an unknown/expired/already-terminal code). */\nexport async function approveDeviceEnrollment(\n services: EnrollmentServices,\n input: {\n accountId: string;\n workspaceId: string;\n userCode: string;\n allowScreenControl: boolean;\n approvedBySubjectId: string;\n approvedBySubjectLabel?: string | null;\n },\n): Promise<{ enrollmentId: string; sandboxId: string; allowScreenControl: boolean } | null> {\n const { db } = services;\n const pending = await getPendingDeviceEnrollmentRequestByUserCode(db, input.workspaceId, input.userCode);\n if (!pending) {\n return null;\n }\n // A generated, human-readable sandbox name (the machine name, or a fallback).\n const sandboxName = (pending.machineName?.trim() || `${pending.os} machine`).slice(0, 256);\n const result = await approveDeviceEnrollmentRequest(db, {\n accountId: input.accountId,\n workspaceId: input.workspaceId,\n requestId: pending.id,\n allowScreenControl: input.allowScreenControl,\n approvedBySubjectId: input.approvedBySubjectId,\n approvedBySubjectLabel: input.approvedBySubjectLabel ?? null,\n sandboxName,\n });\n if (!result.approved || !result.enrollment || !result.sandbox) {\n return null;\n }\n return {\n enrollmentId: result.enrollment.id,\n sandboxId: result.sandbox.id,\n allowScreenControl: result.enrollment.allowScreenControl,\n };\n}\n\n/** LOOK UP a pending flow by user_code GLOBALLY (the click-Grant approve page;\n * design 11 §B.1). Returns the resolved pending record (carrying its workspaceId)\n * or null when no live pending row matches the code. The ROUTE authorizes the\n * caller against the resolved workspaceId (enrollments:read) BEFORE exposing\n * anything — a failed grant OR a null here both surface as 404 so cross-workspace\n * existence is never revealed. This does NOT consume the request. */\nexport async function lookupDeviceEnrollment(\n services: EnrollmentServices,\n input: { userCode: string },\n): Promise<DeviceEnrollmentRequestRecord | null> {\n const { db } = services;\n return await getPendingDeviceEnrollmentRequestByUserCodeGlobal(db, input.userCode);\n}\n\n/** Project a resolved pending record to the presentational lookup response (no\n * secrets, no device_code) the approve screen (EnrollmentConsent) renders. */\nexport function toLookupResponse(record: DeviceEnrollmentRequestRecord): DeviceEnrollmentLookupResponse {\n return {\n workspaceId: record.workspaceId,\n userCode: record.userCode,\n machine: {\n machineName: record.machineName,\n os: record.os,\n arch: record.arch,\n canOfferDisplay: record.canOfferDisplay,\n requestsScreenControl: record.requestsScreenControl,\n },\n expiresAt: record.expiresAt,\n };\n}\n\n/** DENY a flow by user_code (the explicit \"no\" at the approve page; design 11\n * §B.2). Workspace-scoped (the route asserts the grant). Returns whether a pending\n * row was flipped to denied (false for an unknown / already-terminal code). */\nexport async function denyDeviceEnrollment(\n services: EnrollmentServices,\n input: { accountId: string; workspaceId: string; userCode: string },\n): Promise<{ denied: boolean }> {\n const { db } = services;\n const pending = await getPendingDeviceEnrollmentRequestByUserCode(db, input.workspaceId, input.userCode);\n if (!pending) {\n return { denied: false };\n }\n return await denyDeviceEnrollmentRequest(db, {\n accountId: input.accountId,\n workspaceId: input.workspaceId,\n requestId: pending.id,\n });\n}\n\n/** MINT a headless enroll token (design 11 §A2.2). Signs an `oget_` token bound to\n * the workspace + account + the screen-control consent, with a 1h TTL. Returns\n * null when the credential plane is disabled (no signing secret) so the route can\n * mirror poll's \"disabled\" handling. The token value is NEVER logged. */\nexport async function mintEnrollToken(\n services: EnrollmentServices,\n input: { accountId: string; workspaceId: string; allowScreenControl: boolean },\n): Promise<MintEnrollTokenResponse | null> {\n const { settings } = services;\n const secret = resolveEnrollmentSigningSecret(settings);\n if (!secret) {\n return null;\n }\n const nowSeconds = Math.floor(Date.now() / 1000);\n const exp = nowSeconds + ENROLL_TOKEN_TTL_SECONDS;\n const token = await signEnrollToken(secret, {\n typ: \"enroll\",\n workspaceId: input.workspaceId,\n accountId: input.accountId,\n allowScreenControl: input.allowScreenControl,\n iat: nowSeconds,\n exp,\n });\n return {\n token,\n expiresAt: new Date(exp * 1000).toISOString(),\n expiresInSeconds: ENROLL_TOKEN_TTL_SECONDS,\n };\n}\n\n/** Distinguishes the two exchange failure modes for the route. */\nexport type ExchangeEnrollTokenResult =\n | { ok: true; credentials: EnrollTokenExchangeResponse[\"credentials\"] }\n | { ok: false; reason: \"disabled\" }\n | { ok: false; reason: \"invalid\" };\n\n/** EXCHANGE a headless enroll token (design 11 §A2.3) — the UNAUTHENTICATED path\n * where the token IS the auth. Verifies the `oget_` token, then performs the SAME\n * finalize as approve (upsert enrollment + ensure selfhosted sandbox,\n * consentedWholeMachine=true, consentedScreenControl=token.allowScreenControl) and\n * builds the IDENTICAL EnrollmentCredentials the poll authorized branch returns.\n * Returns reason \"disabled\" when no signing secret (mirror poll), \"invalid\" when\n * the token fails verification (the route 401s). */\nexport async function exchangeEnrollToken(\n services: EnrollmentServices,\n input: {\n token: string;\n publicKey: string;\n os: EnrollmentOs;\n arch: string;\n machineName?: string | null;\n canOfferDisplay: boolean;\n },\n): Promise<ExchangeEnrollTokenResult> {\n const { db, settings } = services;\n const secret = resolveEnrollmentSigningSecret(settings);\n if (!secret) {\n // The credential plane is off for this deployment — surface disabled, never a\n // 500, never an unsigned credential (mirror poll's disabled handling).\n return { ok: false, reason: \"disabled\" };\n }\n const claims = await verifyEnrollToken(secret, input.token);\n if (!claims) {\n // Bad prefix / signature / typ / expired — the token is not a valid grant.\n return { ok: false, reason: \"invalid\" };\n }\n\n // The SAME finalize as approve, but driven by the token's claims (no pending row).\n const sandboxName = (input.machineName?.trim() || `${input.os} machine`).slice(0, 256);\n const { enrollment } = await finalizeEnrollmentByToken(db, {\n accountId: claims.accountId,\n workspaceId: claims.workspaceId,\n pubkey: input.publicKey,\n hasDisplay: input.canOfferDisplay,\n // The token's allowScreenControl is the AUTHORITATIVE consent (NOT the agent's\n // requestsScreenControl) — it was baked in at mint by the authorizing user.\n allowScreenControl: claims.allowScreenControl,\n os: input.os,\n arch: input.arch,\n sandboxName,\n });\n\n const credentials = await buildEnrollmentCredentials(services, {\n secret,\n workspaceId: claims.workspaceId,\n agentId: enrollment.id,\n consentedScreenControl: enrollment.allowScreenControl,\n });\n return { ok: true, credentials };\n}\n\n/**\n * POLL a flow by device_code. Resolves the state machine:\n * - unknown code → \"expired\" (do not leak existence; an unknown code\n * behaves like an expired one to the agent).\n * - pending + within TTL → \"pending\".\n * - pending + past TTL → \"expired\".\n * - denied → \"denied\".\n * - approved | consumed → \"authorized\" + the EnrollmentCredentials (the\n * approved row is flipped to consumed; a legitimate\n * re-poll of a consumed row still returns the creds).\n * When the credential plane is disabled (no resolvable signing secret), an\n * otherwise-authorized poll returns \"disabled\" so the agent surfaces a clear reason\n * rather than half-enrolling.\n */\nexport async function pollDeviceEnrollment(\n services: EnrollmentServices,\n input: { deviceCode: string },\n): Promise<DeviceEnrollmentPollResponse> {\n const { db, settings } = services;\n const request = await getDeviceEnrollmentRequestByDeviceCode(db, input.deviceCode);\n if (!request) {\n return { state: \"expired\" };\n }\n if (request.status === \"denied\") {\n return { state: \"denied\" };\n }\n if (request.status === \"pending\") {\n if (new Date(request.expiresAt).getTime() <= Date.now()) {\n return { state: \"expired\" };\n }\n return { state: \"pending\" };\n }\n\n // approved | consumed → AUTHORIZED. Build the credentials.\n if (!request.enrollmentId) {\n // Defensive: an approved row must carry the enrollment id.\n return { state: \"expired\" };\n }\n const secret = resolveEnrollmentSigningSecret(settings);\n if (!secret) {\n // The credential plane is off for this deployment (no signing secret) — surface\n // a clear disabled state, never a 500 and never an unsigned credential.\n return { state: \"disabled\" };\n }\n\n const enrollment = await getEnrollment(db, request.workspaceId, request.enrollmentId);\n if (!enrollment || enrollment.status !== \"active\") {\n // The machine was revoked between approve and poll — treat as denied.\n return { state: \"denied\" };\n }\n\n const credentials = await buildEnrollmentCredentials(services, {\n secret,\n workspaceId: request.workspaceId,\n agentId: enrollment.id,\n consentedScreenControl: enrollment.allowScreenControl,\n });\n\n // Single-use: flip approved → consumed (idempotent; a re-poll of an already-\n // consumed row still returns the creds above — the agent may legitimately retry).\n if (request.status === \"approved\") {\n await consumeDeviceEnrollmentRequest(db, {\n accountId: request.accountId,\n workspaceId: request.workspaceId,\n requestId: request.id,\n });\n }\n\n return { state: DeviceEnrollmentState.enum.authorized, credentials };\n}\n\n/** Build the EnrollmentCredentials the poll returns: the signed `oge_` bearer +\n * the Account-scoped subject prefix + the connect info. The bearer-as-NATS-token\n * model (M-AUTH) closes the M5 placeholder: the agent presents the bearer as the\n * connect AUTH TOKEN, nats-server's auth-callout responder validates it and mints a\n * workspace-scoped user JWT, so there is NO per-machine NATS creds file to ship.\n * `natsAccountCreds` is therefore vestigial — kept (proto-additive) and set to the\n * bearer so an agent using it as the connect-token credential works uniformly. */\nasync function buildEnrollmentCredentials(\n services: EnrollmentServices,\n input: { secret: string; workspaceId: string; agentId: string; consentedScreenControl: boolean },\n): Promise<EnrollmentCredentialsResponse> {\n const { settings } = services;\n // The control-plane subject prefix the agent subscribes to: agent.<ws>.<id>.\n const subjectPrefix = `agent.${input.workspaceId}.${input.agentId}`;\n const nowSeconds = Math.floor(Date.now() / 1000);\n const exp = nowSeconds + ENROLLMENT_BEARER_TTL_SECONDS;\n const bearer = await signEnrollmentBearer(input.secret, {\n workspaceId: input.workspaceId,\n agentId: input.agentId,\n enrollmentId: input.agentId,\n subjectPrefix,\n exp,\n });\n const natsUrls = settings.selfhostedNatsUrl ? [settings.selfhostedNatsUrl] : [];\n // Mint the agent's relay PRODUCER token (M8b) when the relay-token plane is\n // configured. The relay verifies it (the `ogr_` envelope) and pairs the producer\n // with the viewer. Absent secret → empty token (graceful degrade; the stream plane\n // is unavailable until the secret is provisioned via ops-repo IaC). The token binds\n // (workspaceId, agentId) so the agent can only register ITS OWN channels.\n const relayTokenSecret = resolveRelayTokenSecret(settings);\n const relayToken = relayTokenSecret\n ? await signRelayToken(relayTokenSecret, {\n workspaceId: input.workspaceId,\n agentId: input.agentId,\n exp: nowSeconds + RELAY_TOKEN_TTL_SECONDS,\n })\n : \"\";\n return {\n agentId: input.agentId,\n workspaceId: input.workspaceId,\n bearer,\n subjectPrefix,\n natsUrls,\n // Hand the agent the canonical `/stream` dial base, NOT the raw configured URL.\n // The agent's relay producer appends only its routing query and assumes the base\n // already carries the relay's `/stream` route; a path-less base 400s the dial and\n // makes the terminal/desktop streams unreachable (dossier §V5/§V6).\n relayUrl: relayDialBaseFromSettings(settings),\n relayToken,\n // M-AUTH closes the placeholder: there is NO per-machine NATS Account creds\n // file. The agent presents the BEARER as the NATS connect auth-token; the\n // server's auth-callout responder validates it and mints a workspace-scoped\n // user JWT. We echo the bearer here so a consumer reading this (vestigial) field\n // as the connect credential still works — the value IS the bearer.\n natsAccountCreds: bearer,\n updatePublicKey: settings.agentUpdatePublicKey ?? \"\",\n consentedWholeMachine: true,\n consentedScreenControl: input.consentedScreenControl,\n };\n}\n","// apps/api/src/routes/machines.ts — the M10 Machines-dashboard + per-machine\n// metrics-series ROUTES (dossier §10.7). Mirrors registerEnrollmentRoutes: thin\n// routes over a focused service (../sandbox/machines.ts), requireAccessGrant\n// BEFORE any work, the whole router gated behind sandboxSelfhostedEnabled\n// (default OFF → 404, invisible). Both routes need perm enrollments:read.\n//\n// GET /v1/workspaces/:ws/machines[?sessionId=...] -> MachinesResponse\n// The dashboard list: the workspace's enrolled selfhosted machines (state +\n// latest metrics + sharedSessionCount) and, when sessionId is supplied, the\n// session's synthetic Modal group box + the active-sandbox pointer.\n//\n// GET /v1/workspaces/:ws/machines/:enrollmentId/metrics/series?window=1h\n// -> { samples: MetricSample[] }\n// The downsampled (~1/min) history for ONE machine over a time window.\n\nimport {\n MachineMetricsSeriesResponse,\n MachinesResponse,\n SwapActiveSandboxRequest,\n SwapActiveSandboxResponse,\n} from \"@opengeni/contracts\";\nimport {\n getEnrollment,\n readMachineMetricsSeries,\n} from \"@opengeni/db\";\nimport type { Hono } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport { requireAccessGrant } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\nimport { buildFleetContextForSession, swapActiveSandbox } from \"@opengeni/core\";\nimport { listMachines, metricRowToSample } from \"../sandbox/machines\";\n\n// The supported series windows → milliseconds. An unknown/absent window defaults\n// to 1h (the dossier default). Bounded so a caller cannot request an unbounded\n// scan; longer ranges are a later concern (retention is ~N days).\nconst SERIES_WINDOWS_MS: Record<string, number> = {\n \"15m\": 15 * 60_000,\n \"1h\": 60 * 60_000,\n \"6h\": 6 * 60 * 60_000,\n \"24h\": 24 * 60 * 60_000,\n};\nconst DEFAULT_SERIES_WINDOW_MS = SERIES_WINDOWS_MS[\"1h\"]!;\n\nexport function registerMachineRoutes(app: Hono, deps: ApiRouteDeps): void {\n const { settings, db, bus } = deps;\n\n // The whole surface is behind sandboxSelfhostedEnabled. A 404 (not 403) keeps it\n // invisible while disabled — it does not exist for this deployment yet.\n function assertSelfhostedEnabled(): void {\n if (!settings.sandboxSelfhostedEnabled) {\n throw new HTTPException(404, { message: \"selfhosted machines are not enabled for this deployment\" });\n }\n }\n\n // ── GET /workspaces/:ws/machines (the dashboard list) ───────────────────────\n app.get(\"/v1/workspaces/:workspaceId/machines\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"enrollments:read\");\n assertSelfhostedEnabled();\n // sessionId is OPTIONAL: present → an in-session view (synthetic group box +\n // active pointer); absent → the pure workspace dashboard.\n const sessionId = c.req.query(\"sessionId\") ?? null;\n const response = await listMachines({ db, settings, bus }, { workspaceId, sessionId });\n return c.json(MachinesResponse.parse(response));\n });\n\n // ── GET /workspaces/:ws/machines/:enrollmentId/metrics/series ───────────────\n app.get(\"/v1/workspaces/:workspaceId/machines/:enrollmentId/metrics/series\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"enrollments:read\");\n assertSelfhostedEnabled();\n const enrollmentId = c.req.param(\"enrollmentId\");\n // Validate the machine belongs to this workspace (RLS already scopes the read,\n // but a clear 404 beats an empty series for an unknown/cross-workspace id).\n const enrollment = await getEnrollment(db, workspaceId, enrollmentId);\n if (!enrollment) {\n throw new HTTPException(404, { message: \"machine not found in this workspace\" });\n }\n const windowMs = SERIES_WINDOWS_MS[c.req.query(\"window\") ?? \"\"] ?? DEFAULT_SERIES_WINDOW_MS;\n const since = new Date(Date.now() - windowMs);\n const rows = await readMachineMetricsSeries(db, { workspaceId, enrollmentId, since });\n return c.json(MachineMetricsSeriesResponse.parse({\n samples: rows.map(metricRowToSample),\n }));\n });\n\n // ── POST /workspaces/:ws/sessions/:sessionId/active-sandbox (swap) ───────────\n // The user-authenticated equivalent of the M7 `sandbox_swap` MCP tool: repoint\n // a session's active sandbox under the epoch fence. Same perm as PATCH session\n // (sessions:control); gated behind sandboxSelfhostedEnabled (404 when off, the\n // surface is invisible). All ownership/liveness/epoch validation lives inside\n // swapActiveSandbox — the route only builds the session-scoped FleetContext.\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/active-sandbox\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"sessions:control\");\n assertSelfhostedEnabled();\n const sessionId = c.req.param(\"sessionId\");\n const body = SwapActiveSandboxRequest.parse(await c.req.json());\n const ctx = await buildFleetContextForSession(deps, {\n accountId: grant.accountId,\n workspaceId,\n sessionId,\n });\n const result = await swapActiveSandbox({ db, settings, bus }, ctx, body.target);\n return c.json(SwapActiveSandboxResponse.parse(result));\n });\n}\n","// apps/api/src/sandbox/machines.ts — the M10 Machines-DASHBOARD service (dossier\n// §10.7). Builds the `MachinesResponse` the dashboard renders: the workspace's\n// enrolled selfhosted machines, each enriched with\n// * STATE — the M3 liveness (online/reconnecting/offline) overlaid with the\n// enrollment-derived consent/display reasons (consent_required /\n// display_unavailable) — a real ControlRpc ping (the subject IS the registry),\n// reusing the M7 fleet probe;\n// * METRICS — the latest machine_metrics_latest row (or null before a first\n// heartbeat), projected to the contract's MetricSample;\n// * sharedSessionCount — the lease refcount (how many sessions share this one\n// whole machine, the maxSandboxes:1 disclosure).\n// PLUS, when a session context is supplied, the session's synthetic Modal group\n// box (isSessionGroup:true) + the active-sandbox pointer (activeSandboxId/Epoch).\n//\n// This is workspace-scoped (perm enrollments:read) and flag-gated upstream\n// (sandboxSelfhostedEnabled). It deliberately does NOT depend on a FleetContext\n// (which is session-coupled): the pure workspace dashboard works without a\n// session; an in-session view passes the optional session to add the group box +\n// active pointer.\n\nimport type { Settings } from \"@opengeni/config\";\nimport {\n getSession,\n listEnrollments,\n listSandboxes,\n readActiveSandbox,\n readLease,\n readMachineMetricsLatestForWorkspace,\n type Database,\n type EnrollmentRecord,\n type MachineMetricsRow,\n} from \"@opengeni/db\";\nimport type { EventBus } from \"@opengeni/events\";\nimport {\n MachineView,\n MetricSample,\n type MachinesResponse,\n} from \"@opengeni/contracts\";\nimport {\n NatsControlRpc,\n selfhostedLiveness,\n SelfhostedSession,\n type ControlRpc,\n type NatsRequestConnection,\n} from \"@opengeni/runtime/sandbox\";\nimport { relayConfigFromSettings } from \"@opengeni/core\";\n\nexport type MachinesServices = {\n db: Database;\n settings: Settings;\n bus?: EventBus;\n};\n\nconst PROBE_TIMEOUT_MS = 5_000;\n\nfunction controlRpc(bus: EventBus | undefined): ControlRpc {\n return new NatsControlRpc(async (): Promise<NatsRequestConnection | null> => {\n if (!bus) {\n return null;\n }\n return bus.getRequestConnection();\n });\n}\n\n/**\n * Project a stored `machine_metrics_latest` row to the contract `MetricSample`.\n * The DB carries `gpuUtilPercent` + `gpuMemUsedBytes`/`gpuMemTotalBytes`; the wire\n * `MetricSample` exposes the single `gpuUtilPct` + `gpuMemBytes` (USED bytes — the\n * \"how much VRAM is in use\" the dashboard reads). A null any-numeric stays null\n * (the not-reported contract); the byte/load fields default to 0 when a sample\n * carried no value (the agent reports 0 == not-reported for those).\n */\nexport function metricRowToSample(row: MachineMetricsRow): MetricSample {\n return MetricSample.parse({\n cpuPct: row.cpuPercent ?? 0,\n load1: row.load1 ?? 0,\n load5: row.load5 ?? 0,\n load15: row.load15 ?? 0,\n memUsedBytes: row.memUsedBytes ?? 0,\n memTotalBytes: row.memTotalBytes ?? 0,\n diskUsedBytes: row.diskUsedBytes ?? 0,\n diskTotalBytes: row.diskTotalBytes ?? 0,\n gpuUtilPct: row.gpuUtilPercent,\n gpuMemBytes: row.gpuMemUsedBytes,\n runQueue: row.contention ?? 0,\n sampledAt: row.sampledAt,\n });\n}\n\n/** Probe an enrolled machine's liveness — a real ControlRpc ping mapped through\n * `selfhostedLiveness` (the enrollment status/consent/display + lastSeenAt\n * disambiguate a probe-miss into reconnecting vs offline). Mirrors the M7 fleet\n * probe. A non-active enrollment is offline without a probe. */\nasync function probeEnrollment(\n services: MachinesServices,\n workspaceId: string,\n enrollment: EnrollmentRecord,\n): Promise<{ state: \"online\" | \"reconnecting\" | \"offline\"; consented: boolean; hasDisplay: boolean }> {\n const { settings, bus } = services;\n let probeResponded = false;\n if (enrollment.status === \"active\") {\n const session = new SelfhostedSession({\n workspaceId,\n agentId: enrollment.id,\n controlRpc: controlRpc(bus),\n relay: relayConfigFromSettings(settings),\n timeoutMs: PROBE_TIMEOUT_MS,\n });\n try {\n probeResponded = await session.ping();\n } catch {\n probeResponded = false;\n }\n }\n const derived = selfhostedLiveness({\n enrollment: {\n status: enrollment.status,\n exposure: enrollment.exposure,\n allowScreenControl: enrollment.allowScreenControl,\n hasDisplay: enrollment.hasDisplay,\n lastSeenAt: enrollment.lastSeenAt,\n },\n probeResponded,\n });\n return { state: derived.state, consented: derived.consented, hasDisplay: derived.hasDisplay };\n}\n\n/**\n * Resolve the dashboard STATE of a machine. State reflects REACHABILITY + the\n * VIEW plane only: an online machine with no display → `display_unavailable` (no\n * desktop stream, but compute — exec/fs/git/terminal — still works); otherwise\n * the liveness state (online/reconnecting/offline). It deliberately does NOT fold\n * in screen-control consent: a displayed machine can be VIEWED (read-only) and\n * used for compute regardless of `allowScreenControl` — only INPUT (ComputerUse /\n * an interactive stream) needs that consent, which is a per-capability concern\n * carried by the separate `allowScreenControl` field (surfaced in the viewer's\n * Take-control affordance), NOT a blocking machine state. This mirrors the\n * view/control split in the selfhosted capability negotiation so the dashboard\n * pill, the dock, and the \"Run on\" picker agree (a machine is never wrongly\n * un-selectable just because its input isn't consented).\n */\nfunction machineStateFor(\n liveness: \"online\" | \"reconnecting\" | \"offline\",\n hasDisplay: boolean,\n): MachinesResponse[\"machines\"][number][\"state\"] {\n if (liveness !== \"online\") {\n return liveness;\n }\n if (!hasDisplay) {\n return \"display_unavailable\";\n }\n return \"online\";\n}\n\n/**\n * Build the Machines dashboard response for a workspace. When `sessionId` is\n * supplied (an in-session view) the session's synthetic Modal group box is\n * prepended (`isSessionGroup:true`) and the active-sandbox pointer is echoed;\n * without it (the pure workspace dashboard) `activeSandboxId` is null and only\n * the enrolled machines are listed.\n */\nexport async function listMachines(\n services: MachinesServices,\n input: { workspaceId: string; sessionId?: string | null },\n): Promise<MachinesResponse> {\n const { db } = services;\n const { workspaceId } = input;\n\n // The session's active pointer (in-session view only). Absent session → the\n // default null pointer (the workspace dashboard has no \"active\" machine).\n let activeSandboxId: string | null = null;\n let activeEpoch = 0;\n let session: Awaited<ReturnType<typeof getSession>> | null = null;\n if (input.sessionId) {\n session = await getSession(db, workspaceId, input.sessionId);\n if (session) {\n const pointer = await readActiveSandbox(db, workspaceId, input.sessionId);\n activeSandboxId = pointer?.activeSandboxId ?? null;\n activeEpoch = pointer?.activeEpoch ?? 0;\n }\n }\n\n const machines: MachineView[] = [];\n\n // The session's own Modal group box (synthetic): the default/home sandbox a\n // null active pointer routes to. Only present in an in-session view.\n if (session) {\n const groupActive = activeSandboxId === null;\n machines.push(MachineView.parse({\n sandboxId: session.sandboxGroupId,\n enrollmentId: null,\n name: \"session sandbox\",\n kind: session.sandboxBackend === \"selfhosted\" ? \"selfhosted\" : \"modal\",\n state: \"online\",\n active: groupActive,\n isSessionGroup: true,\n // The Modal group box is a cloud Linux box; its precise OS/arch is not\n // surfaced as a metric, so the dashboard shows the canonical linux/x86_64.\n os: \"linux\",\n arch: \"x86_64\",\n hasDisplay: false,\n allowScreenControl: false,\n sharedSessionCount: 1,\n lastSeenAt: null,\n metrics: null,\n }));\n }\n\n // The workspace's enrolled selfhosted machines. One bulk metrics read joined\n // onto the machines (no N+1). Each machine is probed for liveness.\n const [sandboxes, enrollments, metricsByEnrollment] = await Promise.all([\n listSandboxes(db, workspaceId),\n listEnrollments(db, workspaceId),\n readMachineMetricsLatestForWorkspace(db, workspaceId),\n ]);\n const enrollmentById = new Map(enrollments.map((e) => [e.id, e]));\n\n for (const sandbox of sandboxes) {\n if (sandbox.kind !== \"selfhosted\" || !sandbox.enrollmentId) {\n continue;\n }\n const enrollment = enrollmentById.get(sandbox.enrollmentId) ?? null;\n if (!enrollment) {\n continue;\n }\n const probe = await probeEnrollment(services, workspaceId, enrollment);\n const state = machineStateFor(probe.state, probe.hasDisplay);\n\n // sharedSessionCount = the lease refcount for this machine's group. The\n // selfhosted sandbox id IS the lease group key (maxSandboxes:1, N sessions\n // share via refcount). No lease yet → 0 sessions sharing.\n const lease = await readLease(db, workspaceId, sandbox.id);\n const sharedSessionCount = lease?.refcount ?? 0;\n\n const metricsRow = metricsByEnrollment.get(enrollment.id) ?? null;\n machines.push(MachineView.parse({\n sandboxId: sandbox.id,\n enrollmentId: enrollment.id,\n name: sandbox.name,\n kind: \"selfhosted\",\n state,\n active: activeSandboxId === sandbox.id,\n isSessionGroup: false,\n os: enrollment.os,\n arch: enrollment.arch,\n hasDisplay: enrollment.hasDisplay,\n allowScreenControl: enrollment.allowScreenControl,\n sharedSessionCount,\n lastSeenAt: enrollment.lastSeenAt,\n metrics: metricsRow ? metricRowToSample(metricsRow) : null,\n }));\n }\n\n return { activeSandboxId, activeEpoch, machines };\n}\n","import {\n CreateWorkspaceEnvironmentRequest,\n SetWorkspaceEnvironmentVariableRequest,\n UpdateWorkspaceEnvironmentRequest,\n WorkspaceEnvironmentVariableName,\n} from \"@opengeni/contracts\";\nimport {\n countActiveSessionsUsingEnvironment,\n countScheduledTasksUsingEnvironment,\n countWorkspaceEnvironments,\n createWorkspaceEnvironment,\n deleteWorkspaceEnvironment,\n deleteWorkspaceEnvironmentVariable,\n encryptEnvironmentValue,\n getWorkspaceEnvironmentByName,\n listWorkspaceEnvironments,\n setWorkspaceEnvironmentVariable,\n updateWorkspaceEnvironment,\n} from \"@opengeni/db\";\nimport type { Hono } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport { requireAccessGrant } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\nimport {\n assertAllowedEnvironmentVariableName,\n MAX_ENVIRONMENTS_PER_WORKSPACE,\n MAX_VARIABLES_PER_ENVIRONMENT,\n recordEnvironmentAuditEvent,\n requireEnvironmentEncryption,\n requireEnvironmentForApi,\n} from \"@opengeni/core\";\n\nexport function registerEnvironmentRoutes(app: Hono, deps: ApiRouteDeps): void {\n const { settings, db } = deps;\n\n app.get(\"/v1/workspaces/:workspaceId/environments\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"environments:use\");\n return c.json(await listWorkspaceEnvironments(db, workspaceId));\n });\n\n app.post(\"/v1/workspaces/:workspaceId/environments\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"environments:manage\");\n const key = requireEnvironmentEncryption(settings);\n const payload = CreateWorkspaceEnvironmentRequest.parse(await c.req.json());\n const name = trimmedEnvironmentName(payload.name);\n if (payload.variables.length > MAX_VARIABLES_PER_ENVIRONMENT) {\n throw new HTTPException(422, { message: `an environment supports at most ${MAX_VARIABLES_PER_ENVIRONMENT} variables` });\n }\n const variableNames = new Set<string>();\n for (const variable of payload.variables) {\n assertAllowedEnvironmentVariableName(variable.name);\n if (variableNames.has(variable.name)) {\n throw new HTTPException(422, { message: `duplicate environment variable name: ${variable.name}` });\n }\n variableNames.add(variable.name);\n }\n if (await countWorkspaceEnvironments(db, workspaceId) >= MAX_ENVIRONMENTS_PER_WORKSPACE) {\n throw new HTTPException(422, { message: `a workspace supports at most ${MAX_ENVIRONMENTS_PER_WORKSPACE} environments` });\n }\n if (await getWorkspaceEnvironmentByName(db, workspaceId, name)) {\n throw new HTTPException(409, { message: `environment name is already in use: ${name}` });\n }\n // Values are encrypted up front and the environment plus all initial\n // variables are written in one transaction: a failure leaves nothing.\n const created = await createWorkspaceEnvironment(db, {\n accountId: grant.accountId,\n workspaceId,\n name,\n description: payload.description ?? null,\n variables: payload.variables.map((variable) => ({\n name: variable.name,\n valueEncrypted: encryptEnvironmentValue(key, variable.value),\n })),\n });\n await recordEnvironmentAuditEvent(db, { grant, action: \"environment.created\", environmentId: created.id });\n return c.json(created, 201);\n });\n\n app.get(\"/v1/workspaces/:workspaceId/environments/:environmentId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"environments:use\");\n return c.json(await requireEnvironmentForApi(db, workspaceId, c.req.param(\"environmentId\")));\n });\n\n app.patch(\"/v1/workspaces/:workspaceId/environments/:environmentId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"environments:manage\");\n const environment = await requireEnvironmentForApi(db, workspaceId, c.req.param(\"environmentId\"));\n const payload = UpdateWorkspaceEnvironmentRequest.parse(await c.req.json());\n const name = payload.name !== undefined ? trimmedEnvironmentName(payload.name) : undefined;\n if (name !== undefined && name !== environment.name) {\n const existing = await getWorkspaceEnvironmentByName(db, workspaceId, name);\n if (existing && existing.id !== environment.id) {\n throw new HTTPException(409, { message: `environment name is already in use: ${name}` });\n }\n }\n const updated = await updateWorkspaceEnvironment(db, workspaceId, environment.id, {\n ...(name !== undefined ? { name } : {}),\n ...(payload.description !== undefined ? { description: payload.description } : {}),\n });\n await recordEnvironmentAuditEvent(db, { grant, action: \"environment.updated\", environmentId: environment.id });\n return c.json(updated);\n });\n\n app.delete(\"/v1/workspaces/:workspaceId/environments/:environmentId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"environments:manage\");\n const environment = await requireEnvironmentForApi(db, workspaceId, c.req.param(\"environmentId\"));\n const attachedTasks = await countScheduledTasksUsingEnvironment(db, workspaceId, environment.id);\n if (attachedTasks > 0) {\n throw new HTTPException(409, { message: `environment is attached to ${attachedTasks} scheduled task(s); detach first` });\n }\n const activeSessions = await countActiveSessionsUsingEnvironment(db, workspaceId, environment.id);\n if (activeSessions > 0) {\n throw new HTTPException(409, { message: `environment is attached to ${activeSessions} active session(s); wait for them to finish or cancel them first` });\n }\n await deleteWorkspaceEnvironment(db, workspaceId, environment.id);\n await recordEnvironmentAuditEvent(db, { grant, action: \"environment.deleted\", environmentId: environment.id });\n return c.json({ ok: true });\n });\n\n app.put(\"/v1/workspaces/:workspaceId/environments/:environmentId/variables/:name\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"environments:manage\");\n const key = requireEnvironmentEncryption(settings);\n const name = parseVariableName(c.req.param(\"name\"));\n const environment = await requireEnvironmentForApi(db, workspaceId, c.req.param(\"environmentId\"));\n const payload = SetWorkspaceEnvironmentVariableRequest.parse(await c.req.json());\n const exists = environment.variables.some((variable) => variable.name === name);\n if (!exists && environment.variables.length >= MAX_VARIABLES_PER_ENVIRONMENT) {\n throw new HTTPException(422, { message: `an environment supports at most ${MAX_VARIABLES_PER_ENVIRONMENT} variables` });\n }\n const metadata = await setWorkspaceEnvironmentVariable(db, {\n accountId: grant.accountId,\n workspaceId,\n environmentId: environment.id,\n name,\n valueEncrypted: encryptEnvironmentValue(key, payload.value),\n });\n await recordEnvironmentAuditEvent(db, { grant, action: \"environment.variable.set\", environmentId: environment.id, variableName: name });\n return c.json(metadata);\n });\n\n app.delete(\"/v1/workspaces/:workspaceId/environments/:environmentId/variables/:name\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"environments:manage\");\n const name = parseVariableName(c.req.param(\"name\"));\n const environment = await requireEnvironmentForApi(db, workspaceId, c.req.param(\"environmentId\"));\n const deleted = await deleteWorkspaceEnvironmentVariable(db, workspaceId, environment.id, name);\n if (!deleted) {\n throw new HTTPException(404, { message: \"environment variable not found\" });\n }\n await recordEnvironmentAuditEvent(db, { grant, action: \"environment.variable.deleted\", environmentId: environment.id, variableName: name });\n return c.json({ ok: true });\n });\n}\n\nfunction parseVariableName(raw: string): string {\n const parsed = WorkspaceEnvironmentVariableName.safeParse(raw);\n if (!parsed.success) {\n throw new HTTPException(422, { message: \"environment variable names must match ^[A-Z][A-Z0-9_]*$\" });\n }\n assertAllowedEnvironmentVariableName(parsed.data);\n return parsed.data;\n}\n\nfunction trimmedEnvironmentName(name: string): string {\n const trimmed = name.trim();\n if (!trimmed) {\n throw new HTTPException(422, { message: \"environment name is required\" });\n }\n return trimmed;\n}\n","import {\n CompleteFileUploadResponse,\n CreateFileUploadRequest,\n CreateFileUploadResponse,\n FileAsset,\n FileDownloadUrlResponse,\n} from \"@opengeni/contracts\";\nimport {\n completeFileUpload,\n createFileUpload,\n getFileUpload,\n markFileUploadFailed,\n requireFile,\n} from \"@opengeni/db\";\nimport type { Hono } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport { requireAccessGrant } from \"@opengeni/core\";\nimport { recordWorkspaceUsage, requireLimit } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\n\nexport function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {\n const { db, objectStorage } = deps;\n\n app.post(\"/v1/workspaces/:workspaceId/files/uploads\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"files:upload\");\n if (!objectStorage) {\n throw new HTTPException(503, { message: \"object storage is not configured\" });\n }\n const payload = CreateFileUploadRequest.parse(await c.req.json());\n await requireLimit(deps, { accountId: grant.accountId, workspaceId, action: \"file:upload\", quantity: payload.sizeBytes });\n if (payload.sizeBytes > objectStorage.maxSinglePutSizeBytes) {\n throw new HTTPException(413, { message: `file exceeds single PUT limit of ${objectStorage.maxSinglePutSizeBytes} bytes` });\n }\n const fileId = crypto.randomUUID();\n const safeFilename = sanitizeFilename(payload.filename);\n const objectKey = `workspaces/${workspaceId}/files/${fileId}/original/${safeFilename}`;\n const signed = await objectStorage.createPutUrl({\n key: objectKey,\n contentType: payload.contentType,\n ...(payload.sha256 ? { sha256: payload.sha256 } : {}),\n });\n const upload = await createFileUpload(db, {\n accountId: grant.accountId,\n workspaceId,\n fileId,\n filename: payload.filename,\n safeFilename,\n contentType: payload.contentType,\n sizeBytes: payload.sizeBytes,\n sha256: payload.sha256 ?? null,\n bucket: objectStorage.bucket,\n objectKey,\n expiresAt: signed.expiresAt,\n });\n return c.json(CreateFileUploadResponse.parse({\n fileId: upload.file.id,\n uploadId: upload.uploadId,\n putUrl: signed.url,\n requiredHeaders: signed.requiredHeaders,\n expiresAt: upload.expiresAt,\n maxSizeBytes: objectStorage.maxSinglePutSizeBytes,\n }), 201);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/files/uploads/:uploadId/complete\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"files:upload\");\n if (!objectStorage) {\n throw new HTTPException(503, { message: \"object storage is not configured\" });\n }\n const upload = await getFileUpload(db, workspaceId, c.req.param(\"uploadId\"));\n if (!upload) {\n throw new HTTPException(404, { message: \"file upload not found\" });\n }\n if (upload.status !== \"pending\") {\n throw new HTTPException(409, { message: `file upload is ${upload.status}` });\n }\n if (upload.expiresAt.getTime() < Date.now()) {\n await markFileUploadFailed(db, workspaceId, upload.id, upload.file.id);\n throw new HTTPException(409, { message: \"file upload has expired\" });\n }\n const head = await objectStorage.headFile(upload.file).catch((error) => {\n throw new HTTPException(409, { message: `uploaded object is not available: ${error instanceof Error ? error.message : String(error)}` });\n });\n if (Number(head.ContentLength ?? -1) !== upload.file.sizeBytes) {\n await markFileUploadFailed(db, workspaceId, upload.id, upload.file.id);\n throw new HTTPException(422, { message: \"uploaded object size does not match file metadata\" });\n }\n if (upload.file.contentType && head.ContentType && head.ContentType !== upload.file.contentType) {\n await markFileUploadFailed(db, workspaceId, upload.id, upload.file.id);\n throw new HTTPException(422, { message: \"uploaded object content type does not match file metadata\" });\n }\n if (upload.file.sha256 && head.Metadata?.sha256 !== upload.file.sha256) {\n await markFileUploadFailed(db, workspaceId, upload.id, upload.file.id);\n throw new HTTPException(422, { message: \"uploaded object checksum metadata does not match file metadata\" });\n }\n const file = await completeFileUpload(db, workspaceId, upload.id);\n await recordWorkspaceUsage(deps, {\n accountId: grant.accountId,\n workspaceId,\n subjectId: grant.subjectId,\n eventType: \"file.uploaded\",\n quantity: file.sizeBytes,\n unit: \"byte\",\n sourceResourceType: \"file\",\n sourceResourceId: file.id,\n idempotencyKey: `file.uploaded:${workspaceId}:${file.id}`,\n });\n return c.json(CompleteFileUploadResponse.parse({ file }));\n });\n\n app.get(\"/v1/workspaces/:workspaceId/files/:fileId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"files:read\");\n const file = await requireFile(db, workspaceId, c.req.param(\"fileId\")).catch(() => null);\n if (!file) {\n throw new HTTPException(404, { message: \"file not found\" });\n }\n return c.json(FileAsset.parse(file));\n });\n\n app.post(\"/v1/workspaces/:workspaceId/files/:fileId/download-url\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"files:read\");\n if (!objectStorage) {\n throw new HTTPException(503, { message: \"object storage is not configured\" });\n }\n const file = await requireFile(db, workspaceId, c.req.param(\"fileId\")).catch(() => null);\n if (!file) {\n throw new HTTPException(404, { message: \"file not found\" });\n }\n if (file.status !== \"ready\") {\n throw new HTTPException(409, { message: `file is ${file.status}` });\n }\n const signed = await objectStorage.createGetUrl({ key: file.objectKey });\n return c.json(FileDownloadUrlResponse.parse({\n url: signed.url,\n expiresAt: signed.expiresAt.toISOString(),\n }));\n });\n}\n\nfunction sanitizeFilename(filename: string): string {\n const trimmed = filename.trim().replace(/[/\\\\]/g, \"_\");\n const safe = trimmed.replace(/[^A-Za-z0-9._ -]+/g, \"_\").replace(/\\s+/g, \" \").trim();\n return safe || \"file\";\n}\n","import { CreateApiKeyRequest, CreateApiKeyResponse, Permission } from \"@opengeni/contracts\";\nimport { createApiKey, listApiKeys, revokeApiKey } from \"@opengeni/db\";\nimport { zValidator } from \"@hono/zod-validator\";\nimport type { Hono } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\nimport { requireAccessGrant } from \"@opengeni/core\";\nimport { requireLimit } from \"@opengeni/core\";\n\nexport function registerApiKeyRoutes(app: Hono, deps: ApiRouteDeps): void {\n app.get(\"/v1/workspaces/:workspaceId/api-keys\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"api_keys:manage\");\n return c.json({ apiKeys: await listApiKeys(deps.db, workspaceId) });\n });\n\n app.post(\"/v1/workspaces/:workspaceId/api-keys\", zValidator(\"json\", CreateApiKeyRequest.omit({ workspaceId: true })), async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"api_keys:manage\");\n const body = c.req.valid(\"json\");\n const permissions: Permission[] = body.permissions.length > 0 ? body.permissions as Permission[] : [\"workspace:read\"];\n ensureDelegablePermissions(grant.permissions, permissions);\n await requireLimit(deps, { accountId: grant.accountId, workspaceId, action: \"api_key:create\", quantity: 1 });\n const token = generateApiKeyToken();\n const prefix = token.slice(0, 14);\n const apiKey = await createApiKey(deps.db, {\n accountId: grant.accountId,\n workspaceId: grant.workspaceId,\n name: body.name,\n prefix,\n keyHash: await sha256Hex(token),\n permissions,\n expiresAt: body.expiresAt ? new Date(body.expiresAt) : null,\n });\n return c.json(CreateApiKeyResponse.parse({ apiKey, token }), 201);\n });\n\n app.delete(\"/v1/workspaces/:workspaceId/api-keys/:apiKeyId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"api_keys:manage\");\n return c.json(await revokeApiKey(deps.db, workspaceId, c.req.param(\"apiKeyId\")));\n });\n}\n\nfunction ensureDelegablePermissions(grantPermissions: Permission[], requested: Permission[]): void {\n if (grantPermissions.includes(\"workspace:admin\")) {\n return;\n }\n const missing = requested.filter((permission) => !grantPermissions.includes(permission));\n if (missing.length > 0) {\n throw new HTTPException(403, { message: `cannot delegate missing permissions: ${missing.join(\", \")}` });\n }\n}\n\nfunction generateApiKeyToken(): string {\n const bytes = new Uint8Array(32);\n crypto.getRandomValues(bytes);\n const secret = Array.from(bytes).map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n return `ogk_${secret}`;\n}\n\nasync function sha256Hex(value: string): Promise<string> {\n const digest = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(value));\n return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n","import {\n CreateCheckoutRequest,\n CreateCheckoutResponse,\n type AccessContext,\n type Permission,\n} from \"@opengeni/contracts\";\nimport { configuredEntitlements } from \"@opengeni/config\";\nimport {\n applyCreditLedgerEntry,\n getBillingBalance,\n getBillingCustomer,\n hasCreditLedgerEntry,\n isStripeWebhookProcessed,\n listUsageEvents,\n getManagedAccount,\n markStripeWebhookProcessed,\n recordStripeWebhookEvent,\n upsertBillingCustomer,\n} from \"@opengeni/db\";\nimport type { Hono } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport Stripe from \"stripe\";\nimport { requireAccessContext } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\n\nexport function registerBillingRoutes(app: Hono, deps: ApiRouteDeps): void {\n app.get(\"/v1/billing\", async (c) => {\n const context = await requireAccessContext(c, deps);\n const accountId = requireSelectedAccount(context, c.req.query(\"accountId\"), \"billing:read\");\n return c.json({ mode: deps.settings.billingMode, balance: await getBillingBalance(deps.db, accountId) });\n });\n\n app.get(\"/v1/billing/usage\", async (c) => {\n const context = await requireAccessContext(c, deps);\n const accountId = requireSelectedAccount(context, c.req.query(\"accountId\"), \"billing:read\");\n const workspaceId = c.req.query(\"workspaceId\");\n if (workspaceId && !context.workspaceGrants.some((grant) => grant.accountId === accountId && grant.workspaceId === workspaceId)) {\n throw new HTTPException(403, { message: \"missing workspace access for usage query\" });\n }\n return c.json({\n balance: await getBillingBalance(deps.db, accountId),\n usage: await listUsageEvents(deps.db, {\n accountId,\n ...(workspaceId ? { workspaceId } : {}),\n limit: 100,\n }),\n });\n });\n\n app.get(\"/v1/billing/entitlements\", async (c) => {\n const context = await requireAccessContext(c, deps);\n const accountId = requireSelectedAccount(context, c.req.query(\"accountId\"), \"billing:read\");\n return c.json({ accountId, mode: deps.settings.entitlementsMode, entitlements: configuredEntitlements(deps.settings) });\n });\n\n app.post(\"/v1/billing/checkout\", async (c) => {\n if (deps.settings.billingMode !== \"stripe\") {\n throw new HTTPException(404, { message: \"stripe billing is not enabled\" });\n }\n const context = await requireAccessContext(c, deps);\n const parsed = CreateCheckoutRequest.safeParse(await c.req.json());\n if (!parsed.success) {\n throw new HTTPException(400, { message: parsed.error.issues[0]?.message ?? \"invalid checkout request\" });\n }\n const body = parsed.data;\n const accountId = requireSelectedAccount(context, body.accountId, \"billing:manage\");\n const amountCents = usdToCents(body.amountUsd);\n const amountMicros = centsToMicros(amountCents);\n const stripe = stripeClient(deps);\n const customerId = await getOrCreateStripeCustomer(deps, stripe, context, accountId);\n const idempotencyKey = `checkout:${accountId}:${amountMicros}:${crypto.randomUUID()}`;\n const session = await stripe.checkout.sessions.create(stripeCheckoutSessionCreateParams({\n accountId,\n customerId,\n amountCents,\n amountMicros,\n creditsProductId: deps.settings.stripeCreditsProductId,\n publicBaseUrl: deps.settings.publicBaseUrl,\n successUrl: body.successUrl,\n cancelUrl: body.cancelUrl,\n idempotencyKey,\n }), { idempotencyKey });\n if (!session.url) {\n throw new HTTPException(502, { message: \"Stripe did not return a checkout URL\" });\n }\n return c.json(CreateCheckoutResponse.parse({\n checkoutSessionId: session.id,\n url: session.url,\n }));\n });\n\n app.post(\"/v1/webhooks/stripe\", async (c) => {\n if (deps.settings.billingMode !== \"stripe\") {\n throw new HTTPException(404, { message: \"stripe billing is not enabled\" });\n }\n const signature = c.req.header(\"stripe-signature\");\n if (!signature) {\n throw new HTTPException(400, { message: \"missing stripe-signature\" });\n }\n const payload = await c.req.text();\n let event: Stripe.Event;\n try {\n event = await stripeClient(deps).webhooks.constructEventAsync(payload, signature, deps.settings.stripeWebhookSecret!);\n } catch (error) {\n throw new HTTPException(400, { message: error instanceof Error ? error.message : \"invalid stripe signature\" });\n }\n const firstSeen = await recordStripeWebhookEvent(deps.db, {\n id: event.id,\n type: event.type,\n livemode: event.livemode,\n payload: event,\n });\n if (!firstSeen) {\n if (await isStripeWebhookProcessed(deps.db, event.id)) {\n return c.json({ received: true, duplicate: true });\n }\n }\n try {\n await handleStripeWebhookEvent(deps, stripeClient(deps), event);\n await markStripeWebhookProcessed(deps.db, event.id);\n return c.json({ received: true });\n } catch (error) {\n throw new HTTPException(500, { message: error instanceof Error ? error.message : String(error) });\n }\n });\n}\n\nexport function stripeCheckoutSessionCreateParams(input: {\n accountId: string;\n customerId: string;\n amountCents: number;\n amountMicros: number;\n creditsProductId?: string | undefined;\n publicBaseUrl?: string | undefined;\n successUrl?: string | undefined;\n cancelUrl?: string | undefined;\n idempotencyKey: string;\n}): Stripe.Checkout.SessionCreateParams {\n const successUrl = checkoutReturnUrl(input.publicBaseUrl, input.successUrl, \"/billing?checkout=success\", \"successUrl\");\n const cancelUrl = checkoutReturnUrl(input.publicBaseUrl, input.cancelUrl, \"/billing?checkout=cancelled\", \"cancelUrl\");\n return {\n mode: \"payment\",\n customer: input.customerId,\n customer_update: {\n address: \"auto\",\n name: \"auto\",\n },\n success_url: successUrl,\n cancel_url: cancelUrl,\n automatic_tax: { enabled: true },\n billing_address_collection: \"auto\",\n line_items: [{\n quantity: 1,\n price_data: {\n currency: \"usd\",\n unit_amount: input.amountCents,\n ...(input.creditsProductId\n ? { product: input.creditsProductId }\n : {\n product_data: {\n name: \"OpenGeni credits\",\n metadata: {\n app: \"opengeni\",\n billing_model: \"prepaid_credits\",\n },\n },\n }),\n },\n }],\n metadata: {\n opengeni_account_id: input.accountId,\n opengeni_credit_amount_usd: (input.amountCents / 100).toFixed(2),\n opengeni_credit_micros: String(input.amountMicros),\n opengeni_credit_idempotency_key: input.idempotencyKey,\n },\n payment_intent_data: {\n metadata: {\n opengeni_account_id: input.accountId,\n opengeni_credit_amount_usd: (input.amountCents / 100).toFixed(2),\n opengeni_credit_micros: String(input.amountMicros),\n opengeni_credit_idempotency_key: input.idempotencyKey,\n },\n },\n };\n}\n\nfunction checkoutReturnUrl(publicBaseUrl: string | undefined, candidate: string | undefined, fallbackPath: string, field: string): string {\n if (!publicBaseUrl) {\n throw new HTTPException(500, { message: \"OPENGENI_PUBLIC_BASE_URL is required for Stripe checkout\" });\n }\n const base = new URL(publicBaseUrl);\n const fallback = new URL(fallbackPath, base).toString();\n if (!candidate) {\n return fallback;\n }\n const parsed = new URL(candidate);\n if (parsed.origin !== base.origin) {\n throw new HTTPException(400, { message: `${field} must use the OpenGeni public origin` });\n }\n return parsed.toString();\n}\n\nasync function handleStripeWebhookEvent(deps: ApiRouteDeps, stripe: Stripe, event: Stripe.Event): Promise<void> {\n switch (event.type) {\n case \"checkout.session.completed\":\n await handleCheckoutSessionCompleted(deps, event);\n return;\n case \"checkout.session.expired\":\n case \"payment_intent.succeeded\":\n case \"payment_intent.payment_failed\":\n case \"payment_intent.canceled\":\n await mirrorPaymentIntentCustomer(deps, event);\n return;\n case \"charge.refunded\":\n await handleChargeRefunded(deps, stripe, event);\n return;\n case \"refund.created\":\n case \"refund.updated\":\n await handleRefundEvent(deps, stripe, event);\n return;\n case \"refund.failed\":\n return;\n case \"charge.dispute.created\":\n case \"charge.dispute.funds_withdrawn\":\n await holdDisputedCredits(deps, stripe, event);\n return;\n case \"charge.dispute.closed\":\n case \"charge.dispute.funds_reinstated\":\n await releaseDisputedCredits(deps, stripe, event);\n return;\n case \"charge.dispute.updated\":\n return;\n case \"customer.created\":\n case \"customer.updated\":\n await mirrorCustomer(deps, event, event.data.object as Stripe.Customer);\n return;\n default:\n return;\n }\n}\n\nasync function handleCheckoutSessionCompleted(deps: ApiRouteDeps, event: Stripe.Event): Promise<void> {\n const session = event.data.object as Stripe.Checkout.Session;\n if (session.mode !== \"payment\" || session.payment_status !== \"paid\") {\n return;\n }\n const credit = creditMetadata(session.metadata, `Stripe checkout session ${session.id}`);\n const customerId = typeof session.customer === \"string\" ? session.customer : session.customer?.id;\n if (customerId) {\n await upsertBillingCustomer(deps.db, {\n accountId: credit.accountId,\n provider: stripeCustomerProvider(event),\n providerCustomerId: customerId,\n email: session.customer_details?.email ?? session.customer_email ?? null,\n });\n }\n await applyCreditLedgerEntry(deps.db, {\n accountId: credit.accountId,\n type: \"credit_topup\",\n amountMicros: credit.amountMicros,\n sourceType: \"stripe_checkout_session\",\n sourceId: session.id,\n idempotencyKey: credit.idempotencyKey,\n metadata: {\n stripeEventId: event.id,\n stripePaymentIntentId: typeof session.payment_intent === \"string\" ? session.payment_intent : session.payment_intent?.id ?? null,\n stripePackageId: credit.packageId,\n stripeCreditAmountUsd: credit.amountUsd,\n },\n });\n}\n\nasync function mirrorPaymentIntentCustomer(deps: ApiRouteDeps, event: Stripe.Event): Promise<void> {\n const intent = event.data.object as Stripe.PaymentIntent;\n const accountId = intent.metadata?.opengeni_account_id;\n const customerId = typeof intent.customer === \"string\" ? intent.customer : intent.customer?.id;\n if (accountId && customerId) {\n await upsertBillingCustomer(deps.db, {\n accountId,\n provider: stripeCustomerProvider(event),\n providerCustomerId: customerId,\n email: null,\n });\n }\n}\n\nasync function handleChargeRefunded(deps: ApiRouteDeps, stripe: Stripe, event: Stripe.Event): Promise<void> {\n const charge = event.data.object as Stripe.Charge;\n for (const refund of charge.refunds?.data ?? []) {\n await applyRefundDebit(deps, stripe, refund);\n }\n}\n\nasync function handleRefundEvent(deps: ApiRouteDeps, stripe: Stripe, event: Stripe.Event): Promise<void> {\n await applyRefundDebit(deps, stripe, event.data.object as Stripe.Refund);\n}\n\nasync function applyRefundDebit(deps: ApiRouteDeps, stripe: Stripe, refund: Stripe.Refund): Promise<void> {\n if (refund.status && refund.status !== \"succeeded\") {\n return;\n }\n const metadata = await metadataForRefund(stripe, refund);\n const accountId = metadata?.opengeni_account_id;\n if (!accountId) {\n return;\n }\n await applyCreditLedgerEntry(deps.db, {\n accountId,\n type: \"credit_refund\",\n amountMicros: -centsToMicros(refund.amount),\n sourceType: \"stripe_refund\",\n sourceId: refund.id,\n idempotencyKey: `stripe:refund:${refund.id}`,\n metadata: {\n stripeRefundId: refund.id,\n stripePaymentIntentId: paymentIntentId(refund.payment_intent),\n },\n });\n}\n\nasync function holdDisputedCredits(deps: ApiRouteDeps, stripe: Stripe, event: Stripe.Event): Promise<void> {\n const dispute = event.data.object as Stripe.Dispute;\n const metadata = await metadataForDispute(stripe, dispute);\n const accountId = metadata?.opengeni_account_id;\n if (!accountId) {\n return;\n }\n await applyCreditLedgerEntry(deps.db, {\n accountId,\n type: \"credit_dispute_hold\",\n amountMicros: -centsToMicros(dispute.amount),\n sourceType: \"stripe_dispute\",\n sourceId: dispute.id,\n idempotencyKey: `stripe:dispute_hold:${dispute.id}`,\n metadata: { stripeDisputeId: dispute.id, stripeEventType: event.type },\n });\n}\n\nasync function releaseDisputedCredits(deps: ApiRouteDeps, stripe: Stripe, event: Stripe.Event): Promise<void> {\n const dispute = event.data.object as Stripe.Dispute;\n if (event.type === \"charge.dispute.closed\" && dispute.status !== \"won\") {\n return;\n }\n const metadata = await metadataForDispute(stripe, dispute);\n const accountId = metadata?.opengeni_account_id;\n if (!accountId) {\n return;\n }\n const holdIdempotencyKey = `stripe:dispute_hold:${dispute.id}`;\n if (!await hasCreditLedgerEntry(deps.db, accountId, holdIdempotencyKey)) {\n return;\n }\n await applyCreditLedgerEntry(deps.db, {\n accountId,\n type: \"credit_dispute_release\",\n amountMicros: centsToMicros(dispute.amount),\n sourceType: \"stripe_dispute\",\n sourceId: dispute.id,\n idempotencyKey: `stripe:dispute_release:${dispute.id}`,\n metadata: { stripeDisputeId: dispute.id, stripeEventType: event.type },\n });\n}\n\nasync function mirrorCustomer(deps: ApiRouteDeps, event: Stripe.Event, customer: Stripe.Customer): Promise<void> {\n const accountId = customer.metadata?.opengeni_account_id;\n if (!accountId || customer.deleted) {\n return;\n }\n await upsertBillingCustomer(deps.db, {\n accountId,\n provider: stripeCustomerProvider(event),\n providerCustomerId: customer.id,\n email: typeof customer.email === \"string\" ? customer.email : null,\n });\n}\n\nasync function metadataForRefund(stripe: Stripe, refund: Stripe.Refund): Promise<Stripe.Metadata | null> {\n if (Object.keys(refund.metadata ?? {}).length > 0) {\n return refund.metadata;\n }\n const paymentIntent = paymentIntentId(refund.payment_intent);\n return paymentIntent ? (await stripe.paymentIntents.retrieve(paymentIntent)).metadata : null;\n}\n\nasync function metadataForDispute(stripe: Stripe, dispute: Stripe.Dispute): Promise<Stripe.Metadata | null> {\n if (Object.keys(dispute.metadata ?? {}).length > 0) {\n return dispute.metadata;\n }\n const paymentIntent = paymentIntentId((dispute as unknown as { payment_intent?: string | Stripe.PaymentIntent | null }).payment_intent);\n if (paymentIntent) {\n return (await stripe.paymentIntents.retrieve(paymentIntent)).metadata;\n }\n const chargeId = typeof dispute.charge === \"string\" ? dispute.charge : dispute.charge?.id;\n if (!chargeId) {\n return null;\n }\n const charge = await stripe.charges.retrieve(chargeId);\n const chargePaymentIntent = paymentIntentId(charge.payment_intent);\n return chargePaymentIntent ? (await stripe.paymentIntents.retrieve(chargePaymentIntent)).metadata : charge.metadata;\n}\n\nfunction creditMetadata(metadata: Stripe.Metadata | null | undefined, label: string): {\n accountId: string;\n amountMicros: number;\n idempotencyKey: string;\n amountUsd?: string;\n packageId?: string;\n} {\n const accountId = metadata?.opengeni_account_id;\n const amountMicros = Number(metadata?.opengeni_credit_micros);\n const idempotencyKey = metadata?.opengeni_credit_idempotency_key;\n if (!accountId || !Number.isSafeInteger(amountMicros) || amountMicros <= 0 || !idempotencyKey) {\n throw new Error(`${label} is missing OpenGeni credit metadata`);\n }\n return {\n accountId,\n amountMicros,\n idempotencyKey,\n ...(metadata?.opengeni_credit_amount_usd ? { amountUsd: metadata.opengeni_credit_amount_usd } : {}),\n ...(metadata?.opengeni_package_id ? { packageId: metadata.opengeni_package_id } : {}),\n };\n}\n\nasync function getOrCreateStripeCustomer(deps: ApiRouteDeps, stripe: Stripe, context: AccessContext, accountId: string): Promise<string> {\n const provider = stripeCustomerProvider(deps);\n const existing = await getBillingCustomer(deps.db, accountId, provider);\n if (existing) {\n return existing.providerCustomerId;\n }\n const account = await getManagedAccount(deps.db, accountId);\n if (!account) {\n throw new HTTPException(404, { message: \"account not found\" });\n }\n const customer = await stripe.customers.create({\n name: account.name,\n ...(looksLikeEmail(context.subjectLabel) ? { email: context.subjectLabel } : {}),\n metadata: {\n opengeni_account_id: accountId,\n },\n });\n await upsertBillingCustomer(deps.db, {\n accountId,\n provider,\n providerCustomerId: customer.id,\n email: customer.email,\n });\n return customer.id;\n}\n\nexport function stripeCustomerProvider(input: ApiRouteDeps | Stripe.Event): \"stripe:live\" | \"stripe:test\" {\n if (\"livemode\" in input) {\n return input.livemode ? \"stripe:live\" : \"stripe:test\";\n }\n return input.settings.stripeSecretKey?.startsWith(\"sk_live_\") || input.settings.stripeSecretKey?.startsWith(\"rk_live_\")\n ? \"stripe:live\"\n : \"stripe:test\";\n}\n\nfunction requireSelectedAccount(context: AccessContext, requested: string | undefined, permission: Permission): string {\n const accountId = requested ?? context.defaultAccountId ?? undefined;\n if (!accountId) {\n throw new HTTPException(409, { message: \"account selection is required\" });\n }\n const grant = context.accountGrants.find((candidate) => candidate.accountId === accountId);\n if (!grant || (!grant.permissions.includes(permission) && !grant.permissions.includes(\"account:admin\"))) {\n throw new HTTPException(403, { message: `missing permission: ${permission}` });\n }\n return accountId;\n}\n\nfunction stripeClient(deps: ApiRouteDeps): Stripe {\n if (!deps.settings.stripeSecretKey) {\n throw new HTTPException(500, { message: \"Stripe secret key is not configured\" });\n }\n return new Stripe(deps.settings.stripeSecretKey);\n}\n\nfunction centsToMicros(cents: number): number {\n return cents * 10_000;\n}\n\nfunction usdToCents(amountUsd: number): number {\n return Math.round(amountUsd * 100);\n}\n\nfunction paymentIntentId(value: string | Stripe.PaymentIntent | null | undefined): string | null {\n if (!value) {\n return null;\n }\n return typeof value === \"string\" ? value : value.id;\n}\n\nfunction looksLikeEmail(value: string | undefined): value is string {\n return Boolean(value && value.includes(\"@\"));\n}\n","import { GitHubAppManifestCreate } from \"@opengeni/contracts\";\nimport {\n listGitHubInstallationIdsForWorkspace,\n upsertGitHubInstallation,\n} from \"@opengeni/db\";\nimport {\n buildGitHubAppManifest,\n convertGitHubAppManifest,\n createSignedState,\n envLinesFromGitHubManifestConversion,\n GitHubAppApiError,\n GitHubAppConfigurationError,\n githubOAuthAuthorizeUrl,\n githubAppMissingSettings,\n listGitHubAppRepositories,\n organizationAppManifestUrl,\n personalAppManifestUrl,\n readSignedState,\n stateMaxAgeSeconds,\n verifyGitHubInstallationAccessForUser,\n verifySignedState,\n} from \"@opengeni/github\";\nimport type { Context, Hono } from \"hono\";\nimport { deleteCookie, getCookie, setCookie } from \"hono/cookie\";\nimport { HTTPException } from \"hono/http-exception\";\nimport { requireAccessGrant } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\n\nconst githubStateCookie = \"opengeni_github_state\";\n\nexport function registerGitHubRoutes(app: Hono, deps: ApiRouteDeps): void {\n const { db, settings, githubStateSecret } = deps;\n\n app.get(\"/v1/workspaces/:workspaceId/github/app\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"github:use\");\n const missing = githubAppMissingSettings(settings);\n const slug = settings.githubAppSlug?.trim() || null;\n const state = createSignedState(githubStateSecret, {\n accountId: grant.accountId,\n workspaceId: grant.workspaceId,\n });\n setGitHubStateCookie(c, deps, state);\n return c.json({\n configured: missing.length === 0,\n appId: settings.githubAppId ?? null,\n clientId: settings.githubClientId ?? null,\n appSlug: slug,\n installUrl: slug ? `https://github.com/apps/${slug}/installations/new?state=${encodeURIComponent(state)}` : null,\n missing,\n });\n });\n\n // Browser entry point for install links issued outside a browser context\n // (the first-party MCP github_connect_link tool): it plants the CSRF state\n // cookie the install/OAuth callbacks require and forwards to GitHub.\n // Deliberately unauthenticated: the signed state is only ever minted for\n // grants holding github:use, expires after stateMaxAgeSeconds, and is bound\n // to this workspace; completing the installation binding still requires an\n // authenticated github:manage grant in the same browser at the callback.\n app.get(\"/v1/workspaces/:workspaceId/github/connect\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const state = c.req.query(\"state\");\n if (!state) {\n throw new HTTPException(400, { message: \"missing GitHub installation state\" });\n }\n const statePayload = readSignedState(state, githubStateSecret);\n if (!statePayload || statePayload.workspaceId !== workspaceId) {\n throw new HTTPException(400, { message: \"invalid or expired GitHub installation state\" });\n }\n const slug = settings.githubAppSlug?.trim();\n if (!slug) {\n throw new HTTPException(409, { message: JSON.stringify({ message: \"GitHub App is not configured\", missing: githubAppMissingSettings(settings) }) });\n }\n setGitHubStateCookie(c, deps, state);\n return c.redirect(`https://github.com/apps/${slug}/installations/new?state=${encodeURIComponent(state)}`);\n });\n\n app.get(\"/v1/workspaces/:workspaceId/github/repositories\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"github:use\");\n try {\n return c.json({ repositories: await listWorkspaceGitHubRepositories(deps, workspaceId) });\n } catch (error) {\n if (error instanceof GitHubAppConfigurationError) {\n throw new HTTPException(409, { message: JSON.stringify({ message: error.message, missing: error.missing }) });\n }\n throw new HTTPException(502, { message: error instanceof Error ? error.message : String(error) });\n }\n });\n\n app.post(\"/v1/workspaces/:workspaceId/github/repositories/sync\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"github:use\");\n try {\n return c.json({ repositories: await listWorkspaceGitHubRepositories(deps, workspaceId) });\n } catch (error) {\n if (error instanceof GitHubAppConfigurationError) {\n throw new HTTPException(409, { message: JSON.stringify({ message: error.message, missing: error.missing }) });\n }\n throw new HTTPException(502, { message: error instanceof Error ? error.message : String(error) });\n }\n });\n\n app.post(\"/v1/workspaces/:workspaceId/github/app-manifest\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"github:manage\");\n const payload = GitHubAppManifestCreate.parse(await c.req.json());\n const baseUrl = (settings.githubAppManifestBaseUrl ?? new URL(c.req.url).origin).replace(/\\/+$/, \"\");\n const state = createSignedState(githubStateSecret, {\n accountId: grant.accountId,\n workspaceId: grant.workspaceId,\n });\n setGitHubStateCookie(c, deps, state);\n const appName = payload.appName?.trim() || \"OpenGeni\";\n const manifest = buildGitHubAppManifest({\n appName,\n baseUrl,\n public: payload.public,\n includeCiPermissions: payload.includeCiPermissions,\n setupUrl: `${baseUrl}/v1/github/setup`,\n });\n const organization = payload.organization?.trim();\n return c.json({\n actionUrl: organization ? organizationAppManifestUrl(organization, state) : personalAppManifestUrl(state),\n state,\n manifest,\n });\n });\n\n app.get(\"/v1/github/app-manifest/callback\", async (c) => {\n const code = c.req.query(\"code\");\n const state = c.req.query(\"state\");\n if (!code) {\n throw new HTTPException(400, { message: \"missing GitHub manifest code\" });\n }\n if (!state || !verifySignedState(state, githubStateSecret)) {\n throw new HTTPException(400, { message: \"invalid or expired GitHub manifest state\" });\n }\n try {\n const conversion = await convertGitHubAppManifest(code);\n const envLines = envLinesFromGitHubManifestConversion(conversion);\n const slug = String(conversion.slug ?? \"\");\n const installUrl = slug ? `https://github.com/apps/${slug}/installations/new?state=${encodeURIComponent(state)}` : \"\";\n setGitHubStateCookie(c, deps, state);\n return c.html(githubSuccessHtml(envLines, installUrl));\n } catch (error) {\n const message = error instanceof GitHubAppApiError ? error.message : String(error);\n throw new HTTPException(502, { message });\n }\n });\n\n const handleGitHubInstallCallback = async (c: Context) => {\n const code = c.req.query(\"code\");\n const state = c.req.query(\"state\");\n const installationIdRaw = c.req.query(\"installation_id\");\n const setupAction = c.req.query(\"setup_action\") ?? null;\n if (!state) {\n throw new HTTPException(400, { message: \"missing GitHub installation state\" });\n }\n const statePayload = readSignedState(state, githubStateSecret);\n if (!statePayload || typeof statePayload.accountId !== \"string\" || typeof statePayload.workspaceId !== \"string\") {\n throw new HTTPException(400, { message: \"invalid or expired GitHub installation state\" });\n }\n requireGitHubStateCookie(c, state);\n const grant = await requireAccessGrant(c, deps, statePayload.workspaceId, \"github:manage\");\n if (grant.accountId !== statePayload.accountId) {\n throw new HTTPException(403, { message: \"GitHub installation state does not match this workspace\" });\n }\n if (setupAction === \"request\" && !installationIdRaw) {\n return c.html(githubSetupPendingHtml());\n }\n const installationId = parsePositiveInteger(installationIdRaw);\n if (installationId === null) {\n throw new HTTPException(400, { message: \"missing or invalid GitHub installation_id\" });\n }\n if (!code) {\n const clientId = settings.githubClientId?.trim();\n if (!clientId) {\n throw new HTTPException(409, { message: JSON.stringify({ message: \"GitHub App is not configured\", missing: [\"OPENGENI_GITHUB_CLIENT_ID\"] }) });\n }\n const oauthState = createSignedState(githubStateSecret, {\n accountId: grant.accountId,\n workspaceId: grant.workspaceId,\n installationId,\n });\n const baseUrl = (settings.githubAppManifestBaseUrl ?? settings.publicBaseUrl ?? new URL(c.req.url).origin).replace(/\\/+$/, \"\");\n setGitHubStateCookie(c, deps, oauthState);\n return c.redirect(githubOAuthAuthorizeUrl({\n clientId,\n state: oauthState,\n redirectUri: `${baseUrl}/v1/github/oauth/callback`,\n }));\n }\n return await completeGitHubInstallationBinding(deps, c, {\n code,\n statePayload,\n installationId,\n });\n };\n\n app.get(\"/v1/github/setup\", handleGitHubInstallCallback);\n app.get(\"/v1/github/install/callback\", handleGitHubInstallCallback);\n\n app.get(\"/v1/github/oauth/callback\", async (c) => {\n const code = c.req.query(\"code\");\n const state = c.req.query(\"state\");\n if (!code) {\n throw new HTTPException(400, { message: \"missing GitHub OAuth code\" });\n }\n if (!state) {\n throw new HTTPException(400, { message: \"missing GitHub OAuth state\" });\n }\n const statePayload = readSignedState(state, githubStateSecret);\n const installationId = parsePositiveInteger(String(statePayload?.installationId ?? \"\"));\n if (!statePayload || typeof statePayload.accountId !== \"string\" || typeof statePayload.workspaceId !== \"string\" || installationId === null) {\n throw new HTTPException(400, { message: \"invalid or expired GitHub OAuth state\" });\n }\n requireGitHubStateCookie(c, state);\n return await completeGitHubInstallationBinding(deps, c, {\n code,\n statePayload,\n installationId,\n });\n });\n}\n\nasync function completeGitHubInstallationBinding(\n deps: ApiRouteDeps,\n c: Context,\n input: {\n code: string;\n statePayload: { accountId?: string; workspaceId?: string };\n installationId: number;\n },\n) {\n const { db, settings } = deps;\n if (!input.statePayload.workspaceId || !input.statePayload.accountId) {\n throw new HTTPException(400, { message: \"invalid or expired GitHub installation state\" });\n }\n const grant = await requireAccessGrant(c, deps, input.statePayload.workspaceId, \"github:manage\");\n if (grant.accountId !== input.statePayload.accountId) {\n throw new HTTPException(403, { message: \"GitHub installation state does not match this workspace\" });\n }\n try {\n const installation = await verifyGitHubInstallationAccessForUser(settings, { code: input.code, installationId: input.installationId });\n if (!installation) {\n throw new HTTPException(404, { message: \"GitHub App installation was not found for this app\" });\n }\n if (installation.suspended) {\n throw new HTTPException(409, { message: \"GitHub App installation is suspended\" });\n }\n await upsertGitHubInstallation(db, {\n accountId: grant.accountId,\n workspaceId: grant.workspaceId,\n installationId: input.installationId,\n accountLogin: installation.accountLogin,\n accountType: installation.accountType,\n });\n const returnUrl = openGeniReturnUrl(settings, c, input.statePayload.workspaceId);\n deleteCookie(c, githubStateCookie, { path: \"/v1/github\" });\n return c.html(githubSetupSuccessHtml(installation.accountLogin ?? `installation ${input.installationId}`, returnUrl));\n } catch (error) {\n if (error instanceof HTTPException) {\n throw error;\n }\n if (error instanceof GitHubAppConfigurationError) {\n throw new HTTPException(409, { message: JSON.stringify({ message: error.message, missing: error.missing }) });\n }\n throw new HTTPException(502, { message: error instanceof Error ? error.message : String(error) });\n }\n}\n\nfunction setGitHubStateCookie(c: Context, deps: ApiRouteDeps, state: string): void {\n setCookie(c, githubStateCookie, state, {\n httpOnly: true,\n sameSite: \"Lax\",\n secure: isSecureRequest(c, deps),\n path: \"/v1/github\",\n maxAge: stateMaxAgeSeconds,\n });\n}\n\nfunction requireGitHubStateCookie(c: Context, state: string): void {\n if (getCookie(c, githubStateCookie) !== state) {\n throw new HTTPException(400, { message: \"invalid or expired GitHub installation browser state\" });\n }\n}\n\nfunction isSecureRequest(c: Context, deps: ApiRouteDeps): boolean {\n return deps.settings.publicBaseUrl?.startsWith(\"https://\")\n || c.req.header(\"x-forwarded-proto\") === \"https\"\n || new URL(c.req.url).protocol === \"https:\";\n}\n\nexport async function listWorkspaceGitHubRepositories(deps: ApiRouteDeps, workspaceId: string) {\n const installationIds = await listGitHubInstallationIdsForWorkspace(deps.db, workspaceId);\n return await listGitHubAppRepositories(deps.settings, { installationIds });\n}\n\nfunction githubSuccessHtml(envLines: string[], installUrl: string): string {\n const envText = envLines.join(\"\\n\");\n const escaped = escapeHtml(envText);\n const install = installUrl ? `<a class=\"button secondary\" href=\"${escapeHtml(installUrl)}\">Install on repositories</a>` : \"\";\n return `<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>GitHub App Created</title><style>body{font-family:system-ui,sans-serif;margin:0;min-height:100vh;display:grid;place-items:center;background:#0b0b0d;color:#f4f4f5}main{width:min(760px,calc(100vw - 32px));border:1px solid #27272a;border-radius:8px;padding:28px;background:#111114}h1{margin:0 0 10px;font-size:24px;line-height:1.2}p{margin:0 0 18px;color:#d4d4d8}.env-header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:22px 0 8px}.env-header h2{margin:0;font-size:13px;line-height:1.2;text-transform:uppercase;letter-spacing:.08em;color:#a1a1aa}pre{white-space:pre-wrap;word-break:break-word;max-height:380px;overflow:auto;background:#09090b;border:1px solid #27272a;border-radius:8px;padding:16px;font-size:13px;line-height:1.5}.actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:18px}.button,button{display:inline-flex;align-items:center;justify-content:center;min-height:36px;border-radius:6px;border:1px solid #3f3f46;padding:0 12px;background:#f4f4f5;color:#09090b;font:600 14px system-ui,sans-serif;text-decoration:none;cursor:pointer}.button.secondary{background:transparent;color:#fafafa}.button.secondary:hover,button.secondary:hover{background:#27272a}button:disabled{cursor:not-allowed;opacity:.7}</style></head><body><main><h1>GitHub App created</h1><p>Add these values to .env, then restart API and worker.</p><div class=\"env-header\"><h2>Environment variables</h2><button id=\"copy-env\" type=\"button\">Copy env</button></div><pre id=\"env-lines\">${escaped}</pre><div class=\"actions\">${install}</div><script>(()=>{const button=document.getElementById(\"copy-env\");const env=document.getElementById(\"env-lines\");async function copyText(text){if(navigator.clipboard&&window.isSecureContext){await navigator.clipboard.writeText(text);return;}const area=document.createElement(\"textarea\");area.value=text;area.setAttribute(\"readonly\",\"\");area.style.position=\"fixed\";area.style.inset=\"-9999px\";document.body.append(area);area.select();document.execCommand(\"copy\");area.remove();}button?.addEventListener(\"click\",async()=>{try{await copyText(env?.textContent||\"\");button.textContent=\"Copied\";setTimeout(()=>button.textContent=\"Copy env\",1600);}catch{button.textContent=\"Copy failed\";setTimeout(()=>button.textContent=\"Copy env\",2200);}});})();</script></main></body></html>`;\n}\n\nfunction githubSetupSuccessHtml(account: string, returnUrl: string): string {\n return `<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>GitHub App Connected</title><style>body{font-family:system-ui,sans-serif;margin:0;min-height:100vh;display:grid;place-items:center;background:#0b0b0d;color:#f4f4f5}main{width:min(640px,calc(100vw - 32px));border:1px solid #27272a;border-radius:8px;padding:28px;background:#111114}h1{margin:0 0 10px;font-size:24px;line-height:1.2}p{margin:0 0 18px;color:#d4d4d8}.button{display:inline-flex;align-items:center;justify-content:center;min-height:36px;border-radius:6px;border:1px solid #3f3f46;padding:0 12px;background:#f4f4f5;color:#09090b;font:600 14px system-ui,sans-serif;text-decoration:none}.button:hover{background:#e4e4e7}</style></head><body><main><h1>GitHub App connected</h1><p>${escapeHtml(account)} is now available to this OpenGeni workspace.</p><a class=\"button\" href=\"${escapeHtml(returnUrl)}\">Back to OpenGeni</a></main></body></html>`;\n}\n\nfunction githubSetupPendingHtml(): string {\n return `<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>GitHub App Requested</title><style>body{font-family:system-ui,sans-serif;margin:0;min-height:100vh;display:grid;place-items:center;background:#0b0b0d;color:#f4f4f5}main{width:min(640px,calc(100vw - 32px));border:1px solid #27272a;border-radius:8px;padding:28px;background:#111114}h1{margin:0 0 10px;font-size:24px;line-height:1.2}p{margin:0;color:#d4d4d8}</style></head><body><main><h1>GitHub App request sent</h1><p>An organization administrator must approve the installation before OpenGeni can connect it to this workspace.</p></main></body></html>`;\n}\n\nfunction parsePositiveInteger(value: string | undefined | null): number | null {\n if (!value || !/^\\d+$/.test(value)) {\n return null;\n }\n const parsed = Number(value);\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;\n}\n\nfunction escapeHtml(value: string): string {\n return value.replace(/[&<>\"']/g, (char) => ({\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n }[char] ?? char));\n}\n\nfunction openGeniReturnUrl(settings: ApiRouteDeps[\"settings\"], c: Context, workspaceId: string | undefined): string {\n const base = (settings.publicBaseUrl ?? new URL(c.req.url).origin).replace(/\\/+$/, \"\");\n const url = new URL(base || new URL(c.req.url).origin);\n if (workspaceId) {\n url.searchParams.set(\"workspaceId\", workspaceId);\n }\n return url.toString();\n}\n","import {\n EnablePackRequest,\n MarketingDailyAnalysisTaskRequest,\n RegisterCapabilityPackRequest,\n type SocialConnection,\n} from \"@opengeni/contracts\";\nimport {\n deleteWorkspacePack,\n disableCapabilityInstallation,\n enablePackInstallation,\n getCapabilityInstallation,\n getPackInstallation,\n getWorkspacePack,\n getSocialConnection,\n listPackInstallations,\n listSocialConnections,\n registerWorkspacePack,\n updatePackInstallationStatus,\n} from \"@opengeni/db\";\nimport { getDocumentBase } from \"@opengeni/documents\";\nimport type { Hono } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport { requireAccessGrant } from \"@opengeni/core\";\nimport { requireLimit } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\nimport { validateEnvironmentAttachment } from \"@opengeni/core\";\nimport {\n assertPackSandboxImageCompatible,\n buildMarketingDailyAnalysisAgentConfig,\n isBuiltInCapabilityPack,\n listWorkspaceCapabilityPacks,\n MARKETING_SOCIAL_PACK_ID,\n resolveCapabilityPack,\n} from \"@opengeni/core\";\nimport {\n createValidatedScheduledTask,\n syncCreatedScheduledTask,\n} from \"@opengeni/core\";\n\nexport function registerPackRoutes(app: Hono, deps: ApiRouteDeps): void {\n const { settings, db, objectStorage, workflowClient } = deps;\n\n app.get(\"/v1/workspaces/:workspaceId/packs\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n return c.json({\n packs: await listWorkspaceCapabilityPacks(db, workspaceId),\n installations: await listPackInstallations(db, workspaceId),\n });\n });\n\n // Registers (or replaces) a workspace-scoped pack from a manifest payload.\n // Built-in pack ids stay reserved so a registration can never shadow them.\n app.post(\"/v1/workspaces/:workspaceId/packs\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const manifest = RegisterCapabilityPackRequest.parse(await c.req.json());\n if (isBuiltInCapabilityPack(manifest.id)) {\n throw new HTTPException(409, { message: `pack id ${manifest.id} is a built-in pack and cannot be replaced` });\n }\n const { pack, created } = await registerWorkspacePack(db, {\n accountId: grant.accountId,\n workspaceId,\n pack: manifest,\n });\n return c.json(pack, created ? 201 : 200);\n });\n\n app.delete(\"/v1/workspaces/:workspaceId/packs/:packId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const packId = c.req.param(\"packId\");\n if (isBuiltInCapabilityPack(packId)) {\n throw new HTTPException(409, { message: \"built-in packs cannot be unregistered\" });\n }\n if (!await getWorkspacePack(db, workspaceId, packId)) {\n throw new HTTPException(404, { message: \"pack not found\" });\n }\n // Disable installations before deleting the registration so a crash in\n // between can never orphan an active installation whose manifest is\n // gone; the capability installation row for pack:{packId} would\n // otherwise keep a future re-registration looking enabled.\n const installation = await getPackInstallation(db, workspaceId, packId);\n if (installation && installation.status === \"active\") {\n await updatePackInstallationStatus(db, workspaceId, packId, \"disabled\");\n }\n const capabilityInstallation = await getCapabilityInstallation(db, workspaceId, `pack:${packId}`);\n if (capabilityInstallation && capabilityInstallation.status === \"active\") {\n await disableCapabilityInstallation(db, workspaceId, `pack:${packId}`);\n }\n await deleteWorkspacePack(db, workspaceId, packId);\n return c.body(null, 204);\n });\n\n app.get(\"/v1/workspaces/:workspaceId/packs/installations\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n return c.json(await listPackInstallations(db, workspaceId));\n });\n\n app.get(\"/v1/workspaces/:workspaceId/packs/:packId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n const pack = await requirePack(db, workspaceId, c.req.param(\"packId\"));\n return c.json({\n pack,\n installation: await getPackInstallation(db, workspaceId, pack.id),\n });\n });\n\n app.post(\"/v1/workspaces/:workspaceId/packs/:packId/enable\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const pack = await requirePack(db, workspaceId, c.req.param(\"packId\"));\n await assertPackSandboxImageCompatible(db, workspaceId, pack);\n const existing = await getPackInstallation(db, workspaceId, pack.id);\n const payload = EnablePackRequest.parse(await c.req.json());\n // Re-enabling without environmentId keeps the stored attachment instead of\n // silently dropping it; the inherited attachment is re-validated below in\n // case the environment was deleted or its variables changed since. The\n // inherited attachment was authorized with environments:use when it was\n // first attached, so only a fresh attachment re-checks that permission.\n const storedEnvironmentId = typeof existing?.metadata.environmentId === \"string\" ? existing.metadata.environmentId : undefined;\n const environmentId = payload.environmentId ?? storedEnvironmentId;\n if (pack.environment?.required && !environmentId) {\n throw new HTTPException(422, { message: \"this pack requires an environment attachment; pass environmentId\" });\n }\n if (environmentId) {\n const environment = await validateEnvironmentAttachment({ settings, db }, grant, workspaceId, environmentId, { preauthorized: !payload.environmentId });\n const missing = (pack.environment?.requiredVariables ?? [])\n .filter((name) => !environment.variables.some((variable) => variable.name === name));\n if (missing.length > 0) {\n throw new HTTPException(422, { message: `environment is missing required variable(s): ${missing.join(\", \")}` });\n }\n }\n const installation = await enablePackInstallation(db, {\n accountId: grant.accountId,\n workspaceId,\n packId: pack.id,\n metadata: {\n ...payload.metadata,\n packVersion: pack.version,\n ...(environmentId ? { environmentId } : {}),\n },\n });\n return c.json(installation, existing ? 200 : 201);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/packs/marketing-social-daily-analysis/scheduled-tasks\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"scheduled_tasks:manage\");\n const pack = await requirePack(db, workspaceId, MARKETING_SOCIAL_PACK_ID);\n const installation = await getPackInstallation(db, workspaceId, pack.id);\n if (installation?.status !== \"active\") {\n throw new HTTPException(409, { message: \"enable the marketing social pack before creating its scheduled tasks\" });\n }\n const payload = MarketingDailyAnalysisTaskRequest.parse(await c.req.json());\n await requireLimit(deps, { accountId: grant.accountId, workspaceId, action: \"schedule:create\", quantity: 1 });\n const connections = await resolveSocialConnections(db, workspaceId, payload.connectionIds);\n if (connections.length === 0) {\n throw new HTTPException(422, { message: \"at least one connected social account is required\" });\n }\n await validateDocumentBaseIds(db, workspaceId, payload.documentBaseIds);\n const agentConfig = buildMarketingDailyAnalysisAgentConfig({\n connections,\n documentBaseIds: payload.documentBaseIds,\n ...(payload.promptInstructions ? { promptInstructions: payload.promptInstructions } : {}),\n });\n // Installation-inherited environment attachment: it was authorized with\n // environments:use at pack-enable time, so the scheduled_tasks:manage\n // caller here is not re-checked for that permission.\n const installationEnvironmentId = typeof installation.metadata.environmentId === \"string\"\n ? installation.metadata.environmentId\n : undefined;\n const task = await createValidatedScheduledTask({\n settings,\n db,\n objectStorage,\n grant,\n environmentPreauthorized: true,\n payload: {\n name: payload.name ?? \"Daily social media analysis\",\n status: payload.status,\n schedule: {\n type: \"calendar\",\n timeZone: payload.timeZone,\n hour: payload.hour,\n minute: payload.minute,\n },\n runMode: payload.runMode,\n overlapPolicy: payload.overlapPolicy,\n agentConfig,\n ...(installationEnvironmentId ? { environmentId: installationEnvironmentId } : {}),\n metadata: {\n packId: pack.id,\n packVersion: pack.version,\n packTemplateId: \"daily-social-analysis\",\n socialConnectionIds: connections.map((connection) => connection.id),\n documentBaseIds: payload.documentBaseIds,\n },\n },\n });\n await syncCreatedScheduledTask({ db, workflowClient, task });\n return c.json(task, 201);\n });\n}\n\nasync function requirePack(db: ApiRouteDeps[\"db\"], workspaceId: string, packId: string) {\n const pack = await resolveCapabilityPack(db, workspaceId, packId);\n if (!pack) {\n throw new HTTPException(404, { message: \"pack not found\" });\n }\n return pack;\n}\n\nasync function resolveSocialConnections(db: ApiRouteDeps[\"db\"], workspaceId: string, connectionIds: string[]): Promise<SocialConnection[]> {\n const ids = [...new Set(connectionIds)];\n const connections = ids.length > 0\n ? await Promise.all(ids.map(async (id) => {\n const connection = await getSocialConnection(db, workspaceId, id);\n if (!connection) {\n throw new HTTPException(422, { message: `unknown social connection: ${id}` });\n }\n return connection;\n }))\n : (await listSocialConnections(db, workspaceId, 500)).filter((connection) => connection.status === \"connected\");\n const inactive = connections.find((connection) => connection.status !== \"connected\");\n if (inactive) {\n throw new HTTPException(422, { message: `social connection ${inactive.id} is ${inactive.status}` });\n }\n return connections;\n}\n\nasync function validateDocumentBaseIds(db: ApiRouteDeps[\"db\"], workspaceId: string, documentBaseIds: string[]): Promise<void> {\n for (const baseId of [...new Set(documentBaseIds)]) {\n const base = await getDocumentBase(db, workspaceId, baseId);\n if (!base) {\n throw new HTTPException(422, { message: `unknown document base: ${baseId}` });\n }\n }\n}\n","import { CreateScheduledTaskRequest, TriggerScheduledTaskRequest, UpdateScheduledTaskRequest } from \"@opengeni/contracts\";\nimport {\n deleteScheduledTask,\n listScheduledTaskRuns,\n listScheduledTasks,\n updateScheduledTask,\n} from \"@opengeni/db\";\nimport type { Hono } from \"hono\";\nimport { requireAccessGrant } from \"@opengeni/core\";\nimport { recordWorkspaceUsage, requireLimit } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\nimport {\n createValidatedScheduledTask,\n manualScheduledTaskTriggerUsageKey,\n manualScheduledTaskTriggerWorkflowId,\n scheduledTaskToolsProvided,\n scheduledTaskTriggerToken,\n requireScheduledTaskForApi,\n syncCreatedScheduledTask,\n syncUpdatedScheduledTask,\n validatedScheduledTaskUpdate,\n} from \"@opengeni/core\";\nimport { boundedLimit } from \"../http/common\";\n\nexport function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void {\n const { settings, db, workflowClient, objectStorage } = deps;\n\n app.post(\"/v1/workspaces/:workspaceId/scheduled-tasks\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"scheduled_tasks:manage\");\n const rawPayload = await c.req.json();\n const payload = CreateScheduledTaskRequest.parse(rawPayload);\n await requireLimit(deps, { accountId: grant.accountId, workspaceId, action: \"schedule:create\", quantity: 1 });\n const task = await createValidatedScheduledTask({ settings, db, objectStorage, grant, payload, toolsProvided: scheduledTaskToolsProvided(rawPayload) });\n await syncCreatedScheduledTask({ db, workflowClient, task });\n return c.json(task, 201);\n });\n\n app.get(\"/v1/workspaces/:workspaceId/scheduled-tasks\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"scheduled_tasks:run\");\n return c.json(await listScheduledTasks(db, workspaceId, boundedLimit(c.req.query(\"limit\"))));\n });\n\n app.get(\"/v1/workspaces/:workspaceId/scheduled-tasks/:taskId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"scheduled_tasks:run\");\n return c.json(await requireScheduledTaskForApi(db, workspaceId, c.req.param(\"taskId\")));\n });\n\n app.patch(\"/v1/workspaces/:workspaceId/scheduled-tasks/:taskId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"scheduled_tasks:manage\");\n const taskId = c.req.param(\"taskId\");\n const existing = await requireScheduledTaskForApi(db, workspaceId, taskId);\n const rawPayload = await c.req.json();\n const payload = UpdateScheduledTaskRequest.parse(rawPayload);\n const update = await validatedScheduledTaskUpdate({ settings, db, objectStorage, grant, existing, payload, toolsProvided: scheduledTaskToolsProvided(rawPayload) });\n const task = await updateScheduledTask(db, workspaceId, taskId, update);\n await syncUpdatedScheduledTask({ db, workflowClient, previous: existing, task });\n return c.json(task);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/scheduled-tasks/:taskId/pause\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"scheduled_tasks:manage\");\n const existing = await requireScheduledTaskForApi(db, workspaceId, c.req.param(\"taskId\"));\n const task = await updateScheduledTask(db, workspaceId, existing.id, { status: \"paused\" });\n await syncUpdatedScheduledTask({ db, workflowClient, previous: existing, task });\n return c.json(task);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/scheduled-tasks/:taskId/resume\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"scheduled_tasks:manage\");\n const existing = await requireScheduledTaskForApi(db, workspaceId, c.req.param(\"taskId\"));\n const task = await updateScheduledTask(db, workspaceId, existing.id, { status: \"active\" });\n await syncUpdatedScheduledTask({ db, workflowClient, previous: existing, task });\n return c.json(task);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/scheduled-tasks/:taskId/trigger\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"scheduled_tasks:run\");\n // Load the task before the gate so a codex-model scheduled task can be\n // recognised as codex-billed and skip the credit/cost gates at the edge.\n const task = await requireScheduledTaskForApi(db, workspaceId, c.req.param(\"taskId\"));\n await requireLimit(deps, { accountId: grant.accountId, workspaceId, action: \"agent_run:create\", quantity: 1, model: task.agentConfig.model ?? deps.settings.openaiModel });\n // Body is optional (a bare POST is still a valid trigger); only a present,\n // non-empty body must parse against the contract.\n const body = await c.req.json().catch(() => ({}));\n const { triggerId } = TriggerScheduledTaskRequest.parse(body ?? {});\n const triggerToken = scheduledTaskTriggerToken(triggerId);\n const agentRunUsageIdempotencyKey = manualScheduledTaskTriggerUsageKey(workspaceId, task.id, triggerToken);\n const triggerWorkflowId = manualScheduledTaskTriggerWorkflowId(task.id, triggerToken);\n await workflowClient.triggerScheduledTask({ task, agentRunUsageIdempotencyKey, triggerWorkflowId });\n await recordWorkspaceUsage(deps, {\n accountId: grant.accountId,\n workspaceId,\n subjectId: grant.subjectId,\n eventType: \"agent_run.created\",\n quantity: 1,\n unit: \"run\",\n sourceResourceType: \"scheduled_task\",\n sourceResourceId: task.id,\n idempotencyKey: agentRunUsageIdempotencyKey,\n });\n return c.json(task, 202);\n });\n\n app.delete(\"/v1/workspaces/:workspaceId/scheduled-tasks/:taskId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"scheduled_tasks:manage\");\n const task = await requireScheduledTaskForApi(db, workspaceId, c.req.param(\"taskId\"));\n await workflowClient.deleteScheduledTaskSchedule({ temporalScheduleId: task.temporalScheduleId });\n await deleteScheduledTask(db, workspaceId, task.id);\n return c.json({ ok: true });\n });\n\n app.get(\"/v1/workspaces/:workspaceId/scheduled-tasks/:taskId/runs\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"scheduled_tasks:run\");\n const task = await requireScheduledTaskForApi(db, workspaceId, c.req.param(\"taskId\"));\n return c.json(await listScheduledTaskRuns(db, workspaceId, task.id, boundedLimit(c.req.query(\"limit\"))));\n });\n}\n","import {\n AcknowledgeStreamRequest,\n AttachViewerRequest,\n ClearSessionContextRequest,\n ClientSessionEvent,\n CompactSessionContextRequest,\n FsDeleteRequest,\n FsListRequest,\n FsMkdirRequest,\n FsMoveRequest,\n FsReadRequest,\n FsWriteRequest,\n GitDiffRequest,\n GitLogRequest,\n GitShowRequest,\n GitStatusRequest,\n PtyCloseRequest,\n PtyOpenRequest,\n PtyResizeRequest,\n PtyWriteRequest,\n ReorderSessionTurnsRequest,\n TerminalExecRequest,\n UpdateSessionGoalRequest,\n UpdateSessionRequest,\n UpdateSessionTurnRequest,\n ViewerHeartbeatRequest,\n type SandboxBackend,\n type Session,\n type TerminalPtyExitedPayload,\n type TerminalPtyOutputDeltaPayload,\n type TerminalPtyStartedPayload,\n} from \"@opengeni/contracts\";\nimport { resolveContextCompactionMode, streamTokenDegraded } from \"@opengeni/config\";\nimport {\n cancelQueuedSessionTurn,\n clearSessionContext,\n closePtySession,\n getOpenPtySession,\n getSandbox,\n getSession,\n getSessionGoal,\n getStreamAcknowledgment,\n insertPtySession,\n listSessionEvents,\n listSessionIdsInGroup,\n listSessions,\n listSessionTurns,\n recordStreamAcknowledgment,\n reorderQueuedSessionTurns,\n requestSessionCompaction,\n requireSession,\n setSessionCodexPin,\n revokeViewer,\n setSessionGoalStatus,\n updatePtySessionActivity,\n updateQueuedSessionTurn,\n type AppendEventInput,\n} from \"@opengeni/db\";\nimport { appendAndPublishEvents, coalesceSessionEventDeltas } from \"@opengeni/events\";\nimport { withChannelA } from \"../sandbox/channel-a\";\nimport { negotiateCapabilities } from \"@opengeni/runtime/sandbox\";\nimport type { Context, Hono } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport { requireAccessGrant } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\nimport { attachViewer, detachViewer, heartbeatViewer, mintDesktopStream, mintTerminalStream, readGroupLease, resolveActiveDesktopTransport, viewerHeartbeatIntervalMs, type DesktopStreamMint, type TerminalStreamMint } from \"../sandbox/viewer\";\nimport { settingsWithEnabledCapabilityMcpServers, settingsWithSessionMcpServerMetadata } from \"@opengeni/core\";\nimport {\n normalizeResources,\n validateFileResources,\n validateGitHubRepositorySelection,\n validateToolRefs,\n} from \"@opengeni/core\";\nimport {\n acceptSessionUserMessage,\n assertConfiguredModel,\n createSessionForRequest,\n requireQueuedTurnForApi,\n updateSessionTitle,\n workflowIdForSession,\n} from \"@opengeni/core\";\nimport { assertSessionExists, boundedLimit } from \"../http/common\";\nimport { sseSessionStream } from \"../http/sse\";\n\nexport function registerSessionRoutes(app: Hono, deps: ApiRouteDeps): void {\n const { settings, db, bus, workflowClient, objectStorage } = deps;\n\n app.post(\"/v1/workspaces/:workspaceId/sessions\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"sessions:create\");\n const session = await createSessionForRequest(deps, grant, workspaceId, await c.req.json());\n return c.json(session, 202);\n });\n\n app.get(\"/v1/workspaces/:workspaceId/sessions\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"sessions:read\");\n return c.json(await listSessions(db, workspaceId, boundedLimit(c.req.query(\"limit\"))));\n });\n\n app.get(\"/v1/workspaces/:workspaceId/sessions/:sessionId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"sessions:read\");\n const session = await getSession(db, workspaceId, c.req.param(\"sessionId\"));\n if (!session) {\n throw new HTTPException(404, { message: \"session not found\" });\n }\n return c.json(session);\n });\n\n // Pin (or unpin) the session's Codex account. body { target: \"auto\" | \"<id>\" }:\n // \"auto\" clears the pin (the session follows the workspace active pointer); a\n // uuid pins the session to that specific account. The pin applies to the NEXT\n // turn (the worker reads it at turn start). 404 when the session or the target\n // account id isn't in the workspace.\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/codex-account\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"sessions:control\");\n const sessionId = c.req.param(\"sessionId\");\n const body = (await c.req.json()) as { target?: string };\n const target = typeof body.target === \"string\" ? body.target : \"\";\n if (!target) {\n throw new HTTPException(400, { message: \"target is required (\\\"auto\\\" or an account id)\" });\n }\n const pinned = target === \"auto\" ? null : target;\n const ok = await setSessionCodexPin(db, workspaceId, sessionId, pinned);\n if (!ok) {\n throw new HTTPException(404, { message: \"session or codex account not found\" });\n }\n return c.json({ pinned: target === \"auto\" ? \"auto\" : target });\n });\n\n // Manual rename. A user-set title is permanent: the db write is\n // unconditional (source='user'), so it always pins the session over later\n // agent writes. Returns the refreshed session, mirroring GET detail.\n app.patch(\"/v1/workspaces/:workspaceId/sessions/:sessionId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"sessions:control\");\n const sessionId = c.req.param(\"sessionId\");\n await assertSessionExists(db, workspaceId, sessionId);\n const payload = UpdateSessionRequest.parse(await c.req.json());\n await updateSessionTitle({ db, bus }, workspaceId, sessionId, payload.title, \"user\");\n const session = await getSession(db, workspaceId, sessionId);\n if (!session) {\n throw new HTTPException(404, { message: \"session not found\" });\n }\n return c.json(session);\n });\n\n app.get(\"/v1/workspaces/:workspaceId/sessions/:sessionId/goal\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"sessions:read\");\n const sessionId = c.req.param(\"sessionId\");\n await assertSessionExists(db, workspaceId, sessionId);\n const goal = await getSessionGoal(db, workspaceId, sessionId);\n if (!goal) {\n throw new HTTPException(404, { message: \"session goal not found\" });\n }\n return c.json(goal);\n });\n\n app.patch(\"/v1/workspaces/:workspaceId/sessions/:sessionId/goal\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"sessions:control\");\n const sessionId = c.req.param(\"sessionId\");\n await assertSessionExists(db, workspaceId, sessionId);\n const payload = UpdateSessionGoalRequest.parse(await c.req.json());\n const existing = await getSessionGoal(db, workspaceId, sessionId);\n if (!existing) {\n throw new HTTPException(404, { message: \"session goal not found\" });\n }\n if (existing.status === \"completed\") {\n throw new HTTPException(409, { message: \"session goal is completed; set a new goal instead\" });\n }\n if (payload.status === \"paused\") {\n const { goal, changed } = await setSessionGoalStatus(db, workspaceId, sessionId, {\n status: \"paused\",\n ...(payload.rationale ? { rationale: payload.rationale } : {}),\n pausedReason: \"api\",\n });\n if (changed) {\n await appendAndPublishEvents(db, bus, workspaceId, sessionId, [{\n type: \"goal.paused\",\n payload: {\n goalId: goal.id,\n actor: \"api\",\n reason: \"api\",\n ...(payload.rationale ? { rationale: payload.rationale } : {}),\n autoContinuations: goal.autoContinuations,\n noProgressStreak: goal.noProgressStreak,\n },\n }]);\n }\n return c.json(goal);\n }\n // Resume: only valid from paused; resets counters and re-arms the loop.\n if (existing.status !== \"paused\") {\n throw new HTTPException(409, { message: `session goal is ${existing.status}; only paused goals can be resumed` });\n }\n const { goal, changed } = await setSessionGoalStatus(db, workspaceId, sessionId, { status: \"active\" });\n // `changed` guards the racing-PATCH case: both requests can pass the\n // status pre-check, but only the transition winner emits and wakes.\n if (changed) {\n await appendAndPublishEvents(db, bus, workspaceId, sessionId, [{\n type: \"goal.resumed\",\n payload: {\n goalId: goal.id,\n text: goal.text,\n ...(goal.successCriteria ? { successCriteria: goal.successCriteria } : {}),\n version: goal.version,\n actor: \"api\",\n },\n }]);\n // signalWithStart restarts a completed workflow whose first claim finds no\n // queued turn, so maybeContinueGoal fires — resume works on an idle session.\n await workflowClient.wakeSessionWorkflow({ accountId: grant.accountId, workspaceId, sessionId, workflowId: workflowIdForSession(sessionId) });\n }\n return c.json(goal);\n });\n\n // Operator context controls (slash-command palette: /clear, /compact). These\n // are session/operator actions — NOT a structured channel to the agent. Both\n // require sessions:control.\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/context/clear\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"sessions:control\");\n const sessionId = c.req.param(\"sessionId\");\n await assertSessionExists(db, workspaceId, sessionId);\n // Explicit confirm on the wire (literal true) — an empty/accidental POST\n // cannot wipe context. Mirrors the client-side confirm affordance. A\n // missing/false confirm is a client error (400), not a server fault.\n const clearBody = ClearSessionContextRequest.safeParse(await c.req.json().catch(() => ({})));\n if (!clearBody.success) {\n throw new HTTPException(400, { message: \"context clear requires an explicit { confirm: true }\" });\n }\n const session = await requireSession(db, workspaceId, sessionId);\n // Clearing mid-turn would strand the in-flight RunState (and, in\n // requires_action, an awaiting approval whose resume needs that blob).\n // Refuse, mirroring the goal 409 guards.\n if (session.status === \"queued\" || session.status === \"running\" || session.status === \"requires_action\") {\n throw new HTTPException(409, { message: `session is ${session.status}; cannot clear context mid-turn — stop the turn first` });\n }\n const result = await clearSessionContext(db, { accountId: grant.accountId, workspaceId, sessionId });\n await appendAndPublishEvents(db, bus, workspaceId, sessionId, [{\n type: \"session.context.cleared\",\n payload: {\n clearedBy: \"api\",\n supersededItems: result.supersededItems,\n markerPosition: result.markerPosition,\n },\n }]);\n return c.body(null, 204);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/context/compact\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"sessions:control\");\n const sessionId = c.req.param(\"sessionId\");\n await assertSessionExists(db, workspaceId, sessionId);\n CompactSessionContextRequest.parse((await c.req.json().catch(() => ({}))) ?? {});\n // /compact is only a TRIGGER — it never duplicates the compaction engine.\n // Client-managed (Azure) path: set a durable request flag the worker honors\n // before the next turn (forced compaction). Server-managed provider / off:\n // compaction is automatic or disabled, so this is an honest no-op.\n //\n // Integration seam with provider-aware-compaction: when that work exposes a\n // synchronous \"compact now\" entry callable from the API process, this route\n // should call it directly and return its result; until then the flag +\n // worker maybeCompactContext(force) is the minimal honored interface.\n const mode = resolveContextCompactionMode(settings);\n if (mode === \"client\") {\n await requestSessionCompaction(db, workspaceId, sessionId);\n return c.json({ status: \"queued\", message: \"Compaction will run before the next turn.\" });\n }\n if (mode === \"server\") {\n return c.json({ status: \"noop\", message: \"This session's provider compacts context automatically; no manual compaction is needed.\" });\n }\n return c.json({ status: \"noop\", message: \"Context compaction is disabled for this session.\" });\n });\n\n app.get(\"/v1/workspaces/:workspaceId/sessions/:sessionId/events\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"sessions:read\");\n const sessionId = c.req.param(\"sessionId\");\n await assertSessionExists(db, workspaceId, sessionId);\n const after = eventSequence(c.req.query(\"after\"), 0);\n const before = optionalEventSequence(c.req.query(\"before\"));\n const compact = compactEvents(c.req.query(\"compact\"));\n const limit = eventListLimit(c.req.query(\"limit\"), compact ? 5000 : 2000);\n const events = await listSessionEvents(db, workspaceId, sessionId, {\n after,\n ...(before !== undefined ? { before } : {}),\n limit,\n });\n return c.json(compact ? coalesceSessionEventDeltas(events) : events);\n });\n\n app.get(\"/v1/workspaces/:workspaceId/sessions/:sessionId/events/stream\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"sessions:read\");\n const sessionId = c.req.param(\"sessionId\");\n await assertSessionExists(db, workspaceId, sessionId);\n const after = Number(c.req.query(\"after\") ?? c.req.header(\"Last-Event-ID\") ?? 0);\n return sseSessionStream(db, bus, workspaceId, sessionId, Number.isFinite(after) ? after : 0, c.req.raw.signal);\n });\n\n app.get(\"/v1/workspaces/:workspaceId/sessions/:sessionId/turns\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"sessions:read\");\n const sessionId = c.req.param(\"sessionId\");\n await assertSessionExists(db, workspaceId, sessionId);\n return c.json(await listSessionTurns(db, workspaceId, sessionId, boundedLimit(c.req.query(\"limit\"))));\n });\n\n app.patch(\"/v1/workspaces/:workspaceId/sessions/:sessionId/turns/:turnId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"sessions:control\");\n const sessionId = c.req.param(\"sessionId\");\n const turnId = c.req.param(\"turnId\");\n await assertSessionExists(db, workspaceId, sessionId);\n const existing = await requireQueuedTurnForApi(db, workspaceId, sessionId, turnId);\n const payload = UpdateSessionTurnRequest.parse(await c.req.json());\n // A turn-update may switch the queued turn's model; reject one the host\n // does not expose (omitted leaves the existing model unchanged).\n assertConfiguredModel(settings, payload.model);\n const session = await requireSession(db, workspaceId, sessionId);\n const runtimeSettings = settingsWithSessionMcpServerMetadata(\n await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings),\n session.mcpServers,\n );\n const resources = payload.resources !== undefined ? normalizeResources(payload.resources) : existing.resources;\n const tools = payload.tools !== undefined ? validateToolRefs(payload.tools, runtimeSettings) : existing.tools;\n if (resources.some((resource) => resource.kind === \"file\") && !objectStorage) {\n throw new HTTPException(503, { message: \"object storage is not configured\" });\n }\n await validateFileResources(db, workspaceId, resources);\n await validateGitHubRepositorySelection(db, workspaceId, [...session.resources, ...resources]);\n const turn = await updateQueuedSessionTurn(db, workspaceId, turnId, {\n ...(payload.prompt !== undefined ? { prompt: payload.prompt.trim() } : {}),\n ...(payload.model !== undefined ? { model: payload.model } : {}),\n ...(payload.reasoningEffort !== undefined ? { reasoningEffort: payload.reasoningEffort } : {}),\n ...(payload.sandboxBackend !== undefined ? { sandboxBackend: payload.sandboxBackend } : {}),\n ...(payload.metadata !== undefined ? { metadata: payload.metadata } : {}),\n resources,\n tools,\n });\n await appendAndPublishEvents(db, bus, workspaceId, sessionId, [{\n type: \"turn.updated\",\n turnId: turn.id,\n payload: { turnId: turn.id },\n }]);\n return c.json(turn);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/turns/reorder\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"sessions:control\");\n const sessionId = c.req.param(\"sessionId\");\n await assertSessionExists(db, workspaceId, sessionId);\n const payload = ReorderSessionTurnsRequest.parse(await c.req.json());\n const turns = await reorderQueuedSessionTurns(db, workspaceId, sessionId, payload.turnIds);\n await appendAndPublishEvents(db, bus, workspaceId, sessionId, [{\n type: \"turn.updated\",\n payload: { reorderedTurnIds: payload.turnIds },\n }]);\n await workflowClient.wakeSessionWorkflow({ accountId: grant.accountId, workspaceId, sessionId, workflowId: workflowIdForSession(sessionId) });\n return c.json(turns);\n });\n\n app.delete(\"/v1/workspaces/:workspaceId/sessions/:sessionId/turns/:turnId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"sessions:control\");\n const sessionId = c.req.param(\"sessionId\");\n const turnId = c.req.param(\"turnId\");\n await assertSessionExists(db, workspaceId, sessionId);\n await requireQueuedTurnForApi(db, workspaceId, sessionId, turnId);\n const turn = await cancelQueuedSessionTurn(db, workspaceId, turnId);\n await appendAndPublishEvents(db, bus, workspaceId, sessionId, [{\n type: \"turn.cancelled\",\n turnId: turn.id,\n payload: { turnId: turn.id, triggerEventId: turn.triggerEventId },\n }]);\n return c.json(turn);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/events\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"sessions:control\");\n const sessionId = c.req.param(\"sessionId\");\n const rawEvent = await c.req.json();\n const event = ClientSessionEvent.parse(rawEvent);\n if (event.type === \"user.message\") {\n const { accepted } = await acceptSessionUserMessage(deps, grant, workspaceId, sessionId, {\n text: event.payload.text,\n resources: event.payload.resources ?? [],\n tools: event.payload.tools ?? [],\n toolsProvided: userMessagePayloadHasOwnProperty(rawEvent, \"tools\"),\n model: event.payload.model ?? null,\n reasoningEffort: event.payload.reasoningEffort ?? null,\n mcpCredentialUpdates: event.payload.mcpCredentialUpdates ?? [],\n ...(event.clientEventId ? { clientEventId: event.clientEventId } : {}),\n });\n return c.json(accepted, 202);\n }\n\n const session = await requireSession(db, workspaceId, sessionId);\n if (event.type === \"user.approvalDecision\" && session.status !== \"requires_action\") {\n throw new HTTPException(409, { message: `session is ${session.status}; no approval is pending` });\n }\n const eventsToAppend: AppendEventInput[] = [{\n type: event.type,\n payload: event.payload,\n ...(event.clientEventId ? { clientEventId: event.clientEventId } : {}),\n }];\n const appended = await appendAndPublishEvents(db, bus, workspaceId, sessionId, eventsToAppend);\n const accepted = appended[0];\n if (!accepted) {\n throw new HTTPException(500, { message: \"failed to append client event\" });\n }\n const workflowId = workflowIdForSession(sessionId);\n if (event.type === \"user.approvalDecision\") {\n await workflowClient.signalApprovalDecision({ sessionId, eventId: accepted.id, workflowId });\n } else {\n await workflowClient.signalInterrupt({\n accountId: grant.accountId,\n workspaceId,\n sessionId,\n eventId: accepted.id,\n workflowId,\n });\n }\n return c.json(accepted, 202);\n });\n\n // ── API-direct stream capabilities + viewer attach (P1.4) ─────────────────\n //\n // All IN-PROCESS: capability negotiation reads the descriptor + the group\n // lease (liveness/epoch); viewer attach acquires a holder on the group lease\n // and (when cold) spins the box up via resume-by-id — NO worker, NO Temporal.\n // Gated behind sandboxOwnershipEnabled (the lease is inert with the flag off).\n //\n // ROUTE DISCIPLINE: requireAccessGrant BEFORE any Zod parse; explicit\n // HTTPException(400) on a parse failure (never a raw ZodError → 500);\n // HTTPException(409) on an epoch fence.\n\n function assertOwnershipEnabled(): void {\n if (!settings.sandboxOwnershipEnabled) {\n // The viewer-holder lifecycle rides the sandbox lease, which is dormant\n // until the flag flips per-environment. A 404 (not 403) keeps the route\n // invisible while disabled — it does not exist for this deployment yet.\n throw new HTTPException(404, { message: \"sandbox ownership is not enabled for this deployment\" });\n }\n }\n\n // Resolve the shared-exposure disclosure for a session's group: `shared` when\n // the group has >1 session (addendum E.1), and the OTHER sessions' ids ONLY\n // (never their conversation/metadata; the query selects only id — stress g).\n async function resolveSharedExposure(\n workspaceId: string,\n session: { id: string; sandboxGroupId: string },\n ): Promise<{ shared: boolean; sharedSessionIds: string[] }> {\n const ids = await listSessionIdsInGroup(db, workspaceId, session.sandboxGroupId);\n const others = ids.filter((id) => id !== session.id);\n return { shared: others.length > 0, sharedSessionIds: others };\n }\n\n // GET .../stream-capabilities — the capability-negotiation read. Returns the\n // SessionCapabilities doc (descriptor + lease liveness/epoch + os + the\n // shared-exposure disclosure + the calling principal's acknowledgment state),\n // API-direct. The desktop URL/token stay null until P4 mints them (gated by\n // liveness=cold until a box is warm); the read is non-mutating.\n app.get(\"/v1/workspaces/:workspaceId/sessions/:sessionId/stream-capabilities\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"sessions:read\");\n assertOwnershipEnabled();\n const sessionId = c.req.param(\"sessionId\");\n const session = await getSession(db, workspaceId, sessionId);\n if (!session) {\n throw new HTTPException(404, { message: \"session not found\" });\n }\n const lease = await readGroupLease({ db, settings }, { workspaceId, sandboxGroupId: session.sandboxGroupId });\n const { shared, sharedSessionIds } = await resolveSharedExposure(workspaceId, session);\n // Per-principal acknowledgment: A acknowledging does not consent for B. The\n // un-redacted desktop stream ALWAYS requires the un-redacted ack; a shared box\n // ADDITIONALLY requires the shared-exposure ack. Both must match the POST\n // /viewers gate EXACTLY — otherwise a principal who recorded shared consent\n // WITHOUT un-redacted consent could be handed a live VNC URL + scoped token\n // from this read path while being correctly 409'd on attach (a consent-gate\n // bypass of the un-redacted pixel plane).\n const ack = await getStreamAcknowledgment(db, { workspaceId, sandboxGroupId: session.sandboxGroupId, subjectId: grant.subjectId });\n const acknowledged = ack ? (ack.acknowledgedUnredacted && (!shared || ack.acknowledgedShared)) : false;\n\n // P4.2 — the pixel DATA PLANE, served API-direct. When the backend is\n // desktop-capable AND sandboxDesktopEnabled AND the (shared, if shared)\n // acknowledgment is present AND the box is WARM, mint the REAL DesktopStream\n // cell IN-PROCESS: resume the box by id, ensureDisplayStack (idempotent),\n // exposeStreamPort (resolve the 6080 tunnel + mint the scoped token), record\n // data_plane_url under the epoch fence, and emit stream.url.rotated to other\n // viewers on a box rollover. The handshake never SPINS UP a cold box (that is\n // the viewer-attach path) — a cold lease stays lease_cold. A degraded mint\n // (no secret / display-stack or tunnel failure) returns null → transport:null.\n let desktopStream: DesktopStreamMint | null = null;\n const desktopUnlocked =\n settings.sandboxDesktopEnabled\n && !streamTokenDegraded(settings)\n && acknowledged\n && (session.activeSandboxId != null || lease?.liveness === \"warm\" || lease?.liveness === \"draining\");\n if (desktopUnlocked) {\n desktopStream = await mintDesktopStream({ db, settings, bus }, {\n accountId: grant.accountId,\n workspaceId,\n session,\n // The handshake's token is scoped to the calling principal (it is a read,\n // not a viewer-holder acquire); the per-holder token is re-minted on\n // POST /viewers. A previousEpoch != current would have rotated already\n // via the warming-commit; the read does not itself drive rotation.\n viewerId: grant.subjectId,\n ...(lease ? { lease } : {}),\n });\n }\n\n // P5.t — the REAL PTY terminal cell, served API-DIRECT. Independent of the\n // desktop: it gates ONLY on sandboxTerminalEnabled + a real-PTY backend + a\n // WARM box (NO un-redacted ack — the terminal cell has no acknowledgment\n // gate). A degraded mint (terminal off / no secret / ttyd or tunnel failure)\n // returns null → the Terminal cell falls back to the sse-events firehose.\n let terminalStream: TerminalStreamMint | null = null;\n const terminalUnlocked =\n settings.sandboxTerminalEnabled\n && !streamTokenDegraded(settings)\n && (session.activeSandboxId != null || lease?.liveness === \"warm\" || lease?.liveness === \"draining\");\n if (terminalUnlocked) {\n terminalStream = await mintTerminalStream({ db, settings, bus }, {\n accountId: grant.accountId,\n workspaceId,\n session,\n viewerId: grant.subjectId,\n ...(lease ? { lease } : {}),\n });\n }\n\n const capabilities = negotiateCapabilities({\n sessionId,\n backend: session.sandboxBackend as SandboxBackend,\n os: session.sandboxOs,\n liveness: lease?.liveness ?? \"cold\",\n leaseEpoch: lease?.leaseEpoch ?? 0,\n desktopEnabled: settings.sandboxDesktopEnabled,\n // Human take-control: when the desktop is available + this policy is on\n // (default), the cell is mode \"interactive\" — the noVNC viewer drives :0\n // (x11vnc runs without -viewonly). Off → mode \"read-only\" (client disables\n // take-control). Independent of the agent's computerUseReadOnly.\n desktopInteractive: settings.sandboxDesktopInteractive,\n // P4.3 computer-use: the agent drives :0 (xdotool/scrot); availability\n // tracks the desktop tier + a desktop-capable backend.\n computerUseEnabled: settings.computerUseEnabled,\n computerUseReadOnly: settings.computerUseReadOnly,\n // Graceful degrade (I8/OD-8): if desktop is enabled but no stream-token\n // secret is resolvable, the desktop cell reports transport:null rather\n // than advertising a plane we can never authorize.\n streamTokenSecretAvailable: !streamTokenDegraded(settings),\n desktopAcknowledged: acknowledged,\n shared,\n sharedSessionIds,\n // The minted live address (null when not unlocked/degraded). The resolver\n // only folds it in when the desktop gates pass + the ack is present.\n ...(desktopStream\n ? {\n desktopStream: {\n url: desktopStream.url,\n token: desktopStream.token,\n expiresAt: desktopStream.expiresAt,\n resolution: desktopStream.resolution,\n },\n }\n : {}),\n // P5.t — the terminal policy toggle + the minted pty-ws address. The\n // resolver advertises sse-events (firehose) on a cold/disabled terminal and\n // folds the live pty-ws url/token in only when the gates passed + minted.\n terminalEnabled: settings.sandboxTerminalEnabled,\n ...(terminalStream\n ? {\n terminalStream: {\n url: terminalStream.url,\n token: terminalStream.token,\n expiresAt: terminalStream.expiresAt,\n },\n }\n : {}),\n });\n\n // SWAP-CASE desktop transport (BOTH directions): negotiateCapabilities keyed on\n // the HOME backend, but the pixel plane actually runs on the ACTIVE sandbox — and\n // the two backends use DIFFERENT wire transports. The advertised transport MUST\n // match where mintDesktopStream routed the pixels (relay IFF the active sandbox is\n // a selfhosted machine), or the client picks the wrong renderer and the socket\n // closes before it opens:\n // • modal-HOME swapped ONTO a selfhosted machine: negotiate says vnc-ws, but the\n // machine's desktop is the RELAY framebuffer (PNG-per-frame) → flip to\n // relay-frames/frames. (#171)\n // • selfhosted-HOME swapped AWAY to the cloud group box (activeSandboxId=null OR a\n // non-selfhosted active sandbox): negotiate says relay-frames (home=selfhosted),\n // but there is NO relay producer on the Modal box → the client hangs on a dead\n // relay socket (\"desktop stream closed before it opened\"). Flip to the Modal\n // noVNC/RFB tunnel (vnc-ws/novnc). This is the mirror of #171 and the missing\n // half that this fixes.\n // The single invariant: advertise relay-frames IFF (activeSandboxId set AND the\n // active sandbox kind is \"selfhosted\") — EXACTLY mintDesktopStream's routing. When\n // the desktop is available we set the transport from the ACTIVE sandbox in one\n // place (resolveActiveDesktopTransport), covering BOTH swap directions.\n let responseCapabilities = capabilities;\n if (capabilities.DesktopStream.transport !== null) {\n const activeSandbox = session.activeSandboxId ? await getSandbox(db, workspaceId, session.activeSandboxId) : null;\n const wire = resolveActiveDesktopTransport(activeSandbox?.kind === \"selfhosted\", settings.sandboxDesktopInteractive !== false);\n responseCapabilities = {\n ...capabilities,\n DesktopStream: { ...capabilities.DesktopStream, ...wire },\n };\n }\n return c.json(responseCapabilities);\n });\n\n // POST .../stream-capabilities/acknowledge — record the calling principal's\n // acknowledgment of the un-redacted pixel plane (and, when shared, the\n // shared-exposure disclosure). Reuses the acknowledgment machinery — gated on\n // stream:acknowledge, no new permission. Until this is recorded the\n // desktop-stream (viewer attach) path returns 409 (P3.2 consent gate).\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/stream-capabilities/acknowledge\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"stream:acknowledge\");\n assertOwnershipEnabled();\n const sessionId = c.req.param(\"sessionId\");\n const session = await getSession(db, workspaceId, sessionId);\n if (!session) {\n throw new HTTPException(404, { message: \"session not found\" });\n }\n const parsed = AcknowledgeStreamRequest.safeParse(await c.req.json().catch(() => ({})));\n if (!parsed.success) {\n throw new HTTPException(400, { message: \"invalid stream acknowledgment request\" });\n }\n const recorded = await recordStreamAcknowledgment(db, {\n accountId: grant.accountId,\n workspaceId,\n sandboxGroupId: session.sandboxGroupId,\n subjectId: grant.subjectId,\n acknowledgeUnredacted: parsed.data.acknowledgeUnredacted,\n acknowledgeShared: parsed.data.acknowledgeShared,\n });\n return c.json({ acknowledged: recorded.acknowledgedUnredacted, acknowledgedShared: recorded.acknowledgedShared });\n });\n\n // POST .../viewers — acquire a viewer holder on the desktop-stream (un-redacted\n // pixel) path. Gated on stream:view (strictly broader than sessions:read: the\n // pixel plane is un-redacted). THE CONSENT GATE: until the calling principal\n // has acknowledged the un-redacted plane this returns 409\n // stream_acknowledgment_required; when the box is shared and the shared-exposure\n // disclosure is not acknowledged it returns 409 shared_acknowledgment_required.\n // Only after consent does it acquire the holder (spinning the box up in-process\n // when cold).\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/viewers\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"stream:view\");\n assertOwnershipEnabled();\n const sessionId = c.req.param(\"sessionId\");\n const session = await getSession(db, workspaceId, sessionId);\n if (!session) {\n throw new HTTPException(404, { message: \"session not found\" });\n }\n const parsed = AttachViewerRequest.safeParse(await c.req.json().catch(() => ({})));\n if (!parsed.success) {\n throw new HTTPException(400, { message: \"invalid viewer attach request\" });\n }\n // Consent gate (P3.2 / addendum E.1): ONLY the un-redacted DESKTOP pixel plane\n // requires the calling principal's acknowledgment (recorded per group+subject;\n // a shared box additionally needs the shared-exposure consent). A TERMINAL-ONLY\n // warm attach (`desktop:false`, the default) carries NO consent gate — a shell\n // is interactive by nature and the gate is the scoped tunnel URL + stream token\n // — so it warms the box and mints the pty-ws terminal cell without a 409. Gating\n // the terminal attach behind the desktop ack (the bug this fixes) dead-ended the\n // interactive terminal: the box never warmed → the Terminal cell stayed on the\n // read-only sse-events firehose forever (\"read only\"), and with the desktop tier\n // off by default there was no consent flow to ever clear the gate.\n const wantDesktop = parsed.data.desktop ?? false;\n const { shared } = await resolveSharedExposure(workspaceId, session);\n if (wantDesktop) {\n const ack = await getStreamAcknowledgment(db, { workspaceId, sandboxGroupId: session.sandboxGroupId, subjectId: grant.subjectId });\n if (!ack?.acknowledgedUnredacted) {\n throw new HTTPException(409, { message: \"stream_acknowledgment_required\" });\n }\n if (shared && !ack.acknowledgedShared) {\n throw new HTTPException(409, { message: \"shared_acknowledgment_required\" });\n }\n }\n // SELFHOSTED ACTIVE: when the session's active sandbox is selfhosted, skip\n // attachViewer (it warms the Modal group box — the wrong target). Synthesize a\n // result shaped like ViewerAttachResult and mint relay cells directly.\n const activeSandbox = session.activeSandboxId ? await getSandbox(db, workspaceId, session.activeSandboxId) : null;\n const selfhostedActive = activeSandbox?.kind === \"selfhosted\";\n\n let stream: DesktopStreamMint | null = null;\n let terminal: TerminalStreamMint | null = null;\n\n let result: Awaited<ReturnType<typeof attachViewer>>;\n if (selfhostedActive) {\n const viewerId = parsed.data.viewerId ?? crypto.randomUUID();\n result = {\n viewerId,\n liveness: \"warm\",\n leaseEpoch: session.activeEpoch,\n sandboxGroupId: session.sandboxGroupId,\n viewerHeartbeatIntervalMs: viewerHeartbeatIntervalMs(settings),\n dataPlaneUrl: null,\n };\n if (\n (settings.sandboxDesktopEnabled || settings.sandboxTerminalEnabled)\n && !streamTokenDegraded(settings)\n ) {\n if (wantDesktop && settings.sandboxDesktopEnabled) {\n stream = await mintDesktopStream({ db, settings, bus }, {\n accountId: grant.accountId,\n workspaceId,\n session,\n viewerId,\n // No Modal lease for selfhosted-active; the mint routes to the relay.\n });\n }\n if (settings.sandboxTerminalEnabled) {\n terminal = await mintTerminalStream({ db, settings, bus }, {\n accountId: grant.accountId,\n workspaceId,\n session,\n viewerId,\n // No Modal lease for selfhosted-active; the mint routes to the relay.\n });\n }\n }\n } else {\n result = await attachViewer({ db, settings }, {\n accountId: grant.accountId,\n workspaceId,\n session,\n ...(parsed.data.viewerId ? { viewerId: parsed.data.viewerId } : {}),\n });\n\n // P4.2 — the viewer now holds a WARM box; mint the real pixel cell IN-PROCESS\n // (resume by id → ensureDisplayStack → exposeStreamPort) scoped to THIS\n // viewer holder, record data_plane_url, and fold the live address into the\n // response. A degraded mint (no secret / headless / display-stack or tunnel\n // failure) leaves dataPlaneUrl null — the client falls back to Channel-A. The\n // box is warm here (attachViewer spun it up or attached), so the handshake's\n // never-spin-up rule does not apply.\n if (\n (settings.sandboxDesktopEnabled || settings.sandboxTerminalEnabled)\n && !streamTokenDegraded(settings)\n ) {\n const lease = await readGroupLease({ db, settings }, { workspaceId, sandboxGroupId: session.sandboxGroupId });\n if (lease) {\n // The pixel cell is minted only when the caller asked for the desktop plane\n // (and consented above). A terminal-only attach skips it — the box is warm,\n // the terminal mint below still runs.\n if (wantDesktop && settings.sandboxDesktopEnabled) {\n stream = await mintDesktopStream({ db, settings, bus }, {\n accountId: grant.accountId,\n workspaceId,\n session,\n viewerId: result.viewerId,\n lease,\n });\n }\n // P5.t — the same warm-box viewer attach also mints the REAL PTY terminal\n // address (independent of the desktop toggle). A degraded mint leaves the\n // terminal fields null → the client falls back to the sse-events firehose.\n if (settings.sandboxTerminalEnabled) {\n terminal = await mintTerminalStream({ db, settings, bus }, {\n accountId: grant.accountId,\n workspaceId,\n session,\n viewerId: result.viewerId,\n lease,\n });\n }\n }\n }\n }\n return c.json(\n {\n ...result,\n dataPlaneUrl: stream?.url ?? result.dataPlaneUrl,\n streamToken: stream?.token ?? null,\n streamExpiresAt: stream?.expiresAt ?? null,\n resolution: stream?.resolution ?? null,\n // Transport MUST match where the pixels were minted: a selfhosted-active box\n // serves the RELAY framebuffer (relay-frames/frames), a Modal box serves noVNC\n // (vnc-ws/novnc). Hardcoding vnc-ws here handed a machine's relay URL to the\n // noVNC renderer (and vice-versa on the swap-away case) → \"closed before it\n // opened\". Key off the SAME selfhostedActive the mint routed on.\n transport: stream ? (selfhostedActive ? (\"relay-frames\" as const) : (\"vnc-ws\" as const)) : null,\n client: stream ? (selfhostedActive ? (\"frames\" as const) : (\"novnc\" as const)) : null,\n // The REAL PTY terminal address (pty-ws), null when degraded.\n terminalUrl: terminal?.url ?? null,\n terminalToken: terminal?.token ?? null,\n terminalExpiresAt: terminal?.expiresAt ?? null,\n terminalTransport: terminal ? (\"pty-ws\" as const) : null,\n },\n 201,\n );\n });\n\n // POST .../viewers/:viewerId/heartbeat — refresh the holder TTL (epoch-fenced).\n // The desktop-stream lifecycle is gated on stream:view (the un-redacted plane).\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/viewers/:viewerId/heartbeat\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"stream:view\");\n assertOwnershipEnabled();\n const sessionId = c.req.param(\"sessionId\");\n const session = await getSession(db, workspaceId, sessionId);\n if (!session) {\n throw new HTTPException(404, { message: \"session not found\" });\n }\n const parsed = ViewerHeartbeatRequest.safeParse(await c.req.json().catch(() => ({})));\n if (!parsed.success) {\n throw new HTTPException(400, { message: \"viewer heartbeat requires { leaseEpoch }\" });\n }\n const alive = await heartbeatViewer({ db, settings }, {\n accountId: grant.accountId,\n workspaceId,\n sandboxGroupId: session.sandboxGroupId,\n viewerId: c.req.param(\"viewerId\"),\n expectedEpoch: parsed.data.leaseEpoch,\n });\n return c.json({ alive });\n });\n\n // DELETE .../viewers/:viewerId — release the holder (idempotent).\n app.delete(\"/v1/workspaces/:workspaceId/sessions/:sessionId/viewers/:viewerId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"stream:view\");\n assertOwnershipEnabled();\n const sessionId = c.req.param(\"sessionId\");\n const session = await getSession(db, workspaceId, sessionId);\n if (!session) {\n throw new HTTPException(404, { message: \"session not found\" });\n }\n await detachViewer({ db, settings }, {\n accountId: grant.accountId,\n workspaceId,\n sandboxGroupId: session.sandboxGroupId,\n viewerId: c.req.param(\"viewerId\"),\n });\n return c.body(null, 204);\n });\n\n // POST .../viewers/:viewerId/revoke — OD-6 v1 revocation. Drops the named\n // viewer's holder from the GROUP lease so refcount recomputes; the box drains\n // iff nothing else holds it (a turn-held or other-viewer-held box survives —\n // group-refcount liveness). Gated on stream:view (no new permission). The\n // live-RFB force-disconnect of an already-open socket is a P4 follow-up; the\n // holder-drop (so the box can drain) is the v1 deliverable.\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/viewers/:viewerId/revoke\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"stream:view\");\n assertOwnershipEnabled();\n const sessionId = c.req.param(\"sessionId\");\n const session = await getSession(db, workspaceId, sessionId);\n if (!session) {\n throw new HTTPException(404, { message: \"session not found\" });\n }\n const result = await revokeViewer(db, {\n accountId: grant.accountId,\n workspaceId,\n sandboxGroupId: session.sandboxGroupId,\n viewerId: c.req.param(\"viewerId\"),\n idleGraceMs: settings.sandboxIdleGraceMs,\n });\n // null ⇒ the lease was already cold-and-reaped (revoke is an idempotent no-op).\n return c.json({ liveness: result?.liveness ?? null, refcount: result?.refcount ?? null });\n });\n\n // ══════════════════════ Channel-A structured services (P4.4) ══════════════\n //\n // FileSystem (list/read/write/delete) + Git (status/diff/log/show) + Terminal\n // (exec + interactive PTY), all served API-DIRECT: each route does\n // requireAccessGrant BEFORE Zod parse -> resume the box by id in-process\n // (cold->warming CAS + viewer holder) -> SandboxChannelAService method\n // -> inline JSON -> release holder + drop handle.\n // NO Temporal, NO worker RPC, NO NATS round-trip — reads never ride the bus\n // (which would corrupt SSE gap-fill). The notifications (fs.changed/git.changed\n // /terminal.pty.*) ride A1 via appendAndPublishEvents. Gated behind\n // sandboxOwnershipEnabled (the lease is dormant otherwise). Explicit\n // HTTPException(400/404/409) — never a raw ZodError -> 500.\n\n // FS uses files:read for reads, files:write for mutations; Git is read-only\n // (rides files:read); Terminal exec + PTY ride terminal:attach.\n\n type ChannelARouteCtx = {\n accountId: string;\n workspaceId: string;\n session: Session;\n subjectId: string;\n };\n\n // Shared preamble: grant BEFORE parse, ownership gate, session lookup. Returns\n // the resolved context the channel-a seam needs (session narrowed non-null).\n async function channelAPreamble(\n c: Context,\n permission: \"files:read\" | \"files:write\" | \"terminal:attach\",\n ): Promise<ChannelARouteCtx> {\n const workspaceId = c.req.param(\"workspaceId\") ?? \"\";\n const grant = await requireAccessGrant(c, deps, workspaceId, permission);\n assertOwnershipEnabled();\n const sessionId = c.req.param(\"sessionId\") ?? \"\";\n const session = await getSession(db, workspaceId, sessionId);\n if (!session) {\n throw new HTTPException(404, { message: \"session not found\" });\n }\n return { accountId: grant.accountId, workspaceId, session, subjectId: grant.subjectId };\n }\n\n async function parseChannelABody<T>(c: Context, schema: { safeParse: (v: unknown) => { success: true; data: T } | { success: false } }): Promise<T> {\n const raw = await c.req.json().catch(() => undefined);\n const result = schema.safeParse(raw ?? {});\n if (!result.success) {\n throw new HTTPException(400, { message: \"invalid request body\" });\n }\n return result.data;\n }\n\n // ── FileSystem ──────────────────────────────────────────────────────────\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/fs/list\", async (c) => {\n const ctx = await channelAPreamble(c, \"files:read\");\n const req = await parseChannelABody(c, FsListRequest);\n const out = await withChannelA({ db, settings, bus }, ctx, ({ service }) => service.fsList(req));\n return c.json(out);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/fs/read\", async (c) => {\n const ctx = await channelAPreamble(c, \"files:read\");\n const req = await parseChannelABody(c, FsReadRequest);\n const out = await withChannelA({ db, settings, bus }, ctx, ({ service }) => service.fsRead(req));\n return c.json(out);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/fs/write\", async (c) => {\n const ctx = await channelAPreamble(c, \"files:write\");\n const req = await parseChannelABody(c, FsWriteRequest);\n const out = await withChannelA({ db, settings, bus }, ctx, ({ service }) => service.fsWrite(req));\n return c.json(out);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/fs/delete\", async (c) => {\n const ctx = await channelAPreamble(c, \"files:write\");\n const req = await parseChannelABody(c, FsDeleteRequest);\n const out = await withChannelA({ db, settings, bus }, ctx, ({ service }) => service.fsDelete(req));\n return c.json(out);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/fs/move\", async (c) => {\n const ctx = await channelAPreamble(c, \"files:write\");\n const req = await parseChannelABody(c, FsMoveRequest);\n const out = await withChannelA({ db, settings, bus }, ctx, ({ service }) => service.fsMove(req));\n return c.json(out);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/fs/mkdir\", async (c) => {\n const ctx = await channelAPreamble(c, \"files:write\");\n const req = await parseChannelABody(c, FsMkdirRequest);\n const out = await withChannelA({ db, settings, bus }, ctx, ({ service }) => service.fsMkdir(req));\n return c.json(out);\n });\n\n // ── Git (read-only) ─────────────────────────────────────────────────────\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/git/status\", async (c) => {\n const ctx = await channelAPreamble(c, \"files:read\");\n const req = await parseChannelABody(c, GitStatusRequest);\n const out = await withChannelA({ db, settings, bus }, ctx, ({ service }) => service.gitStatus(req));\n return c.json(out);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/git/diff\", async (c) => {\n const ctx = await channelAPreamble(c, \"files:read\");\n const req = await parseChannelABody(c, GitDiffRequest);\n const out = await withChannelA({ db, settings, bus }, ctx, ({ service }) => service.gitDiff(req));\n return c.json(out);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/git/log\", async (c) => {\n const ctx = await channelAPreamble(c, \"files:read\");\n const req = await parseChannelABody(c, GitLogRequest);\n const out = await withChannelA({ db, settings, bus }, ctx, ({ service }) => service.gitLog(req));\n return c.json(out);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/git/show\", async (c) => {\n const ctx = await channelAPreamble(c, \"files:read\");\n const req = await parseChannelABody(c, GitShowRequest);\n const out = await withChannelA({ db, settings, bus }, ctx, ({ service }) => service.gitShow(req));\n return c.json(out);\n });\n\n // ── Terminal: synchronous exec ────────────────────────────────────────────\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/terminal/exec\", async (c) => {\n const ctx = await channelAPreamble(c, \"terminal:attach\");\n const req = await parseChannelABody(c, TerminalExecRequest);\n const out = await withChannelA({ db, settings, bus }, ctx, ({ service }) => service.terminalExec(req));\n return c.json(out);\n });\n\n // ── Terminal: interactive PTY control (output rides A1) ───────────────────\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/terminal/pty\", async (c) => {\n const ctx = await channelAPreamble(c, \"terminal:attach\");\n const req = await parseChannelABody(c, PtyOpenRequest);\n const ptyId = crypto.randomUUID();\n const out = await withChannelA({ db, settings, bus }, ctx, async ({ service, lease }) => {\n const opened = await service.ptyOpen(req, ptyId);\n // Persist the ptyId<->exec-session map fenced to the box's epoch.\n await insertPtySession(db, {\n id: ptyId,\n accountId: ctx.accountId,\n workspaceId: ctx.workspaceId,\n sessionId: ctx.session.id,\n execSessionId: opened.execSessionId,\n leaseEpoch: lease.leaseEpoch,\n cols: req.cols,\n rows: req.rows,\n shell: opened.shell,\n cwd: req.cwd,\n openedBy: ctx.subjectId,\n });\n // Emit terminal.pty.started + any initial banner output on A1.\n const started: TerminalPtyStartedPayload = { ptyId, cols: req.cols, rows: req.rows, shell: opened.shell, cwd: req.cwd };\n const events: AppendEventInput[] = [{ type: \"terminal.pty.started\", payload: started }];\n if (opened.initialOutput) {\n const delta: TerminalPtyOutputDeltaPayload = { ptyId, stream: \"stdout\", chunk: opened.initialOutput, seq: 0 };\n events.push({ type: \"terminal.pty.output.delta\", payload: delta });\n }\n await appendAndPublishEvents(db, bus, ctx.workspaceId, ctx.session.id, events);\n return opened.response;\n });\n return c.json(out, 201);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/terminal/pty/write\", async (c) => {\n const ctx = await channelAPreamble(c, \"terminal:attach\");\n const req = await parseChannelABody(c, PtyWriteRequest);\n const pty = await getOpenPtySession(db, ctx.workspaceId, req.ptyId);\n if (!pty) {\n throw new HTTPException(404, { message: \"pty not found or closed\" });\n }\n if (pty.execSessionId === null) {\n throw new HTTPException(409, { message: \"interactive terminal unsupported on this backend\" });\n }\n let seq = 1;\n await withChannelA({ db, settings, bus }, ctx, async ({ service }) => {\n const output = await service.ptyWrite(req, pty.execSessionId!, req.data);\n await updatePtySessionActivity(db, { accountId: ctx.accountId, workspaceId: ctx.workspaceId, ptyId: req.ptyId, execSessionId: pty.execSessionId });\n if (output) {\n const delta: TerminalPtyOutputDeltaPayload = { ptyId: req.ptyId, stream: \"stdout\", chunk: output, seq: seq++ };\n await appendAndPublishEvents(db, bus, ctx.workspaceId, ctx.session.id, [{ type: \"terminal.pty.output.delta\", payload: delta }]);\n }\n });\n return c.body(null, 204);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/terminal/pty/resize\", async (c) => {\n const ctx = await channelAPreamble(c, \"terminal:attach\");\n const req = await parseChannelABody(c, PtyResizeRequest);\n const pty = await getOpenPtySession(db, ctx.workspaceId, req.ptyId);\n if (!pty) {\n throw new HTTPException(404, { message: \"pty not found or closed\" });\n }\n if (pty.execSessionId !== null) {\n await withChannelA({ db, settings, bus }, ctx, ({ service }) => service.ptyResize(req, pty.execSessionId!));\n }\n await updatePtySessionActivity(db, { accountId: ctx.accountId, workspaceId: ctx.workspaceId, ptyId: req.ptyId, cols: req.cols, rows: req.rows });\n return c.body(null, 204);\n });\n\n app.post(\"/v1/workspaces/:workspaceId/sessions/:sessionId/terminal/pty/close\", async (c) => {\n const ctx = await channelAPreamble(c, \"terminal:attach\");\n const req = await parseChannelABody(c, PtyCloseRequest);\n const pty = await getOpenPtySession(db, ctx.workspaceId, req.ptyId);\n // Idempotent: closing an already-closed/absent PTY is a 204 no-op.\n if (pty) {\n await withChannelA({ db, settings, bus }, ctx, ({ service }) => service.ptyClose(req, pty.execSessionId));\n await closePtySession(db, { accountId: ctx.accountId, workspaceId: ctx.workspaceId, ptyId: req.ptyId });\n const exited: TerminalPtyExitedPayload = { ptyId: req.ptyId, exitCode: 0, reason: \"exit\" };\n await appendAndPublishEvents(db, bus, ctx.workspaceId, ctx.session.id, [{ type: \"terminal.pty.exited\", payload: exited }]);\n }\n return c.body(null, 204);\n });\n}\n\nfunction eventListLimit(raw: string | undefined, max = 2000): number {\n const limit = Number(raw ?? 500);\n if (!Number.isFinite(limit)) {\n return 500;\n }\n return Math.min(max, Math.max(1, Math.floor(limit)));\n}\n\nfunction compactEvents(raw: string | undefined): boolean {\n return raw === \"1\" || raw === \"true\";\n}\n\nfunction eventSequence(raw: string | undefined, fallback: number): number {\n const sequence = Number(raw ?? fallback);\n if (!Number.isFinite(sequence)) {\n return fallback;\n }\n return Math.floor(sequence);\n}\n\nfunction optionalEventSequence(raw: string | undefined): number | undefined {\n if (raw === undefined) {\n return undefined;\n }\n const sequence = Number(raw);\n if (!Number.isFinite(sequence)) {\n return undefined;\n }\n return Math.floor(sequence);\n}\n\nfunction hasOwnProperty(value: unknown, key: string): boolean {\n return Boolean(value && typeof value === \"object\" && Object.prototype.hasOwnProperty.call(value, key));\n}\n\nfunction userMessagePayloadHasOwnProperty(value: unknown, key: string): boolean {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const payload = (value as { payload?: unknown }).payload;\n return hasOwnProperty(payload, key);\n}\n","// apps/api/src/sandbox/channel-a.ts — the API-DIRECT Channel-A seam (P4.4).\n//\n// The structured services (FileSystem / Git / Terminal) are SYNCHRONOUS point\n// queries served client -> API -> box IN-PROCESS. Each call:\n//\n// 1. acquires a viewer-kind lease holder (warming the box when cold — the\n// same cold->warming CAS attachViewer runs; a Postgres txn the API OWNS),\n// 2. resumes the box BY ID from the group lease's resume_state envelope,\n// 3. builds ONE SandboxChannelAService around the live `session` handle,\n// 4. runs the op (fsList/gitDiff/ptyWrite/...), returns inline JSON,\n// 5. releases the viewer holder + drops the live handle.\n//\n// NO Temporal, NO worker RPC, NO NATS round-trip in this path — reads never ride\n// the bus (which would corrupt SSE gap-fill). Only the side-effect NOTIFICATIONS\n// (fs.changed/git.changed/terminal.pty.*) ride A1 via appendAndPublishEvents.\n//\n// IMPORT DISCIPLINE: sandbox symbols come ONLY from @opengeni/runtime/sandbox\n// (the agent-loop-free leaf) — enforced by sandbox-access-import-guard.test.ts.\n\nimport { applyGitAuthPointerEnvironment, hasGitHubRepositorySelection, stableSandboxEnvironmentForRun, type Settings } from \"@opengeni/config\";\nimport { githubAppBotIdentity } from \"@opengeni/github\";\nimport type { Session } from \"@opengeni/contracts\";\nimport {\n acquireLease,\n commitWarmingToWarm,\n failWarmingToCold,\n getSandboxSessionEnvelope,\n loadWorkspaceEnvironmentForRun,\n readLease,\n releaseLeaseHolder,\n SandboxLeaseSupersededError,\n type Database,\n type LeaseSnapshot,\n} from \"@opengeni/db\";\nimport { appendAndPublishEvents, type EventBus } from \"@opengeni/events\";\nimport { HTTPException } from \"hono/http-exception\";\n\nimport {\n establishSandboxSessionFromEnvelope,\n serializeEstablishedSandboxEnvelope,\n SandboxChannelAService,\n ChannelAConflictError,\n ChannelANotFoundError,\n ChannelAUnsupportedError,\n ChannelAValidationError,\n type ChannelASession,\n type EstablishedSandboxSession,\n} from \"@opengeni/runtime/sandbox\";\nimport { routingEnabled, wrapChannelABoxWithRouting } from \"@opengeni/core\";\n\nexport type ChannelAServices = {\n db: Database;\n settings: Settings;\n bus: EventBus;\n};\n\nexport type ChannelAContext = {\n accountId: string;\n workspaceId: string;\n session: Session;\n // The principal that drives the op (for emit attribution + pty opened_by).\n subjectId: string;\n};\n\n// The live op surface handed to a route's callback: the service + the live lease\n// (for the pty exec-session epoch fence + revision seeding).\nexport type ChannelAHandle = {\n service: SandboxChannelAService;\n lease: LeaseSnapshot;\n};\n\n/**\n * Run a Channel-A op against a live box, API-direct. Acquires a viewer holder\n * (warming the box when cold), resumes by id, builds the service, runs `fn`, and\n * ALWAYS releases the holder + drops the handle in `finally`. Maps the service's\n * typed errors to HTTP status (the route never sees a raw ChannelA*Error).\n *\n * Gated behind sandboxOwnershipEnabled at the route (the lease is dormant\n * otherwise). A `backend:none` session has no box -> 409 before touching it.\n */\nexport async function withChannelA<T>(\n services: ChannelAServices,\n ctx: ChannelAContext,\n fn: (handle: ChannelAHandle) => Promise<T>,\n): Promise<T> {\n const { db, settings, bus } = services;\n const { accountId, workspaceId, session, subjectId } = ctx;\n\n if (session.sandboxBackend === \"none\") {\n throw new HTTPException(409, { message: \"sandbox not available\" });\n }\n\n const sandboxGroupId = session.sandboxGroupId;\n const viewerId = crypto.randomUUID();\n const leaseTtlMs = settings.sandboxLeaseTtlMs;\n\n const release = async (): Promise<void> => {\n await releaseLeaseHolder(db, {\n accountId,\n workspaceId,\n sandboxGroupId,\n kind: \"viewer\",\n holderId: viewerId,\n idleGraceMs: settings.sandboxIdleGraceMs,\n });\n };\n\n // Acquire a viewer holder; the cold->warming CAS spawns the box when cold.\n const acquired = await acquireLease(db, {\n accountId,\n workspaceId,\n sandboxGroupId,\n kind: \"viewer\",\n holderId: viewerId,\n subjectId: session.id,\n backend: session.sandboxBackend,\n os: session.sandboxOs,\n leaseTtlMs,\n });\n\n if (acquired.role === \"fenced\") {\n await release();\n throw new HTTPException(409, { message: `sandbox lease superseded (epoch ${acquired.lease.leaseEpoch}); retry` });\n }\n\n let established: EstablishedSandboxSession | undefined;\n let leaseSnapshot: LeaseSnapshot = acquired.lease;\n\n try {\n const envelope = await getSandboxSessionEnvelope(db, workspaceId, session.id);\n // The STABLE run-environment a COLD box must be created with so a later worker\n // turn's agent-manifest apply finds an EMPTY env delta (config base + git\n // identity + decrypted workspace env + HOME + — for a repo-attached session —\n // the stable git-auth pointers the turn declares). Only the rotating token\n // VALUE stays off (it lives in the box file the clone hook seeds). Keyed off\n // the SESSION's backend (the establish below passes backendOverride:\n // session.sandboxBackend, and HOME/token-file/askpass are backend-derived),\n // NOT the deployment default — mirrors sessionAttachEnvironment.\n const workspaceEnvironment = await loadWorkspaceEnvironmentForRun(db, settings, workspaceId, session.environmentId);\n const settingsForSession = session.sandboxBackend !== settings.sandboxBackend\n ? { ...settings, sandboxBackend: session.sandboxBackend }\n : settings;\n const environment = stableSandboxEnvironmentForRun(settingsForSession, workspaceEnvironment?.values ?? {});\n if (hasGitHubRepositorySelection(session.resources)) {\n applyGitAuthPointerEnvironment(environment, githubAppBotIdentity(settings));\n }\n\n if (acquired.role === \"spawner\") {\n // We won the cold->warming CAS: establish the box from the envelope, then\n // commit warm. The established handle IS our live handle for the op.\n const expectedEpoch = acquired.lease.leaseEpoch;\n // Prefer the COLD lease's preserved resume_state when it carries a persisted\n // /workspace snapshot (confirmDrainCold keeps a minimal archive-only envelope\n // across draining->cold for exactly this re-warm). establishSandboxSessionFromEnvelope\n // cold-creates a fresh box and replays the archive via hydrateWorkspace, so\n // /workspace survives the box churn (sandbox-file-persistence). No archive ->\n // the bare session envelope (a never-warmed cold start). The order matters:\n // resume_state is the lease's authoritative box descriptor; the session\n // `_sandbox` envelope is only the per-session fallback.\n const spawnEnvelope = acquired.lease.resumeState ?? envelope;\n try {\n established = await establishSandboxSessionFromEnvelope(settings, spawnEnvelope, {\n sessionId: session.id,\n backendOverride: session.sandboxBackend,\n environment,\n });\n } catch (error) {\n await failWarmingToCold(db, { accountId, workspaceId, sandboxGroupId, expectedEpoch });\n throw new HTTPException(409, { message: `sandbox not available (${error instanceof Error ? error.message : \"spawn failed\"})` });\n }\n // Persist the LIVE box as the lease's resume_state so the NEXT op resumes\n // this box by id rather than cold-creating a rival (the box-churn the\n // prove-it surfaced). Fall back to the session envelope when serialize is\n // unavailable.\n const resumeEnvelope = (await serializeEstablishedSandboxEnvelope(established)) ?? envelope ?? null;\n const committed = await commitWarmingToWarm(db, {\n accountId,\n workspaceId,\n sandboxGroupId,\n expectedEpoch,\n instanceId: established.instanceId,\n dataPlaneUrl: acquired.lease.dataPlaneUrl,\n resumeBackendId: established.backendId,\n resumeState: resumeEnvelope,\n leaseTtlMs,\n });\n if (!committed.committed || !committed.lease) {\n throw new HTTPException(409, { message: `sandbox lease superseded (epoch ${expectedEpoch}); retry` });\n }\n leaseSnapshot = committed.lease;\n } else {\n // ATTACHED / REARMED: the box is live. Read the lease to get the\n // authoritative resume_state, then resume by id for this op.\n const live = await readLease(db, workspaceId, sandboxGroupId);\n if (live) leaseSnapshot = live;\n const resumeEnvelope = leaseSnapshot.resumeState ?? envelope;\n established = await establishSandboxSessionFromEnvelope(settings, resumeEnvelope, {\n sessionId: session.id,\n backendOverride: session.sandboxBackend,\n environment,\n });\n }\n\n const emit = async (events: { type: string; payload: unknown }[]): Promise<void> => {\n await appendAndPublishEvents(\n db,\n bus,\n workspaceId,\n session.id,\n // SessionEventType is a string enum at the contract; the producer parses\n // the payload, so this cast is the same shape the worker emits.\n events.map((e) => ({ type: e.type as never, payload: e.payload })),\n );\n };\n\n // M7 hot-swap: when the selfhosted feature is on, route the Channel-A op to\n // the session's currently-active sandbox (not always the group box). The\n // proxy re-reads (active_sandbox_id, active_epoch) on each session method the\n // service calls and dispatches to the active backend (the group box by\n // default, or a swapped-to selfhosted machine). With the flag off the\n // established group session is used unchanged.\n const routedSession = routingEnabled(settings)\n ? wrapChannelABoxWithRouting({ db, settings, bus }, { workspaceId, sessionId: session.id }, established).session\n : established.session;\n\n const service = new SandboxChannelAService({\n session: routedSession as ChannelASession,\n leaseEpoch: leaseSnapshot.leaseEpoch,\n emit,\n });\n\n return await fn({ service, lease: leaseSnapshot });\n } catch (error) {\n throw mapChannelAError(error);\n } finally {\n await release();\n await dropEstablishedHandle(established);\n }\n}\n\n/** Map the service's typed errors to HTTP status (the §5.3 matrix). Re-throws an\n * already-HTTPException unchanged. */\nexport function mapChannelAError(error: unknown): unknown {\n if (error instanceof HTTPException) return error;\n if (error instanceof ChannelAValidationError) return new HTTPException(400, { message: error.message });\n if (error instanceof ChannelANotFoundError) return new HTTPException(404, { message: error.message });\n if (error instanceof ChannelAConflictError) return new HTTPException(409, { message: error.message });\n if (error instanceof ChannelAUnsupportedError) return new HTTPException(409, { message: error.message });\n return error;\n}\n\n// Drop a transiently-established, NON-OWNED handle WITHOUT terminating the box.\n// The box is owned by the LEASE (resumed by id); this handle is incidental.\n//\n// CRITICAL (deployed-integration bug, prove-it D2): a provider session's\n// `close()` is NOT a neutral local-resource free — Modal's session.close() calls\n// sandbox.terminate(), KILLING THE BOX. Calling it after each Channel-A op\n// destroyed the box mid-flight, so a subsequent fs.read/git/exec hit a different\n// (cold-restored) box and 404'd. We DO NOT close the session; only the reaper\n// (provider stop at refcount 0) terminates a box.\nasync function dropEstablishedHandle(established: EstablishedSandboxSession | undefined): Promise<void> {\n // No-op beyond dropping the reference: the lease owns lifecycle, the reaper\n // owns teardown. Never session.close()/terminate() a non-owned handle here.\n void established;\n}\n","// apps/api/src/sandbox/viewer.ts — the API-DIRECT viewer-holder lifecycle (P1.4).\n//\n// A viewer (a human watching a session's box) acquires a `viewer` holder on the\n// GROUP lease so the box stays warm WHILE WATCHED — liveness = turn OR viewer\n// (the §C group-refcount win). All IN-PROCESS: the API runs the cold->warming\n// CAS as a Postgres txn it owns, resumes the box BY ID via the leaf\n// (@opengeni/runtime/sandbox), and never signals Temporal or a worker.\n//\n// attach -> acquireLease(kind:'viewer') under FOR UPDATE + cold->warming CAS.\n// spawner role -> establish the box in-process + commitWarmingToWarm.\n// attached/rearmed -> the holder alone keeps the box warm.\n// fenced -> release + surface a 409 (a newer epoch re-established it).\n// heartbeat -> heartbeatLeaseHolder (epoch-fenced) refreshes the holder TTL.\n// detach -> releaseLeaseHolder (idempotent); the reaper (P1.3) stop()s the\n// box at refcount 0 past the drain grace.\n//\n// The desktop pixel tunnel-URL mint + the un-redacted acknowledgment + the\n// scoped token are P3/P4. Here we surface only the holder lifecycle + the\n// lease's recorded data_plane_url (null until P4 mints it).\n\nimport { createHash } from \"node:crypto\";\nimport { applyGitAuthPointerEnvironment, hasGitHubRepositorySelection, resolveStreamTokenSecret, stableSandboxEnvironmentForRun } from \"@opengeni/config\";\nimport type { Settings } from \"@opengeni/config\";\nimport { githubAppBotIdentity } from \"@opengeni/github\";\nimport { type Session, type StreamUrlRotatedPayload } from \"@opengeni/contracts\";\nimport {\n acquireLease,\n commitWarmingToWarm,\n failWarmingToCold,\n getSandbox,\n getSandboxSessionEnvelope,\n heartbeatLeaseHolder,\n loadWorkspaceEnvironmentForRun,\n readLease,\n recordLeaseDataPlaneUrl,\n recordLeaseTerminalDataPlaneUrl,\n releaseLeaseHolder,\n SandboxLeaseSupersededError,\n type Database,\n type LeaseSnapshot,\n type SandboxRecord,\n} from \"@opengeni/db\";\nimport { appendAndPublishEvents, type EventBus } from \"@opengeni/events\";\nimport { HTTPException } from \"hono/http-exception\";\n\n// The leaf — agent-loop-free. apps/api imports sandbox symbols ONLY from here\n// (enforced by sandbox-access-import-guard.test.ts).\nimport {\n DESKTOP_STREAM_PORT,\n ensureDisplayStack,\n ensureTerminalServer,\n establishSandboxSessionFromEnvelope,\n exposeStreamPort,\n desktopCapableBackend,\n NatsControlRpc,\n SelfhostedSandboxClient,\n serializeEstablishedSandboxEnvelope,\n mintStreamToken,\n STREAM_TOKEN_DEFAULT_TTL_SECONDS,\n TERMINAL_STREAM_PORT,\n DisplayStackUnsupportedError,\n TerminalServerUnsupportedError,\n StreamPortUnavailableError,\n type ControlRpc,\n type EstablishedSandboxSession,\n type NatsRequestConnection,\n} from \"@opengeni/runtime/sandbox\";\nimport { relayConfigFromSettings } from \"@opengeni/core\";\n\n/** The minimal services a viewer op needs: the DB + settings (lease cadence +\n * the sandbox client construction the leaf reads from settings). The bus is\n * optional — only the rotation path (emitting stream.url.rotated to OTHER\n * viewers) needs it. */\nexport type ViewerServices = {\n db: Database;\n settings: Settings;\n bus?: EventBus;\n};\n\n/** A coherent snapshot the routes echo back: the holder id (the viewer's fence-\n * carrying handle), the lease liveness/epoch, and the recorded data-plane URL\n * (null until P4 mints the desktop tunnel). */\nexport type ViewerAttachResult = {\n viewerId: string;\n liveness: LeaseSnapshot[\"liveness\"];\n leaseEpoch: number;\n sandboxGroupId: string;\n // The viewer heartbeat cadence the client must beat at to keep the holder\n // alive (shorter than the viewer-holder TTL the reaper enforces).\n viewerHeartbeatIntervalMs: number;\n // The desktop pixel tunnel URL the viewer connects to directly. Null in P1.4\n // (the mint is P4); surfaced here so the shape is stable.\n dataPlaneUrl: string | null;\n};\n\n/**\n * The STABLE run-scoped sandbox environment a COLD box must be created with so\n * that — whether the box is first warmed by an API-direct ATTACH (here) or by the\n * worker TURN — its manifest environment matches the environment the agent later\n * declares for a turn. Without this, an attach-warmed box was created with the\n * BASE allowlist env only (establishSandboxSessionFromEnvelope's\n * collectSandboxEnvironment default), so the next turn's fuller env (git identity\n * + workspace environment + HOME) introduced a delta and the SDK's\n * `validateNoEnvironmentDelta` threw \"Live sandbox sessions cannot change manifest\n * environment variables\" — the BLOCKING error this fixes.\n *\n * Mirrors the worker turn's STABLE env (config.stableSandboxEnvironmentForRun +\n * the session's attached, decrypted workspace environment + — for a repo-attached\n * session — the stable git-auth POINTERS the turn declares since the token-broker:\n * GIT_ASKPASS / GIT_TERMINAL_PROMPT / bot identity). The pointers carry NO rotating\n * value (the token lives in the box FILE the clone hook seeds), so they are\n * attach-reproducible; omitting them cold-created a box whose env lacked keys the\n * next repo turn's manifest declares → the SDK guard threw \"Live sandbox sessions\n * cannot change manifest environment variables\" whenever a viewer attach (an open\n * session page) won the cold-create race against the first turn.\n */\nexport async function sessionAttachEnvironment(\n services: ViewerServices,\n workspaceId: string,\n session: Session,\n): Promise<Record<string, string>> {\n const workspaceEnvironment = await loadWorkspaceEnvironmentForRun(\n services.db,\n services.settings,\n workspaceId,\n session.environmentId,\n );\n // Build the env with the SESSION's backend, not the deployment default: the\n // stable base is backend-aware (HOME = the descriptor workspaceRoot, and the\n // git token-file/askpass pointers derive from HOME), the box is established\n // with `backendOverride: session.sandboxBackend`, and the worker turn builds\n // the same env from runSettings.sandboxBackend = the session's backend. An\n // attach env keyed off the deployment default would cold-create e.g. an e2b\n // session's box with /workspace-rooted values while its turn declares\n // /home/user-rooted ones — the same guard-killed first turn all over again.\n const settingsForSession = session.sandboxBackend !== services.settings.sandboxBackend\n ? { ...services.settings, sandboxBackend: session.sandboxBackend }\n : services.settings;\n const environment = stableSandboxEnvironmentForRun(settingsForSession, workspaceEnvironment?.values ?? {});\n if (hasGitHubRepositorySelection(session.resources)) {\n applyGitAuthPointerEnvironment(environment, githubAppBotIdentity(services.settings));\n }\n return environment;\n}\n\n/**\n * Acquire a `viewer` holder on the group lease, spinning up the box IN-PROCESS\n * when cold. Mirrors the worker's resumeBoxForTurn spawner/attached branches,\n * but with kind:'viewer' and run by the API process — no Temporal, no worker.\n *\n * `viewerId` is the unique-per-connection holder id (a uuid the client carries\n * through heartbeats + detach); generated when absent.\n */\nexport async function attachViewer(\n services: ViewerServices,\n input: { accountId: string; workspaceId: string; session: Session; viewerId?: string },\n): Promise<ViewerAttachResult> {\n const { db, settings } = services;\n const { accountId, workspaceId, session } = input;\n const viewerId = input.viewerId ?? crypto.randomUUID();\n const leaseTtlMs = settings.sandboxLeaseTtlMs;\n const sandboxGroupId = session.sandboxGroupId;\n\n const release = async (): Promise<void> => {\n await releaseLeaseHolder(db, {\n accountId,\n workspaceId,\n sandboxGroupId,\n kind: \"viewer\",\n holderId: viewerId,\n idleGraceMs: settings.sandboxIdleGraceMs,\n });\n };\n\n const acquired = await acquireLease(db, {\n accountId,\n workspaceId,\n sandboxGroupId,\n kind: \"viewer\",\n holderId: viewerId,\n subjectId: session.id,\n backend: session.sandboxBackend,\n os: session.sandboxOs,\n leaseTtlMs,\n });\n\n // FENCED: a newer epoch re-established the box. Release our just-registered\n // holder and surface a 409 — the client re-reads capabilities and re-attaches.\n if (acquired.role === \"fenced\") {\n await release();\n throw new HTTPException(409, { message: `sandbox lease superseded (epoch ${acquired.lease.leaseEpoch}); re-read capabilities and re-attach` });\n }\n\n // SPAWNER: we won the cold->warming CAS. Establish the box in-process from the\n // session's persisted envelope (warm reattach by id, or cold-restore on a\n // provider NotFound), then commit warm (the lease_epoch++ fence + fold the\n // resume envelope onto the lease). A held in-memory handle is dropped after\n // commit — the lease owns lifecycle, not this handle (non-owned by id).\n if (acquired.role === \"spawner\") {\n const expectedEpoch = acquired.lease.leaseEpoch;\n let established: EstablishedSandboxSession | undefined;\n try {\n const envelope = await getSandboxSessionEnvelope(db, workspaceId, session.id);\n // Create a cold box with the SAME stable run-environment the worker turn\n // will declare (config base + git identity + decrypted workspace env + HOME)\n // so the next turn's agent-manifest apply finds an EMPTY environment delta in\n // the SDK's validateNoEnvironmentDelta (otherwise: \"Live sandbox sessions\n // cannot change manifest environment variables\").\n const environment = await sessionAttachEnvironment(services, workspaceId, session);\n // Prefer the COLD lease's preserved resume_state when it carries a persisted\n // /workspace snapshot (confirmDrainCold keeps a minimal archive-only envelope\n // across draining->cold). establishSandboxSessionFromEnvelope cold-creates a\n // fresh box and replays the archive via hydrateWorkspace, so /workspace\n // survives the box churn (sandbox-file-persistence). No archive -> the bare\n // session envelope (a never-warmed cold start).\n const spawnEnvelope = acquired.lease.resumeState ?? envelope;\n established = await establishSandboxSessionFromEnvelope(settings, spawnEnvelope, {\n sessionId: session.id,\n backendOverride: session.sandboxBackend,\n environment,\n });\n // Fold the LIVE box into a re-resumable envelope and persist it as the\n // lease's resume_state, so EVERY later op (another viewer, a Channel-A\n // call, the reaper) resumes THIS box by id instead of cold-creating a\n // rival. Fall back to the session envelope only when serialize is\n // unavailable. (Without this the box churned: each op spawned its own box.)\n const resumeEnvelope = (await serializeEstablishedSandboxEnvelope(established)) ?? envelope ?? null;\n const committed = await commitWarmingToWarm(db, {\n accountId,\n workspaceId,\n sandboxGroupId,\n expectedEpoch,\n instanceId: established.instanceId,\n // The desktop tunnel-URL mint is P4; record null for now.\n dataPlaneUrl: null,\n resumeBackendId: established.backendId,\n resumeState: resumeEnvelope,\n leaseTtlMs,\n });\n if (!committed.committed || !committed.lease) {\n // A reaper reset our warming row (we were too slow) or a sibling\n // re-established and bumped the epoch. Release our holder and surface a\n // 409. NEVER provider-delete the box (it rides the provider idle-timeout).\n await release();\n throw new SandboxLeaseSupersededError(sandboxGroupId, expectedEpoch);\n }\n return {\n viewerId,\n liveness: committed.lease.liveness,\n leaseEpoch: committed.lease.leaseEpoch,\n sandboxGroupId,\n viewerHeartbeatIntervalMs: viewerHeartbeatIntervalMs(settings),\n dataPlaneUrl: committed.lease.dataPlaneUrl,\n };\n } catch (error) {\n if (error instanceof SandboxLeaseSupersededError) {\n throw new HTTPException(409, { message: `sandbox lease superseded (epoch ${error.leaseEpoch}); re-read capabilities and re-attach` });\n }\n // Caught spawn failure: roll the warming row back to cold so the next\n // arrival (a turn or another viewer) re-acquires and re-spawns. Holders\n // are intentionally kept by failWarmingToCold for the re-acquire; then\n // release our own holder so we don't pin a cold lease.\n await failWarmingToCold(db, { accountId, workspaceId, sandboxGroupId, expectedEpoch });\n await release();\n // Mirror the Channel-A spawner (channel-a.ts): a provider/config failure to\n // bring up the cold box is a client-actionable 409 (\"sandbox not available;\n // re-attach to retry\"), NOT a raw 500 — the warming row was just rolled back\n // to cold, so a re-attach re-acquires and re-spawns. Preserve an already-typed\n // HTTPException unchanged.\n if (error instanceof HTTPException) throw error;\n throw new HTTPException(409, { message: `sandbox not available (${error instanceof Error ? error.message : \"spawn failed\"})` });\n } finally {\n // Drop the in-process handle: the API resumed BY ID for the cold-spawn,\n // it does NOT own the box. The lease's refcount (this viewer holder) keeps\n // it warm; the reaper stops it at refcount 0.\n await dropEstablishedHandle(established);\n }\n }\n\n // ATTACHED / REARMED: the box is live (or a sibling is mid-warm). The viewer\n // holder alone keeps it warm — no establish needed (the holder lifecycle is\n // the P1.4 deliverable; P4 mints the pixel URL on the negotiation read).\n return {\n viewerId,\n liveness: acquired.lease.liveness,\n leaseEpoch: acquired.lease.leaseEpoch,\n sandboxGroupId,\n viewerHeartbeatIntervalMs: viewerHeartbeatIntervalMs(settings),\n dataPlaneUrl: acquired.lease.dataPlaneUrl,\n };\n}\n\n/**\n * Refresh a viewer holder's TTL (the app-level viewer heartbeat). Epoch-fenced:\n * a stale-epoch heartbeat (a box re-established under a newer epoch) returns\n * false and the client must re-attach. Returns whether the holder is still live.\n */\nexport async function heartbeatViewer(\n services: ViewerServices,\n input: { accountId: string; workspaceId: string; sandboxGroupId: string; viewerId: string; expectedEpoch: number },\n): Promise<boolean> {\n return await heartbeatLeaseHolder(services.db, {\n accountId: input.accountId,\n workspaceId: input.workspaceId,\n sandboxGroupId: input.sandboxGroupId,\n kind: \"viewer\",\n holderId: input.viewerId,\n leaseTtlMs: services.settings.sandboxLeaseTtlMs,\n expectedEpoch: input.expectedEpoch,\n });\n}\n\n/**\n * Release a viewer holder (the client disconnected). Idempotent: a double\n * detach (or a detach after the reaper already TTL-reaped the holder) is a\n * no-op. The box drains/stops only when no turn AND no viewer holds it.\n */\nexport async function detachViewer(\n services: ViewerServices,\n input: { accountId: string; workspaceId: string; sandboxGroupId: string; viewerId: string },\n): Promise<{ liveness: LeaseSnapshot[\"liveness\"]; refcount: number } | null> {\n return await releaseLeaseHolder(services.db, {\n accountId: input.accountId,\n workspaceId: input.workspaceId,\n sandboxGroupId: input.sandboxGroupId,\n kind: \"viewer\",\n holderId: input.viewerId,\n idleGraceMs: services.settings.sandboxIdleGraceMs,\n });\n}\n\n/** Non-locking lease snapshot for the capability-negotiation read. */\nexport async function readGroupLease(\n services: ViewerServices,\n input: { workspaceId: string; sandboxGroupId: string },\n): Promise<LeaseSnapshot | null> {\n return await readLease(services.db, input.workspaceId, input.sandboxGroupId);\n}\n\n// The viewer heartbeat cadence: half the viewer-holder TTL, floored at 5s, so a\n// single missed beat never reaps a live viewer (two beats fit inside the TTL).\nexport function viewerHeartbeatIntervalMs(settings: Settings): number {\n return Math.max(5_000, Math.floor(settings.sandboxViewerHolderTtlMs / 2));\n}\n\n// Drop a transiently-established, NON-OWNED handle. The box is owned by the LEASE\n// (resumed by id), not by this in-process handle, so we MUST NOT terminate it on\n// drop — we only release the local reference and let GC reclaim the client's\n// transport. This mirrors the worker's resume-by-id path (sandbox-resume.ts),\n// which injects the session NON-OWNED and never closes it.\n//\n// CRITICAL (deployed-integration bug, prove-it D1/D2/D5): a provider session's\n// `close()` is NOT a neutral \"free local resources\" call. For Modal,\n// `ModalSandboxSession.close()` calls `sandbox.terminate()` — it KILLS THE BOX.\n// Calling it here terminated the very box the lease had just committed warm, so\n// every viewer attach / Channel-A op spawned a box and immediately destroyed it\n// (the lease showed warm while Modal showed the box gone; reads 404'd against a\n// fresh box). We therefore DO NOT call session.close()/shutdown()/delete() — the\n// reaper (provider stop at refcount 0) is the ONLY sanctioned box terminator.\nasync function dropEstablishedHandle(established: EstablishedSandboxSession | undefined): Promise<void> {\n // Intentionally a no-op beyond dropping the reference: terminating the box here\n // is wrong (see above). The lease owns lifecycle; the reaper owns teardown.\n void established;\n}\n\n// ============================================================================\n// P4.2 — the pixel DATA PLANE, served API-DIRECT.\n//\n// mintDesktopStream resumes the WARM box BY ID in-process, idempotently ensures\n// the display stack, resolves the provider's scoped tunnel for port 6080, mints\n// the scoped per-viewer stream token, records the resolved URL on the lease under\n// the epoch fence, and (on a box rollover — a lease_epoch advance vs what the\n// caller last saw) emits a `stream.url.rotated` Channel-A event so OTHER\n// connected viewers reconnect. NO Temporal, NO worker, NO NATS req/reply: the API\n// process holds the live handle for the duration of the call and drops it on\n// return (the lease, not this handle, owns the box).\n//\n// Rotation is EVENT-DRIVEN, not a timer: the URL only changes when the box is\n// re-keyed (Modal 24h ceiling / death → re-establish under a new epoch). The\n// requester always gets the fresh cell as the HTTP response; the rotation event\n// is the out-of-band signal to the OTHER viewers of the same session.\n// ============================================================================\n\n/**\n * The desktop WIRE transport for the session's ACTIVE sandbox — the single\n * invariant that keeps the advertised transport in lockstep with where\n * mintDesktopStream routed the pixels. It MUST be derived from the ACTIVE sandbox\n * (not the session's HOME backend that negotiateCapabilities keys on): a selfhosted\n * machine serves the RELAY framebuffer (PNG-per-frame → \"relay-frames\"/\"frames\",\n * view-only in v1), and a Modal group box serves noVNC/RFB over the 6080 tunnel\n * (→ \"vnc-ws\"/\"novnc\", take-control unless the deployment disabled it). Advertising\n * relay-frames for a Modal box (the swap-away case) hands the client a dead relay\n * socket → \"desktop stream closed before it opened\"; the reverse hands a machine's\n * relay URL to the noVNC renderer. `selfhostedActive` == (activeSandboxId set AND the\n * active sandbox kind is \"selfhosted\") — EXACTLY mintDesktopStream's routing predicate.\n */\nexport function resolveActiveDesktopTransport(\n selfhostedActive: boolean,\n interactive: boolean,\n): { transport: \"relay-frames\" | \"vnc-ws\"; client: \"frames\" | \"novnc\"; mode: \"read-only\" | \"interactive\" } {\n if (selfhostedActive) {\n return { transport: \"relay-frames\", client: \"frames\", mode: \"read-only\" };\n }\n return { transport: \"vnc-ws\", client: \"novnc\", mode: interactive ? \"interactive\" : \"read-only\" };\n}\n\n/** The minted pixel cell the handshake/attach folds into the DesktopStream\n * capability. Null when degraded (no secret, headless backend, display-stack\n * failure, provider tunnel failure) — degradation is a value, never a throw. */\nexport type DesktopStreamMint = {\n url: string;\n token: string;\n expiresAt: string;\n resolution: [number, number];\n leaseEpoch: number;\n};\n\nexport type MintDesktopStreamInput = {\n accountId: string;\n workspaceId: string;\n session: Session;\n /** The viewer holder id the scoped token is minted for. */\n viewerId: string;\n /** The live lease (must be warm/draining — the box is up). A selfhosted-active\n * session may have no Modal group lease; omit and the selfhosted branch handles it. */\n lease?: LeaseSnapshot;\n /** The epoch the CALLER last observed the URL minted under. When the live\n * lease epoch is greater, the box rolled over → emit stream.url.rotated to the\n * other viewers. Omit on a first mint (no prior URL to rotate from). */\n previousEpoch?: number;\n /** Test seam: override how the box is re-established by id. Defaults to the\n * real leaf `establishSandboxSessionFromEnvelope`. Production NEVER passes\n * this; it exists so a real-lease integration test can inject a fake provider\n * session carrying `resolveExposedPort` without a live cloud box. */\n establish?: (\n envelope: Record<string, unknown> | null,\n ) => Promise<EstablishedSandboxSession>;\n /** Test seam: inject a fake relay-resolving session for the selfhosted-active\n * branch. Production NEVER passes this. */\n resolveSelfhostedSession?: (sandbox: SandboxRecord) => Promise<{ resolveExposedPort?: (port: number) => Promise<unknown> }>;\n};\n\n/**\n * Mint (or re-mint) the desktop pixel cell for a viewer against a WARM box,\n * IN-PROCESS. Returns the minted cell, or null when the desktop tier degrades\n * (no resolvable stream-token secret, a headless backend, a display-stack\n * failure, or a provider-tunnel failure) — the caller surfaces transport:null,\n * never an exception to the user.\n *\n * Idempotent display-stack + resolveExposedPort are safe to call N times. The\n * resolved URL is recorded on the lease (data_plane_url) under the epoch fence; a\n * stale-epoch write (the box re-established under a newer epoch mid-call) is a\n * no-op and we return the freshly-minted cell anyway (it is for the epoch we\n * resumed under; the next op reconciles).\n */\nexport async function mintDesktopStream(\n services: ViewerServices,\n input: MintDesktopStreamInput,\n): Promise<DesktopStreamMint | null> {\n const { db, settings, bus } = services;\n const { accountId, workspaceId, session } = input;\n const lease = input.lease;\n // The scoped token's viewerId must be a UUID (StreamTokenPayload). The GET caps\n // handshake passes grant.subjectId, which is a non-UUID for an API-key principal\n // (\"configured:key\") — coerce it to a deterministic UUID so the mint never 500s\n // (caps-500 fix). A managed-session subject (already a UUID) is unchanged.\n const viewerId = viewerIdAsUuid(input.viewerId);\n\n // GATE 1: a desktop tier that is off, headless, or lacks a stream-token secret\n // cannot mint a live URL. (The handshake's negotiateCapabilities already\n // reports the typed reason; here we just refuse to mint.)\n if (!settings.sandboxDesktopEnabled) {\n return null;\n }\n if (!desktopCapableBackend(session.sandboxBackend)) {\n return null;\n }\n const secret = resolveStreamTokenSecret(settings);\n if (!secret) {\n return null;\n }\n\n // SELFHOSTED ACTIVE: when the session's active sandbox is a selfhosted machine,\n // route to the relay (NOT the Modal group-box path — it would resume the wrong\n // box and return a Modal URL). No Modal lease required.\n if (session.activeSandboxId) {\n const active = await getSandbox(db, workspaceId, session.activeSandboxId);\n if (active?.kind === \"selfhosted\") {\n const m = await tryMintActiveSelfhostedStream(services, { session, viewerId: input.viewerId, workspaceId, port: DESKTOP_STREAM_PORT, sandbox: active }, input.resolveSelfhostedSession);\n // mintSelfhostedStream returns no resolution; the desktop cell needs it.\n return m ? { url: m.url, token: m.token, expiresAt: m.expiresAt, resolution: defaultResolution(settings), leaseEpoch: m.leaseEpoch } : null;\n }\n // A Modal swap target (or unknown) falls through to the existing group-box path.\n }\n\n // GATE 2: the box must be live (the handshake never spins one up — a cold box\n // returns lease_cold; the viewer-attach path warms it first, then mints).\n if (!lease || (lease.liveness !== \"warm\" && lease.liveness !== \"draining\")) {\n return null;\n }\n\n // FAST PATH (P4.2 perf): when the lease already holds the data-plane URL for\n // this epoch, the box is warm, exposed, and the display stack is already up.\n // Re-resuming the box by id (Modal resume-by-id is ~40s) + re-running\n // ensureDisplayStack + exposeStreamPort on EVERY stream-capabilities poll made\n // the desktop look like it was \"starting\" forever. The tunnel URL is stable for\n // the life of the (epoch-fenced) box, so mint ONLY a fresh scoped token (HMAC,\n // sub-millisecond) against the cached URL and return — no box touch at all. A\n // rollover advances the epoch and re-records dataPlaneUrl via the slow path, so\n // a cached URL here is always the current epoch's live tunnel.\n if (lease.dataPlaneUrl) {\n const nowSeconds = Math.floor(Date.now() / 1000);\n const token = await mintStreamToken(secret, {\n workspaceId,\n sessionId: session.id,\n viewerId,\n leaseEpoch: lease.leaseEpoch,\n nowSeconds,\n });\n return {\n url: lease.dataPlaneUrl,\n token,\n expiresAt: new Date((nowSeconds + STREAM_TOKEN_DEFAULT_TTL_SECONDS) * 1000).toISOString(),\n resolution: defaultResolution(settings),\n leaseEpoch: lease.leaseEpoch,\n };\n }\n\n // Resume the LIVE box by id. The lease's resume_state is authoritative (it is\n // the box the lease currently fences); fall back to the session envelope only\n // when the lease has none (a freshly-warmed lease always has it).\n const envelope = lease.resumeState ?? (await getSandboxSessionEnvelope(db, workspaceId, session.id));\n let established: EstablishedSandboxSession | undefined;\n try {\n // On a cold-restore (the lease's box is gone) this create() must carry the\n // SAME stable run-env the turn declares, so a later turn finds no env delta.\n const environment = await sessionAttachEnvironment(services, workspaceId, session);\n established = input.establish\n ? await input.establish(envelope)\n : await establishSandboxSessionFromEnvelope(settings, envelope, {\n sessionId: session.id,\n backendOverride: session.sandboxBackend,\n environment,\n });\n\n // Idempotent display stack (flock-guarded; a no-op when already up). A box\n // that genuinely can't run the stack degrades to transport:null, not a throw.\n try {\n await ensureDisplayStack(established.session);\n } catch (error) {\n if (error instanceof DisplayStackUnsupportedError) {\n return null;\n }\n throw error;\n }\n\n // Resolve the provider tunnel + mint the scoped token, IN-PROCESS.\n let exposed: Awaited<ReturnType<typeof exposeStreamPort>>;\n try {\n exposed = await exposeStreamPort(established.session, {\n workspaceId,\n sessionId: session.id,\n viewerId,\n leaseEpoch: lease.leaseEpoch,\n streamTokenSecret: secret,\n resolution: defaultResolution(settings),\n });\n } catch (error) {\n // A transient/headless provider failure degrades the desktop cell.\n if (error instanceof StreamPortUnavailableError) {\n return null;\n }\n throw error;\n }\n\n // Record the resolved URL on the lease under the epoch fence (rotation +\n // disclosure). A fence miss (the box re-established under a newer epoch\n // mid-call) is a no-op; we still return the cell we minted for our epoch.\n await recordLeaseDataPlaneUrl(db, {\n accountId,\n workspaceId,\n sandboxGroupId: session.sandboxGroupId,\n expectedEpoch: lease.leaseEpoch,\n dataPlaneUrl: exposed.url,\n });\n\n const mint: DesktopStreamMint = {\n url: exposed.url,\n token: exposed.token,\n expiresAt: exposed.expiresAt,\n resolution: exposed.resolution,\n leaseEpoch: lease.leaseEpoch,\n };\n\n // ROLLOVER ROTATION (event-driven): when the live epoch advanced past what\n // the caller last saw, the box was re-keyed → the OLD data-plane URL is\n // stale. Emit stream.url.rotated so OTHER connected viewers hot-swap their\n // noVNC socket. The requester already has the fresh cell as its response, so\n // this is purely the out-of-band signal to the rest. Best-effort: a publish\n // failure must never fail the mint.\n if (bus && input.previousEpoch !== undefined && lease.leaseEpoch > input.previousEpoch) {\n const payload: StreamUrlRotatedPayload = {\n url: exposed.url,\n token: exposed.token,\n expiresAt: exposed.expiresAt,\n leaseEpoch: lease.leaseEpoch,\n transport: \"vnc-ws\",\n viewerId,\n };\n try {\n await appendAndPublishEvents(db, bus, workspaceId, session.id, [\n { type: \"stream.url.rotated\", payload },\n ]);\n } catch {\n // The durable SSE spine retries; a dropped publish here is not fatal.\n }\n }\n\n return mint;\n } catch {\n // Any other failure (resume error, exec error) degrades the desktop cell to\n // transport:null rather than failing the whole handshake — Channel-A still\n // works. The capability resolver reports the desktop as available; the live\n // URL is simply absent until the next op succeeds.\n return null;\n } finally {\n await dropEstablishedHandle(established);\n }\n}\n\n// ============================================================================\n// P5.t — the REAL PTY terminal DATA PLANE, served API-DIRECT.\n//\n// mintTerminalStream is the EXACT terminal twin of mintDesktopStream: it resumes\n// the WARM box BY ID in-process, idempotently ensures the ttyd PTY-over-websocket\n// server (ensureTerminalServer), resolves the provider's scoped tunnel for port\n// 7681 (a SEPARATE tunnel from the 6080 desktop noVNC → a different URL), mints\n// the scoped per-viewer stream token, and records the resolved URL on the lease's\n// terminal_data_plane_url column under the epoch fence. The fast-path re-mints\n// ONLY a fresh token against the cached terminal URL (no box touch).\n//\n// It does NOT require the desktop to be on — it gates on the separate\n// sandboxTerminalEnabled toggle. Degradation (no secret, headless backend, ttyd\n// failure, provider tunnel failure) returns null → the Terminal cell falls back\n// to the read-only sse-events firehose (a value, never a throw).\n// ============================================================================\n\n/** The minted terminal cell the handshake/attach folds into the Terminal\n * capability (pty-ws). Null when degraded — the caller surfaces transport\n * \"sse-events\" (the read-only firehose), never an exception. */\nexport type TerminalStreamMint = {\n url: string;\n token: string;\n expiresAt: string;\n leaseEpoch: number;\n};\n\nexport type MintTerminalStreamInput = {\n accountId: string;\n workspaceId: string;\n session: Session;\n /** The viewer holder / principal id the scoped token is minted for. */\n viewerId: string;\n /** The live lease (must be warm/draining — the box is up). A selfhosted-active\n * session may have no Modal group lease; omit and the selfhosted branch handles it. */\n lease?: LeaseSnapshot;\n /** Test seam: override how the box is re-established by id (see\n * MintDesktopStreamInput.establish). Production NEVER passes this. */\n establish?: (\n envelope: Record<string, unknown> | null,\n ) => Promise<EstablishedSandboxSession>;\n /** Test seam: inject a fake relay-resolving session for the selfhosted-active\n * branch. Production NEVER passes this. */\n resolveSelfhostedSession?: (sandbox: SandboxRecord) => Promise<{ resolveExposedPort?: (port: number) => Promise<unknown> }>;\n};\n\n/**\n * Mint (or re-mint) the REAL PTY (ttyd pty-ws) terminal cell for a viewer against\n * a WARM box, IN-PROCESS. Returns the minted cell, or null when the terminal tier\n * degrades (terminal off, no resolvable stream-token secret, a headless backend,\n * a ttyd-launch failure, or a provider-tunnel failure) — the caller surfaces the\n * sse-events firehose, never an exception to the user. Mirrors mintDesktopStream.\n */\nexport async function mintTerminalStream(\n services: ViewerServices,\n input: MintTerminalStreamInput,\n): Promise<TerminalStreamMint | null> {\n const { db, settings } = services;\n const { accountId, workspaceId, session } = input;\n const lease = input.lease;\n // Same caps-500 fix as the desktop mint: coerce a non-UUID principal id\n // (grant.subjectId = \"configured:key\" for an API key) to a deterministic UUID\n // so StreamTokenPayload.parse never throws an uncaught 500.\n const viewerId = viewerIdAsUuid(input.viewerId);\n\n // GATE 1: the terminal pty-ws plane requires the toggle ON, a real-PTY backend\n // (desktop-capable images bake ttyd), and a resolvable stream-token secret.\n if (!settings.sandboxTerminalEnabled) {\n return null;\n }\n if (!desktopCapableBackend(session.sandboxBackend)) {\n return null;\n }\n const secret = resolveStreamTokenSecret(settings);\n if (!secret) {\n return null;\n }\n\n // SELFHOSTED ACTIVE: when the session's active sandbox is a selfhosted machine,\n // route to the relay. NEVER fall through to the Modal group-box path (it would\n // resume the wrong box / return a Modal URL).\n if (session.activeSandboxId) {\n const active = await getSandbox(db, workspaceId, session.activeSandboxId);\n if (active?.kind === \"selfhosted\") {\n return await tryMintActiveSelfhostedStream(services, { session, viewerId: input.viewerId, workspaceId, port: TERMINAL_STREAM_PORT, sandbox: active }, input.resolveSelfhostedSession);\n }\n // A Modal swap target (or unknown) falls through to the existing group-box path\n // (unchanged — Modal swap-target streaming is out of scope for this fix).\n }\n\n // GATE 2: the box must be live (the handshake never spins one up).\n if (!lease || (lease.liveness !== \"warm\" && lease.liveness !== \"draining\")) {\n return null;\n }\n\n // FAST PATH: the terminal tunnel URL is stable for the life of the (epoch-fenced)\n // box, so when the lease already caches it, mint ONLY a fresh scoped token (HMAC,\n // sub-millisecond) against the cached URL — no box resume/exec at all. A rollover\n // advances the epoch and clears terminalDataPlaneUrl (commitWarmingToWarm), so a\n // cached URL here is always the current epoch's live ttyd tunnel.\n if (lease.terminalDataPlaneUrl) {\n const nowSeconds = Math.floor(Date.now() / 1000);\n const token = await mintStreamToken(secret, {\n workspaceId,\n sessionId: session.id,\n viewerId,\n leaseEpoch: lease.leaseEpoch,\n port: TERMINAL_STREAM_PORT,\n nowSeconds,\n });\n return {\n url: lease.terminalDataPlaneUrl,\n token,\n expiresAt: new Date((nowSeconds + STREAM_TOKEN_DEFAULT_TTL_SECONDS) * 1000).toISOString(),\n leaseEpoch: lease.leaseEpoch,\n };\n }\n\n // Resume the LIVE box by id (lease.resume_state authoritative), ensure ttyd, and\n // resolve the 7681 tunnel + mint the scoped token, IN-PROCESS.\n const envelope = lease.resumeState ?? (await getSandboxSessionEnvelope(db, workspaceId, session.id));\n let established: EstablishedSandboxSession | undefined;\n try {\n // On a cold-restore this create() must carry the SAME stable run-env the turn\n // declares, so a later turn finds no manifest-env delta.\n const environment = await sessionAttachEnvironment(services, workspaceId, session);\n established = input.establish\n ? await input.establish(envelope)\n : await establishSandboxSessionFromEnvelope(settings, envelope, {\n sessionId: session.id,\n backendOverride: session.sandboxBackend,\n environment,\n });\n\n // Idempotent ttyd launch (flock-guarded; a no-op when already up). A box that\n // genuinely can't run it degrades to the sse-events firehose, not a throw.\n try {\n await ensureTerminalServer(established.session, { port: TERMINAL_STREAM_PORT });\n } catch (error) {\n if (error instanceof TerminalServerUnsupportedError) {\n return null;\n }\n throw error;\n }\n\n // Resolve the provider tunnel for 7681 + mint the scoped token, IN-PROCESS.\n // exposeStreamPort is port-agnostic; it returns transport \"vnc-ws\"/client\n // \"novnc\" tags we ignore for the terminal (the contract carries pty-ws) — we\n // use only its url/token/expiresAt.\n let exposed: Awaited<ReturnType<typeof exposeStreamPort>>;\n try {\n exposed = await exposeStreamPort(established.session, {\n workspaceId,\n sessionId: session.id,\n viewerId,\n leaseEpoch: lease.leaseEpoch,\n streamTokenSecret: secret,\n port: TERMINAL_STREAM_PORT,\n });\n } catch (error) {\n if (error instanceof StreamPortUnavailableError) {\n return null;\n }\n throw error;\n }\n\n // Record the resolved terminal URL on the lease under the epoch fence. A fence\n // miss (box re-established under a newer epoch mid-call) is a no-op; we still\n // return the cell we minted for our epoch.\n await recordLeaseTerminalDataPlaneUrl(db, {\n accountId,\n workspaceId,\n sandboxGroupId: session.sandboxGroupId,\n expectedEpoch: lease.leaseEpoch,\n terminalDataPlaneUrl: exposed.url,\n });\n\n return {\n url: exposed.url,\n token: exposed.token,\n expiresAt: exposed.expiresAt,\n leaseEpoch: lease.leaseEpoch,\n };\n } catch {\n // Any other failure degrades the terminal pty-ws cell to the sse-events\n // firehose rather than failing the whole handshake.\n return null;\n } finally {\n await dropEstablishedHandle(established);\n }\n}\n\n// ============================================================================\n// M8b — the SELFHOSTED relay stream cell.\n//\n// When the session's ACTIVE sandbox is a selfhosted machine (a swap target, or the\n// session's own selfhosted group box), the desktop/terminal stream does NOT ride a\n// Modal provider tunnel — it rides the `opengeni-relay` edge. The selfhosted\n// session's `resolveExposedPort(port)` returns the relay URL SHAPE (host/port/tls/\n// path + the `ws=&agent=&port=&channel=` routing query), and exposeStreamPort mints\n// the scoped `ogs_` token. The CRITICAL M8b seam: the token is fenced by the swap\n// `active_epoch` (NOT the Modal lease epoch), so the relay's stale-viewer fence\n// rejects a viewer whose token predates a swap-away — it cannot reach a machine the\n// session swapped off of. control ops are already active-epoch-fenced (the routing\n// proxy); this closes the STREAM side.\n// ============================================================================\n\n// Build a ControlRpc backed by the NATS events bus (mirrors fleet.ts:controlRpc).\nfunction controlRpc(bus: EventBus | undefined): ControlRpc {\n return new NatsControlRpc(async (): Promise<NatsRequestConnection | null> => {\n if (!bus) {\n return null;\n }\n return bus.getRequestConnection();\n });\n}\n\n/**\n * Mint the relay stream cell against the session's ACTIVE selfhosted machine,\n * fenced by active_epoch. Returns null (degrade, never throw) when the active\n * sandbox is not selfhosted, the agent is offline, or the relay channel can't be\n * ensured. `sandbox` is passed in already-fetched to avoid a duplicate getSandbox.\n */\nasync function tryMintActiveSelfhostedStream(\n services: ViewerServices,\n input: { session: Session; viewerId: string; workspaceId: string; port: number; sandbox: SandboxRecord },\n // optional test seam (mirrors the existing `establish?` seam pattern): inject a\n // fake relay-resolving session; production NEVER passes it.\n resolveSelfhostedSession?: (sandbox: SandboxRecord) => Promise<{ resolveExposedPort?: (port: number) => Promise<unknown> }>,\n): Promise<TerminalStreamMint | null> {\n const { settings, bus } = services;\n const { session, workspaceId, port, sandbox } = input;\n if (!sandbox.enrollmentId) {\n return null;\n }\n // The relay needs NATS; degrade to null without a bus.\n if (!bus && !resolveSelfhostedSession) {\n return null;\n }\n let shSession: { resolveExposedPort?: (port: number) => Promise<unknown> };\n try {\n if (resolveSelfhostedSession) {\n shSession = await resolveSelfhostedSession(sandbox);\n } else {\n const client = new SelfhostedSandboxClient({\n workspaceId,\n relay: relayConfigFromSettings(settings),\n controlRpcFactory: () => controlRpc(bus),\n agentId: sandbox.enrollmentId,\n epoch: session.activeEpoch,\n });\n shSession = await client.resume({ agentId: sandbox.enrollmentId });\n }\n } catch (error) {\n console.warn(\n `[tryMintActiveSelfhostedStream] resume failed for agent=${sandbox.enrollmentId} ` +\n `port=${input.port} epoch=${session.activeEpoch}: ` +\n `${error instanceof Error ? error.message : String(error)}`,\n );\n return null;\n }\n return mintSelfhostedStream(services, {\n workspaceId,\n sessionId: session.id,\n viewerId: input.viewerId,\n activeEpoch: session.activeEpoch,\n port,\n session: shSession,\n });\n}\n\n/** The structural slice of a selfhosted session the relay stream mint needs. */\ntype RelayResolvableSession = {\n resolveExposedPort?: (port: number) => Promise<unknown>;\n};\n\nexport type MintSelfhostedStreamInput = {\n workspaceId: string;\n sessionId: string;\n /** The viewer holder / principal id the scoped token is minted for. */\n viewerId: string;\n /** The swap fence: the session's `active_epoch`. The minted `ogs_` token carries\n * THIS as its leaseEpoch claim so the relay rejects a stale-epoch (swapped-away)\n * viewer. */\n activeEpoch: number;\n /** The exposed stream port (6080 desktop / 7681 terminal). */\n port: number;\n /** The resolvable selfhosted session (the routing proxy resolves the active\n * selfhosted backend; its `resolveExposedPort` returns the relay endpoint). */\n session: RelayResolvableSession;\n};\n\n/**\n * Mint the selfhosted relay stream cell for a viewer against the session's ACTIVE\n * selfhosted machine, IN-PROCESS. Resolves the relay endpoint via the selfhosted\n * session's `resolveExposedPort` and mints the scoped `ogs_` token FENCED BY THE\n * SWAP `active_epoch`. Returns null when the stream tier degrades (no stream-token\n * secret, the agent is offline / cannot ensure a channel) — the caller surfaces\n * transport:null, never an exception.\n *\n * The token is RECORDED against the viewer holder by the caller and is NEVER a URL\n * query param (the relay validates the in-band token); the relay's stale-viewer\n * fence uses the token's leaseEpoch claim (== activeEpoch here).\n */\nexport async function mintSelfhostedStream(\n services: ViewerServices,\n input: MintSelfhostedStreamInput,\n): Promise<TerminalStreamMint | null> {\n const { settings } = services;\n const secret = resolveStreamTokenSecret(settings);\n if (!secret) {\n return null;\n }\n const viewerId = viewerIdAsUuid(input.viewerId);\n if (typeof input.session?.resolveExposedPort !== \"function\") {\n return null;\n }\n try {\n // exposeStreamPort threads the epoch we pass into the `ogs_` token's leaseEpoch\n // claim. For selfhosted we pass the swap active_epoch — THE fence the relay\n // enforces so a swapped-away viewer is rejected.\n const exposed = await exposeStreamPort(input.session, {\n workspaceId: input.workspaceId,\n sessionId: input.sessionId,\n viewerId,\n leaseEpoch: input.activeEpoch,\n streamTokenSecret: secret,\n port: input.port,\n });\n return {\n url: exposed.url,\n token: exposed.token,\n expiresAt: exposed.expiresAt,\n leaseEpoch: input.activeEpoch,\n };\n } catch (error) {\n // A headless / offline / channel-ensure failure degrades the cell to\n // transport:null rather than throwing (mirrors the Modal mint paths). The\n // mint degrades SILENTLY to the client, so log WHY here — otherwise a relay\n // ensure failure (agent display probe, producer dial) is invisible.\n console.warn(\n `[mintSelfhostedStream] relay stream mint degraded to transport:null ` +\n `(session=${input.sessionId} port=${input.port} epoch=${input.activeEpoch}): ` +\n `${error instanceof Error ? error.message : String(error)}`,\n );\n if (error instanceof StreamPortUnavailableError) {\n return null;\n }\n return null;\n }\n}\n\n// The framebuffer geometry from settings (streamResolutionWidth/Height; default\n// 1280x800, the spike's proven geometry).\nfunction defaultResolution(settings: Settings): [number, number] {\n return [settings.streamResolutionWidth, settings.streamResolutionHeight];\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Coerce a viewer/principal id into a valid UUID for the scoped stream-token\n * payload (StreamTokenPayload.viewerId is z.string().uuid()).\n *\n * The GET stream-capabilities handshake mints a token scoped to the CALLING\n * PRINCIPAL — grant.subjectId — which for an API-key principal is a NON-UUID like\n * \"configured:key\". Passing that straight to mintStreamToken threw a ZodError in\n * StreamTokenPayload.parse, which escaped as an uncaught 500 (caps-500 bug). The\n * browser's managed-session subject IS a UUID and is returned unchanged, so it is\n * unaffected. A non-UUID principal is mapped to a DETERMINISTIC v5-shaped UUID\n * (SHA-256 of the raw id, RFC-4122 version/variant bits set) so the same\n * principal always mints the same viewerId (stable scoping; idempotent re-mint).\n */\nfunction viewerIdAsUuid(rawViewerId: string): string {\n if (UUID_RE.test(rawViewerId)) {\n return rawViewerId;\n }\n const hex = createHash(\"sha256\").update(`opengeni:stream-viewer:${rawViewerId}`).digest(\"hex\");\n // Shape the first 16 bytes as a version-5 UUID (deterministic, name-based).\n const b = hex.slice(0, 32).split(\"\");\n b[12] = \"5\"; // version 5\n const variantNibble = (parseInt(b[16]!, 16) & 0x3) | 0x8; // RFC-4122 variant (8-b)\n b[16] = variantNibble.toString(16);\n const s = b.join(\"\");\n return `${s.slice(0, 8)}-${s.slice(8, 12)}-${s.slice(12, 16)}-${s.slice(16, 20)}-${s.slice(20, 32)}`;\n}\n","import type { SessionEvent } from \"@opengeni/contracts\";\nimport { listSessionEvents, type Database } from \"@opengeni/db\";\nimport { formatSse, type EventBus } from \"@opengeni/events\";\n\nexport async function sseSessionStream(db: Database, bus: EventBus, workspaceId: string, sessionId: string, after: number, signal: AbortSignal): Promise<Response> {\n const encoder = new TextEncoder();\n let controller: ReadableStreamDefaultController<Uint8Array>;\n let lastSent = after;\n let replaying = true;\n const buffered: SessionEvent[] = [];\n let unsubscribe: (() => void) | null = null;\n\n const stream = new ReadableStream<Uint8Array>({\n start: async (rawController) => {\n controller = rawController;\n const send = async (event: SessionEvent) => {\n if (event.sequence <= lastSent) {\n return;\n }\n if (event.sequence > lastSent + 1) {\n const missing = await listSessionEvents(db, workspaceId, sessionId, lastSent, event.sequence - lastSent - 1);\n for (const missed of missing) {\n if (missed.sequence > lastSent) {\n controller.enqueue(encoder.encode(formatSse(missed)));\n lastSent = missed.sequence;\n }\n }\n }\n controller.enqueue(encoder.encode(formatSse(event)));\n lastSent = event.sequence;\n };\n\n unsubscribe = await bus.subscribe(workspaceId, sessionId, async (events) => {\n if (replaying) {\n buffered.push(...events);\n return;\n }\n for (const event of events.sort((a, b) => a.sequence - b.sequence)) {\n await send(event);\n }\n });\n\n await replaySessionEvents((cursor, limit) => listSessionEvents(db, workspaceId, sessionId, cursor, limit), send, after);\n replaying = false;\n for (const event of buffered.sort((a, b) => a.sequence - b.sequence)) {\n await send(event);\n }\n buffered.length = 0;\n controller.enqueue(encoder.encode(\": connected\\n\\n\"));\n },\n cancel: () => {\n unsubscribe?.();\n },\n });\n\n signal.addEventListener(\"abort\", () => {\n unsubscribe?.();\n }, { once: true });\n\n return new Response(stream, {\n headers: {\n \"Content-Type\": \"text/event-stream; charset=utf-8\",\n \"Cache-Control\": \"no-cache, no-transform\",\n Connection: \"keep-alive\",\n },\n });\n}\n\nexport async function replaySessionEvents(\n loadPage: (after: number, limit: number) => Promise<SessionEvent[]>,\n send: (event: SessionEvent) => Promise<void>,\n after: number,\n pageSize = 1000,\n): Promise<void> {\n let cursor = after;\n while (true) {\n const page = await loadPage(cursor, pageSize);\n if (page.length === 0) {\n return;\n }\n for (const event of page.sort((a, b) => a.sequence - b.sequence)) {\n await send(event);\n cursor = Math.max(cursor, event.sequence);\n }\n if (page.length < pageSize) {\n return;\n }\n }\n}\n","import {\n CreateSocialConnectionRequest,\n CreateSocialPostRequest,\n} from \"@opengeni/contracts\";\nimport {\n createSocialConnection,\n createSocialPost,\n listSocialConnections,\n listSocialPosts,\n} from \"@opengeni/db\";\nimport type { Hono } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport { z } from \"zod\";\nimport { requireAccessGrant } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\nimport { boundedLimit } from \"../http/common\";\n\nexport function registerSocialRoutes(app: Hono, deps: ApiRouteDeps): void {\n const { db } = deps;\n\n app.get(\"/v1/workspaces/:workspaceId/social/connections\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n return c.json(await listSocialConnections(db, workspaceId, boundedLimit(c.req.query(\"limit\"))));\n });\n\n app.post(\"/v1/workspaces/:workspaceId/social/connections\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const payload = CreateSocialConnectionRequest.parse(await c.req.json());\n try {\n return c.json(await createSocialConnection(db, {\n accountId: grant.accountId,\n workspaceId,\n provider: payload.provider,\n accountHandle: payload.accountHandle,\n accountName: payload.accountName ?? null,\n externalAccountId: payload.externalAccountId ?? null,\n status: payload.status,\n scopes: payload.scopes,\n credentialRef: payload.credentialRef ?? null,\n tokenMetadata: payload.tokenMetadata,\n metadata: payload.metadata,\n }), 201);\n } catch (error) {\n throw socialHttpException(error);\n }\n });\n\n app.get(\"/v1/workspaces/:workspaceId/social/posts\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n const since = parseSince(c.req.query(\"since\"));\n const connectionIds = parseConnectionIds(c.req.query(\"connectionIds\") ?? c.req.query(\"connectionId\"));\n return c.json(await listSocialPosts(db, {\n workspaceId,\n ...(connectionIds?.length ? { connectionIds } : {}),\n ...(since ? { since } : {}),\n limit: boundedLimit(c.req.query(\"limit\")),\n }));\n });\n\n app.post(\"/v1/workspaces/:workspaceId/social/posts\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const payload = CreateSocialPostRequest.parse(await c.req.json());\n try {\n return c.json(await createSocialPost(db, {\n accountId: grant.accountId,\n workspaceId,\n connectionId: payload.connectionId,\n externalPostId: payload.externalPostId ?? null,\n url: payload.url ?? null,\n authorHandle: payload.authorHandle ?? null,\n text: payload.text,\n publishedAt: new Date(payload.publishedAt),\n metrics: payload.metrics,\n raw: payload.raw,\n }), 201);\n } catch (error) {\n throw socialHttpException(error);\n }\n });\n}\n\nfunction parseSince(raw: string | undefined): Date | undefined {\n if (!raw) {\n return undefined;\n }\n const since = new Date(raw);\n if (Number.isNaN(since.getTime())) {\n throw new HTTPException(422, { message: \"since must be an ISO date-time\" });\n }\n return since;\n}\n\nfunction parseConnectionIds(raw: string | undefined): string[] | undefined {\n if (!raw) {\n return undefined;\n }\n const values = raw.split(\",\").map((value) => value.trim()).filter(Boolean);\n const parsed = z.array(z.string().uuid()).safeParse(values);\n if (!parsed.success) {\n throw new HTTPException(422, { message: \"connectionIds must be a comma-separated list of UUIDs\" });\n }\n const ids = parsed.data;\n return [...new Set(ids)];\n}\n\nfunction socialHttpException(error: unknown): HTTPException {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"not found\")) {\n return new HTTPException(404, { message });\n }\n if (message.includes(\"duplicate key\")) {\n return new HTTPException(409, { message: \"social connection or post already exists\" });\n }\n return new HTTPException(500, { message });\n}\n","import {\n AddWorkspaceMemberRequest,\n CreateWorkspaceRequest,\n ListWorkspaceMembersResponse,\n UpdateWorkspaceMemberRequest,\n UpdateWorkspaceRequest,\n Workspace,\n WorkspaceMember,\n type AccessContext,\n type Permission,\n} from \"@opengeni/contracts\";\nimport {\n allWorkspacePermissions,\n countActiveSessionsForWorkspace,\n countWorkspacesForAccount,\n createWorkspace,\n deleteWorkspace,\n getManagedUserByEmail,\n grantWorkspaceAccess,\n listScheduledTasks,\n listWorkspaceMembers,\n listWorkspacesForSubject,\n removeWorkspaceMember,\n requireWorkspace,\n updateWorkspace,\n} from \"@opengeni/db\";\nimport type { Hono } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport { hasPermission, requireAccessContext, requireAccessGrant } from \"@opengeni/core\";\nimport { requireLimit } from \"@opengeni/core\";\nimport type { ApiRouteDeps } from \"@opengeni/core\";\nimport { assertWorkspaceDeletable, assertWorkspaceMemberRemovable, resolveMemberSubjectId } from \"@opengeni/core\";\n\nexport function registerWorkspaceRoutes(app: Hono, deps: ApiRouteDeps): void {\n app.get(\"/v1/access/me\", async (c) => {\n return c.json(await requireAccessContext(c, deps));\n });\n\n app.get(\"/v1/workspaces\", async (c) => {\n const context = await requireAccessContext(c, deps);\n const readableWorkspaceIds = [...new Set(context.workspaceGrants\n .filter((grant) => hasPermission(grant.permissions, \"workspace:read\"))\n .map((grant) => grant.workspaceId))];\n if (readableWorkspaceIds.length > 0) {\n const workspaces = await Promise.all(readableWorkspaceIds.map((workspaceId) => requireWorkspace(deps.db, workspaceId)));\n return c.json(workspaces.map((workspace) => Workspace.parse(workspace)));\n }\n return c.json((await listWorkspacesForSubject(deps.db, context.subjectId)).map((workspace) => Workspace.parse(workspace)));\n });\n\n app.post(\"/v1/workspaces\", async (c) => {\n const context = await requireAccessContext(c, deps);\n const payload = CreateWorkspaceRequest.parse(await c.req.json());\n const accountId = payload.accountId ?? context.defaultAccountId;\n if (!accountId) {\n throw new HTTPException(409, { message: \"account selection is required\" });\n }\n requireAccountPermission(context, accountId, \"workspace:create\");\n await requireLimit(deps, { accountId, action: \"workspace:create\", quantity: 1 });\n const workspace = await createWorkspace(deps.db, {\n accountId,\n name: payload.name.trim(),\n slug: payload.slug?.trim() || null,\n externalSource: payload.externalSource ?? null,\n externalId: payload.externalId ?? null,\n ...(payload.agentInstructions !== undefined ? { agentInstructions: normalizeAgentInstructions(payload.agentInstructions) } : {}),\n });\n await grantWorkspaceAccess(deps.db, {\n accountId,\n workspaceId: workspace.id,\n subjectId: context.subjectId,\n role: \"owner\",\n permissions: allWorkspacePermissions,\n ...(context.subjectLabel ? { subjectLabel: context.subjectLabel } : {}),\n });\n return c.json(Workspace.parse(workspace), 201);\n });\n\n app.get(\"/v1/workspaces/:workspaceId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n return c.json(Workspace.parse(await requireWorkspace(deps.db, workspaceId)));\n });\n\n app.patch(\"/v1/workspaces/:workspaceId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n const payload = UpdateWorkspaceRequest.parse(await c.req.json());\n const workspace = await updateWorkspace(deps.db, workspaceId, {\n ...(payload.name !== undefined ? { name: payload.name.trim() } : {}),\n ...(payload.slug !== undefined ? { slug: payload.slug?.trim() || null } : {}),\n ...(payload.agentInstructions !== undefined ? { agentInstructions: normalizeAgentInstructions(payload.agentInstructions) } : {}),\n });\n return c.json(Workspace.parse(workspace));\n });\n\n app.delete(\"/v1/workspaces/:workspaceId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"workspace:admin\");\n // Refuse before any external/DB mutation: never delete the account's only\n // workspace, and never delete while a session could still be running in\n // Temporal (there is no clean per-session terminate to call, so deleting\n // the row would orphan the workflow — the operator must stop them first).\n const [workspaceCountForAccount, activeSessionCount] = await Promise.all([\n countWorkspacesForAccount(deps.db, grant.accountId),\n countActiveSessionsForWorkspace(deps.db, workspaceId),\n ]);\n assertWorkspaceDeletable({ workspaceCountForAccount, activeSessionCount });\n // Clean external Temporal state the FK cascade can't reach: every scheduled\n // task's schedule (best-effort, mirroring the scheduled-task delete path).\n const tasks = await listScheduledTasks(deps.db, workspaceId, 1000);\n await Promise.all(tasks.map((task) =>\n deps.workflowClient.deleteScheduledTaskSchedule({ temporalScheduleId: task.temporalScheduleId }).catch(() => undefined),\n ));\n await deleteWorkspace(deps.db, workspaceId);\n return c.body(null, 204);\n });\n\n // --- Members (\"People with access\") ---------------------------------------\n\n app.get(\"/v1/workspaces/:workspaceId/members\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n await requireAccessGrant(c, deps, workspaceId, \"workspace:read\");\n const members = await listWorkspaceMembers(deps.db, workspaceId);\n return c.json(ListWorkspaceMembersResponse.parse({ members }));\n });\n\n app.post(\"/v1/workspaces/:workspaceId/members\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"members:manage\");\n const payload = AddWorkspaceMemberRequest.parse(await c.req.json());\n const email = payload.email.trim();\n // Email invites for not-yet-registered users are deferred: an unknown email\n // resolves to null, which resolveMemberSubjectId turns into a 404.\n const subjectId = resolveMemberSubjectId(await getManagedUserByEmail(deps.db, email));\n await grantWorkspaceAccess(deps.db, {\n accountId: grant.accountId,\n workspaceId,\n subjectId,\n subjectLabel: email,\n ...(payload.role !== undefined ? { role: payload.role } : {}),\n permissions: payload.permissions,\n });\n const members = await listWorkspaceMembers(deps.db, workspaceId);\n const member = members.find((candidate) => candidate.subjectId === subjectId);\n if (!member) {\n throw new HTTPException(500, { message: \"failed to add member\" });\n }\n return c.json(WorkspaceMember.parse(member), 201);\n });\n\n app.patch(\"/v1/workspaces/:workspaceId/members/:subjectId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"members:manage\");\n const subjectId = decodeURIComponent(c.req.param(\"subjectId\"));\n const payload = UpdateWorkspaceMemberRequest.parse(await c.req.json());\n const existing = await listWorkspaceMembers(deps.db, workspaceId);\n const current = existing.find((member) => member.subjectId === subjectId);\n if (!current) {\n throw new HTTPException(404, { message: \"member not found\" });\n }\n await grantWorkspaceAccess(deps.db, {\n accountId: grant.accountId,\n workspaceId,\n subjectId,\n ...(current.subjectLabel ? { subjectLabel: current.subjectLabel } : {}),\n role: payload.role ?? current.role,\n permissions: payload.permissions,\n });\n const members = await listWorkspaceMembers(deps.db, workspaceId);\n const member = members.find((candidate) => candidate.subjectId === subjectId);\n if (!member) {\n throw new HTTPException(500, { message: \"failed to update member\" });\n }\n return c.json(WorkspaceMember.parse(member));\n });\n\n app.delete(\"/v1/workspaces/:workspaceId/members/:subjectId\", async (c) => {\n const workspaceId = c.req.param(\"workspaceId\");\n const grant = await requireAccessGrant(c, deps, workspaceId, \"members:manage\");\n const subjectId = decodeURIComponent(c.req.param(\"subjectId\"));\n const members = await listWorkspaceMembers(deps.db, workspaceId);\n // Never remove yourself, and never remove the last administering member.\n assertWorkspaceMemberRemovable({ members, subjectId, callerSubjectId: grant.subjectId });\n await removeWorkspaceMember(deps.db, workspaceId, subjectId);\n return c.body(null, 204);\n });\n}\n\n// A persona override that is null or trims to empty collapses to null (use the\n// deployment default). Otherwise the template is stored verbatim so the runtime\n// can substitute the non-bypassable CORE at its {{core}} marker.\nfunction normalizeAgentInstructions(value: string | null): string | null {\n if (value === null) {\n return null;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : null;\n}\n\nfunction requireAccountPermission(context: AccessContext, accountId: string, permission: Permission): void {\n const grant = context.accountGrants.find((candidate) => candidate.accountId === accountId);\n if (!grant || (!grant.permissions.includes(permission) && !grant.permissions.includes(\"account:admin\"))) {\n throw new HTTPException(403, { message: `missing permission: ${permission}` });\n }\n}\n"],"mappings":"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","names":["WebStandardStreamableHTTPServerTransport","HTTPException","requireAccessGrant","requireLimit","trimmedEnvironmentName","installationId","getSession","HTTPException","encryptEnvironmentValue","createSignedState","HTTPException","requireAccessGrant","listDocumentBases","searchDocuments","HTTPException","requireAccessGrant","recordWorkspaceUsage","requireLimit","McpServer","requireAccessGrant","listDocumentBases","HTTPException","requireLimit","recordWorkspaceUsage","searchDocuments","HTTPException","requireAccessGrant","record","HTTPException","record","requireAccessGrant","getEnrollment","HTTPException","requireAccessGrant","buildFleetContextForSession","swapActiveSandbox","getSession","listEnrollments","HTTPException","requireAccessGrant","getEnrollment","buildFleetContextForSession","swapActiveSandbox","WorkspaceEnvironmentVariableName","countWorkspaceEnvironments","createWorkspaceEnvironment","encryptEnvironmentValue","getWorkspaceEnvironmentByName","listWorkspaceEnvironments","setWorkspaceEnvironmentVariable","HTTPException","requireAccessGrant","assertAllowedEnvironmentVariableName","MAX_ENVIRONMENTS_PER_WORKSPACE","MAX_VARIABLES_PER_ENVIRONMENT","recordEnvironmentAuditEvent","requireEnvironmentEncryption","requireFile","HTTPException","requireAccessGrant","recordWorkspaceUsage","requireLimit","HTTPException","requireAccessGrant","requireLimit","HTTPException","listGitHubInstallationIdsForWorkspace","createSignedState","GitHubAppConfigurationError","githubAppMissingSettings","listGitHubAppRepositories","readSignedState","stateMaxAgeSeconds","HTTPException","requireAccessGrant","escapeHtml","listSocialConnections","getDocumentBase","HTTPException","requireAccessGrant","requireLimit","createValidatedScheduledTask","syncCreatedScheduledTask","CreateScheduledTaskRequest","UpdateScheduledTaskRequest","deleteScheduledTask","listScheduledTaskRuns","listScheduledTasks","updateScheduledTask","requireAccessGrant","recordWorkspaceUsage","requireLimit","createValidatedScheduledTask","manualScheduledTaskTriggerUsageKey","manualScheduledTaskTriggerWorkflowId","scheduledTaskToolsProvided","scheduledTaskTriggerToken","syncCreatedScheduledTask","syncUpdatedScheduledTask","validatedScheduledTaskUpdate","requireAccessGrant","CreateScheduledTaskRequest","requireLimit","createValidatedScheduledTask","scheduledTaskToolsProvided","syncCreatedScheduledTask","listScheduledTasks","UpdateScheduledTaskRequest","validatedScheduledTaskUpdate","updateScheduledTask","syncUpdatedScheduledTask","scheduledTaskTriggerToken","manualScheduledTaskTriggerUsageKey","manualScheduledTaskTriggerWorkflowId","recordWorkspaceUsage","deleteScheduledTask","listScheduledTaskRuns","getSandbox","getSession","getSessionGoal","listSessionEvents","listSessions","requireSession","setSessionGoalStatus","appendAndPublishEvents","readLease","appendAndPublishEvents","HTTPException","HTTPException","requireAccessGrant","applyGitAuthPointerEnvironment","hasGitHubRepositorySelection","stableSandboxEnvironmentForRun","githubAppBotIdentity","acquireLease","commitWarmingToWarm","failWarmingToCold","getSandboxSessionEnvelope","loadWorkspaceEnvironmentForRun","readLease","releaseLeaseHolder","SandboxLeaseSupersededError","appendAndPublishEvents","HTTPException","establishSandboxSessionFromEnvelope","NatsControlRpc","serializeEstablishedSandboxEnvelope","relayConfigFromSettings","dropEstablishedHandle","controlRpc","acceptSessionUserMessage","createSessionForRequest","updateSessionTitle","workflowIdForSession","listSessionEvents","requireAccessGrant","createSessionForRequest","listSessions","getSession","HTTPException","updateSessionTitle","getSessionGoal","goal","changed","setSessionGoalStatus","appendAndPublishEvents","workflowIdForSession","requireSession","listSessionEvents","accepted","acceptSessionUserMessage","getSandbox","listSocialConnections","listSocialPosts","HTTPException","z","requireAccessGrant","requireAccessGrant","listSocialConnections","listSocialPosts","HTTPException","z","listScheduledTasks","HTTPException","hasPermission","requireAccessContext","requireAccessGrant","requireLimit","member","normalizeResources","validateFileResources","validateGitHubRepositorySelection","validateToolRefs","workflowIdForSession","HTTPException","requireLimit","requireAccessGrant","WebStandardStreamableHTTPServerTransport"]}
|