windows-development-sandbox 1.7.0 → 1.10.0
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/config.example.json +12 -1
- package/dist/app/context.d.ts +8 -0
- package/dist/app/context.js +12 -0
- package/dist/app/features/defs.d.ts +322 -0
- package/dist/app/features/defs.js +28 -0
- package/dist/app/features/sandbox/index.d.ts +2 -0
- package/dist/app/features/sandbox/index.js +3 -0
- package/dist/app/features/sandbox/sandbox.def.d.ts +193 -0
- package/dist/app/features/sandbox/sandbox.def.js +245 -0
- package/dist/app/features/sandbox/sandbox.run.d.ts +72 -0
- package/dist/app/features/sandbox/sandbox.run.js +98 -0
- package/dist/app/features/sessions/index.d.ts +2 -0
- package/dist/app/features/sessions/index.js +3 -0
- package/dist/app/features/sessions/sessions.def.d.ts +141 -0
- package/dist/app/features/sessions/sessions.def.js +176 -0
- package/dist/app/features/sessions/sessions.run.d.ts +39 -0
- package/dist/app/features/sessions/sessions.run.js +58 -0
- package/dist/{cli/menu/settings/categories.d.ts → app/features/settings/categories.def.d.ts} +2 -0
- package/dist/{cli/menu/settings/categories.js → app/features/settings/categories.def.js} +24 -5
- package/dist/app/features/settings/index.d.ts +5 -0
- package/dist/app/features/settings/index.js +6 -0
- package/dist/{cli/menu/settings/config-file.d.ts → app/features/settings/settings-file.d.ts} +1 -1
- package/dist/{cli/menu/settings/config-file.js → app/features/settings/settings-file.js} +1 -1
- package/dist/app/features/settings/settings-rows.d.ts +35 -0
- package/dist/app/features/settings/settings-rows.js +94 -0
- package/dist/app/features/settings/settings.def.d.ts +157 -0
- package/dist/app/features/settings/settings.def.js +179 -0
- package/dist/app/features/settings/settings.run.d.ts +67 -0
- package/dist/app/features/settings/settings.run.js +84 -0
- package/dist/app/kernel/define.d.ts +143 -0
- package/dist/app/kernel/define.js +42 -0
- package/dist/app/kernel/errors.d.ts +34 -0
- package/dist/app/kernel/errors.js +45 -0
- package/dist/app/kernel/fields.d.ts +70 -0
- package/dist/app/kernel/fields.js +243 -0
- package/dist/app/kernel/index.d.ts +8 -0
- package/dist/app/kernel/index.js +10 -0
- package/dist/app/kernel/io.d.ts +193 -0
- package/dist/app/kernel/io.js +126 -0
- package/dist/app/kernel/rpc.d.ts +156 -0
- package/dist/app/kernel/rpc.js +47 -0
- package/dist/app/kernel/text.d.ts +6 -0
- package/dist/app/kernel/text.js +57 -0
- package/dist/app/kernel/view.d.ts +146 -0
- package/dist/app/kernel/view.js +28 -0
- package/dist/app/registry.d.ts +10 -0
- package/dist/app/registry.js +50 -0
- package/dist/cli/args.d.ts +5 -0
- package/dist/cli/args.js +6 -0
- package/dist/cli/commands/engine.d.ts +2 -0
- package/dist/cli/commands/engine.js +60 -0
- package/dist/cli/commands/index.d.ts +1 -1
- package/dist/cli/commands/index.js +1 -1
- package/dist/cli/commands/launch/launch.js +49 -7
- package/dist/cli/commands/launch/prepare.js +3 -3
- package/dist/cli/commands/launch/running.js +2 -1
- package/dist/cli/commands/launch/summary.d.ts +2 -0
- package/dist/cli/commands/maintenance/config-share.d.ts +12 -0
- package/dist/cli/commands/maintenance/config-share.js +80 -0
- package/dist/cli/commands/maintenance/config.js +8 -82
- package/dist/cli/commands/maintenance/logs.js +14 -0
- package/dist/cli/commands/registry.js +3 -7
- package/dist/cli/commands/repair/checks/contained.d.ts +2 -0
- package/dist/cli/commands/repair/checks/contained.js +139 -0
- package/dist/cli/commands/repair/checks/hcs.d.ts +7 -0
- package/dist/cli/commands/repair/checks/hcs.js +28 -0
- package/dist/cli/commands/repair/checks/index.js +4 -0
- package/dist/cli/commands/repair/checks/pc.js +32 -1
- package/dist/cli/commands/repair/checks/sandbox.js +16 -1
- package/dist/cli/commands/repair/checks/vpn.js +34 -9
- package/dist/cli/commands/repair/env.js +2 -1
- package/dist/cli/commands/sessions/index.d.ts +0 -4
- package/dist/cli/commands/sessions/index.js +3 -7
- package/dist/cli/commands/shared-use.d.ts +5 -2
- package/dist/cli/commands/shared-use.js +11 -8
- package/dist/cli/commands/storage/contained.d.ts +3 -0
- package/dist/cli/commands/storage/contained.js +179 -0
- package/dist/cli/commands/storage/offer.js +7 -1
- package/dist/cli/commands/storage/storage.js +10 -2
- package/dist/cli/commands/user-config.js +1 -1
- package/dist/cli/commands/vpn.js +20 -3
- package/dist/cli/commands/web.d.ts +5 -0
- package/dist/cli/commands/web.js +40 -0
- package/dist/cli/index.js +2 -1
- package/dist/cli/manifest.d.ts +72 -104
- package/dist/cli/manifest.js +64 -39
- package/dist/cli/menu/add-agent.d.ts +1 -1
- package/dist/cli/menu/add-agent.js +1 -1
- package/dist/cli/menu/apps.d.ts +7 -1
- package/dist/cli/menu/apps.js +36 -0
- package/dist/cli/menu/command-fields.d.ts +1 -1
- package/dist/cli/menu/command-form.js +2 -2
- package/dist/cli/menu/io-menu.d.ts +29 -0
- package/dist/cli/menu/io-menu.js +126 -0
- package/dist/cli/menu/live.d.ts +27 -0
- package/dist/cli/menu/live.js +69 -0
- package/dist/cli/menu/memory.d.ts +19 -0
- package/dist/cli/menu/memory.js +53 -0
- package/dist/cli/menu/menu.d.ts +10 -1
- package/dist/cli/menu/menu.js +27 -9
- package/dist/cli/menu/pages/agents-page.d.ts +3 -1
- package/dist/cli/menu/pages/agents-page.js +12 -5
- package/dist/cli/menu/pages/apps-page.d.ts +3 -1
- package/dist/cli/menu/pages/apps-page.js +19 -2
- package/dist/cli/menu/pages/list-page.d.ts +27 -0
- package/dist/cli/menu/pages/list-page.js +109 -7
- package/dist/cli/menu/pages/maintenance-page.d.ts +0 -5
- package/dist/cli/menu/pages/maintenance-page.js +17 -19
- package/dist/cli/menu/pages/sandbox-page.d.ts +4 -6
- package/dist/cli/menu/pages/sandbox-page.js +26 -118
- package/dist/cli/menu/pages/scopes.d.ts +1 -1
- package/dist/cli/menu/pages/sessions-page.d.ts +5 -11
- package/dist/cli/menu/pages/sessions-page.js +19 -88
- package/dist/cli/menu/pages/settings-page.d.ts +2 -10
- package/dist/cli/menu/pages/settings-page.js +64 -94
- package/dist/cli/menu/render-view.d.ts +28 -0
- package/dist/cli/menu/render-view.js +112 -0
- package/dist/cli/menu/settings/{path-kind.d.ts → ask-path.d.ts} +1 -7
- package/dist/cli/menu/settings/ask-path.js +14 -0
- package/dist/cli/menu/settings/collections/collection-editor.d.ts +1 -1
- package/dist/cli/menu/settings/collections/string-list-editor.d.ts +1 -2
- package/dist/cli/menu/settings/collections/string-list-editor.js +1 -1
- package/dist/cli/menu/settings/draft.d.ts +1 -1
- package/dist/cli/menu/settings/format.d.ts +1 -3
- package/dist/cli/menu/settings/format.js +6 -36
- package/dist/cli/menu/settings/index.d.ts +1 -3
- package/dist/cli/menu/settings/index.js +3 -3
- package/dist/cli/menu/settings/object-editor.d.ts +1 -1
- package/dist/cli/menu/settings/share.d.ts +36 -0
- package/dist/cli/menu/settings/share.js +131 -0
- package/dist/cli/menu/settings/value-editor.d.ts +5 -1
- package/dist/cli/menu/settings/value-editor.js +57 -54
- package/dist/cli/menu/several.js +1 -1
- package/dist/cli/menu/share-settings.d.ts +21 -0
- package/dist/cli/menu/share-settings.js +112 -0
- package/dist/cli/menu/shell.d.ts +21 -5
- package/dist/cli/menu/shell.js +31 -6
- package/dist/cli/menu/views.d.ts +7 -0
- package/dist/cli/menu/views.js +11 -0
- package/dist/cli/menu/vpn-login.js +8 -0
- package/dist/cli/operations/handler.d.ts +6 -0
- package/dist/cli/operations/handler.js +61 -0
- package/dist/cli/operations/index.d.ts +4 -0
- package/dist/cli/operations/index.js +5 -0
- package/dist/cli/operations/input.d.ts +6 -0
- package/dist/cli/operations/input.js +37 -0
- package/dist/cli/operations/io-cli.d.ts +7 -0
- package/dist/cli/operations/io-cli.js +62 -0
- package/dist/cli/operations/spec.d.ts +18 -0
- package/dist/cli/operations/spec.js +108 -0
- package/dist/cli/output/colors.d.ts +2 -2
- package/dist/cli/output/colors.js +2 -11
- package/dist/cli/output/index.d.ts +1 -0
- package/dist/cli/output/index.js +1 -0
- package/dist/cli/output/print.d.ts +3 -1
- package/dist/cli/output/print.js +7 -3
- package/dist/cli/output/progress.d.ts +15 -0
- package/dist/cli/output/progress.js +44 -0
- package/dist/cli/output/render-view.d.ts +4 -0
- package/dist/cli/output/render-view.js +63 -0
- package/dist/cli/services/agent-commands-service.d.ts +2 -0
- package/dist/cli/services/agent-commands-service.js +11 -2
- package/dist/cli/services/logs-service.js +4 -3
- package/dist/cli/tui/app.d.ts +6 -1
- package/dist/cli/tui/app.js +26 -4
- package/dist/cli/tui/list/list-screen.d.ts +14 -0
- package/dist/cli/tui/list/list-screen.js +64 -36
- package/dist/cli/tui/list/list-types.d.ts +2 -0
- package/dist/cli/tui/terminal.d.ts +7 -0
- package/dist/cli/tui/terminal.js +30 -10
- package/dist/core/config/defaults.js +9 -1
- package/dist/core/schema/config.d.ts +14 -0
- package/dist/core/schema/config.js +16 -1
- package/dist/core/schema/harness.d.ts +1 -0
- package/dist/core/schema/harness.js +5 -0
- package/dist/core/schema/sandbox.d.ts +6 -0
- package/dist/core/schema/sandbox.js +20 -3
- package/dist/core/schema/vpn.d.ts +2 -1
- package/dist/core/schema/vpn.js +4 -4
- package/dist/core/schema/web.d.ts +1 -0
- package/dist/core/schema/web.js +4 -1
- package/dist/core/types.d.ts +23 -5
- package/dist/core/util/fs.d.ts +2 -0
- package/dist/core/util/fs.js +9 -0
- package/dist/core/util/index.d.ts +1 -1
- package/dist/core/util/index.js +1 -1
- package/dist/guest/agents/agent-files.d.ts +5 -0
- package/dist/guest/agents/agent-files.js +4 -0
- package/dist/guest/agents/local-state.d.ts +2 -0
- package/dist/guest/agents/local-state.js +21 -1
- package/dist/guest/boot/boot.js +5 -2
- package/dist/guest/daemon/daemon.js +4 -0
- package/dist/guest/daemon/sessions.d.ts +5 -0
- package/dist/guest/daemon/sessions.js +17 -3
- package/dist/guest/entries/daemon.js +3 -0
- package/dist/guest/governor/decide.d.ts +71 -0
- package/dist/guest/governor/decide.js +211 -0
- package/dist/guest/governor/governor.d.ts +45 -0
- package/dist/guest/governor/governor.js +230 -0
- package/dist/guest/governor/index.d.ts +2 -0
- package/dist/guest/governor/index.js +2 -0
- package/dist/guest/network/firewall.d.ts +1 -0
- package/dist/guest/network/firewall.js +65 -23
- package/dist/guest/runtime/csc.d.ts +1 -0
- package/dist/guest/runtime/csc.js +1 -0
- package/dist/guest/runtime/process.js +53 -5
- package/dist/guest/sessions/viewer.js +3 -1
- package/dist/guest/setup/claude-mem.js +12 -4
- package/dist/guest/toolchains/languages/node.js +29 -3
- package/dist/guest/toolchains/native/rust-extras.js +4 -2
- package/dist/guest/vpn/vpn.js +69 -21
- package/dist/guest/vpn/wireguard.d.ts +32 -0
- package/dist/guest/vpn/wireguard.js +132 -0
- package/dist/host/agent-commands/commands.d.ts +4 -0
- package/dist/host/agent-commands/commands.js +31 -7
- package/dist/host/audit/log.d.ts +1 -1
- package/dist/host/channel/attach.d.ts +3 -1
- package/dist/host/channel/attach.js +5 -2
- package/dist/host/channel/channel.d.ts +2 -0
- package/dist/host/channel/channel.js +4 -0
- package/dist/host/engines/engine.d.ts +12 -0
- package/dist/host/engines/engine.js +1 -0
- package/dist/host/engines/hcs/check.d.ts +25 -0
- package/dist/host/engines/hcs/check.js +95 -0
- package/dist/host/engines/hcs/documents.d.ts +44 -0
- package/dist/host/engines/hcs/documents.js +155 -0
- package/dist/host/engines/hcs/engine.d.ts +39 -0
- package/dist/host/engines/hcs/engine.js +171 -0
- package/dist/host/engines/hcs/helper.d.ts +43 -0
- package/dist/host/engines/hcs/helper.js +173 -0
- package/dist/host/engines/hcs/images.d.ts +37 -0
- package/dist/host/engines/hcs/images.js +152 -0
- package/dist/host/engines/index.d.ts +7 -0
- package/dist/host/engines/index.js +5 -0
- package/dist/host/engines/registry.d.ts +16 -0
- package/dist/host/engines/registry.js +30 -0
- package/dist/host/engines/types.d.ts +58 -0
- package/dist/host/engines/types.js +1 -0
- package/dist/host/engines/windows-sandbox/engine.d.ts +16 -0
- package/dist/host/engines/windows-sandbox/engine.js +63 -0
- package/dist/host/engines/windows-sandbox/wsb-text.d.ts +2 -0
- package/dist/host/engines/windows-sandbox/wsb-text.js +29 -0
- package/dist/host/launch/generate.d.ts +5 -1
- package/dist/host/launch/generate.js +50 -8
- package/dist/host/launch/index.d.ts +3 -1
- package/dist/host/launch/index.js +3 -1
- package/dist/host/launch/launch-plan.d.ts +13 -0
- package/dist/host/launch/launch-plan.js +26 -0
- package/dist/host/launch/mappings.js +2 -1
- package/dist/host/launch/risk-notes.d.ts +10 -0
- package/dist/host/launch/risk-notes.js +18 -0
- package/dist/host/launch/session-file.d.ts +2 -0
- package/dist/host/launch/session-file.js +7 -1
- package/dist/host/launch/wsb-file.d.ts +28 -4
- package/dist/host/launch/wsb-file.js +61 -42
- package/dist/host/life/crash-report.d.ts +27 -1
- package/dist/host/life/crash-report.js +173 -7
- package/dist/host/life/host-memory.d.ts +41 -0
- package/dist/host/life/host-memory.js +140 -0
- package/dist/host/life/index.d.ts +2 -0
- package/dist/host/life/index.js +2 -0
- package/dist/host/life/stop-events.d.ts +2 -0
- package/dist/host/life/stop-events.js +20 -3
- package/dist/host/life/stop-warning.js +1 -1
- package/dist/host/life/watchdog.d.ts +56 -0
- package/dist/host/life/watchdog.js +124 -0
- package/dist/host/native.d.ts +3 -2
- package/dist/host/native.js +22 -3
- package/dist/host/profile/claude-login.js +3 -2
- package/dist/host/sandbox/contained-deps.d.ts +2 -0
- package/dist/host/sandbox/contained-deps.js +17 -0
- package/dist/host/sandbox/index.d.ts +2 -0
- package/dist/host/sandbox/index.js +2 -0
- package/dist/host/sandbox/platform.d.ts +2 -0
- package/dist/host/sandbox/platform.js +7 -7
- package/dist/host/sandbox/tasklist.d.ts +13 -0
- package/dist/host/sandbox/tasklist.js +47 -0
- package/dist/host/secrets/launch-secrets.js +2 -1
- package/dist/host/sharing/current.js +2 -6
- package/dist/host/storage/contained/check.d.ts +9 -0
- package/dist/host/storage/contained/check.js +37 -0
- package/dist/host/storage/contained/contained-storage.d.ts +66 -0
- package/dist/host/storage/contained/contained-storage.js +168 -0
- package/dist/host/storage/contained/disk-port.d.ts +43 -0
- package/dist/host/storage/contained/disk-port.js +122 -0
- package/dist/host/storage/contained/index.d.ts +9 -0
- package/dist/host/storage/contained/index.js +8 -0
- package/dist/host/storage/contained/layout.d.ts +28 -0
- package/dist/host/storage/contained/layout.js +45 -0
- package/dist/host/storage/contained/resolve.d.ts +4 -0
- package/dist/host/storage/contained/resolve.js +30 -0
- package/dist/host/storage/contained/setting.d.ts +1 -0
- package/dist/host/storage/contained/setting.js +5 -0
- package/dist/host/storage/contained/state.d.ts +89 -0
- package/dist/host/storage/contained/state.js +166 -0
- package/dist/host/storage/contained/transfer.d.ts +24 -0
- package/dist/host/storage/contained/transfer.js +121 -0
- package/dist/host/storage/index.d.ts +2 -0
- package/dist/host/storage/index.js +2 -0
- package/dist/host/storage/providers/contained.d.ts +15 -0
- package/dist/host/storage/providers/contained.js +39 -0
- package/dist/host/storage/providers/folder.d.ts +12 -0
- package/dist/host/storage/providers/folder.js +20 -0
- package/dist/host/storage/providers/index.d.ts +4 -0
- package/dist/host/storage/providers/index.js +3 -0
- package/dist/host/storage/providers/registry.d.ts +9 -0
- package/dist/host/storage/providers/registry.js +14 -0
- package/dist/host/storage/providers/types.d.ts +33 -0
- package/dist/host/storage/providers/types.js +1 -0
- package/dist/host/tools/conpty.js +3 -2
- package/dist/host/tools/windows-terminal.js +8 -7
- package/dist/host/vpn/index.d.ts +1 -1
- package/dist/host/vpn/index.js +1 -1
- package/dist/host/vpn/prepare.d.ts +2 -0
- package/dist/host/vpn/prepare.js +63 -2
- package/dist/host/web/browser.d.ts +2 -0
- package/dist/host/web/browser.js +14 -1
- package/dist/host/web/index.d.ts +3 -2
- package/dist/host/web/index.js +3 -2
- package/dist/host/web/rpc.d.ts +24 -0
- package/dist/host/web/rpc.js +149 -0
- package/dist/host/web/runner.d.ts +2 -0
- package/dist/host/web/runner.js +1 -2
- package/dist/host/web/server.d.ts +7 -0
- package/dist/host/web/server.js +23 -3
- package/dist/runtime/agent.js +83 -18
- package/dist/runtime/daemon.js +1805 -1221
- package/dist/runtime/db.js +83 -18
- package/dist/runtime/main.js +779 -219
- package/dist/runtime/netproxy.js +73 -11
- package/dist/runtime/view.js +91 -16
- package/dist/selftest/agent-paths.test.js +11 -1
- package/dist/selftest/app.test.d.ts +2 -0
- package/dist/selftest/app.test.js +324 -0
- package/dist/selftest/channel.test.js +13 -0
- package/dist/selftest/cli.test.js +2 -1
- package/dist/selftest/config.test.js +59 -0
- package/dist/selftest/contained.test.d.ts +2 -0
- package/dist/selftest/contained.test.js +544 -0
- package/dist/selftest/credentials.test.js +1 -1
- package/dist/selftest/engine.test.d.ts +2 -0
- package/dist/selftest/engine.test.js +127 -0
- package/dist/selftest/guest-boot.test.js +47 -1
- package/dist/selftest/hcs.test.d.ts +2 -0
- package/dist/selftest/hcs.test.js +482 -0
- package/dist/selftest/heavy-load.test.d.ts +2 -0
- package/dist/selftest/heavy-load.test.js +355 -0
- package/dist/selftest/index.js +12 -0
- package/dist/selftest/layout.test.js +12 -0
- package/dist/selftest/menu-perf.test.d.ts +2 -0
- package/dist/selftest/menu-perf.test.js +248 -0
- package/dist/selftest/menu-screens.js +4 -2
- package/dist/selftest/menu.test.js +36 -8
- package/dist/selftest/network.test.js +20 -1
- package/dist/selftest/support/engine-cases.d.ts +8 -0
- package/dist/selftest/support/engine-cases.js +65 -0
- package/dist/selftest/support/engine-golden.d.ts +3 -0
- package/dist/selftest/support/engine-golden.js +66 -0
- package/dist/selftest/support/fixtures.d.ts +8 -1
- package/dist/selftest/support/hcs-probes.d.ts +17 -0
- package/dist/selftest/support/hcs-probes.js +41 -0
- package/dist/selftest/vpn.test.js +136 -3
- package/dist/selftest/web.test.js +101 -3
- package/dist/shared/channel/run-dir.d.ts +4 -1
- package/dist/shared/channel/run-dir.js +7 -1
- package/dist/shared/channel/schema.d.ts +35 -1
- package/dist/shared/channel/schema.js +27 -0
- package/dist/shared/channel/secrets.d.ts +1 -0
- package/dist/shared/channel/secrets.js +6 -0
- package/dist/shared/io/copy-tree.d.ts +7 -0
- package/dist/shared/io/copy-tree.js +45 -0
- package/dist/shared/io/index.d.ts +2 -0
- package/dist/shared/io/index.js +2 -0
- package/dist/shared/io/robocopy-result.d.ts +6 -0
- package/dist/shared/io/robocopy-result.js +23 -0
- package/dist/shared/memory/index.d.ts +1 -0
- package/dist/shared/memory/index.js +1 -0
- package/dist/shared/memory/policy.d.ts +38 -0
- package/dist/shared/memory/policy.js +63 -0
- package/dist/shared/network/firewall-rules.d.ts +8 -0
- package/dist/shared/network/firewall-rules.js +28 -0
- package/dist/shared/pty/output-filter.d.ts +14 -1
- package/dist/shared/pty/output-filter.js +22 -4
- package/dist/shared/pty/terminal.d.ts +4 -1
- package/dist/shared/pty/terminal.js +4 -2
- package/dist/shared/system.d.ts +1 -0
- package/dist/shared/system.js +1 -0
- package/dist/shared/vpn/download.d.ts +16 -0
- package/dist/shared/vpn/download.js +16 -0
- package/dist/shared/vpn/wireguard.d.ts +51 -0
- package/dist/shared/vpn/wireguard.js +227 -0
- package/dist/sync/sync-cycle.js +5 -5
- package/dist/sync/watcher.d.ts +13 -0
- package/dist/sync/watcher.js +92 -3
- package/dist/web/index.html +4 -3
- package/dist/web/main-NRRS5O6Z.js +157 -0
- package/dist/web/main-T5DMCHYV.css +1 -0
- package/native/csharp/HcsHost/AssemblyInfo.cs +14 -0
- package/native/csharp/HcsHost/Guard.cs +100 -0
- package/native/csharp/HcsHost/Hcs.cs +256 -0
- package/native/csharp/HcsHost/Json.cs +78 -0
- package/native/csharp/HcsHost/Program.cs +185 -0
- package/native/csharp/HcsHost/VirtualDisk.cs +95 -0
- package/native/csharp/HostTools/AssemblyInfo.cs +14 -0
- package/native/csharp/HostTools/MemoryStatus.cs +128 -0
- package/native/csharp/HostTools/Program.cs +58 -17
- package/native/scripts/check-contained.ps1 +28 -0
- package/native/scripts/contained-common.ps1 +241 -0
- package/native/scripts/dismount-contained.ps1 +66 -0
- package/native/scripts/memory-status.ps1 +42 -0
- package/native/scripts/mount-contained.ps1 +139 -0
- package/native/scripts/run-elevated.ps1 +17 -4
- package/native/scripts/stop-events.ps1 +23 -8
- package/package.json +9 -2
- package/sandbox/csharp/MemGov.cs +466 -0
- package/sandbox/csharp/PtyHost.cs +111 -5
- package/sandbox/scripts/new-shortcuts.ps1 +3 -1
- package/schemas/config.schema.json +66 -8
- package/schemas/sandbox-config.schema.json +3 -3
- package/dist/cli/commands/sessions/control.d.ts +0 -4
- package/dist/cli/commands/sessions/control.js +0 -66
- package/dist/cli/commands/sessions/list.d.ts +0 -2
- package/dist/cli/commands/sessions/list.js +0 -48
- package/dist/cli/commands/sessions/status.d.ts +0 -4
- package/dist/cli/commands/sessions/status.js +0 -114
- package/dist/cli/commands/sessions/window.d.ts +0 -3
- package/dist/cli/commands/sessions/window.js +0 -18
- package/dist/cli/menu/settings/path-kind.js +0 -44
- package/dist/web/main-CXESL347.js +0 -45
- package/dist/web/main-VLR7DVQ4.css +0 -1
- /package/dist/{cli/menu/settings → app/kernel}/schema-node.d.ts +0 -0
- /package/dist/{cli/menu/settings → app/kernel}/schema-node.js +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EngineCapabilities, EngineCheck, EngineId, EnginePlan, LaunchPlan, RunningSandbox } from './types.js';
|
|
2
|
+
export interface SandboxEngine {
|
|
3
|
+
readonly id: EngineId;
|
|
4
|
+
capabilities(): EngineCapabilities;
|
|
5
|
+
check(): Promise<EngineCheck>;
|
|
6
|
+
/** Pure: the engine's own files for this launch. */
|
|
7
|
+
plan(launch: LaunchPlan): EnginePlan;
|
|
8
|
+
/** Starts the sandbox from a plan whose files are written, and waits until it is up. */
|
|
9
|
+
start(plan: EnginePlan): Promise<RunningSandbox>;
|
|
10
|
+
running(): Promise<boolean>;
|
|
11
|
+
stop(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { EngineCheck } from '../types.js';
|
|
2
|
+
import { type ImageStore } from './images.js';
|
|
3
|
+
/** Windows 10 1809: the first build with the Host Compute Service API (Microsoft HCS overview). */
|
|
4
|
+
export declare const MIN_BUILD = 17763;
|
|
5
|
+
export interface TokenGroups {
|
|
6
|
+
/** The token is an elevated member of Administrators. */
|
|
7
|
+
readonly elevatedAdministrator: boolean;
|
|
8
|
+
/** The token has Hyper-V Administrators (S-1-5-32-578). */
|
|
9
|
+
readonly hyperVAdministrators: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Everything the check looks at, so tests give it a fake PC. */
|
|
12
|
+
export interface HcsProbes {
|
|
13
|
+
isWindows(): boolean;
|
|
14
|
+
windowsBuild(): number | null;
|
|
15
|
+
/** Whether a Windows service exists; null when that could not be told. */
|
|
16
|
+
serviceInstalled(name: string): boolean | null;
|
|
17
|
+
fileExists(file: string): boolean;
|
|
18
|
+
tokenGroups(): TokenGroups | null;
|
|
19
|
+
imageStores(): readonly ImageStore[];
|
|
20
|
+
}
|
|
21
|
+
export declare function checkHcs(p: HcsProbes): EngineCheck;
|
|
22
|
+
/** The groups of a token from `whoami /groups /fo csv /nh`: name, type, SID, attributes per line. */
|
|
23
|
+
export declare function parseWhoamiGroups(csv: string): TokenGroups;
|
|
24
|
+
/** The real probes: sc.exe query, whoami, file checks. They only read. */
|
|
25
|
+
export declare function realHcsProbes(): HcsProbes;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// The HCS engine's check: what this PC has and lacks. Read-only: it looks (service list, files, the token's groups)
|
|
2
|
+
// and answers with the reasons and the exact commands that would fix each one. It NEVER runs a fix: turning on a
|
|
3
|
+
// Windows feature or adding a group member is the owner's choice (docs/design/engines.md, "Never").
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
import { SYSTEM, system32 } from '../../../shared/system.js';
|
|
9
|
+
import { isWin } from '../../../core/util/index.js';
|
|
10
|
+
import { defaultImageStores, findImage } from './images.js';
|
|
11
|
+
/** Windows 10 1809: the first build with the Host Compute Service API (Microsoft HCS overview). */
|
|
12
|
+
export const MIN_BUILD = 17763;
|
|
13
|
+
const ENABLE = 'Enable-WindowsOptionalFeature -Online -All -FeatureName ';
|
|
14
|
+
export function checkHcs(p) {
|
|
15
|
+
const reasons = [];
|
|
16
|
+
const fixes = [];
|
|
17
|
+
const need = (reason, fix) => {
|
|
18
|
+
reasons.push(reason);
|
|
19
|
+
fixes.push(fix);
|
|
20
|
+
};
|
|
21
|
+
if (!p.isWindows())
|
|
22
|
+
return { available: false, reasons: ['The HCS engine only runs on Windows.'], fixes: ['Use "windows-sandbox".'] };
|
|
23
|
+
const build = p.windowsBuild();
|
|
24
|
+
if (build !== null && build < MIN_BUILD)
|
|
25
|
+
need('Windows build ' +
|
|
26
|
+
build +
|
|
27
|
+
' is too old: the Host Compute Service needs Windows 10 1809 (build ' +
|
|
28
|
+
MIN_BUILD +
|
|
29
|
+
') or newer.', 'Update Windows.');
|
|
30
|
+
if (!p.fileExists(path.join(system32Dir(), 'computecore.dll')))
|
|
31
|
+
need('computecore.dll (the Host Compute Service API) is not on this PC.', 'It comes with the Windows virtualization features (not in Windows Home). In an admin PowerShell, then restart: ' +
|
|
32
|
+
ENABLE +
|
|
33
|
+
'VirtualMachinePlatform (the owner decides: this changes Windows)');
|
|
34
|
+
if (p.serviceInstalled('vmcompute') === false)
|
|
35
|
+
need('The Hyper-V Host Compute Service (vmcompute) is not installed.', 'In an admin PowerShell, then restart: ' +
|
|
36
|
+
ENABLE +
|
|
37
|
+
'Microsoft-Hyper-V-All (needs Windows Pro, Enterprise or Education)');
|
|
38
|
+
if (p.serviceInstalled('cexecsvc') === false)
|
|
39
|
+
need('The Windows Containers feature is not turned on (its Container Execution Agent service is missing).', 'In an admin PowerShell, then restart: ' + ENABLE + 'Containers');
|
|
40
|
+
const token = p.tokenGroups();
|
|
41
|
+
if (token && !token.elevatedAdministrator && !token.hyperVAdministrators)
|
|
42
|
+
need('This Windows user is neither an elevated Administrator nor in Hyper-V Administrators, and the Host Compute Service refuses anyone else (HCS_E_ACCESS_DENIED, https://aka.ms/hcsadmin).', 'Either start the terminal as administrator, or in an admin PowerShell add yourself, then sign out and in: net localgroup "Hyper-V Administrators" "%USERDOMAIN%\\%USERNAME%" /add');
|
|
43
|
+
const image = findImage(p.imageStores());
|
|
44
|
+
if (!image.image)
|
|
45
|
+
need('No Windows Server Core (ltsc2022) image is on this PC. ' + image.reason, 'Pull one with Docker Desktop (Windows containers mode): docker pull mcr.microsoft.com/windows/servercore:ltsc2022 - or put your own in a folder with an image.json and set AGENT_SANDBOX_HCS_IMAGE to it. This tool does not download images in this alpha.');
|
|
46
|
+
return { available: reasons.length === 0, reasons, fixes };
|
|
47
|
+
}
|
|
48
|
+
function system32Dir() {
|
|
49
|
+
return path.dirname(system32('x'));
|
|
50
|
+
}
|
|
51
|
+
/** The groups of a token from `whoami /groups /fo csv /nh`: name, type, SID, attributes per line. */
|
|
52
|
+
export function parseWhoamiGroups(csv) {
|
|
53
|
+
let elevated = false;
|
|
54
|
+
let hyperV = false;
|
|
55
|
+
for (const line of csv.split(/\r?\n/)) {
|
|
56
|
+
const cells = [...line.matchAll(/"([^"]*)"/g)].map((m) => m[1]);
|
|
57
|
+
if (cells.length < 4)
|
|
58
|
+
continue;
|
|
59
|
+
const [, , sid, attributes] = cells;
|
|
60
|
+
const denyOnly = /deny only/i.test(attributes);
|
|
61
|
+
if (sid === 'S-1-5-32-544' && !denyOnly)
|
|
62
|
+
elevated = true;
|
|
63
|
+
if (sid === 'S-1-5-32-578' && !denyOnly)
|
|
64
|
+
hyperV = true;
|
|
65
|
+
}
|
|
66
|
+
return { elevatedAdministrator: elevated, hyperVAdministrators: hyperV };
|
|
67
|
+
}
|
|
68
|
+
const QUERY_TIMEOUT_MS = 10000;
|
|
69
|
+
/** The real probes: sc.exe query, whoami, file checks. They only read. */
|
|
70
|
+
export function realHcsProbes() {
|
|
71
|
+
return {
|
|
72
|
+
isWindows: () => isWin,
|
|
73
|
+
windowsBuild: () => {
|
|
74
|
+
const m = /^\d+\.\d+\.(\d+)/.exec(os.release());
|
|
75
|
+
return m ? Number(m[1]) : null;
|
|
76
|
+
},
|
|
77
|
+
serviceInstalled: (name) => {
|
|
78
|
+
const r = spawnSync(SYSTEM.sc(), ['query', name], { windowsHide: true, timeout: QUERY_TIMEOUT_MS });
|
|
79
|
+
if (r.status === 0)
|
|
80
|
+
return true;
|
|
81
|
+
return r.status === 1060 ? false : null; // 1060: the service does not exist
|
|
82
|
+
},
|
|
83
|
+
fileExists: (file) => fs.existsSync(file),
|
|
84
|
+
tokenGroups: () => {
|
|
85
|
+
const r = spawnSync(SYSTEM.whoami(), ['/groups', '/fo', 'csv', '/nh'], {
|
|
86
|
+
windowsHide: true,
|
|
87
|
+
timeout: QUERY_TIMEOUT_MS,
|
|
88
|
+
encoding: 'utf8',
|
|
89
|
+
maxBuffer: 1024 * 1024,
|
|
90
|
+
});
|
|
91
|
+
return r.status === 0 ? parseWhoamiGroups(r.stdout) : null;
|
|
92
|
+
},
|
|
93
|
+
imageStores: () => defaultImageStores(),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { LaunchPlan } from '../types.js';
|
|
2
|
+
export declare const HCS_OWNER = "agent-sandbox";
|
|
3
|
+
/** The Container's Windows-side folder for the agent disk's own sub-folders. */
|
|
4
|
+
export declare const AGENT_DISK_FOLDERS: readonly ["tools", "state", "logs"];
|
|
5
|
+
export type HcsDocument = Readonly<Record<string, unknown>>;
|
|
6
|
+
/** A base image already on this PC. Layers are the folders of the image, base layer first. */
|
|
7
|
+
export interface HcsImage {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
/** Folders of the image layers, base first. */
|
|
10
|
+
readonly layers: readonly string[];
|
|
11
|
+
/** The base layer's UtilityVM\Files folder (the kernel and files of the utility VM). */
|
|
12
|
+
readonly utilityVmFiles: string;
|
|
13
|
+
}
|
|
14
|
+
export interface HcsDocumentInput {
|
|
15
|
+
readonly launch: LaunchPlan;
|
|
16
|
+
readonly image: HcsImage;
|
|
17
|
+
/** The utility VM's id; the container's is `<id>-c`. [a-zA-Z0-9._-] only. */
|
|
18
|
+
readonly id: string;
|
|
19
|
+
/** The writable layer (scratch) VHDX of this run: made before start, deleted after. */
|
|
20
|
+
readonly scratchVhdx: string;
|
|
21
|
+
/** Processors of this PC, to turn a CPU percentage into a count. */
|
|
22
|
+
readonly hostCpus: number;
|
|
23
|
+
}
|
|
24
|
+
export interface HcsDiskPlan {
|
|
25
|
+
readonly image: string;
|
|
26
|
+
readonly maxGb: number;
|
|
27
|
+
readonly lun: number;
|
|
28
|
+
}
|
|
29
|
+
export interface HcsDocuments {
|
|
30
|
+
readonly uvmId: string;
|
|
31
|
+
readonly containerId: string;
|
|
32
|
+
readonly uvm: HcsDocument;
|
|
33
|
+
readonly container: HcsDocument;
|
|
34
|
+
/** The agent disks (read-write) to create, give the utility VM access to and attach. */
|
|
35
|
+
readonly disks: readonly HcsDiskPlan[];
|
|
36
|
+
/** Things the plan asked for that this engine does not do (shown to the user). */
|
|
37
|
+
readonly notes: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
/** A GUID made from a path, so a layer has the same id every time. [U] hcsshim names layers the same way. */
|
|
40
|
+
export declare function layerGuid(folder: string): string;
|
|
41
|
+
export declare function containerId(uvmId: string): string;
|
|
42
|
+
/** Processors for a percentage of this PC ('none' = all of them), at least one. */
|
|
43
|
+
export declare function cpuCount(limit: LaunchPlan['cpuLimit'], hostCpus: number): number;
|
|
44
|
+
export declare function buildDocuments(input: HcsDocumentInput): HcsDocuments;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Pure: the HCS compute-system documents (schema 2.x JSON) for a Hyper-V isolated Windows container, from a
|
|
2
|
+
// LaunchPlan.
|
|
3
|
+
//
|
|
4
|
+
// Two documents, as hcsshim does it for a container with Hyper-V isolation:
|
|
5
|
+
// 1. the utility VM: ComputeSystem { VirtualMachine { Chipset, ComputeTopology,
|
|
6
|
+
// Devices { Scsi, VirtualSmb, HvSocket }, GuestConnection } }
|
|
7
|
+
// 2. the container inside it: ComputeSystem { HostingSystemId, HostedSystem { Container { GuestOs, Storage, Memory,
|
|
8
|
+
// MappedDirectories, HvSocket } } }
|
|
9
|
+
//
|
|
10
|
+
// Schema reference used (learn.microsoft.com/virtualization/api/hcs/schemareference), field names and casing taken
|
|
11
|
+
// from these tables: ComputeSystem, VirtualMachine, Topology, VirtualMachine_Memory, VirtualMachine_Processor,
|
|
12
|
+
// Devices, Scsi, Attachment (AttachmentType "VirtualDisk", CachingMode), VirtualSmb, VirtualSmbShare,
|
|
13
|
+
// VirtualSmbShareOptions, VirtualMachine_HvSocket, HvSocketSystemConfig, Chipset, Uefi, UefiBootEntry,
|
|
14
|
+
// GuestConnection, HostedSystem, Container, GuestOs, Container_Memory, Layer (PathType "VirtualSmbShareName"),
|
|
15
|
+
// MappedDirectory (HostPathType), Container_HvSocket, Version.
|
|
16
|
+
//
|
|
17
|
+
// NOT verified (each is marked [U] where it is used, and listed in the report):
|
|
18
|
+
// - the boot entry of the utility VM and the "os" share of the base layer's UtilityVM\Files (hcsshim from memory);
|
|
19
|
+
// - the order of the layers (topmost first here);
|
|
20
|
+
// - where a SCSI disk appears inside the utility VM and how a blank VHDX becomes a formatted volume there;
|
|
21
|
+
// - the scratch layer (a writable layer VHDX made by HcsInitializeWritableLayer: not built in this phase);
|
|
22
|
+
// - that Layers / MappedDirectories may name vSMB shares by share name (the PathType values exist in the schema).
|
|
23
|
+
import crypto from 'node:crypto';
|
|
24
|
+
export const HCS_OWNER = 'agent-sandbox';
|
|
25
|
+
/** Schema 2.1 is the first with VirtualSmb options, SCSI caching and Layer.PathType (Windows 10 1809). */
|
|
26
|
+
const SCHEMA = { Major: 2, Minor: 1 };
|
|
27
|
+
/** The Container's Windows-side folder for the agent disk's own sub-folders. */
|
|
28
|
+
export const AGENT_DISK_FOLDERS = ['tools', 'state', 'logs'];
|
|
29
|
+
const GUEST_DATA = 'C:\\AgentData';
|
|
30
|
+
/** Placeholder SDDL: SYSTEM and Administrators only. [U] whether this ACL is what the host side of HvSocket needs. */
|
|
31
|
+
const HVSOCKET_SDDL = 'D:P(A;;FA;;;SY)(A;;FA;;;BA)';
|
|
32
|
+
/** A GUID made from a path, so a layer has the same id every time. [U] hcsshim names layers the same way. */
|
|
33
|
+
export function layerGuid(folder) {
|
|
34
|
+
const h = crypto.createHash('sha1').update(folder.toLowerCase()).digest('hex');
|
|
35
|
+
return [h.slice(0, 8), h.slice(8, 12), '5' + h.slice(13, 16), '8' + h.slice(17, 20), h.slice(20, 32)].join('-');
|
|
36
|
+
}
|
|
37
|
+
export function containerId(uvmId) {
|
|
38
|
+
return uvmId + '-c';
|
|
39
|
+
}
|
|
40
|
+
/** Processors for a percentage of this PC ('none' = all of them), at least one. */
|
|
41
|
+
export function cpuCount(limit, hostCpus) {
|
|
42
|
+
if (limit === 'none')
|
|
43
|
+
return Math.max(1, hostCpus);
|
|
44
|
+
return Math.min(Math.max(1, hostCpus), Math.max(1, Math.ceil((hostCpus * limit) / 100)));
|
|
45
|
+
}
|
|
46
|
+
const hvSocketConfig = {
|
|
47
|
+
DefaultBindSecurityDescriptor: HVSOCKET_SDDL,
|
|
48
|
+
DefaultConnectSecurityDescriptor: HVSOCKET_SDDL,
|
|
49
|
+
ServiceTable: {},
|
|
50
|
+
};
|
|
51
|
+
export function buildDocuments(input) {
|
|
52
|
+
const { launch, image } = input;
|
|
53
|
+
const notes = [];
|
|
54
|
+
const uvmId = input.id;
|
|
55
|
+
const baseLayers = [...image.layers].reverse(); // topmost first [U]
|
|
56
|
+
// Utility VM shares: the OS files, each image layer, then the folders shared in.
|
|
57
|
+
const shares = [
|
|
58
|
+
{
|
|
59
|
+
Name: 'os',
|
|
60
|
+
Path: image.utilityVmFiles,
|
|
61
|
+
// [U] the options hcsshim gives the OS share of a Windows utility VM.
|
|
62
|
+
Options: { ReadOnly: true, ShareRead: true, CacheIo: true, TakeBackupPrivilege: true, PseudoOplocks: true },
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
const layers = baseLayers.map((folder, i) => {
|
|
66
|
+
const name = 'layer' + i;
|
|
67
|
+
shares.push({ Name: name, Path: folder, Options: { ReadOnly: true, ShareRead: true, NoDirectmap: true } });
|
|
68
|
+
return { Id: layerGuid(folder), Path: name, PathType: 'VirtualSmbShareName' };
|
|
69
|
+
});
|
|
70
|
+
const folderMaps = [];
|
|
71
|
+
launch.mappings.forEach((m, i) => {
|
|
72
|
+
const name = 'dir' + i;
|
|
73
|
+
// NoDirectmap on every folder shared in: no direct file mapping into the guest.
|
|
74
|
+
shares.push({ Name: name, Path: m.host, Options: { ReadOnly: m.readOnly, NoDirectmap: true } });
|
|
75
|
+
folderMaps.push({
|
|
76
|
+
HostPath: name,
|
|
77
|
+
HostPathType: 'VirtualSmbShareName',
|
|
78
|
+
ContainerPath: m.guest,
|
|
79
|
+
ReadOnly: m.readOnly,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
// SCSI controller 0: the agent disks (read-write), then the scratch layer.
|
|
83
|
+
const disks = [];
|
|
84
|
+
const attachments = {};
|
|
85
|
+
const attach = (file, readOnly, lun) => {
|
|
86
|
+
attachments[String(lun)] = { Type: 'VirtualDisk', Path: file, ReadOnly: readOnly, CachingMode: 'Uncached' };
|
|
87
|
+
};
|
|
88
|
+
for (const a of launch.storage) {
|
|
89
|
+
if (a.kind !== 'disk')
|
|
90
|
+
continue;
|
|
91
|
+
const lun = disks.length;
|
|
92
|
+
disks.push({ image: a.image, maxGb: a.maxGb, lun });
|
|
93
|
+
attach(a.image, false, lun);
|
|
94
|
+
// [U] the disk is formatted and mounted in the utility VM at this folder (a blank VHDX is not formatted yet).
|
|
95
|
+
const volume = 'C:\\agent-disk' + lun;
|
|
96
|
+
const target = a.guest || GUEST_DATA;
|
|
97
|
+
for (const folder of AGENT_DISK_FOLDERS)
|
|
98
|
+
folderMaps.push({
|
|
99
|
+
HostPath: volume + '\\' + folder,
|
|
100
|
+
HostPathType: 'AbsolutePath',
|
|
101
|
+
ContainerPath: target + '\\' + folder,
|
|
102
|
+
ReadOnly: false,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
attach(input.scratchVhdx, false, disks.length);
|
|
106
|
+
if (launch.networking)
|
|
107
|
+
notes.push('Networking is not available in the HCS engine yet: the sandbox has no network adapter.');
|
|
108
|
+
if (launch.devices.gpu || launch.devices.clipboard || launch.devices.audioInput || launch.devices.videoInput)
|
|
109
|
+
notes.push('GPU, clipboard, microphone and camera do not exist in the HCS engine.');
|
|
110
|
+
const uvm = {
|
|
111
|
+
Owner: HCS_OWNER,
|
|
112
|
+
SchemaVersion: SCHEMA,
|
|
113
|
+
// false on purpose: the helper exits after creating the system, which closes its handle; true would end the
|
|
114
|
+
// system at once. Leftovers are found by owner (sandbox engine clean).
|
|
115
|
+
ShouldTerminateOnLastHandleClosed: false,
|
|
116
|
+
VirtualMachine: {
|
|
117
|
+
StopOnReset: true,
|
|
118
|
+
Chipset: {
|
|
119
|
+
// [U] hcsshim's boot entry for a Windows utility VM.
|
|
120
|
+
Uefi: { BootThis: { DeviceType: 'VmbFs', DevicePath: '\\EFI\\Microsoft\\Boot\\bootmgfw.efi' } },
|
|
121
|
+
},
|
|
122
|
+
ComputeTopology: {
|
|
123
|
+
Memory: { SizeInMB: launch.memoryMb, AllowOvercommit: true },
|
|
124
|
+
Processor: { Count: cpuCount(launch.cpuLimit, input.hostCpus) },
|
|
125
|
+
},
|
|
126
|
+
Devices: {
|
|
127
|
+
Scsi: { '0': { Attachments: attachments } },
|
|
128
|
+
VirtualSmb: { Shares: shares },
|
|
129
|
+
// Placeholder: the control channel's service ids are added when the guest side exists.
|
|
130
|
+
HvSocket: { HvSocketConfig: hvSocketConfig },
|
|
131
|
+
// No NetworkAdapters: no network by default.
|
|
132
|
+
},
|
|
133
|
+
GuestConnection: {},
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
const container = {
|
|
137
|
+
Owner: HCS_OWNER,
|
|
138
|
+
SchemaVersion: SCHEMA,
|
|
139
|
+
ShouldTerminateOnLastHandleClosed: false,
|
|
140
|
+
HostingSystemId: uvmId,
|
|
141
|
+
HostedSystem: {
|
|
142
|
+
SchemaVersion: SCHEMA,
|
|
143
|
+
Container: {
|
|
144
|
+
GuestOs: { HostName: 'agent-sandbox' },
|
|
145
|
+
// [U] Path is the scratch layer's folder as the utility VM sees it.
|
|
146
|
+
Storage: { Layers: layers, Path: 'C:\\scratch' },
|
|
147
|
+
Memory: { SizeInMB: launch.memoryMb },
|
|
148
|
+
MappedDirectories: folderMaps,
|
|
149
|
+
HvSocket: { Config: hvSocketConfig },
|
|
150
|
+
// No Networking section: no endpoint.
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
return { uvmId, containerId: containerId(uvmId), uvm, container, disks, notes };
|
|
155
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { SandboxEngine } from '../engine.js';
|
|
2
|
+
import type { EngineCapabilities, EngineCheck, EnginePlan, LaunchPlan, RunningSandbox } from '../types.js';
|
|
3
|
+
import { type HcsProbes } from './check.js';
|
|
4
|
+
import { type HcsImage } from './documents.js';
|
|
5
|
+
import { HcsHelper } from './helper.js';
|
|
6
|
+
export declare const HCS_CAPABILITIES: EngineCapabilities;
|
|
7
|
+
export declare const HCS_ALPHA_MESSAGE = "The HCS engine is in alpha and starting it is not enabled yet; see docs/design/engines.md";
|
|
8
|
+
export declare const HCS_FLAG = "AGENT_SANDBOX_HCS_EXPERIMENTAL";
|
|
9
|
+
/** Starting is allowed only with the owner's flag, and never inside the self-test. */
|
|
10
|
+
export declare function hcsStartEnabled(env: NodeJS.ProcessEnv, selfTest: boolean): boolean;
|
|
11
|
+
export interface HcsEngineDeps {
|
|
12
|
+
readonly helper?: HcsHelper;
|
|
13
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
14
|
+
/** Inside the self-test, starting is refused whatever the flag says. */
|
|
15
|
+
readonly selfTest?: () => boolean;
|
|
16
|
+
readonly hostCpus?: () => number;
|
|
17
|
+
readonly exists?: (file: string) => boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare class HcsEngine implements SandboxEngine {
|
|
20
|
+
private readonly image;
|
|
21
|
+
private readonly probes;
|
|
22
|
+
private readonly storageRoot;
|
|
23
|
+
private readonly deps;
|
|
24
|
+
readonly id = "hcs";
|
|
25
|
+
/** `image` and `probes` come from the check (see HcsEngine.open); `storageRoot` is where HcsHost.exe is built. */
|
|
26
|
+
constructor(image: HcsImage | null, probes: HcsProbes, storageRoot: string, deps?: HcsEngineDeps);
|
|
27
|
+
/** The engine for this PC when its check passes; otherwise a clear stop with the check's reasons and fixes. */
|
|
28
|
+
static open(storageRoot: string, probes?: HcsProbes, deps?: HcsEngineDeps): Promise<HcsEngine>;
|
|
29
|
+
capabilities(): EngineCapabilities;
|
|
30
|
+
check(): Promise<EngineCheck>;
|
|
31
|
+
plan(launch: LaunchPlan): EnginePlan;
|
|
32
|
+
private helper;
|
|
33
|
+
start(plan: EnginePlan): Promise<RunningSandbox>;
|
|
34
|
+
private tryTerminate;
|
|
35
|
+
/** Ours: the compute systems the helper lists with our owner name. */
|
|
36
|
+
private ours;
|
|
37
|
+
running(): Promise<boolean>;
|
|
38
|
+
stop(): Promise<void>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// The HCS engine (alpha): a Hyper-V isolated Windows container for agents, behind the SandboxEngine interface.
|
|
2
|
+
// plan() is pure and tested; start() is DISABLED until the owner's go/no-go test (docs/design/engines.md) has passed
|
|
3
|
+
// on a real PC.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { fail, UserError } from '../../../core/util/index.js';
|
|
9
|
+
import { selfTestRunning } from '../../test-guard.js';
|
|
10
|
+
import { checkHcs, realHcsProbes } from './check.js';
|
|
11
|
+
import { buildDocuments, HCS_OWNER } from './documents.js';
|
|
12
|
+
import { HcsHelper, hcsHostExe } from './helper.js';
|
|
13
|
+
import { findImage } from './images.js';
|
|
14
|
+
export const HCS_CAPABILITIES = {
|
|
15
|
+
desktop: false,
|
|
16
|
+
gpu: false,
|
|
17
|
+
clipboard: false,
|
|
18
|
+
audio: false,
|
|
19
|
+
video: false,
|
|
20
|
+
printers: false,
|
|
21
|
+
liveFolderAdd: true,
|
|
22
|
+
attachedDisks: true,
|
|
23
|
+
multipleInstances: true,
|
|
24
|
+
hvsocket: true,
|
|
25
|
+
};
|
|
26
|
+
export const HCS_ALPHA_MESSAGE = 'The HCS engine is in alpha and starting it is not enabled yet; see docs/design/engines.md';
|
|
27
|
+
export const HCS_FLAG = 'AGENT_SANDBOX_HCS_EXPERIMENTAL';
|
|
28
|
+
/** Starting is allowed only with the owner's flag, and never inside the self-test. */
|
|
29
|
+
export function hcsStartEnabled(env, selfTest) {
|
|
30
|
+
return env[HCS_FLAG] === '1' && !selfTest;
|
|
31
|
+
}
|
|
32
|
+
const PlanFileSchema = z.object({
|
|
33
|
+
version: z.literal(1),
|
|
34
|
+
uvmId: z.string().regex(/^agent-sandbox-[A-Za-z0-9._-]{1,100}$/),
|
|
35
|
+
containerId: z.string().regex(/^agent-sandbox-[A-Za-z0-9._-]{1,100}$/),
|
|
36
|
+
uvmFile: z.string(),
|
|
37
|
+
containerFile: z.string(),
|
|
38
|
+
scratchVhdx: z.string(),
|
|
39
|
+
disks: z.array(z.object({ image: z.string(), maxGb: z.number().int().min(1).max(4096), lun: z.number().int() })),
|
|
40
|
+
logonCommand: z.string(),
|
|
41
|
+
notes: z.array(z.string()),
|
|
42
|
+
});
|
|
43
|
+
const SystemListSchema = z.array(z.object({ Id: z.string(), Owner: z.string().optional() }).passthrough());
|
|
44
|
+
export class HcsEngine {
|
|
45
|
+
image;
|
|
46
|
+
probes;
|
|
47
|
+
storageRoot;
|
|
48
|
+
deps;
|
|
49
|
+
id = 'hcs';
|
|
50
|
+
/** `image` and `probes` come from the check (see HcsEngine.open); `storageRoot` is where HcsHost.exe is built. */
|
|
51
|
+
constructor(image, probes, storageRoot, deps = {}) {
|
|
52
|
+
this.image = image;
|
|
53
|
+
this.probes = probes;
|
|
54
|
+
this.storageRoot = storageRoot;
|
|
55
|
+
this.deps = deps;
|
|
56
|
+
}
|
|
57
|
+
/** The engine for this PC when its check passes; otherwise a clear stop with the check's reasons and fixes. */
|
|
58
|
+
static async open(storageRoot, probes = realHcsProbes(), deps = {}) {
|
|
59
|
+
const check = checkHcs(probes);
|
|
60
|
+
if (!check.available)
|
|
61
|
+
fail('The HCS engine cannot run on this PC: ' + check.reasons.join(' '), 'Fix: ' + check.fixes.join(' | '));
|
|
62
|
+
return new HcsEngine(findImage(probes.imageStores()).image, probes, storageRoot, deps);
|
|
63
|
+
}
|
|
64
|
+
capabilities() {
|
|
65
|
+
return HCS_CAPABILITIES;
|
|
66
|
+
}
|
|
67
|
+
check() {
|
|
68
|
+
return Promise.resolve(checkHcs(this.probes));
|
|
69
|
+
}
|
|
70
|
+
plan(launch) {
|
|
71
|
+
if (!this.image)
|
|
72
|
+
fail('No base image is available for the HCS engine.', 'Run: sandbox engine check hcs');
|
|
73
|
+
const slug = path.basename(launch.launchPath).replace(/[^A-Za-z0-9._-]/g, '-');
|
|
74
|
+
const dir = path.join(launch.launchPath, 'hcs');
|
|
75
|
+
const scratchVhdx = path.join(dir, 'scratch.vhdx');
|
|
76
|
+
const docs = buildDocuments({
|
|
77
|
+
launch,
|
|
78
|
+
image: this.image,
|
|
79
|
+
id: 'agent-sandbox-' + slug,
|
|
80
|
+
scratchVhdx,
|
|
81
|
+
hostCpus: (this.deps.hostCpus ?? (() => os.cpus().length))(),
|
|
82
|
+
});
|
|
83
|
+
const planFile = {
|
|
84
|
+
version: 1,
|
|
85
|
+
uvmId: docs.uvmId,
|
|
86
|
+
containerId: docs.containerId,
|
|
87
|
+
uvmFile: path.join(dir, 'uvm.json'),
|
|
88
|
+
containerFile: path.join(dir, 'container.json'),
|
|
89
|
+
scratchVhdx,
|
|
90
|
+
disks: [...docs.disks],
|
|
91
|
+
logonCommand: launch.logonCommand,
|
|
92
|
+
notes: [...docs.notes],
|
|
93
|
+
};
|
|
94
|
+
const entry = path.join(dir, 'plan.json');
|
|
95
|
+
const pretty = (v) => JSON.stringify(v, null, 2) + '\n';
|
|
96
|
+
return {
|
|
97
|
+
engine: this.id,
|
|
98
|
+
entry,
|
|
99
|
+
files: [
|
|
100
|
+
{ path: planFile.uvmFile, text: pretty(docs.uvm) },
|
|
101
|
+
{ path: planFile.containerFile, text: pretty(docs.container) },
|
|
102
|
+
{ path: entry, text: pretty(planFile) },
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
helper() {
|
|
107
|
+
return this.deps.helper ?? new HcsHelper(() => hcsHostExe(this.storageRoot));
|
|
108
|
+
}
|
|
109
|
+
async start(plan) {
|
|
110
|
+
const enabled = hcsStartEnabled(this.deps.env ?? process.env, (this.deps.selfTest ?? selfTestRunning)());
|
|
111
|
+
if (!enabled)
|
|
112
|
+
fail(HCS_ALPHA_MESSAGE);
|
|
113
|
+
const file = PlanFileSchema.parse(JSON.parse(fs.readFileSync(plan.entry, 'utf8')));
|
|
114
|
+
const exists = this.deps.exists ?? fs.existsSync;
|
|
115
|
+
const helper = this.helper();
|
|
116
|
+
const disks = [...file.disks.map((d) => d.image), file.scratchVhdx];
|
|
117
|
+
for (const d of file.disks)
|
|
118
|
+
if (!exists(d.image))
|
|
119
|
+
helper.createVhdx(d.image, d.maxGb);
|
|
120
|
+
// The writable layer is made by HcsInitializeWritableLayer, which this phase does not have.
|
|
121
|
+
if (!exists(file.scratchVhdx))
|
|
122
|
+
fail('The writable layer of this sandbox has not been prepared (not built yet).');
|
|
123
|
+
const created = [];
|
|
124
|
+
try {
|
|
125
|
+
for (const disk of disks)
|
|
126
|
+
helper.grantVmAccess(file.uvmId, disk);
|
|
127
|
+
helper.create(file.uvmId, fs.readFileSync(file.uvmFile, 'utf8'));
|
|
128
|
+
created.push(file.uvmId);
|
|
129
|
+
helper.start(file.uvmId);
|
|
130
|
+
helper.create(file.containerId, fs.readFileSync(file.containerFile, 'utf8'));
|
|
131
|
+
created.push(file.containerId);
|
|
132
|
+
helper.start(file.containerId);
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
for (const id of created.reverse())
|
|
136
|
+
this.tryTerminate(helper, id);
|
|
137
|
+
throw e;
|
|
138
|
+
}
|
|
139
|
+
return { id: file.uvmId, engine: this.id, started: Date.now() };
|
|
140
|
+
}
|
|
141
|
+
tryTerminate(helper, id) {
|
|
142
|
+
try {
|
|
143
|
+
helper.terminate(id);
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
if (!(e instanceof UserError))
|
|
147
|
+
throw e; // a system that is already gone is fine
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/** Ours: the compute systems the helper lists with our owner name. */
|
|
151
|
+
ours(helper) {
|
|
152
|
+
const parsed = SystemListSchema.safeParse(JSON.parse(helper.enumerate().json || '[]'));
|
|
153
|
+
return parsed.success ? parsed.data.filter((s) => s.Owner === HCS_OWNER).map((s) => s.Id) : [];
|
|
154
|
+
}
|
|
155
|
+
running() {
|
|
156
|
+
try {
|
|
157
|
+
return Promise.resolve(this.ours(this.helper()).length > 0);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return Promise.resolve(false); // no helper or no service: nothing of ours can be running
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
stop() {
|
|
164
|
+
const helper = this.helper();
|
|
165
|
+
// The container goes before the machine that hosts it.
|
|
166
|
+
const ids = this.ours(helper).sort((a, b) => Number(b.endsWith('-c')) - Number(a.endsWith('-c')));
|
|
167
|
+
for (const id of ids)
|
|
168
|
+
this.tryTerminate(helper, id);
|
|
169
|
+
return Promise.resolve();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const HcsReplySchema: z.ZodObject<{
|
|
3
|
+
ok: z.ZodBoolean;
|
|
4
|
+
hresult: z.ZodString;
|
|
5
|
+
message: z.ZodString;
|
|
6
|
+
json: z.ZodString;
|
|
7
|
+
values: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
export type HcsReply = z.infer<typeof HcsReplySchema>;
|
|
10
|
+
export interface RunResult {
|
|
11
|
+
readonly status: number | null;
|
|
12
|
+
readonly error?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Starts the helper: replaceable in tests. */
|
|
15
|
+
export type HelperRunner = (exe: string, args: readonly string[], timeoutMs: number) => RunResult;
|
|
16
|
+
export declare const spawnRunner: HelperRunner;
|
|
17
|
+
/** Compiles the helper's sources to `exe` with the .NET Framework csc (C# 5). False when it cannot be built. */
|
|
18
|
+
export declare function buildHcsHostTo(exe: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* <storage>\bin\hcs\<hash of its sources>\HcsHost.exe, built the first time. Its own folder: HostTools' cleanup of old
|
|
21
|
+
* builds only touches twelve-digit hash folders directly in bin. Null when it cannot be built.
|
|
22
|
+
*/
|
|
23
|
+
export declare function hcsHostExe(storageRoot: string): string | null;
|
|
24
|
+
export declare class HcsHelper {
|
|
25
|
+
private readonly exe;
|
|
26
|
+
private readonly run;
|
|
27
|
+
private readonly tempRoot;
|
|
28
|
+
/** `exe`: where HcsHost.exe is (built when asked); `tempRoot`: where the private folders for one call go. */
|
|
29
|
+
constructor(exe: () => string | null, run?: HelperRunner, tempRoot?: string);
|
|
30
|
+
/** One helper call: `args` then `--out <response>`. Returns the checked answer, whatever it says. */
|
|
31
|
+
call(args: readonly string[], request?: unknown): HcsReply;
|
|
32
|
+
/** The same, but a failed answer stops with the helper's own message. */
|
|
33
|
+
private must;
|
|
34
|
+
check(): HcsReply;
|
|
35
|
+
enumerate(): HcsReply;
|
|
36
|
+
properties(id: string): HcsReply;
|
|
37
|
+
/** `document` is the HCS JSON text; it travels as a string inside the request file. */
|
|
38
|
+
create(id: string, document: string): HcsReply;
|
|
39
|
+
start(id: string): HcsReply;
|
|
40
|
+
terminate(id: string): HcsReply;
|
|
41
|
+
grantVmAccess(vmId: string, file: string): HcsReply;
|
|
42
|
+
createVhdx(file: string, maxGb: number): HcsReply;
|
|
43
|
+
}
|