@geminixiang/mikan 1.0.0-beta.2 → 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 +388 -0
- package/README.md +49 -16
- package/dist/adapter.d.ts +1 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- 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/bot.d.ts +1 -0
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +36 -67
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +44 -204
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/github/bot.d.ts +86 -0
- package/dist/adapters/github/bot.d.ts.map +1 -0
- package/dist/adapters/github/bot.js +640 -0
- package/dist/adapters/github/bot.js.map +1 -0
- package/dist/adapters/github/client.d.ts +122 -0
- package/dist/adapters/github/client.d.ts.map +1 -0
- package/dist/adapters/github/client.js +314 -0
- package/dist/adapters/github/client.js.map +1 -0
- package/dist/adapters/github/cloudbuild.d.ts +24 -0
- package/dist/adapters/github/cloudbuild.d.ts.map +1 -0
- package/dist/adapters/github/cloudbuild.js +71 -0
- package/dist/adapters/github/cloudbuild.js.map +1 -0
- package/dist/adapters/github/context.d.ts +9 -0
- package/dist/adapters/github/context.d.ts.map +1 -0
- package/dist/adapters/github/context.js +103 -0
- package/dist/adapters/github/context.js.map +1 -0
- package/dist/adapters/github/gcp-auth.d.ts +31 -0
- package/dist/adapters/github/gcp-auth.d.ts.map +1 -0
- package/dist/adapters/github/gcp-auth.js +196 -0
- package/dist/adapters/github/gcp-auth.js.map +1 -0
- 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/ids.d.ts +34 -0
- package/dist/adapters/github/ids.d.ts.map +1 -0
- package/dist/adapters/github/ids.js +53 -0
- package/dist/adapters/github/ids.js.map +1 -0
- package/dist/adapters/github/repo.d.ts +32 -0
- package/dist/adapters/github/repo.d.ts.map +1 -0
- package/dist/adapters/github/repo.js +141 -0
- package/dist/adapters/github/repo.js.map +1 -0
- package/dist/adapters/github/tool-pack.d.ts +14 -0
- package/dist/adapters/github/tool-pack.d.ts.map +1 -0
- package/dist/adapters/github/tool-pack.js +56 -0
- package/dist/adapters/github/tool-pack.js.map +1 -0
- package/dist/adapters/github/tools/checks.d.ts +24 -0
- package/dist/adapters/github/tools/checks.d.ts.map +1 -0
- package/dist/adapters/github/tools/checks.js +105 -0
- package/dist/adapters/github/tools/checks.js.map +1 -0
- package/dist/adapters/github/tools/issue.d.ts +22 -0
- package/dist/adapters/github/tools/issue.d.ts.map +1 -0
- package/dist/adapters/github/tools/issue.js +48 -0
- package/dist/adapters/github/tools/issue.js.map +1 -0
- package/dist/adapters/github/tools/pr.d.ts +22 -0
- package/dist/adapters/github/tools/pr.d.ts.map +1 -0
- package/dist/adapters/github/tools/pr.js +48 -0
- package/dist/adapters/github/tools/pr.js.map +1 -0
- package/dist/adapters/github/tools/read.d.ts +22 -0
- package/dist/adapters/github/tools/read.d.ts.map +1 -0
- package/dist/adapters/github/tools/read.js +141 -0
- package/dist/adapters/github/tools/read.js.map +1 -0
- package/dist/adapters/github/tools/review-reply.d.ts +19 -0
- package/dist/adapters/github/tools/review-reply.d.ts.map +1 -0
- package/dist/adapters/github/tools/review-reply.js +38 -0
- package/dist/adapters/github/tools/review-reply.js.map +1 -0
- package/dist/adapters/github/tools/sync.d.ts +17 -0
- package/dist/adapters/github/tools/sync.d.ts.map +1 -0
- package/dist/adapters/github/tools/sync.js +34 -0
- package/dist/adapters/github/tools/sync.js.map +1 -0
- package/dist/adapters/github/types.d.ts +350 -0
- package/dist/adapters/github/types.d.ts.map +1 -0
- package/dist/adapters/github/types.js +2 -0
- package/dist/adapters/github/types.js.map +1 -0
- package/dist/adapters/intake.d.ts +16 -5
- package/dist/adapters/intake.d.ts.map +1 -1
- package/dist/adapters/intake.js +79 -22
- package/dist/adapters/intake.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +8 -6
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +82 -134
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +3 -14
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
- package/dist/adapters/slack/response-lifecycle.js +14 -17
- package/dist/adapters/slack/response-lifecycle.js.map +1 -1
- package/dist/adapters/slack/session.d.ts.map +1 -1
- package/dist/adapters/slack/session.js +5 -8
- package/dist/adapters/slack/session.js.map +1 -1
- package/dist/adapters/streaming.d.ts +4 -0
- package/dist/adapters/streaming.d.ts.map +1 -1
- package/dist/adapters/streaming.js +11 -0
- package/dist/adapters/streaming.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts +1 -2
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +27 -62
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +37 -174
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/adapters/types.d.ts +28 -2
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/adapters/types.js.map +1 -1
- package/dist/agent.d.ts +37 -14
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +359 -229
- 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/download.d.ts.map +1 -0
- package/dist/cli/download.js.map +1 -0
- package/dist/cli/ext-git.d.ts +22 -0
- package/dist/cli/ext-git.d.ts.map +1 -0
- package/dist/cli/ext-git.js +76 -0
- package/dist/cli/ext-git.js.map +1 -0
- package/dist/cli/ext.d.ts +2 -0
- package/dist/cli/ext.d.ts.map +1 -0
- package/dist/cli/ext.js +215 -0
- package/dist/cli/ext.js.map +1 -0
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +2 -1
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/auto-reply.d.ts.map +1 -1
- package/dist/commands/auto-reply.js +2 -6
- package/dist/commands/auto-reply.js.map +1 -1
- package/dist/commands/extensions.d.ts.map +1 -1
- package/dist/commands/extensions.js +18 -8
- package/dist/commands/extensions.js.map +1 -1
- package/dist/commands/login.d.ts +2 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +35 -3
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/manifest.d.ts +72 -0
- package/dist/commands/manifest.d.ts.map +1 -0
- package/dist/commands/manifest.js +121 -0
- package/dist/commands/manifest.js.map +1 -0
- package/dist/commands/model.d.ts.map +1 -1
- package/dist/commands/model.js +11 -9
- package/dist/commands/model.js.map +1 -1
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +2 -1
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/sandbox.d.ts.map +1 -1
- package/dist/commands/sandbox.js +23 -17
- package/dist/commands/sandbox.js.map +1 -1
- package/dist/commands/text.d.ts +2 -0
- package/dist/commands/text.d.ts.map +1 -0
- package/dist/commands/text.js +16 -0
- package/dist/commands/text.js.map +1 -0
- package/dist/commands/types.d.ts +14 -1
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/config.d.ts +1 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +98 -95
- 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.map +1 -1
- package/dist/events.js +9 -73
- package/dist/events.js.map +1 -1
- package/dist/execution-resolver.d.ts +4 -2
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +54 -41
- 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 +44 -11
- package/dist/harness/extensions/loader.d.ts.map +1 -1
- package/dist/harness/extensions/loader.js +273 -84
- package/dist/harness/extensions/loader.js.map +1 -1
- package/dist/harness/extensions/registry.d.ts +48 -1
- package/dist/harness/extensions/registry.d.ts.map +1 -1
- package/dist/harness/extensions/registry.js +146 -0
- package/dist/harness/extensions/registry.js.map +1 -1
- package/dist/harness/extensions/types.d.ts +165 -22
- 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 +6 -5
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +3 -2
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/models.d.ts +5 -0
- package/dist/harness/models.d.ts.map +1 -1
- package/dist/harness/models.js +10 -0
- package/dist/harness/models.js.map +1 -1
- package/dist/harness/runner.d.ts +37 -2
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +107 -23
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/session-store.d.ts +4 -1
- package/dist/harness/session-store.d.ts.map +1 -1
- package/dist/harness/session-store.js +11 -2
- package/dist/harness/session-store.js.map +1 -1
- package/dist/harness/subagent-runner.d.ts +34 -0
- package/dist/harness/subagent-runner.d.ts.map +1 -0
- package/dist/harness/subagent-runner.js +339 -0
- package/dist/harness/subagent-runner.js.map +1 -0
- package/dist/harness/types.d.ts +53 -0
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/index.d.ts +3 -3
- 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 +260 -109
- 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/provisioner.d.ts.map +1 -1
- package/dist/provisioner.js +2 -5
- package/dist/provisioner.js.map +1 -1
- package/dist/runtime/conversation-runtime.d.ts +1 -1
- package/dist/runtime/conversation-runtime.d.ts.map +1 -1
- package/dist/runtime/conversation-runtime.js +276 -43
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +36 -5
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sandbox/cloudflare.d.ts +3 -0
- package/dist/sandbox/cloudflare.d.ts.map +1 -1
- package/dist/sandbox/cloudflare.js +10 -0
- package/dist/sandbox/cloudflare.js.map +1 -1
- package/dist/sandbox/container.d.ts +4 -0
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +11 -2
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sandbox/firecracker.d.ts +3 -0
- package/dist/sandbox/firecracker.d.ts.map +1 -1
- package/dist/sandbox/firecracker.js +10 -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 +104 -0
- package/dist/sandbox/gondolin-fleet.d.ts.map +1 -0
- package/dist/sandbox/gondolin-fleet.js +330 -0
- package/dist/sandbox/gondolin-fleet.js.map +1 -0
- package/dist/sandbox/gondolin-gateway.d.ts +86 -0
- package/dist/sandbox/gondolin-gateway.d.ts.map +1 -0
- package/dist/sandbox/gondolin-gateway.js +407 -0
- package/dist/sandbox/gondolin-gateway.js.map +1 -0
- package/dist/sandbox/gondolin-inventory.d.ts +75 -0
- package/dist/sandbox/gondolin-inventory.d.ts.map +1 -0
- package/dist/sandbox/gondolin-inventory.js +257 -0
- package/dist/sandbox/gondolin-inventory.js.map +1 -0
- package/dist/sandbox/gondolin-join.d.ts +62 -0
- package/dist/sandbox/gondolin-join.d.ts.map +1 -0
- package/dist/sandbox/gondolin-join.js +261 -0
- package/dist/sandbox/gondolin-join.js.map +1 -0
- package/dist/sandbox/gondolin-nfs-advisory.d.ts +15 -0
- package/dist/sandbox/gondolin-nfs-advisory.d.ts.map +1 -0
- package/dist/sandbox/gondolin-nfs-advisory.js +60 -0
- package/dist/sandbox/gondolin-nfs-advisory.js.map +1 -0
- package/dist/sandbox/gondolin-placement.d.ts +30 -0
- package/dist/sandbox/gondolin-placement.d.ts.map +1 -0
- package/dist/sandbox/gondolin-placement.js +77 -0
- package/dist/sandbox/gondolin-placement.js.map +1 -0
- 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 +70 -0
- package/dist/sandbox/gondolin-remote.d.ts.map +1 -0
- package/dist/sandbox/gondolin-remote.js +325 -0
- package/dist/sandbox/gondolin-remote.js.map +1 -0
- package/dist/sandbox/gondolin-worker-client.d.ts +135 -0
- package/dist/sandbox/gondolin-worker-client.d.ts.map +1 -0
- package/dist/sandbox/gondolin-worker-client.js +323 -0
- package/dist/sandbox/gondolin-worker-client.js.map +1 -0
- package/dist/sandbox/gondolin-worker-main.d.ts +2 -0
- package/dist/sandbox/gondolin-worker-main.d.ts.map +1 -0
- package/dist/sandbox/gondolin-worker-main.js +22 -0
- package/dist/sandbox/gondolin-worker-main.js.map +1 -0
- package/dist/sandbox/gondolin-worker.d.ts +17 -0
- package/dist/sandbox/gondolin-worker.d.ts.map +1 -0
- package/dist/sandbox/gondolin-worker.js +201 -0
- package/dist/sandbox/gondolin-worker.js.map +1 -0
- package/dist/sandbox/gondolin.d.ts +53 -0
- package/dist/sandbox/gondolin.d.ts.map +1 -0
- package/dist/sandbox/gondolin.js +429 -0
- package/dist/sandbox/gondolin.js.map +1 -0
- package/dist/sandbox/host.d.ts +3 -0
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +14 -0
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sandbox/identity.d.ts +21 -0
- package/dist/sandbox/identity.d.ts.map +1 -0
- package/dist/sandbox/identity.js +36 -0
- package/dist/sandbox/identity.js.map +1 -0
- package/dist/sandbox/index.d.ts +13 -4
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +19 -3
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/types.d.ts +31 -1
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sandbox/types.js.map +1 -1
- package/dist/sandbox/utils.d.ts +12 -0
- package/dist/sandbox/utils.d.ts.map +1 -1
- package/dist/sandbox/utils.js +38 -0
- 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} +40 -113
- 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/history-line.d.ts +20 -0
- package/dist/sessions/history-line.d.ts.map +1 -0
- package/dist/sessions/history-line.js +24 -0
- package/dist/sessions/history-line.js.map +1 -0
- package/dist/sessions/policy.d.ts.map +1 -1
- package/dist/sessions/policy.js +5 -3
- package/dist/sessions/policy.js.map +1 -1
- 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/session-key.d.ts +31 -0
- package/dist/sessions/session-key.d.ts.map +1 -0
- package/dist/sessions/session-key.js +44 -0
- package/dist/sessions/session-key.js.map +1 -0
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +6 -12
- 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/bash.d.ts +3 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +24 -17
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +9 -12
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/event.d.ts +1 -1
- package/dist/tools/event.d.ts.map +1 -1
- package/dist/tools/event.js +53 -43
- package/dist/tools/event.js.map +1 -1
- package/dist/tools/generate-image.d.ts +17 -0
- package/dist/tools/generate-image.d.ts.map +1 -0
- package/dist/tools/generate-image.js +63 -0
- package/dist/tools/generate-image.js.map +1 -0
- 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 +17 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +28 -3
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/react.d.ts +15 -0
- package/dist/tools/react.d.ts.map +1 -0
- package/dist/tools/react.js +29 -0
- package/dist/tools/react.js.map +1 -0
- 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 +2 -2
- package/dist/tools/sandbox.d.ts.map +1 -1
- package/dist/tools/sandbox.js +13 -9
- 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 +74 -0
- package/dist/tools/subagent.d.ts.map +1 -0
- package/dist/tools/subagent.js +335 -0
- package/dist/tools/subagent.js.map +1 -0
- package/dist/tools/types.d.ts +40 -6
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js.map +1 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +1 -8
- package/dist/tools/write.js.map +1 -1
- package/dist/trigger.d.ts.map +1 -1
- package/dist/trigger.js +1 -2
- package/dist/trigger.js.map +1 -1
- package/dist/types.d.ts +144 -31
- 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 +9 -0
- package/dist/vault/disabled.d.ts.map +1 -0
- package/dist/vault/disabled.js +22 -0
- package/dist/vault/disabled.js.map +1 -0
- package/dist/vault/index.d.ts +0 -2
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +1 -32
- 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/policy.d.ts +20 -0
- package/dist/vault/policy.d.ts.map +1 -0
- package/dist/vault/policy.js +20 -0
- package/dist/vault/policy.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 +27 -0
- package/dist/web/admin/portal.d.ts.map +1 -1
- package/dist/web/admin/portal.js +117 -154
- package/dist/web/admin/portal.js.map +1 -1
- package/dist/web/admin/types.d.ts +6 -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/dist/web/login/oauth.d.ts +2 -3
- package/dist/web/login/oauth.d.ts.map +1 -1
- package/dist/web/login/oauth.js +0 -31
- package/dist/web/login/oauth.js.map +1 -1
- package/dist/web/login/portal.js +1 -1
- package/dist/web/login/portal.js.map +1 -1
- package/dist/web/login/types.d.ts +0 -11
- package/dist/web/login/types.d.ts.map +1 -1
- package/dist/web/login/types.js.map +1 -1
- package/dist/web/portal-shell.d.ts +4 -0
- package/dist/web/portal-shell.d.ts.map +1 -0
- package/dist/{portal-shell.js → web/portal-shell.js} +2 -2
- package/dist/web/portal-shell.js.map +1 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +7 -0
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-view/command.d.ts.map +1 -1
- package/dist/web/session-view/command.js +4 -1
- package/dist/web/session-view/command.js.map +1 -1
- package/dist/web/session-view/portal.d.ts.map +1 -1
- package/dist/web/session-view/portal.js +4 -3
- package/dist/web/session-view/portal.js.map +1 -1
- package/dist/web/session-view/service.d.ts.map +1 -1
- package/dist/web/session-view/service.js +2 -1
- package/dist/web/session-view/service.js.map +1 -1
- package/dist/web/session-view/types.d.ts +2 -1
- package/dist/web/session-view/types.d.ts.map +1 -1
- package/dist/web/session-view/types.js.map +1 -1
- package/package.json +7 -4
- package/dist/download.d.ts.map +0 -1
- package/dist/download.js.map +0 -1
- package/dist/model-registry.d.ts +0 -4
- package/dist/model-registry.d.ts.map +0 -1
- package/dist/model-registry.js +0 -7
- package/dist/model-registry.js.map +0 -1
- package/dist/portal-shell.d.ts +0 -4
- package/dist/portal-shell.d.ts.map +0 -1
- package/dist/portal-shell.js.map +0 -1
- package/dist/runtime/agent-run-controller.d.ts +0 -31
- package/dist/runtime/agent-run-controller.d.ts.map +0 -1
- package/dist/runtime/agent-run-controller.js +0 -195
- package/dist/runtime/agent-run-controller.js.map +0 -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 -3
- package/dist/vault/routing.d.ts.map +0 -1
- package/dist/vault/routing.js +0 -13
- package/dist/vault/routing.js.map +0 -1
- /package/dist/{download.d.ts → cli/download.d.ts} +0 -0
- /package/dist/{download.js → cli/download.js} +0 -0
|
@@ -0,0 +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// 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"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ResourceLimits, SandboxLimitStatus, SandboxResourceController } from "../types.js";
|
|
2
|
+
import type { ExecOptions, ExecResult, Executor, GondolinSandboxConfig, RuntimePathContext, SandboxAdapter } from "./types.js";
|
|
3
|
+
declare class GondolinResourceManager implements SandboxResourceController {
|
|
4
|
+
private defaultLimits?;
|
|
5
|
+
private boostLimits?;
|
|
6
|
+
private readonly boostedKeys;
|
|
7
|
+
private readonly overrideLimits;
|
|
8
|
+
configure(defaultLimits?: ResourceLimits, boostLimits?: ResourceLimits): void;
|
|
9
|
+
boost(key: string): Promise<SandboxLimitStatus>;
|
|
10
|
+
setLimits(key: string, limits: ResourceLimits): Promise<SandboxLimitStatus>;
|
|
11
|
+
getLimitStatus(key: string): SandboxLimitStatus;
|
|
12
|
+
getDefaultLimits(): ResourceLimits | undefined;
|
|
13
|
+
getBoostLimits(): ResourceLimits | undefined;
|
|
14
|
+
clear(key: string): void;
|
|
15
|
+
private effectiveLimits;
|
|
16
|
+
}
|
|
17
|
+
export declare const gondolinResources: GondolinResourceManager;
|
|
18
|
+
/**
|
|
19
|
+
* Fail fast when `gondolin:remote` is selected but no worker source is
|
|
20
|
+
* configured. Call after the fleet and gateway have been configured.
|
|
21
|
+
*/
|
|
22
|
+
export declare function assertRemoteConfigured(): void;
|
|
23
|
+
export declare function stopIdleGondolinVms(maxIdleMs: number, now?: number): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Stop workers surviving from a previous mikan that no conversation has
|
|
26
|
+
* adopted — without this, a runtime whose conversation went quiet before the
|
|
27
|
+
* restart would idle forever (its worker only self-stops when every mikan is
|
|
28
|
+
* gone, and the in-memory idle sweep only tracks acquired sessions).
|
|
29
|
+
*/
|
|
30
|
+
export declare function sweepUnadoptedGondolinWorkers(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Forget every session without stopping the workers: runtimes deliberately
|
|
33
|
+
* outlive the mikan process so the next one adopts them instead of paying a
|
|
34
|
+
* VM boot per conversation on every deploy.
|
|
35
|
+
*/
|
|
36
|
+
export declare function disconnectAllGondolinRuntimes(): Promise<void>;
|
|
37
|
+
export declare class GondolinExecutor implements Executor {
|
|
38
|
+
private readonly config;
|
|
39
|
+
private readonly env?;
|
|
40
|
+
private readonly workspacePath;
|
|
41
|
+
private readonly instanceId;
|
|
42
|
+
constructor(config: GondolinSandboxConfig, env?: Record<string, string> | undefined);
|
|
43
|
+
exec(command: string, options?: ExecOptions): Promise<ExecResult>;
|
|
44
|
+
readFile(path: string): Promise<string>;
|
|
45
|
+
readFileBase64(path: string): Promise<string>;
|
|
46
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
47
|
+
getWorkspacePath(): string;
|
|
48
|
+
getPathContext(hostWorkspaceRoot: string): RuntimePathContext;
|
|
49
|
+
getSandboxConfig(): GondolinSandboxConfig;
|
|
50
|
+
}
|
|
51
|
+
export declare const gondolinSandboxAdapter: SandboxAdapter<GondolinSandboxConfig>;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=gondolin.d.ts.map
|
|
@@ -0,0 +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;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"}
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync, statSync } from "node:fs";
|
|
3
|
+
import * as log from "../log.js";
|
|
4
|
+
import { SandboxError } from "./errors.js";
|
|
5
|
+
import { gondolinInventory } from "./gondolin-inventory.js";
|
|
6
|
+
import { isRuntimeGone, isRuntimeInterrupted } from "./gondolin-recovery.js";
|
|
7
|
+
import { gondolinWorkers, } from "./gondolin-worker-client.js";
|
|
8
|
+
import { gondolinFleet } from "./gondolin-fleet.js";
|
|
9
|
+
import { gondolinGateway } from "./gondolin-gateway.js";
|
|
10
|
+
import { createMountedRuntimePathContext } from "./path-context.js";
|
|
11
|
+
import { withRuntimeBootstrap } from "./container.js";
|
|
12
|
+
import { execReadFile, execReadFileBase64, execWriteFile } from "./utils.js";
|
|
13
|
+
const MINIMUM_NODE_VERSION = [23, 6, 0];
|
|
14
|
+
const MIKAN_IMAGE = "mikan-sandbox:latest";
|
|
15
|
+
const sessions = new Map();
|
|
16
|
+
const transitions = new Map();
|
|
17
|
+
let activeShutdowns = 0;
|
|
18
|
+
let shutdownGeneration = 0;
|
|
19
|
+
class GondolinResourceManager {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.boostedKeys = new Set();
|
|
22
|
+
this.overrideLimits = new Map();
|
|
23
|
+
}
|
|
24
|
+
configure(defaultLimits, boostLimits) {
|
|
25
|
+
this.defaultLimits = defaultLimits;
|
|
26
|
+
this.boostLimits = boostLimits;
|
|
27
|
+
this.boostedKeys.clear();
|
|
28
|
+
this.overrideLimits.clear();
|
|
29
|
+
}
|
|
30
|
+
async boost(key) {
|
|
31
|
+
if (this.boostLimits?.cpus || this.boostLimits?.memory) {
|
|
32
|
+
this.overrideLimits.delete(key);
|
|
33
|
+
this.boostedKeys.add(key);
|
|
34
|
+
}
|
|
35
|
+
return this.getLimitStatus(key);
|
|
36
|
+
}
|
|
37
|
+
async setLimits(key, limits) {
|
|
38
|
+
this.boostedKeys.delete(key);
|
|
39
|
+
this.overrideLimits.set(key, { ...this.defaultLimits, ...limits });
|
|
40
|
+
return this.getLimitStatus(key);
|
|
41
|
+
}
|
|
42
|
+
getLimitStatus(key) {
|
|
43
|
+
return { limits: this.effectiveLimits(key), boosted: this.boostedKeys.has(key) };
|
|
44
|
+
}
|
|
45
|
+
getDefaultLimits() {
|
|
46
|
+
return this.defaultLimits;
|
|
47
|
+
}
|
|
48
|
+
getBoostLimits() {
|
|
49
|
+
return this.boostLimits;
|
|
50
|
+
}
|
|
51
|
+
clear(key) {
|
|
52
|
+
this.boostedKeys.delete(key);
|
|
53
|
+
this.overrideLimits.delete(key);
|
|
54
|
+
}
|
|
55
|
+
effectiveLimits(key) {
|
|
56
|
+
const override = this.overrideLimits.get(key);
|
|
57
|
+
if (override)
|
|
58
|
+
return override;
|
|
59
|
+
if (!this.boostedKeys.has(key))
|
|
60
|
+
return this.defaultLimits;
|
|
61
|
+
return { ...this.defaultLimits, ...this.boostLimits };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export const gondolinResources = new GondolinResourceManager();
|
|
65
|
+
function parseGondolinSandboxArg(value) {
|
|
66
|
+
if (!value.startsWith("gondolin:"))
|
|
67
|
+
return undefined;
|
|
68
|
+
const profile = value.slice("gondolin:".length);
|
|
69
|
+
if (!profile) {
|
|
70
|
+
throw new SandboxError("Error: gondolin sandbox requires a profile (e.g., gondolin:default)");
|
|
71
|
+
}
|
|
72
|
+
if (profile !== "default" && profile !== "remote") {
|
|
73
|
+
throw new SandboxError(`Error: unsupported gondolin profile '${profile}'. Use 'gondolin:default' or 'gondolin:remote'`);
|
|
74
|
+
}
|
|
75
|
+
return { type: "gondolin", profile };
|
|
76
|
+
}
|
|
77
|
+
function assertSupportedNodeVersion() {
|
|
78
|
+
const [major = 0, minor = 0] = process.versions.node.split(".").map(Number);
|
|
79
|
+
if (major < MINIMUM_NODE_VERSION[0] || (major === MINIMUM_NODE_VERSION[0] && minor < 6)) {
|
|
80
|
+
throw new SandboxError(`Error: gondolin:default requires Node.js >=23.6.0 (current: ${process.versions.node}). Other sandbox modes remain available on Node.js >=22.19.0.`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function validateGondolinSandbox(config) {
|
|
84
|
+
if (config?.profile === "remote") {
|
|
85
|
+
// The remote transports are configured from settings later in startup, so
|
|
86
|
+
// "is a worker source configured?" is checked there (assertRemoteConfigured),
|
|
87
|
+
// not here where nothing has been wired yet.
|
|
88
|
+
console.log(" Gondolin microVM enabled. Profile: remote");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
assertSupportedNodeVersion();
|
|
92
|
+
console.log(" Gondolin microVM enabled. Profile: default");
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Fail fast when `gondolin:remote` is selected but no worker source is
|
|
96
|
+
* configured. Call after the fleet and gateway have been configured.
|
|
97
|
+
*/
|
|
98
|
+
export function assertRemoteConfigured() {
|
|
99
|
+
if (!gondolinFleet.isConfigured() && !gondolinGateway.isConfigured()) {
|
|
100
|
+
throw new SandboxError("Error: gondolin:remote requires sandbox.gondolin.remote settings (workers[]/url, or gateway for dial-home workers)");
|
|
101
|
+
}
|
|
102
|
+
// Without a workspaceRoot, mount sources cannot be translated to the worker's
|
|
103
|
+
// filesystem and every runtime is rejected with "escapes the workspace root".
|
|
104
|
+
const missing = gondolinFleet.workersMissingWorkspaceRoot();
|
|
105
|
+
if (missing.length > 0) {
|
|
106
|
+
throw new SandboxError(`Error: gondolin:remote worker(s) [${missing.join(", ")}] have no workspaceRoot; ` +
|
|
107
|
+
`set sandbox.gondolin.remote.workspaceRoot (or gateway.workspaceRoot) to the worker-side shared workspace path`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function transportFor(config) {
|
|
111
|
+
return config.profile === "remote" ? gondolinFleet : gondolinWorkers;
|
|
112
|
+
}
|
|
113
|
+
async function resolveDesiredRuntime(config) {
|
|
114
|
+
let image;
|
|
115
|
+
let imageIdentity;
|
|
116
|
+
if (config.profile === "remote") {
|
|
117
|
+
// image assets live on the worker host; the selector is the identity
|
|
118
|
+
image = gondolinFleet.imageSelector() ?? config.image ?? MIKAN_IMAGE;
|
|
119
|
+
imageIdentity = image;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
const selector = config.image ?? MIKAN_IMAGE;
|
|
123
|
+
const { ensureImageSelector } = (await import("@earendil-works/gondolin"));
|
|
124
|
+
const resolvedImage = await ensureImageSelector(selector);
|
|
125
|
+
image = resolvedImage.assetDir;
|
|
126
|
+
imageIdentity = resolvedImage.buildId ?? resolvedImage.assetDir;
|
|
127
|
+
}
|
|
128
|
+
const mounts = (config.mounts ?? [{ source: config.workspacePath ?? process.cwd(), target: "/workspace" }]).toSorted((left, right) => left.target.localeCompare(right.target));
|
|
129
|
+
return {
|
|
130
|
+
image,
|
|
131
|
+
imageIdentity,
|
|
132
|
+
mounts,
|
|
133
|
+
credentialIdentity: credentialIdentity(mounts),
|
|
134
|
+
limits: config.resourceKey
|
|
135
|
+
? gondolinResources.getLimitStatus(config.resourceKey).limits
|
|
136
|
+
: undefined,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Content hashes of credential file mounts (files projected outside
|
|
141
|
+
* /workspace). Rotating a credential on the host changes the fingerprint, so
|
|
142
|
+
* the next command recreates the runtime with a fresh projection instead of
|
|
143
|
+
* serving the stale copy for the rest of the VM's life. Workspace files are
|
|
144
|
+
* excluded: the guest writes them back, which would drift the runtime it runs
|
|
145
|
+
* in.
|
|
146
|
+
*/
|
|
147
|
+
function credentialIdentity(mounts) {
|
|
148
|
+
const identity = {};
|
|
149
|
+
for (const mount of mounts) {
|
|
150
|
+
if (mount.target.startsWith("/workspace/") || mount.target === "/workspace")
|
|
151
|
+
continue;
|
|
152
|
+
try {
|
|
153
|
+
if (!statSync(mount.source).isFile())
|
|
154
|
+
continue;
|
|
155
|
+
identity[mount.target] = createHash("sha256")
|
|
156
|
+
.update(readFileSync(mount.source))
|
|
157
|
+
.digest("hex");
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// missing source: the worker skips it too, so it has no runtime identity
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return identity;
|
|
164
|
+
}
|
|
165
|
+
function runtimeFingerprint(desired) {
|
|
166
|
+
return createHash("sha256")
|
|
167
|
+
.update(JSON.stringify({
|
|
168
|
+
image: desired.imageIdentity,
|
|
169
|
+
mounts: desired.mounts,
|
|
170
|
+
credentials: desired.credentialIdentity,
|
|
171
|
+
limits: desired.limits,
|
|
172
|
+
}))
|
|
173
|
+
.digest("hex");
|
|
174
|
+
}
|
|
175
|
+
function gondolinCpuCount(cpus) {
|
|
176
|
+
if (cpus === undefined)
|
|
177
|
+
return undefined;
|
|
178
|
+
const parsed = Number(cpus);
|
|
179
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
180
|
+
throw new SandboxError(`Error: invalid Gondolin CPU limit '${cpus}'`);
|
|
181
|
+
}
|
|
182
|
+
// ponytail: Gondolin exposes whole vCPUs; add host cgroup quotas if fractional enforcement matters.
|
|
183
|
+
return Math.ceil(parsed);
|
|
184
|
+
}
|
|
185
|
+
function createSession(key, config, desired, fingerprint) {
|
|
186
|
+
const transport = transportFor(config);
|
|
187
|
+
let session;
|
|
188
|
+
const runtime = transport
|
|
189
|
+
.ensure(key, {
|
|
190
|
+
image: desired.image,
|
|
191
|
+
mounts: desired.mounts,
|
|
192
|
+
cpus: desired.limits?.cpus,
|
|
193
|
+
vmCpus: gondolinCpuCount(desired.limits?.cpus),
|
|
194
|
+
memory: desired.limits?.memory,
|
|
195
|
+
fingerprint,
|
|
196
|
+
workspacePath: config.workspacePath,
|
|
197
|
+
})
|
|
198
|
+
.catch((error) => {
|
|
199
|
+
if (sessions.get(key) === session)
|
|
200
|
+
sessions.delete(key);
|
|
201
|
+
throw error;
|
|
202
|
+
});
|
|
203
|
+
session = {
|
|
204
|
+
runtime,
|
|
205
|
+
transport,
|
|
206
|
+
fingerprint,
|
|
207
|
+
resourceKey: config.resourceKey,
|
|
208
|
+
activeOperations: 0,
|
|
209
|
+
lastUsed: Date.now(),
|
|
210
|
+
idleWaiters: [],
|
|
211
|
+
};
|
|
212
|
+
return session;
|
|
213
|
+
}
|
|
214
|
+
async function acquireSession(key, config, generation = shutdownGeneration) {
|
|
215
|
+
if (activeShutdowns > 0 || generation !== shutdownGeneration) {
|
|
216
|
+
throw new SandboxError("Error: Gondolin runtime is shutting down");
|
|
217
|
+
}
|
|
218
|
+
const pending = transitions.get(key);
|
|
219
|
+
if (pending) {
|
|
220
|
+
await pending;
|
|
221
|
+
return acquireSession(key, config, generation);
|
|
222
|
+
}
|
|
223
|
+
const desired = await resolveDesiredRuntime(config);
|
|
224
|
+
if (activeShutdowns > 0 || generation !== shutdownGeneration) {
|
|
225
|
+
throw new SandboxError("Error: Gondolin runtime is shutting down");
|
|
226
|
+
}
|
|
227
|
+
const concurrentTransition = transitions.get(key);
|
|
228
|
+
if (concurrentTransition) {
|
|
229
|
+
await concurrentTransition;
|
|
230
|
+
return acquireSession(key, config, generation);
|
|
231
|
+
}
|
|
232
|
+
const fingerprint = runtimeFingerprint(desired);
|
|
233
|
+
const existing = sessions.get(key);
|
|
234
|
+
if (existing?.fingerprint === fingerprint) {
|
|
235
|
+
existing.activeOperations += 1;
|
|
236
|
+
return existing;
|
|
237
|
+
}
|
|
238
|
+
const transition = replaceSession(key, config, desired, fingerprint, existing);
|
|
239
|
+
transitions.set(key, transition);
|
|
240
|
+
try {
|
|
241
|
+
await transition;
|
|
242
|
+
}
|
|
243
|
+
finally {
|
|
244
|
+
if (transitions.get(key) === transition)
|
|
245
|
+
transitions.delete(key);
|
|
246
|
+
}
|
|
247
|
+
return acquireSession(key, config, generation);
|
|
248
|
+
}
|
|
249
|
+
async function replaceSession(key, config, desired, fingerprint, existing) {
|
|
250
|
+
if (existing) {
|
|
251
|
+
await closeSession(key, existing, {
|
|
252
|
+
waitForActiveOperations: true,
|
|
253
|
+
resetResources: false,
|
|
254
|
+
throwOnError: true,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
const replacement = createSession(key, config, desired, fingerprint);
|
|
258
|
+
sessions.set(key, replacement);
|
|
259
|
+
await replacement.runtime;
|
|
260
|
+
}
|
|
261
|
+
function discardDeadSession(key, session) {
|
|
262
|
+
if (sessions.get(key) !== session)
|
|
263
|
+
return;
|
|
264
|
+
sessions.delete(key);
|
|
265
|
+
log.logWarning(`Gondolin runtime for '${key}' died; the session will be recreated`);
|
|
266
|
+
}
|
|
267
|
+
async function withRuntime(key, config, operation) {
|
|
268
|
+
for (let attempt = 0;; attempt += 1) {
|
|
269
|
+
const session = await acquireSession(key, config);
|
|
270
|
+
let handle;
|
|
271
|
+
try {
|
|
272
|
+
handle = await session.runtime;
|
|
273
|
+
return await operation(handle);
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
const gone = isRuntimeGone(error);
|
|
277
|
+
const interrupted = isRuntimeInterrupted(error);
|
|
278
|
+
if (gone || (interrupted && handle && !(await session.transport.isRuntimeAlive(handle)))) {
|
|
279
|
+
discardDeadSession(key, session);
|
|
280
|
+
}
|
|
281
|
+
// Nothing reached a gone runtime, so recreating and retrying is safe;
|
|
282
|
+
// an interrupted command may have side effects and must surface.
|
|
283
|
+
if (gone && attempt === 0)
|
|
284
|
+
continue;
|
|
285
|
+
if (gone || interrupted) {
|
|
286
|
+
throw new SandboxError(`Error: Gondolin runtime for '${key}' died (${error.message}); it is recreated on the next command`);
|
|
287
|
+
}
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
session.activeOperations -= 1;
|
|
292
|
+
session.lastUsed = Date.now();
|
|
293
|
+
if (session.activeOperations === 0) {
|
|
294
|
+
for (const resolve of session.idleWaiters.splice(0))
|
|
295
|
+
resolve();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function waitForIdle(session) {
|
|
301
|
+
if (session.activeOperations === 0)
|
|
302
|
+
return Promise.resolve();
|
|
303
|
+
return new Promise((resolve) => session.idleWaiters.push(resolve));
|
|
304
|
+
}
|
|
305
|
+
async function closeSession(key, session, options = {}) {
|
|
306
|
+
if (sessions.get(key) !== session)
|
|
307
|
+
return;
|
|
308
|
+
sessions.delete(key);
|
|
309
|
+
try {
|
|
310
|
+
if (options.waitForActiveOperations)
|
|
311
|
+
await waitForIdle(session);
|
|
312
|
+
const handle = await session.runtime;
|
|
313
|
+
if (options.stopRuntime !== false)
|
|
314
|
+
await session.transport.stop(handle);
|
|
315
|
+
if (options.resetResources !== false &&
|
|
316
|
+
session.resourceKey &&
|
|
317
|
+
!Array.from(sessions.values()).some(({ resourceKey }) => resourceKey === session.resourceKey)) {
|
|
318
|
+
gondolinResources.clear(session.resourceKey);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
session.fingerprint = "";
|
|
323
|
+
// Re-track for a retried close, unless a replacement already claimed the
|
|
324
|
+
// key — then the failed runtime stays for the reconcile paths and
|
|
325
|
+
// clobbering the replacement would leak it too.
|
|
326
|
+
if (!sessions.has(key))
|
|
327
|
+
sessions.set(key, session);
|
|
328
|
+
if (options.throwOnError)
|
|
329
|
+
throw error;
|
|
330
|
+
log.logWarning(`Failed to close Gondolin session '${key}'`, error instanceof Error ? error.message : String(error));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
export async function stopIdleGondolinVms(maxIdleMs, now = Date.now()) {
|
|
334
|
+
const idle = Array.from(sessions.entries()).filter(([key, session]) => !transitions.has(key) &&
|
|
335
|
+
session.activeOperations === 0 &&
|
|
336
|
+
now - session.lastUsed >= maxIdleMs);
|
|
337
|
+
await Promise.all(idle.map(([key, session]) => {
|
|
338
|
+
// Register the close as a transition so a concurrent acquire waits for
|
|
339
|
+
// it instead of racing the shared session slot.
|
|
340
|
+
const transition = closeSession(key, session).finally(() => {
|
|
341
|
+
if (transitions.get(key) === transition)
|
|
342
|
+
transitions.delete(key);
|
|
343
|
+
});
|
|
344
|
+
transitions.set(key, transition);
|
|
345
|
+
return transition;
|
|
346
|
+
}));
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Stop workers surviving from a previous mikan that no conversation has
|
|
350
|
+
* adopted — without this, a runtime whose conversation went quiet before the
|
|
351
|
+
* restart would idle forever (its worker only self-stops when every mikan is
|
|
352
|
+
* gone, and the in-memory idle sweep only tracks acquired sessions).
|
|
353
|
+
*/
|
|
354
|
+
export async function sweepUnadoptedGondolinWorkers() {
|
|
355
|
+
for (const record of gondolinInventory.listWorkerRecords()) {
|
|
356
|
+
if (sessions.has(record.instanceId) || transitions.has(record.instanceId))
|
|
357
|
+
continue;
|
|
358
|
+
log.logInfo(`Stopping unadopted Gondolin worker ${record.ownerPid} (instance '${record.instanceId}')`);
|
|
359
|
+
await gondolinWorkers.stop({ workerPid: record.ownerPid, sessionId: record.sessionId });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Forget every session without stopping the workers: runtimes deliberately
|
|
364
|
+
* outlive the mikan process so the next one adopts them instead of paying a
|
|
365
|
+
* VM boot per conversation on every deploy.
|
|
366
|
+
*/
|
|
367
|
+
export async function disconnectAllGondolinRuntimes() {
|
|
368
|
+
shutdownGeneration += 1;
|
|
369
|
+
activeShutdowns += 1;
|
|
370
|
+
try {
|
|
371
|
+
await Promise.allSettled(transitions.values());
|
|
372
|
+
const current = Array.from(sessions.entries());
|
|
373
|
+
await Promise.all(current.map(([key, session]) => closeSession(key, session, { waitForActiveOperations: true, stopRuntime: false })));
|
|
374
|
+
}
|
|
375
|
+
finally {
|
|
376
|
+
activeShutdowns -= 1;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function executionSignal(options) {
|
|
380
|
+
const signals = [options?.signal];
|
|
381
|
+
if (options?.timeout && options.timeout > 0) {
|
|
382
|
+
signals.push(AbortSignal.timeout(options.timeout * 1000));
|
|
383
|
+
}
|
|
384
|
+
const active = signals.filter((signal) => signal !== undefined);
|
|
385
|
+
return active.length > 0 ? AbortSignal.any(active) : undefined;
|
|
386
|
+
}
|
|
387
|
+
export class GondolinExecutor {
|
|
388
|
+
constructor(config, env) {
|
|
389
|
+
this.config = config;
|
|
390
|
+
this.env = env;
|
|
391
|
+
// remote runtimes never load gondolin in this process, so the mikan host
|
|
392
|
+
// may stay on the supported Node floor
|
|
393
|
+
if (config.profile !== "remote")
|
|
394
|
+
assertSupportedNodeVersion();
|
|
395
|
+
this.workspacePath = config.workspacePath ?? process.cwd();
|
|
396
|
+
this.instanceId = config.instanceId ?? this.workspacePath;
|
|
397
|
+
}
|
|
398
|
+
async exec(command, options) {
|
|
399
|
+
return withRuntime(this.instanceId, this.config, (handle) => transportFor(this.config).exec(handle, withRuntimeBootstrap(command, this.env), {
|
|
400
|
+
env: this.env,
|
|
401
|
+
signal: executionSignal(options),
|
|
402
|
+
}));
|
|
403
|
+
}
|
|
404
|
+
async readFile(path) {
|
|
405
|
+
return execReadFile(this, path);
|
|
406
|
+
}
|
|
407
|
+
async readFileBase64(path) {
|
|
408
|
+
return execReadFileBase64(this, path);
|
|
409
|
+
}
|
|
410
|
+
async writeFile(path, content) {
|
|
411
|
+
return execWriteFile(this, path, content);
|
|
412
|
+
}
|
|
413
|
+
getWorkspacePath() {
|
|
414
|
+
return "/workspace";
|
|
415
|
+
}
|
|
416
|
+
getPathContext(hostWorkspaceRoot) {
|
|
417
|
+
return createMountedRuntimePathContext(hostWorkspaceRoot, "/workspace");
|
|
418
|
+
}
|
|
419
|
+
getSandboxConfig() {
|
|
420
|
+
return this.config;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
export const gondolinSandboxAdapter = {
|
|
424
|
+
type: "gondolin",
|
|
425
|
+
parse: parseGondolinSandboxArg,
|
|
426
|
+
validate: validateGondolinSandbox,
|
|
427
|
+
createExecutor: (config, env) => new GondolinExecutor(config, env),
|
|
428
|
+
};
|
|
429
|
+
//# sourceMappingURL=gondolin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gondolin.js","sourceRoot":"","sources":["../../src/sandbox/gondolin.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFileSync, statSync } from \"node:fs\";\nimport * as log from \"../log.js\";\nimport type { ResourceLimits, SandboxLimitStatus, SandboxResourceController } from \"../types.js\";\nimport { SandboxError } from \"./errors.js\";\nimport { gondolinInventory } from \"./gondolin-inventory.js\";\nimport { isRuntimeGone, isRuntimeInterrupted } from \"./gondolin-recovery.js\";\nimport {\n gondolinWorkers,\n type GondolinRuntimeHandle,\n type GondolinRuntimeTransport,\n} from \"./gondolin-worker-client.js\";\nimport { gondolinFleet } from \"./gondolin-fleet.js\";\nimport { gondolinGateway } from \"./gondolin-gateway.js\";\nimport { createMountedRuntimePathContext } from \"./path-context.js\";\nimport { withRuntimeBootstrap } from \"./container.js\";\nimport { execReadFile, execReadFileBase64, execWriteFile } from \"./utils.js\";\nimport type {\n ExecOptions,\n ExecResult,\n Executor,\n GondolinSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n} from \"./types.js\";\n\ntype GondolinModule = typeof import(\"@earendil-works/gondolin\");\n\ninterface GondolinSession {\n runtime: Promise<GondolinRuntimeHandle>;\n transport: GondolinRuntimeTransport;\n fingerprint: string;\n resourceKey?: string;\n activeOperations: number;\n lastUsed: number;\n idleWaiters: Array<() => void>;\n}\n\ninterface GondolinDesiredRuntime {\n image: string;\n imageIdentity: string;\n mounts: Array<{ source: string; target: string }>;\n /** Content identity of projected credential files (rotation → drift). */\n credentialIdentity: Record<string, string>;\n limits?: ResourceLimits;\n}\n\nconst MINIMUM_NODE_VERSION = [23, 6, 0] as const;\nconst MIKAN_IMAGE = \"mikan-sandbox:latest\";\nconst sessions = new Map<string, GondolinSession>();\nconst transitions = new Map<string, Promise<void>>();\nlet activeShutdowns = 0;\nlet shutdownGeneration = 0;\n\nclass GondolinResourceManager implements SandboxResourceController {\n private defaultLimits?: ResourceLimits;\n private boostLimits?: ResourceLimits;\n private readonly boostedKeys = new Set<string>();\n private readonly overrideLimits = new Map<string, ResourceLimits>();\n\n configure(defaultLimits?: ResourceLimits, boostLimits?: ResourceLimits): void {\n this.defaultLimits = defaultLimits;\n this.boostLimits = boostLimits;\n this.boostedKeys.clear();\n this.overrideLimits.clear();\n }\n\n async boost(key: string): Promise<SandboxLimitStatus> {\n if (this.boostLimits?.cpus || this.boostLimits?.memory) {\n this.overrideLimits.delete(key);\n this.boostedKeys.add(key);\n }\n return this.getLimitStatus(key);\n }\n\n async setLimits(key: string, limits: ResourceLimits): Promise<SandboxLimitStatus> {\n this.boostedKeys.delete(key);\n this.overrideLimits.set(key, { ...this.defaultLimits, ...limits });\n return this.getLimitStatus(key);\n }\n\n getLimitStatus(key: string): SandboxLimitStatus {\n return { limits: this.effectiveLimits(key), boosted: this.boostedKeys.has(key) };\n }\n\n getDefaultLimits(): ResourceLimits | undefined {\n return this.defaultLimits;\n }\n\n getBoostLimits(): ResourceLimits | undefined {\n return this.boostLimits;\n }\n\n clear(key: string): void {\n this.boostedKeys.delete(key);\n this.overrideLimits.delete(key);\n }\n\n private effectiveLimits(key: string): ResourceLimits | undefined {\n const override = this.overrideLimits.get(key);\n if (override) return override;\n if (!this.boostedKeys.has(key)) return this.defaultLimits;\n return { ...this.defaultLimits, ...this.boostLimits };\n }\n}\n\nexport const gondolinResources = new GondolinResourceManager();\n\nfunction parseGondolinSandboxArg(value: string): GondolinSandboxConfig | undefined {\n if (!value.startsWith(\"gondolin:\")) return undefined;\n\n const profile = value.slice(\"gondolin:\".length);\n if (!profile) {\n throw new SandboxError(\"Error: gondolin sandbox requires a profile (e.g., gondolin:default)\");\n }\n if (profile !== \"default\" && profile !== \"remote\") {\n throw new SandboxError(\n `Error: unsupported gondolin profile '${profile}'. Use 'gondolin:default' or 'gondolin:remote'`,\n );\n }\n return { type: \"gondolin\", profile };\n}\n\nfunction assertSupportedNodeVersion(): void {\n const [major = 0, minor = 0] = process.versions.node.split(\".\").map(Number);\n if (major < MINIMUM_NODE_VERSION[0] || (major === MINIMUM_NODE_VERSION[0] && minor < 6)) {\n throw new SandboxError(\n `Error: gondolin:default requires Node.js >=23.6.0 (current: ${process.versions.node}). Other sandbox modes remain available on Node.js >=22.19.0.`,\n );\n }\n}\n\nasync function validateGondolinSandbox(config?: GondolinSandboxConfig): Promise<void> {\n if (config?.profile === \"remote\") {\n // The remote transports are configured from settings later in startup, so\n // \"is a worker source configured?\" is checked there (assertRemoteConfigured),\n // not here where nothing has been wired yet.\n console.log(\" Gondolin microVM enabled. Profile: remote\");\n return;\n }\n assertSupportedNodeVersion();\n console.log(\" Gondolin microVM enabled. Profile: default\");\n}\n\n/**\n * Fail fast when `gondolin:remote` is selected but no worker source is\n * configured. Call after the fleet and gateway have been configured.\n */\nexport function assertRemoteConfigured(): void {\n if (!gondolinFleet.isConfigured() && !gondolinGateway.isConfigured()) {\n throw new SandboxError(\n \"Error: gondolin:remote requires sandbox.gondolin.remote settings (workers[]/url, or gateway for dial-home workers)\",\n );\n }\n // Without a workspaceRoot, mount sources cannot be translated to the worker's\n // filesystem and every runtime is rejected with \"escapes the workspace root\".\n const missing = gondolinFleet.workersMissingWorkspaceRoot();\n if (missing.length > 0) {\n throw new SandboxError(\n `Error: gondolin:remote worker(s) [${missing.join(\", \")}] have no workspaceRoot; ` +\n `set sandbox.gondolin.remote.workspaceRoot (or gateway.workspaceRoot) to the worker-side shared workspace path`,\n );\n }\n}\n\nfunction transportFor(config: GondolinSandboxConfig): GondolinRuntimeTransport {\n return config.profile === \"remote\" ? gondolinFleet : gondolinWorkers;\n}\n\nasync function resolveDesiredRuntime(\n config: GondolinSandboxConfig,\n): Promise<GondolinDesiredRuntime> {\n let image: string;\n let imageIdentity: string;\n if (config.profile === \"remote\") {\n // image assets live on the worker host; the selector is the identity\n image = gondolinFleet.imageSelector() ?? config.image ?? MIKAN_IMAGE;\n imageIdentity = image;\n } else {\n const selector = config.image ?? MIKAN_IMAGE;\n const { ensureImageSelector } = (await import(\"@earendil-works/gondolin\")) as GondolinModule;\n const resolvedImage = await ensureImageSelector(selector);\n image = resolvedImage.assetDir;\n imageIdentity = resolvedImage.buildId ?? resolvedImage.assetDir;\n }\n const mounts = (\n config.mounts ?? [{ source: config.workspacePath ?? process.cwd(), target: \"/workspace\" }]\n ).toSorted((left, right) => left.target.localeCompare(right.target));\n return {\n image,\n imageIdentity,\n mounts,\n credentialIdentity: credentialIdentity(mounts),\n limits: config.resourceKey\n ? gondolinResources.getLimitStatus(config.resourceKey).limits\n : undefined,\n };\n}\n\n/**\n * Content hashes of credential file mounts (files projected outside\n * /workspace). Rotating a credential on the host changes the fingerprint, so\n * the next command recreates the runtime with a fresh projection instead of\n * serving the stale copy for the rest of the VM's life. Workspace files are\n * excluded: the guest writes them back, which would drift the runtime it runs\n * in.\n */\nfunction credentialIdentity(\n mounts: Array<{ source: string; target: string }>,\n): Record<string, string> {\n const identity: Record<string, string> = {};\n for (const mount of mounts) {\n if (mount.target.startsWith(\"/workspace/\") || mount.target === \"/workspace\") continue;\n try {\n if (!statSync(mount.source).isFile()) continue;\n identity[mount.target] = createHash(\"sha256\")\n .update(readFileSync(mount.source))\n .digest(\"hex\");\n } catch {\n // missing source: the worker skips it too, so it has no runtime identity\n }\n }\n return identity;\n}\n\nfunction runtimeFingerprint(desired: GondolinDesiredRuntime): string {\n return createHash(\"sha256\")\n .update(\n JSON.stringify({\n image: desired.imageIdentity,\n mounts: desired.mounts,\n credentials: desired.credentialIdentity,\n limits: desired.limits,\n }),\n )\n .digest(\"hex\");\n}\n\nfunction gondolinCpuCount(cpus: string | undefined): number | undefined {\n if (cpus === undefined) return undefined;\n const parsed = Number(cpus);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n throw new SandboxError(`Error: invalid Gondolin CPU limit '${cpus}'`);\n }\n // ponytail: Gondolin exposes whole vCPUs; add host cgroup quotas if fractional enforcement matters.\n return Math.ceil(parsed);\n}\n\nfunction createSession(\n key: string,\n config: GondolinSandboxConfig,\n desired: GondolinDesiredRuntime,\n fingerprint: string,\n): GondolinSession {\n const transport = transportFor(config);\n let session: GondolinSession;\n const runtime = transport\n .ensure(key, {\n image: desired.image,\n mounts: desired.mounts,\n cpus: desired.limits?.cpus,\n vmCpus: gondolinCpuCount(desired.limits?.cpus),\n memory: desired.limits?.memory,\n fingerprint,\n workspacePath: config.workspacePath,\n })\n .catch((error) => {\n if (sessions.get(key) === session) sessions.delete(key);\n throw error;\n });\n session = {\n runtime,\n transport,\n fingerprint,\n resourceKey: config.resourceKey,\n activeOperations: 0,\n lastUsed: Date.now(),\n idleWaiters: [],\n };\n return session;\n}\n\nasync function acquireSession(\n key: string,\n config: GondolinSandboxConfig,\n generation = shutdownGeneration,\n): Promise<GondolinSession> {\n if (activeShutdowns > 0 || generation !== shutdownGeneration) {\n throw new SandboxError(\"Error: Gondolin runtime is shutting down\");\n }\n\n const pending = transitions.get(key);\n if (pending) {\n await pending;\n return acquireSession(key, config, generation);\n }\n\n const desired = await resolveDesiredRuntime(config);\n if (activeShutdowns > 0 || generation !== shutdownGeneration) {\n throw new SandboxError(\"Error: Gondolin runtime is shutting down\");\n }\n const concurrentTransition = transitions.get(key);\n if (concurrentTransition) {\n await concurrentTransition;\n return acquireSession(key, config, generation);\n }\n\n const fingerprint = runtimeFingerprint(desired);\n const existing = sessions.get(key);\n if (existing?.fingerprint === fingerprint) {\n existing.activeOperations += 1;\n return existing;\n }\n\n const transition = replaceSession(key, config, desired, fingerprint, existing);\n transitions.set(key, transition);\n try {\n await transition;\n } finally {\n if (transitions.get(key) === transition) transitions.delete(key);\n }\n return acquireSession(key, config, generation);\n}\n\nasync function replaceSession(\n key: string,\n config: GondolinSandboxConfig,\n desired: GondolinDesiredRuntime,\n fingerprint: string,\n existing: GondolinSession | undefined,\n): Promise<void> {\n if (existing) {\n await closeSession(key, existing, {\n waitForActiveOperations: true,\n resetResources: false,\n throwOnError: true,\n });\n }\n const replacement = createSession(key, config, desired, fingerprint);\n sessions.set(key, replacement);\n await replacement.runtime;\n}\n\nfunction discardDeadSession(key: string, session: GondolinSession): void {\n if (sessions.get(key) !== session) return;\n sessions.delete(key);\n log.logWarning(`Gondolin runtime for '${key}' died; the session will be recreated`);\n}\n\nasync function withRuntime<T>(\n key: string,\n config: GondolinSandboxConfig,\n operation: (handle: GondolinRuntimeHandle) => Promise<T>,\n): Promise<T> {\n for (let attempt = 0; ; attempt += 1) {\n const session = await acquireSession(key, config);\n let handle: GondolinRuntimeHandle | undefined;\n try {\n handle = await session.runtime;\n return await operation(handle);\n } catch (error) {\n const gone = isRuntimeGone(error);\n const interrupted = isRuntimeInterrupted(error);\n if (gone || (interrupted && handle && !(await session.transport.isRuntimeAlive(handle)))) {\n discardDeadSession(key, session);\n }\n // Nothing reached a gone runtime, so recreating and retrying is safe;\n // an interrupted command may have side effects and must surface.\n if (gone && attempt === 0) continue;\n if (gone || interrupted) {\n throw new SandboxError(\n `Error: Gondolin runtime for '${key}' died (${(error as Error).message}); it is recreated on the next command`,\n );\n }\n throw error;\n } finally {\n session.activeOperations -= 1;\n session.lastUsed = Date.now();\n if (session.activeOperations === 0) {\n for (const resolve of session.idleWaiters.splice(0)) resolve();\n }\n }\n }\n}\n\nfunction waitForIdle(session: GondolinSession): Promise<void> {\n if (session.activeOperations === 0) return Promise.resolve();\n return new Promise((resolve) => session.idleWaiters.push(resolve));\n}\n\nasync function closeSession(\n key: string,\n session: GondolinSession,\n options: {\n waitForActiveOperations?: boolean;\n resetResources?: boolean;\n throwOnError?: boolean;\n stopRuntime?: boolean;\n } = {},\n): Promise<void> {\n if (sessions.get(key) !== session) return;\n sessions.delete(key);\n try {\n if (options.waitForActiveOperations) await waitForIdle(session);\n const handle = await session.runtime;\n if (options.stopRuntime !== false) await session.transport.stop(handle);\n if (\n options.resetResources !== false &&\n session.resourceKey &&\n !Array.from(sessions.values()).some(({ resourceKey }) => resourceKey === session.resourceKey)\n ) {\n gondolinResources.clear(session.resourceKey);\n }\n } catch (error) {\n session.fingerprint = \"\";\n // Re-track for a retried close, unless a replacement already claimed the\n // key — then the failed runtime stays for the reconcile paths and\n // clobbering the replacement would leak it too.\n if (!sessions.has(key)) sessions.set(key, session);\n if (options.throwOnError) throw error;\n log.logWarning(\n `Failed to close Gondolin session '${key}'`,\n error instanceof Error ? error.message : String(error),\n );\n }\n}\n\nexport async function stopIdleGondolinVms(maxIdleMs: number, now = Date.now()): Promise<void> {\n const idle = Array.from(sessions.entries()).filter(\n ([key, session]) =>\n !transitions.has(key) &&\n session.activeOperations === 0 &&\n now - session.lastUsed >= maxIdleMs,\n );\n await Promise.all(\n idle.map(([key, session]) => {\n // Register the close as a transition so a concurrent acquire waits for\n // it instead of racing the shared session slot.\n const transition = closeSession(key, session).finally(() => {\n if (transitions.get(key) === transition) transitions.delete(key);\n });\n transitions.set(key, transition);\n return transition;\n }),\n );\n}\n\n/**\n * Stop workers surviving from a previous mikan that no conversation has\n * adopted — without this, a runtime whose conversation went quiet before the\n * restart would idle forever (its worker only self-stops when every mikan is\n * gone, and the in-memory idle sweep only tracks acquired sessions).\n */\nexport async function sweepUnadoptedGondolinWorkers(): Promise<void> {\n for (const record of gondolinInventory.listWorkerRecords()) {\n if (sessions.has(record.instanceId) || transitions.has(record.instanceId)) continue;\n log.logInfo(\n `Stopping unadopted Gondolin worker ${record.ownerPid} (instance '${record.instanceId}')`,\n );\n await gondolinWorkers.stop({ workerPid: record.ownerPid, sessionId: record.sessionId });\n }\n}\n\n/**\n * Forget every session without stopping the workers: runtimes deliberately\n * outlive the mikan process so the next one adopts them instead of paying a\n * VM boot per conversation on every deploy.\n */\nexport async function disconnectAllGondolinRuntimes(): Promise<void> {\n shutdownGeneration += 1;\n activeShutdowns += 1;\n try {\n await Promise.allSettled(transitions.values());\n const current = Array.from(sessions.entries());\n await Promise.all(\n current.map(([key, session]) =>\n closeSession(key, session, { waitForActiveOperations: true, stopRuntime: false }),\n ),\n );\n } finally {\n activeShutdowns -= 1;\n }\n}\n\nfunction executionSignal(options?: ExecOptions): AbortSignal | undefined {\n const signals = [options?.signal];\n if (options?.timeout && options.timeout > 0) {\n signals.push(AbortSignal.timeout(options.timeout * 1000));\n }\n const active = signals.filter((signal): signal is AbortSignal => signal !== undefined);\n return active.length > 0 ? AbortSignal.any(active) : undefined;\n}\n\nexport class GondolinExecutor implements Executor {\n private readonly workspacePath: string;\n private readonly instanceId: string;\n\n constructor(\n private readonly config: GondolinSandboxConfig,\n private readonly env?: Record<string, string>,\n ) {\n // remote runtimes never load gondolin in this process, so the mikan host\n // may stay on the supported Node floor\n if (config.profile !== \"remote\") assertSupportedNodeVersion();\n this.workspacePath = config.workspacePath ?? process.cwd();\n this.instanceId = config.instanceId ?? this.workspacePath;\n }\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n return withRuntime(this.instanceId, this.config, (handle) =>\n transportFor(this.config).exec(handle, withRuntimeBootstrap(command, this.env), {\n env: this.env,\n signal: executionSignal(options),\n }),\n );\n }\n\n async readFile(path: string): Promise<string> {\n return execReadFile(this, path);\n }\n\n async readFileBase64(path: string): Promise<string> {\n return execReadFileBase64(this, path);\n }\n\n async writeFile(path: string, content: string): Promise<void> {\n return execWriteFile(this, path, content);\n }\n\n getWorkspacePath(): string {\n return \"/workspace\";\n }\n\n getPathContext(hostWorkspaceRoot: string): RuntimePathContext {\n return createMountedRuntimePathContext(hostWorkspaceRoot, \"/workspace\");\n }\n\n getSandboxConfig(): GondolinSandboxConfig {\n return this.config;\n }\n}\n\nexport const gondolinSandboxAdapter: SandboxAdapter<GondolinSandboxConfig> = {\n type: \"gondolin\",\n parse: parseGondolinSandboxArg,\n validate: validateGondolinSandbox,\n createExecutor: (config, env) => new GondolinExecutor(config, env),\n};\n"]}
|
package/dist/sandbox/host.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ExecOptions, ExecResult, Executor, HostSandboxConfig, RuntimePathContext, SandboxAdapter } from "./types.js";
|
|
2
2
|
export declare class HostExecutor implements Executor {
|
|
3
3
|
exec(command: string, options?: ExecOptions): Promise<ExecResult>;
|
|
4
|
+
readFile(path: string): Promise<string>;
|
|
5
|
+
readFileBase64(path: string): Promise<string>;
|
|
6
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
4
7
|
getWorkspacePath(hostPath: string): string;
|
|
5
8
|
getPathContext(hostWorkspaceRoot: string): RuntimePathContext;
|
|
6
9
|
getSandboxConfig(): HostSandboxConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/sandbox/host.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/sandbox/host.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACf,MAAM,YAAY,CAAC;AAWpB,qBAAa,YAAa,YAAW,QAAQ;IACrC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAuEtE;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,CAK5D;IAED,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzC;IAED,cAAc,CAAC,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,CAE5D;IAED,gBAAgB,IAAI,iBAAiB,CAEpC;CACF;AAED,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,iBAAiB,CAIhE,CAAC"}
|
package/dist/sandbox/host.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
|
+
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
3
|
+
import { dirname } from "path";
|
|
2
4
|
import { killProcessTree } from "./utils.js";
|
|
3
5
|
import { createMountedRuntimePathContext } from "./path-context.js";
|
|
4
6
|
function parseHostSandboxArg(value) {
|
|
@@ -67,6 +69,18 @@ export class HostExecutor {
|
|
|
67
69
|
});
|
|
68
70
|
});
|
|
69
71
|
}
|
|
72
|
+
async readFile(path) {
|
|
73
|
+
return readFile(path, "utf-8");
|
|
74
|
+
}
|
|
75
|
+
async readFileBase64(path) {
|
|
76
|
+
return (await readFile(path)).toString("base64");
|
|
77
|
+
}
|
|
78
|
+
async writeFile(path, content) {
|
|
79
|
+
await mkdir(dirname(path), { recursive: true });
|
|
80
|
+
const stage = `${path}.mikan-stage`;
|
|
81
|
+
await writeFile(stage, content, "utf-8");
|
|
82
|
+
await rename(stage, path);
|
|
83
|
+
}
|
|
70
84
|
getWorkspacePath(hostPath) {
|
|
71
85
|
return hostPath;
|
|
72
86
|
}
|