@agimon-ai/doompi-core 0.0.1-alpha.78 → 0.0.1-alpha.80
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/headless-hub.cjs +48 -3
- package/dist/headless-hub.cjs.map +1 -1
- package/dist/headless-hub.d.cts +5 -1
- package/dist/headless-hub.d.cts.map +1 -1
- package/dist/headless-hub.d.mts +5 -1
- package/dist/headless-hub.d.mts.map +1 -1
- package/dist/headless-hub.mjs +48 -3
- package/dist/headless-hub.mjs.map +1 -1
- package/dist/headless-server.cjs +17 -2
- package/dist/headless-server.cjs.map +1 -1
- package/dist/headless-server.d.cts +5 -0
- package/dist/headless-server.d.cts.map +1 -1
- package/dist/headless-server.d.mts +5 -0
- package/dist/headless-server.d.mts.map +1 -1
- package/dist/headless-server.mjs +17 -2
- package/dist/headless-server.mjs.map +1 -1
- package/dist/hub-channel.d.cts +2 -2
- package/dist/hub-channel.d.mts +2 -2
- package/dist/mcp-facet.cjs +1 -0
- package/dist/mcp-facet.d.cts +2 -2
- package/dist/mcp-facet.d.mts +2 -2
- package/dist/mcp-facet.mjs +2 -2
- package/dist/schemas/headless.cjs.map +1 -1
- package/dist/schemas/headless.d.cts +21 -1
- package/dist/schemas/headless.d.cts.map +1 -1
- package/dist/schemas/headless.d.mts +21 -1
- package/dist/schemas/headless.d.mts.map +1 -1
- package/dist/schemas/headless.mjs.map +1 -1
- package/dist/schemas/httpApiContracts.cjs +6 -0
- package/dist/schemas/httpApiContracts.cjs.map +1 -1
- package/dist/schemas/httpApiContracts.mjs +6 -0
- package/dist/schemas/httpApiContracts.mjs.map +1 -1
- package/dist/schemas/hubChannel.d.cts +21 -0
- package/dist/schemas/hubChannel.d.cts.map +1 -1
- package/dist/schemas/hubChannel.d.mts +21 -0
- package/dist/schemas/hubChannel.d.mts.map +1 -1
- package/dist/schemas/mcpFacet.cjs +5 -0
- package/dist/schemas/mcpFacet.cjs.map +1 -1
- package/dist/schemas/mcpFacet.d.cts +25 -0
- package/dist/schemas/mcpFacet.d.cts.map +1 -1
- package/dist/schemas/mcpFacet.d.mts +25 -0
- package/dist/schemas/mcpFacet.d.mts.map +1 -1
- package/dist/schemas/mcpFacet.mjs +5 -1
- package/dist/schemas/mcpFacet.mjs.map +1 -1
- package/dist/server/headlessProtocol.cjs +7 -1
- package/dist/server/headlessProtocol.cjs.map +1 -1
- package/dist/server/headlessProtocol.mjs +7 -1
- package/dist/server/headlessProtocol.mjs.map +1 -1
- package/dist/server/sessionMcpHandler.cjs +112 -25
- package/dist/server/sessionMcpHandler.cjs.map +1 -1
- package/dist/server/sessionMcpHandler.d.cts +6 -1
- package/dist/server/sessionMcpHandler.d.cts.map +1 -1
- package/dist/server/sessionMcpHandler.d.mts +6 -1
- package/dist/server/sessionMcpHandler.d.mts.map +1 -1
- package/dist/server/sessionMcpHandler.mjs +112 -25
- package/dist/server/sessionMcpHandler.mjs.map +1 -1
- package/dist/server/sessionMcpRoutes.cjs +224 -15
- package/dist/server/sessionMcpRoutes.cjs.map +1 -1
- package/dist/server/sessionMcpRoutes.mjs +213 -7
- package/dist/server/sessionMcpRoutes.mjs.map +1 -1
- package/dist/services/openSessionRegistry/index.cjs +13 -6
- package/dist/services/openSessionRegistry/index.cjs.map +1 -1
- package/dist/services/openSessionRegistry/index.d.cts +2 -1
- package/dist/services/openSessionRegistry/index.d.cts.map +1 -1
- package/dist/services/openSessionRegistry/index.d.mts +2 -1
- package/dist/services/openSessionRegistry/index.d.mts.map +1 -1
- package/dist/services/openSessionRegistry/index.mjs +13 -6
- package/dist/services/openSessionRegistry/index.mjs.map +1 -1
- package/dist/services/piExtensionHost/index.cjs +1 -1
- package/dist/services/piExtensionHost/index.mjs +1 -1
- package/dist/services/sessionMcpAuthorization/index.cjs +56 -0
- package/dist/services/sessionMcpAuthorization/index.cjs.map +1 -1
- package/dist/services/sessionMcpAuthorization/index.d.cts +12 -0
- package/dist/services/sessionMcpAuthorization/index.d.cts.map +1 -1
- package/dist/services/sessionMcpAuthorization/index.d.mts +12 -0
- package/dist/services/sessionMcpAuthorization/index.d.mts.map +1 -1
- package/dist/services/sessionMcpAuthorization/index.mjs +56 -1
- package/dist/services/sessionMcpAuthorization/index.mjs.map +1 -1
- package/dist/services/sessionMcpConversations/index.cjs +170 -0
- package/dist/services/sessionMcpConversations/index.cjs.map +1 -0
- package/dist/services/sessionMcpConversations/index.mjs +165 -0
- package/dist/services/sessionMcpConversations/index.mjs.map +1 -0
- package/dist/services/sessionMcpRegistrationStore/index.cjs +85 -0
- package/dist/services/sessionMcpRegistrationStore/index.cjs.map +1 -0
- package/dist/services/sessionMcpRegistrationStore/index.mjs +82 -0
- package/dist/services/sessionMcpRegistrationStore/index.mjs.map +1 -0
- package/dist/session-mcp.d.cts +2 -2
- package/dist/session-mcp.d.mts +2 -2
- package/dist/systems/main/adapters/headlessHost.cjs +6 -3
- package/dist/systems/main/adapters/headlessHost.cjs.map +1 -1
- package/dist/systems/main/adapters/headlessHost.d.cts +2 -0
- package/dist/systems/main/adapters/headlessHost.d.cts.map +1 -1
- package/dist/systems/main/adapters/headlessHost.d.mts +2 -0
- package/dist/systems/main/adapters/headlessHost.d.mts.map +1 -1
- package/dist/systems/main/adapters/headlessHost.mjs +6 -3
- package/dist/systems/main/adapters/headlessHost.mjs.map +1 -1
- package/dist/systems/main/adapters/headlessSessionHost.cjs +54 -5
- package/dist/systems/main/adapters/headlessSessionHost.cjs.map +1 -1
- package/dist/systems/main/adapters/headlessSessionHost.d.cts.map +1 -1
- package/dist/systems/main/adapters/headlessSessionHost.d.mts.map +1 -1
- package/dist/systems/main/adapters/headlessSessionHost.mjs +54 -5
- package/dist/systems/main/adapters/headlessSessionHost.mjs.map +1 -1
- package/dist/types/server/sessionToolSurface.d.cts +8 -1
- package/dist/types/server/sessionToolSurface.d.cts.map +1 -1
- package/dist/types/server/sessionToolSurface.d.mts +8 -1
- package/dist/types/server/sessionToolSurface.d.mts.map +1 -1
- package/dist/web/types/webPlugin.d.cts +8 -2
- package/dist/web/types/webPlugin.d.cts.map +1 -1
- package/dist/web/types/webPlugin.d.mts +8 -2
- package/dist/web/types/webPlugin.d.mts.map +1 -1
- package/package.json +11 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-server.cjs","names":["path","fs","Readable","pipeline","WebSocketServer","createHeadlessProtocol","createSessionMcpRoutes","createServer","isPublicSessionMcpRoute","isSessionMcpHostRoute","DOOM_API_CALLER_HEADERS","DOOM_API_CALLER_LOCALITY_HEADER","DOOM_API_CALLER_STEP_UP_HEADER","BACKGROUND_CONTEXT","parseDoomSocketPath"],"sources":["../src/server/headlessServer.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\n\nimport type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { WebSocket } from 'ws';\nimport { WebSocketServer } from 'ws';\n\nimport {\n DOOM_API_CALLER_HEADERS,\n DOOM_API_CALLER_LOCALITY_HEADER,\n DOOM_API_CALLER_STEP_UP_HEADER,\n parseDoomSocketPath,\n type DoomApiMount,\n} from '../exports/packageApi';\nimport type { TranscriptPage, TranscriptPageRequest } from '../exports/sessionProtocol';\nimport type { OpenSessionRecord } from '../services/openSessionRegistry';\nimport { observe, type ServerTelemetry } from '../services/serverTelemetry';\nimport type { SavedSession } from '../services/sqliteSessionHistory';\nimport type { HeadlessHub, HeadlessHubEvent, HeadlessHubSession } from './headlessHub';\nimport { createHeadlessProtocol } from './headlessProtocol';\nimport { createSessionMcpRoutes, isPublicSessionMcpRoute, isSessionMcpHostRoute } from './sessionMcpRoutes';\n\n/**\n * The canonical public shape of a package API request, as dispatch reads it.\n *\n * Groups: workspace id, session id, plugin base path, and the remainder handed\n * to the package. `settings` is the host's own API and sits beside `plugins`\n * rather than under it.\n *\n * Exported so the browser's URL construction can be pinned against it. The two\n * are written independently, in halves of this package that may not import each\n * other, and a disagreement between them is not a 404 from the route: the\n * service worker answers an unrecognised path out of the signed bundle cache,\n * so the symptom appears nowhere near the cause.\n */\nexport const DOOM_PACKAGE_API_PATH_PATTERN =\n /^\\/api(?:\\/workspaces\\/([^/]+)(?:\\/sessions\\/([^/]+))?)?\\/(?:plugins\\/([^/]+)|settings)(?:\\/(.*))?$/u;\n\nconst HEALTH_ROLE = 'hub';\nconst PROTOCOL_VERSION = 1;\nconst MAX_BODY_BYTES = 8 * 1024 * 1024;\nconst MAX_SESSION_FILE_BYTES = 25 * 1024 * 1024;\nconst SESSION_FILE_CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.avif': 'image/avif',\n '.bmp': 'image/bmp',\n '.gif': 'image/gif',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.m4v': 'video/mp4',\n '.mov': 'video/quicktime',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.pdf': 'application/pdf',\n};\n\nexport interface HeadlessServerOptions {\n headlessHub: HeadlessHub;\n port: number;\n host?: string;\n /** Requests other than health require this bearer token when set. */\n token?: string;\n telemetry?: ServerTelemetry;\n onNotice?: (message: string) => void;\n compositions?: () => unknown;\n requestAsset?: (request: Request) => Promise<Response | undefined>;\n sessionHistory?: (session: HeadlessHubSession) => Promise<SavedSession[]>;\n workspaceHistory?: (workspaceId: string) => Promise<SavedSession[]>;\n resumeWorkspaceSession?: (workspaceId: string, targetSessionId: string) => Promise<string>;\n readDormantTranscript?: (\n record: OpenSessionRecord,\n request: TranscriptPageRequest,\n context: Context,\n ) => Promise<TranscriptPage>;\n restartSession?: (session: HeadlessHubSession) => Promise<void>;\n resumeSession?: (session: HeadlessHubSession, targetSessionId: string) => Promise<string>;\n /** Recorded sessions this server has not reopened, surfaced so a client can ask for one. */\n dormantSessions?: () => readonly OpenSessionRecord[];\n removeDormantSession?: (record: OpenSessionRecord) => void | Promise<void>;\n reviveSession?: (record: OpenSessionRecord) => Promise<void>;\n /** Trusted HTTPS origin advertised for public session MCP and OAuth endpoints. */\n sessionMcpPublicOrigin?: () => string | undefined;\n /** Monotonic exposure revision, including disable/re-enable cycles. */\n sessionMcpPublicOriginRevision?: () => number;\n}\n\nexport interface HeadlessServer {\n readonly url: string;\n close(): Promise<void>;\n}\n\ntype ProtocolHandler = NonNullable<ReturnType<Awaited<ReturnType<typeof createHeadlessProtocol>>['accept']>>;\ntype Client = { socket: WebSocket; handler: ProtocolHandler };\n\nfunction sessionView(session: HeadlessHubSession): Record<string, unknown> {\n return {\n id: session.id,\n workspaceId: session.workspaceId,\n webComposition: session.webComposition,\n name: session.name,\n cwd: session.cwd,\n createdAt: session.createdAt,\n updatedAt: session.updatedAt ?? session.createdAt,\n phase: session.phase ?? 'idle',\n phaseSince: session.phaseSince ?? session.createdAt,\n attach: 'attached',\n pendingMessageCount: session.pendingMessageCount ?? 0,\n everPrompted: session.everPrompted ?? false,\n awaitingInput: session.awaitingInput ?? false,\n ...(session.lastSettledAt === undefined ? {} : { lastSettledAt: session.lastSettledAt }),\n ...(session.parentSessionId === undefined ? {} : { parentSessionId: session.parentSessionId }),\n ...(session.sessionProvenance === undefined ? {} : { sessionProvenance: session.sessionProvenance }),\n };\n}\n\n/** The dormant counterpart of `sessionView`; same shape, no runtime behind it. */\nfunction dormantView(record: OpenSessionRecord): Record<string, unknown> {\n return {\n id: record.sessionId,\n workspaceId: record.workspaceId,\n name: record.name,\n cwd: record.cwd,\n createdAt: record.createdAt,\n updatedAt: record.createdAt,\n phase: 'idle',\n phaseSince: record.createdAt,\n attach: 'detached',\n pendingMessageCount: 0,\n everPrompted: false,\n awaitingInput: false,\n dormant: true,\n ...(record.parentSessionId === undefined ? {} : { parentSessionId: record.parentSessionId }),\n ...(record.sessionProvenance === undefined ? {} : { sessionProvenance: record.sessionProvenance }),\n };\n}\n\nfunction json(response: ServerResponse, status: number, value: unknown): void {\n const body = JSON.stringify(value);\n response.writeHead(status, {\n 'content-type': 'application/json; charset=utf-8',\n 'content-length': Buffer.byteLength(body),\n });\n response.end(body);\n}\n\nfunction authorized(request: IncomingMessage, token: string | undefined): boolean {\n if (token === undefined) return true;\n const authorization = request.headers.authorization;\n if (authorization?.startsWith('Bearer ') && authorization.slice('Bearer '.length) === token) return true;\n const header = request.headers['x-doompi-token'];\n if (typeof header === 'string' && header === token) return true;\n try {\n return new URL(request.url ?? '/', 'http://doompi.local').searchParams.get('token') === token;\n } catch {\n return false;\n }\n}\n\nfunction requestPath(request: IncomingMessage): URL {\n return new URL(request.url ?? '/', 'http://doompi.local');\n}\n\nfunction parseTranscriptRequest(url: URL): TranscriptPageRequest {\n const request: TranscriptPageRequest = {};\n const cursor = url.searchParams.get('cursor');\n const direction = url.searchParams.get('direction');\n const limit = url.searchParams.get('limit');\n if (cursor !== null) request.cursor = cursor;\n if (direction !== null) {\n if (direction !== 'older' && direction !== 'newer') throw new Error('Invalid transcript direction');\n request.direction = direction;\n }\n if (limit !== null) {\n if (!/^\\d+$/u.test(limit)) throw new Error('Invalid transcript page limit');\n const parsed = Number(limit);\n if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 100) throw new Error('Invalid transcript page limit');\n request.limit = parsed;\n }\n return request;\n}\n\nasync function sessionFile(session: HeadlessHubSession, relativePath: string | null): Promise<Response> {\n if (!relativePath || relativePath.includes('\\0') || path.isAbsolute(relativePath))\n return Response.json({ error: 'Invalid session file path.' }, { status: 400 });\n let root: string;\n let filePath: string;\n try {\n root = await fs.promises.realpath(session.cwd);\n filePath = await fs.promises.realpath(path.resolve(root, relativePath));\n } catch {\n return Response.json({ error: 'Session file not found.' }, { status: 404 });\n }\n const contained = path.relative(root, filePath);\n if (contained === '..' || contained.startsWith(`..${path.sep}`) || path.isAbsolute(contained))\n return Response.json({ error: 'Session file is outside the working directory.' }, { status: 403 });\n let handle: fs.promises.FileHandle;\n try {\n handle = await fs.promises.open(filePath, 'r');\n } catch {\n return Response.json({ error: 'Session file not found.' }, { status: 404 });\n }\n try {\n const stat = await handle.stat();\n if (!stat.isFile()) return Response.json({ error: 'Session file not found.' }, { status: 404 });\n if (stat.size > MAX_SESSION_FILE_BYTES)\n return Response.json({ error: 'Session file is too large.' }, { status: 413 });\n const bytes = await handle.readFile();\n if (bytes.byteLength > MAX_SESSION_FILE_BYTES)\n return Response.json({ error: 'Session file is too large.' }, { status: 413 });\n return new Response(bytes, {\n headers: {\n 'content-type': SESSION_FILE_CONTENT_TYPES[path.extname(filePath).toLowerCase()] ?? 'application/octet-stream',\n 'content-length': String(bytes.byteLength),\n 'cache-control': 'no-store',\n 'x-content-type-options': 'nosniff',\n },\n });\n } finally {\n await handle.close();\n }\n}\n\nasync function directorySuggestions(query: string, sessions: readonly HeadlessHubSession[]): Promise<string[]> {\n const typed = query.trim();\n if (typed === '') return [];\n const matches = (value: string): boolean => value.toLowerCase().includes(typed.toLowerCase());\n const known = [...new Set([process.cwd(), ...sessions.map((session) => session.cwd)])].filter(matches);\n const candidates = path.isAbsolute(typed)\n ? [typed]\n : typed.includes(path.sep)\n ? [path.resolve(process.cwd(), typed)]\n : [path.join(path.dirname(process.cwd()), typed), path.join(process.cwd(), typed)];\n const completed: string[] = [];\n for (const candidate of candidates) {\n const drilling = typed.endsWith(path.sep);\n const parent = drilling ? candidate : path.dirname(candidate);\n const partial = drilling ? '' : path.basename(candidate).toLowerCase();\n try {\n const entries = await fs.promises.readdir(parent, { withFileTypes: true });\n completed.push(\n ...entries\n .filter((entry) => entry.isDirectory() && entry.name.toLowerCase().includes(partial))\n .map((entry) => path.join(parent, entry.name)),\n );\n } catch {\n // A missing or unreadable parent simply has no completions.\n }\n }\n return [...new Set([...known, ...completed.sort((left, right) => left.localeCompare(right))])].slice(0, 12);\n}\n\nasync function readBody(request: IncomingMessage): Promise<Uint8Array> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n size += buffer.byteLength;\n if (size > MAX_BODY_BYTES) throw new Error('Request body is too large.');\n chunks.push(buffer);\n }\n return Buffer.concat(chunks);\n}\n\nfunction parseJson(body: Uint8Array): unknown {\n if (body.byteLength === 0) return {};\n return JSON.parse(Buffer.from(body).toString('utf8')) as unknown;\n}\n\nfunction webRequest(request: IncomingMessage, url: URL, signal?: AbortSignal): Request {\n const headers = new Headers();\n for (const [name, value] of Object.entries(request.headers)) {\n if (value !== undefined) headers.set(name, Array.isArray(value) ? value.join(', ') : value);\n }\n return new Request(url, {\n method: request.method,\n headers,\n signal,\n ...(request.method === 'GET' || request.method === 'HEAD'\n ? {}\n : {\n body: Readable.toWeb(request) as never,\n duplex: 'half' as const,\n }),\n });\n}\n\nasync function writeResponse(response: ServerResponse, result: Response): Promise<void> {\n response.writeHead(result.status, Object.fromEntries(result.headers.entries()));\n if (result.body === null) {\n response.end();\n return;\n }\n await pipeline(Readable.fromWeb(result.body as import('node:stream/web').ReadableStream), response);\n}\n\nfunction eventFrame(event: HeadlessHubEvent): Record<string, unknown> {\n switch (event.kind) {\n case 'upsert':\n return { type: 'session_upsert', session: sessionView(event.session) };\n case 'removed':\n return { type: 'session_removed', sessionId: event.sessionId };\n case 'workspace_upsert':\n return { type: 'workspace_upsert', workspace: event.workspace };\n case 'workspace_removed':\n return { type: 'workspace_removed', workspaceId: event.workspaceId };\n case 'channel':\n return {\n type: 'channel_frame',\n frameType: event.frameType,\n sessionId: event.sessionId,\n payload: event.payload,\n ...(event.connectionId === undefined ? {} : { connectionId: event.connectionId }),\n };\n }\n}\n\nfunction writeSse(response: ServerResponse, event: string, value: unknown): void {\n response.write(`event: ${event}\\ndata: ${JSON.stringify(value)}\\n\\n`);\n}\n\n/**\n * Client-neutral HTTP and WebSocket boundary for the process-local headless hub.\n * It deliberately delegates every session operation to HeadlessHub, so transport\n * clients cannot create a second session backend or bypass its lifecycle.\n */\nexport async function serveHeadlessServer(options: HeadlessServerOptions): Promise<HeadlessServer> {\n const clients = new Set<Client>();\n const scopedProtocols = new Set<Awaited<ReturnType<typeof createHeadlessProtocol>>>();\n const sse = new Set<ServerResponse>();\n let closed = false;\n const webSockets = new WebSocketServer({ noServer: true });\n /**\n * A recorded session is dormant exactly while the hub has no session for its\n * id, so reviving one needs no bookkeeping beyond the hub itself.\n */\n const dormant = (): readonly OpenSessionRecord[] =>\n (options.dormantSessions?.() ?? []).filter((record) => options.headlessHub.session(record.sessionId) === undefined);\n const protocol = await createHeadlessProtocol({\n hub: options.headlessHub,\n telemetry: options.telemetry,\n onNotice: options.onNotice,\n dormantSessions: dormant,\n });\n const sessionMcp = createSessionMcpRoutes({\n headlessHub: options.headlessHub,\n publicOrigin: options.sessionMcpPublicOrigin ?? (() => undefined),\n publicOriginRevision: options.sessionMcpPublicOriginRevision,\n });\n const server = createServer((request, response) => {\n void handleRequest(request, response).catch((error: unknown) => {\n if (response.headersSent) {\n response.destroy();\n return;\n }\n json(response, 500, { error: error instanceof Error ? error.message : String(error) });\n });\n });\n\n const unsubscribe = options.headlessHub.onEvent((event) => {\n const frame = eventFrame(event);\n for (const response of sse) writeSse(response, String(frame.type), frame);\n });\n\n async function handleRequest(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const url = requestPath(request);\n if (url.pathname === '/api/health' && request.method === 'GET') {\n json(response, 200, {\n ok: true,\n role: HEALTH_ROLE,\n protocol: PROTOCOL_VERSION,\n sessions: options.headlessHub.snapshot().length,\n });\n return;\n }\n if (isPublicSessionMcpRoute(request.method ?? 'GET', url.pathname)) {\n const sessionMcpAbort = new AbortController();\n const abortSessionMcp = (): void => {\n if (!response.writableEnded) sessionMcpAbort.abort();\n };\n response.once('close', abortSessionMcp);\n const publicSessionMcp = await sessionMcp.handlePublic(webRequest(request, url, sessionMcpAbort.signal));\n if (publicSessionMcp !== undefined) {\n try {\n await writeResponse(response, publicSessionMcp);\n } finally {\n response.off('close', abortSessionMcp);\n }\n return;\n }\n response.off('close', abortSessionMcp);\n }\n if (!authorized(request, options.token)) {\n json(response, 401, { error: 'Unauthorized.' });\n return;\n }\n if (isSessionMcpHostRoute(url.pathname)) {\n const hostSessionMcp = await sessionMcp.handleHost(webRequest(request, url));\n if (hostSessionMcp !== undefined) {\n await writeResponse(response, hostSessionMcp);\n return;\n }\n }\n if (url.pathname === '/api/remote' || url.pathname.startsWith('/api/remote/')) {\n const headers = new Headers();\n for (const [name, value] of Object.entries(request.headers)) {\n if (value !== undefined && name !== 'authorization' && name !== 'x-doompi-token')\n headers.set(name, Array.isArray(value) ? value.join(', ') : value);\n }\n const result = await options.headlessHub.requestApi(\n { scope: 'global' },\n 'remote',\n new Request(`http://doompi.local${url.pathname.slice('/api/remote'.length)}${url.search}`, {\n method: request.method,\n headers,\n ...(request.method === 'GET' || request.method === 'HEAD'\n ? {}\n : { body: Buffer.from(await readBody(request)) }),\n }),\n );\n await writeResponse(response, result);\n return;\n }\n if (url.pathname === '/api/telemetry/browser' && request.method === 'POST') {\n const body = JSON.parse(new TextDecoder().decode(await readBody(request))) as { v?: unknown; events?: unknown };\n if (body.v !== 1 || !Array.isArray(body.events) || body.events.length > 10) {\n json(response, 400, { error: 'Invalid browser telemetry batch.' });\n return;\n }\n for (const event of body.events) {\n if (\n !event ||\n typeof event !== 'object' ||\n typeof event.name !== 'string' ||\n !/^web\\.browser\\.[a-z_]{1,40}$/u.test(event.name)\n )\n continue;\n if (options.telemetry)\n observe(\n options.telemetry.recordEvent(event.name, {\n ...(typeof event.duration_ms === 'number' && Number.isFinite(event.duration_ms) && event.duration_ms >= 0\n ? { duration_ms: event.duration_ms }\n : {}),\n ...(typeof event.count === 'number' && Number.isFinite(event.count) && event.count >= 0\n ? { count: event.count }\n : {}),\n }),\n );\n }\n json(response, 200, { ok: true });\n return;\n }\n if (url.pathname === '/api/compositions' && request.method === 'GET') {\n json(response, 200, options.compositions?.() ?? { workspaces: [] });\n return;\n }\n if (\n (url.pathname.startsWith('/api/web-plugins/') ||\n url.pathname === '/bundle-manifest.json' ||\n url.pathname.startsWith('/bundle-assets/')) &&\n options.requestAsset\n ) {\n const result = await options.requestAsset(new Request(url, { method: request.method }));\n if (result) {\n await writeResponse(response, result);\n return;\n }\n }\n if (url.pathname === '/api/directories' && request.method === 'GET') {\n json(response, 200, {\n directories: await directorySuggestions(url.searchParams.get('q') ?? '', options.headlessHub.snapshot()),\n });\n return;\n }\n // Verified browser bundles can remain active across a server upgrade. Keep\n // their former create route working while they refresh to workspace routes.\n if (url.pathname === '/api/sessions' && request.method === 'POST') {\n const body = parseJson(await readBody(request));\n if (\n !body ||\n typeof body !== 'object' ||\n !('cwd' in body) ||\n typeof body.cwd !== 'string' ||\n !body.cwd.trim() ||\n ('name' in body && typeof body.name !== 'string')\n ) {\n json(response, 400, { error: 'A session needs a working directory and an optional name.' });\n return;\n }\n const created = await options.headlessHub.sessionService.create({\n cwd: body.cwd,\n name: 'name' in body ? String(body.name) : path.basename(body.cwd),\n });\n json(response, 201, { sessionId: created.sessionId });\n return;\n }\n const workspaceSessions = /^\\/api\\/workspaces\\/([^/]+)\\/sessions$/u.exec(url.pathname);\n if (workspaceSessions) {\n const workspaceId = decodeURIComponent(workspaceSessions[1]);\n const workspace = options.headlessHub.workspaces().find((entry) => entry.id === workspaceId);\n if (!workspace) {\n json(response, 404, { error: 'Workspace not found.' });\n return;\n }\n if (request.method === 'GET') {\n json(response, 200, {\n sessions: [\n ...options.headlessHub\n .snapshot()\n .filter((session) => session.workspaceId === workspaceId)\n .map(sessionView),\n ...dormant()\n .filter((record) => record.workspaceId === workspaceId)\n .map(dormantView),\n ],\n });\n return;\n }\n if (request.method === 'POST') {\n if (workspace.available === false) {\n json(response, 409, { error: 'Workspace is unavailable.' });\n return;\n }\n const body = parseJson(await readBody(request));\n if (!body || typeof body !== 'object' || ('name' in body && typeof body.name !== 'string')) {\n json(response, 400, { error: 'A session accepts an optional name.' });\n return;\n }\n const created = await options.headlessHub.sessionService.create({\n cwd: workspace.root,\n name: 'name' in body ? String(body.name) : path.basename(workspace.root),\n });\n json(response, 201, { sessionId: created.sessionId });\n return;\n }\n }\n if (url.pathname === '/api/events' && request.method === 'GET') {\n response.writeHead(200, {\n 'cache-control': 'no-cache',\n connection: 'keep-alive',\n 'content-type': 'text/event-stream; charset=utf-8',\n });\n sse.add(response);\n response.on('close', () => sse.delete(response));\n writeSse(response, 'sessions_snapshot', {\n type: 'sessions_snapshot',\n sessions: [...options.headlessHub.snapshot().map(sessionView), ...dormant().map(dormantView)],\n });\n writeSse(response, 'workspaces_snapshot', {\n type: 'workspaces_snapshot',\n workspaces: options.headlessHub.workspaces(),\n });\n return;\n }\n if (url.pathname === '/api/workspaces') {\n if (request.method === 'GET') {\n json(response, 200, { workspaces: options.headlessHub.workspaces() });\n return;\n }\n if (request.method === 'POST') {\n const body = parseJson(await readBody(request));\n if (typeof body !== 'object' || body === null || !('root' in body) || typeof body.root !== 'string') {\n json(response, 400, { error: 'A workspace root is required.' });\n return;\n }\n const workspace = await options.headlessHub.admitWorkspace(body.root);\n json(response, 201, { workspace });\n return;\n }\n }\n const workspaceHistoryMatch = /^\\/api\\/workspaces\\/([^/]+)\\/history$/u.exec(url.pathname);\n if (workspaceHistoryMatch && request.method === 'GET' && options.workspaceHistory) {\n const workspaceId = decodeURIComponent(workspaceHistoryMatch[1]);\n const workspace = options.headlessHub.workspaces().find((entry) => entry.id === workspaceId);\n if (!workspace) {\n json(response, 404, { error: 'Workspace not found.' });\n return;\n }\n if (workspace.available === false) {\n json(response, 409, { error: 'Workspace is unavailable.' });\n return;\n }\n json(response, 200, { sessions: await options.workspaceHistory(workspaceId) });\n return;\n }\n const workspaceResumeMatch = /^\\/api\\/workspaces\\/([^/]+)\\/resume$/u.exec(url.pathname);\n if (workspaceResumeMatch && request.method === 'POST' && options.resumeWorkspaceSession) {\n const workspaceId = decodeURIComponent(workspaceResumeMatch[1]);\n const workspace = options.headlessHub.workspaces().find((entry) => entry.id === workspaceId);\n if (!workspace) {\n json(response, 404, { error: 'Workspace not found.' });\n return;\n }\n if (workspace.available === false) {\n json(response, 409, { error: 'Workspace is unavailable.' });\n return;\n }\n const body = parseJson(await readBody(request));\n const targetSessionId =\n body && typeof body === 'object' && 'targetSessionId' in body ? body.targetSessionId : undefined;\n if (typeof targetSessionId !== 'string' || !/^[A-Za-z0-9_-]+$/u.test(targetSessionId)) {\n json(response, 400, { error: 'Invalid target session id.' });\n return;\n }\n json(response, 200, { sessionId: await options.resumeWorkspaceSession(workspaceId, targetSessionId) });\n return;\n }\n const workspaceMatch = /^\\/api\\/workspaces\\/([^/]+)$/u.exec(url.pathname);\n if (workspaceMatch && request.method === 'GET') {\n const workspace = options.headlessHub\n .workspaces()\n .find((entry) => entry.id === decodeURIComponent(workspaceMatch[1]));\n json(response, workspace ? 200 : 404, workspace ? { workspace } : { error: 'Workspace not found.' });\n return;\n }\n if (workspaceMatch && request.method === 'DELETE') {\n const id = decodeURIComponent(workspaceMatch[1]);\n if (!options.headlessHub.workspaces().some((workspace) => workspace.id === id)) {\n json(response, 404, { error: 'Workspace not found.' });\n return;\n }\n if (\n options.headlessHub.snapshot().some((session) => session.workspaceId === id) ||\n dormant().some((session) => session.workspaceId === id)\n ) {\n json(response, 409, { error: 'Workspace still has sessions.' });\n return;\n }\n await options.headlessHub.removeWorkspace(id);\n json(response, 200, { ok: true });\n return;\n }\n const pluginMatch = DOOM_PACKAGE_API_PATH_PATTERN.exec(url.pathname);\n if (pluginMatch !== null && request.method !== 'CONNECT') {\n const workspaceId = pluginMatch[1] === undefined ? undefined : decodeURIComponent(pluginMatch[1]);\n const sessionId = pluginMatch[2] === undefined ? undefined : decodeURIComponent(pluginMatch[2]);\n if (workspaceId !== undefined && !options.headlessHub.workspaces().some((entry) => entry.id === workspaceId)) {\n json(response, 404, { error: 'Workspace not found.' });\n return;\n }\n if (sessionId !== undefined && options.headlessHub.session(sessionId)?.workspaceId !== workspaceId) {\n json(response, 404, { error: 'Session not found.' });\n return;\n }\n const mount: DoomApiMount =\n sessionId !== undefined\n ? { scope: 'session', sessionId }\n : workspaceId !== undefined\n ? { scope: 'workspace', workspaceId }\n : { scope: 'global' };\n const basePath = pluginMatch[3] === undefined ? 'settings' : decodeURIComponent(pluginMatch[3]);\n if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(basePath)) {\n json(response, 400, { error: 'Invalid package API base path.' });\n return;\n }\n if (pluginMatch[3] === 'settings' || (sessionId !== undefined && basePath === 'settings')) {\n json(response, 404, { error: 'Not found.' });\n return;\n }\n const headers = new Headers();\n // Only the authenticated loopback proxy knows the server token and may\n // forward caller stamps. Browser-supplied stamps remain untrusted.\n const trustedCaller = options.token !== undefined && authorized(request, options.token);\n for (const [name, value] of Object.entries(request.headers)) {\n if (\n value !== undefined &&\n !['host', 'authorization', 'x-doompi-token'].includes(name) &&\n (trustedCaller || !DOOM_API_CALLER_HEADERS.includes(name as (typeof DOOM_API_CALLER_HEADERS)[number]))\n )\n headers.set(name, Array.isArray(value) ? value.join(', ') : value);\n }\n if (!trustedCaller) {\n headers.set(DOOM_API_CALLER_LOCALITY_HEADER, 'local');\n headers.set(DOOM_API_CALLER_STEP_UP_HEADER, 'not-required');\n }\n const abort = new AbortController();\n const disconnected = (): void => {\n if (!response.writableEnded) abort.abort();\n };\n response.once('close', disconnected);\n try {\n const started = performance.now();\n const attributes = {\n scope: mount.scope,\n 'plugin.name': basePath,\n 'http.operation': request.method,\n ...(mount.scope === 'workspace' ? { 'workspace.id': mount.workspaceId } : {}),\n ...(mount.scope === 'session' ? { 'session.id': mount.sessionId } : {}),\n };\n const dispatch = async () =>\n options.headlessHub.requestApi(\n mount,\n basePath,\n new Request(`http://doompi.local/${pluginMatch[4] ?? ''}${url.search}`, {\n method: request.method,\n headers,\n signal: abort.signal,\n ...(request.method === 'GET' || request.method === 'HEAD'\n ? {}\n : { body: Buffer.from(await readBody(request)) }),\n }),\n );\n const result = options.telemetry\n ? await options.telemetry.runInSpan('doompi_server.plugin.request', attributes, dispatch)\n : await dispatch();\n if (options.telemetry)\n observe(\n options.telemetry.recordEvent('doompi_server.plugin.response', {\n ...attributes,\n status: result.status,\n durationMs: performance.now() - started,\n }),\n options.onNotice,\n );\n await writeResponse(response, result);\n } finally {\n response.off('close', disconnected);\n }\n return;\n }\n const sessionMatch = /^\\/api\\/workspaces\\/([^/]+)\\/sessions\\/([^/]+)(?:\\/(.*))?$/u.exec(url.pathname);\n if (sessionMatch === null) {\n json(response, 404, { error: 'Not found.' });\n return;\n }\n const sessionId = decodeURIComponent(sessionMatch[2]);\n const workspaceId = decodeURIComponent(sessionMatch[1]);\n const session = options.headlessHub.session(sessionId);\n const suffix = sessionMatch[3] === undefined ? '' : `/${sessionMatch[3]}`;\n if (suffix === '/transcript' && request.method === 'GET' && options.readDormantTranscript) {\n const record = dormant().find(\n (candidate) => candidate.sessionId === sessionId && candidate.workspaceId === workspaceId,\n );\n if (record === undefined) {\n json(response, 404, { error: 'Dormant session not found.' });\n return;\n }\n let transcriptRequest: TranscriptPageRequest;\n try {\n transcriptRequest = parseTranscriptRequest(url);\n } catch (error) {\n json(response, 400, { error: error instanceof Error ? error.message : 'Invalid transcript request.' });\n return;\n }\n try {\n const page = await options.readDormantTranscript(record, transcriptRequest, BACKGROUND_CONTEXT);\n json(response, 200, page);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const status = message === 'STALE_TRANSCRIPT_CURSOR' ? 409 : 422;\n json(response, status, {\n error: status === 409 ? message : 'Saved transcript is unavailable.',\n });\n }\n return;\n }\n // Revive is the one session route that answers before a runtime exists: it\n // is what creates one.\n if (sessionMatch[3] === 'revive' && request.method === 'POST' && options.reviveSession) {\n const record = dormant().find(\n (candidate) => candidate.sessionId === sessionId && candidate.workspaceId === workspaceId,\n );\n if (record === undefined) {\n json(response, 404, { error: 'Dormant session not found.' });\n return;\n }\n await options.reviveSession(record);\n json(response, 200, { ok: true });\n return;\n }\n if (session === undefined || session.workspaceId !== workspaceId) {\n const record = dormant().find(\n (candidate) => candidate.sessionId === sessionId && candidate.workspaceId === workspaceId,\n );\n if (request.method === 'DELETE' && record !== undefined && options.removeDormantSession !== undefined) {\n await options.removeDormantSession(record);\n options.headlessHub.notifySessionRemoved(record.sessionId);\n json(response, 200, { ok: true });\n return;\n }\n json(response, 404, { error: 'Session not found.' });\n return;\n }\n if (suffix === '' && request.method === 'GET') {\n json(response, 200, sessionView(session));\n return;\n }\n if (suffix === '' && request.method === 'DELETE') {\n await options.headlessHub.closeSession(sessionId);\n json(response, 200, { ok: true });\n return;\n }\n if (suffix === '/history' && request.method === 'GET' && options.sessionHistory) {\n json(response, 200, { sessions: await options.sessionHistory(session) });\n return;\n }\n if (suffix === '/file' && request.method === 'GET') {\n await writeResponse(response, await sessionFile(session, url.searchParams.get('path')));\n return;\n }\n if (suffix === '/restart' && request.method === 'POST' && options.restartSession) {\n await options.restartSession(session);\n json(response, 200, { ok: true });\n return;\n }\n if (suffix === '/resume' && request.method === 'POST' && options.resumeSession) {\n const body = parseJson(await readBody(request));\n const targetSessionId =\n body && typeof body === 'object' && 'targetSessionId' in body ? body.targetSessionId : undefined;\n if (typeof targetSessionId !== 'string' || !/^[A-Za-z0-9_-]+$/u.test(targetSessionId)) {\n json(response, 400, { error: 'Invalid target session id.' });\n return;\n }\n json(response, 200, { sessionId: await options.resumeSession(session, targetSessionId) });\n return;\n }\n if (suffix === '/channels' && request.method === 'GET') {\n json(response, 200, { channels: options.headlessHub.channelFrames(sessionId) });\n return;\n }\n const channelMatch = /^\\/channel\\/([^/]+)$/u.exec(suffix);\n if (channelMatch !== null && request.method === 'POST') {\n const frameType = decodeURIComponent(channelMatch[1]);\n const connectionId = request.headers['x-doompi-connection'];\n options.headlessHub.receiveChannel(\n sessionId,\n frameType,\n parseJson(await readBody(request)),\n typeof connectionId === 'string' ? connectionId : 'http',\n );\n json(response, 202, { ok: true });\n return;\n }\n json(response, 404, { error: 'Not found.' });\n }\n\n server.on('upgrade', (request, socket, head) => {\n const url = requestPath(request);\n const mount = parseDoomSocketPath(url.pathname);\n if (!mount) {\n socket.write('HTTP/1.1 404 Not Found\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n if (!authorized(request, options.token)) {\n socket.write('HTTP/1.1 401 Unauthorized\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n if (\n mount.scope !== 'global' &&\n (!options.headlessHub.workspaces().some((entry) => entry.id === mount.workspaceId && entry.available !== false) ||\n (mount.scope === 'session' && options.headlessHub.session(mount.sessionId)?.workspaceId !== mount.workspaceId))\n ) {\n socket.write('HTTP/1.1 404 Not Found\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n const prepare =\n mount.scope === 'global'\n ? Promise.resolve(protocol)\n : createHeadlessProtocol({\n hub: options.headlessHub,\n mount,\n telemetry: options.telemetry,\n onNotice: options.onNotice,\n dormantSessions: dormant,\n });\n void prepare\n .then(async (selected) => {\n if (closed || socket.destroyed) {\n if (selected !== protocol) await selected.close();\n socket.destroy();\n return;\n }\n if (selected !== protocol) {\n scopedProtocols.add(selected);\n socket.once('close', () => {\n void selected.close().finally(() => scopedProtocols.delete(selected));\n });\n }\n webSockets.handleUpgrade(request, socket, head, (client) => {\n webSockets.emit('connection', client, selected);\n });\n })\n .catch(() => {\n socket.destroy();\n });\n });\n webSockets.on('connection', (socket, selected: typeof protocol) => {\n const handler = selected.accept(socket);\n if (!handler) return;\n const client: Client = { socket, handler };\n clients.add(client);\n socket.on('message', (data) => {\n if (Buffer.isBuffer(data)) handler.onData(data);\n else if (Array.isArray(data)) handler.onData(Buffer.concat(data));\n else handler.onData(new Uint8Array(data as ArrayBuffer));\n });\n socket.on('close', () => {\n clients.delete(client);\n handler.onClose();\n });\n socket.on('error', (error) => handler.onError(error));\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(options.port, options.host ?? '127.0.0.1', () => resolve());\n });\n const address = server.address();\n if (address === null || typeof address === 'string') {\n await new Promise<void>((resolve) => server.close(() => resolve()));\n throw new Error('The headless server did not expose a TCP address.');\n }\n const host = options.host ?? '127.0.0.1';\n const url = `http://${host}:${address.port}`;\n return {\n url,\n async close() {\n if (closed) return;\n closed = true;\n unsubscribe();\n for (const response of sse) response.end();\n sse.clear();\n for (const client of clients) client.socket.terminate();\n clients.clear();\n sessionMcp.close();\n await Promise.all([protocol.close(), ...[...scopedProtocols].map((selected) => selected.close())]);\n webSockets.close();\n server.closeAllConnections();\n await new Promise<void>((resolve, reject) =>\n server.close((error) => (error === undefined ? resolve() : reject(error))),\n );\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"headless-server.cjs","names":["path","fs","Readable","pipeline","WebSocketServer","createHeadlessProtocol","createSessionMcpRoutes","createSessionMcpConversationStore","createSessionMcpRegistrationStore","createServer","isPublicSessionMcpRoute","isSessionMcpHostRoute","DOOM_API_CALLER_HEADERS","DOOM_API_CALLER_LOCALITY_HEADER","DOOM_API_CALLER_STEP_UP_HEADER","BACKGROUND_CONTEXT","parseDoomSocketPath"],"sources":["../src/server/headlessServer.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\n\nimport type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { WebSocket } from 'ws';\nimport { WebSocketServer } from 'ws';\n\nimport {\n DOOM_API_CALLER_HEADERS,\n DOOM_API_CALLER_LOCALITY_HEADER,\n DOOM_API_CALLER_STEP_UP_HEADER,\n parseDoomSocketPath,\n type DoomApiMount,\n} from '../exports/packageApi';\nimport type { TranscriptPage, TranscriptPageRequest } from '../exports/sessionProtocol';\nimport type { DoomHubSessionReservations } from '../schemas/hubChannel';\nimport type { OpenSessionRecord } from '../services/openSessionRegistry';\nimport { observe, type ServerTelemetry } from '../services/serverTelemetry';\nimport { createSessionMcpConversationStore } from '../services/sessionMcpConversations';\nimport { createSessionMcpRegistrationStore } from '../services/sessionMcpRegistrationStore';\nimport type { SavedSession } from '../services/sqliteSessionHistory';\nimport type { HeadlessHub, HeadlessHubEvent, HeadlessHubSession } from './headlessHub';\nimport { createHeadlessProtocol } from './headlessProtocol';\nimport { createSessionMcpRoutes, isPublicSessionMcpRoute, isSessionMcpHostRoute } from './sessionMcpRoutes';\n\n/**\n * The canonical public shape of a package API request, as dispatch reads it.\n *\n * Groups: workspace id, session id, plugin base path, and the remainder handed\n * to the package. `settings` is the host's own API and sits beside `plugins`\n * rather than under it.\n *\n * Exported so the browser's URL construction can be pinned against it. The two\n * are written independently, in halves of this package that may not import each\n * other, and a disagreement between them is not a 404 from the route: the\n * service worker answers an unrecognised path out of the signed bundle cache,\n * so the symptom appears nowhere near the cause.\n */\nexport const DOOM_PACKAGE_API_PATH_PATTERN =\n /^\\/api(?:\\/workspaces\\/([^/]+)(?:\\/sessions\\/([^/]+))?)?\\/(?:plugins\\/([^/]+)|settings)(?:\\/(.*))?$/u;\n\nconst HEALTH_ROLE = 'hub';\nconst PROTOCOL_VERSION = 1;\nconst MAX_BODY_BYTES = 8 * 1024 * 1024;\nconst MAX_SESSION_FILE_BYTES = 25 * 1024 * 1024;\nconst SESSION_FILE_CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.avif': 'image/avif',\n '.bmp': 'image/bmp',\n '.gif': 'image/gif',\n '.jpeg': 'image/jpeg',\n '.jpg': 'image/jpeg',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.m4v': 'video/mp4',\n '.mov': 'video/quicktime',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.pdf': 'application/pdf',\n};\n\nexport interface HeadlessServerOptions {\n headlessHub: HeadlessHub;\n port: number;\n host?: string;\n /** Requests other than health require this bearer token when set. */\n token?: string;\n telemetry?: ServerTelemetry;\n onNotice?: (message: string) => void;\n compositions?: () => unknown;\n requestAsset?: (request: Request) => Promise<Response | undefined>;\n sessionHistory?: (session: HeadlessHubSession) => Promise<SavedSession[]>;\n workspaceHistory?: (workspaceId: string) => Promise<SavedSession[]>;\n resumeWorkspaceSession?: (workspaceId: string, targetSessionId: string) => Promise<string>;\n readDormantTranscript?: (\n record: OpenSessionRecord,\n request: TranscriptPageRequest,\n context: Context,\n ) => Promise<TranscriptPage>;\n restartSession?: (session: HeadlessHubSession) => Promise<void>;\n resumeSession?: (session: HeadlessHubSession, targetSessionId: string) => Promise<string>;\n /** Recorded sessions this server has not reopened, surfaced so a client can ask for one. */\n dormantSessions?: () => readonly OpenSessionRecord[];\n removeDormantSession?: (record: OpenSessionRecord) => void | Promise<void>;\n reviveSession?: (record: OpenSessionRecord) => Promise<void>;\n /** Trusted HTTPS origin advertised for public session MCP and OAuth endpoints. */\n sessionMcpPublicOrigin?: () => string | undefined;\n /** Durable state directory for verified Session MCP clients. */\n sessionMcpStateDir?: string;\n /** Monotonic exposure revision, including disable/re-enable cycles. */\n sessionMcpPublicOriginRevision?: () => number;\n isSessionPersisted?: (sessionId: string, cwd: string) => boolean;\n}\n\nexport interface HeadlessServer {\n readonly url: string;\n readonly sessionReservations: DoomHubSessionReservations;\n close(): Promise<void>;\n}\n\ntype ProtocolHandler = NonNullable<ReturnType<Awaited<ReturnType<typeof createHeadlessProtocol>>['accept']>>;\ntype Client = { socket: WebSocket; handler: ProtocolHandler };\n\nfunction sessionView(session: HeadlessHubSession): Record<string, unknown> {\n return {\n id: session.id,\n workspaceId: session.workspaceId,\n webComposition: session.webComposition,\n name: session.name,\n cwd: session.cwd,\n createdAt: session.createdAt,\n updatedAt: session.updatedAt ?? session.createdAt,\n phase: session.phase ?? 'idle',\n phaseSince: session.phaseSince ?? session.createdAt,\n attach: 'attached',\n pendingMessageCount: session.pendingMessageCount ?? 0,\n everPrompted: session.everPrompted ?? false,\n awaitingInput: session.awaitingInput ?? false,\n ...(session.lastSettledAt === undefined ? {} : { lastSettledAt: session.lastSettledAt }),\n ...(session.parentSessionId === undefined ? {} : { parentSessionId: session.parentSessionId }),\n ...(session.sessionProvenance === undefined ? {} : { sessionProvenance: session.sessionProvenance }),\n ...(session.pendingSetups === undefined ? {} : { pendingSetups: session.pendingSetups }),\n };\n}\n\n/** The dormant counterpart of `sessionView`; same shape, no runtime behind it. */\nfunction dormantView(record: OpenSessionRecord): Record<string, unknown> {\n return {\n id: record.sessionId,\n workspaceId: record.workspaceId,\n name: record.name,\n cwd: record.cwd,\n createdAt: record.createdAt,\n updatedAt: record.createdAt,\n phase: 'idle',\n phaseSince: record.createdAt,\n attach: 'detached',\n pendingMessageCount: 0,\n everPrompted: false,\n awaitingInput: false,\n dormant: true,\n ...(record.parentSessionId === undefined ? {} : { parentSessionId: record.parentSessionId }),\n ...(record.sessionProvenance === undefined ? {} : { sessionProvenance: record.sessionProvenance }),\n };\n}\n\nfunction json(response: ServerResponse, status: number, value: unknown): void {\n const body = JSON.stringify(value);\n response.writeHead(status, {\n 'content-type': 'application/json; charset=utf-8',\n 'content-length': Buffer.byteLength(body),\n });\n response.end(body);\n}\n\nfunction authorized(request: IncomingMessage, token: string | undefined): boolean {\n if (token === undefined) return true;\n const authorization = request.headers.authorization;\n if (authorization?.startsWith('Bearer ') && authorization.slice('Bearer '.length) === token) return true;\n const header = request.headers['x-doompi-token'];\n if (typeof header === 'string' && header === token) return true;\n try {\n return new URL(request.url ?? '/', 'http://doompi.local').searchParams.get('token') === token;\n } catch {\n return false;\n }\n}\n\nfunction requestPath(request: IncomingMessage): URL {\n return new URL(request.url ?? '/', 'http://doompi.local');\n}\n\nfunction parseTranscriptRequest(url: URL): TranscriptPageRequest {\n const request: TranscriptPageRequest = {};\n const cursor = url.searchParams.get('cursor');\n const direction = url.searchParams.get('direction');\n const limit = url.searchParams.get('limit');\n if (cursor !== null) request.cursor = cursor;\n if (direction !== null) {\n if (direction !== 'older' && direction !== 'newer') throw new Error('Invalid transcript direction');\n request.direction = direction;\n }\n if (limit !== null) {\n if (!/^\\d+$/u.test(limit)) throw new Error('Invalid transcript page limit');\n const parsed = Number(limit);\n if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 100) throw new Error('Invalid transcript page limit');\n request.limit = parsed;\n }\n return request;\n}\n\nasync function sessionFile(session: HeadlessHubSession, relativePath: string | null): Promise<Response> {\n if (!relativePath || relativePath.includes('\\0') || path.isAbsolute(relativePath))\n return Response.json({ error: 'Invalid session file path.' }, { status: 400 });\n let root: string;\n let filePath: string;\n try {\n root = await fs.promises.realpath(session.cwd);\n filePath = await fs.promises.realpath(path.resolve(root, relativePath));\n } catch {\n return Response.json({ error: 'Session file not found.' }, { status: 404 });\n }\n const contained = path.relative(root, filePath);\n if (contained === '..' || contained.startsWith(`..${path.sep}`) || path.isAbsolute(contained))\n return Response.json({ error: 'Session file is outside the working directory.' }, { status: 403 });\n let handle: fs.promises.FileHandle;\n try {\n handle = await fs.promises.open(filePath, 'r');\n } catch {\n return Response.json({ error: 'Session file not found.' }, { status: 404 });\n }\n try {\n const stat = await handle.stat();\n if (!stat.isFile()) return Response.json({ error: 'Session file not found.' }, { status: 404 });\n if (stat.size > MAX_SESSION_FILE_BYTES)\n return Response.json({ error: 'Session file is too large.' }, { status: 413 });\n const bytes = await handle.readFile();\n if (bytes.byteLength > MAX_SESSION_FILE_BYTES)\n return Response.json({ error: 'Session file is too large.' }, { status: 413 });\n return new Response(bytes, {\n headers: {\n 'content-type': SESSION_FILE_CONTENT_TYPES[path.extname(filePath).toLowerCase()] ?? 'application/octet-stream',\n 'content-length': String(bytes.byteLength),\n 'cache-control': 'no-store',\n 'x-content-type-options': 'nosniff',\n },\n });\n } finally {\n await handle.close();\n }\n}\n\nasync function directorySuggestions(query: string, sessions: readonly HeadlessHubSession[]): Promise<string[]> {\n const typed = query.trim();\n if (typed === '') return [];\n const matches = (value: string): boolean => value.toLowerCase().includes(typed.toLowerCase());\n const known = [...new Set([process.cwd(), ...sessions.map((session) => session.cwd)])].filter(matches);\n const candidates = path.isAbsolute(typed)\n ? [typed]\n : typed.includes(path.sep)\n ? [path.resolve(process.cwd(), typed)]\n : [path.join(path.dirname(process.cwd()), typed), path.join(process.cwd(), typed)];\n const completed: string[] = [];\n for (const candidate of candidates) {\n const drilling = typed.endsWith(path.sep);\n const parent = drilling ? candidate : path.dirname(candidate);\n const partial = drilling ? '' : path.basename(candidate).toLowerCase();\n try {\n const entries = await fs.promises.readdir(parent, { withFileTypes: true });\n completed.push(\n ...entries\n .filter((entry) => entry.isDirectory() && entry.name.toLowerCase().includes(partial))\n .map((entry) => path.join(parent, entry.name)),\n );\n } catch {\n // A missing or unreadable parent simply has no completions.\n }\n }\n return [...new Set([...known, ...completed.sort((left, right) => left.localeCompare(right))])].slice(0, 12);\n}\n\nasync function readBody(request: IncomingMessage): Promise<Uint8Array> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n size += buffer.byteLength;\n if (size > MAX_BODY_BYTES) throw new Error('Request body is too large.');\n chunks.push(buffer);\n }\n return Buffer.concat(chunks);\n}\n\nfunction parseJson(body: Uint8Array): unknown {\n if (body.byteLength === 0) return {};\n return JSON.parse(Buffer.from(body).toString('utf8')) as unknown;\n}\n\nfunction webRequest(request: IncomingMessage, url: URL, signal?: AbortSignal): Request {\n const headers = new Headers();\n for (const [name, value] of Object.entries(request.headers)) {\n if (value !== undefined) headers.set(name, Array.isArray(value) ? value.join(', ') : value);\n }\n return new Request(url, {\n method: request.method,\n headers,\n signal,\n ...(request.method === 'GET' || request.method === 'HEAD'\n ? {}\n : {\n body: Readable.toWeb(request) as never,\n duplex: 'half' as const,\n }),\n });\n}\n\nasync function writeResponse(response: ServerResponse, result: Response): Promise<void> {\n response.writeHead(result.status, Object.fromEntries(result.headers.entries()));\n if (result.body === null) {\n response.end();\n return;\n }\n await pipeline(Readable.fromWeb(result.body as import('node:stream/web').ReadableStream), response);\n}\n\nfunction eventFrame(event: HeadlessHubEvent): Record<string, unknown> {\n switch (event.kind) {\n case 'upsert':\n return { type: 'session_upsert', session: sessionView(event.session) };\n case 'removed':\n return { type: 'session_removed', sessionId: event.sessionId };\n case 'workspace_upsert':\n return { type: 'workspace_upsert', workspace: event.workspace };\n case 'workspace_removed':\n return { type: 'workspace_removed', workspaceId: event.workspaceId };\n case 'channel':\n return {\n type: 'channel_frame',\n frameType: event.frameType,\n sessionId: event.sessionId,\n payload: event.payload,\n ...(event.connectionId === undefined ? {} : { connectionId: event.connectionId }),\n };\n }\n}\n\nfunction writeSse(response: ServerResponse, event: string, value: unknown): void {\n response.write(`event: ${event}\\ndata: ${JSON.stringify(value)}\\n\\n`);\n}\n\n/**\n * Client-neutral HTTP and WebSocket boundary for the process-local headless hub.\n * It deliberately delegates every session operation to HeadlessHub, so transport\n * clients cannot create a second session backend or bypass its lifecycle.\n */\nexport async function serveHeadlessServer(options: HeadlessServerOptions): Promise<HeadlessServer> {\n const clients = new Set<Client>();\n const scopedProtocols = new Set<Awaited<ReturnType<typeof createHeadlessProtocol>>>();\n const sse = new Set<ServerResponse>();\n let closed = false;\n const webSockets = new WebSocketServer({ noServer: true });\n /**\n * A recorded session is dormant exactly while the hub has no session for its\n * id, so reviving one needs no bookkeeping beyond the hub itself.\n */\n const dormant = (): readonly OpenSessionRecord[] =>\n (options.dormantSessions?.() ?? []).filter((record) => options.headlessHub.session(record.sessionId) === undefined);\n const protocol = await createHeadlessProtocol({\n hub: options.headlessHub,\n telemetry: options.telemetry,\n onNotice: options.onNotice,\n dormantSessions: dormant,\n });\n const sessionMcp = createSessionMcpRoutes({\n headlessHub: options.headlessHub,\n publicOrigin: options.sessionMcpPublicOrigin ?? (() => undefined),\n publicOriginRevision: options.sessionMcpPublicOriginRevision,\n onNotice: options.onNotice,\n isSessionPersisted: options.isSessionPersisted,\n ...(options.sessionMcpStateDir === undefined\n ? {}\n : {\n conversationStore: createSessionMcpConversationStore(options.sessionMcpStateDir),\n registrationStore: createSessionMcpRegistrationStore({\n stateDir: options.sessionMcpStateDir,\n onNotice: options.onNotice,\n }),\n }),\n });\n const server = createServer((request, response) => {\n void handleRequest(request, response).catch((error: unknown) => {\n if (response.headersSent) {\n response.destroy();\n return;\n }\n json(response, 500, { error: error instanceof Error ? error.message : String(error) });\n });\n });\n\n const unsubscribe = options.headlessHub.onEvent((event) => {\n const frame = eventFrame(event);\n for (const response of sse) writeSse(response, String(frame.type), frame);\n });\n\n async function handleRequest(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const url = requestPath(request);\n if (url.pathname === '/api/health' && request.method === 'GET') {\n json(response, 200, {\n ok: true,\n role: HEALTH_ROLE,\n protocol: PROTOCOL_VERSION,\n sessions: options.headlessHub.snapshot().length,\n });\n return;\n }\n if (isPublicSessionMcpRoute(request.method ?? 'GET', url.pathname)) {\n const sessionMcpAbort = new AbortController();\n const abortSessionMcp = (): void => {\n if (!response.writableEnded) sessionMcpAbort.abort();\n };\n response.once('close', abortSessionMcp);\n const publicSessionMcp = await sessionMcp.handlePublic(webRequest(request, url, sessionMcpAbort.signal));\n if (publicSessionMcp !== undefined) {\n try {\n await writeResponse(response, publicSessionMcp);\n } finally {\n response.off('close', abortSessionMcp);\n }\n return;\n }\n response.off('close', abortSessionMcp);\n }\n if (!authorized(request, options.token)) {\n json(response, 401, { error: 'Unauthorized.' });\n return;\n }\n if (isSessionMcpHostRoute(url.pathname)) {\n const hostSessionMcp = await sessionMcp.handleHost(webRequest(request, url));\n if (hostSessionMcp !== undefined) {\n await writeResponse(response, hostSessionMcp);\n return;\n }\n }\n if (url.pathname === '/api/remote' || url.pathname.startsWith('/api/remote/')) {\n const headers = new Headers();\n for (const [name, value] of Object.entries(request.headers)) {\n if (value !== undefined && name !== 'authorization' && name !== 'x-doompi-token')\n headers.set(name, Array.isArray(value) ? value.join(', ') : value);\n }\n const result = await options.headlessHub.requestApi(\n { scope: 'global' },\n 'remote',\n new Request(`http://doompi.local${url.pathname.slice('/api/remote'.length)}${url.search}`, {\n method: request.method,\n headers,\n ...(request.method === 'GET' || request.method === 'HEAD'\n ? {}\n : { body: Buffer.from(await readBody(request)) }),\n }),\n );\n await writeResponse(response, result);\n return;\n }\n if (url.pathname === '/api/telemetry/browser' && request.method === 'POST') {\n const body = JSON.parse(new TextDecoder().decode(await readBody(request))) as { v?: unknown; events?: unknown };\n if (body.v !== 1 || !Array.isArray(body.events) || body.events.length > 10) {\n json(response, 400, { error: 'Invalid browser telemetry batch.' });\n return;\n }\n for (const event of body.events) {\n if (\n !event ||\n typeof event !== 'object' ||\n typeof event.name !== 'string' ||\n !/^web\\.browser\\.[a-z_]{1,40}$/u.test(event.name)\n )\n continue;\n if (options.telemetry)\n observe(\n options.telemetry.recordEvent(event.name, {\n ...(typeof event.duration_ms === 'number' && Number.isFinite(event.duration_ms) && event.duration_ms >= 0\n ? { duration_ms: event.duration_ms }\n : {}),\n ...(typeof event.count === 'number' && Number.isFinite(event.count) && event.count >= 0\n ? { count: event.count }\n : {}),\n }),\n );\n }\n json(response, 200, { ok: true });\n return;\n }\n if (url.pathname === '/api/compositions' && request.method === 'GET') {\n json(response, 200, options.compositions?.() ?? { workspaces: [] });\n return;\n }\n if (\n (url.pathname.startsWith('/api/web-plugins/') ||\n url.pathname === '/bundle-manifest.json' ||\n url.pathname.startsWith('/bundle-assets/')) &&\n options.requestAsset\n ) {\n const result = await options.requestAsset(new Request(url, { method: request.method }));\n if (result) {\n await writeResponse(response, result);\n return;\n }\n }\n if (url.pathname === '/api/directories' && request.method === 'GET') {\n json(response, 200, {\n directories: await directorySuggestions(url.searchParams.get('q') ?? '', options.headlessHub.snapshot()),\n });\n return;\n }\n // Verified browser bundles can remain active across a server upgrade. Keep\n // their former create route working while they refresh to workspace routes.\n if (url.pathname === '/api/sessions' && request.method === 'POST') {\n const body = parseJson(await readBody(request));\n if (\n !body ||\n typeof body !== 'object' ||\n !('cwd' in body) ||\n typeof body.cwd !== 'string' ||\n !body.cwd.trim() ||\n ('name' in body && typeof body.name !== 'string')\n ) {\n json(response, 400, { error: 'A session needs a working directory and an optional name.' });\n return;\n }\n const created = await options.headlessHub.sessionService.create({\n cwd: body.cwd,\n name: 'name' in body ? String(body.name) : path.basename(body.cwd),\n });\n json(response, 201, { sessionId: created.sessionId });\n return;\n }\n const workspaceSessions = /^\\/api\\/workspaces\\/([^/]+)\\/sessions$/u.exec(url.pathname);\n if (workspaceSessions) {\n const workspaceId = decodeURIComponent(workspaceSessions[1]);\n const workspace = options.headlessHub.workspaces().find((entry) => entry.id === workspaceId);\n if (!workspace) {\n json(response, 404, { error: 'Workspace not found.' });\n return;\n }\n if (request.method === 'GET') {\n json(response, 200, {\n sessions: [\n ...options.headlessHub\n .snapshot()\n .filter((session) => session.workspaceId === workspaceId)\n .map(sessionView),\n ...dormant()\n .filter((record) => record.workspaceId === workspaceId)\n .map(dormantView),\n ],\n });\n return;\n }\n if (request.method === 'POST') {\n if (workspace.available === false) {\n json(response, 409, { error: 'Workspace is unavailable.' });\n return;\n }\n const body = parseJson(await readBody(request));\n if (!body || typeof body !== 'object' || ('name' in body && typeof body.name !== 'string')) {\n json(response, 400, { error: 'A session accepts an optional name.' });\n return;\n }\n const created = await options.headlessHub.sessionService.create({\n cwd: workspace.root,\n name: 'name' in body ? String(body.name) : path.basename(workspace.root),\n });\n json(response, 201, { sessionId: created.sessionId });\n return;\n }\n }\n if (url.pathname === '/api/events' && request.method === 'GET') {\n response.writeHead(200, {\n 'cache-control': 'no-cache',\n connection: 'keep-alive',\n 'content-type': 'text/event-stream; charset=utf-8',\n });\n sse.add(response);\n response.on('close', () => sse.delete(response));\n writeSse(response, 'sessions_snapshot', {\n type: 'sessions_snapshot',\n sessions: [...options.headlessHub.snapshot().map(sessionView), ...dormant().map(dormantView)],\n });\n writeSse(response, 'workspaces_snapshot', {\n type: 'workspaces_snapshot',\n workspaces: options.headlessHub.workspaces(),\n });\n return;\n }\n if (url.pathname === '/api/workspaces') {\n if (request.method === 'GET') {\n json(response, 200, { workspaces: options.headlessHub.workspaces() });\n return;\n }\n if (request.method === 'POST') {\n const body = parseJson(await readBody(request));\n if (typeof body !== 'object' || body === null || !('root' in body) || typeof body.root !== 'string') {\n json(response, 400, { error: 'A workspace root is required.' });\n return;\n }\n const workspace = await options.headlessHub.admitWorkspace(body.root);\n json(response, 201, { workspace });\n return;\n }\n }\n const workspaceHistoryMatch = /^\\/api\\/workspaces\\/([^/]+)\\/history$/u.exec(url.pathname);\n if (workspaceHistoryMatch && request.method === 'GET' && options.workspaceHistory) {\n const workspaceId = decodeURIComponent(workspaceHistoryMatch[1]);\n const workspace = options.headlessHub.workspaces().find((entry) => entry.id === workspaceId);\n if (!workspace) {\n json(response, 404, { error: 'Workspace not found.' });\n return;\n }\n if (workspace.available === false) {\n json(response, 409, { error: 'Workspace is unavailable.' });\n return;\n }\n json(response, 200, { sessions: await options.workspaceHistory(workspaceId) });\n return;\n }\n const workspaceResumeMatch = /^\\/api\\/workspaces\\/([^/]+)\\/resume$/u.exec(url.pathname);\n if (workspaceResumeMatch && request.method === 'POST' && options.resumeWorkspaceSession) {\n const workspaceId = decodeURIComponent(workspaceResumeMatch[1]);\n const workspace = options.headlessHub.workspaces().find((entry) => entry.id === workspaceId);\n if (!workspace) {\n json(response, 404, { error: 'Workspace not found.' });\n return;\n }\n if (workspace.available === false) {\n json(response, 409, { error: 'Workspace is unavailable.' });\n return;\n }\n const body = parseJson(await readBody(request));\n const targetSessionId =\n body && typeof body === 'object' && 'targetSessionId' in body ? body.targetSessionId : undefined;\n if (typeof targetSessionId !== 'string' || !/^[A-Za-z0-9_-]+$/u.test(targetSessionId)) {\n json(response, 400, { error: 'Invalid target session id.' });\n return;\n }\n json(response, 200, { sessionId: await options.resumeWorkspaceSession(workspaceId, targetSessionId) });\n return;\n }\n const workspaceMatch = /^\\/api\\/workspaces\\/([^/]+)$/u.exec(url.pathname);\n if (workspaceMatch && request.method === 'GET') {\n const workspace = options.headlessHub\n .workspaces()\n .find((entry) => entry.id === decodeURIComponent(workspaceMatch[1]));\n json(response, workspace ? 200 : 404, workspace ? { workspace } : { error: 'Workspace not found.' });\n return;\n }\n if (workspaceMatch && request.method === 'DELETE') {\n const id = decodeURIComponent(workspaceMatch[1]);\n if (!options.headlessHub.workspaces().some((workspace) => workspace.id === id)) {\n json(response, 404, { error: 'Workspace not found.' });\n return;\n }\n if (\n options.headlessHub.snapshot().some((session) => session.workspaceId === id) ||\n dormant().some((session) => session.workspaceId === id)\n ) {\n json(response, 409, { error: 'Workspace still has sessions.' });\n return;\n }\n await options.headlessHub.removeWorkspace(id);\n json(response, 200, { ok: true });\n return;\n }\n const pluginMatch = DOOM_PACKAGE_API_PATH_PATTERN.exec(url.pathname);\n if (pluginMatch !== null && request.method !== 'CONNECT') {\n const workspaceId = pluginMatch[1] === undefined ? undefined : decodeURIComponent(pluginMatch[1]);\n const sessionId = pluginMatch[2] === undefined ? undefined : decodeURIComponent(pluginMatch[2]);\n if (workspaceId !== undefined && !options.headlessHub.workspaces().some((entry) => entry.id === workspaceId)) {\n json(response, 404, { error: 'Workspace not found.' });\n return;\n }\n if (sessionId !== undefined && options.headlessHub.session(sessionId)?.workspaceId !== workspaceId) {\n json(response, 404, { error: 'Session not found.' });\n return;\n }\n const mount: DoomApiMount =\n sessionId !== undefined\n ? { scope: 'session', sessionId }\n : workspaceId !== undefined\n ? { scope: 'workspace', workspaceId }\n : { scope: 'global' };\n const basePath = pluginMatch[3] === undefined ? 'settings' : decodeURIComponent(pluginMatch[3]);\n if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(basePath)) {\n json(response, 400, { error: 'Invalid package API base path.' });\n return;\n }\n if (pluginMatch[3] === 'settings' || (sessionId !== undefined && basePath === 'settings')) {\n json(response, 404, { error: 'Not found.' });\n return;\n }\n const headers = new Headers();\n // Only the authenticated loopback proxy knows the server token and may\n // forward caller stamps. Browser-supplied stamps remain untrusted.\n const trustedCaller = options.token !== undefined && authorized(request, options.token);\n for (const [name, value] of Object.entries(request.headers)) {\n if (\n value !== undefined &&\n !['host', 'authorization', 'x-doompi-token'].includes(name) &&\n (trustedCaller || !DOOM_API_CALLER_HEADERS.includes(name as (typeof DOOM_API_CALLER_HEADERS)[number]))\n )\n headers.set(name, Array.isArray(value) ? value.join(', ') : value);\n }\n if (!trustedCaller) {\n headers.set(DOOM_API_CALLER_LOCALITY_HEADER, 'local');\n headers.set(DOOM_API_CALLER_STEP_UP_HEADER, 'not-required');\n }\n const abort = new AbortController();\n const disconnected = (): void => {\n if (!response.writableEnded) abort.abort();\n };\n response.once('close', disconnected);\n try {\n const started = performance.now();\n const attributes = {\n scope: mount.scope,\n 'plugin.name': basePath,\n 'http.operation': request.method,\n ...(mount.scope === 'workspace' ? { 'workspace.id': mount.workspaceId } : {}),\n ...(mount.scope === 'session' ? { 'session.id': mount.sessionId } : {}),\n };\n const dispatch = async () =>\n options.headlessHub.requestApi(\n mount,\n basePath,\n new Request(`http://doompi.local/${pluginMatch[4] ?? ''}${url.search}`, {\n method: request.method,\n headers,\n signal: abort.signal,\n ...(request.method === 'GET' || request.method === 'HEAD'\n ? {}\n : { body: Buffer.from(await readBody(request)) }),\n }),\n );\n const result = options.telemetry\n ? await options.telemetry.runInSpan('doompi_server.plugin.request', attributes, dispatch)\n : await dispatch();\n if (options.telemetry)\n observe(\n options.telemetry.recordEvent('doompi_server.plugin.response', {\n ...attributes,\n status: result.status,\n durationMs: performance.now() - started,\n }),\n options.onNotice,\n );\n await writeResponse(response, result);\n } finally {\n response.off('close', disconnected);\n }\n return;\n }\n const sessionMatch = /^\\/api\\/workspaces\\/([^/]+)\\/sessions\\/([^/]+)(?:\\/(.*))?$/u.exec(url.pathname);\n if (sessionMatch === null) {\n json(response, 404, { error: 'Not found.' });\n return;\n }\n const sessionId = decodeURIComponent(sessionMatch[2]);\n const workspaceId = decodeURIComponent(sessionMatch[1]);\n const session = options.headlessHub.session(sessionId);\n const suffix = sessionMatch[3] === undefined ? '' : `/${sessionMatch[3]}`;\n if (suffix === '/transcript' && request.method === 'GET' && options.readDormantTranscript) {\n const record = dormant().find(\n (candidate) => candidate.sessionId === sessionId && candidate.workspaceId === workspaceId,\n );\n if (record === undefined) {\n json(response, 404, { error: 'Dormant session not found.' });\n return;\n }\n let transcriptRequest: TranscriptPageRequest;\n try {\n transcriptRequest = parseTranscriptRequest(url);\n } catch (error) {\n json(response, 400, { error: error instanceof Error ? error.message : 'Invalid transcript request.' });\n return;\n }\n try {\n const page = await options.readDormantTranscript(record, transcriptRequest, BACKGROUND_CONTEXT);\n json(response, 200, page);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const status = message === 'STALE_TRANSCRIPT_CURSOR' ? 409 : 422;\n json(response, status, {\n error: status === 409 ? message : 'Saved transcript is unavailable.',\n });\n }\n return;\n }\n // Revive is the one session route that answers before a runtime exists: it\n // is what creates one.\n if (sessionMatch[3] === 'revive' && request.method === 'POST' && options.reviveSession) {\n const record = dormant().find(\n (candidate) => candidate.sessionId === sessionId && candidate.workspaceId === workspaceId,\n );\n if (record === undefined) {\n json(response, 404, { error: 'Dormant session not found.' });\n return;\n }\n await options.reviveSession(record);\n json(response, 200, { ok: true });\n return;\n }\n if (session === undefined || session.workspaceId !== workspaceId) {\n const record = dormant().find(\n (candidate) => candidate.sessionId === sessionId && candidate.workspaceId === workspaceId,\n );\n if (request.method === 'DELETE' && record !== undefined && options.removeDormantSession !== undefined) {\n sessionMcp.closeSessionBinding(record.sessionId);\n await options.removeDormantSession(record);\n options.headlessHub.notifySessionRemoved(record.sessionId);\n json(response, 200, { ok: true });\n return;\n }\n json(response, 404, { error: 'Session not found.' });\n return;\n }\n if (suffix === '' && request.method === 'GET') {\n json(response, 200, sessionView(session));\n return;\n }\n if (suffix === '' && request.method === 'DELETE') {\n sessionMcp.closeSessionBinding(sessionId);\n await options.headlessHub.closeSession(sessionId);\n json(response, 200, { ok: true });\n return;\n }\n if (suffix === '/history' && request.method === 'GET' && options.sessionHistory) {\n json(response, 200, { sessions: await options.sessionHistory(session) });\n return;\n }\n if (suffix === '/file' && request.method === 'GET') {\n await writeResponse(response, await sessionFile(session, url.searchParams.get('path')));\n return;\n }\n if (suffix === '/restart' && request.method === 'POST' && options.restartSession) {\n await options.restartSession(session);\n json(response, 200, { ok: true });\n return;\n }\n if (suffix === '/resume' && request.method === 'POST' && options.resumeSession) {\n const body = parseJson(await readBody(request));\n const targetSessionId =\n body && typeof body === 'object' && 'targetSessionId' in body ? body.targetSessionId : undefined;\n if (typeof targetSessionId !== 'string' || !/^[A-Za-z0-9_-]+$/u.test(targetSessionId)) {\n json(response, 400, { error: 'Invalid target session id.' });\n return;\n }\n json(response, 200, { sessionId: await options.resumeSession(session, targetSessionId) });\n return;\n }\n if (suffix === '/channels' && request.method === 'GET') {\n json(response, 200, { channels: options.headlessHub.channelFrames(sessionId) });\n return;\n }\n const channelMatch = /^\\/channel\\/([^/]+)$/u.exec(suffix);\n if (channelMatch !== null && request.method === 'POST') {\n const frameType = decodeURIComponent(channelMatch[1]);\n const connectionId = request.headers['x-doompi-connection'];\n options.headlessHub.receiveChannel(\n sessionId,\n frameType,\n parseJson(await readBody(request)),\n typeof connectionId === 'string' ? connectionId : 'http',\n );\n json(response, 202, { ok: true });\n return;\n }\n json(response, 404, { error: 'Not found.' });\n }\n\n server.on('upgrade', (request, socket, head) => {\n const url = requestPath(request);\n const mount = parseDoomSocketPath(url.pathname);\n if (!mount) {\n socket.write('HTTP/1.1 404 Not Found\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n if (!authorized(request, options.token)) {\n socket.write('HTTP/1.1 401 Unauthorized\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n if (\n mount.scope !== 'global' &&\n (!options.headlessHub.workspaces().some((entry) => entry.id === mount.workspaceId && entry.available !== false) ||\n (mount.scope === 'session' && options.headlessHub.session(mount.sessionId)?.workspaceId !== mount.workspaceId))\n ) {\n socket.write('HTTP/1.1 404 Not Found\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n const prepare =\n mount.scope === 'global'\n ? Promise.resolve(protocol)\n : createHeadlessProtocol({\n hub: options.headlessHub,\n mount,\n telemetry: options.telemetry,\n onNotice: options.onNotice,\n dormantSessions: dormant,\n });\n void prepare\n .then(async (selected) => {\n if (closed || socket.destroyed) {\n if (selected !== protocol) await selected.close();\n socket.destroy();\n return;\n }\n if (selected !== protocol) {\n scopedProtocols.add(selected);\n socket.once('close', () => {\n void selected.close().finally(() => scopedProtocols.delete(selected));\n });\n }\n webSockets.handleUpgrade(request, socket, head, (client) => {\n webSockets.emit('connection', client, selected);\n });\n })\n .catch(() => {\n socket.destroy();\n });\n });\n webSockets.on('connection', (socket, selected: typeof protocol) => {\n const handler = selected.accept(socket);\n if (!handler) return;\n const client: Client = { socket, handler };\n clients.add(client);\n socket.on('message', (data) => {\n if (Buffer.isBuffer(data)) handler.onData(data);\n else if (Array.isArray(data)) handler.onData(Buffer.concat(data));\n else handler.onData(new Uint8Array(data as ArrayBuffer));\n });\n socket.on('close', () => {\n clients.delete(client);\n handler.onClose();\n });\n socket.on('error', (error) => handler.onError(error));\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(options.port, options.host ?? '127.0.0.1', () => resolve());\n });\n const address = server.address();\n if (address === null || typeof address === 'string') {\n await new Promise<void>((resolve) => server.close(() => resolve()));\n throw new Error('The headless server did not expose a TCP address.');\n }\n const host = options.host ?? '127.0.0.1';\n const url = `http://${host}:${address.port}`;\n return {\n url,\n sessionReservations: sessionMcp.reservations,\n async close() {\n if (closed) return;\n closed = true;\n unsubscribe();\n for (const response of sse) response.end();\n sse.clear();\n for (const client of clients) client.socket.terminate();\n clients.clear();\n sessionMcp.close();\n await Promise.all([protocol.close(), ...[...scopedProtocols].map((selected) => selected.close())]);\n webSockets.close();\n server.closeAllConnections();\n await new Promise<void>((resolve, reject) =>\n server.close((error) => (error === undefined ? resolve() : reject(error))),\n );\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DoomHubSessionReservations } from "./schemas/hubChannel.cjs";
|
|
1
2
|
import { TranscriptPage, TranscriptPageRequest } from "./schemas/sessionProtocol.cjs";
|
|
2
3
|
import { SavedSession } from "./services/sqliteSessionHistory/index.cjs";
|
|
3
4
|
import "./session-protocol.cjs";
|
|
@@ -42,11 +43,15 @@ export interface HeadlessServerOptions {
|
|
|
42
43
|
reviveSession?: (record: OpenSessionRecord) => Promise<void>;
|
|
43
44
|
/** Trusted HTTPS origin advertised for public session MCP and OAuth endpoints. */
|
|
44
45
|
sessionMcpPublicOrigin?: () => string | undefined;
|
|
46
|
+
/** Durable state directory for verified Session MCP clients. */
|
|
47
|
+
sessionMcpStateDir?: string;
|
|
45
48
|
/** Monotonic exposure revision, including disable/re-enable cycles. */
|
|
46
49
|
sessionMcpPublicOriginRevision?: () => number;
|
|
50
|
+
isSessionPersisted?: (sessionId: string, cwd: string) => boolean;
|
|
47
51
|
}
|
|
48
52
|
export interface HeadlessServer {
|
|
49
53
|
readonly url: string;
|
|
54
|
+
readonly sessionReservations: DoomHubSessionReservations;
|
|
50
55
|
close(): Promise<void>;
|
|
51
56
|
}
|
|
52
57
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-server.d.cts","names":[],"sources":["../src/server/headlessServer.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"headless-server.d.cts","names":[],"sources":["../src/server/headlessServer.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;qBA0Ca,+BAA6B;iBAuBzB;EACf,aAAa;EACb;EACA;;EAEA;EACA,YAAY;EACZ,YAAY;EACZ;EACA,gBAAgB,SAAS,YAAY,QAAQ;EAC7C,kBAAkB,SAAS,uBAAuB,QAAQ;EAC1D,oBAAoB,wBAAwB,QAAQ;EACpD,0BAA0B,qBAAqB,4BAA4B;EAC3E,yBACE,QAAQ,mBACR,SAAS,uBACT,SAAS,YACN,QAAQ;EACb,kBAAkB,SAAS,uBAAuB;EAClD,iBAAiB,SAAS,oBAAoB,4BAA4B;;EAE1E,iCAAiC;EACjC,wBAAwB,QAAQ,6BAA6B;EAC7D,iBAAiB,QAAQ,sBAAsB;;EAE/C;;EAEA;;EAEA;EACA,sBAAsB,mBAAmB;;iBAG1B;WACN;WACA,qBAAqB;EAC9B,SAAS;;;;;;;wBA8OW,oBAAoB,SAAS,wBAAwB,QAAQ"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DoomHubSessionReservations } from "./schemas/hubChannel.mjs";
|
|
1
2
|
import { TranscriptPage, TranscriptPageRequest } from "./schemas/sessionProtocol.mjs";
|
|
2
3
|
import { SavedSession } from "./services/sqliteSessionHistory/index.mjs";
|
|
3
4
|
import "./session-protocol.mjs";
|
|
@@ -42,11 +43,15 @@ export interface HeadlessServerOptions {
|
|
|
42
43
|
reviveSession?: (record: OpenSessionRecord) => Promise<void>;
|
|
43
44
|
/** Trusted HTTPS origin advertised for public session MCP and OAuth endpoints. */
|
|
44
45
|
sessionMcpPublicOrigin?: () => string | undefined;
|
|
46
|
+
/** Durable state directory for verified Session MCP clients. */
|
|
47
|
+
sessionMcpStateDir?: string;
|
|
45
48
|
/** Monotonic exposure revision, including disable/re-enable cycles. */
|
|
46
49
|
sessionMcpPublicOriginRevision?: () => number;
|
|
50
|
+
isSessionPersisted?: (sessionId: string, cwd: string) => boolean;
|
|
47
51
|
}
|
|
48
52
|
export interface HeadlessServer {
|
|
49
53
|
readonly url: string;
|
|
54
|
+
readonly sessionReservations: DoomHubSessionReservations;
|
|
50
55
|
close(): Promise<void>;
|
|
51
56
|
}
|
|
52
57
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-server.d.mts","names":[],"sources":["../src/server/headlessServer.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"headless-server.d.mts","names":[],"sources":["../src/server/headlessServer.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;qBA0Ca,+BAA6B;iBAuBzB;EACf,aAAa;EACb;EACA;;EAEA;EACA,YAAY;EACZ,YAAY;EACZ;EACA,gBAAgB,SAAS,YAAY,QAAQ;EAC7C,kBAAkB,SAAS,uBAAuB,QAAQ;EAC1D,oBAAoB,wBAAwB,QAAQ;EACpD,0BAA0B,qBAAqB,4BAA4B;EAC3E,yBACE,QAAQ,mBACR,SAAS,uBACT,SAAS,YACN,QAAQ;EACb,kBAAkB,SAAS,uBAAuB;EAClD,iBAAiB,SAAS,oBAAoB,4BAA4B;;EAE1E,iCAAiC;EACjC,wBAAwB,QAAQ,6BAA6B;EAC7D,iBAAiB,QAAQ,sBAAsB;;EAE/C;;EAEA;;EAEA;EACA,sBAAsB,mBAAmB;;iBAG1B;WACN;WACA,qBAAqB;EAC9B,SAAS;;;;;;;wBA8OW,oBAAoB,SAAS,wBAAwB,QAAQ"}
|
package/dist/headless-server.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { DOOM_API_CALLER_HEADERS, DOOM_API_CALLER_LOCALITY_HEADER, DOOM_API_CALLER_STEP_UP_HEADER, parseDoomSocketPath } from "./schemas/packageApi.mjs";
|
|
2
|
+
import { createSessionMcpConversationStore } from "./services/sessionMcpConversations/index.mjs";
|
|
2
3
|
import { observe } from "./server-telemetry.mjs";
|
|
4
|
+
import { createSessionMcpRegistrationStore } from "./services/sessionMcpRegistrationStore/index.mjs";
|
|
3
5
|
import { createHeadlessProtocol } from "./server/headlessProtocol.mjs";
|
|
4
6
|
import { createSessionMcpRoutes, isPublicSessionMcpRoute, isSessionMcpHostRoute } from "./server/sessionMcpRoutes.mjs";
|
|
5
7
|
import fs from "node:fs";
|
|
@@ -60,7 +62,8 @@ function sessionView(session) {
|
|
|
60
62
|
awaitingInput: session.awaitingInput ?? false,
|
|
61
63
|
...session.lastSettledAt === void 0 ? {} : { lastSettledAt: session.lastSettledAt },
|
|
62
64
|
...session.parentSessionId === void 0 ? {} : { parentSessionId: session.parentSessionId },
|
|
63
|
-
...session.sessionProvenance === void 0 ? {} : { sessionProvenance: session.sessionProvenance }
|
|
65
|
+
...session.sessionProvenance === void 0 ? {} : { sessionProvenance: session.sessionProvenance },
|
|
66
|
+
...session.pendingSetups === void 0 ? {} : { pendingSetups: session.pendingSetups }
|
|
64
67
|
};
|
|
65
68
|
}
|
|
66
69
|
/** The dormant counterpart of `sessionView`; same shape, no runtime behind it. */
|
|
@@ -267,7 +270,16 @@ async function serveHeadlessServer(options) {
|
|
|
267
270
|
const sessionMcp = createSessionMcpRoutes({
|
|
268
271
|
headlessHub: options.headlessHub,
|
|
269
272
|
publicOrigin: options.sessionMcpPublicOrigin ?? (() => void 0),
|
|
270
|
-
publicOriginRevision: options.sessionMcpPublicOriginRevision
|
|
273
|
+
publicOriginRevision: options.sessionMcpPublicOriginRevision,
|
|
274
|
+
onNotice: options.onNotice,
|
|
275
|
+
isSessionPersisted: options.isSessionPersisted,
|
|
276
|
+
...options.sessionMcpStateDir === void 0 ? {} : {
|
|
277
|
+
conversationStore: createSessionMcpConversationStore(options.sessionMcpStateDir),
|
|
278
|
+
registrationStore: createSessionMcpRegistrationStore({
|
|
279
|
+
stateDir: options.sessionMcpStateDir,
|
|
280
|
+
onNotice: options.onNotice
|
|
281
|
+
})
|
|
282
|
+
}
|
|
271
283
|
});
|
|
272
284
|
const server = createServer((request, response) => {
|
|
273
285
|
handleRequest(request, response).catch((error) => {
|
|
@@ -607,6 +619,7 @@ async function serveHeadlessServer(options) {
|
|
|
607
619
|
if (session === void 0 || session.workspaceId !== workspaceId) {
|
|
608
620
|
const record = dormant().find((candidate) => candidate.sessionId === sessionId && candidate.workspaceId === workspaceId);
|
|
609
621
|
if (request.method === "DELETE" && record !== void 0 && options.removeDormantSession !== void 0) {
|
|
622
|
+
sessionMcp.closeSessionBinding(record.sessionId);
|
|
610
623
|
await options.removeDormantSession(record);
|
|
611
624
|
options.headlessHub.notifySessionRemoved(record.sessionId);
|
|
612
625
|
json(response, 200, { ok: true });
|
|
@@ -620,6 +633,7 @@ async function serveHeadlessServer(options) {
|
|
|
620
633
|
return;
|
|
621
634
|
}
|
|
622
635
|
if (suffix === "" && request.method === "DELETE") {
|
|
636
|
+
sessionMcp.closeSessionBinding(sessionId);
|
|
623
637
|
await options.headlessHub.closeSession(sessionId);
|
|
624
638
|
json(response, 200, { ok: true });
|
|
625
639
|
return;
|
|
@@ -734,6 +748,7 @@ async function serveHeadlessServer(options) {
|
|
|
734
748
|
}
|
|
735
749
|
return {
|
|
736
750
|
url: `http://${options.host ?? "127.0.0.1"}:${address.port}`,
|
|
751
|
+
sessionReservations: sessionMcp.reservations,
|
|
737
752
|
async close() {
|
|
738
753
|
if (closed) return;
|
|
739
754
|
closed = true;
|