@geminixiang/mikan 1.0.0-beta.20 → 1.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +18 -11
- package/dist/adapters/buffered-responder.d.ts +60 -0
- package/dist/adapters/buffered-responder.d.ts.map +1 -0
- package/dist/adapters/buffered-responder.js +201 -0
- package/dist/adapters/buffered-responder.js.map +1 -0
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +42 -177
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/github/bot.d.ts +14 -76
- package/dist/adapters/github/bot.d.ts.map +1 -1
- package/dist/adapters/github/bot.js +52 -443
- package/dist/adapters/github/bot.js.map +1 -1
- package/dist/adapters/github/client.d.ts +4 -0
- package/dist/adapters/github/client.d.ts.map +1 -1
- package/dist/adapters/github/client.js +5 -0
- package/dist/adapters/github/client.js.map +1 -1
- package/dist/adapters/github/context.d.ts.map +1 -1
- package/dist/adapters/github/context.js +32 -104
- package/dist/adapters/github/context.js.map +1 -1
- package/dist/adapters/github/github-ops.d.ts +90 -0
- package/dist/adapters/github/github-ops.d.ts.map +1 -0
- package/dist/adapters/github/github-ops.js +405 -0
- package/dist/adapters/github/github-ops.js.map +1 -0
- package/dist/adapters/github/repo.d.ts +2 -0
- package/dist/adapters/github/repo.d.ts.map +1 -1
- package/dist/adapters/github/repo.js +5 -0
- package/dist/adapters/github/repo.js.map +1 -1
- package/dist/adapters/github/tools/checks.d.ts.map +1 -1
- package/dist/adapters/github/tools/checks.js +6 -17
- package/dist/adapters/github/tools/checks.js.map +1 -1
- package/dist/adapters/github/tools/issue.d.ts.map +1 -1
- package/dist/adapters/github/tools/issue.js +6 -17
- package/dist/adapters/github/tools/issue.js.map +1 -1
- package/dist/adapters/github/tools/pr.d.ts.map +1 -1
- package/dist/adapters/github/tools/pr.js +6 -17
- package/dist/adapters/github/tools/pr.js.map +1 -1
- package/dist/adapters/github/tools/read.d.ts.map +1 -1
- package/dist/adapters/github/tools/read.js +6 -17
- package/dist/adapters/github/tools/read.js.map +1 -1
- package/dist/adapters/github/tools/review-reply.d.ts.map +1 -1
- package/dist/adapters/github/tools/review-reply.js +6 -17
- package/dist/adapters/github/tools/review-reply.js.map +1 -1
- package/dist/adapters/github/tools/sync.d.ts.map +1 -1
- package/dist/adapters/github/tools/sync.js +6 -17
- package/dist/adapters/github/tools/sync.js.map +1 -1
- package/dist/adapters/slack/bot.js +1 -1
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +33 -151
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/agent.d.ts +0 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +28 -32
- package/dist/agent.js.map +1 -1
- package/dist/cli/arg-grammar.d.ts +25 -0
- package/dist/cli/arg-grammar.d.ts.map +1 -0
- package/dist/cli/arg-grammar.js +59 -0
- package/dist/cli/arg-grammar.js.map +1 -0
- package/dist/cli/boot.d.ts +16 -0
- package/dist/cli/boot.d.ts.map +1 -0
- package/dist/cli/boot.js +119 -0
- package/dist/cli/boot.js.map +1 -0
- package/dist/cli/ext.js +9 -11
- package/dist/cli/ext.js.map +1 -1
- package/dist/commands/extensions.js +2 -2
- package/dist/commands/extensions.js.map +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +5 -2
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/model.d.ts.map +1 -1
- package/dist/commands/model.js +8 -8
- package/dist/commands/model.js.map +1 -1
- package/dist/commands/sandbox.d.ts.map +1 -1
- package/dist/commands/sandbox.js +7 -5
- package/dist/commands/sandbox.js.map +1 -1
- package/dist/config.d.ts +0 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -41
- package/dist/config.js.map +1 -1
- package/dist/env-manifest.d.ts +35 -0
- package/dist/env-manifest.d.ts.map +1 -0
- package/dist/env-manifest.js +225 -0
- package/dist/env-manifest.js.map +1 -0
- package/dist/events.d.ts +0 -10
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +9 -82
- package/dist/events.js.map +1 -1
- package/dist/execution-resolver.d.ts +1 -0
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +33 -36
- package/dist/execution-resolver.js.map +1 -1
- package/dist/harness/event-format.d.ts +66 -0
- package/dist/harness/event-format.d.ts.map +1 -0
- package/dist/harness/event-format.js +112 -0
- package/dist/harness/event-format.js.map +1 -0
- package/dist/harness/extensions/loader.d.ts +9 -6
- package/dist/harness/extensions/loader.d.ts.map +1 -1
- package/dist/harness/extensions/loader.js +35 -50
- package/dist/harness/extensions/loader.js.map +1 -1
- package/dist/harness/extensions/types.d.ts +10 -14
- package/dist/harness/extensions/types.d.ts.map +1 -1
- package/dist/harness/extensions/types.js.map +1 -1
- package/dist/harness/index.d.ts +1 -0
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +1 -0
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/runner.d.ts +16 -0
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +27 -1
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/subagent-runner.d.ts +18 -1
- package/dist/harness/subagent-runner.d.ts.map +1 -1
- package/dist/harness/subagent-runner.js +62 -30
- package/dist/harness/subagent-runner.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/main.js +69 -121
- package/dist/main.js.map +1 -1
- package/dist/observability/instrument.js +6 -4
- package/dist/observability/instrument.js.map +1 -1
- package/dist/observability/sentry.d.ts.map +1 -1
- package/dist/observability/sentry.js +3 -2
- package/dist/observability/sentry.js.map +1 -1
- package/dist/runtime/conversation-runtime.js +13 -3
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +4 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sandbox/cloudflare.d.ts +1 -0
- package/dist/sandbox/cloudflare.d.ts.map +1 -1
- package/dist/sandbox/cloudflare.js +4 -1
- package/dist/sandbox/cloudflare.js.map +1 -1
- package/dist/sandbox/container.d.ts +1 -0
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +4 -1
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sandbox/firecracker.d.ts +1 -0
- package/dist/sandbox/firecracker.d.ts.map +1 -1
- package/dist/sandbox/firecracker.js +4 -1
- package/dist/sandbox/firecracker.js.map +1 -1
- package/dist/sandbox/gondolin-bootstrap.d.ts +26 -0
- package/dist/sandbox/gondolin-bootstrap.d.ts.map +1 -0
- package/dist/sandbox/gondolin-bootstrap.js +27 -0
- package/dist/sandbox/gondolin-bootstrap.js.map +1 -0
- package/dist/sandbox/gondolin-contract.d.ts +125 -0
- package/dist/sandbox/gondolin-contract.d.ts.map +1 -0
- package/dist/sandbox/gondolin-contract.js +155 -0
- package/dist/sandbox/gondolin-contract.js.map +1 -0
- package/dist/sandbox/gondolin-fleet.d.ts +1 -1
- package/dist/sandbox/gondolin-fleet.d.ts.map +1 -1
- package/dist/sandbox/gondolin-fleet.js +1 -1
- package/dist/sandbox/gondolin-fleet.js.map +1 -1
- package/dist/sandbox/gondolin-gateway.d.ts +1 -0
- package/dist/sandbox/gondolin-gateway.d.ts.map +1 -1
- package/dist/sandbox/gondolin-gateway.js +13 -6
- package/dist/sandbox/gondolin-gateway.js.map +1 -1
- package/dist/sandbox/gondolin-inventory.d.ts +2 -16
- package/dist/sandbox/gondolin-inventory.d.ts.map +1 -1
- package/dist/sandbox/gondolin-inventory.js +2 -12
- package/dist/sandbox/gondolin-inventory.js.map +1 -1
- package/dist/sandbox/gondolin-placement.d.ts +0 -2
- package/dist/sandbox/gondolin-placement.d.ts.map +1 -1
- package/dist/sandbox/gondolin-placement.js +0 -6
- package/dist/sandbox/gondolin-placement.js.map +1 -1
- package/dist/sandbox/gondolin-recovery.d.ts +33 -0
- package/dist/sandbox/gondolin-recovery.d.ts.map +1 -0
- package/dist/sandbox/gondolin-recovery.js +39 -0
- package/dist/sandbox/gondolin-recovery.js.map +1 -0
- package/dist/sandbox/gondolin-remote.d.ts +2 -5
- package/dist/sandbox/gondolin-remote.d.ts.map +1 -1
- package/dist/sandbox/gondolin-remote.js +12 -39
- package/dist/sandbox/gondolin-remote.js.map +1 -1
- package/dist/sandbox/gondolin-worker-client.d.ts +8 -12
- package/dist/sandbox/gondolin-worker-client.d.ts.map +1 -1
- package/dist/sandbox/gondolin-worker-client.js +13 -15
- package/dist/sandbox/gondolin-worker-client.js.map +1 -1
- package/dist/sandbox/gondolin-worker.d.ts +2 -40
- package/dist/sandbox/gondolin-worker.d.ts.map +1 -1
- package/dist/sandbox/gondolin-worker.js.map +1 -1
- package/dist/sandbox/gondolin.d.ts +1 -0
- package/dist/sandbox/gondolin.d.ts.map +1 -1
- package/dist/sandbox/gondolin.js +8 -4
- package/dist/sandbox/gondolin.js.map +1 -1
- package/dist/sandbox/host.d.ts +1 -0
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +3 -0
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sandbox/index.d.ts +9 -1
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +14 -0
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/types.d.ts +5 -0
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sandbox/types.js.map +1 -1
- package/dist/sandbox/utils.d.ts +1 -0
- package/dist/sandbox/utils.d.ts.map +1 -1
- package/dist/sandbox/utils.js +5 -2
- package/dist/sandbox/utils.js.map +1 -1
- package/dist/sessions/chat-history-sync.d.ts +32 -0
- package/dist/sessions/chat-history-sync.d.ts.map +1 -0
- package/dist/sessions/{agent-memory-file-manager.js → chat-history-sync.js} +24 -83
- package/dist/sessions/chat-history-sync.js.map +1 -0
- package/dist/sessions/conversation-log.d.ts +13 -0
- package/dist/sessions/conversation-log.d.ts.map +1 -0
- package/dist/sessions/conversation-log.js +47 -0
- package/dist/sessions/conversation-log.js.map +1 -0
- package/dist/sessions/rotation.d.ts +2 -0
- package/dist/sessions/rotation.d.ts.map +1 -0
- package/dist/sessions/rotation.js +39 -0
- package/dist/sessions/rotation.js.map +1 -0
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +4 -10
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/types.d.ts +2 -2
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/sessions/types.js.map +1 -1
- package/dist/settings-mutation.d.ts +23 -0
- package/dist/settings-mutation.d.ts.map +1 -0
- package/dist/settings-mutation.js +42 -0
- package/dist/settings-mutation.js.map +1 -0
- package/dist/tools/event.d.ts.map +1 -1
- package/dist/tools/event.js +27 -46
- package/dist/tools/event.js.map +1 -1
- package/dist/tools/host-fn-tool.d.ts +21 -0
- package/dist/tools/host-fn-tool.d.ts.map +1 -0
- package/dist/tools/host-fn-tool.js +32 -0
- package/dist/tools/host-fn-tool.js.map +1 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/react.d.ts.map +1 -1
- package/dist/tools/react.js +6 -17
- package/dist/tools/react.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +7 -26
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/sandbox.d.ts.map +1 -1
- package/dist/tools/sandbox.js +5 -2
- package/dist/tools/sandbox.js.map +1 -1
- package/dist/tools/subagent-slots.d.ts +28 -0
- package/dist/tools/subagent-slots.d.ts.map +1 -0
- package/dist/tools/subagent-slots.js +57 -0
- package/dist/tools/subagent-slots.js.map +1 -0
- package/dist/tools/subagent.d.ts +7 -2
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +160 -111
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/types.d.ts +10 -9
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js.map +1 -1
- package/dist/types.d.ts +5 -24
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/date.d.ts +1 -1
- package/dist/utils/date.js +1 -1
- package/dist/utils/date.js.map +1 -1
- package/dist/vault/disabled.d.ts.map +1 -1
- package/dist/vault/disabled.js +0 -1
- package/dist/vault/disabled.js.map +1 -1
- package/dist/vault/index.d.ts +0 -2
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +0 -10
- package/dist/vault/index.js.map +1 -1
- package/dist/vault/injection.d.ts +20 -0
- package/dist/vault/injection.d.ts.map +1 -0
- package/dist/vault/injection.js +27 -0
- package/dist/vault/injection.js.map +1 -0
- package/dist/vault/types.d.ts +0 -2
- package/dist/vault/types.d.ts.map +1 -1
- package/dist/vault/types.js.map +1 -1
- package/dist/web/admin/portal.d.ts +3 -3
- package/dist/web/admin/portal.d.ts.map +1 -1
- package/dist/web/admin/portal.js +41 -39
- package/dist/web/admin/portal.js.map +1 -1
- package/dist/web/admin/types.d.ts +3 -0
- package/dist/web/admin/types.d.ts.map +1 -1
- package/dist/web/admin/types.js.map +1 -1
- package/dist/web/agent-events/portal.d.ts.map +1 -1
- package/dist/web/agent-events/portal.js +2 -1
- package/dist/web/agent-events/portal.js.map +1 -1
- package/package.json +1 -1
- package/dist/sessions/agent-memory-file-manager.d.ts +0 -19
- package/dist/sessions/agent-memory-file-manager.d.ts.map +0 -1
- package/dist/sessions/agent-memory-file-manager.js.map +0 -1
- package/dist/vault/routing.d.ts +0 -4
- package/dist/vault/routing.d.ts.map +0 -1
- package/dist/vault/routing.js +0 -6
- package/dist/vault/routing.js.map +0 -1
|
@@ -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, 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"]}
|
|
1
|
+
{"version":3,"file":"gondolin-remote.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-remote.ts"],"names":[],"mappings":"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","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 createSessionFrameParser,\n encodeSessionMessage,\n type GondolinCredentialFile,\n type GondolinEnsureRuntimeRequest,\n type GondolinRemoteRuntime,\n} from \"./gondolin-contract.js\";\nimport { GondolinRuntimeGoneError, isLeaseFencedStatus } from \"./gondolin-recovery.js\";\nimport {\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// The framing lives with the rest of the wire contract; re-exported here for\n// existing importers.\nexport { createSessionFrameParser, encodeSessionMessage };\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: GondolinEnsureRuntimeRequest = {\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 const runtime = response.json as Partial<GondolinRemoteRuntime>;\n return {\n sessionId: runtime.sessionId ?? \"\",\n instanceId,\n workerPid: runtime.workerPid ?? 0,\n fingerprint: spec.fingerprint,\n };\n }\n if (isLeaseFencedStatus(response.status) && 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 (isLeaseFencedStatus(response.status)) {\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: GondolinCredentialFile[];\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: GondolinCredentialFile[] = [];\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 (isLeaseFencedStatus(response.statusCode)) {\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,11 +1,15 @@
|
|
|
1
1
|
import type { Readable } from "node:stream";
|
|
2
2
|
import type { GondolinWorkerConfig } from "./gondolin-worker.js";
|
|
3
3
|
import type { ExecResult } from "./types.js";
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* A live worker-hosted runtime as seen from mikan: identity plus fleet
|
|
6
|
+
* routing only. Transport-private connection details (the local worker's
|
|
7
|
+
* session socket, the remote worker's lease/tunnel) stay inside the owning
|
|
8
|
+
* transport — a handle never carries another transport's address fields.
|
|
9
|
+
*/
|
|
5
10
|
export interface GondolinRuntimeHandle {
|
|
6
11
|
sessionId: string;
|
|
7
12
|
instanceId: string;
|
|
8
|
-
socketPath: string;
|
|
9
13
|
workerPid: number;
|
|
10
14
|
fingerprint: string;
|
|
11
15
|
/** Fleet identity of the worker hosting the runtime (remote transport). */
|
|
@@ -41,15 +45,6 @@ export interface GondolinRuntimeTransport {
|
|
|
41
45
|
}): Promise<ExecResult>;
|
|
42
46
|
isRuntimeAlive(handle: GondolinRuntimeHandle): Promise<boolean>;
|
|
43
47
|
}
|
|
44
|
-
/**
|
|
45
|
-
* The runtime is gone and the command never reached it (the session socket
|
|
46
|
-
* refused the connection) — safe to recreate the runtime and retry.
|
|
47
|
-
*/
|
|
48
|
-
export declare class GondolinRuntimeGoneError extends Error {
|
|
49
|
-
}
|
|
50
|
-
/** The runtime died with the command in flight — not safe to retry blindly. */
|
|
51
|
-
export declare class GondolinRuntimeInterruptedError extends Error {
|
|
52
|
-
}
|
|
53
48
|
interface WorkerProcessLike {
|
|
54
49
|
pid?: number;
|
|
55
50
|
stdout: Readable | null;
|
|
@@ -93,6 +88,8 @@ export declare function execOverSessionConnect(connect: (callbacks: SessionClien
|
|
|
93
88
|
idleTimeoutMs?: number;
|
|
94
89
|
}): Promise<ExecResult>;
|
|
95
90
|
declare class GondolinWorkerClient implements GondolinRuntimeTransport {
|
|
91
|
+
/** Session IPC socket per runtime — transport-private, never on the handle. */
|
|
92
|
+
private readonly socketPaths;
|
|
96
93
|
private spawnProcess;
|
|
97
94
|
private connectImpl;
|
|
98
95
|
private killImpl;
|
|
@@ -119,7 +116,6 @@ declare class GondolinWorkerClient implements GondolinRuntimeTransport {
|
|
|
119
116
|
*/
|
|
120
117
|
stop(handle: Pick<GondolinRuntimeHandle, "workerPid" | "sessionId">): Promise<void>;
|
|
121
118
|
/** Whether the worker process behind a handle is still alive. */
|
|
122
|
-
isWorkerAlive(handle: Pick<GondolinRuntimeHandle, "workerPid">): boolean;
|
|
123
119
|
/**
|
|
124
120
|
* Run one command over a dedicated session IPC connection. Aborting (or the
|
|
125
121
|
* caller's timeout signal firing) destroys the connection, which kills the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gondolin-worker-client.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker-client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAI5C,OAAO,KAAK,EAAE,oBAAoB,EAA2B,MAAM,sBAAsB,CAAC;AAC1F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C
|
|
1
|
+
{"version":3,"file":"gondolin-worker-client.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker-client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAI5C,OAAO,KAAK,EAAE,oBAAoB,EAA2B,MAAM,sBAAsB,CAAC;AAC1F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,mFAAmF;AACnF,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACtF,IAAI,CACF,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,CAAC,GAC3F,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,IAAI,CACF,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAC/D,OAAO,CAAC,UAAU,CAAC,CAAC;IACvB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACjE;AAID,UAAU,iBAAiB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC;IACvE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC;IAChE,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;IACvC,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,CAAC,OAAO,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,KAAK,IAAI,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,IAAI,IAAI,CAAC;CACf;AAED,UAAU,6BAA6B;IACrC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,iBAAiB,CAAC;IACtE,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,sBAAsB,KAAK,aAAa,CAAC;IACnF,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,IAAI,CAAC;IACzD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AA0BD;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,CAAC,SAAS,EAAE,sBAAsB,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,EACtF,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3F,OAAO,CAAC,UAAU,CAAC,CAsGrB;AAKD,cAAM,oBAAqB,YAAW,wBAAwB;IAC5D,+EAA+E;IAC/E,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,WAAW,CAG0C;IAC7D,OAAO,CAAC,QAAQ,CACY;IAC5B,OAAO,CAAC,kBAAkB,CAAW;IACrC,OAAO,CAAC,UAAU,CAAU;IAC5B,OAAO,CAAC,kBAAkB,CAAO;IAEjC,SAAS,CAAC,SAAS,CAAC,EAAE,6BAA6B,GAAG,IAAI,CAOzD;IAED,wEAAwE;IAClE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAa1F;IAED,2DAA2D;IACrD,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CAEpE;IAED,+EAA+E;IACzE,KAAK,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAmBxE;IAED;;;;;OAKG;IACG,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAsB/F;IAED;;;OAGG;IACG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,WAAW,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAUxF;IAED,iEAAiE;IACjE;;;;OAIG;IACG,IAAI,CACR,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACnE,OAAO,CAAC,UAAU,CAAC,CAYrB;IAED,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,UAAU;YASJ,WAAW;IASzB,OAAO,CAAC,aAAa;CA0DtB;AAED,eAAO,MAAM,eAAe,sBAA6B,CAAC"}
|
|
@@ -4,15 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { SandboxError } from "./errors.js";
|
|
6
6
|
import { gondolinInventory } from "./gondolin-inventory.js";
|
|
7
|
-
|
|
8
|
-
* The runtime is gone and the command never reached it (the session socket
|
|
9
|
-
* refused the connection) — safe to recreate the runtime and retry.
|
|
10
|
-
*/
|
|
11
|
-
export class GondolinRuntimeGoneError extends Error {
|
|
12
|
-
}
|
|
13
|
-
/** The runtime died with the command in flight — not safe to retry blindly. */
|
|
14
|
-
export class GondolinRuntimeInterruptedError extends Error {
|
|
15
|
-
}
|
|
7
|
+
import { GondolinRuntimeGoneError, GondolinRuntimeInterruptedError } from "./gondolin-recovery.js";
|
|
16
8
|
function defaultSpawnProcess(command, args) {
|
|
17
9
|
return spawn(command, args, { detached: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
18
10
|
}
|
|
@@ -139,6 +131,8 @@ export async function execOverSessionConnect(connect, command, options = {}) {
|
|
|
139
131
|
const WORKER_HEARTBEAT_STALE_MS = 45 * 60 * 1000;
|
|
140
132
|
class GondolinWorkerClient {
|
|
141
133
|
constructor() {
|
|
134
|
+
/** Session IPC socket per runtime — transport-private, never on the handle. */
|
|
135
|
+
this.socketPaths = new Map();
|
|
142
136
|
this.spawnProcess = defaultSpawnProcess;
|
|
143
137
|
this.connectImpl = defaultConnect;
|
|
144
138
|
this.killImpl = (pid, signal) => process.kill(pid, signal);
|
|
@@ -185,10 +179,10 @@ class GondolinWorkerClient {
|
|
|
185
179
|
child.stdout?.destroy();
|
|
186
180
|
child.stderr?.destroy();
|
|
187
181
|
child.unref();
|
|
182
|
+
this.socketPaths.set(handshake.sessionId, handshake.socketPath);
|
|
188
183
|
return {
|
|
189
184
|
sessionId: handshake.sessionId,
|
|
190
185
|
instanceId: config.instanceId,
|
|
191
|
-
socketPath: handshake.socketPath,
|
|
192
186
|
workerPid: handshake.workerPid ?? child.pid,
|
|
193
187
|
fingerprint: config.fingerprint,
|
|
194
188
|
};
|
|
@@ -203,10 +197,10 @@ class GondolinWorkerClient {
|
|
|
203
197
|
const record = await gondolinInventory.findAdoptable(instanceId);
|
|
204
198
|
if (!record?.socketPath)
|
|
205
199
|
return undefined;
|
|
200
|
+
this.socketPaths.set(record.sessionId, record.socketPath);
|
|
206
201
|
const handle = {
|
|
207
202
|
sessionId: record.sessionId,
|
|
208
203
|
instanceId,
|
|
209
|
-
socketPath: record.socketPath,
|
|
210
204
|
workerPid: record.ownerPid,
|
|
211
205
|
fingerprint: record.fingerprint ?? "",
|
|
212
206
|
};
|
|
@@ -228,6 +222,7 @@ class GondolinWorkerClient {
|
|
|
228
222
|
* SIGKILL and reap its leftovers if it will not die.
|
|
229
223
|
*/
|
|
230
224
|
async stop(handle) {
|
|
225
|
+
this.socketPaths.delete(handle.sessionId);
|
|
231
226
|
if (this.isPidAlive(handle.workerPid)) {
|
|
232
227
|
this.signal(handle.workerPid, "SIGTERM");
|
|
233
228
|
if (!(await this.waitForExit(handle.workerPid, this.stopWaitMs))) {
|
|
@@ -238,16 +233,19 @@ class GondolinWorkerClient {
|
|
|
238
233
|
await gondolinInventory.reapSession(handle.sessionId);
|
|
239
234
|
}
|
|
240
235
|
/** Whether the worker process behind a handle is still alive. */
|
|
241
|
-
isWorkerAlive(handle) {
|
|
242
|
-
return this.isPidAlive(handle.workerPid);
|
|
243
|
-
}
|
|
244
236
|
/**
|
|
245
237
|
* Run one command over a dedicated session IPC connection. Aborting (or the
|
|
246
238
|
* caller's timeout signal firing) destroys the connection, which kills the
|
|
247
239
|
* in-flight guest process.
|
|
248
240
|
*/
|
|
249
241
|
async exec(handle, command, options = {}) {
|
|
250
|
-
|
|
242
|
+
const socketPath = this.socketPaths.get(handle.sessionId);
|
|
243
|
+
if (!socketPath) {
|
|
244
|
+
// the socket was registered at ensure/adopt; a missing entry means this
|
|
245
|
+
// process no longer owns the runtime — recreate the session cleanly
|
|
246
|
+
throw new GondolinRuntimeGoneError("no session socket known for this runtime");
|
|
247
|
+
}
|
|
248
|
+
return execOverSessionConnect((callbacks) => this.connectImpl(socketPath, callbacks), command, options);
|
|
251
249
|
}
|
|
252
250
|
signal(pid, signal) {
|
|
253
251
|
try {
|
|
@@ -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 * 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"]}
|
|
1
|
+
{"version":3,"file":"gondolin-worker-client.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAA8B,MAAM,yBAAyB,CAAC;AAmDxF,OAAO,EAAE,wBAAwB,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAC;AAsCnG,SAAS,mBAAmB,CAAC,OAAe,EAAE,IAAc;IAC1D,OAAO,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;AACrF,CAAC;AAED,KAAK,UAAU,cAAc,CAC3B,UAAkB,EAClB,SAAiC;IAEjC,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;IACtE,OAAO,gBAAgB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;GAKG;AACH,MAAM,oBAAoB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE5C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAAsF,EACtF,OAAe,EACf,OAAO,GAAmF,EAAE;IAE5F,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;QAAE,MAAM,UAAU,EAAE,CAAC;IAChD,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,oBAAoB,CAAC;IACpE,OAAO,MAAM,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACvD,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,MAAiC,CAAC;QACtC,IAAI,SAAqC,CAAC;QAC1C,MAAM,MAAM,GAAG,CAAC,EAAc,EAAQ,EAAE;YACtC,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,OAAO,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACtD,EAAE,EAAE,CAAC;QACP,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,GAAS,EAAE,CACzB,MAAM,CAAC,GAAG,EAAE;YACV,MAAM,EAAE,KAAK,EAAE,CAAC;YAChB,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC;QACL,MAAM,MAAM,GAAG,GAAS,EAAE,CACxB,MAAM,CAAC,GAAG,EAAE;YACV,MAAM,EAAE,KAAK,EAAE,CAAC;YAChB,MAAM,CACJ,IAAI,+BAA+B,CACjC,2BAA2B,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,yBAAyB;gBAClF,gGAAgG,CACnG,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;QACL,MAAM,YAAY,GAAG,GAAS,EAAE;YAC9B,IAAI,aAAa,IAAI,CAAC,IAAI,OAAO;gBAAE,OAAO;YAC1C,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,SAAS,GAAG,UAAU,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;YAC9C,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;QACtB,CAAC,CAAC;QACF,YAAY,EAAE,CAAC;QACf,MAAM,SAAS,GAA2B;YACxC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE;gBAClB,QAAQ,GAAG,IAAI,CAAC;gBAChB,YAAY,EAAE,CAAC;gBACf,IAAI,OAAO,CAAC,IAAI,KAAK,eAAe,IAAI,OAAO,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;oBACzD,MAAM,CAAC,GAAG,EAAE;wBACV,MAAM,EAAE,KAAK,EAAE,CAAC;wBAChB,OAAO,CAAC;4BACN,MAAM;4BACN,MAAM;4BACN,IAAI,EAAE,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;yBACpE,CAAC,CAAC;oBACL,CAAC,CAAC,CAAC;gBACL,CAAC;qBAAM,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBACpC,MAAM,CAAC,GAAG,EAAE;wBACV,MAAM,EAAE,KAAK,EAAE,CAAC;wBAChB,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;oBACzF,CAAC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YACD,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;gBAClB,QAAQ,GAAG,IAAI,CAAC;gBAChB,YAAY,EAAE,CAAC;gBACf,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAChD,IAAI,GAAG,KAAK,CAAC;oBAAE,MAAM,IAAI,IAAI,CAAC;;oBACzB,MAAM,IAAI,IAAI,CAAC;YACtB,CAAC;YACD,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBACjB,MAAM,CAAC,GAAG,EAAE;oBACV,MAAM,MAAM,GAAG,KAAK,EAAE,OAAO,IAAI,+CAA+C,CAAC;oBACjF,MAAM,IAAI,GAAI,KAA2C,EAAE,IAAI,CAAC;oBAChE,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,cAAc,CAAC,EAAE,CAAC;wBAChE,MAAM,CAAC,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC/C,CAAC;yBAAM,CAAC;wBACN,MAAM,CAAC,IAAI,+BAA+B,CAAC,MAAM,CAAC,CAAC,CAAC;oBACtD,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC;SACF,CAAC;QACF,OAAO,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACnE,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;aAChC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YAChB,MAAM,GAAG,OAAO,CAAC;YACjB,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,KAAK,EAAE,CAAC;gBAChB,OAAO;YACT,CAAC;YACD,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,MAAM;gBACZ,EAAE,EAAE,CAAC;gBACL,GAAG,EAAE,SAAS;gBACd,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC;gBACrB,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC;gBAC/E,GAAG,EAAE,YAAY;aAClB,CAAC,CAAC;QACL,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CACtB,MAAM,CAAC,GAAG,EAAE,CACV,MAAM,CAAC,IAAI,wBAAwB,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CACvF,CACF,CAAC;IACN,CAAC,CAAC,CAAC;AACL,CAAC;AAED,iFAAiF;AACjF,MAAM,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEjD,MAAM,oBAAoB;IAA1B;QACE,+EAA+E;QAC9D,gBAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;QACjD,iBAAY,GAAG,mBAAmB,CAAC;QACnC,gBAAW,GAG2B,cAAc,CAAC;QACrD,aAAQ,GAAsD,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CACpF,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACpB,uBAAkB,GAAG,OAAO,CAAC;QAC7B,eAAU,GAAG,MAAM,CAAC;QACpB,uBAAkB,GAAG,GAAG,CAAC;IAgNnC,CAAC;IA9MC,SAAS,CAAC,SAAyC;QACjD,IAAI,CAAC,YAAY,GAAG,SAAS,EAAE,YAAY,IAAI,mBAAmB,CAAC;QACnE,IAAI,CAAC,WAAW,GAAG,SAAS,EAAE,OAAO,IAAI,cAAc,CAAC;QACxD,IAAI,CAAC,QAAQ,GAAG,SAAS,EAAE,IAAI,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;QAChF,IAAI,CAAC,kBAAkB,GAAG,SAAS,EAAE,kBAAkB,IAAI,OAAO,CAAC;QACnE,IAAI,CAAC,UAAU,GAAG,SAAS,EAAE,UAAU,IAAI,MAAM,CAAC;QAClD,IAAI,CAAC,kBAAkB,GAAG,SAAS,EAAE,kBAAkB,IAAI,GAAG,CAAC;IACjE,CAAC;IAED,wEAAwE;IACxE,KAAK,CAAC,MAAM,CAAC,UAAkB,EAAE,IAAyB;QACxD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAC/D,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;QAC5B,OAAO,IAAI,CAAC,KAAK,CAAC;YAChB,UAAU;YACV,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,IAAI,EAAE,IAAI,CAAC,MAAM;YACjB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY,EAAE,iBAAiB,CAAC,SAAS,EAAE,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,mBAAmB,CAAC;YAC7F,gBAAgB,EAAE,yBAAyB;SAC5C,CAAC,CAAC;IACL,CAAC;IAED,2DAA2D;IAC3D,KAAK,CAAC,cAAc,CAAC,MAA6B;QAChD,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC3C,CAAC;IAED,+EAA+E;IAC/E,KAAK,CAAC,KAAK,CAAC,MAA4B;QACtC,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,2BAA2B,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACnF,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACnF,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAClD,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;YACtE,MAAM,IAAI,YAAY,CACpB,2CAA2C,SAAS,CAAC,KAAK,IAAI,yBAAyB,EAAE,CAC1F,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;QACxB,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;QACxB,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;QAChE,OAAO;YACL,SAAS,EAAE,SAAS,CAAC,SAAS;YAC9B,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,SAAS,EAAE,SAAS,CAAC,SAAS,IAAK,KAAK,CAAC,GAAc;YACvD,WAAW,EAAE,MAAM,CAAC,WAAW;SAChC,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,KAAK,CAAC,UAAkB,EAAE,WAAmB;QACjD,MAAM,MAAM,GACV,MAAM,iBAAiB,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM,EAAE,UAAU;YAAE,OAAO,SAAS,CAAC;QAC1C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QAC1D,MAAM,MAAM,GAA0B;YACpC,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,UAAU;YACV,SAAS,EAAE,MAAM,CAAC,QAAQ;YAC1B,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE;SACtC,CAAC;QACF,IAAI,MAAM,CAAC,WAAW,KAAK,WAAW,EAAE,CAAC;YACvC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACxB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACzE,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACxB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI,CAAC,MAA8D;QACvE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YACzC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;gBACjE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;gBACzC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QACD,MAAM,iBAAiB,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACxD,CAAC;IAED,iEAAiE;IACjE;;;;OAIG;IACH,KAAK,CAAC,IAAI,CACR,MAA6B,EAC7B,OAAe,EACf,OAAO,GAA2D,EAAE;QAEpE,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC1D,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,wEAAwE;YACxE,oEAAoE;YACpE,MAAM,IAAI,wBAAwB,CAAC,0CAA0C,CAAC,CAAC;QACjF,CAAC;QACD,OAAO,sBAAsB,CAC3B,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,SAAS,CAAC,EACtD,OAAO,EACP,OAAO,CACR,CAAC;IACJ,CAAC;IAEO,MAAM,CAAC,GAAW,EAAE,MAAsB;QAChD,IAAI,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,eAAe;QACjB,CAAC;IACH,CAAC;IAEO,UAAU,CAAC,GAAW;QAC5B,IAAI,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,GAAW,EAAE,MAAc;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC;QACrC,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ;gBAAE,OAAO,KAAK,CAAC;YACzC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,aAAa,CAAC,KAAwB;QAC5C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,MAAM,MAAM,GAAG,CAAC,EAAc,EAAQ,EAAE;gBACtC,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,EAAE,EAAE,CAAC;YACP,CAAC,CAAC;YACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,MAAM,CAAC,GAAG,EAAE;oBACV,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBACtB,MAAM,CACJ,IAAI,YAAY,CACd,sDAAsD,IAAI,CAAC,kBAAkB,KAC3E,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAClC,EAAE,CACH,CACF,CAAC;gBACJ,CAAC,CAAC,CAAC;YACL,CAAC,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;YAC5B,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACjC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBACrC,IAAI,OAAO,GAAG,CAAC;oBAAE,OAAO;gBACxB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;gBACtC,MAAM,CAAC,GAAG,EAAE;oBACV,IAAI,CAAC;wBACH,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAA4B,CAAC,CAAC;oBACvD,CAAC;oBAAC,MAAM,CAAC;wBACP,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;wBACtB,MAAM,CAAC,IAAI,YAAY,CAAC,6CAA6C,IAAI,EAAE,CAAC,CAAC,CAAC;oBAChF,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;gBAC3B,MAAM,CAAC,GAAG,EAAE,CACV,MAAM,CACJ,IAAI,YAAY,CACd,6DAA6D,IAAI,IAC/D,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAClC,EAAE,CACH,CACF,CACF,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBAC5B,MAAM,CAAC,GAAG,EAAE,CACV,MAAM,CAAC,IAAI,YAAY,CAAC,2CAA2C,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CACrF,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,oBAAoB,EAAE,CAAC","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/**\n * A live worker-hosted runtime as seen from mikan: identity plus fleet\n * routing only. Transport-private connection details (the local worker's\n * session socket, the remote worker's lease/tunnel) stay inside the owning\n * transport — a handle never carries another transport's address fields.\n */\nexport interface GondolinRuntimeHandle {\n sessionId: string;\n instanceId: 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\nimport { GondolinRuntimeGoneError, GondolinRuntimeInterruptedError } from \"./gondolin-recovery.js\";\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 /** Session IPC socket per runtime — transport-private, never on the handle. */\n private readonly socketPaths = new Map<string, string>();\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 this.socketPaths.set(handshake.sessionId, handshake.socketPath);\n return {\n sessionId: handshake.sessionId,\n instanceId: config.instanceId,\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 this.socketPaths.set(record.sessionId, record.socketPath);\n const handle: GondolinRuntimeHandle = {\n sessionId: record.sessionId,\n instanceId,\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 this.socketPaths.delete(handle.sessionId);\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 /**\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 const socketPath = this.socketPaths.get(handle.sessionId);\n if (!socketPath) {\n // the socket was registered at ensure/adopt; a missing entry means this\n // process no longer owns the runtime — recreate the session cleanly\n throw new GondolinRuntimeGoneError(\"no session socket known for this runtime\");\n }\n return execOverSessionConnect(\n (callbacks) => this.connectImpl(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,43 +1,6 @@
|
|
|
1
1
|
type GondolinModule = typeof import("@earendil-works/gondolin");
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* single JSON argv entry — it must stay free of secrets (vault env travels
|
|
5
|
-
* per-exec over the session IPC socket, never through the worker command
|
|
6
|
-
* line).
|
|
7
|
-
*/
|
|
8
|
-
export interface GondolinWorkerConfig {
|
|
9
|
-
/** mikan session key the runtime belongs to. */
|
|
10
|
-
instanceId: string;
|
|
11
|
-
/** Resolved guest image asset directory. */
|
|
12
|
-
image?: string;
|
|
13
|
-
/** Image selector resolved inside the worker (remote workers, where the
|
|
14
|
-
* spawning host has no image store). Ignored when `image` is set. */
|
|
15
|
-
imageSelector?: string;
|
|
16
|
-
mounts: Array<{
|
|
17
|
-
source: string;
|
|
18
|
-
target: string;
|
|
19
|
-
}>;
|
|
20
|
-
cpus?: number;
|
|
21
|
-
memory?: string;
|
|
22
|
-
/** Desired-runtime fingerprint recorded for adoption checks. */
|
|
23
|
-
fingerprint: string;
|
|
24
|
-
/** Runtime inventory directory (shared with the spawning mikan). */
|
|
25
|
-
inventoryDir: string;
|
|
26
|
-
/**
|
|
27
|
-
* Self-stop when the mikan heartbeat file is older than this — no mikan is
|
|
28
|
-
* left to adopt the runtime. 0 disables the check.
|
|
29
|
-
*/
|
|
30
|
-
heartbeatStaleMs: number;
|
|
31
|
-
}
|
|
32
|
-
/** First stdout line of a worker, consumed by the spawning mikan. */
|
|
33
|
-
export interface GondolinWorkerHandshake {
|
|
34
|
-
ready: boolean;
|
|
35
|
-
error?: string;
|
|
36
|
-
sessionId?: string;
|
|
37
|
-
socketPath?: string;
|
|
38
|
-
workerPid?: number;
|
|
39
|
-
runnerPid?: number | null;
|
|
40
|
-
}
|
|
2
|
+
export type { GondolinWorkerConfig, GondolinWorkerHandshake } from "./gondolin-contract.js";
|
|
3
|
+
import type { GondolinWorkerConfig } from "./gondolin-contract.js";
|
|
41
4
|
interface GondolinWorkerDeps {
|
|
42
5
|
loadGondolin?: () => Promise<GondolinModule>;
|
|
43
6
|
announce?: (line: string) => void;
|
|
@@ -51,5 +14,4 @@ interface GondolinWorkerDeps {
|
|
|
51
14
|
* when every mikan that could adopt the runtime is gone (stale heartbeat).
|
|
52
15
|
*/
|
|
53
16
|
export declare function runGondolinWorker(config: GondolinWorkerConfig, deps?: GondolinWorkerDeps): Promise<void>;
|
|
54
|
-
export {};
|
|
55
17
|
//# sourceMappingURL=gondolin-worker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gondolin-worker.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker.ts"],"names":[],"mappings":"AAMA,KAAK,cAAc,GAAG,cAAc,0BAA0B,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"gondolin-worker.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker.ts"],"names":[],"mappings":"AAMA,KAAK,cAAc,GAAG,cAAc,0BAA0B,CAAC,CAAC;AAKhE,YAAY,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC5F,OAAO,KAAK,EAAE,oBAAoB,EAA2B,MAAM,wBAAwB,CAAC;AAE5F,UAAU,kBAAkB;IAC1B,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAwED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,oBAAoB,EAC5B,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA8Hf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gondolin-worker.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { chmodSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { gondolinInventory } from \"./gondolin-inventory.js\";\nimport { shellEscape } from \"./utils.js\";\n\ntype GondolinModule = typeof import(\"@earendil-works/gondolin\");\n\n/**\n * Everything a worker process needs to host one Gondolin VM. Serialized as a\n * single JSON argv entry — it must stay free of secrets (vault env travels\n * per-exec over the session IPC socket, never through the worker command\n * line).\n */\nexport interface GondolinWorkerConfig {\n /** mikan session key the runtime belongs to. */\n instanceId: string;\n /** Resolved guest image asset directory. */\n image?: string;\n /** Image selector resolved inside the worker (remote workers, where the\n * spawning host has no image store). Ignored when `image` is set. */\n imageSelector?: string;\n mounts: Array<{ source: string; target: string }>;\n cpus?: number;\n memory?: string;\n /** Desired-runtime fingerprint recorded for adoption checks. */\n fingerprint: string;\n /** Runtime inventory directory (shared with the spawning mikan). */\n inventoryDir: string;\n /**\n * Self-stop when the mikan heartbeat file is older than this — no mikan is\n * left to adopt the runtime. 0 disables the check.\n */\n heartbeatStaleMs: number;\n}\n\n/** First stdout line of a worker, consumed by the spawning mikan. */\nexport interface GondolinWorkerHandshake {\n ready: boolean;\n error?: string;\n sessionId?: string;\n socketPath?: string;\n workerPid?: number;\n runnerPid?: number | null;\n}\n\ninterface GondolinWorkerDeps {\n loadGondolin?: () => Promise<GondolinModule>;\n announce?: (line: string) => void;\n exit?: (code: number) => void;\n pollIntervalMs?: number;\n}\n\n/**\n * A single-file mount handled outside the VFS: Gondolin's guest prepares every\n * VFS mount point with `mkdir -p`, so a file target can never bind (the guest\n * ends up bind-mounting a file onto a directory and the VM refuses to start).\n * Files are copied in after boot instead; the ones under /workspace sync guest\n * edits back to the host so agent-maintained files like MEMORY.md persist.\n */\ninterface FileProjection {\n source: string;\n target: string;\n syncBack: boolean;\n lastHash: string;\n}\n\nfunction sha256(data: Buffer): string {\n return createHash(\"sha256\").update(data).digest(\"hex\");\n}\n\nfunction warn(message: string): void {\n process.stderr.write(`[gondolin-worker] ${message}\\n`);\n}\n\nfunction heartbeatFresh(path: string, since: number, staleMs: number): boolean {\n let mtimeMs = 0;\n try {\n mtimeMs = statSync(path).mtimeMs;\n } catch {\n // no heartbeat yet — fall back to worker start time\n }\n return Date.now() - Math.max(mtimeMs, since) <= staleMs;\n}\n\nfunction restrictSocketAccess(socketPath: string): void {\n // The session socket grants arbitrary exec inside the VM (and through its\n // mounts, the workspace) — keep it to this user even under a lax umask.\n try {\n chmodSync(dirname(socketPath), 0o700);\n chmodSync(socketPath, 0o600);\n } catch {\n // best-effort hardening\n }\n}\n\nfunction partitionMounts(mounts: GondolinWorkerConfig[\"mounts\"]): {\n directories: GondolinWorkerConfig[\"mounts\"];\n files: FileProjection[];\n} {\n const directories: GondolinWorkerConfig[\"mounts\"] = [];\n const files: FileProjection[] = [];\n for (const mount of mounts) {\n let isDirectory: boolean;\n try {\n isDirectory = statSync(mount.source).isDirectory();\n } catch {\n warn(`skipping missing mount source ${mount.source}`);\n continue;\n }\n if (isDirectory) {\n directories.push(mount);\n } else {\n files.push({\n ...mount,\n syncBack: mount.target.startsWith(\"/workspace/\"),\n lastHash: \"\",\n });\n }\n }\n return { directories, files };\n}\n\n/**\n * Host one Gondolin VM for its whole life: boot it, persist the inventory\n * record other processes adopt it through, announce readiness on stdout, then\n * watch the VM runner and shut down when it dies, when a SIGTERM arrives, or\n * when every mikan that could adopt the runtime is gone (stale heartbeat).\n */\nexport async function runGondolinWorker(\n config: GondolinWorkerConfig,\n deps: GondolinWorkerDeps = {},\n): Promise<void> {\n const announce = deps.announce ?? ((line: string) => process.stdout.write(line + \"\\n\"));\n const exit = deps.exit ?? ((code: number) => process.exit(code));\n const pollIntervalMs = deps.pollIntervalMs ?? 2000;\n const loadGondolin =\n deps.loadGondolin ?? (() => import(\"@earendil-works/gondolin\") as Promise<GondolinModule>);\n\n gondolinInventory.configure(config.inventoryDir);\n const { VM, RealFSProvider, findSession, ensureImageSelector } = await loadGondolin();\n let imagePath = config.image;\n if (!imagePath) {\n if (!config.imageSelector) throw new Error(\"worker config needs image or imageSelector\");\n imagePath = (await ensureImageSelector(config.imageSelector)).assetDir;\n }\n const { directories, files } = partitionMounts(config.mounts);\n const vm = await VM.create({\n sandbox: { imagePath },\n env: { TZ: \"Asia/Taipei\" },\n sessionLabel: `mikan:${config.instanceId}`,\n cpus: config.cpus,\n memory: config.memory,\n vfs: {\n mounts: Object.fromEntries(\n directories.map(({ source, target }) => [target, new RealFSProvider(source)]),\n ),\n },\n });\n\n let session: Awaited<ReturnType<typeof findSession>>;\n try {\n await vm.start();\n await projectFiles(vm, files);\n session = await findSession(vm.id);\n if (!session) throw new Error(`Gondolin session '${vm.id}' did not register`);\n } catch (error) {\n // never leave a half-booted runner (and its overlay disk) behind\n await vm.close().catch(() => {});\n throw error;\n }\n restrictSocketAccess(session.socketPath);\n gondolinInventory.record({\n sessionId: vm.id,\n instanceId: config.instanceId,\n runnerPid: vm.getHostPid(),\n socketPath: session.socketPath,\n fingerprint: config.fingerprint,\n });\n\n const startedAt = Date.now();\n let closing = false;\n let syncing = false;\n const syncProjections = async (): Promise<void> => {\n if (syncing) return;\n syncing = true;\n try {\n for (const projection of files) {\n if (!projection.syncBack) continue;\n try {\n const data = await vm.fs.readFile(projection.target);\n const content = Buffer.isBuffer(data) ? data : Buffer.from(String(data));\n const hash = sha256(content);\n if (hash === projection.lastHash) continue;\n // atomic host write: a crash mid-sync must not truncate the file\n const staged = `${projection.source}.mikan-sync`;\n writeFileSync(staged, content);\n renameSync(staged, projection.source);\n projection.lastHash = hash;\n } catch {\n // guest file missing or read raced a writer; next tick retries\n }\n }\n } finally {\n syncing = false;\n }\n };\n\n const shutdown = async (code: number): Promise<void> => {\n if (closing) return;\n closing = true;\n clearInterval(watchdog);\n try {\n await syncProjections();\n } catch {\n // best effort; the periodic sync already captured earlier edits\n }\n try {\n await vm.close();\n } catch {\n // the runner may already be gone\n }\n gondolinInventory.release(vm.id);\n exit(code);\n };\n\n const watchdog = setInterval(() => {\n const runnerPid = vm.getHostPid();\n if (runnerPid === null) {\n // The VM runner died under us; exit so mikan sees the socket close and\n // recreates the runtime instead of hanging on a silent session.\n void shutdown(1);\n return;\n }\n gondolinInventory.refresh(vm.id, runnerPid);\n void syncProjections();\n const heartbeat = gondolinInventory.heartbeatPath();\n if (\n config.heartbeatStaleMs > 0 &&\n heartbeat &&\n !heartbeatFresh(heartbeat, startedAt, config.heartbeatStaleMs)\n ) {\n void shutdown(0);\n }\n }, pollIntervalMs);\n\n process.on(\"SIGTERM\", () => void shutdown(0));\n process.on(\"SIGINT\", () => void shutdown(0));\n\n announce(\n JSON.stringify({\n ready: true,\n sessionId: vm.id,\n socketPath: session.socketPath,\n workerPid: process.pid,\n runnerPid: vm.getHostPid(),\n } satisfies GondolinWorkerHandshake),\n );\n}\n\n/** Copy file mounts into the guest; credentials get user-only permissions. */\nasync function projectFiles(\n vm: {\n fs: {\n mkdir: (path: string, options?: { recursive?: boolean }) => Promise<void>;\n writeFile: (path: string, data: Buffer) => Promise<void>;\n };\n exec: (command: string) => PromiseLike<unknown>;\n },\n files: FileProjection[],\n): Promise<void> {\n for (const projection of files) {\n const content = readFileSync(projection.source);\n await vm.fs.mkdir(dirname(projection.target), { recursive: true });\n await vm.fs.writeFile(projection.target, content);\n if (!projection.syncBack) {\n // credential projection: no write-back, keep it owner-only\n await vm.exec(`chmod 600 ${shellEscape(projection.target)}`);\n }\n projection.lastHash = sha256(content);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"gondolin-worker.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { chmodSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { gondolinInventory } from \"./gondolin-inventory.js\";\nimport { shellEscape } from \"./utils.js\";\n\ntype GondolinModule = typeof import(\"@earendil-works/gondolin\");\n\n// The argv config and handshake are wire contract shapes shared with the Go\n// daemon; their single home is gondolin-contract.ts (re-exported here for\n// the worker entrypoint and spawners).\nexport type { GondolinWorkerConfig, GondolinWorkerHandshake } from \"./gondolin-contract.js\";\nimport type { GondolinWorkerConfig, GondolinWorkerHandshake } from \"./gondolin-contract.js\";\n\ninterface GondolinWorkerDeps {\n loadGondolin?: () => Promise<GondolinModule>;\n announce?: (line: string) => void;\n exit?: (code: number) => void;\n pollIntervalMs?: number;\n}\n\n/**\n * A single-file mount handled outside the VFS: Gondolin's guest prepares every\n * VFS mount point with `mkdir -p`, so a file target can never bind (the guest\n * ends up bind-mounting a file onto a directory and the VM refuses to start).\n * Files are copied in after boot instead; the ones under /workspace sync guest\n * edits back to the host so agent-maintained files like MEMORY.md persist.\n */\ninterface FileProjection {\n source: string;\n target: string;\n syncBack: boolean;\n lastHash: string;\n}\n\nfunction sha256(data: Buffer): string {\n return createHash(\"sha256\").update(data).digest(\"hex\");\n}\n\nfunction warn(message: string): void {\n process.stderr.write(`[gondolin-worker] ${message}\\n`);\n}\n\nfunction heartbeatFresh(path: string, since: number, staleMs: number): boolean {\n let mtimeMs = 0;\n try {\n mtimeMs = statSync(path).mtimeMs;\n } catch {\n // no heartbeat yet — fall back to worker start time\n }\n return Date.now() - Math.max(mtimeMs, since) <= staleMs;\n}\n\nfunction restrictSocketAccess(socketPath: string): void {\n // The session socket grants arbitrary exec inside the VM (and through its\n // mounts, the workspace) — keep it to this user even under a lax umask.\n try {\n chmodSync(dirname(socketPath), 0o700);\n chmodSync(socketPath, 0o600);\n } catch {\n // best-effort hardening\n }\n}\n\nfunction partitionMounts(mounts: GondolinWorkerConfig[\"mounts\"]): {\n directories: GondolinWorkerConfig[\"mounts\"];\n files: FileProjection[];\n} {\n const directories: GondolinWorkerConfig[\"mounts\"] = [];\n const files: FileProjection[] = [];\n for (const mount of mounts) {\n let isDirectory: boolean;\n try {\n isDirectory = statSync(mount.source).isDirectory();\n } catch {\n warn(`skipping missing mount source ${mount.source}`);\n continue;\n }\n if (isDirectory) {\n directories.push(mount);\n } else {\n files.push({\n ...mount,\n syncBack: mount.target.startsWith(\"/workspace/\"),\n lastHash: \"\",\n });\n }\n }\n return { directories, files };\n}\n\n/**\n * Host one Gondolin VM for its whole life: boot it, persist the inventory\n * record other processes adopt it through, announce readiness on stdout, then\n * watch the VM runner and shut down when it dies, when a SIGTERM arrives, or\n * when every mikan that could adopt the runtime is gone (stale heartbeat).\n */\nexport async function runGondolinWorker(\n config: GondolinWorkerConfig,\n deps: GondolinWorkerDeps = {},\n): Promise<void> {\n const announce = deps.announce ?? ((line: string) => process.stdout.write(line + \"\\n\"));\n const exit = deps.exit ?? ((code: number) => process.exit(code));\n const pollIntervalMs = deps.pollIntervalMs ?? 2000;\n const loadGondolin =\n deps.loadGondolin ?? (() => import(\"@earendil-works/gondolin\") as Promise<GondolinModule>);\n\n gondolinInventory.configure(config.inventoryDir);\n const { VM, RealFSProvider, findSession, ensureImageSelector } = await loadGondolin();\n let imagePath = config.image;\n if (!imagePath) {\n if (!config.imageSelector) throw new Error(\"worker config needs image or imageSelector\");\n imagePath = (await ensureImageSelector(config.imageSelector)).assetDir;\n }\n const { directories, files } = partitionMounts(config.mounts);\n const vm = await VM.create({\n sandbox: { imagePath },\n env: { TZ: \"Asia/Taipei\" },\n sessionLabel: `mikan:${config.instanceId}`,\n cpus: config.cpus,\n memory: config.memory,\n vfs: {\n mounts: Object.fromEntries(\n directories.map(({ source, target }) => [target, new RealFSProvider(source)]),\n ),\n },\n });\n\n let session: Awaited<ReturnType<typeof findSession>>;\n try {\n await vm.start();\n await projectFiles(vm, files);\n session = await findSession(vm.id);\n if (!session) throw new Error(`Gondolin session '${vm.id}' did not register`);\n } catch (error) {\n // never leave a half-booted runner (and its overlay disk) behind\n await vm.close().catch(() => {});\n throw error;\n }\n restrictSocketAccess(session.socketPath);\n gondolinInventory.record({\n sessionId: vm.id,\n instanceId: config.instanceId,\n runnerPid: vm.getHostPid(),\n socketPath: session.socketPath,\n fingerprint: config.fingerprint,\n });\n\n const startedAt = Date.now();\n let closing = false;\n let syncing = false;\n const syncProjections = async (): Promise<void> => {\n if (syncing) return;\n syncing = true;\n try {\n for (const projection of files) {\n if (!projection.syncBack) continue;\n try {\n const data = await vm.fs.readFile(projection.target);\n const content = Buffer.isBuffer(data) ? data : Buffer.from(String(data));\n const hash = sha256(content);\n if (hash === projection.lastHash) continue;\n // atomic host write: a crash mid-sync must not truncate the file\n const staged = `${projection.source}.mikan-sync`;\n writeFileSync(staged, content);\n renameSync(staged, projection.source);\n projection.lastHash = hash;\n } catch {\n // guest file missing or read raced a writer; next tick retries\n }\n }\n } finally {\n syncing = false;\n }\n };\n\n const shutdown = async (code: number): Promise<void> => {\n if (closing) return;\n closing = true;\n clearInterval(watchdog);\n try {\n await syncProjections();\n } catch {\n // best effort; the periodic sync already captured earlier edits\n }\n try {\n await vm.close();\n } catch {\n // the runner may already be gone\n }\n gondolinInventory.release(vm.id);\n exit(code);\n };\n\n const watchdog = setInterval(() => {\n const runnerPid = vm.getHostPid();\n if (runnerPid === null) {\n // The VM runner died under us; exit so mikan sees the socket close and\n // recreates the runtime instead of hanging on a silent session.\n void shutdown(1);\n return;\n }\n gondolinInventory.refresh(vm.id, runnerPid);\n void syncProjections();\n const heartbeat = gondolinInventory.heartbeatPath();\n if (\n config.heartbeatStaleMs > 0 &&\n heartbeat &&\n !heartbeatFresh(heartbeat, startedAt, config.heartbeatStaleMs)\n ) {\n void shutdown(0);\n }\n }, pollIntervalMs);\n\n process.on(\"SIGTERM\", () => void shutdown(0));\n process.on(\"SIGINT\", () => void shutdown(0));\n\n announce(\n JSON.stringify({\n ready: true,\n sessionId: vm.id,\n socketPath: session.socketPath,\n workerPid: process.pid,\n runnerPid: vm.getHostPid(),\n } satisfies GondolinWorkerHandshake),\n );\n}\n\n/** Copy file mounts into the guest; credentials get user-only permissions. */\nasync function projectFiles(\n vm: {\n fs: {\n mkdir: (path: string, options?: { recursive?: boolean }) => Promise<void>;\n writeFile: (path: string, data: Buffer) => Promise<void>;\n };\n exec: (command: string) => PromiseLike<unknown>;\n },\n files: FileProjection[],\n): Promise<void> {\n for (const projection of files) {\n const content = readFileSync(projection.source);\n await vm.fs.mkdir(dirname(projection.target), { recursive: true });\n await vm.fs.writeFile(projection.target, content);\n if (!projection.syncBack) {\n // credential projection: no write-back, keep it owner-only\n await vm.exec(`chmod 600 ${shellEscape(projection.target)}`);\n }\n projection.lastHash = sha256(content);\n }\n}\n"]}
|
|
@@ -42,6 +42,7 @@ export declare class GondolinExecutor implements Executor {
|
|
|
42
42
|
constructor(config: GondolinSandboxConfig, env?: Record<string, string> | undefined);
|
|
43
43
|
exec(command: string, options?: ExecOptions): Promise<ExecResult>;
|
|
44
44
|
readFile(path: string): Promise<string>;
|
|
45
|
+
readFileBase64(path: string): Promise<string>;
|
|
45
46
|
writeFile(path: string, content: string): Promise<void>;
|
|
46
47
|
getWorkspacePath(): string;
|
|
47
48
|
getPathContext(hostWorkspaceRoot: string): RuntimePathContext;
|
|
@@ -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;
|
|
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;AAcjG,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,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAElD;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"}
|