@geminixiang/mikan 1.0.0-beta.17 → 1.0.0-beta.18

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.
@@ -1 +1 @@
1
- {"version":3,"file":"gondolin-remote.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-remote.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { request as httpsRequest } from \"node:https\";\nimport { relative, isAbsolute, join } from \"node:path\";\nimport * as log from \"../log.js\";\nimport type { GondolinRemoteWorkerSettings } from \"../types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport {\n GondolinRuntimeGoneError,\n execOverSessionConnect,\n type GondolinRuntimeHandle,\n type GondolinRuntimeSpec,\n type SessionClient,\n type SessionClientCallbacks,\n} from \"./gondolin-worker-client.js\";\nimport type { ExecResult } from \"./types.js\";\n\nconst LEASE_TTL_SECONDS = 300;\nconst REQUEST_TIMEOUT_MS = 15_000;\n/** VM boot happens inside this request; the daemon's own handshake cap is 2 min. */\nconst ENSURE_TIMEOUT_MS = 150_000;\n\ninterface RemoteLease {\n leaseId: string;\n epoch: number;\n renewTimer: NodeJS.Timeout;\n}\n\ninterface RemoteResponse {\n status: number;\n json: Record<string, unknown>;\n}\n\n/** A request that never reached the daemon (network/TLS/timeout failure). */\nexport class GondolinWorkerUnreachableError extends Error {}\n\nexport interface GondolinRemoteOverrides {\n request?: (\n method: string,\n path: string,\n body?: object,\n headers?: Record<string, string>,\n ) => Promise<RemoteResponse>;\n tunnel?: (\n path: string,\n headers: Record<string, string>,\n callbacks: SessionClientCallbacks,\n ) => SessionClient | Promise<SessionClient>;\n leaseTtlSeconds?: number;\n requestTimeoutMs?: number;\n /** Reports lease grants/renewals so the fleet can persist fencing watermarks. */\n onLeaseActivity?: (instanceId: string, expiresAtMs: number) => void;\n}\n\n/** Parses the daemon-to-client session framing: u8 type + u32be length + payload. */\nexport function createSessionFrameParser(\n callbacks: SessionClientCallbacks,\n): (chunk: Buffer) => void {\n let buffer = Buffer.alloc(0);\n return (chunk: Buffer) => {\n buffer = Buffer.concat([buffer, chunk]);\n while (buffer.length >= 5) {\n const type = buffer.readUInt8(0);\n const length = buffer.readUInt32BE(1);\n if (buffer.length < 5 + length) return;\n const payload = buffer.subarray(5, 5 + length);\n buffer = buffer.subarray(5 + length);\n if (type === 1) {\n callbacks.onBinary(Buffer.from(payload));\n continue;\n }\n try {\n callbacks.onJson(JSON.parse(payload.toString(\"utf8\")));\n } catch {\n // ignore malformed frames\n }\n }\n };\n}\n\n/** Encodes a client-to-daemon session message: u32be length + JSON. */\nexport function encodeSessionMessage(message: object): Buffer {\n const payload = Buffer.from(JSON.stringify(message));\n const header = Buffer.alloc(4);\n header.writeUInt32BE(payload.length, 0);\n return Buffer.concat([header, payload]);\n}\n\n/**\n * One mikan-worker daemon as seen from mikan: mTLS requests, per-instance\n * fenced leases with heartbeat renewal, and per-command upgraded tunnels.\n * Commands travel through one tunnel each, so abort semantics match the local\n * transport exactly.\n */\nexport class GondolinRemoteConnection {\n private readonly leases = new Map<string, RemoteLease>();\n private readonly leaseTtlSeconds: number;\n private readonly requestTimeoutMs: number;\n private tls?: { ca?: Buffer; cert: Buffer; key: Buffer };\n\n constructor(\n private readonly settings: GondolinRemoteWorkerSettings,\n private readonly overrides: GondolinRemoteOverrides = {},\n ) {\n this.leaseTtlSeconds = overrides.leaseTtlSeconds ?? LEASE_TTL_SECONDS;\n this.requestTimeoutMs = overrides.requestTimeoutMs ?? REQUEST_TIMEOUT_MS;\n }\n\n /** Drop lease timers and caches (does not touch the daemon). */\n dispose(): void {\n for (const lease of this.leases.values()) clearInterval(lease.renewTimer);\n this.leases.clear();\n }\n\n async health(): Promise<Record<string, unknown>> {\n const response = await this.request(\"GET\", \"/v1/health\");\n if (response.status !== 200) {\n throw new SandboxError(\n `Error: gondolin remote worker health check failed (${response.status})`,\n );\n }\n return response.json;\n }\n\n async ensure(instanceId: string, spec: GondolinRuntimeSpec): Promise<GondolinRuntimeHandle> {\n const body = {\n instanceId,\n imageSelector: spec.image,\n mounts: this.translateMounts(spec),\n cpus: spec.cpus ?? \"\",\n memory: spec.memory ?? \"\",\n fingerprint: spec.fingerprint,\n };\n const idempotencyKey = `${instanceId}:${spec.fingerprint}:${Date.now()}`;\n for (let attempt = 0; ; attempt += 1) {\n const lease = await this.leaseFor(instanceId);\n const response = await this.request(\n \"POST\",\n \"/v1/runtimes\",\n body,\n { ...this.leaseHeaders(lease), \"Idempotency-Key\": idempotencyKey },\n ENSURE_TIMEOUT_MS,\n );\n if (response.status === 200) {\n return {\n sessionId: response.json.sessionId as string,\n instanceId,\n socketPath: \"\",\n workerPid: (response.json.workerPid as number) ?? 0,\n fingerprint: spec.fingerprint,\n };\n }\n if ((response.status === 409 || response.status === 410) && attempt === 0) {\n this.dropLease(instanceId);\n continue;\n }\n throw new SandboxError(\n `Error: remote worker refused runtime (${response.status}): ${String(response.json.message ?? \"\")}`,\n );\n }\n }\n\n async stop(\n handle: Pick<GondolinRuntimeHandle, \"workerPid\" | \"sessionId\" | \"instanceId\">,\n ): Promise<void> {\n const lease = await this.leaseFor(handle.instanceId);\n const response = await this.request(\n \"DELETE\",\n `/v1/runtimes/${handle.sessionId}`,\n undefined,\n this.leaseHeaders(lease),\n );\n if (response.status === 409 || response.status === 410) {\n this.dropLease(handle.instanceId);\n }\n this.releaseLease(handle.instanceId);\n }\n\n async isRuntimeAlive(handle: GondolinRuntimeHandle): Promise<boolean> {\n try {\n const response = await this.request(\"GET\", `/v1/runtimes/${handle.sessionId}`);\n return response.status === 200;\n } catch {\n return false;\n }\n }\n\n /** Live runtimes hosted by this worker (fleet reconciliation). */\n async listRuntimes(): Promise<Array<{ sessionId: string; instanceId: string }>> {\n const response = await this.request(\"GET\", \"/v1/runtimes\");\n if (response.status !== 200) return [];\n const runtimes = response.json.runtimes;\n return Array.isArray(runtimes)\n ? (runtimes as Array<{ sessionId: string; instanceId: string }>)\n : [];\n }\n\n async exec(\n handle: GondolinRuntimeHandle,\n command: string,\n options: { env?: Record<string, string>; signal?: AbortSignal } = {},\n ): Promise<ExecResult> {\n const lease = this.leases.get(handle.instanceId);\n if (!lease) {\n // the lease lapsed (e.g. mikan slept); recreate the session cleanly\n throw new GondolinRuntimeGoneError(\"no live lease for this runtime\");\n }\n return execOverSessionConnect(\n (callbacks) =>\n this.openTunnel(\n handle.instanceId,\n `/v1/runtimes/${handle.sessionId}/session`,\n this.leaseHeaders(lease),\n callbacks,\n ),\n command,\n options,\n );\n }\n\n private translateMounts(spec: GondolinRuntimeSpec): Array<{ source: string; target: string }> {\n const root = this.settings.workspaceRoot;\n if (!root || !spec.workspacePath) return spec.mounts;\n const translated: Array<{ source: string; target: string }> = [];\n for (const mount of spec.mounts) {\n const suffix = relative(spec.workspacePath, mount.source);\n if (suffix.startsWith(\"..\") || isAbsolute(suffix)) {\n log.logWarning(\n `Skipping mount outside the shared workspace for remote runtime: ${mount.source}`,\n );\n continue;\n }\n translated.push({ source: join(root, suffix), target: mount.target });\n }\n return translated;\n }\n\n private async leaseFor(instanceId: string): Promise<RemoteLease> {\n const cached = this.leases.get(instanceId);\n if (cached) return cached;\n const response = await this.request(\"POST\", \"/v1/leases\", {\n instanceId,\n ttlSeconds: this.leaseTtlSeconds,\n });\n if (response.status !== 200) {\n throw new SandboxError(\n `Error: remote worker refused lease (${response.status}): ${String(response.json.message ?? \"\")}`,\n );\n }\n const lease: RemoteLease = {\n leaseId: response.json.id as string,\n epoch: response.json.epoch as number,\n renewTimer: setInterval(() => void this.renew(instanceId), (this.leaseTtlSeconds * 1000) / 3),\n };\n lease.renewTimer.unref?.();\n this.leases.set(instanceId, lease);\n this.reportLeaseActivity(instanceId);\n return lease;\n }\n\n private async renew(instanceId: string): Promise<void> {\n const lease = this.leases.get(instanceId);\n if (!lease) return;\n try {\n const response = await this.request(\"POST\", `/v1/leases/${lease.leaseId}/renew`, {\n ttlSeconds: this.leaseTtlSeconds,\n });\n if (response.status === 200) this.reportLeaseActivity(instanceId);\n else this.dropLease(instanceId);\n } catch (err) {\n log.logWarning(\n `Failed to renew remote worker lease for '${instanceId}'`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n\n private reportLeaseActivity(instanceId: string): void {\n // fencing watermark by mikan's own clock: the worker-side lease cannot\n // outlive this moment plus its ttl, no matter whose clock is skewed\n this.overrides.onLeaseActivity?.(instanceId, Date.now() + this.leaseTtlSeconds * 1000);\n }\n\n private releaseLease(instanceId: string): void {\n const lease = this.leases.get(instanceId);\n if (!lease) return;\n this.dropLease(instanceId);\n void this.request(\"DELETE\", `/v1/leases/${lease.leaseId}`).catch(() => {});\n }\n\n private dropLease(instanceId: string): void {\n const lease = this.leases.get(instanceId);\n if (!lease) return;\n clearInterval(lease.renewTimer);\n this.leases.delete(instanceId);\n }\n\n private leaseHeaders(lease: RemoteLease): Record<string, string> {\n return { \"X-Mikan-Lease\": lease.leaseId, \"X-Mikan-Epoch\": String(lease.epoch) };\n }\n\n private request(\n method: string,\n path: string,\n body?: object,\n headers?: Record<string, string>,\n timeoutMs = this.requestTimeoutMs,\n ): Promise<RemoteResponse> {\n if (this.overrides.request) return this.overrides.request(method, path, body, headers);\n const url = new URL(path, this.settings.url);\n const payload = body === undefined ? undefined : Buffer.from(JSON.stringify(body));\n return new Promise((resolve, reject) => {\n const request = httpsRequest(\n {\n method,\n host: url.hostname,\n port: url.port,\n path: url.pathname + url.search,\n signal: AbortSignal.timeout(timeoutMs),\n headers: {\n ...(payload\n ? { \"Content-Type\": \"application/json\", \"Content-Length\": payload.length }\n : {}),\n ...headers,\n },\n ...this.tlsOptions(),\n },\n (response) => {\n let raw = \"\";\n response.setEncoding(\"utf8\");\n response.on(\"data\", (chunk: string) => {\n raw += chunk;\n });\n response.on(\"end\", () => {\n let json: Record<string, unknown> = {};\n try {\n json = raw ? (JSON.parse(raw) as Record<string, unknown>) : {};\n } catch {\n // non-JSON body; status carries the outcome\n }\n resolve({ status: response.statusCode ?? 0, json });\n });\n },\n );\n request.on(\"error\", (error: Error) =>\n reject(new GondolinWorkerUnreachableError(error.message)),\n );\n if (payload) request.write(payload);\n request.end();\n });\n }\n\n private openTunnel(\n instanceId: string,\n path: string,\n headers: Record<string, string>,\n callbacks: SessionClientCallbacks,\n ): SessionClient | Promise<SessionClient> {\n if (this.overrides.tunnel) return this.overrides.tunnel(path, headers, callbacks);\n const url = new URL(path, this.settings.url);\n return new Promise((resolve, reject) => {\n const request = httpsRequest({\n method: \"GET\",\n host: url.hostname,\n port: url.port,\n path: url.pathname,\n signal: AbortSignal.timeout(this.requestTimeoutMs),\n headers: { ...headers, Connection: \"Upgrade\", Upgrade: \"gondolin-session\" },\n ...this.tlsOptions(),\n });\n request.on(\"upgrade\", (_response, socket) => {\n socket.setNoDelay(true);\n let closed = false;\n const parse = createSessionFrameParser(callbacks);\n socket.on(\"data\", parse);\n socket.on(\"error\", (error: Error) => {\n if (closed) return;\n closed = true;\n callbacks.onClose(error);\n });\n socket.on(\"close\", () => {\n if (closed) return;\n closed = true;\n callbacks.onClose();\n });\n resolve({\n send: (message) => {\n if (!closed) socket.write(encodeSessionMessage(message));\n },\n close: () => {\n if (closed) return;\n closed = true;\n socket.destroy();\n },\n });\n });\n request.on(\"response\", (response) => {\n if (response.statusCode === 409 || response.statusCode === 410) {\n this.dropLease(instanceId);\n }\n const error = new Error(\n `tunnel refused: HTTP ${response.statusCode}`,\n ) as NodeJS.ErrnoException;\n error.code = \"ECONNREFUSED\"; // nothing executed — safe to recreate and retry\n reject(error);\n });\n request.on(\"error\", (error: NodeJS.ErrnoException) => {\n error.code ??= \"ECONNREFUSED\";\n reject(error);\n });\n request.end();\n });\n }\n\n private tlsOptions(): { ca?: Buffer; cert: Buffer; key: Buffer } {\n if (this.tls) return this.tls;\n if (!this.settings.certFile || !this.settings.keyFile) {\n throw new SandboxError(\n `Error: gondolin remote worker '${this.settings.name ?? this.settings.url}' needs certFile and keyFile`,\n );\n }\n this.tls = {\n ...(this.settings.caFile ? { ca: readFileSync(this.settings.caFile) } : {}),\n cert: readFileSync(this.settings.certFile),\n key: readFileSync(this.settings.keyFile),\n };\n return this.tls;\n }\n}\n"]}
1
+ {"version":3,"file":"gondolin-remote.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-remote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,KAAK,GAAG,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EACL,wBAAwB,EACxB,sBAAsB,GAKvB,MAAM,6BAA6B,CAAC;AAGrC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAC9B,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,oFAAoF;AACpF,MAAM,iBAAiB,GAAG,OAAO,CAAC;AAalC,6EAA6E;AAC7E,MAAM,OAAO,8BAA+B,SAAQ,KAAK;CAAG;AAoB5D,qFAAqF;AACrF,MAAM,UAAU,wBAAwB,CACtC,SAAiC;IAEjC,IAAI,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,OAAO,CAAC,KAAa,EAAE,EAAE;QACvB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QACxC,OAAO,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,MAAM;gBAAE,OAAO;YACvC,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC;YAC/C,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;YACrC,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;gBACzC,SAAS;YACX,CAAC;YACD,IAAI,CAAC;gBACH,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACzD,CAAC;YAAC,MAAM,CAAC;gBACP,0BAA0B;YAC5B,CAAC;QACH,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,oBAAoB,CAAC,OAAe;IAClD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;GAKG;AACH,MAAM,OAAO,wBAAwB;IAMnC,YACmB,QAAsC,EACtC,SAAS,GAA4B,EAAE;QADvC,aAAQ,GAAR,QAAQ,CAA8B;QACtC,cAAS,GAAT,SAAS,CAA8B;QAPzC,WAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;QASvD,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC,eAAe,IAAI,iBAAiB,CAAC;QACtE,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC,gBAAgB,IAAI,kBAAkB,CAAC;IAC3E,CAAC;IAED,gEAAgE;IAChE,OAAO;QACL,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAAE,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC1E,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACzD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,IAAI,YAAY,CACpB,sDAAsD,QAAQ,CAAC,MAAM,GAAG,CACzE,CAAC;QACJ,CAAC;QACD,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,UAAkB,EAAE,IAAyB;QACxD,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG;YACX,UAAU;YACV,aAAa,EAAE,IAAI,CAAC,KAAK;YACzB,MAAM;YACN,eAAe;YACf,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;YACrB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;YACzB,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC;QACF,MAAM,cAAc,GAAG,GAAG,UAAU,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACzE,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,IAAI,CAAC,EAAE,CAAC;YACrC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC9C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CACjC,MAAM,EACN,cAAc,EACd,IAAI,EACJ,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,iBAAiB,EAAE,cAAc,EAAE,EAClE,iBAAiB,CAClB,CAAC;YACF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC5B,OAAO;oBACL,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,SAAmB;oBAC5C,UAAU;oBACV,UAAU,EAAE,EAAE;oBACd,SAAS,EAAG,QAAQ,CAAC,IAAI,CAAC,SAAoB,IAAI,CAAC;oBACnD,WAAW,EAAE,IAAI,CAAC,WAAW;iBAC9B,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;gBAC1E,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;gBAC3B,SAAS;YACX,CAAC;YACD,MAAM,IAAI,YAAY,CACpB,yCAAyC,QAAQ,CAAC,MAAM,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CACpG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CACR,MAA6E;QAE7E,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACrD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CACjC,QAAQ,EACR,gBAAgB,MAAM,CAAC,SAAS,EAAE,EAClC,SAAS,EACT,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CACzB,CAAC;QACF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,MAA6B;QAChD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,gBAAgB,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;YAC/E,OAAO,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,YAAY;QAChB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;QAC3D,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;QACxC,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;YAC5B,CAAC,CAAE,QAA6D;YAChE,CAAC,CAAC,EAAE,CAAC;IACT,CAAC;IAED,KAAK,CAAC,IAAI,CACR,MAA6B,EAC7B,OAAe,EACf,OAAO,GAA2D,EAAE;QAEpE,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,oEAAoE;YACpE,MAAM,IAAI,wBAAwB,CAAC,gCAAgC,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,sBAAsB,CAC3B,CAAC,SAAS,EAAE,EAAE,CACZ,IAAI,CAAC,UAAU,CACb,MAAM,CAAC,UAAU,EACjB,gBAAgB,MAAM,CAAC,SAAS,UAAU,EAC1C,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EACxB,SAAS,CACV,EACH,OAAO,EACP,OAAO,CACR,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACK,eAAe,CAAC,IAAyB;QAI/C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC;QACzC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACjC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC;QACtD,CAAC;QACD,MAAM,MAAM,GAA8C,EAAE,CAAC;QAC7D,MAAM,eAAe,GAAqD,EAAE,CAAC;QAC7E,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YAC1D,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACpD,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;gBAClE,SAAS;YACX,CAAC;YACD,IAAI,CAAC;gBACH,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;oBACrC,GAAG,CAAC,UAAU,CACZ,6EAA6E,KAAK,CAAC,MAAM,EAAE,CAC5F,CAAC;oBACF,SAAS;gBACX,CAAC;gBACD,eAAe,CAAC,IAAI,CAAC;oBACnB,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,aAAa,EAAE,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;iBAC7D,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,6DAA6D;YAC/D,CAAC;QACH,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IACrC,CAAC;IAEO,KAAK,CAAC,QAAQ,CAAC,UAAkB;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC3C,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAC1B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE;YACxD,UAAU;YACV,UAAU,EAAE,IAAI,CAAC,eAAe;SACjC,CAAC,CAAC;QACH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,IAAI,YAAY,CACpB,uCAAuC,QAAQ,CAAC,MAAM,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAClG,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAAgB;YACzB,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAY;YACnC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAe;YACpC,UAAU,EAAE,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;SAC9F,CAAC;QACF,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;QAC3B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC;QACrC,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,KAAK,CAAC,KAAK,CAAC,UAAkB;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,cAAc,KAAK,CAAC,OAAO,QAAQ,EAAE;gBAC/E,UAAU,EAAE,IAAI,CAAC,eAAe;aACjC,CAAC,CAAC;YACH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;gBAAE,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC;;gBAC7D,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,UAAU,CACZ,4CAA4C,UAAU,GAAG,EACzD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CACjD,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,mBAAmB,CAAC,UAAkB;QAC5C,uEAAuE;QACvE,oEAAoE;QACpE,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IACzF,CAAC;IAEO,YAAY,CAAC,UAAkB;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAC3B,KAAK,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,cAAc,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC7E,CAAC;IAEO,SAAS,CAAC,UAAkB;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACjC,CAAC;IAEO,YAAY,CAAC,KAAkB;QACrC,OAAO,EAAE,eAAe,EAAE,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;IAClF,CAAC;IAEO,OAAO,CACb,MAAc,EACd,IAAY,EACZ,IAAa,EACb,OAAgC,EAChC,SAAS,GAAG,IAAI,CAAC,gBAAgB;QAEjC,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QACvF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QACnF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,OAAO,GAAG,YAAY,CAC1B;gBACE,MAAM;gBACN,IAAI,EAAE,GAAG,CAAC,QAAQ;gBAClB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,IAAI,EAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM;gBAC/B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;gBACtC,OAAO,EAAE;oBACP,GAAG,CAAC,OAAO;wBACT,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,OAAO,CAAC,MAAM,EAAE;wBAC1E,CAAC,CAAC,EAAE,CAAC;oBACP,GAAG,OAAO;iBACX;gBACD,GAAG,IAAI,CAAC,UAAU,EAAE;aACrB,EACD,CAAC,QAAQ,EAAE,EAAE;gBACX,IAAI,GAAG,GAAG,EAAE,CAAC;gBACb,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;gBAC7B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;oBACpC,GAAG,IAAI,KAAK,CAAC;gBACf,CAAC,CAAC,CAAC;gBACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;oBACtB,IAAI,IAAI,GAA4B,EAAE,CAAC;oBACvC,IAAI,CAAC;wBACH,IAAI,GAAG,GAAG,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAA6B,CAAC,CAAC,CAAC,EAAE,CAAC;oBACjE,CAAC;oBAAC,MAAM,CAAC;wBACP,4CAA4C;oBAC9C,CAAC;oBACD,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,UAAU,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;gBACtD,CAAC,CAAC,CAAC;YACL,CAAC,CACF,CAAC;YACF,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE,CACnC,MAAM,CAAC,IAAI,8BAA8B,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAC1D,CAAC;YACF,IAAI,OAAO;gBAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACpC,OAAO,CAAC,GAAG,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,UAAU,CAChB,UAAkB,EAClB,IAAY,EACZ,OAA+B,EAC/B,SAAiC;QAEjC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;QAClF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,OAAO,GAAG,YAAY,CAAC;gBAC3B,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,GAAG,CAAC,QAAQ;gBAClB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,IAAI,EAAE,GAAG,CAAC,QAAQ;gBAClB,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC;gBAClD,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,EAAE;gBAC3E,GAAG,IAAI,CAAC,UAAU,EAAE;aACrB,CAAC,CAAC;YACH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE;gBAC1C,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;gBACxB,IAAI,MAAM,GAAG,KAAK,CAAC;gBACnB,MAAM,KAAK,GAAG,wBAAwB,CAAC,SAAS,CAAC,CAAC;gBAClD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBACzB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE;oBAClC,IAAI,MAAM;wBAAE,OAAO;oBACnB,MAAM,GAAG,IAAI,CAAC;oBACd,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC3B,CAAC,CAAC,CAAC;gBACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;oBACtB,IAAI,MAAM;wBAAE,OAAO;oBACnB,MAAM,GAAG,IAAI,CAAC;oBACd,SAAS,CAAC,OAAO,EAAE,CAAC;gBACtB,CAAC,CAAC,CAAC;gBACH,OAAO,CAAC;oBACN,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;wBAChB,IAAI,CAAC,MAAM;4BAAE,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;oBAC3D,CAAC;oBACD,KAAK,EAAE,GAAG,EAAE;wBACV,IAAI,MAAM;4BAAE,OAAO;wBACnB,MAAM,GAAG,IAAI,CAAC;wBACd,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,CAAC;iBACF,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,EAAE;gBAClC,IAAI,QAAQ,CAAC,UAAU,KAAK,GAAG,IAAI,QAAQ,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;oBAC/D,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;gBAC7B,CAAC;gBACD,MAAM,KAAK,GAAG,IAAI,KAAK,CACrB,wBAAwB,QAAQ,CAAC,UAAU,EAAE,CACrB,CAAC;gBAC3B,KAAK,CAAC,IAAI,GAAG,cAAc,CAAC,CAAC,gDAAgD;gBAC7E,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAA4B,EAAE,EAAE;gBACnD,KAAK,CAAC,IAAI,KAAK,cAAc,CAAC;gBAC9B,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,UAAU;QAChB,IAAI,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC,GAAG,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YACtD,MAAM,IAAI,YAAY,CACpB,kCAAkC,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,8BAA8B,CACxG,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,GAAG,GAAG;YACT,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3E,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC1C,GAAG,EAAE,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;SACzC,CAAC;QACF,OAAO,IAAI,CAAC,GAAG,CAAC;IAClB,CAAC;CACF","sourcesContent":["import { readFileSync, statSync } from \"node:fs\";\nimport { request as httpsRequest } from \"node:https\";\nimport { relative, isAbsolute, join } from \"node:path\";\nimport * as log from \"../log.js\";\nimport type { GondolinRemoteWorkerSettings } from \"../types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport {\n GondolinRuntimeGoneError,\n execOverSessionConnect,\n type GondolinRuntimeHandle,\n type GondolinRuntimeSpec,\n type SessionClient,\n type SessionClientCallbacks,\n} from \"./gondolin-worker-client.js\";\nimport type { ExecResult } from \"./types.js\";\n\nconst LEASE_TTL_SECONDS = 300;\nconst REQUEST_TIMEOUT_MS = 15_000;\n/** VM boot happens inside this request; the daemon's own handshake cap is 2 min. */\nconst ENSURE_TIMEOUT_MS = 150_000;\n\ninterface RemoteLease {\n leaseId: string;\n epoch: number;\n renewTimer: NodeJS.Timeout;\n}\n\ninterface RemoteResponse {\n status: number;\n json: Record<string, unknown>;\n}\n\n/** A request that never reached the daemon (network/TLS/timeout failure). */\nexport class GondolinWorkerUnreachableError extends Error {}\n\nexport interface GondolinRemoteOverrides {\n request?: (\n method: string,\n path: string,\n body?: object,\n headers?: Record<string, string>,\n ) => Promise<RemoteResponse>;\n tunnel?: (\n path: string,\n headers: Record<string, string>,\n callbacks: SessionClientCallbacks,\n ) => SessionClient | Promise<SessionClient>;\n leaseTtlSeconds?: number;\n requestTimeoutMs?: number;\n /** Reports lease grants/renewals so the fleet can persist fencing watermarks. */\n onLeaseActivity?: (instanceId: string, expiresAtMs: number) => void;\n}\n\n/** Parses the daemon-to-client session framing: u8 type + u32be length + payload. */\nexport function createSessionFrameParser(\n callbacks: SessionClientCallbacks,\n): (chunk: Buffer) => void {\n let buffer = Buffer.alloc(0);\n return (chunk: Buffer) => {\n buffer = Buffer.concat([buffer, chunk]);\n while (buffer.length >= 5) {\n const type = buffer.readUInt8(0);\n const length = buffer.readUInt32BE(1);\n if (buffer.length < 5 + length) return;\n const payload = buffer.subarray(5, 5 + length);\n buffer = buffer.subarray(5 + length);\n if (type === 1) {\n callbacks.onBinary(Buffer.from(payload));\n continue;\n }\n try {\n callbacks.onJson(JSON.parse(payload.toString(\"utf8\")));\n } catch {\n // ignore malformed frames\n }\n }\n };\n}\n\n/** Encodes a client-to-daemon session message: u32be length + JSON. */\nexport function encodeSessionMessage(message: object): Buffer {\n const payload = Buffer.from(JSON.stringify(message));\n const header = Buffer.alloc(4);\n header.writeUInt32BE(payload.length, 0);\n return Buffer.concat([header, payload]);\n}\n\n/**\n * One mikan-worker daemon as seen from mikan: mTLS requests, per-instance\n * fenced leases with heartbeat renewal, and per-command upgraded tunnels.\n * Commands travel through one tunnel each, so abort semantics match the local\n * transport exactly.\n */\nexport class GondolinRemoteConnection {\n private readonly leases = new Map<string, RemoteLease>();\n private readonly leaseTtlSeconds: number;\n private readonly requestTimeoutMs: number;\n private tls?: { ca?: Buffer; cert: Buffer; key: Buffer };\n\n constructor(\n private readonly settings: GondolinRemoteWorkerSettings,\n private readonly overrides: GondolinRemoteOverrides = {},\n ) {\n this.leaseTtlSeconds = overrides.leaseTtlSeconds ?? LEASE_TTL_SECONDS;\n this.requestTimeoutMs = overrides.requestTimeoutMs ?? REQUEST_TIMEOUT_MS;\n }\n\n /** Drop lease timers and caches (does not touch the daemon). */\n dispose(): void {\n for (const lease of this.leases.values()) clearInterval(lease.renewTimer);\n this.leases.clear();\n }\n\n async health(): Promise<Record<string, unknown>> {\n const response = await this.request(\"GET\", \"/v1/health\");\n if (response.status !== 200) {\n throw new SandboxError(\n `Error: gondolin remote worker health check failed (${response.status})`,\n );\n }\n return response.json;\n }\n\n async ensure(instanceId: string, spec: GondolinRuntimeSpec): Promise<GondolinRuntimeHandle> {\n const { mounts, credentialFiles } = this.translateMounts(spec);\n const body = {\n instanceId,\n imageSelector: spec.image,\n mounts,\n credentialFiles,\n cpus: spec.cpus ?? \"\",\n memory: spec.memory ?? \"\",\n fingerprint: spec.fingerprint,\n };\n const idempotencyKey = `${instanceId}:${spec.fingerprint}:${Date.now()}`;\n for (let attempt = 0; ; attempt += 1) {\n const lease = await this.leaseFor(instanceId);\n const response = await this.request(\n \"POST\",\n \"/v1/runtimes\",\n body,\n { ...this.leaseHeaders(lease), \"Idempotency-Key\": idempotencyKey },\n ENSURE_TIMEOUT_MS,\n );\n if (response.status === 200) {\n return {\n sessionId: response.json.sessionId as string,\n instanceId,\n socketPath: \"\",\n workerPid: (response.json.workerPid as number) ?? 0,\n fingerprint: spec.fingerprint,\n };\n }\n if ((response.status === 409 || response.status === 410) && attempt === 0) {\n this.dropLease(instanceId);\n continue;\n }\n throw new SandboxError(\n `Error: remote worker refused runtime (${response.status}): ${String(response.json.message ?? \"\")}`,\n );\n }\n }\n\n async stop(\n handle: Pick<GondolinRuntimeHandle, \"workerPid\" | \"sessionId\" | \"instanceId\">,\n ): Promise<void> {\n const lease = await this.leaseFor(handle.instanceId);\n const response = await this.request(\n \"DELETE\",\n `/v1/runtimes/${handle.sessionId}`,\n undefined,\n this.leaseHeaders(lease),\n );\n if (response.status === 409 || response.status === 410) {\n this.dropLease(handle.instanceId);\n }\n this.releaseLease(handle.instanceId);\n }\n\n async isRuntimeAlive(handle: GondolinRuntimeHandle): Promise<boolean> {\n try {\n const response = await this.request(\"GET\", `/v1/runtimes/${handle.sessionId}`);\n return response.status === 200;\n } catch {\n return false;\n }\n }\n\n /** Live runtimes hosted by this worker (fleet reconciliation). */\n async listRuntimes(): Promise<Array<{ sessionId: string; instanceId: string }>> {\n const response = await this.request(\"GET\", \"/v1/runtimes\");\n if (response.status !== 200) return [];\n const runtimes = response.json.runtimes;\n return Array.isArray(runtimes)\n ? (runtimes as Array<{ sessionId: string; instanceId: string }>)\n : [];\n }\n\n async exec(\n handle: GondolinRuntimeHandle,\n command: string,\n options: { env?: Record<string, string>; signal?: AbortSignal } = {},\n ): Promise<ExecResult> {\n const lease = this.leases.get(handle.instanceId);\n if (!lease) {\n // the lease lapsed (e.g. mikan slept); recreate the session cleanly\n throw new GondolinRuntimeGoneError(\"no live lease for this runtime\");\n }\n return execOverSessionConnect(\n (callbacks) =>\n this.openTunnel(\n handle.instanceId,\n `/v1/runtimes/${handle.sessionId}/session`,\n this.leaseHeaders(lease),\n callbacks,\n ),\n command,\n options,\n );\n }\n\n /**\n * Split mounts for the remote worker:\n * - sources under the mikan-host workspace are translated to the worker's\n * shared `workspaceRoot` (shared POSIX storage), and\n * - sources outside it that are files (vault credentials, which live in the\n * host state dir, not the shared workspace) are read and shipped as\n * content payloads for the worker to project into the guest.\n * A non-file source outside the workspace has nowhere to go and is dropped.\n */\n private translateMounts(spec: GondolinRuntimeSpec): {\n mounts: Array<{ source: string; target: string }>;\n credentialFiles: Array<{ target: string; contentBase64: string }>;\n } {\n const root = this.settings.workspaceRoot;\n if (!root || !spec.workspacePath) {\n return { mounts: spec.mounts, credentialFiles: [] };\n }\n const mounts: Array<{ source: string; target: string }> = [];\n const credentialFiles: Array<{ target: string; contentBase64: string }> = [];\n for (const mount of spec.mounts) {\n const suffix = relative(spec.workspacePath, mount.source);\n if (!suffix.startsWith(\"..\") && !isAbsolute(suffix)) {\n mounts.push({ source: join(root, suffix), target: mount.target });\n continue;\n }\n try {\n if (!statSync(mount.source).isFile()) {\n log.logWarning(\n `Skipping directory mount outside the shared workspace for remote runtime: ${mount.source}`,\n );\n continue;\n }\n credentialFiles.push({\n target: mount.target,\n contentBase64: readFileSync(mount.source).toString(\"base64\"),\n });\n } catch {\n // missing source: skip, matching the local worker's behavior\n }\n }\n return { mounts, credentialFiles };\n }\n\n private async leaseFor(instanceId: string): Promise<RemoteLease> {\n const cached = this.leases.get(instanceId);\n if (cached) return cached;\n const response = await this.request(\"POST\", \"/v1/leases\", {\n instanceId,\n ttlSeconds: this.leaseTtlSeconds,\n });\n if (response.status !== 200) {\n throw new SandboxError(\n `Error: remote worker refused lease (${response.status}): ${String(response.json.message ?? \"\")}`,\n );\n }\n const lease: RemoteLease = {\n leaseId: response.json.id as string,\n epoch: response.json.epoch as number,\n renewTimer: setInterval(() => void this.renew(instanceId), (this.leaseTtlSeconds * 1000) / 3),\n };\n lease.renewTimer.unref?.();\n this.leases.set(instanceId, lease);\n this.reportLeaseActivity(instanceId);\n return lease;\n }\n\n private async renew(instanceId: string): Promise<void> {\n const lease = this.leases.get(instanceId);\n if (!lease) return;\n try {\n const response = await this.request(\"POST\", `/v1/leases/${lease.leaseId}/renew`, {\n ttlSeconds: this.leaseTtlSeconds,\n });\n if (response.status === 200) this.reportLeaseActivity(instanceId);\n else this.dropLease(instanceId);\n } catch (err) {\n log.logWarning(\n `Failed to renew remote worker lease for '${instanceId}'`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n\n private reportLeaseActivity(instanceId: string): void {\n // fencing watermark by mikan's own clock: the worker-side lease cannot\n // outlive this moment plus its ttl, no matter whose clock is skewed\n this.overrides.onLeaseActivity?.(instanceId, Date.now() + this.leaseTtlSeconds * 1000);\n }\n\n private releaseLease(instanceId: string): void {\n const lease = this.leases.get(instanceId);\n if (!lease) return;\n this.dropLease(instanceId);\n void this.request(\"DELETE\", `/v1/leases/${lease.leaseId}`).catch(() => {});\n }\n\n private dropLease(instanceId: string): void {\n const lease = this.leases.get(instanceId);\n if (!lease) return;\n clearInterval(lease.renewTimer);\n this.leases.delete(instanceId);\n }\n\n private leaseHeaders(lease: RemoteLease): Record<string, string> {\n return { \"X-Mikan-Lease\": lease.leaseId, \"X-Mikan-Epoch\": String(lease.epoch) };\n }\n\n private request(\n method: string,\n path: string,\n body?: object,\n headers?: Record<string, string>,\n timeoutMs = this.requestTimeoutMs,\n ): Promise<RemoteResponse> {\n if (this.overrides.request) return this.overrides.request(method, path, body, headers);\n const url = new URL(path, this.settings.url);\n const payload = body === undefined ? undefined : Buffer.from(JSON.stringify(body));\n return new Promise((resolve, reject) => {\n const request = httpsRequest(\n {\n method,\n host: url.hostname,\n port: url.port,\n path: url.pathname + url.search,\n signal: AbortSignal.timeout(timeoutMs),\n headers: {\n ...(payload\n ? { \"Content-Type\": \"application/json\", \"Content-Length\": payload.length }\n : {}),\n ...headers,\n },\n ...this.tlsOptions(),\n },\n (response) => {\n let raw = \"\";\n response.setEncoding(\"utf8\");\n response.on(\"data\", (chunk: string) => {\n raw += chunk;\n });\n response.on(\"end\", () => {\n let json: Record<string, unknown> = {};\n try {\n json = raw ? (JSON.parse(raw) as Record<string, unknown>) : {};\n } catch {\n // non-JSON body; status carries the outcome\n }\n resolve({ status: response.statusCode ?? 0, json });\n });\n },\n );\n request.on(\"error\", (error: Error) =>\n reject(new GondolinWorkerUnreachableError(error.message)),\n );\n if (payload) request.write(payload);\n request.end();\n });\n }\n\n private openTunnel(\n instanceId: string,\n path: string,\n headers: Record<string, string>,\n callbacks: SessionClientCallbacks,\n ): SessionClient | Promise<SessionClient> {\n if (this.overrides.tunnel) return this.overrides.tunnel(path, headers, callbacks);\n const url = new URL(path, this.settings.url);\n return new Promise((resolve, reject) => {\n const request = httpsRequest({\n method: \"GET\",\n host: url.hostname,\n port: url.port,\n path: url.pathname,\n signal: AbortSignal.timeout(this.requestTimeoutMs),\n headers: { ...headers, Connection: \"Upgrade\", Upgrade: \"gondolin-session\" },\n ...this.tlsOptions(),\n });\n request.on(\"upgrade\", (_response, socket) => {\n socket.setNoDelay(true);\n let closed = false;\n const parse = createSessionFrameParser(callbacks);\n socket.on(\"data\", parse);\n socket.on(\"error\", (error: Error) => {\n if (closed) return;\n closed = true;\n callbacks.onClose(error);\n });\n socket.on(\"close\", () => {\n if (closed) return;\n closed = true;\n callbacks.onClose();\n });\n resolve({\n send: (message) => {\n if (!closed) socket.write(encodeSessionMessage(message));\n },\n close: () => {\n if (closed) return;\n closed = true;\n socket.destroy();\n },\n });\n });\n request.on(\"response\", (response) => {\n if (response.statusCode === 409 || response.statusCode === 410) {\n this.dropLease(instanceId);\n }\n const error = new Error(\n `tunnel refused: HTTP ${response.statusCode}`,\n ) as NodeJS.ErrnoException;\n error.code = \"ECONNREFUSED\"; // nothing executed — safe to recreate and retry\n reject(error);\n });\n request.on(\"error\", (error: NodeJS.ErrnoException) => {\n error.code ??= \"ECONNREFUSED\";\n reject(error);\n });\n request.end();\n });\n }\n\n private tlsOptions(): { ca?: Buffer; cert: Buffer; key: Buffer } {\n if (this.tls) return this.tls;\n if (!this.settings.certFile || !this.settings.keyFile) {\n throw new SandboxError(\n `Error: gondolin remote worker '${this.settings.name ?? this.settings.url}' needs certFile and keyFile`,\n );\n }\n this.tls = {\n ...(this.settings.caFile ? { ca: readFileSync(this.settings.caFile) } : {}),\n cert: readFileSync(this.settings.certFile),\n key: readFileSync(this.settings.keyFile),\n };\n return this.tls;\n }\n}\n"]}
@@ -90,6 +90,7 @@ interface GondolinWorkerClientOverrides {
90
90
  export declare function execOverSessionConnect(connect: (callbacks: SessionClientCallbacks) => SessionClient | Promise<SessionClient>, command: string, options?: {
91
91
  env?: Record<string, string>;
92
92
  signal?: AbortSignal;
93
+ idleTimeoutMs?: number;
93
94
  }): Promise<ExecResult>;
94
95
  declare class GondolinWorkerClient implements GondolinRuntimeTransport {
95
96
  private spawnProcess;
@@ -1 +1 @@
1
- {"version":3,"file":"gondolin-worker-client.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gondolin-worker-client.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker-client.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,13 @@ async function defaultConnect(socketPath, callbacks) {
23
23
  function abortError() {
24
24
  return new Error("Error: command aborted");
25
25
  }
26
+ /**
27
+ * A command may quietly work for a long time, but a session that produces no
28
+ * frame at all for this long is indistinguishable from one wedged on dead
29
+ * storage (a hung NFS mount under the workspace blocks guest I/O forever) —
30
+ * kill it and say so instead of hanging the run.
31
+ */
32
+ const EXEC_IDLE_TIMEOUT_MS = 10 * 60 * 1000;
26
33
  /**
27
34
  * Run one command through a fresh session connection — the shared state
28
35
  * machine for both transports. Closing the connection (abort, timeout, or the
@@ -31,16 +38,19 @@ function abortError() {
31
38
  export async function execOverSessionConnect(connect, command, options = {}) {
32
39
  if (options.signal?.aborted)
33
40
  throw abortError();
41
+ const idleTimeoutMs = options.idleTimeoutMs ?? EXEC_IDLE_TIMEOUT_MS;
34
42
  return await new Promise((resolve, reject) => {
35
43
  let stdout = "";
36
44
  let stderr = "";
37
45
  let received = false;
38
46
  let settled = false;
39
47
  let client;
48
+ let idleTimer;
40
49
  const settle = (fn) => {
41
50
  if (settled)
42
51
  return;
43
52
  settled = true;
53
+ clearTimeout(idleTimer);
44
54
  options.signal?.removeEventListener("abort", onAbort);
45
55
  fn();
46
56
  };
@@ -48,9 +58,23 @@ export async function execOverSessionConnect(connect, command, options = {}) {
48
58
  client?.close();
49
59
  reject(abortError());
50
60
  });
61
+ const onIdle = () => settle(() => {
62
+ client?.close();
63
+ reject(new GondolinRuntimeInterruptedError(`no session activity for ${Math.round(idleTimeoutMs / 1000)}s — killed the command ` +
64
+ `(guest I/O hangs when the workspace sits on a dead mount; check the worker's workspace health)`));
65
+ });
66
+ const armIdleTimer = () => {
67
+ if (idleTimeoutMs <= 0 || settled)
68
+ return;
69
+ clearTimeout(idleTimer);
70
+ idleTimer = setTimeout(onIdle, idleTimeoutMs);
71
+ idleTimer.unref?.();
72
+ };
73
+ armIdleTimer();
51
74
  const callbacks = {
52
75
  onJson: (message) => {
53
76
  received = true;
77
+ armIdleTimer();
54
78
  if (message.type === "exec_response" && message.id === 1) {
55
79
  settle(() => {
56
80
  client?.close();
@@ -70,6 +94,7 @@ export async function execOverSessionConnect(connect, command, options = {}) {
70
94
  },
71
95
  onBinary: (frame) => {
72
96
  received = true;
97
+ armIdleTimer();
73
98
  const tag = frame.readUInt8(0);
74
99
  const data = frame.subarray(5).toString("utf8");
75
100
  if (tag === 1)
@@ -1 +1 @@
1
- {"version":3,"file":"gondolin-worker-client.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { Readable } from \"node:stream\";\nimport { fileURLToPath } from \"node:url\";\nimport { SandboxError } from \"./errors.js\";\nimport { gondolinInventory, type GondolinRuntimeRecord } from \"./gondolin-inventory.js\";\nimport type { GondolinWorkerConfig, GondolinWorkerHandshake } from \"./gondolin-worker.js\";\nimport type { ExecResult } from \"./types.js\";\n\n/** A live worker-hosted runtime as seen from mikan. */\nexport interface GondolinRuntimeHandle {\n sessionId: string;\n instanceId: string;\n socketPath: string;\n workerPid: number;\n fingerprint: string;\n /** Fleet identity of the worker hosting the runtime (remote transport). */\n workerName?: string;\n}\n\n/**\n * What a runtime should look like, resolved by the executor layer. `image` is\n * a local asset directory for the local transport and an image selector for\n * the remote one (resolved on the worker host).\n */\nexport interface GondolinRuntimeSpec {\n image: string;\n mounts: Array<{ source: string; target: string }>;\n /** Raw fractional CPU limit (cgroup enforcement on remote Linux workers). */\n cpus?: string;\n /** Whole vCPUs given to the VM. */\n vmCpus?: number;\n memory?: string;\n fingerprint: string;\n /** mikan-host workspace root, for remote mount prefix translation. */\n workspacePath?: string;\n}\n\n/** One way of running Gondolin runtimes: in local workers or on a remote fleet. */\nexport interface GondolinRuntimeTransport {\n ensure(instanceId: string, spec: GondolinRuntimeSpec): Promise<GondolinRuntimeHandle>;\n stop(\n handle: Pick<GondolinRuntimeHandle, \"workerPid\" | \"sessionId\" | \"instanceId\" | \"workerName\">,\n ): Promise<void>;\n exec(\n handle: GondolinRuntimeHandle,\n command: string,\n options?: { env?: Record<string, string>; signal?: AbortSignal },\n ): Promise<ExecResult>;\n isRuntimeAlive(handle: GondolinRuntimeHandle): Promise<boolean>;\n}\n\n/**\n * The runtime is gone and the command never reached it (the session socket\n * refused the connection) — safe to recreate the runtime and retry.\n */\nexport class GondolinRuntimeGoneError extends Error {}\n\n/** The runtime died with the command in flight — not safe to retry blindly. */\nexport class GondolinRuntimeInterruptedError extends Error {}\n\ninterface WorkerProcessLike {\n pid?: number;\n stdout: Readable | null;\n stderr: Readable | null;\n once(event: \"close\", listener: (code: number | null) => void): unknown;\n once(event: \"error\", listener: (error: Error) => void): unknown;\n kill(signal?: NodeJS.Signals): boolean;\n unref(): void;\n}\n\nexport interface SessionClientCallbacks {\n onJson: (message: {\n type: string;\n id?: number;\n exit_code?: number | null;\n code?: string;\n message?: string;\n }) => void;\n onBinary: (frame: Buffer) => void;\n onClose: (error?: Error) => void;\n}\n\nexport interface SessionClient {\n send(message: object): void;\n close(): void;\n}\n\ninterface GondolinWorkerClientOverrides {\n spawnProcess?: (command: string, args: string[]) => WorkerProcessLike;\n connect?: (socketPath: string, callbacks: SessionClientCallbacks) => SessionClient;\n kill?: (pid: number, signal: NodeJS.Signals | 0) => void;\n handshakeTimeoutMs?: number;\n stopWaitMs?: number;\n stopPollIntervalMs?: number;\n}\n\nfunction defaultSpawnProcess(command: string, args: string[]): WorkerProcessLike {\n return spawn(command, args, { detached: true, stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n}\n\nasync function defaultConnect(\n socketPath: string,\n callbacks: SessionClientCallbacks,\n): Promise<SessionClient> {\n const { connectToSession } = await import(\"@earendil-works/gondolin\");\n return connectToSession(socketPath, callbacks);\n}\n\nfunction abortError(): Error {\n return new Error(\"Error: command aborted\");\n}\n\n/**\n * Run one command through a fresh session connection — the shared state\n * machine for both transports. Closing the connection (abort, timeout, or the\n * far side dying) kills the in-flight guest process.\n */\nexport async function execOverSessionConnect(\n connect: (callbacks: SessionClientCallbacks) => SessionClient | Promise<SessionClient>,\n command: string,\n options: { env?: Record<string, string>; signal?: AbortSignal } = {},\n): Promise<ExecResult> {\n if (options.signal?.aborted) throw abortError();\n return await new Promise<ExecResult>((resolve, reject) => {\n let stdout = \"\";\n let stderr = \"\";\n let received = false;\n let settled = false;\n let client: SessionClient | undefined;\n const settle = (fn: () => void): void => {\n if (settled) return;\n settled = true;\n options.signal?.removeEventListener(\"abort\", onAbort);\n fn();\n };\n const onAbort = (): void =>\n settle(() => {\n client?.close();\n reject(abortError());\n });\n const callbacks: SessionClientCallbacks = {\n onJson: (message) => {\n received = true;\n if (message.type === \"exec_response\" && message.id === 1) {\n settle(() => {\n client?.close();\n resolve({\n stdout,\n stderr,\n code: typeof message.exit_code === \"number\" ? message.exit_code : 1,\n });\n });\n } else if (message.type === \"error\") {\n settle(() => {\n client?.close();\n reject(new Error(`Error: Gondolin exec failed (${message.code}): ${message.message}`));\n });\n }\n },\n onBinary: (frame) => {\n received = true;\n const tag = frame.readUInt8(0);\n const data = frame.subarray(5).toString(\"utf8\");\n if (tag === 1) stdout += data;\n else stderr += data;\n },\n onClose: (error) => {\n settle(() => {\n const detail = error?.message ?? \"connection closed before the command finished\";\n const code = (error as NodeJS.ErrnoException | undefined)?.code;\n if (!received && (code === \"ENOENT\" || code === \"ECONNREFUSED\")) {\n reject(new GondolinRuntimeGoneError(detail));\n } else {\n reject(new GondolinRuntimeInterruptedError(detail));\n }\n });\n },\n };\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n Promise.resolve(connect(callbacks))\n .then((created) => {\n client = created;\n if (settled) {\n created.close();\n return;\n }\n created.send({\n type: \"exec\",\n id: 1,\n cmd: \"/bin/sh\",\n argv: [\"-c\", command],\n env: Object.entries(options.env ?? {}).map(([key, value]) => `${key}=${value}`),\n cwd: \"/workspace\",\n });\n })\n .catch((err: unknown) =>\n settle(() =>\n reject(new GondolinRuntimeGoneError(err instanceof Error ? err.message : String(err))),\n ),\n );\n });\n}\n\n/** Workers self-stop once no mikan has refreshed the heartbeat for this long. */\nconst WORKER_HEARTBEAT_STALE_MS = 45 * 60 * 1000;\n\nclass GondolinWorkerClient implements GondolinRuntimeTransport {\n private spawnProcess = defaultSpawnProcess;\n private connectImpl: (\n socketPath: string,\n callbacks: SessionClientCallbacks,\n ) => SessionClient | Promise<SessionClient> = defaultConnect;\n private killImpl: (pid: number, signal: NodeJS.Signals | 0) => void = (pid, signal) =>\n process.kill(pid, signal);\n private handshakeTimeoutMs = 120_000;\n private stopWaitMs = 15_000;\n private stopPollIntervalMs = 100;\n\n configure(overrides?: GondolinWorkerClientOverrides): void {\n this.spawnProcess = overrides?.spawnProcess ?? defaultSpawnProcess;\n this.connectImpl = overrides?.connect ?? defaultConnect;\n this.killImpl = overrides?.kill ?? ((pid, signal) => process.kill(pid, signal));\n this.handshakeTimeoutMs = overrides?.handshakeTimeoutMs ?? 120_000;\n this.stopWaitMs = overrides?.stopWaitMs ?? 15_000;\n this.stopPollIntervalMs = overrides?.stopPollIntervalMs ?? 100;\n }\n\n /** Adopt a surviving local worker or spawn a fresh one for the spec. */\n async ensure(instanceId: string, spec: GondolinRuntimeSpec): Promise<GondolinRuntimeHandle> {\n const adopted = await this.adopt(instanceId, spec.fingerprint);\n if (adopted) return adopted;\n return this.spawn({\n instanceId,\n image: spec.image,\n mounts: spec.mounts,\n cpus: spec.vmCpus,\n memory: spec.memory,\n fingerprint: spec.fingerprint,\n inventoryDir: gondolinInventory.directory() ?? join(homedir(), \".mikan\", \"gondolin-runtimes\"),\n heartbeatStaleMs: WORKER_HEARTBEAT_STALE_MS,\n });\n }\n\n /** Whether the runtime's worker process is still alive. */\n async isRuntimeAlive(handle: GondolinRuntimeHandle): Promise<boolean> {\n return this.isPidAlive(handle.workerPid);\n }\n\n /** Spawn a detached worker for the config and wait for its ready handshake. */\n async spawn(config: GondolinWorkerConfig): Promise<GondolinRuntimeHandle> {\n const entry = fileURLToPath(new URL(\"./gondolin-worker-main.js\", import.meta.url));\n const child = this.spawnProcess(process.execPath, [entry, JSON.stringify(config)]);\n const handshake = await this.readHandshake(child);\n if (!handshake.ready || !handshake.sessionId || !handshake.socketPath) {\n throw new SandboxError(\n `Error: Gondolin worker failed to start: ${handshake.error ?? \"no session in handshake\"}`,\n );\n }\n child.stdout?.destroy();\n child.stderr?.destroy();\n child.unref();\n return {\n sessionId: handshake.sessionId,\n instanceId: config.instanceId,\n socketPath: handshake.socketPath,\n workerPid: handshake.workerPid ?? (child.pid as number),\n fingerprint: config.fingerprint,\n };\n }\n\n /**\n * Adopt a runtime a previous mikan left running: newest live record for the\n * key whose fingerprint still matches and whose session answers a probe.\n * Anything stale — drifted fingerprint, unresponsive session — is stopped so\n * the caller can spawn fresh.\n */\n async adopt(instanceId: string, fingerprint: string): Promise<GondolinRuntimeHandle | undefined> {\n const record: GondolinRuntimeRecord | undefined =\n await gondolinInventory.findAdoptable(instanceId);\n if (!record?.socketPath) return undefined;\n const handle: GondolinRuntimeHandle = {\n sessionId: record.sessionId,\n instanceId,\n socketPath: record.socketPath,\n workerPid: record.ownerPid,\n fingerprint: record.fingerprint ?? \"\",\n };\n if (record.fingerprint !== fingerprint) {\n await this.stop(handle);\n return undefined;\n }\n try {\n await this.exec(handle, \"true\", { signal: AbortSignal.timeout(5000) });\n } catch {\n await this.stop(handle);\n return undefined;\n }\n return handle;\n }\n\n /**\n * Stop a worker and wait for it to clean up after itself; escalate to\n * SIGKILL and reap its leftovers if it will not die.\n */\n async stop(handle: Pick<GondolinRuntimeHandle, \"workerPid\" | \"sessionId\">): Promise<void> {\n if (this.isPidAlive(handle.workerPid)) {\n this.signal(handle.workerPid, \"SIGTERM\");\n if (!(await this.waitForExit(handle.workerPid, this.stopWaitMs))) {\n this.signal(handle.workerPid, \"SIGKILL\");\n await this.waitForExit(handle.workerPid, 2000);\n }\n }\n await gondolinInventory.reapSession(handle.sessionId);\n }\n\n /** Whether the worker process behind a handle is still alive. */\n isWorkerAlive(handle: Pick<GondolinRuntimeHandle, \"workerPid\">): boolean {\n return this.isPidAlive(handle.workerPid);\n }\n\n /**\n * Run one command over a dedicated session IPC connection. Aborting (or the\n * caller's timeout signal firing) destroys the connection, which kills the\n * in-flight guest process.\n */\n async exec(\n handle: GondolinRuntimeHandle,\n command: string,\n options: { env?: Record<string, string>; signal?: AbortSignal } = {},\n ): Promise<ExecResult> {\n return execOverSessionConnect(\n (callbacks) => this.connectImpl(handle.socketPath, callbacks),\n command,\n options,\n );\n }\n\n private signal(pid: number, signal: NodeJS.Signals): void {\n try {\n this.killImpl(pid, signal);\n } catch {\n // already gone\n }\n }\n\n private isPidAlive(pid: number): boolean {\n try {\n this.killImpl(pid, 0);\n return true;\n } catch {\n return false;\n }\n }\n\n private async waitForExit(pid: number, waitMs: number): Promise<boolean> {\n const deadline = Date.now() + waitMs;\n while (this.isPidAlive(pid)) {\n if (Date.now() >= deadline) return false;\n await new Promise((resolve) => setTimeout(resolve, this.stopPollIntervalMs));\n }\n return true;\n }\n\n private readHandshake(child: WorkerProcessLike): Promise<GondolinWorkerHandshake> {\n return new Promise((resolve, reject) => {\n let buffer = \"\";\n let stderr = \"\";\n let settled = false;\n const settle = (fn: () => void): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n fn();\n };\n const timer = setTimeout(() => {\n settle(() => {\n child.kill(\"SIGKILL\");\n reject(\n new SandboxError(\n `Error: Gondolin worker did not become ready within ${this.handshakeTimeoutMs}ms${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`,\n ),\n );\n });\n }, this.handshakeTimeoutMs);\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n buffer += chunk.toString(\"utf8\");\n const newline = buffer.indexOf(\"\\n\");\n if (newline < 0) return;\n const line = buffer.slice(0, newline);\n settle(() => {\n try {\n resolve(JSON.parse(line) as GondolinWorkerHandshake);\n } catch {\n child.kill(\"SIGKILL\");\n reject(new SandboxError(`Error: invalid Gondolin worker handshake: ${line}`));\n }\n });\n });\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString(\"utf8\");\n });\n child.once(\"close\", (code) => {\n settle(() =>\n reject(\n new SandboxError(\n `Error: Gondolin worker exited before becoming ready (code ${code})${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`,\n ),\n ),\n );\n });\n child.once(\"error\", (error) => {\n settle(() =>\n reject(new SandboxError(`Error: failed to spawn Gondolin worker: ${error.message}`)),\n );\n });\n });\n }\n}\n\nexport const gondolinWorkers = new GondolinWorkerClient();\n"]}
1
+ {"version":3,"file":"gondolin-worker-client.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { Readable } from \"node:stream\";\nimport { fileURLToPath } from \"node:url\";\nimport { SandboxError } from \"./errors.js\";\nimport { gondolinInventory, type GondolinRuntimeRecord } from \"./gondolin-inventory.js\";\nimport type { GondolinWorkerConfig, GondolinWorkerHandshake } from \"./gondolin-worker.js\";\nimport type { ExecResult } from \"./types.js\";\n\n/** A live worker-hosted runtime as seen from mikan. */\nexport interface GondolinRuntimeHandle {\n sessionId: string;\n instanceId: string;\n socketPath: string;\n workerPid: number;\n fingerprint: string;\n /** Fleet identity of the worker hosting the runtime (remote transport). */\n workerName?: string;\n}\n\n/**\n * What a runtime should look like, resolved by the executor layer. `image` is\n * a local asset directory for the local transport and an image selector for\n * the remote one (resolved on the worker host).\n */\nexport interface GondolinRuntimeSpec {\n image: string;\n mounts: Array<{ source: string; target: string }>;\n /** Raw fractional CPU limit (cgroup enforcement on remote Linux workers). */\n cpus?: string;\n /** Whole vCPUs given to the VM. */\n vmCpus?: number;\n memory?: string;\n fingerprint: string;\n /** mikan-host workspace root, for remote mount prefix translation. */\n workspacePath?: string;\n}\n\n/** One way of running Gondolin runtimes: in local workers or on a remote fleet. */\nexport interface GondolinRuntimeTransport {\n ensure(instanceId: string, spec: GondolinRuntimeSpec): Promise<GondolinRuntimeHandle>;\n stop(\n handle: Pick<GondolinRuntimeHandle, \"workerPid\" | \"sessionId\" | \"instanceId\" | \"workerName\">,\n ): Promise<void>;\n exec(\n handle: GondolinRuntimeHandle,\n command: string,\n options?: { env?: Record<string, string>; signal?: AbortSignal },\n ): Promise<ExecResult>;\n isRuntimeAlive(handle: GondolinRuntimeHandle): Promise<boolean>;\n}\n\n/**\n * The runtime is gone and the command never reached it (the session socket\n * refused the connection) — safe to recreate the runtime and retry.\n */\nexport class GondolinRuntimeGoneError extends Error {}\n\n/** The runtime died with the command in flight — not safe to retry blindly. */\nexport class GondolinRuntimeInterruptedError extends Error {}\n\ninterface WorkerProcessLike {\n pid?: number;\n stdout: Readable | null;\n stderr: Readable | null;\n once(event: \"close\", listener: (code: number | null) => void): unknown;\n once(event: \"error\", listener: (error: Error) => void): unknown;\n kill(signal?: NodeJS.Signals): boolean;\n unref(): void;\n}\n\nexport interface SessionClientCallbacks {\n onJson: (message: {\n type: string;\n id?: number;\n exit_code?: number | null;\n code?: string;\n message?: string;\n }) => void;\n onBinary: (frame: Buffer) => void;\n onClose: (error?: Error) => void;\n}\n\nexport interface SessionClient {\n send(message: object): void;\n close(): void;\n}\n\ninterface GondolinWorkerClientOverrides {\n spawnProcess?: (command: string, args: string[]) => WorkerProcessLike;\n connect?: (socketPath: string, callbacks: SessionClientCallbacks) => SessionClient;\n kill?: (pid: number, signal: NodeJS.Signals | 0) => void;\n handshakeTimeoutMs?: number;\n stopWaitMs?: number;\n stopPollIntervalMs?: number;\n}\n\nfunction defaultSpawnProcess(command: string, args: string[]): WorkerProcessLike {\n return spawn(command, args, { detached: true, stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n}\n\nasync function defaultConnect(\n socketPath: string,\n callbacks: SessionClientCallbacks,\n): Promise<SessionClient> {\n const { connectToSession } = await import(\"@earendil-works/gondolin\");\n return connectToSession(socketPath, callbacks);\n}\n\nfunction abortError(): Error {\n return new Error(\"Error: command aborted\");\n}\n\n/**\n * A command may quietly work for a long time, but a session that produces no\n * frame at all for this long is indistinguishable from one wedged on dead\n * storage (a hung NFS mount under the workspace blocks guest I/O forever) —\n * kill it and say so instead of hanging the run.\n */\nconst EXEC_IDLE_TIMEOUT_MS = 10 * 60 * 1000;\n\n/**\n * Run one command through a fresh session connection — the shared state\n * machine for both transports. Closing the connection (abort, timeout, or the\n * far side dying) kills the in-flight guest process.\n */\nexport async function execOverSessionConnect(\n connect: (callbacks: SessionClientCallbacks) => SessionClient | Promise<SessionClient>,\n command: string,\n options: { env?: Record<string, string>; signal?: AbortSignal; idleTimeoutMs?: number } = {},\n): Promise<ExecResult> {\n if (options.signal?.aborted) throw abortError();\n const idleTimeoutMs = options.idleTimeoutMs ?? EXEC_IDLE_TIMEOUT_MS;\n return await new Promise<ExecResult>((resolve, reject) => {\n let stdout = \"\";\n let stderr = \"\";\n let received = false;\n let settled = false;\n let client: SessionClient | undefined;\n let idleTimer: NodeJS.Timeout | undefined;\n const settle = (fn: () => void): void => {\n if (settled) return;\n settled = true;\n clearTimeout(idleTimer);\n options.signal?.removeEventListener(\"abort\", onAbort);\n fn();\n };\n const onAbort = (): void =>\n settle(() => {\n client?.close();\n reject(abortError());\n });\n const onIdle = (): void =>\n settle(() => {\n client?.close();\n reject(\n new GondolinRuntimeInterruptedError(\n `no session activity for ${Math.round(idleTimeoutMs / 1000)}s — killed the command ` +\n `(guest I/O hangs when the workspace sits on a dead mount; check the worker's workspace health)`,\n ),\n );\n });\n const armIdleTimer = (): void => {\n if (idleTimeoutMs <= 0 || settled) return;\n clearTimeout(idleTimer);\n idleTimer = setTimeout(onIdle, idleTimeoutMs);\n idleTimer.unref?.();\n };\n armIdleTimer();\n const callbacks: SessionClientCallbacks = {\n onJson: (message) => {\n received = true;\n armIdleTimer();\n if (message.type === \"exec_response\" && message.id === 1) {\n settle(() => {\n client?.close();\n resolve({\n stdout,\n stderr,\n code: typeof message.exit_code === \"number\" ? message.exit_code : 1,\n });\n });\n } else if (message.type === \"error\") {\n settle(() => {\n client?.close();\n reject(new Error(`Error: Gondolin exec failed (${message.code}): ${message.message}`));\n });\n }\n },\n onBinary: (frame) => {\n received = true;\n armIdleTimer();\n const tag = frame.readUInt8(0);\n const data = frame.subarray(5).toString(\"utf8\");\n if (tag === 1) stdout += data;\n else stderr += data;\n },\n onClose: (error) => {\n settle(() => {\n const detail = error?.message ?? \"connection closed before the command finished\";\n const code = (error as NodeJS.ErrnoException | undefined)?.code;\n if (!received && (code === \"ENOENT\" || code === \"ECONNREFUSED\")) {\n reject(new GondolinRuntimeGoneError(detail));\n } else {\n reject(new GondolinRuntimeInterruptedError(detail));\n }\n });\n },\n };\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n Promise.resolve(connect(callbacks))\n .then((created) => {\n client = created;\n if (settled) {\n created.close();\n return;\n }\n created.send({\n type: \"exec\",\n id: 1,\n cmd: \"/bin/sh\",\n argv: [\"-c\", command],\n env: Object.entries(options.env ?? {}).map(([key, value]) => `${key}=${value}`),\n cwd: \"/workspace\",\n });\n })\n .catch((err: unknown) =>\n settle(() =>\n reject(new GondolinRuntimeGoneError(err instanceof Error ? err.message : String(err))),\n ),\n );\n });\n}\n\n/** Workers self-stop once no mikan has refreshed the heartbeat for this long. */\nconst WORKER_HEARTBEAT_STALE_MS = 45 * 60 * 1000;\n\nclass GondolinWorkerClient implements GondolinRuntimeTransport {\n private spawnProcess = defaultSpawnProcess;\n private connectImpl: (\n socketPath: string,\n callbacks: SessionClientCallbacks,\n ) => SessionClient | Promise<SessionClient> = defaultConnect;\n private killImpl: (pid: number, signal: NodeJS.Signals | 0) => void = (pid, signal) =>\n process.kill(pid, signal);\n private handshakeTimeoutMs = 120_000;\n private stopWaitMs = 15_000;\n private stopPollIntervalMs = 100;\n\n configure(overrides?: GondolinWorkerClientOverrides): void {\n this.spawnProcess = overrides?.spawnProcess ?? defaultSpawnProcess;\n this.connectImpl = overrides?.connect ?? defaultConnect;\n this.killImpl = overrides?.kill ?? ((pid, signal) => process.kill(pid, signal));\n this.handshakeTimeoutMs = overrides?.handshakeTimeoutMs ?? 120_000;\n this.stopWaitMs = overrides?.stopWaitMs ?? 15_000;\n this.stopPollIntervalMs = overrides?.stopPollIntervalMs ?? 100;\n }\n\n /** Adopt a surviving local worker or spawn a fresh one for the spec. */\n async ensure(instanceId: string, spec: GondolinRuntimeSpec): Promise<GondolinRuntimeHandle> {\n const adopted = await this.adopt(instanceId, spec.fingerprint);\n if (adopted) return adopted;\n return this.spawn({\n instanceId,\n image: spec.image,\n mounts: spec.mounts,\n cpus: spec.vmCpus,\n memory: spec.memory,\n fingerprint: spec.fingerprint,\n inventoryDir: gondolinInventory.directory() ?? join(homedir(), \".mikan\", \"gondolin-runtimes\"),\n heartbeatStaleMs: WORKER_HEARTBEAT_STALE_MS,\n });\n }\n\n /** Whether the runtime's worker process is still alive. */\n async isRuntimeAlive(handle: GondolinRuntimeHandle): Promise<boolean> {\n return this.isPidAlive(handle.workerPid);\n }\n\n /** Spawn a detached worker for the config and wait for its ready handshake. */\n async spawn(config: GondolinWorkerConfig): Promise<GondolinRuntimeHandle> {\n const entry = fileURLToPath(new URL(\"./gondolin-worker-main.js\", import.meta.url));\n const child = this.spawnProcess(process.execPath, [entry, JSON.stringify(config)]);\n const handshake = await this.readHandshake(child);\n if (!handshake.ready || !handshake.sessionId || !handshake.socketPath) {\n throw new SandboxError(\n `Error: Gondolin worker failed to start: ${handshake.error ?? \"no session in handshake\"}`,\n );\n }\n child.stdout?.destroy();\n child.stderr?.destroy();\n child.unref();\n return {\n sessionId: handshake.sessionId,\n instanceId: config.instanceId,\n socketPath: handshake.socketPath,\n workerPid: handshake.workerPid ?? (child.pid as number),\n fingerprint: config.fingerprint,\n };\n }\n\n /**\n * Adopt a runtime a previous mikan left running: newest live record for the\n * key whose fingerprint still matches and whose session answers a probe.\n * Anything stale — drifted fingerprint, unresponsive session — is stopped so\n * the caller can spawn fresh.\n */\n async adopt(instanceId: string, fingerprint: string): Promise<GondolinRuntimeHandle | undefined> {\n const record: GondolinRuntimeRecord | undefined =\n await gondolinInventory.findAdoptable(instanceId);\n if (!record?.socketPath) return undefined;\n const handle: GondolinRuntimeHandle = {\n sessionId: record.sessionId,\n instanceId,\n socketPath: record.socketPath,\n workerPid: record.ownerPid,\n fingerprint: record.fingerprint ?? \"\",\n };\n if (record.fingerprint !== fingerprint) {\n await this.stop(handle);\n return undefined;\n }\n try {\n await this.exec(handle, \"true\", { signal: AbortSignal.timeout(5000) });\n } catch {\n await this.stop(handle);\n return undefined;\n }\n return handle;\n }\n\n /**\n * Stop a worker and wait for it to clean up after itself; escalate to\n * SIGKILL and reap its leftovers if it will not die.\n */\n async stop(handle: Pick<GondolinRuntimeHandle, \"workerPid\" | \"sessionId\">): Promise<void> {\n if (this.isPidAlive(handle.workerPid)) {\n this.signal(handle.workerPid, \"SIGTERM\");\n if (!(await this.waitForExit(handle.workerPid, this.stopWaitMs))) {\n this.signal(handle.workerPid, \"SIGKILL\");\n await this.waitForExit(handle.workerPid, 2000);\n }\n }\n await gondolinInventory.reapSession(handle.sessionId);\n }\n\n /** Whether the worker process behind a handle is still alive. */\n isWorkerAlive(handle: Pick<GondolinRuntimeHandle, \"workerPid\">): boolean {\n return this.isPidAlive(handle.workerPid);\n }\n\n /**\n * Run one command over a dedicated session IPC connection. Aborting (or the\n * caller's timeout signal firing) destroys the connection, which kills the\n * in-flight guest process.\n */\n async exec(\n handle: GondolinRuntimeHandle,\n command: string,\n options: { env?: Record<string, string>; signal?: AbortSignal } = {},\n ): Promise<ExecResult> {\n return execOverSessionConnect(\n (callbacks) => this.connectImpl(handle.socketPath, callbacks),\n command,\n options,\n );\n }\n\n private signal(pid: number, signal: NodeJS.Signals): void {\n try {\n this.killImpl(pid, signal);\n } catch {\n // already gone\n }\n }\n\n private isPidAlive(pid: number): boolean {\n try {\n this.killImpl(pid, 0);\n return true;\n } catch {\n return false;\n }\n }\n\n private async waitForExit(pid: number, waitMs: number): Promise<boolean> {\n const deadline = Date.now() + waitMs;\n while (this.isPidAlive(pid)) {\n if (Date.now() >= deadline) return false;\n await new Promise((resolve) => setTimeout(resolve, this.stopPollIntervalMs));\n }\n return true;\n }\n\n private readHandshake(child: WorkerProcessLike): Promise<GondolinWorkerHandshake> {\n return new Promise((resolve, reject) => {\n let buffer = \"\";\n let stderr = \"\";\n let settled = false;\n const settle = (fn: () => void): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n fn();\n };\n const timer = setTimeout(() => {\n settle(() => {\n child.kill(\"SIGKILL\");\n reject(\n new SandboxError(\n `Error: Gondolin worker did not become ready within ${this.handshakeTimeoutMs}ms${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`,\n ),\n );\n });\n }, this.handshakeTimeoutMs);\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n buffer += chunk.toString(\"utf8\");\n const newline = buffer.indexOf(\"\\n\");\n if (newline < 0) return;\n const line = buffer.slice(0, newline);\n settle(() => {\n try {\n resolve(JSON.parse(line) as GondolinWorkerHandshake);\n } catch {\n child.kill(\"SIGKILL\");\n reject(new SandboxError(`Error: invalid Gondolin worker handshake: ${line}`));\n }\n });\n });\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString(\"utf8\");\n });\n child.once(\"close\", (code) => {\n settle(() =>\n reject(\n new SandboxError(\n `Error: Gondolin worker exited before becoming ready (code ${code})${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`,\n ),\n ),\n );\n });\n child.once(\"error\", (error) => {\n settle(() =>\n reject(new SandboxError(`Error: failed to spawn Gondolin worker: ${error.message}`)),\n );\n });\n });\n }\n}\n\nexport const gondolinWorkers = new GondolinWorkerClient();\n"]}
@@ -15,6 +15,11 @@ declare class GondolinResourceManager implements SandboxResourceController {
15
15
  private effectiveLimits;
16
16
  }
17
17
  export declare const gondolinResources: GondolinResourceManager;
18
+ /**
19
+ * Fail fast when `gondolin:remote` is selected but no worker source is
20
+ * configured. Call after the fleet and gateway have been configured.
21
+ */
22
+ export declare function assertRemoteConfigured(): void;
18
23
  export declare function stopIdleGondolinVms(maxIdleMs: number, now?: number): Promise<void>;
19
24
  /**
20
25
  * Stop workers surviving from a previous mikan that no conversation has
@@ -1 +1 @@
1
- {"version":3,"file":"gondolin.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAejG,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,QAAQ,EACR,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACf,MAAM,YAAY,CAAC;AA8BpB,cAAM,uBAAwB,YAAW,yBAAyB;IAChE,OAAO,CAAC,aAAa,CAAC,CAAiB;IACvC,OAAO,CAAC,WAAW,CAAC,CAAiB;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqC;IAEpE,SAAS,CAAC,aAAa,CAAC,EAAE,cAAc,EAAE,WAAW,CAAC,EAAE,cAAc,GAAG,IAAI,CAK5E;IAEK,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAMpD;IAEK,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAIhF;IAED,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAE9C;IAED,gBAAgB,IAAI,cAAc,GAAG,SAAS,CAE7C;IAED,cAAc,IAAI,cAAc,GAAG,SAAS,CAE3C;IAED,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGvB;IAED,OAAO,CAAC,eAAe;CAMxB;AAED,eAAO,MAAM,iBAAiB,yBAAgC,CAAC;AA8S/D,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB5F;AAED;;;;;GAKG;AACH,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,IAAI,CAAC,CAQnE;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,IAAI,CAAC,CAcnE;AAWD,qBAAa,gBAAiB,YAAW,QAAQ;IAK7C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;IALvB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,YACmB,MAAM,EAAE,qBAAqB,EAC7B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAAA,EAO9C;IAEK,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAOtE;IAEK,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE5C;IAEK,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5D;IAED,gBAAgB,IAAI,MAAM,CAEzB;IAED,cAAc,CAAC,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,CAE5D;IAED,gBAAgB,IAAI,qBAAqB,CAExC;CACF;AAED,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,qBAAqB,CAKxE,CAAC"}
1
+ {"version":3,"file":"gondolin.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAejG,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,QAAQ,EACR,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACf,MAAM,YAAY,CAAC;AA8BpB,cAAM,uBAAwB,YAAW,yBAAyB;IAChE,OAAO,CAAC,aAAa,CAAC,CAAiB;IACvC,OAAO,CAAC,WAAW,CAAC,CAAiB;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqC;IAEpE,SAAS,CAAC,aAAa,CAAC,EAAE,cAAc,EAAE,WAAW,CAAC,EAAE,cAAc,GAAG,IAAI,CAK5E;IAEK,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAMpD;IAEK,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAIhF;IAED,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAE9C;IAED,gBAAgB,IAAI,cAAc,GAAG,SAAS,CAE7C;IAED,cAAc,IAAI,cAAc,GAAG,SAAS,CAE3C;IAED,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGvB;IAED,OAAO,CAAC,eAAe;CAMxB;AAED,eAAO,MAAM,iBAAiB,yBAAgC,CAAC;AAsC/D;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAe7C;AAwQD,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB5F;AAED;;;;;GAKG;AACH,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,IAAI,CAAC,CAQnE;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,IAAI,CAAC,CAcnE;AAWD,qBAAa,gBAAiB,YAAW,QAAQ;IAK7C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;IALvB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,YACmB,MAAM,EAAE,qBAAqB,EAC7B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAAA,EAO9C;IAEK,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAOtE;IAEK,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE5C;IAEK,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5D;IAED,gBAAgB,IAAI,MAAM,CAEzB;IAED,cAAc,CAAC,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,CAE5D;IAED,gBAAgB,IAAI,qBAAqB,CAExC;CACF;AAED,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,qBAAqB,CAKxE,CAAC"}
@@ -81,15 +81,31 @@ function assertSupportedNodeVersion() {
81
81
  }
82
82
  async function validateGondolinSandbox(config) {
83
83
  if (config?.profile === "remote") {
84
- if (!gondolinFleet.isConfigured() && !gondolinGateway.isConfigured()) {
85
- throw new SandboxError("Error: gondolin:remote requires sandbox.gondolin.remote settings (workers[]/url, or gateway for dial-home workers)");
86
- }
84
+ // The remote transports are configured from settings later in startup, so
85
+ // "is a worker source configured?" is checked there (assertRemoteConfigured),
86
+ // not here where nothing has been wired yet.
87
87
  console.log(" Gondolin microVM enabled. Profile: remote");
88
88
  return;
89
89
  }
90
90
  assertSupportedNodeVersion();
91
91
  console.log(" Gondolin microVM enabled. Profile: default");
92
92
  }
93
+ /**
94
+ * Fail fast when `gondolin:remote` is selected but no worker source is
95
+ * configured. Call after the fleet and gateway have been configured.
96
+ */
97
+ export function assertRemoteConfigured() {
98
+ if (!gondolinFleet.isConfigured() && !gondolinGateway.isConfigured()) {
99
+ throw new SandboxError("Error: gondolin:remote requires sandbox.gondolin.remote settings (workers[]/url, or gateway for dial-home workers)");
100
+ }
101
+ // Without a workspaceRoot, mount sources cannot be translated to the worker's
102
+ // filesystem and every runtime is rejected with "escapes the workspace root".
103
+ const missing = gondolinFleet.workersMissingWorkspaceRoot();
104
+ if (missing.length > 0) {
105
+ throw new SandboxError(`Error: gondolin:remote worker(s) [${missing.join(", ")}] have no workspaceRoot; ` +
106
+ `set sandbox.gondolin.remote.workspaceRoot (or gateway.workspaceRoot) to the worker-side shared workspace path`);
107
+ }
108
+ }
93
109
  function transportFor(config) {
94
110
  return config.profile === "remote" ? gondolinFleet : gondolinWorkers;
95
111
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gondolin.js","sourceRoot":"","sources":["../../src/sandbox/gondolin.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFileSync, statSync } from \"node:fs\";\nimport * as log from \"../log.js\";\nimport type { ResourceLimits, SandboxLimitStatus, SandboxResourceController } from \"../types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { gondolinInventory } from \"./gondolin-inventory.js\";\nimport {\n GondolinRuntimeGoneError,\n GondolinRuntimeInterruptedError,\n gondolinWorkers,\n type GondolinRuntimeHandle,\n type GondolinRuntimeTransport,\n} from \"./gondolin-worker-client.js\";\nimport { gondolinFleet } from \"./gondolin-fleet.js\";\nimport { gondolinGateway } from \"./gondolin-gateway.js\";\nimport { createMountedRuntimePathContext } from \"./path-context.js\";\nimport { withRuntimeBootstrap } from \"./container.js\";\nimport { execReadFile, execWriteFile } from \"./utils.js\";\nimport type {\n ExecOptions,\n ExecResult,\n Executor,\n GondolinSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\n\ntype GondolinModule = typeof import(\"@earendil-works/gondolin\");\n\ninterface GondolinSession {\n runtime: Promise<GondolinRuntimeHandle>;\n transport: GondolinRuntimeTransport;\n fingerprint: string;\n resourceKey?: string;\n activeOperations: number;\n lastUsed: number;\n idleWaiters: Array<() => void>;\n}\n\ninterface GondolinDesiredRuntime {\n image: string;\n imageIdentity: string;\n mounts: Array<{ source: string; target: string }>;\n /** Content identity of projected credential files (rotation → drift). */\n credentialIdentity: Record<string, string>;\n limits?: ResourceLimits;\n}\n\nconst MINIMUM_NODE_VERSION = [23, 6, 0] as const;\nconst MIKAN_IMAGE = \"mikan-sandbox:latest\";\nconst sessions = new Map<string, GondolinSession>();\nconst transitions = new Map<string, Promise<void>>();\nlet activeShutdowns = 0;\nlet shutdownGeneration = 0;\n\nclass GondolinResourceManager implements SandboxResourceController {\n private defaultLimits?: ResourceLimits;\n private boostLimits?: ResourceLimits;\n private readonly boostedKeys = new Set<string>();\n private readonly overrideLimits = new Map<string, ResourceLimits>();\n\n configure(defaultLimits?: ResourceLimits, boostLimits?: ResourceLimits): void {\n this.defaultLimits = defaultLimits;\n this.boostLimits = boostLimits;\n this.boostedKeys.clear();\n this.overrideLimits.clear();\n }\n\n async boost(key: string): Promise<SandboxLimitStatus> {\n if (this.boostLimits?.cpus || this.boostLimits?.memory) {\n this.overrideLimits.delete(key);\n this.boostedKeys.add(key);\n }\n return this.getLimitStatus(key);\n }\n\n async setLimits(key: string, limits: ResourceLimits): Promise<SandboxLimitStatus> {\n this.boostedKeys.delete(key);\n this.overrideLimits.set(key, { ...this.defaultLimits, ...limits });\n return this.getLimitStatus(key);\n }\n\n getLimitStatus(key: string): SandboxLimitStatus {\n return { limits: this.effectiveLimits(key), boosted: this.boostedKeys.has(key) };\n }\n\n getDefaultLimits(): ResourceLimits | undefined {\n return this.defaultLimits;\n }\n\n getBoostLimits(): ResourceLimits | undefined {\n return this.boostLimits;\n }\n\n clear(key: string): void {\n this.boostedKeys.delete(key);\n this.overrideLimits.delete(key);\n }\n\n private effectiveLimits(key: string): ResourceLimits | undefined {\n const override = this.overrideLimits.get(key);\n if (override) return override;\n if (!this.boostedKeys.has(key)) return this.defaultLimits;\n return { ...this.defaultLimits, ...this.boostLimits };\n }\n}\n\nexport const gondolinResources = new GondolinResourceManager();\n\nfunction parseGondolinSandboxArg(value: string): GondolinSandboxConfig | undefined {\n if (!value.startsWith(\"gondolin:\")) return undefined;\n\n const profile = value.slice(\"gondolin:\".length);\n if (!profile) {\n throw new SandboxError(\"Error: gondolin sandbox requires a profile (e.g., gondolin:default)\");\n }\n if (profile !== \"default\" && profile !== \"remote\") {\n throw new SandboxError(\n `Error: unsupported gondolin profile '${profile}'. Use 'gondolin:default' or 'gondolin:remote'`,\n );\n }\n return { type: \"gondolin\", profile };\n}\n\nfunction assertSupportedNodeVersion(): void {\n const [major = 0, minor = 0] = process.versions.node.split(\".\").map(Number);\n if (major < MINIMUM_NODE_VERSION[0] || (major === MINIMUM_NODE_VERSION[0] && minor < 6)) {\n throw new SandboxError(\n `Error: gondolin:default requires Node.js >=23.6.0 (current: ${process.versions.node}). Other sandbox modes remain available on Node.js >=22.19.0.`,\n );\n }\n}\n\nasync function validateGondolinSandbox(config?: GondolinSandboxConfig): Promise<void> {\n if (config?.profile === \"remote\") {\n if (!gondolinFleet.isConfigured() && !gondolinGateway.isConfigured()) {\n throw new SandboxError(\n \"Error: gondolin:remote requires sandbox.gondolin.remote settings (workers[]/url, or gateway for dial-home workers)\",\n );\n }\n console.log(\" Gondolin microVM enabled. Profile: remote\");\n return;\n }\n assertSupportedNodeVersion();\n console.log(\" Gondolin microVM enabled. Profile: default\");\n}\n\nfunction transportFor(config: GondolinSandboxConfig): GondolinRuntimeTransport {\n return config.profile === \"remote\" ? gondolinFleet : gondolinWorkers;\n}\n\nasync function resolveDesiredRuntime(\n config: GondolinSandboxConfig,\n): Promise<GondolinDesiredRuntime> {\n let image: string;\n let imageIdentity: string;\n if (config.profile === \"remote\") {\n // image assets live on the worker host; the selector is the identity\n image = gondolinFleet.imageSelector() ?? config.image ?? MIKAN_IMAGE;\n imageIdentity = image;\n } else {\n const selector = config.image ?? MIKAN_IMAGE;\n const { ensureImageSelector } = (await import(\"@earendil-works/gondolin\")) as GondolinModule;\n const resolvedImage = await ensureImageSelector(selector);\n image = resolvedImage.assetDir;\n imageIdentity = resolvedImage.buildId ?? resolvedImage.assetDir;\n }\n const mounts = (\n config.mounts ?? [{ source: config.workspacePath ?? process.cwd(), target: \"/workspace\" }]\n ).toSorted((left, right) => left.target.localeCompare(right.target));\n return {\n image,\n imageIdentity,\n mounts,\n credentialIdentity: credentialIdentity(mounts),\n limits: config.resourceKey\n ? gondolinResources.getLimitStatus(config.resourceKey).limits\n : undefined,\n };\n}\n\n/**\n * Content hashes of credential file mounts (files projected outside\n * /workspace). Rotating a credential on the host changes the fingerprint, so\n * the next command recreates the runtime with a fresh projection instead of\n * serving the stale copy for the rest of the VM's life. Workspace files are\n * excluded: the guest writes them back, which would drift the runtime it runs\n * in.\n */\nfunction credentialIdentity(\n mounts: Array<{ source: string; target: string }>,\n): Record<string, string> {\n const identity: Record<string, string> = {};\n for (const mount of mounts) {\n if (mount.target.startsWith(\"/workspace/\") || mount.target === \"/workspace\") continue;\n try {\n if (!statSync(mount.source).isFile()) continue;\n identity[mount.target] = createHash(\"sha256\")\n .update(readFileSync(mount.source))\n .digest(\"hex\");\n } catch {\n // missing source: the worker skips it too, so it has no runtime identity\n }\n }\n return identity;\n}\n\nfunction runtimeFingerprint(desired: GondolinDesiredRuntime): string {\n return createHash(\"sha256\")\n .update(\n JSON.stringify({\n image: desired.imageIdentity,\n mounts: desired.mounts,\n credentials: desired.credentialIdentity,\n limits: desired.limits,\n }),\n )\n .digest(\"hex\");\n}\n\nfunction gondolinCpuCount(cpus: string | undefined): number | undefined {\n if (cpus === undefined) return undefined;\n const parsed = Number(cpus);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n throw new SandboxError(`Error: invalid Gondolin CPU limit '${cpus}'`);\n }\n // ponytail: Gondolin exposes whole vCPUs; add host cgroup quotas if fractional enforcement matters.\n return Math.ceil(parsed);\n}\n\nfunction createSession(\n key: string,\n config: GondolinSandboxConfig,\n desired: GondolinDesiredRuntime,\n fingerprint: string,\n): GondolinSession {\n const transport = transportFor(config);\n let session: GondolinSession;\n const runtime = transport\n .ensure(key, {\n image: desired.image,\n mounts: desired.mounts,\n cpus: desired.limits?.cpus,\n vmCpus: gondolinCpuCount(desired.limits?.cpus),\n memory: desired.limits?.memory,\n fingerprint,\n workspacePath: config.workspacePath,\n })\n .catch((error) => {\n if (sessions.get(key) === session) sessions.delete(key);\n throw error;\n });\n session = {\n runtime,\n transport,\n fingerprint,\n resourceKey: config.resourceKey,\n activeOperations: 0,\n lastUsed: Date.now(),\n idleWaiters: [],\n };\n return session;\n}\n\nasync function acquireSession(\n key: string,\n config: GondolinSandboxConfig,\n generation = shutdownGeneration,\n): Promise<GondolinSession> {\n if (activeShutdowns > 0 || generation !== shutdownGeneration) {\n throw new SandboxError(\"Error: Gondolin runtime is shutting down\");\n }\n\n const pending = transitions.get(key);\n if (pending) {\n await pending;\n return acquireSession(key, config, generation);\n }\n\n const desired = await resolveDesiredRuntime(config);\n if (activeShutdowns > 0 || generation !== shutdownGeneration) {\n throw new SandboxError(\"Error: Gondolin runtime is shutting down\");\n }\n const concurrentTransition = transitions.get(key);\n if (concurrentTransition) {\n await concurrentTransition;\n return acquireSession(key, config, generation);\n }\n\n const fingerprint = runtimeFingerprint(desired);\n const existing = sessions.get(key);\n if (existing?.fingerprint === fingerprint) {\n existing.activeOperations += 1;\n return existing;\n }\n\n const transition = replaceSession(key, config, desired, fingerprint, existing);\n transitions.set(key, transition);\n try {\n await transition;\n } finally {\n if (transitions.get(key) === transition) transitions.delete(key);\n }\n return acquireSession(key, config, generation);\n}\n\nasync function replaceSession(\n key: string,\n config: GondolinSandboxConfig,\n desired: GondolinDesiredRuntime,\n fingerprint: string,\n existing: GondolinSession | undefined,\n): Promise<void> {\n if (existing) {\n await closeSession(key, existing, {\n waitForActiveOperations: true,\n resetResources: false,\n throwOnError: true,\n });\n }\n const replacement = createSession(key, config, desired, fingerprint);\n sessions.set(key, replacement);\n await replacement.runtime;\n}\n\nfunction discardDeadSession(key: string, session: GondolinSession): void {\n if (sessions.get(key) !== session) return;\n sessions.delete(key);\n log.logWarning(`Gondolin runtime for '${key}' died; the session will be recreated`);\n}\n\nasync function withRuntime<T>(\n key: string,\n config: GondolinSandboxConfig,\n operation: (handle: GondolinRuntimeHandle) => Promise<T>,\n): Promise<T> {\n for (let attempt = 0; ; attempt += 1) {\n const session = await acquireSession(key, config);\n let handle: GondolinRuntimeHandle | undefined;\n try {\n handle = await session.runtime;\n return await operation(handle);\n } catch (error) {\n const gone = error instanceof GondolinRuntimeGoneError;\n const interrupted = error instanceof GondolinRuntimeInterruptedError;\n if (gone || (interrupted && handle && !(await session.transport.isRuntimeAlive(handle)))) {\n discardDeadSession(key, session);\n }\n // Nothing reached a gone runtime, so recreating and retrying is safe;\n // an interrupted command may have side effects and must surface.\n if (gone && attempt === 0) continue;\n if (gone || interrupted) {\n throw new SandboxError(\n `Error: Gondolin runtime for '${key}' died (${(error as Error).message}); it is recreated on the next command`,\n );\n }\n throw error;\n } finally {\n session.activeOperations -= 1;\n session.lastUsed = Date.now();\n if (session.activeOperations === 0) {\n for (const resolve of session.idleWaiters.splice(0)) resolve();\n }\n }\n }\n}\n\nfunction waitForIdle(session: GondolinSession): Promise<void> {\n if (session.activeOperations === 0) return Promise.resolve();\n return new Promise((resolve) => session.idleWaiters.push(resolve));\n}\n\nasync function closeSession(\n key: string,\n session: GondolinSession,\n options: {\n waitForActiveOperations?: boolean;\n resetResources?: boolean;\n throwOnError?: boolean;\n stopRuntime?: boolean;\n } = {},\n): Promise<void> {\n if (sessions.get(key) !== session) return;\n sessions.delete(key);\n try {\n if (options.waitForActiveOperations) await waitForIdle(session);\n const handle = await session.runtime;\n if (options.stopRuntime !== false) await session.transport.stop(handle);\n if (\n options.resetResources !== false &&\n session.resourceKey &&\n !Array.from(sessions.values()).some(({ resourceKey }) => resourceKey === session.resourceKey)\n ) {\n gondolinResources.clear(session.resourceKey);\n }\n } catch (error) {\n session.fingerprint = \"\";\n // Re-track for a retried close, unless a replacement already claimed the\n // key — then the failed runtime stays for the reconcile paths and\n // clobbering the replacement would leak it too.\n if (!sessions.has(key)) sessions.set(key, session);\n if (options.throwOnError) throw error;\n log.logWarning(\n `Failed to close Gondolin session '${key}'`,\n error instanceof Error ? error.message : String(error),\n );\n }\n}\n\nexport async function stopIdleGondolinVms(maxIdleMs: number, now = Date.now()): Promise<void> {\n const idle = Array.from(sessions.entries()).filter(\n ([key, session]) =>\n !transitions.has(key) &&\n session.activeOperations === 0 &&\n now - session.lastUsed >= maxIdleMs,\n );\n await Promise.all(\n idle.map(([key, session]) => {\n // Register the close as a transition so a concurrent acquire waits for\n // it instead of racing the shared session slot.\n const transition = closeSession(key, session).finally(() => {\n if (transitions.get(key) === transition) transitions.delete(key);\n });\n transitions.set(key, transition);\n return transition;\n }),\n );\n}\n\n/**\n * Stop workers surviving from a previous mikan that no conversation has\n * adopted — without this, a runtime whose conversation went quiet before the\n * restart would idle forever (its worker only self-stops when every mikan is\n * gone, and the in-memory idle sweep only tracks acquired sessions).\n */\nexport async function sweepUnadoptedGondolinWorkers(): Promise<void> {\n for (const record of gondolinInventory.listWorkerRecords()) {\n if (sessions.has(record.instanceId) || transitions.has(record.instanceId)) continue;\n log.logInfo(\n `Stopping unadopted Gondolin worker ${record.ownerPid} (instance '${record.instanceId}')`,\n );\n await gondolinWorkers.stop({ workerPid: record.ownerPid, sessionId: record.sessionId });\n }\n}\n\n/**\n * Forget every session without stopping the workers: runtimes deliberately\n * outlive the mikan process so the next one adopts them instead of paying a\n * VM boot per conversation on every deploy.\n */\nexport async function disconnectAllGondolinRuntimes(): Promise<void> {\n shutdownGeneration += 1;\n activeShutdowns += 1;\n try {\n await Promise.allSettled(transitions.values());\n const current = Array.from(sessions.entries());\n await Promise.all(\n current.map(([key, session]) =>\n closeSession(key, session, { waitForActiveOperations: true, stopRuntime: false }),\n ),\n );\n } finally {\n activeShutdowns -= 1;\n }\n}\n\nfunction executionSignal(options?: ExecOptions): AbortSignal | undefined {\n const signals = [options?.signal];\n if (options?.timeout && options.timeout > 0) {\n signals.push(AbortSignal.timeout(options.timeout * 1000));\n }\n const active = signals.filter((signal): signal is AbortSignal => signal !== undefined);\n return active.length > 0 ? AbortSignal.any(active) : undefined;\n}\n\nexport class GondolinExecutor implements Executor {\n private readonly workspacePath: string;\n private readonly instanceId: string;\n\n constructor(\n private readonly config: GondolinSandboxConfig,\n private readonly env?: Record<string, string>,\n ) {\n // remote runtimes never load gondolin in this process, so the mikan host\n // may stay on the supported Node floor\n if (config.profile !== \"remote\") assertSupportedNodeVersion();\n this.workspacePath = config.workspacePath ?? process.cwd();\n this.instanceId = config.instanceId ?? this.workspacePath;\n }\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n return withRuntime(this.instanceId, this.config, (handle) =>\n transportFor(this.config).exec(handle, withRuntimeBootstrap(command, this.env), {\n env: this.env,\n signal: executionSignal(options),\n }),\n );\n }\n\n async readFile(path: string): Promise<string> {\n return execReadFile(this, path);\n }\n\n async writeFile(path: string, content: string): Promise<void> {\n return execWriteFile(this, path, content);\n }\n\n getWorkspacePath(): string {\n return \"/workspace\";\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return createMountedRuntimePathContext(hostWorkspaceRoot, \"/workspace\");\n }\n\n getSandboxConfig(): GondolinSandboxConfig {\n return this.config;\n }\n}\n\nexport const gondolinSandboxAdapter: SandboxAdapter<GondolinSandboxConfig> = {\n type: \"gondolin\",\n parse: parseGondolinSandboxArg,\n validate: validateGondolinSandbox,\n createExecutor: (config, env) => new GondolinExecutor(config, env),\n};\n"]}
1
+ {"version":3,"file":"gondolin.js","sourceRoot":"","sources":["../../src/sandbox/gondolin.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFileSync, statSync } from \"node:fs\";\nimport * as log from \"../log.js\";\nimport type { ResourceLimits, SandboxLimitStatus, SandboxResourceController } from \"../types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { gondolinInventory } from \"./gondolin-inventory.js\";\nimport {\n GondolinRuntimeGoneError,\n GondolinRuntimeInterruptedError,\n gondolinWorkers,\n type GondolinRuntimeHandle,\n type GondolinRuntimeTransport,\n} from \"./gondolin-worker-client.js\";\nimport { gondolinFleet } from \"./gondolin-fleet.js\";\nimport { gondolinGateway } from \"./gondolin-gateway.js\";\nimport { createMountedRuntimePathContext } from \"./path-context.js\";\nimport { withRuntimeBootstrap } from \"./container.js\";\nimport { execReadFile, execWriteFile } from \"./utils.js\";\nimport type {\n ExecOptions,\n ExecResult,\n Executor,\n GondolinSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\n\ntype GondolinModule = typeof import(\"@earendil-works/gondolin\");\n\ninterface GondolinSession {\n runtime: Promise<GondolinRuntimeHandle>;\n transport: GondolinRuntimeTransport;\n fingerprint: string;\n resourceKey?: string;\n activeOperations: number;\n lastUsed: number;\n idleWaiters: Array<() => void>;\n}\n\ninterface GondolinDesiredRuntime {\n image: string;\n imageIdentity: string;\n mounts: Array<{ source: string; target: string }>;\n /** Content identity of projected credential files (rotation → drift). */\n credentialIdentity: Record<string, string>;\n limits?: ResourceLimits;\n}\n\nconst MINIMUM_NODE_VERSION = [23, 6, 0] as const;\nconst MIKAN_IMAGE = \"mikan-sandbox:latest\";\nconst sessions = new Map<string, GondolinSession>();\nconst transitions = new Map<string, Promise<void>>();\nlet activeShutdowns = 0;\nlet shutdownGeneration = 0;\n\nclass GondolinResourceManager implements SandboxResourceController {\n private defaultLimits?: ResourceLimits;\n private boostLimits?: ResourceLimits;\n private readonly boostedKeys = new Set<string>();\n private readonly overrideLimits = new Map<string, ResourceLimits>();\n\n configure(defaultLimits?: ResourceLimits, boostLimits?: ResourceLimits): void {\n this.defaultLimits = defaultLimits;\n this.boostLimits = boostLimits;\n this.boostedKeys.clear();\n this.overrideLimits.clear();\n }\n\n async boost(key: string): Promise<SandboxLimitStatus> {\n if (this.boostLimits?.cpus || this.boostLimits?.memory) {\n this.overrideLimits.delete(key);\n this.boostedKeys.add(key);\n }\n return this.getLimitStatus(key);\n }\n\n async setLimits(key: string, limits: ResourceLimits): Promise<SandboxLimitStatus> {\n this.boostedKeys.delete(key);\n this.overrideLimits.set(key, { ...this.defaultLimits, ...limits });\n return this.getLimitStatus(key);\n }\n\n getLimitStatus(key: string): SandboxLimitStatus {\n return { limits: this.effectiveLimits(key), boosted: this.boostedKeys.has(key) };\n }\n\n getDefaultLimits(): ResourceLimits | undefined {\n return this.defaultLimits;\n }\n\n getBoostLimits(): ResourceLimits | undefined {\n return this.boostLimits;\n }\n\n clear(key: string): void {\n this.boostedKeys.delete(key);\n this.overrideLimits.delete(key);\n }\n\n private effectiveLimits(key: string): ResourceLimits | undefined {\n const override = this.overrideLimits.get(key);\n if (override) return override;\n if (!this.boostedKeys.has(key)) return this.defaultLimits;\n return { ...this.defaultLimits, ...this.boostLimits };\n }\n}\n\nexport const gondolinResources = new GondolinResourceManager();\n\nfunction parseGondolinSandboxArg(value: string): GondolinSandboxConfig | undefined {\n if (!value.startsWith(\"gondolin:\")) return undefined;\n\n const profile = value.slice(\"gondolin:\".length);\n if (!profile) {\n throw new SandboxError(\"Error: gondolin sandbox requires a profile (e.g., gondolin:default)\");\n }\n if (profile !== \"default\" && profile !== \"remote\") {\n throw new SandboxError(\n `Error: unsupported gondolin profile '${profile}'. Use 'gondolin:default' or 'gondolin:remote'`,\n );\n }\n return { type: \"gondolin\", profile };\n}\n\nfunction assertSupportedNodeVersion(): void {\n const [major = 0, minor = 0] = process.versions.node.split(\".\").map(Number);\n if (major < MINIMUM_NODE_VERSION[0] || (major === MINIMUM_NODE_VERSION[0] && minor < 6)) {\n throw new SandboxError(\n `Error: gondolin:default requires Node.js >=23.6.0 (current: ${process.versions.node}). Other sandbox modes remain available on Node.js >=22.19.0.`,\n );\n }\n}\n\nasync function validateGondolinSandbox(config?: GondolinSandboxConfig): Promise<void> {\n if (config?.profile === \"remote\") {\n // The remote transports are configured from settings later in startup, so\n // \"is a worker source configured?\" is checked there (assertRemoteConfigured),\n // not here where nothing has been wired yet.\n console.log(\" Gondolin microVM enabled. Profile: remote\");\n return;\n }\n assertSupportedNodeVersion();\n console.log(\" Gondolin microVM enabled. Profile: default\");\n}\n\n/**\n * Fail fast when `gondolin:remote` is selected but no worker source is\n * configured. Call after the fleet and gateway have been configured.\n */\nexport function assertRemoteConfigured(): void {\n if (!gondolinFleet.isConfigured() && !gondolinGateway.isConfigured()) {\n throw new SandboxError(\n \"Error: gondolin:remote requires sandbox.gondolin.remote settings (workers[]/url, or gateway for dial-home workers)\",\n );\n }\n // Without a workspaceRoot, mount sources cannot be translated to the worker's\n // filesystem and every runtime is rejected with \"escapes the workspace root\".\n const missing = gondolinFleet.workersMissingWorkspaceRoot();\n if (missing.length > 0) {\n throw new SandboxError(\n `Error: gondolin:remote worker(s) [${missing.join(\", \")}] have no workspaceRoot; ` +\n `set sandbox.gondolin.remote.workspaceRoot (or gateway.workspaceRoot) to the worker-side shared workspace path`,\n );\n }\n}\n\nfunction transportFor(config: GondolinSandboxConfig): GondolinRuntimeTransport {\n return config.profile === \"remote\" ? gondolinFleet : gondolinWorkers;\n}\n\nasync function resolveDesiredRuntime(\n config: GondolinSandboxConfig,\n): Promise<GondolinDesiredRuntime> {\n let image: string;\n let imageIdentity: string;\n if (config.profile === \"remote\") {\n // image assets live on the worker host; the selector is the identity\n image = gondolinFleet.imageSelector() ?? config.image ?? MIKAN_IMAGE;\n imageIdentity = image;\n } else {\n const selector = config.image ?? MIKAN_IMAGE;\n const { ensureImageSelector } = (await import(\"@earendil-works/gondolin\")) as GondolinModule;\n const resolvedImage = await ensureImageSelector(selector);\n image = resolvedImage.assetDir;\n imageIdentity = resolvedImage.buildId ?? resolvedImage.assetDir;\n }\n const mounts = (\n config.mounts ?? [{ source: config.workspacePath ?? process.cwd(), target: \"/workspace\" }]\n ).toSorted((left, right) => left.target.localeCompare(right.target));\n return {\n image,\n imageIdentity,\n mounts,\n credentialIdentity: credentialIdentity(mounts),\n limits: config.resourceKey\n ? gondolinResources.getLimitStatus(config.resourceKey).limits\n : undefined,\n };\n}\n\n/**\n * Content hashes of credential file mounts (files projected outside\n * /workspace). Rotating a credential on the host changes the fingerprint, so\n * the next command recreates the runtime with a fresh projection instead of\n * serving the stale copy for the rest of the VM's life. Workspace files are\n * excluded: the guest writes them back, which would drift the runtime it runs\n * in.\n */\nfunction credentialIdentity(\n mounts: Array<{ source: string; target: string }>,\n): Record<string, string> {\n const identity: Record<string, string> = {};\n for (const mount of mounts) {\n if (mount.target.startsWith(\"/workspace/\") || mount.target === \"/workspace\") continue;\n try {\n if (!statSync(mount.source).isFile()) continue;\n identity[mount.target] = createHash(\"sha256\")\n .update(readFileSync(mount.source))\n .digest(\"hex\");\n } catch {\n // missing source: the worker skips it too, so it has no runtime identity\n }\n }\n return identity;\n}\n\nfunction runtimeFingerprint(desired: GondolinDesiredRuntime): string {\n return createHash(\"sha256\")\n .update(\n JSON.stringify({\n image: desired.imageIdentity,\n mounts: desired.mounts,\n credentials: desired.credentialIdentity,\n limits: desired.limits,\n }),\n )\n .digest(\"hex\");\n}\n\nfunction gondolinCpuCount(cpus: string | undefined): number | undefined {\n if (cpus === undefined) return undefined;\n const parsed = Number(cpus);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n throw new SandboxError(`Error: invalid Gondolin CPU limit '${cpus}'`);\n }\n // ponytail: Gondolin exposes whole vCPUs; add host cgroup quotas if fractional enforcement matters.\n return Math.ceil(parsed);\n}\n\nfunction createSession(\n key: string,\n config: GondolinSandboxConfig,\n desired: GondolinDesiredRuntime,\n fingerprint: string,\n): GondolinSession {\n const transport = transportFor(config);\n let session: GondolinSession;\n const runtime = transport\n .ensure(key, {\n image: desired.image,\n mounts: desired.mounts,\n cpus: desired.limits?.cpus,\n vmCpus: gondolinCpuCount(desired.limits?.cpus),\n memory: desired.limits?.memory,\n fingerprint,\n workspacePath: config.workspacePath,\n })\n .catch((error) => {\n if (sessions.get(key) === session) sessions.delete(key);\n throw error;\n });\n session = {\n runtime,\n transport,\n fingerprint,\n resourceKey: config.resourceKey,\n activeOperations: 0,\n lastUsed: Date.now(),\n idleWaiters: [],\n };\n return session;\n}\n\nasync function acquireSession(\n key: string,\n config: GondolinSandboxConfig,\n generation = shutdownGeneration,\n): Promise<GondolinSession> {\n if (activeShutdowns > 0 || generation !== shutdownGeneration) {\n throw new SandboxError(\"Error: Gondolin runtime is shutting down\");\n }\n\n const pending = transitions.get(key);\n if (pending) {\n await pending;\n return acquireSession(key, config, generation);\n }\n\n const desired = await resolveDesiredRuntime(config);\n if (activeShutdowns > 0 || generation !== shutdownGeneration) {\n throw new SandboxError(\"Error: Gondolin runtime is shutting down\");\n }\n const concurrentTransition = transitions.get(key);\n if (concurrentTransition) {\n await concurrentTransition;\n return acquireSession(key, config, generation);\n }\n\n const fingerprint = runtimeFingerprint(desired);\n const existing = sessions.get(key);\n if (existing?.fingerprint === fingerprint) {\n existing.activeOperations += 1;\n return existing;\n }\n\n const transition = replaceSession(key, config, desired, fingerprint, existing);\n transitions.set(key, transition);\n try {\n await transition;\n } finally {\n if (transitions.get(key) === transition) transitions.delete(key);\n }\n return acquireSession(key, config, generation);\n}\n\nasync function replaceSession(\n key: string,\n config: GondolinSandboxConfig,\n desired: GondolinDesiredRuntime,\n fingerprint: string,\n existing: GondolinSession | undefined,\n): Promise<void> {\n if (existing) {\n await closeSession(key, existing, {\n waitForActiveOperations: true,\n resetResources: false,\n throwOnError: true,\n });\n }\n const replacement = createSession(key, config, desired, fingerprint);\n sessions.set(key, replacement);\n await replacement.runtime;\n}\n\nfunction discardDeadSession(key: string, session: GondolinSession): void {\n if (sessions.get(key) !== session) return;\n sessions.delete(key);\n log.logWarning(`Gondolin runtime for '${key}' died; the session will be recreated`);\n}\n\nasync function withRuntime<T>(\n key: string,\n config: GondolinSandboxConfig,\n operation: (handle: GondolinRuntimeHandle) => Promise<T>,\n): Promise<T> {\n for (let attempt = 0; ; attempt += 1) {\n const session = await acquireSession(key, config);\n let handle: GondolinRuntimeHandle | undefined;\n try {\n handle = await session.runtime;\n return await operation(handle);\n } catch (error) {\n const gone = error instanceof GondolinRuntimeGoneError;\n const interrupted = error instanceof GondolinRuntimeInterruptedError;\n if (gone || (interrupted && handle && !(await session.transport.isRuntimeAlive(handle)))) {\n discardDeadSession(key, session);\n }\n // Nothing reached a gone runtime, so recreating and retrying is safe;\n // an interrupted command may have side effects and must surface.\n if (gone && attempt === 0) continue;\n if (gone || interrupted) {\n throw new SandboxError(\n `Error: Gondolin runtime for '${key}' died (${(error as Error).message}); it is recreated on the next command`,\n );\n }\n throw error;\n } finally {\n session.activeOperations -= 1;\n session.lastUsed = Date.now();\n if (session.activeOperations === 0) {\n for (const resolve of session.idleWaiters.splice(0)) resolve();\n }\n }\n }\n}\n\nfunction waitForIdle(session: GondolinSession): Promise<void> {\n if (session.activeOperations === 0) return Promise.resolve();\n return new Promise((resolve) => session.idleWaiters.push(resolve));\n}\n\nasync function closeSession(\n key: string,\n session: GondolinSession,\n options: {\n waitForActiveOperations?: boolean;\n resetResources?: boolean;\n throwOnError?: boolean;\n stopRuntime?: boolean;\n } = {},\n): Promise<void> {\n if (sessions.get(key) !== session) return;\n sessions.delete(key);\n try {\n if (options.waitForActiveOperations) await waitForIdle(session);\n const handle = await session.runtime;\n if (options.stopRuntime !== false) await session.transport.stop(handle);\n if (\n options.resetResources !== false &&\n session.resourceKey &&\n !Array.from(sessions.values()).some(({ resourceKey }) => resourceKey === session.resourceKey)\n ) {\n gondolinResources.clear(session.resourceKey);\n }\n } catch (error) {\n session.fingerprint = \"\";\n // Re-track for a retried close, unless a replacement already claimed the\n // key — then the failed runtime stays for the reconcile paths and\n // clobbering the replacement would leak it too.\n if (!sessions.has(key)) sessions.set(key, session);\n if (options.throwOnError) throw error;\n log.logWarning(\n `Failed to close Gondolin session '${key}'`,\n error instanceof Error ? error.message : String(error),\n );\n }\n}\n\nexport async function stopIdleGondolinVms(maxIdleMs: number, now = Date.now()): Promise<void> {\n const idle = Array.from(sessions.entries()).filter(\n ([key, session]) =>\n !transitions.has(key) &&\n session.activeOperations === 0 &&\n now - session.lastUsed >= maxIdleMs,\n );\n await Promise.all(\n idle.map(([key, session]) => {\n // Register the close as a transition so a concurrent acquire waits for\n // it instead of racing the shared session slot.\n const transition = closeSession(key, session).finally(() => {\n if (transitions.get(key) === transition) transitions.delete(key);\n });\n transitions.set(key, transition);\n return transition;\n }),\n );\n}\n\n/**\n * Stop workers surviving from a previous mikan that no conversation has\n * adopted — without this, a runtime whose conversation went quiet before the\n * restart would idle forever (its worker only self-stops when every mikan is\n * gone, and the in-memory idle sweep only tracks acquired sessions).\n */\nexport async function sweepUnadoptedGondolinWorkers(): Promise<void> {\n for (const record of gondolinInventory.listWorkerRecords()) {\n if (sessions.has(record.instanceId) || transitions.has(record.instanceId)) continue;\n log.logInfo(\n `Stopping unadopted Gondolin worker ${record.ownerPid} (instance '${record.instanceId}')`,\n );\n await gondolinWorkers.stop({ workerPid: record.ownerPid, sessionId: record.sessionId });\n }\n}\n\n/**\n * Forget every session without stopping the workers: runtimes deliberately\n * outlive the mikan process so the next one adopts them instead of paying a\n * VM boot per conversation on every deploy.\n */\nexport async function disconnectAllGondolinRuntimes(): Promise<void> {\n shutdownGeneration += 1;\n activeShutdowns += 1;\n try {\n await Promise.allSettled(transitions.values());\n const current = Array.from(sessions.entries());\n await Promise.all(\n current.map(([key, session]) =>\n closeSession(key, session, { waitForActiveOperations: true, stopRuntime: false }),\n ),\n );\n } finally {\n activeShutdowns -= 1;\n }\n}\n\nfunction executionSignal(options?: ExecOptions): AbortSignal | undefined {\n const signals = [options?.signal];\n if (options?.timeout && options.timeout > 0) {\n signals.push(AbortSignal.timeout(options.timeout * 1000));\n }\n const active = signals.filter((signal): signal is AbortSignal => signal !== undefined);\n return active.length > 0 ? AbortSignal.any(active) : undefined;\n}\n\nexport class GondolinExecutor implements Executor {\n private readonly workspacePath: string;\n private readonly instanceId: string;\n\n constructor(\n private readonly config: GondolinSandboxConfig,\n private readonly env?: Record<string, string>,\n ) {\n // remote runtimes never load gondolin in this process, so the mikan host\n // may stay on the supported Node floor\n if (config.profile !== \"remote\") assertSupportedNodeVersion();\n this.workspacePath = config.workspacePath ?? process.cwd();\n this.instanceId = config.instanceId ?? this.workspacePath;\n }\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n return withRuntime(this.instanceId, this.config, (handle) =>\n transportFor(this.config).exec(handle, withRuntimeBootstrap(command, this.env), {\n env: this.env,\n signal: executionSignal(options),\n }),\n );\n }\n\n async readFile(path: string): Promise<string> {\n return execReadFile(this, path);\n }\n\n async writeFile(path: string, content: string): Promise<void> {\n return execWriteFile(this, path, content);\n }\n\n getWorkspacePath(): string {\n return \"/workspace\";\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return createMountedRuntimePathContext(hostWorkspaceRoot, \"/workspace\");\n }\n\n getSandboxConfig(): GondolinSandboxConfig {\n return this.config;\n }\n}\n\nexport const gondolinSandboxAdapter: SandboxAdapter<GondolinSandboxConfig> = {\n type: \"gondolin\",\n parse: parseGondolinSandboxArg,\n validate: validateGondolinSandbox,\n createExecutor: (config, env) => new GondolinExecutor(config, env),\n};\n"]}
package/dist/types.d.ts CHANGED
@@ -257,7 +257,12 @@ export interface GondolinGatewaySettings {
257
257
  clientCaFile?: string;
258
258
  /** SANs for the auto-issued server certificate (defaults to os.hostname). */
259
259
  hostnames?: string[];
260
- /** Worker-side path of the shared workspace filesystem. */
260
+ /**
261
+ * Worker-side path of the shared workspace filesystem. Required in practice:
262
+ * mount sources are translated to it, and without it every runtime is
263
+ * rejected with "escapes the workspace root". gondolinGateway.start()
264
+ * enforces it at boot; typed optional to match the settings schema.
265
+ */
261
266
  workspaceRoot?: string;
262
267
  /** Per-worker host-side overrides, keyed by worker name. */
263
268
  workers?: Record<string, {