windows-development-sandbox 1.7.0 → 1.9.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 +10 -0
- 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 +47 -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 +19 -3
- package/dist/cli/commands/web.js +2 -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 +8 -0
- package/dist/core/schema/config.d.ts +13 -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 +3 -3
- 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 +2 -1
- 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 +61 -0
- package/dist/guest/governor/decide.js +182 -0
- package/dist/guest/governor/governor.d.ts +42 -0
- package/dist/guest/governor/governor.js +214 -0
- package/dist/guest/governor/index.d.ts +2 -0
- package/dist/guest/governor/index.js +2 -0
- package/dist/guest/runtime/csc.d.ts +1 -0
- package/dist/guest/runtime/csc.js +1 -0
- package/dist/guest/sessions/viewer.js +3 -1
- 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 +40 -0
- package/dist/host/life/host-memory.js +137 -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 +54 -0
- package/dist/host/life/watchdog.js +108 -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 +69 -8
- package/dist/runtime/daemon.js +1058 -624
- package/dist/runtime/db.js +69 -8
- package/dist/runtime/main.js +624 -182
- package/dist/runtime/netproxy.js +69 -8
- package/dist/runtime/view.js +87 -13
- 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/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 +329 -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/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 +7 -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/memory/index.d.ts +1 -0
- package/dist/shared/memory/index.js +1 -0
- package/dist/shared/memory/policy.d.ts +33 -0
- package/dist/shared/memory/policy.js +55 -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 +107 -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 +34 -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 +398 -0
- package/sandbox/csharp/PtyHost.cs +111 -5
- package/schemas/config.schema.json +60 -6
- 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,214 @@
|
|
|
1
|
+
// The memory governor: samples the sandbox's memory every 2 seconds (memgov.exe, sandbox/csharp/MemGov.cs) and, when it
|
|
2
|
+
// runs low, slows the heaviest build programs the agents started down and, as a last resort, pauses the biggest
|
|
3
|
+
// compiler for a few seconds (decide.ts). What it does goes to the memory log and run\pressure.json, for the host.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import { createInterface } from 'node:readline';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { now } from '../../shared/channel/run-dir.js';
|
|
10
|
+
import { writeJsonAtomic } from '../../shared/io/json.js';
|
|
11
|
+
import { GUEST_THRESHOLDS } from '../../shared/memory/index.js';
|
|
12
|
+
import { ensureCompiled } from '../runtime/csc.js';
|
|
13
|
+
import { dataDir } from '../runtime/paths.js';
|
|
14
|
+
import { warn } from '../runtime/log.js';
|
|
15
|
+
import { run } from '../runtime/paths.js';
|
|
16
|
+
import { session } from '../runtime/session.js';
|
|
17
|
+
import { errMsg } from '../../core/util/errors.js';
|
|
18
|
+
import { decide, INITIAL_STATE } from './decide.js';
|
|
19
|
+
const SampleSchema = z.object({
|
|
20
|
+
ramTotalMb: z.number(),
|
|
21
|
+
ramAvailMb: z.number(),
|
|
22
|
+
commitLimitMb: z.number(),
|
|
23
|
+
commitAvailMb: z.number(),
|
|
24
|
+
procs: z
|
|
25
|
+
.array(z.object({
|
|
26
|
+
pid: z.number().int(),
|
|
27
|
+
name: z.string().max(128),
|
|
28
|
+
mb: z.number(),
|
|
29
|
+
prio: z.enum(['idle', 'below', 'normal']).catch('normal'),
|
|
30
|
+
}))
|
|
31
|
+
.max(1024),
|
|
32
|
+
});
|
|
33
|
+
/** The governor's logic over a port: feed it samples; the host's "slow down" requests go to hostPressure. */
|
|
34
|
+
export class GovernorCore {
|
|
35
|
+
deps;
|
|
36
|
+
state = INITIAL_STATE;
|
|
37
|
+
lastAction = '';
|
|
38
|
+
lastWrite = '';
|
|
39
|
+
lastWriteAt = 0;
|
|
40
|
+
pausedName = '';
|
|
41
|
+
pausedPid = 0;
|
|
42
|
+
constructor(deps) {
|
|
43
|
+
this.deps = deps;
|
|
44
|
+
}
|
|
45
|
+
hostPressure(seconds, reason) {
|
|
46
|
+
const until = this.deps.now() + seconds * 1000;
|
|
47
|
+
if (until > this.state.hostUntil) {
|
|
48
|
+
if (this.state.hostUntil <= this.deps.now())
|
|
49
|
+
this.deps.logLine('governor: this PC is low (' + reason + '); slowing builds down');
|
|
50
|
+
this.state = { ...this.state, hostUntil: until };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
get current() {
|
|
54
|
+
return this.state;
|
|
55
|
+
}
|
|
56
|
+
onSample(sample) {
|
|
57
|
+
const nowMs = this.deps.now();
|
|
58
|
+
const before = this.state.pressure;
|
|
59
|
+
const d = decide(this.state, sample, nowMs, GUEST_THRESHOLDS[this.deps.profile], this.deps.profile);
|
|
60
|
+
this.state = d.state;
|
|
61
|
+
for (const action of d.actions)
|
|
62
|
+
this.apply(action);
|
|
63
|
+
if (d.state.pressure !== before)
|
|
64
|
+
this.deps.logLine(d.state.pressure
|
|
65
|
+
? 'governor: memory is low (' +
|
|
66
|
+
d.ramFreePercent +
|
|
67
|
+
'% RAM free, commit ' +
|
|
68
|
+
d.commitPercent +
|
|
69
|
+
'% used): slowing builds down'
|
|
70
|
+
: 'governor: memory is fine again: builds back to normal');
|
|
71
|
+
const paused = nowMs < d.state.pausedUntil && this.pausedPid
|
|
72
|
+
? [{ name: this.pausedName, pid: this.pausedPid, seconds: Math.ceil((d.state.pausedUntil - nowMs) / 1000) }]
|
|
73
|
+
: [];
|
|
74
|
+
const status = {
|
|
75
|
+
level: d.level,
|
|
76
|
+
profile: this.deps.profile,
|
|
77
|
+
ramFreePercent: Math.max(0, Math.min(100, d.ramFreePercent)),
|
|
78
|
+
commitPercent: Math.max(0, Math.min(100, d.commitPercent)),
|
|
79
|
+
lowered: Object.keys(d.state.lowered).length,
|
|
80
|
+
paused,
|
|
81
|
+
pcLow: nowMs < d.state.hostUntil,
|
|
82
|
+
lastAction: this.lastAction,
|
|
83
|
+
updatedAt: now(),
|
|
84
|
+
};
|
|
85
|
+
// Written when something changed, else once a minute (the host reads it, never polls it hard).
|
|
86
|
+
const shape = JSON.stringify({ ...status, updatedAt: '', paused: paused.map((p) => p.pid) });
|
|
87
|
+
if (shape !== this.lastWrite || nowMs - this.lastWriteAt > 60000) {
|
|
88
|
+
this.lastWrite = shape;
|
|
89
|
+
this.lastWriteAt = nowMs;
|
|
90
|
+
this.deps.saveStatus(status);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
apply(action) {
|
|
94
|
+
try {
|
|
95
|
+
if (action.kind === 'priority') {
|
|
96
|
+
this.deps.port.setPriority(action.pid, action.to);
|
|
97
|
+
this.lastAction = action.name + ' (' + action.pid + ') priority ' + action.to;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
this.deps.port.pause(action.pid, action.ms);
|
|
101
|
+
this.pausedName = action.name;
|
|
102
|
+
this.pausedPid = action.pid;
|
|
103
|
+
this.lastAction = 'paused ' + action.name + ' (' + action.pid + ') for ' + action.ms / 1000 + ' s';
|
|
104
|
+
this.deps.logLine('governor: ' + this.lastAction);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.deps.logLine('governor: ' + this.lastAction);
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
warn('governor: ' + errMsg(e));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** memgov.exe as a child process: samples come out of its stdout, commands go into its stdin. */
|
|
115
|
+
class MemGovProcess {
|
|
116
|
+
onSample;
|
|
117
|
+
child = null;
|
|
118
|
+
constructor(onSample) {
|
|
119
|
+
this.onSample = onSample;
|
|
120
|
+
}
|
|
121
|
+
start() {
|
|
122
|
+
try {
|
|
123
|
+
const child = spawn(ensureCompiled('memgov'), ['serve', '--interval', '2'], {
|
|
124
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
125
|
+
windowsHide: true,
|
|
126
|
+
});
|
|
127
|
+
this.child = child;
|
|
128
|
+
createInterface({ input: child.stdout }).on('line', (line) => {
|
|
129
|
+
try {
|
|
130
|
+
this.onSample(SampleSchema.parse(JSON.parse(line)));
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
warn('governor: bad sample: ' + errMsg(e));
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
child.stdin.on('error', () => undefined);
|
|
137
|
+
child.once('exit', () => {
|
|
138
|
+
this.child = null;
|
|
139
|
+
// It ends only when something is wrong: try again in a while.
|
|
140
|
+
setTimeout(() => this.start(), 30000).unref();
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
warn('governor: could not start memgov.exe (no memory governor): ' + errMsg(e));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
setPriority(pid, to) {
|
|
148
|
+
this.child?.stdin?.write('priority ' + pid + ' ' + to + '\n');
|
|
149
|
+
}
|
|
150
|
+
pause(pid, ms) {
|
|
151
|
+
this.child?.stdin?.write('suspend ' + pid + ' ' + ms + '\n');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/** The newest memory log SandboxTools writes, to add the governor's lines to it. */
|
|
155
|
+
function appendToMemoryLog(line) {
|
|
156
|
+
try {
|
|
157
|
+
const logs = path.join(dataDir, 'logs');
|
|
158
|
+
const newest = fs
|
|
159
|
+
.readdirSync(logs)
|
|
160
|
+
.filter((f) => /^memory-\d{8}-\d{6}\.log$/.test(f))
|
|
161
|
+
.sort()
|
|
162
|
+
.pop();
|
|
163
|
+
if (!newest)
|
|
164
|
+
return;
|
|
165
|
+
const t = new Date();
|
|
166
|
+
const stamp = [t.getHours(), t.getMinutes(), t.getSeconds()].map((n) => String(n).padStart(2, '0')).join(':');
|
|
167
|
+
fs.appendFileSync(path.join(logs, newest), stamp + ' | ' + line + '\r\n');
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// The log is for people; a missing or locked one never matters.
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** The daemon's governor: on unless sandbox.pressureResponse is off. */
|
|
174
|
+
export class Governor {
|
|
175
|
+
core = null;
|
|
176
|
+
describe() {
|
|
177
|
+
const profile = session.loadProfile === 'gentle' ? 'gentle' : 'normal';
|
|
178
|
+
const t = GUEST_THRESHOLDS[profile];
|
|
179
|
+
return session.pressureResponse === false
|
|
180
|
+
? 'off (sandbox.pressureResponse)'
|
|
181
|
+
: profile +
|
|
182
|
+
' profile: slows builds down below ' +
|
|
183
|
+
t.enterAvailablePercent +
|
|
184
|
+
'% free RAM or above ' +
|
|
185
|
+
t.enterCommitPercent +
|
|
186
|
+
'% commit';
|
|
187
|
+
}
|
|
188
|
+
start() {
|
|
189
|
+
if (session.pressureResponse === false)
|
|
190
|
+
return;
|
|
191
|
+
const profile = session.loadProfile === 'gentle' ? 'gentle' : 'normal';
|
|
192
|
+
const proc = new MemGovProcess((s) => this.core?.onSample(s));
|
|
193
|
+
this.core = new GovernorCore({
|
|
194
|
+
port: proc,
|
|
195
|
+
profile,
|
|
196
|
+
now: () => Date.now(),
|
|
197
|
+
logLine: appendToMemoryLog,
|
|
198
|
+
saveStatus: (status) => {
|
|
199
|
+
try {
|
|
200
|
+
writeJsonAtomic(run.pressureFile, status);
|
|
201
|
+
}
|
|
202
|
+
catch (e) {
|
|
203
|
+
warn('governor: could not write its status: ' + errMsg(e));
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
proc.start();
|
|
208
|
+
}
|
|
209
|
+
/** The host's watchdog: this PC is low for a while. */
|
|
210
|
+
hostPressure(seconds, reason) {
|
|
211
|
+
this.core?.hostPressure(seconds, reason);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
export const governor = new Governor();
|
|
@@ -7,6 +7,7 @@ declare const SOURCES: {
|
|
|
7
7
|
readonly cargoshim: "CargoShim.cs";
|
|
8
8
|
readonly linkgate: "LinkGate.cs";
|
|
9
9
|
readonly pathsync: "PathSync.cs";
|
|
10
|
+
readonly memgov: "MemGov.cs";
|
|
10
11
|
};
|
|
11
12
|
export type Helper = keyof typeof SOURCES;
|
|
12
13
|
/** %TEMP%\agent-sandbox\<name>-<hash>.exe, compiled from this launch's source the first time it is needed. */
|
|
@@ -30,7 +30,9 @@ export async function viewSession(id) {
|
|
|
30
30
|
return 1;
|
|
31
31
|
}
|
|
32
32
|
const socket = await connect({ ...session.attach, addresses: ['127.0.0.1'] });
|
|
33
|
-
|
|
33
|
+
// The sandbox's own terminal: a copy in the agent reaches the sandbox's clipboard (and yours only through
|
|
34
|
+
// sandbox.clipboard, which already shares it).
|
|
35
|
+
const result = await runTerminalClient(socket, session.attach.token, { clipboardWrites: true });
|
|
34
36
|
if (result.reason === 'exit')
|
|
35
37
|
console.log(session.harness + ' exited with code ' + result.exitCode + '.');
|
|
36
38
|
else if (result.reason === 'error')
|
|
@@ -7,6 +7,7 @@ import { execOutput } from '../../runtime/process.js';
|
|
|
7
7
|
import { ensureCompiled } from '../../runtime/csc.js';
|
|
8
8
|
import { arch, tool } from '../tool-env.js';
|
|
9
9
|
import { wanted } from '../detect.js';
|
|
10
|
+
import { cargoJobs } from '../../../shared/memory/index.js';
|
|
10
11
|
/**
|
|
11
12
|
* Where cargo builds: shared with the PC's target\, one on the sandbox's disk, or a path. "separate" sets no variable:
|
|
12
13
|
* the cargo shim (sandbox/csharp/CargoShim.cs) gives every checkout its own target\sandbox, so git worktrees of several
|
|
@@ -66,8 +67,9 @@ export function cargoDefaults(host) {
|
|
|
66
67
|
const file = tool('rust', 'cargo', 'config.toml');
|
|
67
68
|
if (exists(file) || !exists(path.dirname(file)))
|
|
68
69
|
return;
|
|
69
|
-
// Several agents often build at once: up to 4 jobs each (8 each crashed Windows inside the sandbox on 13 Oct 2026)
|
|
70
|
-
|
|
70
|
+
// Several agents often build at once: up to 4 jobs each (8 each crashed Windows inside the sandbox on 13 Oct 2026),
|
|
71
|
+
// and about 4 GB of memory per job. The daemon lowers it further per session (CARGO_BUILD_JOBS) when several build.
|
|
72
|
+
const jobs = cargoJobs(os.cpus().length, os.totalmem() / 1024 ** 3, 1);
|
|
71
73
|
const gnu = host.endsWith('-gnu');
|
|
72
74
|
fs.writeFileSync(file, [
|
|
73
75
|
'# Written by windows-development-sandbox once; edit freely, it is never overwritten.',
|
package/dist/guest/vpn/vpn.js
CHANGED
|
@@ -9,6 +9,7 @@ import path from 'node:path';
|
|
|
9
9
|
import { now } from '../../shared/channel/run-dir.js';
|
|
10
10
|
import { writeJsonAtomic } from '../../shared/io/json.js';
|
|
11
11
|
import { assertSafeOvpn } from '../../shared/vpn/ovpn.js';
|
|
12
|
+
import { assertSafeWireguard, renderWireguard, routesEverything, wireguardEndpoints, wireguardTunnelAddress, } from '../../shared/vpn/wireguard.js';
|
|
12
13
|
import { isIPv4 } from '../../shared/network/firewall-rules.js';
|
|
13
14
|
import { killSwitchCommands, tunnelCommands } from '../../shared/vpn/kill-switch.js';
|
|
14
15
|
import { errMsg } from '../../core/util/errors.js';
|
|
@@ -18,6 +19,7 @@ import { log, warn } from '../runtime/log.js';
|
|
|
18
19
|
import { session } from '../runtime/session.js';
|
|
19
20
|
import { ensureOpenVpn } from './openvpn.js';
|
|
20
21
|
import { startTunnel } from './tunnel.js';
|
|
22
|
+
import { ensureWireguard, removeWireguardTunnel, startWireguardTunnel, waitForHandshake } from './wireguard.js';
|
|
21
23
|
/** Writes run\vpn.json (what `sandbox vpn status` and the launch show). Never throws. */
|
|
22
24
|
export function setVpnStatus(state, message, extra = {}) {
|
|
23
25
|
try {
|
|
@@ -46,6 +48,21 @@ async function serverAddresses(remotes) {
|
|
|
46
48
|
}
|
|
47
49
|
return out;
|
|
48
50
|
}
|
|
51
|
+
/** The WireGuard peers' endpoints as IPv4 servers (udp), and the config with those addresses in place of host names. */
|
|
52
|
+
async function wireguardServers(config) {
|
|
53
|
+
const endpointIps = {};
|
|
54
|
+
const servers = [];
|
|
55
|
+
const found = wireguardEndpoints(config);
|
|
56
|
+
for (let i = 0; i < found.length; i++) {
|
|
57
|
+
const [ip] = await serverAddresses([{ host: found[i].host, port: found[i].port, proto: 'udp' }]).then((all) => all.map((s) => s.ip));
|
|
58
|
+
if (!ip)
|
|
59
|
+
throw new Error('the VPN server address could not be found');
|
|
60
|
+
// WireGuard looks the name up itself at start, after the kill switch has closed DNS: give it the address.
|
|
61
|
+
endpointIps[i] = ip;
|
|
62
|
+
servers.push({ ip, port: found[i].port, proto: 'udp' });
|
|
63
|
+
}
|
|
64
|
+
return { servers, text: renderWireguard(config, endpointIps) };
|
|
65
|
+
}
|
|
49
66
|
/** What the sandbox looks like from outside, through the tunnel (null when it cannot be told quickly). */
|
|
50
67
|
async function publicAddress() {
|
|
51
68
|
try {
|
|
@@ -74,31 +91,62 @@ export async function setupVpn(secrets, ptyHostExe) {
|
|
|
74
91
|
try {
|
|
75
92
|
if (!session.networking)
|
|
76
93
|
throw new Error('networking is off for this sandbox');
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
let result;
|
|
95
|
+
let wireguardExe = null;
|
|
96
|
+
if (vpn.provider === 'wireguard-file') {
|
|
97
|
+
// The config came from the host (through the one-shot file), but it is data like any other: checked again here.
|
|
98
|
+
if (!secrets.wireguardConfig)
|
|
99
|
+
throw new Error('the WireGuard config did not arrive (start the sandbox again)');
|
|
100
|
+
const config = assertSafeWireguard(secrets.wireguardConfig, vpn.server);
|
|
101
|
+
const exe = await ensureWireguard();
|
|
102
|
+
const { servers, text } = await wireguardServers(config);
|
|
103
|
+
if (killSwitch && !routesEverything(config))
|
|
104
|
+
warn('VPN: AllowedIPs does not include 0.0.0.0/0: with the kill switch on, only the tunnel traffic can leave the sandbox.');
|
|
105
|
+
if (killSwitch)
|
|
106
|
+
block(servers);
|
|
107
|
+
if (dryRun || !isWin) {
|
|
108
|
+
log('(dry run) would start the WireGuard tunnel ' + vpn.server);
|
|
109
|
+
setVpnStatus('off', 'dry run');
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
log('VPN: connecting with ' + vpn.server);
|
|
113
|
+
wireguardExe = exe;
|
|
114
|
+
result = await startWireguardTunnel({ exe, config: text, tunnelIp: wireguardTunnelAddress(config) });
|
|
115
|
+
if (result.ok && result.tunnelIp && killSwitch)
|
|
116
|
+
netsh(tunnelCommands(result.tunnelIp));
|
|
117
|
+
if (result.ok)
|
|
118
|
+
result = await waitForHandshake(exe, publicAddress);
|
|
93
119
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
120
|
+
else {
|
|
121
|
+
const credentials = secrets.vpn;
|
|
122
|
+
if (!credentials)
|
|
123
|
+
throw new Error('the VPN login did not arrive (start the sandbox again)');
|
|
124
|
+
// The profile came from the host, but it is data like any other: checked again here before OpenVPN reads it.
|
|
125
|
+
const profile = path.join(launchDir, 'vpn', 'server.ovpn');
|
|
126
|
+
const remotes = assertSafeOvpn(fs.readFileSync(profile, 'utf8'), vpn.server);
|
|
127
|
+
const exe = await ensureOpenVpn();
|
|
128
|
+
const servers = await serverAddresses(remotes);
|
|
129
|
+
if (servers.length === 0)
|
|
130
|
+
throw new Error('the VPN server addresses could not be found');
|
|
131
|
+
if (killSwitch)
|
|
132
|
+
block(servers);
|
|
133
|
+
if (dryRun || !isWin) {
|
|
134
|
+
log('(dry run) would start the VPN tunnel to ' + vpn.server);
|
|
135
|
+
setVpnStatus('off', 'dry run');
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
log('VPN: connecting to ' + vpn.server);
|
|
139
|
+
result = await startTunnel({ exe, profile, username: credentials.username, password: credentials.password });
|
|
140
|
+
if (result.ok && result.tunnelIp && hasAdapter(result.tunnelIp) && killSwitch)
|
|
141
|
+
netsh(tunnelCommands(result.tunnelIp));
|
|
142
|
+
}
|
|
143
|
+
if (!result.ok) {
|
|
144
|
+
if (wireguardExe)
|
|
145
|
+
await removeWireguardTunnel(wireguardExe);
|
|
97
146
|
throw new Error(result.message);
|
|
147
|
+
}
|
|
98
148
|
if (!result.tunnelIp || !hasAdapter(result.tunnelIp))
|
|
99
149
|
throw new Error('the tunnel reported it was up, but no network adapter with its address appeared');
|
|
100
|
-
if (killSwitch)
|
|
101
|
-
netsh(tunnelCommands(result.tunnelIp));
|
|
102
150
|
const publicIp = await publicAddress();
|
|
103
151
|
log('VPN: up' + (publicIp ? ' (the sandbox appears as ' + publicIp + ')' : '') + '.');
|
|
104
152
|
setVpnStatus('up', 'The tunnel is up', publicIp ? { publicIp } : {});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { TunnelOutcome } from './tunnel.js';
|
|
2
|
+
/** The tunnel's name: the config file is sandbox.conf and the Windows service WireGuardTunnel$sandbox. */
|
|
3
|
+
export declare const WIREGUARD_TUNNEL = "sandbox";
|
|
4
|
+
export declare function wireguardDir(env?: NodeJS.ProcessEnv): string;
|
|
5
|
+
export declare function wireguardExe(env?: NodeJS.ProcessEnv): string;
|
|
6
|
+
export declare function wireguardDownload(arch?: string): {
|
|
7
|
+
file: string;
|
|
8
|
+
sha256: string;
|
|
9
|
+
url: string;
|
|
10
|
+
};
|
|
11
|
+
/** Installs WireGuard unless it is there; returns the path of wireguard.exe. Throws with the reason when it cannot. */
|
|
12
|
+
export declare function ensureWireguard(): Promise<string>;
|
|
13
|
+
/** Takes the tunnel away again (a failed tunnel must not leave a route into nothing). Never throws. */
|
|
14
|
+
export declare function removeWireguardTunnel(exe: string): Promise<void>;
|
|
15
|
+
export interface WireguardOptions {
|
|
16
|
+
readonly exe: string;
|
|
17
|
+
/** The checked config text (private key inside). */
|
|
18
|
+
readonly config: string;
|
|
19
|
+
/** The tunnel's own IPv4 address from the config's Address. */
|
|
20
|
+
readonly tunnelIp: string;
|
|
21
|
+
readonly timeoutMs?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Installs the tunnel service from a config file that is deleted when the service has it, and waits until the service
|
|
25
|
+
* runs and its adapter has the tunnel address. Removes the service again when that fails.
|
|
26
|
+
*/
|
|
27
|
+
export declare function startWireguardTunnel(o: WireguardOptions): Promise<TunnelOutcome>;
|
|
28
|
+
/**
|
|
29
|
+
* Waits for the server to answer: the first handshake. `poke` sends some traffic through the tunnel (a handshake only
|
|
30
|
+
* starts when there is something to send, unless PersistentKeepalive is set). Without wg.exe the check is skipped.
|
|
31
|
+
*/
|
|
32
|
+
export declare function waitForHandshake(exe: string, poke: () => Promise<unknown>, timeoutMs?: number): Promise<TunnelOutcome>;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// WireGuard for the sandbox: one checksummed MSI from the publisher (installed silently, it brings wireguard.exe and
|
|
2
|
+
// wg.exe), the checked config in a file that exists only while the tunnel service starts, and a wait for the
|
|
3
|
+
// tunnel. The config holds the private key: it is written from the one-shot secrets file and deleted as soon as the
|
|
4
|
+
// service has it (or has failed). The kill switch is applied by the caller BEFORE this runs.
|
|
5
|
+
import { execFile } from 'node:child_process';
|
|
6
|
+
import crypto from 'node:crypto';
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import os from 'node:os';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { downloader, spawnAsync } from '../../shared/download/index.js';
|
|
11
|
+
import { SYSTEM } from '../../shared/system.js';
|
|
12
|
+
import { WIREGUARD_MSI } from '../../shared/vpn/download.js';
|
|
13
|
+
import { handshakeDone, serviceRunning } from '../../shared/vpn/wireguard.js';
|
|
14
|
+
import { dryRun, isWin, toolsDir } from '../runtime/paths.js';
|
|
15
|
+
import { exists, mkdirp } from '../runtime/fs.js';
|
|
16
|
+
import { log } from '../runtime/log.js';
|
|
17
|
+
/** The tunnel's name: the config file is sandbox.conf and the Windows service WireGuardTunnel$sandbox. */
|
|
18
|
+
export const WIREGUARD_TUNNEL = 'sandbox';
|
|
19
|
+
export function wireguardDir(env = process.env) {
|
|
20
|
+
return path.join(env.ProgramFiles ?? 'C:\\Program Files', 'WireGuard');
|
|
21
|
+
}
|
|
22
|
+
export function wireguardExe(env = process.env) {
|
|
23
|
+
return path.join(wireguardDir(env), 'wireguard.exe');
|
|
24
|
+
}
|
|
25
|
+
export function wireguardDownload(arch = process.arch) {
|
|
26
|
+
const entry = arch === 'arm64' ? WIREGUARD_MSI.arm64 : WIREGUARD_MSI.x64;
|
|
27
|
+
return { ...entry, url: WIREGUARD_MSI.base + entry.file };
|
|
28
|
+
}
|
|
29
|
+
const hasHash = (file, sha256) => crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex') === sha256;
|
|
30
|
+
/** Installs WireGuard unless it is there; returns the path of wireguard.exe. Throws with the reason when it cannot. */
|
|
31
|
+
export async function ensureWireguard() {
|
|
32
|
+
const exe = wireguardExe();
|
|
33
|
+
if (!isWin || dryRun)
|
|
34
|
+
return exe;
|
|
35
|
+
if (exists(exe))
|
|
36
|
+
return exe;
|
|
37
|
+
const dl = wireguardDownload();
|
|
38
|
+
const msi = path.join(mkdirp(path.join(toolsDir, 'downloads')), dl.file);
|
|
39
|
+
// The tools folder lasts between sandboxes and the agent can write it: a cached file is trusted only by its hash.
|
|
40
|
+
if (!exists(msi) || !hasHash(msi, dl.sha256)) {
|
|
41
|
+
log('VPN: downloading WireGuard ' + WIREGUARD_MSI.version + ' (about 3 MB)');
|
|
42
|
+
await downloader.file(dl.url, msi, dl.sha256);
|
|
43
|
+
}
|
|
44
|
+
log('VPN: installing WireGuard');
|
|
45
|
+
const logFile = path.join(toolsDir, 'downloads', 'wireguard-install.log');
|
|
46
|
+
// DO_NOT_LAUNCH: the installer would otherwise start the manager window.
|
|
47
|
+
const code = await spawnAsync(SYSTEM.msiexec(), ['/i', msi, '/qn', '/norestart', 'DO_NOT_LAUNCH=1', '/l*v', logFile], {
|
|
48
|
+
stdio: 'ignore',
|
|
49
|
+
});
|
|
50
|
+
if (code !== 0 && code !== 3010)
|
|
51
|
+
throw new Error('The WireGuard installer failed (exit ' + code + '). Its log: ' + logFile);
|
|
52
|
+
if (!exists(exe))
|
|
53
|
+
throw new Error('WireGuard was installed but ' + exe + ' is missing. The installer log: ' + logFile);
|
|
54
|
+
return exe;
|
|
55
|
+
}
|
|
56
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
57
|
+
function output(file, args) {
|
|
58
|
+
return new Promise((resolve) => {
|
|
59
|
+
execFile(file, args, { windowsHide: true, timeout: 15000, maxBuffer: 1024 * 1024 }, (error, stdout, stderr) => {
|
|
60
|
+
const code = error ? (typeof error.code === 'number' ? error.code : -1) : 0;
|
|
61
|
+
resolve({ code, text: String(stdout) + String(stderr) });
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const serviceName = () => 'WireGuardTunnel$' + WIREGUARD_TUNNEL;
|
|
66
|
+
const hasAdapter = (ip) => Object.values(os.networkInterfaces()).some((list) => (list ?? []).some((i) => i.address === ip));
|
|
67
|
+
/** Takes the tunnel away again (a failed tunnel must not leave a route into nothing). Never throws. */
|
|
68
|
+
export async function removeWireguardTunnel(exe) {
|
|
69
|
+
try {
|
|
70
|
+
await output(exe, ['/uninstalltunnelservice', WIREGUARD_TUNNEL]);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Nothing installed, nothing to remove.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Installs the tunnel service from a config file that is deleted when the service has it, and waits until the service
|
|
78
|
+
* runs and its adapter has the tunnel address. Removes the service again when that fails.
|
|
79
|
+
*/
|
|
80
|
+
export async function startWireguardTunnel(o) {
|
|
81
|
+
const root = path.join(process.env.SystemRoot ?? 'C:\\Windows', 'Temp');
|
|
82
|
+
const dir = fs.mkdtempSync(path.join(fs.existsSync(root) ? root : os.tmpdir(), 'sbx-wg-'));
|
|
83
|
+
const file = path.join(dir, WIREGUARD_TUNNEL + '.conf');
|
|
84
|
+
fs.writeFileSync(file, o.config, 'utf8');
|
|
85
|
+
const fail = async (message) => {
|
|
86
|
+
await removeWireguardTunnel(o.exe);
|
|
87
|
+
return { ok: false, message };
|
|
88
|
+
};
|
|
89
|
+
try {
|
|
90
|
+
const install = await output(o.exe, ['/installtunnelservice', file]);
|
|
91
|
+
if (install.code !== 0)
|
|
92
|
+
return await fail('WireGuard could not install the tunnel (exit ' + install.code + ')');
|
|
93
|
+
const deadline = Date.now() + (o.timeoutMs ?? 60000);
|
|
94
|
+
for (;;) {
|
|
95
|
+
const state = await output(SYSTEM.sc(), ['query', serviceName()]);
|
|
96
|
+
if (serviceRunning(state.text) && hasAdapter(o.tunnelIp)) {
|
|
97
|
+
return { ok: true, message: 'the tunnel service is running', tunnelIp: o.tunnelIp };
|
|
98
|
+
}
|
|
99
|
+
if (/STOPPED/i.test(state.text) || /1060/.test(state.text))
|
|
100
|
+
return await fail('the WireGuard tunnel service stopped (check the config: keys, address, endpoint)');
|
|
101
|
+
if (Date.now() > deadline)
|
|
102
|
+
return await fail('the WireGuard tunnel did not start in time (the network driver may not work here)');
|
|
103
|
+
await sleep(500);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
// The service has read the file by now (or never will): the private key is not left on disk.
|
|
108
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Waits for the server to answer: the first handshake. `poke` sends some traffic through the tunnel (a handshake only
|
|
113
|
+
* starts when there is something to send, unless PersistentKeepalive is set). Without wg.exe the check is skipped.
|
|
114
|
+
*/
|
|
115
|
+
export async function waitForHandshake(exe, poke, timeoutMs = 30000) {
|
|
116
|
+
const wg = path.join(path.dirname(exe), 'wg.exe');
|
|
117
|
+
if (!exists(wg))
|
|
118
|
+
return { ok: true, message: 'the tunnel service is running (no wg.exe to check the handshake)' };
|
|
119
|
+
const deadline = Date.now() + timeoutMs;
|
|
120
|
+
for (;;) {
|
|
121
|
+
await poke().catch(() => undefined);
|
|
122
|
+
const shown = await output(wg, ['show', WIREGUARD_TUNNEL, 'latest-handshakes']);
|
|
123
|
+
if (shown.code === 0 && handshakeDone(shown.text))
|
|
124
|
+
return { ok: true, message: 'the server answered' };
|
|
125
|
+
if (Date.now() > deadline)
|
|
126
|
+
return {
|
|
127
|
+
ok: false,
|
|
128
|
+
message: 'the VPN server did not answer (check the endpoint, the keys and that its UDP port is reachable)',
|
|
129
|
+
};
|
|
130
|
+
await sleep(1000);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -9,5 +9,9 @@ export interface AgentCommand {
|
|
|
9
9
|
export declare function cmdSafe(p: string): boolean;
|
|
10
10
|
/** The agent's own executable on this PC, skipping this tool's forwarding files. */
|
|
11
11
|
export declare function realAgent(command: string, skipDir: string, pathValue?: string): string | null;
|
|
12
|
+
/** realAgent without blocking the program: every candidate is looked up at once, the first in PATH order wins. */
|
|
13
|
+
export declare function realAgentAsync(command: string, skipDir: string, pathValue?: string): Promise<string | null>;
|
|
12
14
|
/** The agents that get a forwarding command: installed on this PC, and listed (or "auto" = all of them). */
|
|
13
15
|
export declare function agentCommands(config: Config, storageRoot: string, pathValue?: string): AgentCommand[];
|
|
16
|
+
/** agentCommands without blocking the program. */
|
|
17
|
+
export declare function agentCommandsAsync(config: Config, storageRoot: string, pathValue?: string): Promise<AgentCommand[]>;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// <storage>\agent-commands go first on YOUR PATH (so they win over npm's and the installers' copies); turning it off
|
|
4
4
|
// removes the files and that one PATH entry. SANDBOX_AGENT_DIRECT=1 runs the real agent on this PC instead.
|
|
5
5
|
import path from 'node:path';
|
|
6
|
-
import { fileExists } from '../../core/util/index.js';
|
|
6
|
+
import { fileExists, fileExistsAsync } from '../../core/util/index.js';
|
|
7
7
|
export function commandsDir(storageRoot) {
|
|
8
8
|
return path.join(storageRoot, 'agent-commands');
|
|
9
9
|
}
|
|
@@ -12,11 +12,14 @@ const EXTENSIONS = ['.exe', '.cmd', '.bat'];
|
|
|
12
12
|
export function cmdSafe(p) {
|
|
13
13
|
return !/[%"^\r\n]/.test(p);
|
|
14
14
|
}
|
|
15
|
+
/** The folders of PATH to look in, without this tool's forwarding folder. */
|
|
16
|
+
function searchDirs(skipDir, pathValue) {
|
|
17
|
+
const skip = path.resolve(skipDir).toLowerCase();
|
|
18
|
+
return pathValue.split(path.delimiter).filter((d) => d && path.resolve(d).toLowerCase() !== skip);
|
|
19
|
+
}
|
|
15
20
|
/** The agent's own executable on this PC, skipping this tool's forwarding files. */
|
|
16
21
|
export function realAgent(command, skipDir, pathValue = process.env.PATH ?? process.env.Path ?? '') {
|
|
17
|
-
const
|
|
18
|
-
const dirs = pathValue.split(path.delimiter).filter((d) => d && path.resolve(d).toLowerCase() !== skip);
|
|
19
|
-
for (const dir of dirs)
|
|
22
|
+
for (const dir of searchDirs(skipDir, pathValue))
|
|
20
23
|
for (const ext of EXTENSIONS) {
|
|
21
24
|
const candidate = path.join(dir, command + ext);
|
|
22
25
|
if (fileExists(candidate))
|
|
@@ -24,14 +27,35 @@ export function realAgent(command, skipDir, pathValue = process.env.PATH ?? proc
|
|
|
24
27
|
}
|
|
25
28
|
return null;
|
|
26
29
|
}
|
|
27
|
-
/**
|
|
28
|
-
export function
|
|
30
|
+
/** realAgent without blocking the program: every candidate is looked up at once, the first in PATH order wins. */
|
|
31
|
+
export async function realAgentAsync(command, skipDir, pathValue = process.env.PATH ?? process.env.Path ?? '') {
|
|
32
|
+
const candidates = searchDirs(skipDir, pathValue).flatMap((dir) => EXTENSIONS.map((ext) => path.join(dir, command + ext)));
|
|
33
|
+
const found = await Promise.all(candidates.map(fileExistsAsync));
|
|
34
|
+
const at = found.indexOf(true);
|
|
35
|
+
return at < 0 ? null : candidates[at];
|
|
36
|
+
}
|
|
37
|
+
/** The agents that may get a forwarding command (listed, or "auto" = all) and the command each is typed as. */
|
|
38
|
+
function wantedCommands(config) {
|
|
29
39
|
const wanted = config.agentCommands.agents;
|
|
30
40
|
return Object.entries(config.harnesses).flatMap(([harness, h]) => {
|
|
31
41
|
if (wanted !== 'auto' && !wanted.includes(harness))
|
|
32
42
|
return [];
|
|
33
43
|
const command = h.npmBin ?? (h.hostExecutable ? path.parse(h.hostExecutable).name : harness);
|
|
34
|
-
|
|
44
|
+
return /^[A-Za-z0-9._-]+$/.test(command) ? [{ harness, command }] : [];
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** The agents that get a forwarding command: installed on this PC, and listed (or "auto" = all of them). */
|
|
48
|
+
export function agentCommands(config, storageRoot, pathValue) {
|
|
49
|
+
return wantedCommands(config).flatMap(({ harness, command }) => {
|
|
50
|
+
const real = realAgent(command, commandsDir(storageRoot), pathValue);
|
|
35
51
|
return real && cmdSafe(real) ? [{ harness, command, real }] : [];
|
|
36
52
|
});
|
|
37
53
|
}
|
|
54
|
+
/** agentCommands without blocking the program. */
|
|
55
|
+
export async function agentCommandsAsync(config, storageRoot, pathValue) {
|
|
56
|
+
const found = await Promise.all(wantedCommands(config).map(async ({ harness, command }) => {
|
|
57
|
+
const real = await realAgentAsync(command, commandsDir(storageRoot), pathValue);
|
|
58
|
+
return real && cmdSafe(real) ? [{ harness, command, real }] : [];
|
|
59
|
+
}));
|
|
60
|
+
return found.flat();
|
|
61
|
+
}
|
package/dist/host/audit/log.d.ts
CHANGED