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,482 @@
|
|
|
1
|
+
// The HCS engine (alpha, start disabled). Nothing here can run HCS (no Hyper-V in the test machines): documents are
|
|
2
|
+
// compared as text, the check runs against pretend PCs, the helper against a fake process runner, and HcsHost only
|
|
3
|
+
// has to compile and answer once. Temp folders only.
|
|
4
|
+
import crypto from 'node:crypto';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { DEFAULTS } from '../core/config/index.js';
|
|
8
|
+
import { isWin } from '../core/util/index.js';
|
|
9
|
+
import { HCS_CHECKS, HCS_PROBES } from '../cli/commands/repair/checks/hcs.js';
|
|
10
|
+
import { checkHcs, engineFor, HCS_CAPABILITIES, HcsEngine, openEngine, } from '../host/engines/index.js';
|
|
11
|
+
import { parseWhoamiGroups } from '../host/engines/hcs/check.js';
|
|
12
|
+
import { buildDocuments, cpuCount, layerGuid } from '../host/engines/hcs/documents.js';
|
|
13
|
+
import { HcsHelper, HcsReplySchema, buildHcsHostTo } from '../host/engines/hcs/helper.js';
|
|
14
|
+
import { chainIds, DockerImageStore, FolderImageStore } from '../host/engines/hcs/images.js';
|
|
15
|
+
import { SYSTEM } from '../shared/system.js';
|
|
16
|
+
import { fakeProbes, FAKE_IMAGE } from './support/hcs-probes.js';
|
|
17
|
+
const LAUNCH = {
|
|
18
|
+
launchPath: 'C:\\L\\launches\\1',
|
|
19
|
+
memoryMb: 4096,
|
|
20
|
+
cpuLimit: 50,
|
|
21
|
+
networking: true,
|
|
22
|
+
devices: {
|
|
23
|
+
gpu: false,
|
|
24
|
+
clipboard: true,
|
|
25
|
+
audioInput: false,
|
|
26
|
+
videoInput: false,
|
|
27
|
+
printers: false,
|
|
28
|
+
protectedClient: true,
|
|
29
|
+
},
|
|
30
|
+
mappings: [
|
|
31
|
+
{ host: 'C:\\L\\launches\\1', guest: 'C:\\AgentLaunch', readOnly: true },
|
|
32
|
+
{ host: 'C:\\S\\run', guest: 'C:\\AgentData\\run', readOnly: false },
|
|
33
|
+
{ host: 'C:\\Work\\app', guest: 'C:\\Workspace\\app', readOnly: true },
|
|
34
|
+
],
|
|
35
|
+
logonCommand: 'C:\\AgentLaunch\\sandbox\\boot\\logon.cmd',
|
|
36
|
+
storage: [{ kind: 'disk', image: 'C:\\S\\images\\set.vhdx', guest: 'C:\\AgentData', maxGb: 64 }],
|
|
37
|
+
};
|
|
38
|
+
const SCHEMA = { Major: 2, Minor: 1 };
|
|
39
|
+
/** Where `document` (a ComputeSystem) differs from Microsoft's Swagger schema: unknown properties, values, types. */
|
|
40
|
+
function schemaProblems(schemaFile, document, label) {
|
|
41
|
+
const defs = JSON.parse(fs.readFileSync(schemaFile, 'utf8'))
|
|
42
|
+
.definitions;
|
|
43
|
+
// ComputeSystem.HostedSystem is typed as a plain object; its shape is the separate HostedSystem definition.
|
|
44
|
+
if (defs.HostedSystem && defs.ComputeSystem.properties)
|
|
45
|
+
defs.ComputeSystem.properties.HostedSystem = { $ref: '#/definitions/HostedSystem' };
|
|
46
|
+
const problems = [];
|
|
47
|
+
const walk = (value, schema, where) => {
|
|
48
|
+
if (!schema)
|
|
49
|
+
return;
|
|
50
|
+
if (schema.$ref) {
|
|
51
|
+
const name = schema.$ref.replace('#/definitions/', '');
|
|
52
|
+
if (!defs[name])
|
|
53
|
+
problems.push(where + ': missing definition ' + name);
|
|
54
|
+
else
|
|
55
|
+
walk(value, defs[name], where);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
schema.allOf?.forEach((s) => walk(value, s, where));
|
|
59
|
+
if (schema.enum && !schema.enum.includes(value))
|
|
60
|
+
problems.push(where + ': "' + String(value) + '" is not allowed');
|
|
61
|
+
if (schema.type === 'object' || schema.properties || schema.additionalProperties) {
|
|
62
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
63
|
+
problems.push(where + ': not an object');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
for (const [key, child] of Object.entries(value)) {
|
|
67
|
+
const prop = schema.properties?.[key];
|
|
68
|
+
if (prop)
|
|
69
|
+
walk(child, prop, where + '.' + key);
|
|
70
|
+
else if (typeof schema.additionalProperties === 'object')
|
|
71
|
+
walk(child, schema.additionalProperties, where + '[' + key + ']');
|
|
72
|
+
else
|
|
73
|
+
problems.push(where + ': unknown property "' + key + '"');
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (schema.type === 'array') {
|
|
78
|
+
if (Array.isArray(value))
|
|
79
|
+
value.forEach((item, i) => walk(item, schema.items, where + '[' + i + ']'));
|
|
80
|
+
else
|
|
81
|
+
problems.push(where + ': not an array');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const kind = typeof value;
|
|
85
|
+
if (schema.type === 'string' && kind !== 'string')
|
|
86
|
+
problems.push(where + ': not text');
|
|
87
|
+
if ((schema.type === 'integer' || schema.type === 'number') && kind !== 'number')
|
|
88
|
+
problems.push(where + ': not a number');
|
|
89
|
+
if (schema.type === 'boolean' && kind !== 'boolean')
|
|
90
|
+
problems.push(where + ': not true/false');
|
|
91
|
+
};
|
|
92
|
+
walk(document, { $ref: '#/definitions/ComputeSystem' }, label);
|
|
93
|
+
return problems;
|
|
94
|
+
}
|
|
95
|
+
const hvConfig = {
|
|
96
|
+
DefaultBindSecurityDescriptor: 'D:P(A;;FA;;;SY)(A;;FA;;;BA)',
|
|
97
|
+
DefaultConnectSecurityDescriptor: 'D:P(A;;FA;;;SY)(A;;FA;;;BA)',
|
|
98
|
+
ServiceTable: {},
|
|
99
|
+
};
|
|
100
|
+
/** Docker's image store as files in memory. */
|
|
101
|
+
function dockerDisk(diffIds, extra = {}) {
|
|
102
|
+
const root = 'C:\\ProgramData\\docker';
|
|
103
|
+
const meta = root + '\\image\\windowsfilter';
|
|
104
|
+
const id = 'sha256:' + 'b'.repeat(64);
|
|
105
|
+
const files = {
|
|
106
|
+
[meta + '\\repositories.json']: JSON.stringify({
|
|
107
|
+
Repositories: { 'mcr.microsoft.com/windows/servercore': { 'mcr.microsoft.com/windows/servercore:ltsc2022': id } },
|
|
108
|
+
}),
|
|
109
|
+
[meta + '\\imagedb\\content\\sha256\\' + 'b'.repeat(64)]: JSON.stringify({
|
|
110
|
+
rootfs: { type: 'layers', diff_ids: diffIds },
|
|
111
|
+
}),
|
|
112
|
+
...extra,
|
|
113
|
+
};
|
|
114
|
+
chainIds(diffIds).forEach((chain, i) => {
|
|
115
|
+
files[meta + '\\layerdb\\sha256\\' + chain.slice(7) + '\\cache-id'] = 'cache' + i + '\n';
|
|
116
|
+
});
|
|
117
|
+
const dirs = new Set(diffIds.map((_, i) => root + '\\windowsfilter\\cache' + i));
|
|
118
|
+
dirs.add(root + '\\windowsfilter\\cache0\\UtilityVM\\Files');
|
|
119
|
+
return { readText: (f) => files[f] ?? null, isDir: (d) => dirs.has(d) };
|
|
120
|
+
}
|
|
121
|
+
export const hcsSuite = {
|
|
122
|
+
name: 'hcs',
|
|
123
|
+
title: 'HCS engine (alpha, start disabled): documents, check, image store, helper, HcsHost',
|
|
124
|
+
run: async (t, f) => {
|
|
125
|
+
const hcsConfig = { ...structuredClone(f.baseConfig), sandbox: { ...f.baseConfig.sandbox, engine: 'hcs' } };
|
|
126
|
+
t.section('documents: a Hyper-V isolated container, no network, code read-only');
|
|
127
|
+
const docs = buildDocuments({
|
|
128
|
+
launch: LAUNCH,
|
|
129
|
+
image: FAKE_IMAGE,
|
|
130
|
+
id: 'agent-sandbox-1',
|
|
131
|
+
scratchVhdx: 'C:\\L\\launches\\1\\hcs\\scratch.vhdx',
|
|
132
|
+
hostCpus: 8,
|
|
133
|
+
});
|
|
134
|
+
const os = { ReadOnly: true, ShareRead: true, CacheIo: true, TakeBackupPrivilege: true, PseudoOplocks: true };
|
|
135
|
+
t.equal(docs.uvm, {
|
|
136
|
+
Owner: 'agent-sandbox',
|
|
137
|
+
SchemaVersion: SCHEMA,
|
|
138
|
+
ShouldTerminateOnLastHandleClosed: false,
|
|
139
|
+
VirtualMachine: {
|
|
140
|
+
StopOnReset: true,
|
|
141
|
+
Chipset: {
|
|
142
|
+
Uefi: { BootThis: { DeviceType: 'VmbFs', DevicePath: '\\EFI\\Microsoft\\Boot\\bootmgfw.efi' } },
|
|
143
|
+
},
|
|
144
|
+
ComputeTopology: { Memory: { SizeInMB: 4096, AllowOvercommit: true }, Processor: { Count: 4 } },
|
|
145
|
+
Devices: {
|
|
146
|
+
Scsi: {
|
|
147
|
+
'0': {
|
|
148
|
+
Attachments: {
|
|
149
|
+
'0': {
|
|
150
|
+
Type: 'VirtualDisk',
|
|
151
|
+
Path: 'C:\\S\\images\\set.vhdx',
|
|
152
|
+
ReadOnly: false,
|
|
153
|
+
CachingMode: 'Uncached',
|
|
154
|
+
},
|
|
155
|
+
'1': {
|
|
156
|
+
Type: 'VirtualDisk',
|
|
157
|
+
Path: 'C:\\L\\launches\\1\\hcs\\scratch.vhdx',
|
|
158
|
+
ReadOnly: false,
|
|
159
|
+
CachingMode: 'Uncached',
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
VirtualSmb: {
|
|
165
|
+
Shares: [
|
|
166
|
+
{ Name: 'os', Path: 'C:\\img\\base\\UtilityVM\\Files', Options: os },
|
|
167
|
+
{
|
|
168
|
+
Name: 'layer0',
|
|
169
|
+
Path: 'C:\\img\\l2',
|
|
170
|
+
Options: { ReadOnly: true, ShareRead: true, NoDirectmap: true },
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
Name: 'layer1',
|
|
174
|
+
Path: 'C:\\img\\base',
|
|
175
|
+
Options: { ReadOnly: true, ShareRead: true, NoDirectmap: true },
|
|
176
|
+
},
|
|
177
|
+
{ Name: 'dir0', Path: 'C:\\L\\launches\\1', Options: { ReadOnly: true, NoDirectmap: true } },
|
|
178
|
+
{ Name: 'dir1', Path: 'C:\\S\\run', Options: { ReadOnly: false, NoDirectmap: true } },
|
|
179
|
+
{ Name: 'dir2', Path: 'C:\\Work\\app', Options: { ReadOnly: true, NoDirectmap: true } },
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
HvSocket: { HvSocketConfig: hvConfig },
|
|
183
|
+
},
|
|
184
|
+
GuestConnection: {},
|
|
185
|
+
},
|
|
186
|
+
}, 'the utility VM: memory, 4 of 8 processors, the agent disk and scratch on SCSI, vSMB shares, no network adapter');
|
|
187
|
+
const map = (hostPath, hostPathType, containerPath, readOnly) => ({
|
|
188
|
+
HostPath: hostPath,
|
|
189
|
+
HostPathType: hostPathType,
|
|
190
|
+
ContainerPath: containerPath,
|
|
191
|
+
ReadOnly: readOnly,
|
|
192
|
+
});
|
|
193
|
+
t.equal(docs.container, {
|
|
194
|
+
Owner: 'agent-sandbox',
|
|
195
|
+
SchemaVersion: SCHEMA,
|
|
196
|
+
ShouldTerminateOnLastHandleClosed: false,
|
|
197
|
+
HostingSystemId: 'agent-sandbox-1',
|
|
198
|
+
HostedSystem: {
|
|
199
|
+
SchemaVersion: SCHEMA,
|
|
200
|
+
Container: {
|
|
201
|
+
GuestOs: { HostName: 'agent-sandbox' },
|
|
202
|
+
Storage: {
|
|
203
|
+
Layers: [
|
|
204
|
+
{ Id: layerGuid('C:\\img\\l2'), Path: 'layer0', PathType: 'VirtualSmbShareName' },
|
|
205
|
+
{ Id: layerGuid('C:\\img\\base'), Path: 'layer1', PathType: 'VirtualSmbShareName' },
|
|
206
|
+
],
|
|
207
|
+
Path: 'C:\\scratch',
|
|
208
|
+
},
|
|
209
|
+
Memory: { SizeInMB: 4096 },
|
|
210
|
+
MappedDirectories: [
|
|
211
|
+
map('dir0', 'VirtualSmbShareName', 'C:\\AgentLaunch', true),
|
|
212
|
+
map('dir1', 'VirtualSmbShareName', 'C:\\AgentData\\run', false),
|
|
213
|
+
map('dir2', 'VirtualSmbShareName', 'C:\\Workspace\\app', true),
|
|
214
|
+
map('C:\\agent-disk0\\tools', 'AbsolutePath', 'C:\\AgentData\\tools', false),
|
|
215
|
+
map('C:\\agent-disk0\\state', 'AbsolutePath', 'C:\\AgentData\\state', false),
|
|
216
|
+
map('C:\\agent-disk0\\logs', 'AbsolutePath', 'C:\\AgentData\\logs', false),
|
|
217
|
+
],
|
|
218
|
+
HvSocket: { Config: hvConfig },
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
}, 'the container: layers from shares, the code read-only, run read-write, tools/state/logs from the agent disk');
|
|
222
|
+
const text = JSON.stringify([docs.uvm, docs.container]);
|
|
223
|
+
t.check(!text.includes('NetworkAdapters') && !text.includes('Networking') && !text.includes('EndpointList'), 'no network adapter, endpoint or networking section by default');
|
|
224
|
+
t.check(docs.uvm.VirtualMachine !== undefined &&
|
|
225
|
+
docs.uvm.VirtualMachine.Devices.VirtualSmb.Shares.filter((s) => s.Name !== 'os').every((s) => s.Options.NoDirectmap === true), 'every share except the OS files has NoDirectmap');
|
|
226
|
+
t.equal([docs.disks, docs.notes.length, docs.uvmId, docs.containerId], [[{ image: 'C:\\S\\images\\set.vhdx', maxGb: 64, lun: 0 }], 2, 'agent-sandbox-1', 'agent-sandbox-1-c'], 'one agent disk to create; notes say networking and the clipboard are not there');
|
|
227
|
+
t.equal([cpuCount('none', 8), cpuCount(50, 8), cpuCount(5, 8), cpuCount(100, 2), cpuCount(10, 1)], [8, 4, 1, 2, 1], 'processors from the CPU percentage');
|
|
228
|
+
t.check(/^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-8[0-9a-f]{3}-[0-9a-f]{12}$/.test(layerGuid('C:\\x')) &&
|
|
229
|
+
layerGuid('C:\\X') === layerGuid('c:\\x'), 'layer ids are stable GUIDs');
|
|
230
|
+
const noDisk = buildDocuments({
|
|
231
|
+
launch: { ...LAUNCH, networking: false, storage: [], devices: { ...LAUNCH.devices, clipboard: false } },
|
|
232
|
+
image: FAKE_IMAGE,
|
|
233
|
+
id: 'x',
|
|
234
|
+
scratchVhdx: 'C:\\s.vhdx',
|
|
235
|
+
hostCpus: 4,
|
|
236
|
+
});
|
|
237
|
+
t.check(noDisk.disks.length === 0 && noDisk.notes.length === 0 && JSON.stringify(noDisk.container).includes('dir0'), 'without a disk there is nothing to create, and no notes when nothing was asked for that is missing');
|
|
238
|
+
t.section('capabilities');
|
|
239
|
+
t.equal(HCS_CAPABILITIES, {
|
|
240
|
+
desktop: false,
|
|
241
|
+
gpu: false,
|
|
242
|
+
clipboard: false,
|
|
243
|
+
audio: false,
|
|
244
|
+
video: false,
|
|
245
|
+
printers: false,
|
|
246
|
+
liveFolderAdd: true,
|
|
247
|
+
attachedDisks: true,
|
|
248
|
+
multipleInstances: true,
|
|
249
|
+
hvsocket: true,
|
|
250
|
+
}, 'HCS: headless, no GPU or clipboard; disks, several at once, Hyper-V sockets');
|
|
251
|
+
t.section('check: each missing piece gives its reason and the exact fix');
|
|
252
|
+
const good = fakeProbes();
|
|
253
|
+
t.equal(checkHcs(good), { available: true, reasons: [], fixes: [] }, 'a PC with everything is available');
|
|
254
|
+
const cases = [
|
|
255
|
+
{ name: 'not Windows', pc: { windows: false }, reason: /only runs on Windows/, fix: /windows-sandbox/ },
|
|
256
|
+
{ name: 'old build', pc: { build: 14393 }, reason: /build 14393 is too old/, fix: /Update Windows/ },
|
|
257
|
+
{
|
|
258
|
+
name: 'no computecore.dll',
|
|
259
|
+
pc: { computecore: false },
|
|
260
|
+
reason: /computecore\.dll/,
|
|
261
|
+
fix: /Enable-WindowsOptionalFeature -Online -All -FeatureName VirtualMachinePlatform/,
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
name: 'no vmcompute',
|
|
265
|
+
pc: { services: { vmcompute: false, cexecsvc: true } },
|
|
266
|
+
reason: /vmcompute/,
|
|
267
|
+
fix: /FeatureName Microsoft-Hyper-V-All/,
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: 'no Containers',
|
|
271
|
+
pc: { services: { vmcompute: true, cexecsvc: false } },
|
|
272
|
+
reason: /Containers feature/,
|
|
273
|
+
fix: /FeatureName Containers$/,
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: 'not an administrator',
|
|
277
|
+
pc: { token: { elevatedAdministrator: false, hyperVAdministrators: false } },
|
|
278
|
+
reason: /neither an elevated Administrator nor in Hyper-V Administrators/,
|
|
279
|
+
fix: /net localgroup "Hyper-V Administrators"/,
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: 'no image',
|
|
283
|
+
pc: { image: null },
|
|
284
|
+
reason: /No Windows Server Core \(ltsc2022\) image.*fake store has no image/,
|
|
285
|
+
fix: /docker pull mcr\.microsoft\.com\/windows\/servercore:ltsc2022.*AGENT_SANDBOX_HCS_IMAGE/,
|
|
286
|
+
},
|
|
287
|
+
];
|
|
288
|
+
for (const c of cases) {
|
|
289
|
+
const r = checkHcs(fakeProbes(c.pc));
|
|
290
|
+
t.check(!r.available && r.reasons.length === 1 && c.reason.test(r.reasons[0]) && c.fix.test(r.fixes[0]), 'check: ' + c.name, JSON.stringify(r));
|
|
291
|
+
}
|
|
292
|
+
t.check(checkHcs(fakeProbes({ token: { elevatedAdministrator: false, hyperVAdministrators: true } })).available, 'Hyper-V Administrators is enough without elevation');
|
|
293
|
+
const bare = checkHcs(fakeProbes({ computecore: false, services: { vmcompute: false, cexecsvc: false }, image: null }));
|
|
294
|
+
t.check(bare.reasons.length === 4 && bare.fixes.length === 4, 'several things missing: one reason and one fix each, in step');
|
|
295
|
+
t.check(good.calls.every((c) => /^(serviceInstalled|fileExists|imageStore\.find)/.test(c)), 'the check only looked (service queries, file checks, image lookup)');
|
|
296
|
+
const csv = '"BUILTIN\\Administrators","Alias","S-1-5-32-544","Group used for deny only"\r\n' +
|
|
297
|
+
'"BUILTIN\\Hyper-V Administrators","Alias","S-1-5-32-578","Mandatory group, Enabled by default, Enabled group"\r\n';
|
|
298
|
+
t.equal([
|
|
299
|
+
parseWhoamiGroups(csv),
|
|
300
|
+
parseWhoamiGroups('"BUILTIN\\Administrators","Alias","S-1-5-32-544","Mandatory group, Enabled by default, Enabled group, Group owner"'),
|
|
301
|
+
], [
|
|
302
|
+
{ elevatedAdministrator: false, hyperVAdministrators: true },
|
|
303
|
+
{ elevatedAdministrator: true, hyperVAdministrators: false },
|
|
304
|
+
], 'an Administrators group that is deny-only (not elevated) does not count');
|
|
305
|
+
t.section('images: an image that is already on this PC');
|
|
306
|
+
const diffs = ['sha256:' + '1'.repeat(64), 'sha256:' + '2'.repeat(64)];
|
|
307
|
+
const second = crypto
|
|
308
|
+
.createHash('sha256')
|
|
309
|
+
.update(diffs[0] + ' ' + diffs[1])
|
|
310
|
+
.digest('hex');
|
|
311
|
+
t.equal(chainIds(diffs), [diffs[0], 'sha256:' + second], "Docker's chain ids");
|
|
312
|
+
const found = new DockerImageStore('C:\\ProgramData\\docker', dockerDisk(diffs)).find();
|
|
313
|
+
t.equal(found.image, {
|
|
314
|
+
id: 'sha256:' + 'b'.repeat(64),
|
|
315
|
+
layers: ['C:\\ProgramData\\docker\\windowsfilter\\cache0', 'C:\\ProgramData\\docker\\windowsfilter\\cache1'],
|
|
316
|
+
utilityVmFiles: 'C:\\ProgramData\\docker\\windowsfilter\\cache0\\UtilityVM\\Files',
|
|
317
|
+
}, 'the Server Core image is found from its metadata, base layer first');
|
|
318
|
+
t.check(new DockerImageStore('C:\\nothing', { readText: () => null, isDir: () => false })
|
|
319
|
+
.find()
|
|
320
|
+
.reason.includes('no Windows images'), 'no Docker store: says so');
|
|
321
|
+
const noTag = {
|
|
322
|
+
readText: (p) => p.endsWith('repositories.json')
|
|
323
|
+
? JSON.stringify({
|
|
324
|
+
Repositories: { 'mcr.microsoft.com/windows/nanoserver': { 'x:ltsc2022': 'sha256:' + 'c'.repeat(64) } },
|
|
325
|
+
})
|
|
326
|
+
: null,
|
|
327
|
+
isDir: () => false,
|
|
328
|
+
};
|
|
329
|
+
t.check(new DockerImageStore('C:\\d', noTag).find().reason.includes('no windows/servercore:ltsc2022'), 'only Nano Server: not the image we need');
|
|
330
|
+
const missingLayer = dockerDisk(diffs);
|
|
331
|
+
t.check(new DockerImageStore('C:\\ProgramData\\docker', { ...missingLayer, isDir: () => false }).find().image === null, 'a layer folder that is gone: no image');
|
|
332
|
+
const folderDisk = {
|
|
333
|
+
readText: (p) => p === 'C:\\mine\\image.json'
|
|
334
|
+
? JSON.stringify({ layers: ['C:\\mine\\l0'], utilityVmFiles: 'C:\\mine\\l0\\UtilityVM\\Files' })
|
|
335
|
+
: null,
|
|
336
|
+
isDir: () => true,
|
|
337
|
+
};
|
|
338
|
+
t.check(new FolderImageStore('C:\\mine', folderDisk).find().image?.layers[0] === 'C:\\mine\\l0' &&
|
|
339
|
+
new FolderImageStore(undefined, folderDisk).find().image === null, 'your own image folder (image.json) is used when set, ignored when not');
|
|
340
|
+
t.section('the engine: refused until its check passes, start disabled');
|
|
341
|
+
t.equal([new HcsEngine(FAKE_IMAGE, fakeProbes(), t.dir('hcs-root')).id], ['hcs'], 'the engine id is hcs');
|
|
342
|
+
await t.throws(() => engineFor(hcsConfig), 'The HCS engine has to be opened first', 'engineFor(hcs) does not hand it out');
|
|
343
|
+
await t.throws(() => openEngine(hcsConfig, fakeProbes({ computecore: false, image: null })), /cannot run on this PC: computecore\.dll .*No Windows Server Core.*Fix: .*Enable-WindowsOptionalFeature/s, 'openEngine(hcs) fails with the check reasons and fixes when the check fails');
|
|
344
|
+
const opened = await openEngine(hcsConfig, fakeProbes());
|
|
345
|
+
t.check(opened.id === 'hcs' && opened.capabilities().hvsocket, 'openEngine(hcs) returns the engine when the check passes');
|
|
346
|
+
t.check((await openEngine(f.baseConfig)).id === 'windows-sandbox', 'the default engine is still Windows Sandbox');
|
|
347
|
+
const root = t.dir('hcs-engine');
|
|
348
|
+
const launch = { ...LAUNCH, launchPath: path.join(root, 'launches', '1') };
|
|
349
|
+
const plan = opened.plan(launch);
|
|
350
|
+
t.check(plan.engine === 'hcs' &&
|
|
351
|
+
plan.files.map((x) => path.basename(x.path)).join() === 'uvm.json,container.json,plan.json' &&
|
|
352
|
+
plan.entry === path.join(launch.launchPath, 'hcs', 'plan.json'), 'plan(): the two documents and the plan file, entry is plan.json');
|
|
353
|
+
await t.throws(() => opened.start(plan), 'starting it is not enabled yet; see docs/design/engines.md', 'start() is refused by default');
|
|
354
|
+
const flagged = new HcsEngine(FAKE_IMAGE, fakeProbes(), root, { env: { AGENT_SANDBOX_HCS_EXPERIMENTAL: '1' } });
|
|
355
|
+
await t.throws(() => flagged.start(plan), 'starting it is not enabled yet', 'start() is refused inside the self-test even with the flag (the real self-test check)');
|
|
356
|
+
t.check(process.env.AGENT_SANDBOX_SELFTEST === '1', 'this run really is the self-test');
|
|
357
|
+
await t.throws(() => new HcsEngine(FAKE_IMAGE, fakeProbes(), root, {
|
|
358
|
+
env: { AGENT_SANDBOX_HCS_EXPERIMENTAL: '0' },
|
|
359
|
+
selfTest: () => false,
|
|
360
|
+
}).start(plan), 'starting it is not enabled yet', 'start() is refused without the flag outside the self-test too');
|
|
361
|
+
t.section('start() order, with the flag and a fake helper (the self-test guard switched off for this one engine)');
|
|
362
|
+
const log = [];
|
|
363
|
+
const fakeRun = (_exe, args) => {
|
|
364
|
+
log.push(args
|
|
365
|
+
.slice(0, args.indexOf('--out'))
|
|
366
|
+
.map((a) => (a.endsWith('request.json') ? '<request>' : a))
|
|
367
|
+
.join(' '));
|
|
368
|
+
const answer = { ok: true, hresult: '0x00000000', message: 'ok', json: '[]', values: {} };
|
|
369
|
+
fs.writeFileSync(args[args.indexOf('--out') + 1], JSON.stringify(answer), 'utf8');
|
|
370
|
+
return { status: 0 };
|
|
371
|
+
};
|
|
372
|
+
const helper = new HcsHelper(() => 'C:\\fake\\HcsHost.exe', fakeRun, t.dir('hcs-helper-temp'));
|
|
373
|
+
const live = new HcsEngine(FAKE_IMAGE, fakeProbes(), root, {
|
|
374
|
+
helper,
|
|
375
|
+
env: { AGENT_SANDBOX_HCS_EXPERIMENTAL: '1' },
|
|
376
|
+
selfTest: () => false,
|
|
377
|
+
exists: () => true,
|
|
378
|
+
});
|
|
379
|
+
for (const file of plan.files)
|
|
380
|
+
t.write(file.path, file.text);
|
|
381
|
+
const running = await live.start(plan);
|
|
382
|
+
t.equal(log, [
|
|
383
|
+
'grant-vm-access agent-sandbox-1 C:\\S\\images\\set.vhdx',
|
|
384
|
+
'grant-vm-access agent-sandbox-1 ' + path.join(launch.launchPath, 'hcs', 'scratch.vhdx'),
|
|
385
|
+
'create <request>',
|
|
386
|
+
'start agent-sandbox-1',
|
|
387
|
+
'create <request>',
|
|
388
|
+
'start agent-sandbox-1-c',
|
|
389
|
+
], 'disks get access, the utility VM is created and started, then the container');
|
|
390
|
+
t.check(running.id === 'agent-sandbox-1' && running.engine === 'hcs', 'start() reports the sandbox');
|
|
391
|
+
t.section('the helper: private temp folder, checked answers');
|
|
392
|
+
const tempRoot = t.dir('hcs-helper-checks');
|
|
393
|
+
const answers = [];
|
|
394
|
+
const answering = (text) => (_e, args) => {
|
|
395
|
+
text(args[args.indexOf('--out') + 1]);
|
|
396
|
+
answers.push(path.dirname(args[args.indexOf('--out') + 1]));
|
|
397
|
+
return { status: 1 };
|
|
398
|
+
};
|
|
399
|
+
await t.throws(() => new HcsHelper(() => 'C:\\x.exe', answering((o) => fs.writeFileSync(o, 'not json')), tempRoot).enumerate(), 'not JSON', 'an answer that is not JSON is refused');
|
|
400
|
+
await t.throws(() => new HcsHelper(() => 'C:\\x.exe', answering((o) => fs.writeFileSync(o, '{"ok":true}')), tempRoot).enumerate(), 'form this version does not know', 'an answer of the wrong shape is refused');
|
|
401
|
+
await t.throws(() => new HcsHelper(() => 'C:\\x.exe', answering(() => undefined), tempRoot).enumerate(), 'gave no answer', 'no answer file is refused');
|
|
402
|
+
await t.throws(() => new HcsHelper(() => 'C:\\x.exe', answering((o) => fs.writeFileSync(o, JSON.stringify({ ok: false, hresult: '0x8037011B', message: 'Access is denied', json: '', values: {} }))), tempRoot).start('agent-sandbox-abc'), 'Access is denied', "a failed answer stops with the helper's message");
|
|
403
|
+
t.equal(fs.readdirSync(tempRoot), [], 'the private temp folders are removed after every call');
|
|
404
|
+
await t.throws(() => new HcsHelper(() => 'C:\\x.exe', fakeRun, tempRoot).start('--evil'), 'Not a valid sandbox id', 'ids are checked');
|
|
405
|
+
await t.throws(() => new HcsHelper(() => 'C:\\x.exe', fakeRun, tempRoot).createVhdx('rel.vhdx', 8), 'Not a full path', 'paths must be full paths');
|
|
406
|
+
await t.throws(() => new HcsHelper(() => null, fakeRun, tempRoot).check(), 'HcsHost.exe could not be built', 'no helper: a clear stop');
|
|
407
|
+
t.check(HcsReplySchema.safeParse({ ok: true, hresult: '0x0', message: '', json: '', values: {} }).success, 'the reply schema');
|
|
408
|
+
t.section('doctor row: only when sandbox.engine is hcs');
|
|
409
|
+
const rowEnv = (config) => ({ ctx: { loaded: { config } } });
|
|
410
|
+
const row = HCS_CHECKS[0];
|
|
411
|
+
t.check(row.name === 'HCS engine (alpha)' &&
|
|
412
|
+
!row.applies?.(rowEnv(f.baseConfig)) &&
|
|
413
|
+
row.applies?.(rowEnv(hcsConfig)) === true, 'the "HCS engine (alpha)" row is hidden by default and shown for engine hcs');
|
|
414
|
+
HCS_PROBES.current = fakeProbes({ image: null });
|
|
415
|
+
const finding = await row.run(rowEnv(hcsConfig));
|
|
416
|
+
HCS_PROBES.current = null;
|
|
417
|
+
t.check(finding.status === 'needs-you' && /docker pull/.test(finding.fix ?? ''), 'the row says what is missing and the command, and repair has no automatic fix for it');
|
|
418
|
+
t.check(row.fix === undefined, 'repair never fixes HCS prerequisites (they change Windows)');
|
|
419
|
+
t.section('sandbox engine check hcs');
|
|
420
|
+
const out = await f.runBin('agent-sandbox', ['engine', 'check', 'hcs', '--config', t.writeJson(t.at('hcs-config.json'), {})], {
|
|
421
|
+
cwd: f.workA,
|
|
422
|
+
env: { ...process.env, NO_COLOR: '1', AGENT_SANDBOX_HOME: t.dir('hcs-home'), AGENT_SANDBOX_HOST_ONLY: '1' },
|
|
423
|
+
});
|
|
424
|
+
t.check(/hcs can be used|hcs cannot be used on this PC yet/.test(out.out) &&
|
|
425
|
+
(out.status === 0 || out.out.includes('To fix:')), 'the command prints the check in plain words with the fixes', out.out);
|
|
426
|
+
t.check(DEFAULTS.sandbox.engine === 'windows-sandbox', 'the default engine is unchanged');
|
|
427
|
+
t.section("documents against Microsoft's published HCS schema (Windows 11 21H2 and Windows 10 1809)");
|
|
428
|
+
const schemaDir = path.join(t.packageRoot, 'test-fixtures', 'hcs-schema');
|
|
429
|
+
if (!fs.existsSync(schemaDir)) {
|
|
430
|
+
t.check(true, 'skipped: the schema fixtures are only in the repository');
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
for (const file of fs.readdirSync(schemaDir).filter((f) => f.endsWith('.json'))) {
|
|
434
|
+
const problems = [
|
|
435
|
+
...schemaProblems(path.join(schemaDir, file), docs.uvm, 'utility VM'),
|
|
436
|
+
...schemaProblems(path.join(schemaDir, file), docs.container, 'container'),
|
|
437
|
+
];
|
|
438
|
+
t.check(problems.length === 0, 'every property and value is in ' + file, problems.join('\n'));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
t.section('the helpers say the version they ship with (file details)');
|
|
442
|
+
const version = JSON.parse(fs.readFileSync(path.join(t.packageRoot, 'package.json'), 'utf8')).version;
|
|
443
|
+
for (const helper of ['HostTools', 'HcsHost']) {
|
|
444
|
+
const info = fs.readFileSync(path.join(t.packageRoot, 'native', 'csharp', helper, 'AssemblyInfo.cs'), 'utf8');
|
|
445
|
+
t.check(info.includes('AssemblyInformationalVersion("' + version + '")') &&
|
|
446
|
+
info.includes('AssemblyFileVersion("' + version + '.0")'), helper + '.exe file version is ' + version + ' (update AssemblyInfo.cs with package.json)');
|
|
447
|
+
}
|
|
448
|
+
t.section('HcsHost.exe compiles (C# 5, the csc that ships with Windows)');
|
|
449
|
+
if (!isWin || !fs.existsSync(SYSTEM.csc())) {
|
|
450
|
+
t.check(true, 'skipped: no Windows csc on this machine');
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
const exe = t.at('hcshost', 'HcsHost.exe');
|
|
454
|
+
t.check(buildHcsHostTo(exe), 'HcsHost.exe builds');
|
|
455
|
+
if (fs.existsSync(exe)) {
|
|
456
|
+
const reply = new HcsHelper(() => exe, undefined, tempRoot).check();
|
|
457
|
+
t.check(reply.values.computecore === 'yes' || reply.values.computecore === 'no', 'HcsHost.exe check answers with valid JSON (computecore: ' + reply.values.computecore + ')', JSON.stringify(reply));
|
|
458
|
+
const usage = new HcsHelper(() => exe, undefined, tempRoot).call(['frobnicate']);
|
|
459
|
+
t.check(!usage.ok && /Usage/.test(usage.message), 'an unknown verb is a usage error');
|
|
460
|
+
// Raw calls (past the TypeScript checks): HcsHost.exe itself refuses anything that is not ours.
|
|
461
|
+
const raw = new HcsHelper(() => exe, undefined, tempRoot);
|
|
462
|
+
const refused = (args, request) => {
|
|
463
|
+
const r = raw.call(args, request);
|
|
464
|
+
return !r.ok && /^Refused: /.test(r.message);
|
|
465
|
+
};
|
|
466
|
+
t.check(['WindowsSandbox-1', 'wsl', '--help', 'agent-sandbox-', 'agent-sandbox-a b'].every((id) => refused(['terminate', id]) && refused(['start', id]) && refused(['properties', id])), "start, terminate and properties refuse every id that is not this tool's (Windows Sandbox, WSL, options)");
|
|
467
|
+
t.check(refused(['create'], { id: 'agent-sandbox-1', document: JSON.stringify({ Owner: 'docker' }) }) &&
|
|
468
|
+
refused(['create'], { id: 'agent-sandbox-1', document: '{"SchemaVersion":{}}' }) &&
|
|
469
|
+
refused(['create'], { id: 'agent-sandbox-1', document: 'not json' }) &&
|
|
470
|
+
refused(['create'], { id: 'wsl-vm', document: JSON.stringify({ Owner: 'agent-sandbox' }) }), 'create refuses a document owned by anyone else, without an Owner, not JSON, or under a foreign id');
|
|
471
|
+
const notDisk = t.write(path.join(tempRoot, 'hosts.txt'), 'x');
|
|
472
|
+
const missing = path.join(tempRoot, 'missing.vhdx');
|
|
473
|
+
t.check(refused(['grant-vm-access', 'agent-sandbox-1', notDisk]) &&
|
|
474
|
+
refused(['grant-vm-access', 'agent-sandbox-1', missing]) &&
|
|
475
|
+
refused(['grant-vm-access', 'agent-sandbox-1', 'relative.vhdx']), 'grant-vm-access refuses anything but an existing .vhdx at a full path');
|
|
476
|
+
const existing = t.write(path.join(tempRoot, 'existing.vhdx'), 'keep me');
|
|
477
|
+
const over = raw.call(['create-vhdx', existing, '1']);
|
|
478
|
+
t.check(!over.ok && fs.readFileSync(existing, 'utf8') === 'keep me', 'create-vhdx never touches an existing file', over.message);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
};
|