@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gondolin-bootstrap.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAkB7D;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAiC;IACxE,iBAAiB,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IACjE,iBAAiB,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC;IACzE,kBAAkB,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,yBAAyB,CAAC,CAAC,CAAC;IAChF,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACnE,eAAe,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnD,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QAC1B,sBAAsB,EAAE,CAAC;IAC3B,CAAC;AACH,CAAC","sourcesContent":["import { join } from \"node:path\";\nimport type { GondolinRemoteSettings, ResourceLimits } from \"../types.js\";\nimport { assertRemoteConfigured, gondolinResources } from \"./gondolin.js\";\nimport { gondolinFleet } from \"./gondolin-fleet.js\";\nimport { gondolinGateway } from \"./gondolin-gateway.js\";\nimport { gondolinInventory } from \"./gondolin-inventory.js\";\nimport { gondolinJoin } from \"./gondolin-join.js\";\nimport { gondolinPlacements } from \"./gondolin-placement.js\";\n\nexport interface GondolinBootstrapOptions {\n /** Host-only state dir; inventory, placements, and gateway CA live under it. */\n stateDir: string;\n /** Default per-runtime resource limits. */\n limits?: ResourceLimits;\n /** Boosted limits applied while a runtime holds a boost. */\n boostLimits?: ResourceLimits;\n /** Remote fleet settings (workers, gateway); omit for local-only gondolin. */\n remote?: GondolinRemoteSettings;\n /**\n * Fail loudly when the remote profile is requested but no worker source is\n * configured, instead of discovering it on the first runtime ensure.\n */\n requireRemote?: boolean;\n}\n\n/**\n * Configure every gondolin runtime singleton for one process — resources,\n * inventory, placements, fleet, join, gateway — from one options object.\n * This is the managed-sandbox bootstrap seam: main.ts and embedders\n * (src/index.ts) call it before creating conversation runtimes with a\n * gondolin sandbox config; nothing else may call the individual configure()\n * methods outside tests.\n */\nexport function configureGondolinRuntime(options: GondolinBootstrapOptions): void {\n gondolinResources.configure(options.limits, options.boostLimits);\n gondolinInventory.configure(join(options.stateDir, \"gondolin-runtimes\"));\n gondolinPlacements.configure(join(options.stateDir, \"gondolin-placement.json\"));\n gondolinFleet.configure(options.remote);\n gondolinJoin.configure(join(options.stateDir, \"gondolin-gateway\"));\n gondolinGateway.configure(options.remote?.gateway);\n if (options.requireRemote) {\n assertRemoteConfigured();\n }\n}\n"]}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { type Static } from "@sinclair/typebox";
|
|
2
|
+
/**
|
|
3
|
+
* Everything a worker process needs to host one Gondolin VM. Serialized as a
|
|
4
|
+
* single JSON argv entry by both spawners (the local TS worker client and
|
|
5
|
+
* the Go daemon's supervisor).
|
|
6
|
+
*/
|
|
7
|
+
export declare const GondolinWorkerConfigSchema: import("@sinclair/typebox").TObject<{
|
|
8
|
+
/** mikan session key the runtime belongs to. */
|
|
9
|
+
instanceId: import("@sinclair/typebox").TString;
|
|
10
|
+
/** Resolved guest image asset directory. */
|
|
11
|
+
image: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
12
|
+
/** Image selector resolved inside the worker (remote workers, where the
|
|
13
|
+
* spawning host has no image store). Ignored when `image` is set. */
|
|
14
|
+
imageSelector: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
15
|
+
mounts: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
16
|
+
source: import("@sinclair/typebox").TString;
|
|
17
|
+
target: import("@sinclair/typebox").TString;
|
|
18
|
+
}>>;
|
|
19
|
+
cpus: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
20
|
+
memory: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
21
|
+
/** Desired-runtime fingerprint recorded for adoption checks. */
|
|
22
|
+
fingerprint: import("@sinclair/typebox").TString;
|
|
23
|
+
/** Runtime inventory directory (shared with the spawning mikan). */
|
|
24
|
+
inventoryDir: import("@sinclair/typebox").TString;
|
|
25
|
+
/** Self-stop when the mikan heartbeat file is older than this. 0 disables. */
|
|
26
|
+
heartbeatStaleMs: import("@sinclair/typebox").TNumber;
|
|
27
|
+
}>;
|
|
28
|
+
export type GondolinWorkerConfig = Static<typeof GondolinWorkerConfigSchema>;
|
|
29
|
+
/** First stdout line of a worker, consumed by both spawners. */
|
|
30
|
+
export declare const GondolinWorkerHandshakeSchema: import("@sinclair/typebox").TObject<{
|
|
31
|
+
ready: import("@sinclair/typebox").TBoolean;
|
|
32
|
+
error: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
33
|
+
sessionId: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
34
|
+
socketPath: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
35
|
+
workerPid: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
36
|
+
runnerPid: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TNumber, import("@sinclair/typebox").TNull]>>;
|
|
37
|
+
}>;
|
|
38
|
+
export type GondolinWorkerHandshake = Static<typeof GondolinWorkerHandshakeSchema>;
|
|
39
|
+
/**
|
|
40
|
+
* One `<sessionId>.json` inventory record, written by the worker process and
|
|
41
|
+
* read back by the TS inventory and the Go daemon's rediscovery.
|
|
42
|
+
*/
|
|
43
|
+
export declare const GondolinRuntimeRecordSchema: import("@sinclair/typebox").TObject<{
|
|
44
|
+
/** Gondolin session id (`vm.id`); also names the record file. */
|
|
45
|
+
sessionId: import("@sinclair/typebox").TString;
|
|
46
|
+
/** mikan session key (vault key) the runtime was created for. */
|
|
47
|
+
instanceId: import("@sinclair/typebox").TString;
|
|
48
|
+
/** Process that owns the runtime (the worker process). */
|
|
49
|
+
ownerPid: import("@sinclair/typebox").TNumber;
|
|
50
|
+
/** Host pid of the active VM runner process (QEMU/krun), if started. */
|
|
51
|
+
runnerPid: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TNumber, import("@sinclair/typebox").TNull]>;
|
|
52
|
+
createdAt: import("@sinclair/typebox").TString;
|
|
53
|
+
/** Session IPC socket for adopting the runtime from a new mikan process. */
|
|
54
|
+
socketPath: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
55
|
+
/** Desired-runtime fingerprint the VM was created from. */
|
|
56
|
+
fingerprint: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
57
|
+
}>;
|
|
58
|
+
export type GondolinRuntimeRecord = Static<typeof GondolinRuntimeRecordSchema>;
|
|
59
|
+
export declare function isGondolinRuntimeRecord(value: unknown): value is GondolinRuntimeRecord;
|
|
60
|
+
/** A vault credential shipped by content on the ensure request (never a shared-fs mount). */
|
|
61
|
+
declare const GondolinCredentialFileSchema: import("@sinclair/typebox").TObject<{
|
|
62
|
+
target: import("@sinclair/typebox").TString;
|
|
63
|
+
contentBase64: import("@sinclair/typebox").TString;
|
|
64
|
+
}>;
|
|
65
|
+
export type GondolinCredentialFile = Static<typeof GondolinCredentialFileSchema>;
|
|
66
|
+
/** POST /v1/runtimes body sent to the remote worker daemon. */
|
|
67
|
+
export declare const GondolinEnsureRuntimeRequestSchema: import("@sinclair/typebox").TObject<{
|
|
68
|
+
instanceId: import("@sinclair/typebox").TString;
|
|
69
|
+
imageSelector: import("@sinclair/typebox").TString;
|
|
70
|
+
mounts: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
71
|
+
source: import("@sinclair/typebox").TString;
|
|
72
|
+
target: import("@sinclair/typebox").TString;
|
|
73
|
+
}>>;
|
|
74
|
+
credentialFiles: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
75
|
+
target: import("@sinclair/typebox").TString;
|
|
76
|
+
contentBase64: import("@sinclair/typebox").TString;
|
|
77
|
+
}>>;
|
|
78
|
+
/** Fractional cgroup CPU limit as a string; "" when unset. */
|
|
79
|
+
cpus: import("@sinclair/typebox").TString;
|
|
80
|
+
memory: import("@sinclair/typebox").TString;
|
|
81
|
+
fingerprint: import("@sinclair/typebox").TString;
|
|
82
|
+
/** Accepted by the daemon; the TS client currently leaves it unset (0). */
|
|
83
|
+
heartbeatStaleMs: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
84
|
+
}>;
|
|
85
|
+
export type GondolinEnsureRuntimeRequest = Static<typeof GondolinEnsureRuntimeRequestSchema>;
|
|
86
|
+
/**
|
|
87
|
+
* One runtime as reported by the daemon (ensure response, GET, and the
|
|
88
|
+
* elements of the list response). The daemon's socketPath never crosses the
|
|
89
|
+
* wire — sessions reach the runtime through the tunnel, not the socket.
|
|
90
|
+
*/
|
|
91
|
+
export declare const GondolinRemoteRuntimeSchema: import("@sinclair/typebox").TObject<{
|
|
92
|
+
sessionId: import("@sinclair/typebox").TString;
|
|
93
|
+
instanceId: import("@sinclair/typebox").TString;
|
|
94
|
+
fingerprint: import("@sinclair/typebox").TString;
|
|
95
|
+
workerPid: import("@sinclair/typebox").TNumber;
|
|
96
|
+
runnerPid: import("@sinclair/typebox").TNumber;
|
|
97
|
+
epoch: import("@sinclair/typebox").TNumber;
|
|
98
|
+
adopted: import("@sinclair/typebox").TBoolean;
|
|
99
|
+
}>;
|
|
100
|
+
export type GondolinRemoteRuntime = Static<typeof GondolinRemoteRuntimeSchema>;
|
|
101
|
+
/** POST /v1/leases response: a fenced, epoch-monotonic per-instance lease. */
|
|
102
|
+
export declare const GondolinLeaseGrantSchema: import("@sinclair/typebox").TObject<{
|
|
103
|
+
id: import("@sinclair/typebox").TString;
|
|
104
|
+
instanceId: import("@sinclair/typebox").TString;
|
|
105
|
+
epoch: import("@sinclair/typebox").TNumber;
|
|
106
|
+
/** RFC 3339 timestamp (Go time.Time marshalling). */
|
|
107
|
+
expiresAt: import("@sinclair/typebox").TString;
|
|
108
|
+
}>;
|
|
109
|
+
/** Structural shape of the JSON messages on the session protocol. */
|
|
110
|
+
export interface SessionFrameCallbacks {
|
|
111
|
+
onJson: (message: {
|
|
112
|
+
type: string;
|
|
113
|
+
id?: number;
|
|
114
|
+
exit_code?: number | null;
|
|
115
|
+
code?: string;
|
|
116
|
+
message?: string;
|
|
117
|
+
}) => void;
|
|
118
|
+
onBinary: (frame: Buffer) => void;
|
|
119
|
+
}
|
|
120
|
+
/** Parses the daemon-to-client session framing: u8 type + u32be length + payload. */
|
|
121
|
+
export declare function createSessionFrameParser(callbacks: SessionFrameCallbacks): (chunk: Buffer) => void;
|
|
122
|
+
/** Encodes a client-to-daemon session message: u32be length + JSON. */
|
|
123
|
+
export declare function encodeSessionMessage(message: object): Buffer;
|
|
124
|
+
export {};
|
|
125
|
+
//# sourceMappingURL=gondolin-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gondolin-contract.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin-contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAuBtD;;;;GAIG;AACH,eAAO,MAAM,0BAA0B;IACrC,gDAAgD;;IAEhD,4CAA4C;;IAE5C;yEACqE;;;;;;;;IAKrE,gEAAgE;;IAEhE,oEAAoE;;IAEpE,8EAA8E;;EAE9E,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE7E,gEAAgE;AAChE,eAAO,MAAM,6BAA6B;;;;;;;EAOxC,CAAC;AAEH,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEnF;;;GAGG;AACH,eAAO,MAAM,2BAA2B;IACtC,iEAAiE;;IAEjE,iEAAiE;;IAEjE,0DAA0D;;IAE1D,wEAAwE;;;IAGxE,4EAA4E;;IAE5E,2DAA2D;;EAE3D,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAE/E,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAYtF;AAED,6FAA6F;AAC7F,QAAA,MAAM,4BAA4B;;;EAGhC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,MAAM,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAEjF,+DAA+D;AAC/D,eAAO,MAAM,kCAAkC;;;;;;;;;;;IAK7C,8DAA8D;;;;IAI9D,2EAA2E;;EAE3E,CAAC;AAEH,MAAM,MAAM,4BAA4B,GAAG,MAAM,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE7F;;;;GAIG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;EAQtC,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAE/E,8EAA8E;AAC9E,eAAO,MAAM,wBAAwB;;;;IAInC,qDAAqD;;EAErD,CAAC;AAOH,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IACpC,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;CACnC;AAED,qFAAqF;AACrF,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,qBAAqB,GAC/B,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAqBzB;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAK5D"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { isRecord } from "../utils/file-guards.js";
|
|
3
|
+
/**
|
|
4
|
+
* Single home for every wire shape that crosses the mikan ⇄ gondolin-worker
|
|
5
|
+
* boundary. The Go daemon mirrors these shapes by hand
|
|
6
|
+
* (worker/internal/runtime/supervisor.go, worker/internal/api/server.go,
|
|
7
|
+
* worker/internal/lease/lease.go); the shared fixtures under
|
|
8
|
+
* `worker/contract-fixtures/` pin both sides — the TS test
|
|
9
|
+
* (test/gondolin-contract.test.ts) checks each fixture against the schemas
|
|
10
|
+
* here, and the Go contract tests round-trip the same files through the Go
|
|
11
|
+
* structs. Add a field on one side only and one of those tests fails.
|
|
12
|
+
*
|
|
13
|
+
* Secrets never travel through these shapes: vault env rides per-exec over
|
|
14
|
+
* the session IPC socket, and credential files are shipped by content only
|
|
15
|
+
* on the authenticated ensure request.
|
|
16
|
+
*/
|
|
17
|
+
const GondolinMountSchema = Type.Object({
|
|
18
|
+
source: Type.String(),
|
|
19
|
+
target: Type.String(),
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* Everything a worker process needs to host one Gondolin VM. Serialized as a
|
|
23
|
+
* single JSON argv entry by both spawners (the local TS worker client and
|
|
24
|
+
* the Go daemon's supervisor).
|
|
25
|
+
*/
|
|
26
|
+
export const GondolinWorkerConfigSchema = Type.Object({
|
|
27
|
+
/** mikan session key the runtime belongs to. */
|
|
28
|
+
instanceId: Type.String(),
|
|
29
|
+
/** Resolved guest image asset directory. */
|
|
30
|
+
image: Type.Optional(Type.String()),
|
|
31
|
+
/** Image selector resolved inside the worker (remote workers, where the
|
|
32
|
+
* spawning host has no image store). Ignored when `image` is set. */
|
|
33
|
+
imageSelector: Type.Optional(Type.String()),
|
|
34
|
+
mounts: Type.Array(GondolinMountSchema),
|
|
35
|
+
cpus: Type.Optional(Type.Number()),
|
|
36
|
+
memory: Type.Optional(Type.String()),
|
|
37
|
+
/** Desired-runtime fingerprint recorded for adoption checks. */
|
|
38
|
+
fingerprint: Type.String(),
|
|
39
|
+
/** Runtime inventory directory (shared with the spawning mikan). */
|
|
40
|
+
inventoryDir: Type.String(),
|
|
41
|
+
/** Self-stop when the mikan heartbeat file is older than this. 0 disables. */
|
|
42
|
+
heartbeatStaleMs: Type.Number(),
|
|
43
|
+
});
|
|
44
|
+
/** First stdout line of a worker, consumed by both spawners. */
|
|
45
|
+
export const GondolinWorkerHandshakeSchema = Type.Object({
|
|
46
|
+
ready: Type.Boolean(),
|
|
47
|
+
error: Type.Optional(Type.String()),
|
|
48
|
+
sessionId: Type.Optional(Type.String()),
|
|
49
|
+
socketPath: Type.Optional(Type.String()),
|
|
50
|
+
workerPid: Type.Optional(Type.Number()),
|
|
51
|
+
runnerPid: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* One `<sessionId>.json` inventory record, written by the worker process and
|
|
55
|
+
* read back by the TS inventory and the Go daemon's rediscovery.
|
|
56
|
+
*/
|
|
57
|
+
export const GondolinRuntimeRecordSchema = Type.Object({
|
|
58
|
+
/** Gondolin session id (`vm.id`); also names the record file. */
|
|
59
|
+
sessionId: Type.String(),
|
|
60
|
+
/** mikan session key (vault key) the runtime was created for. */
|
|
61
|
+
instanceId: Type.String(),
|
|
62
|
+
/** Process that owns the runtime (the worker process). */
|
|
63
|
+
ownerPid: Type.Number(),
|
|
64
|
+
/** Host pid of the active VM runner process (QEMU/krun), if started. */
|
|
65
|
+
runnerPid: Type.Union([Type.Number(), Type.Null()]),
|
|
66
|
+
createdAt: Type.String(),
|
|
67
|
+
/** Session IPC socket for adopting the runtime from a new mikan process. */
|
|
68
|
+
socketPath: Type.Optional(Type.String()),
|
|
69
|
+
/** Desired-runtime fingerprint the VM was created from. */
|
|
70
|
+
fingerprint: Type.Optional(Type.String()),
|
|
71
|
+
});
|
|
72
|
+
export function isGondolinRuntimeRecord(value) {
|
|
73
|
+
return (isRecord(value) &&
|
|
74
|
+
typeof value.sessionId === "string" &&
|
|
75
|
+
value.sessionId.length > 0 &&
|
|
76
|
+
typeof value.instanceId === "string" &&
|
|
77
|
+
typeof value.ownerPid === "number" &&
|
|
78
|
+
(value.runnerPid === null || typeof value.runnerPid === "number") &&
|
|
79
|
+
typeof value.createdAt === "string" &&
|
|
80
|
+
(value.socketPath === undefined || typeof value.socketPath === "string") &&
|
|
81
|
+
(value.fingerprint === undefined || typeof value.fingerprint === "string"));
|
|
82
|
+
}
|
|
83
|
+
/** A vault credential shipped by content on the ensure request (never a shared-fs mount). */
|
|
84
|
+
const GondolinCredentialFileSchema = Type.Object({
|
|
85
|
+
target: Type.String(),
|
|
86
|
+
contentBase64: Type.String(),
|
|
87
|
+
});
|
|
88
|
+
/** POST /v1/runtimes body sent to the remote worker daemon. */
|
|
89
|
+
export const GondolinEnsureRuntimeRequestSchema = Type.Object({
|
|
90
|
+
instanceId: Type.String(),
|
|
91
|
+
imageSelector: Type.String(),
|
|
92
|
+
mounts: Type.Array(GondolinMountSchema),
|
|
93
|
+
credentialFiles: Type.Array(GondolinCredentialFileSchema),
|
|
94
|
+
/** Fractional cgroup CPU limit as a string; "" when unset. */
|
|
95
|
+
cpus: Type.String(),
|
|
96
|
+
memory: Type.String(),
|
|
97
|
+
fingerprint: Type.String(),
|
|
98
|
+
/** Accepted by the daemon; the TS client currently leaves it unset (0). */
|
|
99
|
+
heartbeatStaleMs: Type.Optional(Type.Number()),
|
|
100
|
+
});
|
|
101
|
+
/**
|
|
102
|
+
* One runtime as reported by the daemon (ensure response, GET, and the
|
|
103
|
+
* elements of the list response). The daemon's socketPath never crosses the
|
|
104
|
+
* wire — sessions reach the runtime through the tunnel, not the socket.
|
|
105
|
+
*/
|
|
106
|
+
export const GondolinRemoteRuntimeSchema = Type.Object({
|
|
107
|
+
sessionId: Type.String(),
|
|
108
|
+
instanceId: Type.String(),
|
|
109
|
+
fingerprint: Type.String(),
|
|
110
|
+
workerPid: Type.Number(),
|
|
111
|
+
runnerPid: Type.Number(),
|
|
112
|
+
epoch: Type.Number(),
|
|
113
|
+
adopted: Type.Boolean(),
|
|
114
|
+
});
|
|
115
|
+
/** POST /v1/leases response: a fenced, epoch-monotonic per-instance lease. */
|
|
116
|
+
export const GondolinLeaseGrantSchema = Type.Object({
|
|
117
|
+
id: Type.String(),
|
|
118
|
+
instanceId: Type.String(),
|
|
119
|
+
epoch: Type.Number(),
|
|
120
|
+
/** RFC 3339 timestamp (Go time.Time marshalling). */
|
|
121
|
+
expiresAt: Type.String(),
|
|
122
|
+
});
|
|
123
|
+
/** Parses the daemon-to-client session framing: u8 type + u32be length + payload. */
|
|
124
|
+
export function createSessionFrameParser(callbacks) {
|
|
125
|
+
let buffer = Buffer.alloc(0);
|
|
126
|
+
return (chunk) => {
|
|
127
|
+
buffer = Buffer.concat([buffer, chunk]);
|
|
128
|
+
while (buffer.length >= 5) {
|
|
129
|
+
const type = buffer.readUInt8(0);
|
|
130
|
+
const length = buffer.readUInt32BE(1);
|
|
131
|
+
if (buffer.length < 5 + length)
|
|
132
|
+
return;
|
|
133
|
+
const payload = buffer.subarray(5, 5 + length);
|
|
134
|
+
buffer = buffer.subarray(5 + length);
|
|
135
|
+
if (type === 1) {
|
|
136
|
+
callbacks.onBinary(Buffer.from(payload));
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
callbacks.onJson(JSON.parse(payload.toString("utf8")));
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// ignore malformed frames
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** Encodes a client-to-daemon session message: u32be length + JSON. */
|
|
149
|
+
export function encodeSessionMessage(message) {
|
|
150
|
+
const payload = Buffer.from(JSON.stringify(message));
|
|
151
|
+
const header = Buffer.alloc(4);
|
|
152
|
+
header.writeUInt32BE(payload.length, 0);
|
|
153
|
+
return Buffer.concat([header, payload]);
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=gondolin-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gondolin-contract.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-contract.ts"],"names":[],"mappings":"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","sourcesContent":["import { Type, type Static } from \"@sinclair/typebox\";\nimport { isRecord } from \"../utils/file-guards.js\";\n\n/**\n * Single home for every wire shape that crosses the mikan ⇄ gondolin-worker\n * boundary. The Go daemon mirrors these shapes by hand\n * (worker/internal/runtime/supervisor.go, worker/internal/api/server.go,\n * worker/internal/lease/lease.go); the shared fixtures under\n * `worker/contract-fixtures/` pin both sides — the TS test\n * (test/gondolin-contract.test.ts) checks each fixture against the schemas\n * here, and the Go contract tests round-trip the same files through the Go\n * structs. Add a field on one side only and one of those tests fails.\n *\n * Secrets never travel through these shapes: vault env rides per-exec over\n * the session IPC socket, and credential files are shipped by content only\n * on the authenticated ensure request.\n */\n\nconst GondolinMountSchema = Type.Object({\n source: Type.String(),\n target: Type.String(),\n});\n\n/**\n * Everything a worker process needs to host one Gondolin VM. Serialized as a\n * single JSON argv entry by both spawners (the local TS worker client and\n * the Go daemon's supervisor).\n */\nexport const GondolinWorkerConfigSchema = Type.Object({\n /** mikan session key the runtime belongs to. */\n instanceId: Type.String(),\n /** Resolved guest image asset directory. */\n image: Type.Optional(Type.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: Type.Optional(Type.String()),\n mounts: Type.Array(GondolinMountSchema),\n cpus: Type.Optional(Type.Number()),\n memory: Type.Optional(Type.String()),\n /** Desired-runtime fingerprint recorded for adoption checks. */\n fingerprint: Type.String(),\n /** Runtime inventory directory (shared with the spawning mikan). */\n inventoryDir: Type.String(),\n /** Self-stop when the mikan heartbeat file is older than this. 0 disables. */\n heartbeatStaleMs: Type.Number(),\n});\n\nexport type GondolinWorkerConfig = Static<typeof GondolinWorkerConfigSchema>;\n\n/** First stdout line of a worker, consumed by both spawners. */\nexport const GondolinWorkerHandshakeSchema = Type.Object({\n ready: Type.Boolean(),\n error: Type.Optional(Type.String()),\n sessionId: Type.Optional(Type.String()),\n socketPath: Type.Optional(Type.String()),\n workerPid: Type.Optional(Type.Number()),\n runnerPid: Type.Optional(Type.Union([Type.Number(), Type.Null()])),\n});\n\nexport type GondolinWorkerHandshake = Static<typeof GondolinWorkerHandshakeSchema>;\n\n/**\n * One `<sessionId>.json` inventory record, written by the worker process and\n * read back by the TS inventory and the Go daemon's rediscovery.\n */\nexport const GondolinRuntimeRecordSchema = Type.Object({\n /** Gondolin session id (`vm.id`); also names the record file. */\n sessionId: Type.String(),\n /** mikan session key (vault key) the runtime was created for. */\n instanceId: Type.String(),\n /** Process that owns the runtime (the worker process). */\n ownerPid: Type.Number(),\n /** Host pid of the active VM runner process (QEMU/krun), if started. */\n runnerPid: Type.Union([Type.Number(), Type.Null()]),\n createdAt: Type.String(),\n /** Session IPC socket for adopting the runtime from a new mikan process. */\n socketPath: Type.Optional(Type.String()),\n /** Desired-runtime fingerprint the VM was created from. */\n fingerprint: Type.Optional(Type.String()),\n});\n\nexport type GondolinRuntimeRecord = Static<typeof GondolinRuntimeRecordSchema>;\n\nexport function isGondolinRuntimeRecord(value: unknown): value is GondolinRuntimeRecord {\n return (\n isRecord(value) &&\n typeof value.sessionId === \"string\" &&\n value.sessionId.length > 0 &&\n typeof value.instanceId === \"string\" &&\n typeof value.ownerPid === \"number\" &&\n (value.runnerPid === null || typeof value.runnerPid === \"number\") &&\n typeof value.createdAt === \"string\" &&\n (value.socketPath === undefined || typeof value.socketPath === \"string\") &&\n (value.fingerprint === undefined || typeof value.fingerprint === \"string\")\n );\n}\n\n/** A vault credential shipped by content on the ensure request (never a shared-fs mount). */\nconst GondolinCredentialFileSchema = Type.Object({\n target: Type.String(),\n contentBase64: Type.String(),\n});\n\nexport type GondolinCredentialFile = Static<typeof GondolinCredentialFileSchema>;\n\n/** POST /v1/runtimes body sent to the remote worker daemon. */\nexport const GondolinEnsureRuntimeRequestSchema = Type.Object({\n instanceId: Type.String(),\n imageSelector: Type.String(),\n mounts: Type.Array(GondolinMountSchema),\n credentialFiles: Type.Array(GondolinCredentialFileSchema),\n /** Fractional cgroup CPU limit as a string; \"\" when unset. */\n cpus: Type.String(),\n memory: Type.String(),\n fingerprint: Type.String(),\n /** Accepted by the daemon; the TS client currently leaves it unset (0). */\n heartbeatStaleMs: Type.Optional(Type.Number()),\n});\n\nexport type GondolinEnsureRuntimeRequest = Static<typeof GondolinEnsureRuntimeRequestSchema>;\n\n/**\n * One runtime as reported by the daemon (ensure response, GET, and the\n * elements of the list response). The daemon's socketPath never crosses the\n * wire — sessions reach the runtime through the tunnel, not the socket.\n */\nexport const GondolinRemoteRuntimeSchema = Type.Object({\n sessionId: Type.String(),\n instanceId: Type.String(),\n fingerprint: Type.String(),\n workerPid: Type.Number(),\n runnerPid: Type.Number(),\n epoch: Type.Number(),\n adopted: Type.Boolean(),\n});\n\nexport type GondolinRemoteRuntime = Static<typeof GondolinRemoteRuntimeSchema>;\n\n/** POST /v1/leases response: a fenced, epoch-monotonic per-instance lease. */\nexport const GondolinLeaseGrantSchema = Type.Object({\n id: Type.String(),\n instanceId: Type.String(),\n epoch: Type.Number(),\n /** RFC 3339 timestamp (Go time.Time marshalling). */\n expiresAt: Type.String(),\n});\n\n// ── session tunnel framing ────────────────────────────────────────────────────\n// The session protocol itself is TS⇄TS (worker session server ⇄ client); the\n// Go daemon splices it byte-for-byte through the upgraded tunnel. The framing\n// still lives here because it is part of the boundary a transport must carry.\n\n/** Structural shape of the JSON messages on the session protocol. */\nexport interface SessionFrameCallbacks {\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}\n\n/** Parses the daemon-to-client session framing: u8 type + u32be length + payload. */\nexport function createSessionFrameParser(\n callbacks: SessionFrameCallbacks,\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"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { GondolinRemoteSettings, GondolinRemoteWorkerSettings } from "../types.js";
|
|
2
2
|
import { type GondolinPlacementStore } from "./gondolin-placement.js";
|
|
3
3
|
import { type GondolinRemoteOverrides } from "./gondolin-remote.js";
|
|
4
|
-
import {
|
|
4
|
+
import type { GondolinRuntimeHandle, GondolinRuntimeSpec, GondolinRuntimeTransport } from "./gondolin-worker-client.js";
|
|
5
5
|
import type { ExecResult } from "./types.js";
|
|
6
6
|
/** The connection surface the fleet needs from each worker. */
|
|
7
7
|
export interface GondolinWorkerConnection {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gondolin-fleet.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin-fleet.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAExF,OAAO,EAAsB,KAAK,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAC1F,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"gondolin-fleet.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin-fleet.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import * as log from "../log.js";
|
|
|
2
2
|
import { SandboxError } from "./errors.js";
|
|
3
3
|
import { gondolinPlacements } from "./gondolin-placement.js";
|
|
4
4
|
import { GondolinRemoteConnection, GondolinWorkerUnreachableError, } from "./gondolin-remote.js";
|
|
5
|
-
import { GondolinRuntimeGoneError
|
|
5
|
+
import { GondolinRuntimeGoneError } from "./gondolin-recovery.js";
|
|
6
6
|
const LEASE_TTL_SECONDS = 300;
|
|
7
7
|
const QUEUE_WAIT_SECONDS = 60;
|
|
8
8
|
const QUEUE_POLL_MS = 2000;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gondolin-fleet.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-fleet.ts"],"names":[],"mappings":"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","sourcesContent":["import * as log from \"../log.js\";\nimport type { GondolinRemoteSettings, GondolinRemoteWorkerSettings } from \"../types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { gondolinPlacements, type GondolinPlacementStore } from \"./gondolin-placement.js\";\nimport {\n GondolinRemoteConnection,\n GondolinWorkerUnreachableError,\n type GondolinRemoteOverrides,\n} from \"./gondolin-remote.js\";\nimport {\n GondolinRuntimeGoneError,\n type GondolinRuntimeHandle,\n type GondolinRuntimeSpec,\n type GondolinRuntimeTransport,\n} from \"./gondolin-worker-client.js\";\nimport type { ExecResult } from \"./types.js\";\n\nconst LEASE_TTL_SECONDS = 300;\nconst QUEUE_WAIT_SECONDS = 60;\nconst QUEUE_POLL_MS = 2000;\n\n/** The connection surface the fleet needs from each worker. */\nexport interface GondolinWorkerConnection {\n health(): Promise<Record<string, unknown>>;\n ensure(instanceId: string, spec: GondolinRuntimeSpec): Promise<GondolinRuntimeHandle>;\n stop(\n handle: Pick<GondolinRuntimeHandle, \"workerPid\" | \"sessionId\" | \"instanceId\">,\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 listRuntimes(): Promise<Array<{ sessionId: string; instanceId: string }>>;\n dispose(): void;\n}\n\ninterface FleetWorker {\n name: string;\n settings: GondolinRemoteWorkerSettings;\n connection: GondolinWorkerConnection;\n /** Non-empty when the worker's shared workspace failed its usability probe. */\n workspaceError?: string;\n}\n\ninterface GondolinFleetOverrides {\n createConnection?: (\n worker: GondolinRemoteWorkerSettings,\n connectionOverrides: GondolinRemoteOverrides,\n ) => GondolinWorkerConnection;\n placements?: GondolinPlacementStore;\n connectionOverrides?: GondolinRemoteOverrides;\n now?: () => number;\n queuePollMs?: number;\n sleep?: (ms: number) => Promise<void>;\n}\n\nfunction normalizeWorkers(settings: GondolinRemoteSettings): GondolinRemoteWorkerSettings[] {\n const inline = settings.workers ?? [{ url: settings.url as string }];\n return inline.map((worker) => ({\n ...worker,\n name: worker.name ?? worker.url,\n caFile: worker.caFile ?? settings.caFile,\n certFile: worker.certFile ?? settings.certFile,\n keyFile: worker.keyFile ?? settings.keyFile,\n workspaceRoot: worker.workspaceRoot ?? settings.workspaceRoot,\n maxRuntimes: worker.maxRuntimes ?? settings.maxRuntimes,\n }));\n}\n\n/**\n * Places each conversation's runtime on one worker of the fleet and keeps it\n * there (sticky placement). Failover to another worker happens only when the\n * placed worker is unreachable AND its lease fencing watermark has passed —\n * the point after which the old worker's janitor has provably stopped the\n * runtime, so shared storage never sees two writers.\n */\nclass GondolinFleetClient implements GondolinRuntimeTransport {\n private settings?: GondolinRemoteSettings;\n private workers = new Map<string, FleetWorker>();\n private connectionOverrides: GondolinRemoteOverrides = {};\n private placements: GondolinPlacementStore = gondolinPlacements;\n private leaseTtlMs = LEASE_TTL_SECONDS * 1000;\n private queueWaitMs = QUEUE_WAIT_SECONDS * 1000;\n private queuePollMs = QUEUE_POLL_MS;\n private now: () => number = Date.now;\n private sleep: (ms: number) => Promise<void> = (ms) =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n configure(settings?: GondolinRemoteSettings, overrides?: GondolinFleetOverrides): void {\n for (const worker of this.workers.values()) worker.connection.dispose();\n this.workers.clear();\n this.settings = settings;\n this.placements = overrides?.placements ?? gondolinPlacements;\n this.now = overrides?.now ?? Date.now;\n this.sleep = overrides?.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n this.queuePollMs = overrides?.queuePollMs ?? QUEUE_POLL_MS;\n this.connectionOverrides = overrides?.connectionOverrides ?? {};\n this.leaseTtlMs = (overrides?.connectionOverrides?.leaseTtlSeconds ?? LEASE_TTL_SECONDS) * 1000;\n this.queueWaitMs = (settings?.queueWaitSeconds ?? QUEUE_WAIT_SECONDS) * 1000;\n if (!settings) return;\n if (!settings.workers?.length && !settings.url) {\n return; // not configured; validate() reports the actionable error\n }\n const createConnection =\n overrides?.createConnection ??\n ((worker: GondolinRemoteWorkerSettings, connectionOverrides: GondolinRemoteOverrides) =>\n new GondolinRemoteConnection(worker, connectionOverrides));\n for (const worker of normalizeWorkers(settings)) {\n const name = worker.name as string;\n const connection = createConnection(worker, {\n ...overrides?.connectionOverrides,\n onLeaseActivity: (instanceId, expiresAtMs) => {\n if (this.placements.get(instanceId)?.worker === name) {\n this.placements.touch(instanceId, expiresAtMs);\n }\n },\n });\n this.workers.set(name, { name, settings: worker, connection });\n }\n }\n\n isConfigured(): boolean {\n return this.workers.size > 0;\n }\n\n /** Names of statically-configured workers with no workspaceRoot (startup guard). */\n workersMissingWorkspaceRoot(): string[] {\n return Array.from(this.workers.values())\n .filter((worker) => !worker.settings.workspaceRoot)\n .map((worker) => worker.name);\n }\n\n /**\n * Attach a dial-home worker at runtime (the gateway calls this when a\n * worker registers). The factory receives the fleet's connection overrides\n * plus the lease-watermark callback, mirroring configure()'s wiring.\n */\n attachWorker(\n settings: GondolinRemoteWorkerSettings,\n createConnection: (overrides: GondolinRemoteOverrides) => GondolinWorkerConnection,\n ): void {\n const name = settings.name ?? settings.url;\n this.workers.get(name)?.connection.dispose();\n const connection = createConnection({\n ...this.connectionOverrides,\n onLeaseActivity: (instanceId, expiresAtMs) => {\n if (this.placements.get(instanceId)?.worker === name) {\n this.placements.touch(instanceId, expiresAtMs);\n }\n },\n });\n this.workers.set(name, { name, settings: { ...settings, name }, connection });\n }\n\n /**\n * Record a worker's shared-workspace health (the gateway pushes this from\n * register and ping frames). A degraded worker keeps its runtimes and its\n * connection, but new placements skip it and runtime operations fail fast\n * with the probe's diagnosis instead of hanging on a dead mount.\n */\n setWorkspaceHealth(name: string, error?: string): void {\n const worker = this.workers.get(name);\n if (!worker) return;\n worker.workspaceError = error || undefined;\n }\n\n /**\n * Detach a dial-home worker (disconnect). Placements survive: their lease\n * watermarks keep fencing failover until expiry, exactly as for an\n * unreachable static worker.\n */\n detachWorker(name: string): void {\n const worker = this.workers.get(name);\n if (!worker) return;\n worker.connection.dispose();\n this.workers.delete(name);\n }\n\n imageSelector(): string | undefined {\n return this.settings?.imageSelector;\n }\n\n /** At least one worker must answer for the sandbox mode to come up. */\n async health(): Promise<Array<{ worker: string; health?: Record<string, unknown> }>> {\n const report = await Promise.all(\n Array.from(this.workers.values()).map(async (worker) => {\n try {\n return { worker: worker.name, health: await worker.connection.health() };\n } catch {\n return { worker: worker.name };\n }\n }),\n );\n if (report.length > 0 && !report.some((entry) => entry.health)) {\n throw new SandboxError(\"Error: no gondolin remote worker is reachable\");\n }\n return report;\n }\n\n async ensure(instanceId: string, spec: GondolinRuntimeSpec): Promise<GondolinRuntimeHandle> {\n const placed = this.placements.get(instanceId);\n if (placed) {\n const worker = this.workers.get(placed.worker);\n if (worker) {\n try {\n return await this.ensureOn(worker, instanceId, spec);\n } catch (err) {\n if (!(err instanceof GondolinWorkerUnreachableError)) throw err;\n this.assertFailoverAllowed(instanceId, placed.worker, placed.leaseExpiresAt, err);\n }\n } else {\n this.assertFailoverAllowed(\n instanceId,\n placed.worker,\n placed.leaseExpiresAt,\n new GondolinWorkerUnreachableError(\"worker is no longer configured\"),\n );\n }\n log.logWarning(\n `Gondolin worker '${placed.worker}' lost runtime for '${instanceId}'; failing over`,\n );\n }\n const worker = await this.selectWorker();\n return this.ensureOn(worker, instanceId, spec);\n }\n\n async stop(\n handle: Pick<GondolinRuntimeHandle, \"workerPid\" | \"sessionId\" | \"instanceId\" | \"workerName\">,\n ): Promise<void> {\n const worker = this.workerFor(handle.workerName);\n if (!worker) {\n // the worker left the fleet config; its janitor stops the runtime when\n // the lease expires, and the record keeps fencing until then\n return;\n }\n try {\n await worker.connection.stop(handle);\n this.placements.delete(handle.instanceId);\n } catch (err) {\n if (err instanceof GondolinWorkerUnreachableError) {\n // cannot confirm the stop; keep the placement so its watermark still\n // fences any failover, and let the daemon's expiry janitor finish it\n log.logWarning(\n `Gondolin worker '${worker.name}' unreachable while stopping '${handle.instanceId}'; leaving it to lease expiry`,\n );\n return;\n }\n throw err;\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 worker = this.workerFor(handle.workerName);\n if (!worker) throw new GondolinRuntimeGoneError(\"worker is no longer configured\");\n this.assertWorkspaceUsable(worker);\n log.logInfo(`Gondolin execution '${handle.instanceId}' routed to worker '${worker.name}'`);\n return worker.connection.exec(handle, command, options);\n }\n\n async isRuntimeAlive(handle: GondolinRuntimeHandle): Promise<boolean> {\n const worker = this.workerFor(handle.workerName);\n if (!worker) return false;\n return worker.connection.isRuntimeAlive(handle);\n }\n\n /**\n * Fleet reconciliation: a runtime whose conversation is placed on a\n * different worker is a stray from a superseded placement — stop it. A\n * runtime with no placement at all (crash between daemon ensure and the\n * placement write) is adopted instead of killed.\n */\n async reconcile(): Promise<void> {\n for (const worker of this.workers.values()) {\n let runtimes: Array<{ sessionId: string; instanceId: string }>;\n try {\n runtimes = await worker.connection.listRuntimes();\n } catch {\n continue; // unreachable workers reconcile on a later sweep\n }\n for (const runtime of runtimes) {\n const record = this.placements.get(runtime.instanceId);\n if (!record) {\n log.logInfo(\n `Adopting unplaced Gondolin runtime '${runtime.instanceId}' on worker '${worker.name}'`,\n );\n this.placements.set(runtime.instanceId, worker.name, this.now() + this.leaseTtlMs);\n continue;\n }\n if (record.worker === worker.name) continue;\n log.logInfo(\n `Stopping stray Gondolin runtime '${runtime.instanceId}' on worker '${worker.name}' (placed on '${record.worker}')`,\n );\n try {\n await worker.connection.stop({\n sessionId: runtime.sessionId,\n instanceId: runtime.instanceId,\n workerPid: 0,\n });\n } catch (err) {\n log.logWarning(\n `Failed to stop stray runtime '${runtime.instanceId}' on '${worker.name}'`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n }\n\n private async ensureOn(\n worker: FleetWorker,\n instanceId: string,\n spec: GondolinRuntimeSpec,\n ): Promise<GondolinRuntimeHandle> {\n this.assertWorkspaceUsable(worker);\n const handle = await worker.connection.ensure(instanceId, spec);\n this.placements.set(instanceId, worker.name, this.now() + this.leaseTtlMs);\n return { ...handle, workerName: worker.name };\n }\n\n /**\n * Fail fast — with the probe's diagnosis — instead of dispatching work that\n * would hang on a dead shared-workspace mount. Deliberately NOT a\n * GondolinWorkerUnreachableError: a broken mount must not trigger failover,\n * because the workspace is shared and likely broken for every worker.\n */\n private assertWorkspaceUsable(worker: FleetWorker): void {\n if (!worker.workspaceError) return;\n throw new SandboxError(\n `Error: Gondolin worker '${worker.name}' has an unusable shared workspace: ` +\n `${worker.workspaceError} (fix the mount on the worker; health refreshes with its next heartbeat)`,\n );\n }\n\n private assertFailoverAllowed(\n instanceId: string,\n workerName: string,\n leaseExpiresAt: number,\n cause: Error,\n ): void {\n const remainingMs = leaseExpiresAt - this.now();\n if (remainingMs <= 0) return;\n // Failing over earlier could put a second writable VM on the same shared\n // workspace: the old worker may be alive behind a partition, and its\n // daemon only stops the runtime once the lease expires.\n throw new SandboxError(\n `Error: Gondolin worker '${workerName}' is unreachable (${cause.message}); ` +\n `retrying '${instanceId}' elsewhere in ${Math.ceil(remainingMs / 1000)}s once its lease has expired`,\n );\n }\n\n private async selectWorker(): Promise<FleetWorker> {\n const deadline = this.now() + this.queueWaitMs;\n for (;;) {\n let best: { worker: FleetWorker; load: number } | undefined;\n let reachable = 0;\n let draining = 0;\n let full = 0;\n const degraded: string[] = [];\n for (const worker of this.workers.values()) {\n if (worker.settings.draining) {\n draining += 1;\n continue;\n }\n if (worker.workspaceError) {\n degraded.push(`'${worker.name}': ${worker.workspaceError}`);\n continue;\n }\n let active: number;\n let workspaceError: string | undefined;\n try {\n const health = await worker.connection.health();\n active = typeof health.activeRuntimes === \"number\" ? health.activeRuntimes : 0;\n workspaceError =\n typeof health.workspaceError === \"string\" && health.workspaceError\n ? health.workspaceError\n : undefined;\n } catch {\n continue;\n }\n reachable += 1;\n if (workspaceError) {\n // static workers report probe results via /v1/health only\n degraded.push(`'${worker.name}': ${workspaceError}`);\n continue;\n }\n const cap = worker.settings.maxRuntimes;\n if (cap !== undefined && active >= cap) {\n full += 1;\n continue;\n }\n const load = cap !== undefined ? active / cap : active / Number.MAX_SAFE_INTEGER;\n if (!best || load < best.load) best = { worker, load };\n }\n if (best) return best.worker;\n if (reachable === 0 && draining === 0 && degraded.length === 0) {\n throw new SandboxError(\"Error: no gondolin remote worker is reachable\");\n }\n if (full === 0) {\n const detail = degraded.length > 0 ? ` — degraded: ${degraded.join(\"; \")}` : \"\";\n throw new SandboxError(\n `Error: every gondolin remote worker is draining, degraded, or unreachable${detail}`,\n );\n }\n if (this.now() >= deadline) {\n throw new SandboxError(\n `Error: all gondolin remote workers are at capacity (waited ${Math.round(this.queueWaitMs / 1000)}s)`,\n );\n }\n await this.sleep(this.queuePollMs);\n }\n }\n\n private workerFor(workerName: string | undefined): FleetWorker | undefined {\n if (!workerName) return undefined;\n return this.workers.get(workerName);\n }\n}\n\nexport const gondolinFleet = new GondolinFleetClient();\n"]}
|
|
1
|
+
{"version":3,"file":"gondolin-fleet.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-fleet.ts"],"names":[],"mappings":"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","sourcesContent":["import * as log from \"../log.js\";\nimport type { GondolinRemoteSettings, GondolinRemoteWorkerSettings } from \"../types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { gondolinPlacements, type GondolinPlacementStore } from \"./gondolin-placement.js\";\nimport {\n GondolinRemoteConnection,\n GondolinWorkerUnreachableError,\n type GondolinRemoteOverrides,\n} from \"./gondolin-remote.js\";\nimport { GondolinRuntimeGoneError } from \"./gondolin-recovery.js\";\nimport type {\n GondolinRuntimeHandle,\n GondolinRuntimeSpec,\n GondolinRuntimeTransport,\n} from \"./gondolin-worker-client.js\";\nimport type { ExecResult } from \"./types.js\";\n\nconst LEASE_TTL_SECONDS = 300;\nconst QUEUE_WAIT_SECONDS = 60;\nconst QUEUE_POLL_MS = 2000;\n\n/** The connection surface the fleet needs from each worker. */\nexport interface GondolinWorkerConnection {\n health(): Promise<Record<string, unknown>>;\n ensure(instanceId: string, spec: GondolinRuntimeSpec): Promise<GondolinRuntimeHandle>;\n stop(\n handle: Pick<GondolinRuntimeHandle, \"workerPid\" | \"sessionId\" | \"instanceId\">,\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 listRuntimes(): Promise<Array<{ sessionId: string; instanceId: string }>>;\n dispose(): void;\n}\n\ninterface FleetWorker {\n name: string;\n settings: GondolinRemoteWorkerSettings;\n connection: GondolinWorkerConnection;\n /** Non-empty when the worker's shared workspace failed its usability probe. */\n workspaceError?: string;\n}\n\ninterface GondolinFleetOverrides {\n createConnection?: (\n worker: GondolinRemoteWorkerSettings,\n connectionOverrides: GondolinRemoteOverrides,\n ) => GondolinWorkerConnection;\n placements?: GondolinPlacementStore;\n connectionOverrides?: GondolinRemoteOverrides;\n now?: () => number;\n queuePollMs?: number;\n sleep?: (ms: number) => Promise<void>;\n}\n\nfunction normalizeWorkers(settings: GondolinRemoteSettings): GondolinRemoteWorkerSettings[] {\n const inline = settings.workers ?? [{ url: settings.url as string }];\n return inline.map((worker) => ({\n ...worker,\n name: worker.name ?? worker.url,\n caFile: worker.caFile ?? settings.caFile,\n certFile: worker.certFile ?? settings.certFile,\n keyFile: worker.keyFile ?? settings.keyFile,\n workspaceRoot: worker.workspaceRoot ?? settings.workspaceRoot,\n maxRuntimes: worker.maxRuntimes ?? settings.maxRuntimes,\n }));\n}\n\n/**\n * Places each conversation's runtime on one worker of the fleet and keeps it\n * there (sticky placement). Failover to another worker happens only when the\n * placed worker is unreachable AND its lease fencing watermark has passed —\n * the point after which the old worker's janitor has provably stopped the\n * runtime, so shared storage never sees two writers.\n */\nclass GondolinFleetClient implements GondolinRuntimeTransport {\n private settings?: GondolinRemoteSettings;\n private workers = new Map<string, FleetWorker>();\n private connectionOverrides: GondolinRemoteOverrides = {};\n private placements: GondolinPlacementStore = gondolinPlacements;\n private leaseTtlMs = LEASE_TTL_SECONDS * 1000;\n private queueWaitMs = QUEUE_WAIT_SECONDS * 1000;\n private queuePollMs = QUEUE_POLL_MS;\n private now: () => number = Date.now;\n private sleep: (ms: number) => Promise<void> = (ms) =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n configure(settings?: GondolinRemoteSettings, overrides?: GondolinFleetOverrides): void {\n for (const worker of this.workers.values()) worker.connection.dispose();\n this.workers.clear();\n this.settings = settings;\n this.placements = overrides?.placements ?? gondolinPlacements;\n this.now = overrides?.now ?? Date.now;\n this.sleep = overrides?.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n this.queuePollMs = overrides?.queuePollMs ?? QUEUE_POLL_MS;\n this.connectionOverrides = overrides?.connectionOverrides ?? {};\n this.leaseTtlMs = (overrides?.connectionOverrides?.leaseTtlSeconds ?? LEASE_TTL_SECONDS) * 1000;\n this.queueWaitMs = (settings?.queueWaitSeconds ?? QUEUE_WAIT_SECONDS) * 1000;\n if (!settings) return;\n if (!settings.workers?.length && !settings.url) {\n return; // not configured; validate() reports the actionable error\n }\n const createConnection =\n overrides?.createConnection ??\n ((worker: GondolinRemoteWorkerSettings, connectionOverrides: GondolinRemoteOverrides) =>\n new GondolinRemoteConnection(worker, connectionOverrides));\n for (const worker of normalizeWorkers(settings)) {\n const name = worker.name as string;\n const connection = createConnection(worker, {\n ...overrides?.connectionOverrides,\n onLeaseActivity: (instanceId, expiresAtMs) => {\n if (this.placements.get(instanceId)?.worker === name) {\n this.placements.touch(instanceId, expiresAtMs);\n }\n },\n });\n this.workers.set(name, { name, settings: worker, connection });\n }\n }\n\n isConfigured(): boolean {\n return this.workers.size > 0;\n }\n\n /** Names of statically-configured workers with no workspaceRoot (startup guard). */\n workersMissingWorkspaceRoot(): string[] {\n return Array.from(this.workers.values())\n .filter((worker) => !worker.settings.workspaceRoot)\n .map((worker) => worker.name);\n }\n\n /**\n * Attach a dial-home worker at runtime (the gateway calls this when a\n * worker registers). The factory receives the fleet's connection overrides\n * plus the lease-watermark callback, mirroring configure()'s wiring.\n */\n attachWorker(\n settings: GondolinRemoteWorkerSettings,\n createConnection: (overrides: GondolinRemoteOverrides) => GondolinWorkerConnection,\n ): void {\n const name = settings.name ?? settings.url;\n this.workers.get(name)?.connection.dispose();\n const connection = createConnection({\n ...this.connectionOverrides,\n onLeaseActivity: (instanceId, expiresAtMs) => {\n if (this.placements.get(instanceId)?.worker === name) {\n this.placements.touch(instanceId, expiresAtMs);\n }\n },\n });\n this.workers.set(name, { name, settings: { ...settings, name }, connection });\n }\n\n /**\n * Record a worker's shared-workspace health (the gateway pushes this from\n * register and ping frames). A degraded worker keeps its runtimes and its\n * connection, but new placements skip it and runtime operations fail fast\n * with the probe's diagnosis instead of hanging on a dead mount.\n */\n setWorkspaceHealth(name: string, error?: string): void {\n const worker = this.workers.get(name);\n if (!worker) return;\n worker.workspaceError = error || undefined;\n }\n\n /**\n * Detach a dial-home worker (disconnect). Placements survive: their lease\n * watermarks keep fencing failover until expiry, exactly as for an\n * unreachable static worker.\n */\n detachWorker(name: string): void {\n const worker = this.workers.get(name);\n if (!worker) return;\n worker.connection.dispose();\n this.workers.delete(name);\n }\n\n imageSelector(): string | undefined {\n return this.settings?.imageSelector;\n }\n\n /** At least one worker must answer for the sandbox mode to come up. */\n async health(): Promise<Array<{ worker: string; health?: Record<string, unknown> }>> {\n const report = await Promise.all(\n Array.from(this.workers.values()).map(async (worker) => {\n try {\n return { worker: worker.name, health: await worker.connection.health() };\n } catch {\n return { worker: worker.name };\n }\n }),\n );\n if (report.length > 0 && !report.some((entry) => entry.health)) {\n throw new SandboxError(\"Error: no gondolin remote worker is reachable\");\n }\n return report;\n }\n\n async ensure(instanceId: string, spec: GondolinRuntimeSpec): Promise<GondolinRuntimeHandle> {\n const placed = this.placements.get(instanceId);\n if (placed) {\n const worker = this.workers.get(placed.worker);\n if (worker) {\n try {\n return await this.ensureOn(worker, instanceId, spec);\n } catch (err) {\n if (!(err instanceof GondolinWorkerUnreachableError)) throw err;\n this.assertFailoverAllowed(instanceId, placed.worker, placed.leaseExpiresAt, err);\n }\n } else {\n this.assertFailoverAllowed(\n instanceId,\n placed.worker,\n placed.leaseExpiresAt,\n new GondolinWorkerUnreachableError(\"worker is no longer configured\"),\n );\n }\n log.logWarning(\n `Gondolin worker '${placed.worker}' lost runtime for '${instanceId}'; failing over`,\n );\n }\n const worker = await this.selectWorker();\n return this.ensureOn(worker, instanceId, spec);\n }\n\n async stop(\n handle: Pick<GondolinRuntimeHandle, \"workerPid\" | \"sessionId\" | \"instanceId\" | \"workerName\">,\n ): Promise<void> {\n const worker = this.workerFor(handle.workerName);\n if (!worker) {\n // the worker left the fleet config; its janitor stops the runtime when\n // the lease expires, and the record keeps fencing until then\n return;\n }\n try {\n await worker.connection.stop(handle);\n this.placements.delete(handle.instanceId);\n } catch (err) {\n if (err instanceof GondolinWorkerUnreachableError) {\n // cannot confirm the stop; keep the placement so its watermark still\n // fences any failover, and let the daemon's expiry janitor finish it\n log.logWarning(\n `Gondolin worker '${worker.name}' unreachable while stopping '${handle.instanceId}'; leaving it to lease expiry`,\n );\n return;\n }\n throw err;\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 worker = this.workerFor(handle.workerName);\n if (!worker) throw new GondolinRuntimeGoneError(\"worker is no longer configured\");\n this.assertWorkspaceUsable(worker);\n log.logInfo(`Gondolin execution '${handle.instanceId}' routed to worker '${worker.name}'`);\n return worker.connection.exec(handle, command, options);\n }\n\n async isRuntimeAlive(handle: GondolinRuntimeHandle): Promise<boolean> {\n const worker = this.workerFor(handle.workerName);\n if (!worker) return false;\n return worker.connection.isRuntimeAlive(handle);\n }\n\n /**\n * Fleet reconciliation: a runtime whose conversation is placed on a\n * different worker is a stray from a superseded placement — stop it. A\n * runtime with no placement at all (crash between daemon ensure and the\n * placement write) is adopted instead of killed.\n */\n async reconcile(): Promise<void> {\n for (const worker of this.workers.values()) {\n let runtimes: Array<{ sessionId: string; instanceId: string }>;\n try {\n runtimes = await worker.connection.listRuntimes();\n } catch {\n continue; // unreachable workers reconcile on a later sweep\n }\n for (const runtime of runtimes) {\n const record = this.placements.get(runtime.instanceId);\n if (!record) {\n log.logInfo(\n `Adopting unplaced Gondolin runtime '${runtime.instanceId}' on worker '${worker.name}'`,\n );\n this.placements.set(runtime.instanceId, worker.name, this.now() + this.leaseTtlMs);\n continue;\n }\n if (record.worker === worker.name) continue;\n log.logInfo(\n `Stopping stray Gondolin runtime '${runtime.instanceId}' on worker '${worker.name}' (placed on '${record.worker}')`,\n );\n try {\n await worker.connection.stop({\n sessionId: runtime.sessionId,\n instanceId: runtime.instanceId,\n workerPid: 0,\n });\n } catch (err) {\n log.logWarning(\n `Failed to stop stray runtime '${runtime.instanceId}' on '${worker.name}'`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n }\n\n private async ensureOn(\n worker: FleetWorker,\n instanceId: string,\n spec: GondolinRuntimeSpec,\n ): Promise<GondolinRuntimeHandle> {\n this.assertWorkspaceUsable(worker);\n const handle = await worker.connection.ensure(instanceId, spec);\n this.placements.set(instanceId, worker.name, this.now() + this.leaseTtlMs);\n return { ...handle, workerName: worker.name };\n }\n\n /**\n * Fail fast — with the probe's diagnosis — instead of dispatching work that\n * would hang on a dead shared-workspace mount. Deliberately NOT a\n * GondolinWorkerUnreachableError: a broken mount must not trigger failover,\n * because the workspace is shared and likely broken for every worker.\n */\n private assertWorkspaceUsable(worker: FleetWorker): void {\n if (!worker.workspaceError) return;\n throw new SandboxError(\n `Error: Gondolin worker '${worker.name}' has an unusable shared workspace: ` +\n `${worker.workspaceError} (fix the mount on the worker; health refreshes with its next heartbeat)`,\n );\n }\n\n private assertFailoverAllowed(\n instanceId: string,\n workerName: string,\n leaseExpiresAt: number,\n cause: Error,\n ): void {\n const remainingMs = leaseExpiresAt - this.now();\n if (remainingMs <= 0) return;\n // Failing over earlier could put a second writable VM on the same shared\n // workspace: the old worker may be alive behind a partition, and its\n // daemon only stops the runtime once the lease expires.\n throw new SandboxError(\n `Error: Gondolin worker '${workerName}' is unreachable (${cause.message}); ` +\n `retrying '${instanceId}' elsewhere in ${Math.ceil(remainingMs / 1000)}s once its lease has expired`,\n );\n }\n\n private async selectWorker(): Promise<FleetWorker> {\n const deadline = this.now() + this.queueWaitMs;\n for (;;) {\n let best: { worker: FleetWorker; load: number } | undefined;\n let reachable = 0;\n let draining = 0;\n let full = 0;\n const degraded: string[] = [];\n for (const worker of this.workers.values()) {\n if (worker.settings.draining) {\n draining += 1;\n continue;\n }\n if (worker.workspaceError) {\n degraded.push(`'${worker.name}': ${worker.workspaceError}`);\n continue;\n }\n let active: number;\n let workspaceError: string | undefined;\n try {\n const health = await worker.connection.health();\n active = typeof health.activeRuntimes === \"number\" ? health.activeRuntimes : 0;\n workspaceError =\n typeof health.workspaceError === \"string\" && health.workspaceError\n ? health.workspaceError\n : undefined;\n } catch {\n continue;\n }\n reachable += 1;\n if (workspaceError) {\n // static workers report probe results via /v1/health only\n degraded.push(`'${worker.name}': ${workspaceError}`);\n continue;\n }\n const cap = worker.settings.maxRuntimes;\n if (cap !== undefined && active >= cap) {\n full += 1;\n continue;\n }\n const load = cap !== undefined ? active / cap : active / Number.MAX_SAFE_INTEGER;\n if (!best || load < best.load) best = { worker, load };\n }\n if (best) return best.worker;\n if (reachable === 0 && draining === 0 && degraded.length === 0) {\n throw new SandboxError(\"Error: no gondolin remote worker is reachable\");\n }\n if (full === 0) {\n const detail = degraded.length > 0 ? ` — degraded: ${degraded.join(\"; \")}` : \"\";\n throw new SandboxError(\n `Error: every gondolin remote worker is draining, degraded, or unreachable${detail}`,\n );\n }\n if (this.now() >= deadline) {\n throw new SandboxError(\n `Error: all gondolin remote workers are at capacity (waited ${Math.round(this.queueWaitMs / 1000)}s)`,\n );\n }\n await this.sleep(this.queuePollMs);\n }\n }\n\n private workerFor(workerName: string | undefined): FleetWorker | undefined {\n if (!workerName) return undefined;\n return this.workers.get(workerName);\n }\n}\n\nexport const gondolinFleet = new GondolinFleetClient();\n"]}
|
|
@@ -69,6 +69,7 @@ declare class GondolinWorkerGateway {
|
|
|
69
69
|
private applyWorkspaceHealth;
|
|
70
70
|
/** RPC to a worker over its control channel (the reverse of HTTPS requests). */
|
|
71
71
|
private rpc;
|
|
72
|
+
private rejectPendingRpc;
|
|
72
73
|
/** Ask the worker to dial back a data connection for one session tunnel. */
|
|
73
74
|
private openTunnel;
|
|
74
75
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gondolin-gateway.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin-gateway.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAI/C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"gondolin-gateway.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin-gateway.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAI/C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAU3D,kFAAkF;AAClF,UAAU,0BAA0B;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,+EAA+E;IAC/E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAyED,UAAU,gBAAgB;IACxB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,CAAC;IAClE,yEAAyE;IACzE,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAMD;;;;;;GAMG;AACH,cAAM,qBAAqB;IACzB,OAAO,CAAC,QAAQ,CAAC,CAA0B;IAC3C,OAAO,CAAC,SAAS,CAAwB;IACzC,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAG3B;IAEJ,SAAS,CAAC,QAAQ,CAAC,EAAE,uBAAuB,EAAE,SAAS,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAIhF;IAED,YAAY,IAAI,OAAO,CAEtB;IAED,2EAA2E;IAC3E,IAAI,IAAI,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,OAAO,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,0BAA0B,CAAC;KAClC,CAAC,CAQD;IAED;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CA6C3B;IAED,IAAI,IAAI,IAAI,CAUX;IAED,2EAA2E;IAC3E,OAAO,CAAC,gBAAgB;IA8BxB,OAAO,CAAC,oBAAoB;IAgE5B,OAAO,CAAC,kBAAkB;IAsB1B;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAiB5B,gFAAgF;IAChF,OAAO,CAAC,GAAG;IAqCX,OAAO,CAAC,gBAAgB;IAQxB,4EAA4E;IAC5E,OAAO,CAAC,UAAU;IAyDlB;;;;OAIG;YACW,UAAU;IA0BxB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,cAAc;CAMvB;AAED,eAAO,MAAM,eAAe,uBAA8B,CAAC"}
|
|
@@ -5,7 +5,8 @@ import { TLSSocket, createServer as createTlsServer } from "node:tls";
|
|
|
5
5
|
import * as log from "../log.js";
|
|
6
6
|
import { gondolinFleet } from "./gondolin-fleet.js";
|
|
7
7
|
import { gondolinJoin } from "./gondolin-join.js";
|
|
8
|
-
import {
|
|
8
|
+
import { createSessionFrameParser, encodeSessionMessage } from "./gondolin-contract.js";
|
|
9
|
+
import { GondolinRemoteConnection, GondolinWorkerUnreachableError } from "./gondolin-remote.js";
|
|
9
10
|
const RPC_TIMEOUT_MS = 150_000;
|
|
10
11
|
const TUNNEL_TIMEOUT_MS = 15_000;
|
|
11
12
|
/**
|
|
@@ -131,8 +132,7 @@ class GondolinWorkerGateway {
|
|
|
131
132
|
stop() {
|
|
132
133
|
for (const worker of this.workers.values()) {
|
|
133
134
|
worker.socket.destroy();
|
|
134
|
-
|
|
135
|
-
clearTimeout(pending.timer);
|
|
135
|
+
this.rejectPendingRpc(worker, "gateway stopped");
|
|
136
136
|
}
|
|
137
137
|
this.workers.clear();
|
|
138
138
|
for (const pending of this.pendingTunnels.values())
|
|
@@ -144,6 +144,7 @@ class GondolinWorkerGateway {
|
|
|
144
144
|
/** Routes one inbound connection by its first frame: control or tunnel. */
|
|
145
145
|
handleConnection(socket) {
|
|
146
146
|
socket.setNoDelay(true);
|
|
147
|
+
socket.setKeepAlive(true, 30_000);
|
|
147
148
|
let routed = false;
|
|
148
149
|
const authorized = (this.overrides.isAuthorized ?? defaultIsAuthorized)(socket);
|
|
149
150
|
const reader = createControlFrameReader((frame, leftover) => {
|
|
@@ -214,12 +215,10 @@ class GondolinWorkerGateway {
|
|
|
214
215
|
// a worker that reconnected may carry runtimes whose placements moved
|
|
215
216
|
void gondolinFleet.reconcile();
|
|
216
217
|
socket.on("close", () => {
|
|
218
|
+
this.rejectPendingRpc(worker, `worker '${name}' disconnected`);
|
|
217
219
|
const current = this.workers.get(name);
|
|
218
220
|
if (current !== worker)
|
|
219
221
|
return; // superseded
|
|
220
|
-
for (const pending of worker.pendingRpc.values())
|
|
221
|
-
clearTimeout(pending.timer);
|
|
222
|
-
worker.pendingRpc.clear();
|
|
223
222
|
log.logWarning(`Gondolin worker disconnected: ${name} (placements fence until lease expiry)`);
|
|
224
223
|
gondolinFleet.detachWorker(name);
|
|
225
224
|
this.workers.delete(name);
|
|
@@ -284,6 +283,7 @@ class GondolinWorkerGateway {
|
|
|
284
283
|
timer.unref?.();
|
|
285
284
|
worker.pendingRpc.set(id, {
|
|
286
285
|
timer,
|
|
286
|
+
reject,
|
|
287
287
|
resolve: (frame) => resolve({
|
|
288
288
|
status: frame.status ?? 0,
|
|
289
289
|
json: (frame.body ?? {}),
|
|
@@ -292,6 +292,13 @@ class GondolinWorkerGateway {
|
|
|
292
292
|
worker.socket.write(encodeControlFrame({ type: "request", id, method, path, body, headers }));
|
|
293
293
|
});
|
|
294
294
|
}
|
|
295
|
+
rejectPendingRpc(worker, reason) {
|
|
296
|
+
for (const pending of worker.pendingRpc.values()) {
|
|
297
|
+
clearTimeout(pending.timer);
|
|
298
|
+
pending.reject(new GondolinWorkerUnreachableError(reason));
|
|
299
|
+
}
|
|
300
|
+
worker.pendingRpc.clear();
|
|
301
|
+
}
|
|
295
302
|
/** Ask the worker to dial back a data connection for one session tunnel. */
|
|
296
303
|
openTunnel(name, path, headers, callbacks) {
|
|
297
304
|
const worker = this.workers.get(name);
|