windows-development-sandbox 1.9.0 → 1.11.3
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/README.md +64 -17
- package/config.example.json +7 -2
- package/dist/app/features/accounts/accounts.def.d.ts +122 -0
- package/dist/app/features/accounts/accounts.def.js +243 -0
- package/dist/app/features/accounts/accounts.run.d.ts +51 -0
- package/dist/app/features/accounts/accounts.run.js +125 -0
- package/dist/app/features/accounts/index.d.ts +2 -0
- package/dist/app/features/accounts/index.js +3 -0
- package/dist/app/features/agents/agent-entry.d.ts +17 -0
- package/dist/app/features/agents/agent-entry.js +50 -0
- package/dist/app/features/agents/agents.def.d.ts +65 -0
- package/dist/app/features/agents/agents.def.js +142 -0
- package/dist/app/features/agents/agents.run.d.ts +31 -0
- package/dist/app/features/agents/agents.run.js +189 -0
- package/dist/app/features/agents/index.d.ts +3 -0
- package/dist/app/features/agents/index.js +4 -0
- package/dist/app/features/apps/apps-rows.d.ts +31 -0
- package/dist/app/features/apps/apps-rows.js +73 -0
- package/dist/app/features/apps/apps.def.d.ts +181 -0
- package/dist/app/features/apps/apps.def.js +264 -0
- package/dist/app/features/apps/apps.run.d.ts +58 -0
- package/dist/app/features/apps/apps.run.js +76 -0
- package/dist/app/features/apps/index.d.ts +3 -0
- package/dist/app/features/apps/index.js +4 -0
- package/dist/app/features/data/data.def.d.ts +154 -0
- package/dist/app/features/data/data.def.js +201 -0
- package/dist/app/features/data/data.run.d.ts +46 -0
- package/dist/app/features/data/data.run.js +141 -0
- package/dist/app/features/data/index.d.ts +2 -0
- package/dist/app/features/data/index.js +3 -0
- package/dist/app/features/defs.d.ts +952 -4
- package/dist/app/features/defs.js +147 -10
- package/dist/app/features/engine/engine.def.d.ts +228 -0
- package/dist/app/features/engine/engine.def.js +194 -0
- package/dist/app/features/engine/engine.run.d.ts +72 -0
- package/dist/app/features/engine/engine.run.js +77 -0
- package/dist/app/features/engine/index.d.ts +2 -0
- package/dist/app/features/engine/index.js +4 -0
- package/dist/app/features/files/files.def.d.ts +51 -0
- package/dist/app/features/files/files.def.js +38 -0
- package/dist/app/features/files/files.run.d.ts +20 -0
- package/dist/app/features/files/files.run.js +59 -0
- package/dist/app/features/files/index.d.ts +2 -0
- package/dist/app/features/files/index.js +3 -0
- package/dist/app/features/github/github.def.d.ts +80 -0
- package/dist/app/features/github/github.def.js +142 -0
- package/dist/app/features/github/github.run.d.ts +36 -0
- package/dist/app/features/github/github.run.js +59 -0
- package/dist/app/features/github/index.d.ts +2 -0
- package/dist/app/features/github/index.js +3 -0
- package/dist/app/features/health/health.def.d.ts +275 -0
- package/dist/app/features/health/health.def.js +281 -0
- package/dist/app/features/health/health.run.d.ts +125 -0
- package/dist/app/features/health/health.run.js +204 -0
- package/dist/app/features/health/index.d.ts +2 -0
- package/dist/app/features/health/index.js +3 -0
- package/dist/app/features/logs/index.d.ts +2 -0
- package/dist/app/features/logs/index.js +3 -0
- package/dist/app/features/logs/logs.def.d.ts +78 -0
- package/dist/app/features/logs/logs.def.js +115 -0
- package/dist/app/features/logs/logs.run.d.ts +20 -0
- package/dist/app/features/logs/logs.run.js +42 -0
- package/dist/app/features/projects/index.d.ts +2 -0
- package/dist/app/features/projects/index.js +3 -0
- package/dist/app/features/projects/projects.def.d.ts +101 -0
- package/dist/app/features/projects/projects.def.js +208 -0
- package/dist/app/features/projects/projects.run.d.ts +56 -0
- package/dist/app/features/projects/projects.run.js +155 -0
- package/dist/app/features/sandbox/sandbox.def.js +11 -5
- package/dist/app/features/sessions/sessions.def.js +3 -0
- package/dist/app/features/settings/categories.def.js +3 -2
- package/dist/{cli/menu/settings/share.d.ts → app/features/settings/settings-share.d.ts} +1 -1
- package/dist/{cli/menu/settings/share.js → app/features/settings/settings-share.js} +3 -2
- package/dist/app/features/settings/settings.def.d.ts +22 -5
- package/dist/app/features/settings/settings.def.js +81 -6
- package/dist/app/features/settings/settings.run.d.ts +17 -0
- package/dist/app/features/settings/settings.run.js +89 -4
- package/dist/app/features/storage/index.d.ts +2 -0
- package/dist/app/features/storage/index.js +3 -0
- package/dist/app/features/storage/storage.def.d.ts +133 -0
- package/dist/app/features/storage/storage.def.js +225 -0
- package/dist/app/features/storage/storage.run.d.ts +62 -0
- package/dist/app/features/storage/storage.run.js +370 -0
- package/dist/app/features/updates/index.d.ts +3 -0
- package/dist/app/features/updates/index.js +4 -0
- package/dist/app/features/updates/update-lines.d.ts +13 -0
- package/dist/app/features/updates/update-lines.js +61 -0
- package/dist/app/features/updates/updates.def.d.ts +89 -0
- package/dist/app/features/updates/updates.def.js +225 -0
- package/dist/app/features/updates/updates.run.d.ts +48 -0
- package/dist/app/features/updates/updates.run.js +154 -0
- package/dist/app/features/vpn/index.d.ts +2 -0
- package/dist/app/features/vpn/index.js +3 -0
- package/dist/app/features/vpn/vpn.def.d.ts +112 -0
- package/dist/app/features/vpn/vpn.def.js +174 -0
- package/dist/app/features/vpn/vpn.run.d.ts +37 -0
- package/dist/app/features/vpn/vpn.run.js +54 -0
- package/dist/app/features/web/index.d.ts +2 -0
- package/dist/app/features/web/index.js +3 -0
- package/dist/app/features/web/web.def.d.ts +33 -0
- package/dist/app/features/web/web.def.js +90 -0
- package/dist/app/features/web/web.run.d.ts +24 -0
- package/dist/app/features/web/web.run.js +93 -0
- package/dist/app/kernel/define.d.ts +10 -1
- package/dist/app/kernel/io.d.ts +34 -0
- package/dist/app/kernel/io.js +9 -0
- package/dist/app/kernel/rpc.d.ts +17 -0
- package/dist/app/kernel/text.d.ts +2 -0
- package/dist/app/kernel/text.js +7 -0
- package/dist/app/kernel/view.d.ts +24 -1
- package/dist/app/kernel/view.js +1 -1
- package/dist/app/registry.js +74 -1
- package/dist/cli/args.js +1 -1
- package/dist/cli/commands/context.d.ts +7 -0
- package/dist/cli/commands/context.js +29 -2
- package/dist/cli/commands/engine.js +5 -58
- package/dist/cli/commands/github.d.ts +2 -0
- package/dist/cli/commands/github.js +11 -0
- package/dist/cli/commands/index.d.ts +3 -5
- package/dist/cli/commands/index.js +3 -5
- package/dist/cli/commands/launch/launch.js +23 -4
- package/dist/cli/commands/launch/running.js +4 -1
- package/dist/cli/commands/maintenance/config.js +1 -6
- package/dist/cli/commands/maintenance/index.d.ts +0 -1
- package/dist/cli/commands/maintenance/index.js +1 -2
- package/dist/cli/commands/maintenance/logs.js +27 -32
- package/dist/cli/commands/maintenance/migrate.d.ts +0 -8
- package/dist/cli/commands/maintenance/migrate.js +2 -44
- package/dist/cli/commands/maintenance/update.d.ts +0 -19
- package/dist/cli/commands/maintenance/update.js +1 -99
- package/dist/cli/commands/projects/index.d.ts +0 -1
- package/dist/cli/commands/projects/index.js +0 -1
- package/dist/cli/commands/registry.d.ts +1 -1
- package/dist/cli/commands/registry.js +29 -11
- package/dist/cli/commands/repair/checks/data.js +1 -1
- package/dist/cli/commands/repair/checks/hcs.js +28 -1
- package/dist/cli/commands/repair/checks/sandbox.js +1 -1
- package/dist/cli/commands/repair/health-run.d.ts +30 -0
- package/dist/cli/commands/repair/health-run.js +32 -0
- package/dist/cli/commands/repair/printer.d.ts +2 -2
- package/dist/cli/commands/repair/printer.js +2 -11
- package/dist/cli/commands/repair/run.d.ts +1 -21
- package/dist/cli/commands/repair/run.js +13 -70
- package/dist/cli/commands/storage/index.d.ts +0 -1
- package/dist/cli/commands/storage/index.js +0 -1
- package/dist/cli/commands/storage/offer.js +6 -2
- package/dist/cli/commands/vpn.d.ts +1 -3
- package/dist/cli/commands/vpn.js +8 -88
- package/dist/cli/commands/web.d.ts +7 -1
- package/dist/cli/commands/web.js +45 -71
- package/dist/cli/index.js +3 -2
- package/dist/cli/manifest.d.ts +16 -153
- package/dist/cli/manifest.js +20 -50
- package/dist/cli/menu/command-form.js +1 -2
- package/dist/cli/menu/io-menu.d.ts +2 -2
- package/dist/cli/menu/io-menu.js +6 -4
- package/dist/cli/menu/menu.js +1 -1
- package/dist/cli/menu/pages/accounts-page.d.ts +5 -17
- package/dist/cli/menu/pages/accounts-page.js +28 -170
- package/dist/cli/menu/pages/agents-page.d.ts +17 -18
- package/dist/cli/menu/pages/agents-page.js +67 -124
- package/dist/cli/menu/pages/apps-page.d.ts +15 -8
- package/dist/cli/menu/pages/apps-page.js +121 -29
- package/dist/cli/menu/pages/list-page.js +10 -1
- package/dist/cli/menu/pages/maintenance-page.d.ts +51 -52
- package/dist/cli/menu/pages/maintenance-page.js +246 -427
- package/dist/cli/menu/pages/sandbox-page.d.ts +3 -5
- package/dist/cli/menu/pages/sandbox-page.js +34 -112
- package/dist/cli/menu/pages/settings-page.d.ts +21 -3
- package/dist/cli/menu/pages/settings-page.js +89 -17
- package/dist/cli/menu/render-view.js +2 -1
- package/dist/cli/menu/settings/index.d.ts +0 -1
- package/dist/cli/menu/settings/index.js +3 -3
- package/dist/cli/menu/views.js +28 -0
- package/dist/cli/operations/handler.js +7 -3
- package/dist/cli/operations/input.d.ts +1 -1
- package/dist/cli/operations/input.js +29 -5
- package/dist/cli/operations/io-cli.js +9 -1
- package/dist/cli/operations/spec.js +8 -2
- package/dist/cli/output/render-view.js +2 -0
- package/dist/cli/{commands/data/collect.js → services/data-collect.js} +2 -2
- package/dist/cli/{commands/data/remove.js → services/data-remove.js} +2 -2
- package/dist/cli/services/data-service.d.ts +1 -1
- package/dist/cli/services/data-service.js +2 -2
- package/dist/cli/services/databases-service.d.ts +5 -0
- package/dist/cli/services/databases-service.js +15 -0
- package/dist/cli/services/health-service.d.ts +8 -4
- package/dist/cli/services/health-service.js +10 -8
- package/dist/cli/services/index.d.ts +4 -1
- package/dist/cli/services/index.js +4 -1
- package/dist/cli/services/logs-service.d.ts +7 -0
- package/dist/cli/services/logs-service.js +13 -0
- package/dist/cli/services/storage-service.d.ts +19 -0
- package/dist/cli/services/storage-service.js +71 -0
- package/dist/cli/services/vpn-service.d.ts +6 -4
- package/dist/cli/services/vpn-service.js +5 -11
- package/dist/cli/services/web-service.d.ts +4 -3
- package/dist/cli/services/web-service.js +12 -32
- package/dist/core/config/defaults.js +3 -2
- package/dist/core/schema/config.d.ts +6 -0
- package/dist/core/schema/config.js +2 -0
- package/dist/core/schema/github.d.ts +4 -0
- package/dist/core/schema/github.js +7 -0
- package/dist/core/schema/index.d.ts +1 -0
- package/dist/core/schema/index.js +1 -0
- package/dist/core/schema/profile.d.ts +1 -0
- package/dist/core/schema/profile.js +3 -0
- package/dist/core/schema/sandbox.js +3 -3
- package/dist/core/schema/toolchains.js +2 -2
- package/dist/core/schema/vpn.js +1 -1
- package/dist/core/schema/web.d.ts +1 -0
- package/dist/core/schema/web.js +4 -1
- package/dist/core/types.d.ts +6 -3
- package/dist/guest/boot/boot.js +11 -1
- package/dist/guest/daemon/daemon.js +1 -1
- package/dist/guest/daemon/sessions.d.ts +3 -2
- package/dist/guest/daemon/sessions.js +38 -7
- package/dist/guest/governor/core.d.ts +44 -0
- package/dist/guest/governor/core.js +124 -0
- package/dist/guest/governor/decide.d.ts +53 -15
- package/dist/guest/governor/decide.js +155 -72
- package/dist/guest/governor/governor.d.ts +2 -35
- package/dist/guest/governor/governor.js +35 -99
- package/dist/guest/governor/index.d.ts +1 -0
- package/dist/guest/governor/index.js +1 -0
- package/dist/guest/network/firewall.d.ts +3 -0
- package/dist/guest/network/firewall.js +65 -23
- package/dist/guest/runtime/process.js +53 -5
- package/dist/guest/sessions/viewer.js +5 -1
- package/dist/guest/setup/claude-mem.js +12 -4
- package/dist/guest/setup/github-token.d.ts +7 -0
- package/dist/guest/setup/github-token.js +65 -0
- package/dist/guest/toolchains/languages/node.js +40 -4
- package/dist/guest/toolchains/native/rust-extras.js +2 -2
- package/dist/guest/toolchains/tools/cli.d.ts +8 -0
- package/dist/guest/toolchains/tools/cli.js +16 -2
- package/dist/guest/vpn/openvpn.d.ts +5 -0
- package/dist/guest/vpn/openvpn.js +14 -1
- package/dist/guest/vpn/vpn.js +55 -16
- package/dist/guest/vpn/wireguard.js +2 -0
- package/dist/host/channel/attach.d.ts +1 -0
- package/dist/host/channel/attach.js +1 -0
- package/dist/host/channel/channel.d.ts +1 -1
- package/dist/host/channel/channel.js +2 -2
- package/dist/host/engines/hcs/check.d.ts +1 -0
- package/dist/host/engines/hcs/check.js +1 -1
- package/dist/host/engines/hcs/engine.d.ts +1 -1
- package/dist/host/engines/hcs/engine.js +6 -4
- package/dist/host/engines/hcs/helper.d.ts +11 -0
- package/dist/host/engines/hcs/helper.js +31 -2
- package/dist/host/engines/hcs/leftovers.d.ts +42 -0
- package/dist/host/engines/hcs/leftovers.js +101 -0
- package/dist/host/engines/index.d.ts +1 -0
- package/dist/host/engines/index.js +1 -0
- package/dist/host/github/deliver.d.ts +13 -0
- package/dist/host/github/deliver.js +51 -0
- package/dist/host/github/index.d.ts +2 -0
- package/dist/host/github/index.js +3 -0
- package/dist/host/github/token.d.ts +21 -0
- package/dist/host/github/token.js +74 -0
- package/dist/host/launch/generate.js +11 -5
- package/dist/host/launch/index.d.ts +1 -1
- package/dist/host/launch/index.js +1 -1
- package/dist/host/launch/session-file.js +1 -2
- package/dist/host/launch/wsb-file.d.ts +11 -7
- package/dist/host/launch/wsb-file.js +30 -12
- package/dist/host/life/host-memory.d.ts +1 -0
- package/dist/host/life/host-memory.js +4 -1
- package/dist/host/life/watchdog.d.ts +11 -0
- package/dist/host/life/watchdog.js +36 -4
- package/dist/host/sandbox/tasklist.d.ts +6 -0
- package/dist/host/sandbox/tasklist.js +37 -0
- package/dist/host/secrets/launch-secrets.d.ts +2 -1
- package/dist/host/secrets/launch-secrets.js +4 -2
- package/dist/host/vpn/status.js +13 -3
- package/dist/host/web/backend.d.ts +1 -3
- package/dist/host/web/index.d.ts +2 -1
- package/dist/host/web/index.js +2 -1
- package/dist/host/web/live.d.ts +23 -0
- package/dist/host/web/live.js +107 -0
- package/dist/host/web/rpc.d.ts +6 -1
- package/dist/host/web/rpc.js +23 -31
- package/dist/host/web/runner.d.ts +4 -0
- package/dist/host/web/runner.js +12 -1
- package/dist/host/web/server.d.ts +30 -2
- package/dist/host/web/server.js +87 -29
- package/dist/host/web/state.d.ts +1 -0
- package/dist/host/web/state.js +2 -0
- package/dist/runtime/agent.js +44 -16
- package/dist/runtime/daemon.js +1154 -857
- package/dist/runtime/db.js +44 -16
- package/dist/runtime/main.js +532 -115
- package/dist/runtime/netproxy.js +34 -9
- package/dist/runtime/view.js +499 -149
- package/dist/selftest/app.test.js +620 -12
- package/dist/selftest/channel.test.js +18 -1
- package/dist/selftest/cli.test.js +1 -1
- package/dist/selftest/config.test.js +1 -1
- package/dist/selftest/contained.test.js +1 -1
- package/dist/selftest/data.test.js +1 -2
- package/dist/selftest/engine.test.js +3 -1
- package/dist/selftest/github.test.d.ts +2 -0
- package/dist/selftest/github.test.js +243 -0
- package/dist/selftest/guest-boot.test.js +47 -1
- package/dist/selftest/hcs.test.js +105 -8
- package/dist/selftest/health.test.js +71 -0
- package/dist/selftest/heavy-load.test.js +152 -66
- package/dist/selftest/index.js +4 -0
- package/dist/selftest/layout.test.js +8 -0
- package/dist/selftest/live-terminal.test.js +74 -0
- package/dist/selftest/menu-screens.js +171 -56
- package/dist/selftest/menu.test.js +54 -37
- package/dist/selftest/migrate.test.js +14 -12
- package/dist/selftest/network.test.js +20 -1
- package/dist/selftest/powershell-variables.test.d.ts +2 -0
- package/dist/selftest/powershell-variables.test.js +61 -0
- package/dist/selftest/support/engine-golden.js +1 -0
- package/dist/selftest/support/fixtures.d.ts +5 -0
- package/dist/selftest/support/web-preview.d.ts +1 -0
- package/dist/selftest/support/web-preview.js +856 -0
- package/dist/selftest/update-service.test.js +15 -8
- package/dist/selftest/vpn.test.js +86 -2
- package/dist/selftest/web.test.js +58 -11
- package/dist/shared/channel/run-dir.d.ts +2 -0
- package/dist/shared/channel/run-dir.js +3 -0
- package/dist/shared/channel/schema.d.ts +7 -0
- package/dist/shared/channel/schema.js +9 -1
- package/dist/shared/channel/secrets.d.ts +1 -0
- package/dist/shared/channel/secrets.js +3 -0
- package/dist/shared/github/index.d.ts +1 -0
- package/dist/shared/github/index.js +2 -0
- package/dist/shared/github/token.d.ts +29 -0
- package/dist/shared/github/token.js +89 -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/policy.d.ts +38 -12
- package/dist/shared/memory/policy.js +58 -19
- package/dist/shared/network/firewall-rules.d.ts +8 -0
- package/dist/shared/network/firewall-rules.js +28 -0
- package/dist/shared/pty/ctrl-c.d.ts +18 -0
- package/dist/shared/pty/ctrl-c.js +36 -0
- package/dist/shared/pty/index.d.ts +1 -0
- package/dist/shared/pty/index.js +1 -0
- package/dist/shared/pty/terminal.d.ts +2 -0
- package/dist/shared/pty/terminal.js +21 -2
- package/dist/shared/vpn/kill-switch.d.ts +13 -2
- package/dist/shared/vpn/kill-switch.js +88 -8
- package/dist/shared/web/protocol.d.ts +0 -4
- package/dist/sync/watcher.js +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/{main-T5DMCHYV.css → main-6JF3ASB5.css} +1 -1
- package/dist/web/main-Z645W45E.js +219 -0
- package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/MemoryStatus.cs +128 -107
- package/native/scripts/memory-status.ps1 +10 -2
- package/package.json +10 -3
- package/sandbox/csharp/MemGov.cs +668 -398
- package/sandbox/csharp/PtyHost.cs +104 -15
- package/sandbox/csharp/SandboxTools/CpuCap.cs +50 -1
- package/sandbox/csharp/SandboxTools/Program.cs +1 -1
- package/sandbox/scripts/new-shortcuts.ps1 +3 -1
- package/sandbox/scripts/repair-wmi-services.ps1 +26 -0
- package/schemas/config.schema.json +25 -7
- package/schemas/sandbox-config.schema.json +7 -3
- package/dist/cli/commands/accounts.d.ts +0 -2
- package/dist/cli/commands/accounts.js +0 -96
- package/dist/cli/commands/data/clean.d.ts +0 -3
- package/dist/cli/commands/data/clean.js +0 -60
- package/dist/cli/commands/data/index.d.ts +0 -4
- package/dist/cli/commands/data/index.js +0 -62
- package/dist/cli/commands/maintenance/clean.d.ts +0 -2
- package/dist/cli/commands/maintenance/clean.js +0 -24
- package/dist/cli/commands/maintenance/config-share.d.ts +0 -12
- package/dist/cli/commands/maintenance/config-share.js +0 -80
- package/dist/cli/commands/projects/projects.d.ts +0 -2
- package/dist/cli/commands/projects/projects.js +0 -162
- package/dist/cli/commands/storage/contained.d.ts +0 -3
- package/dist/cli/commands/storage/contained.js +0 -179
- package/dist/cli/commands/storage/storage.d.ts +0 -8
- package/dist/cli/commands/storage/storage.js +0 -179
- package/dist/cli/menu/add-agent.d.ts +0 -47
- package/dist/cli/menu/add-agent.js +0 -168
- package/dist/cli/menu/apps.d.ts +0 -69
- package/dist/cli/menu/apps.js +0 -214
- package/dist/cli/menu/data-folder.d.ts +0 -18
- package/dist/cli/menu/data-folder.js +0 -99
- package/dist/cli/menu/share-settings.d.ts +0 -21
- package/dist/cli/menu/share-settings.js +0 -112
- package/dist/cli/menu/vpn-login.d.ts +0 -16
- package/dist/cli/menu/vpn-login.js +0 -74
- package/dist/web/main-NRRS5O6Z.js +0 -157
- /package/dist/cli/{commands/data/collect.d.ts → services/data-collect.d.ts} +0 -0
- /package/dist/cli/{commands/data/remove.d.ts → services/data-remove.d.ts} +0 -0
|
@@ -9,7 +9,7 @@ import { fail, UserError } from '../../../core/util/index.js';
|
|
|
9
9
|
import { selfTestRunning } from '../../test-guard.js';
|
|
10
10
|
import { checkHcs, realHcsProbes } from './check.js';
|
|
11
11
|
import { buildDocuments, HCS_OWNER } from './documents.js';
|
|
12
|
-
import { HcsHelper, hcsHostExe } from './helper.js';
|
|
12
|
+
import { HcsHelper, hcsHostExe, isMySystemId, userSystemPrefix } from './helper.js';
|
|
13
13
|
import { findImage } from './images.js';
|
|
14
14
|
export const HCS_CAPABILITIES = {
|
|
15
15
|
desktop: false,
|
|
@@ -76,7 +76,7 @@ export class HcsEngine {
|
|
|
76
76
|
const docs = buildDocuments({
|
|
77
77
|
launch,
|
|
78
78
|
image: this.image,
|
|
79
|
-
id:
|
|
79
|
+
id: userSystemPrefix() + slug,
|
|
80
80
|
scratchVhdx,
|
|
81
81
|
hostCpus: (this.deps.hostCpus ?? (() => os.cpus().length))(),
|
|
82
82
|
});
|
|
@@ -147,10 +147,12 @@ export class HcsEngine {
|
|
|
147
147
|
throw e; // a system that is already gone is fine
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
-
/** Ours: the compute systems the helper lists with our owner name. */
|
|
150
|
+
/** Ours: the compute systems the helper lists with our owner name and this Windows user's id prefix. */
|
|
151
151
|
ours(helper) {
|
|
152
152
|
const parsed = SystemListSchema.safeParse(JSON.parse(helper.enumerate().json || '[]'));
|
|
153
|
-
return parsed.success
|
|
153
|
+
return parsed.success
|
|
154
|
+
? parsed.data.filter((s) => s.Owner === HCS_OWNER && isMySystemId(s.Id)).map((s) => s.Id)
|
|
155
|
+
: [];
|
|
154
156
|
}
|
|
155
157
|
running() {
|
|
156
158
|
try {
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
/** Whether an id has the shape of the ones this tool gives its compute systems. */
|
|
3
|
+
export declare function isOurSystemId(id: string): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* The start of the ids this Windows user's sandboxes get: the Owner is the same for everyone, so on a PC shared with
|
|
6
|
+
* other users this tag is what keeps one user from listing or stopping another's compute systems.
|
|
7
|
+
*/
|
|
8
|
+
export declare function userSystemPrefix(user?: string, domain?: string): string;
|
|
9
|
+
/** One of this tool's ids, given by this Windows user. */
|
|
10
|
+
export declare function isMySystemId(id: string): boolean;
|
|
2
11
|
export declare const HcsReplySchema: z.ZodObject<{
|
|
3
12
|
ok: z.ZodBoolean;
|
|
4
13
|
hresult: z.ZodString;
|
|
@@ -16,6 +25,8 @@ export type HelperRunner = (exe: string, args: readonly string[], timeoutMs: num
|
|
|
16
25
|
export declare const spawnRunner: HelperRunner;
|
|
17
26
|
/** Compiles the helper's sources to `exe` with the .NET Framework csc (C# 5). False when it cannot be built. */
|
|
18
27
|
export declare function buildHcsHostTo(exe: string): boolean;
|
|
28
|
+
/** HcsHost.exe when it was built already (never builds it): a PC that never used HCS has none. */
|
|
29
|
+
export declare function builtHcsHostExe(storageRoot: string): string | null;
|
|
19
30
|
/**
|
|
20
31
|
* <storage>\bin\hcs\<hash of its sources>\HcsHost.exe, built the first time. Its own folder: HostTools' cleanup of old
|
|
21
32
|
* builds only touches twelve-digit hash folders directly in bin. Null when it cannot be built.
|
|
@@ -15,6 +15,26 @@ const MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
|
15
15
|
const CALL_TIMEOUT_MS = 240000;
|
|
16
16
|
/** Only ids this tool gives its compute systems (HcsHost.exe refuses any other, and checks the Owner too). */
|
|
17
17
|
const SAFE_ID = /^agent-sandbox-[A-Za-z0-9._-]{1,100}$/;
|
|
18
|
+
/** Whether an id has the shape of the ones this tool gives its compute systems. */
|
|
19
|
+
export function isOurSystemId(id) {
|
|
20
|
+
return SAFE_ID.test(id);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The start of the ids this Windows user's sandboxes get: the Owner is the same for everyone, so on a PC shared with
|
|
24
|
+
* other users this tag is what keeps one user from listing or stopping another's compute systems.
|
|
25
|
+
*/
|
|
26
|
+
export function userSystemPrefix(user = os.userInfo().username, domain = process.env.USERDOMAIN ?? '') {
|
|
27
|
+
const tag = crypto
|
|
28
|
+
.createHash('sha256')
|
|
29
|
+
.update((domain + '\\' + user).toLowerCase())
|
|
30
|
+
.digest('hex')
|
|
31
|
+
.slice(0, 8);
|
|
32
|
+
return 'agent-sandbox-' + tag + '-';
|
|
33
|
+
}
|
|
34
|
+
/** One of this tool's ids, given by this Windows user. */
|
|
35
|
+
export function isMySystemId(id) {
|
|
36
|
+
return isOurSystemId(id) && id.startsWith(userSystemPrefix());
|
|
37
|
+
}
|
|
18
38
|
export const HcsReplySchema = z.object({
|
|
19
39
|
ok: z.boolean(),
|
|
20
40
|
hresult: z.string().max(16),
|
|
@@ -51,12 +71,22 @@ export function buildHcsHostTo(exe) {
|
|
|
51
71
|
const r = spawnSync(SYSTEM.csc(), ['/nologo', '/optimize', '/target:exe', '/r:System.Web.Extensions.dll', '/out:' + exe, ...sources], { windowsHide: true, timeout: 120000 });
|
|
52
72
|
return r.status === 0 && fs.existsSync(exe);
|
|
53
73
|
}
|
|
74
|
+
/** HcsHost.exe when it was built already (never builds it): a PC that never used HCS has none. */
|
|
75
|
+
export function builtHcsHostExe(storageRoot) {
|
|
76
|
+
const exe = hcsHostPath(storageRoot);
|
|
77
|
+
return exe && fs.existsSync(exe) ? exe : null;
|
|
78
|
+
}
|
|
54
79
|
/**
|
|
55
80
|
* <storage>\bin\hcs\<hash of its sources>\HcsHost.exe, built the first time. Its own folder: HostTools' cleanup of old
|
|
56
81
|
* builds only touches twelve-digit hash folders directly in bin. Null when it cannot be built.
|
|
57
82
|
*/
|
|
58
83
|
export function hcsHostExe(storageRoot) {
|
|
59
84
|
refuseInSelfTest('build HcsHost.exe');
|
|
85
|
+
const exe = hcsHostPath(storageRoot);
|
|
86
|
+
return exe && (fs.existsSync(exe) || buildHcsHostTo(exe)) ? exe : null;
|
|
87
|
+
}
|
|
88
|
+
/** Where HcsHost.exe is (or would be built): named by the hash of its sources. Null without the sources. */
|
|
89
|
+
function hcsHostPath(storageRoot) {
|
|
60
90
|
const dir = nativeFile('csharp', 'HcsHost');
|
|
61
91
|
const hash = crypto.createHash('sha256');
|
|
62
92
|
try {
|
|
@@ -69,8 +99,7 @@ export function hcsHostExe(storageRoot) {
|
|
|
69
99
|
catch {
|
|
70
100
|
return null; // no sources
|
|
71
101
|
}
|
|
72
|
-
|
|
73
|
-
return fs.existsSync(exe) || buildHcsHostTo(exe) ? exe : null;
|
|
102
|
+
return path.join(storageRoot, 'bin', 'hcs', hash.digest('hex').slice(0, 12), 'HcsHost.exe');
|
|
74
103
|
}
|
|
75
104
|
/** A path argument: absolute, not an option. Nothing else reaches the helper's command line. */
|
|
76
105
|
function pathArg(value, what) {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type HcsProbes } from './check.js';
|
|
2
|
+
import { HcsHelper } from './helper.js';
|
|
3
|
+
export interface LeftoverSystem {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
/** Container or VirtualMachine (the utility VM). */
|
|
6
|
+
readonly kind: string;
|
|
7
|
+
readonly state: string;
|
|
8
|
+
/** When it was created (ISO text), when HCS says. */
|
|
9
|
+
readonly since: string | null;
|
|
10
|
+
}
|
|
11
|
+
export type LeftoverLook = {
|
|
12
|
+
readonly available: true;
|
|
13
|
+
readonly systems: readonly LeftoverSystem[];
|
|
14
|
+
} | {
|
|
15
|
+
readonly available: false;
|
|
16
|
+
readonly reason: string;
|
|
17
|
+
};
|
|
18
|
+
export interface LeftoverOutcome {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly stopped: boolean;
|
|
21
|
+
/** Why not, in plain words. */
|
|
22
|
+
readonly problem?: string;
|
|
23
|
+
}
|
|
24
|
+
/** What the look runs on: replaceable by tests (probes of a pretend PC, a fake helper). */
|
|
25
|
+
export interface LeftoverDeps {
|
|
26
|
+
readonly probes: HcsProbes;
|
|
27
|
+
/** The helper, or null when HcsHost.exe was never built (then nothing of ours can be running). Never builds it. */
|
|
28
|
+
helper(storageRoot: string): HcsHelper | null;
|
|
29
|
+
}
|
|
30
|
+
export declare const LEFTOVER_DEPS: {
|
|
31
|
+
current: LeftoverDeps | null;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The compute systems of ours that exist now. Never builds HcsHost.exe: a PC that never used HCS has none, and
|
|
35
|
+
* nothing of ours can be running there.
|
|
36
|
+
*/
|
|
37
|
+
export declare function lookForLeftovers(storageRoot: string): LeftoverLook;
|
|
38
|
+
/**
|
|
39
|
+
* Stops the named systems, containers before the utility VM that hosts them. Each id is looked up again first: one that
|
|
40
|
+
* is not one of ours (or is gone) never reaches the helper; the helper checks the Owner with HCS itself once more.
|
|
41
|
+
*/
|
|
42
|
+
export declare function stopLeftovers(storageRoot: string, ids: readonly string[]): LeftoverOutcome[];
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// HCS compute systems this tool left running (a crashed command: they are created to outlive their handle). Only
|
|
2
|
+
// systems with our id prefix AND our Owner are ever listed or stopped; HcsHost.exe checks both again before it
|
|
3
|
+
// terminates one. Nothing here runs unless the PC has the Host Compute Service.
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { errMsg, fail } from '../../../core/util/index.js';
|
|
7
|
+
import { realHcsProbes, system32Dir } from './check.js';
|
|
8
|
+
import { HCS_OWNER } from './documents.js';
|
|
9
|
+
import { HcsHelper, builtHcsHostExe, isMySystemId } from './helper.js';
|
|
10
|
+
export const LEFTOVER_DEPS = { current: null };
|
|
11
|
+
function realDeps() {
|
|
12
|
+
return {
|
|
13
|
+
probes: realHcsProbes(),
|
|
14
|
+
helper: (root) => {
|
|
15
|
+
const exe = builtHcsHostExe(root);
|
|
16
|
+
return exe ? new HcsHelper(() => exe) : null;
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const SystemListSchema = z.array(z
|
|
21
|
+
.object({
|
|
22
|
+
Id: z.string().max(300),
|
|
23
|
+
SystemType: z.string().max(100).optional(),
|
|
24
|
+
Owner: z.string().max(300).optional(),
|
|
25
|
+
State: z.string().max(100).optional(),
|
|
26
|
+
CreateTimestamp: z.string().max(100).optional(),
|
|
27
|
+
})
|
|
28
|
+
.passthrough());
|
|
29
|
+
/** The reason HCS cannot be there at all, or null. Looks only (service list, one file). */
|
|
30
|
+
function noHcsReason(p) {
|
|
31
|
+
if (!p.isWindows())
|
|
32
|
+
return 'HCS only exists on Windows.';
|
|
33
|
+
if (!p.fileExists(path.join(system32Dir(), 'computecore.dll')) || p.serviceInstalled('vmcompute') === false)
|
|
34
|
+
return 'The Host Compute Service is not on this PC.';
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
/** Ours only: the id has our prefix and the system names us as its Owner. Anything else is never shown or touched. */
|
|
38
|
+
function ownedSystems(helper) {
|
|
39
|
+
let list;
|
|
40
|
+
try {
|
|
41
|
+
list = JSON.parse(helper.enumerate().json || '[]');
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
if (e instanceof SyntaxError)
|
|
45
|
+
return fail('The Host Compute Service answered with something that is not JSON.');
|
|
46
|
+
throw e;
|
|
47
|
+
}
|
|
48
|
+
const parsed = SystemListSchema.safeParse(list);
|
|
49
|
+
if (!parsed.success)
|
|
50
|
+
return [];
|
|
51
|
+
return parsed.data
|
|
52
|
+
.filter((s) => s.Owner === HCS_OWNER && isMySystemId(s.Id))
|
|
53
|
+
.map((s) => ({
|
|
54
|
+
id: s.Id,
|
|
55
|
+
kind: s.SystemType ?? 'unknown',
|
|
56
|
+
state: s.State ?? 'unknown',
|
|
57
|
+
since: s.CreateTimestamp && !Number.isNaN(Date.parse(s.CreateTimestamp)) ? s.CreateTimestamp : null,
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The compute systems of ours that exist now. Never builds HcsHost.exe: a PC that never used HCS has none, and
|
|
62
|
+
* nothing of ours can be running there.
|
|
63
|
+
*/
|
|
64
|
+
export function lookForLeftovers(storageRoot) {
|
|
65
|
+
const deps = LEFTOVER_DEPS.current ?? realDeps();
|
|
66
|
+
const none = noHcsReason(deps.probes);
|
|
67
|
+
if (none)
|
|
68
|
+
return { available: false, reason: none };
|
|
69
|
+
try {
|
|
70
|
+
const helper = deps.helper(storageRoot);
|
|
71
|
+
if (!helper)
|
|
72
|
+
return { available: true, systems: [] };
|
|
73
|
+
return { available: true, systems: ownedSystems(helper) };
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
return { available: false, reason: 'The Host Compute Service could not be asked: ' + errMsg(e) };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Stops the named systems, containers before the utility VM that hosts them. Each id is looked up again first: one that
|
|
81
|
+
* is not one of ours (or is gone) never reaches the helper; the helper checks the Owner with HCS itself once more.
|
|
82
|
+
*/
|
|
83
|
+
export function stopLeftovers(storageRoot, ids) {
|
|
84
|
+
const deps = LEFTOVER_DEPS.current ?? realDeps();
|
|
85
|
+
const helper = deps.helper(storageRoot);
|
|
86
|
+
if (!helper)
|
|
87
|
+
return ids.map((id) => ({ id, stopped: false, problem: 'HcsHost.exe is not available.' }));
|
|
88
|
+
const now = ownedSystems(helper);
|
|
89
|
+
const wanted = [...ids].sort((a, b) => Number(b.endsWith('-c')) - Number(a.endsWith('-c')));
|
|
90
|
+
return wanted.map((id) => {
|
|
91
|
+
if (!now.some((s) => s.id === id))
|
|
92
|
+
return { id, stopped: false, problem: 'it is gone, or it is not one of ours' };
|
|
93
|
+
try {
|
|
94
|
+
helper.terminate(id);
|
|
95
|
+
return { id, stopped: true };
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
return { id, stopped: false, problem: errMsg(e) };
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -5,3 +5,4 @@ export { WindowsSandboxEngine } from './windows-sandbox/engine.js';
|
|
|
5
5
|
export { wsbText } from './windows-sandbox/wsb-text.js';
|
|
6
6
|
export { HcsEngine, HCS_CAPABILITIES, hcsStartEnabled } from './hcs/engine.js';
|
|
7
7
|
export { checkHcs, realHcsProbes, type HcsProbes } from './hcs/check.js';
|
|
8
|
+
export { lookForLeftovers, stopLeftovers, LEFTOVER_DEPS, type LeftoverDeps, type LeftoverLook, type LeftoverOutcome, type LeftoverSystem, } from './hcs/leftovers.js';
|
|
@@ -3,3 +3,4 @@ export { WindowsSandboxEngine } from './windows-sandbox/engine.js';
|
|
|
3
3
|
export { wsbText } from './windows-sandbox/wsb-text.js';
|
|
4
4
|
export { HcsEngine, HCS_CAPABILITIES, hcsStartEnabled } from './hcs/engine.js';
|
|
5
5
|
export { checkHcs, realHcsProbes } from './hcs/check.js';
|
|
6
|
+
export { lookForLeftovers, stopLeftovers, LEFTOVER_DEPS, } from './hcs/leftovers.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BootWant } from '../../core/local-config/index.js';
|
|
2
|
+
import type { Config } from '../../core/types.js';
|
|
3
|
+
import { GithubToken } from './token.js';
|
|
4
|
+
/** github.shareToken is on and git access is "full" for every project this sandbox boots with. */
|
|
5
|
+
export declare function githubTokenAllowed(config: Config, others?: readonly BootWant[]): boolean;
|
|
6
|
+
export interface GithubDelivery {
|
|
7
|
+
/** The token for the one-shot secrets file, when it is handed over. */
|
|
8
|
+
readonly token?: string;
|
|
9
|
+
/** What to tell you about it (never the token). */
|
|
10
|
+
readonly notes: readonly string[];
|
|
11
|
+
}
|
|
12
|
+
/** What this launch hands over. Never throws: a token that cannot be read is a note, and the launch goes on. */
|
|
13
|
+
export declare function prepareGithubToken(config: Config, storageRoot: string, store?: GithubToken, others?: readonly BootWant[]): GithubDelivery;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { GithubToken } from './token.js';
|
|
2
|
+
/** The first project (the launch's own, then the others at boot) whose git access is not "full", or null. */
|
|
3
|
+
function stricterProject(config, others) {
|
|
4
|
+
if (config.provisioning.git.access !== 'full')
|
|
5
|
+
return { name: '', access: config.provisioning.git.access };
|
|
6
|
+
const other = others.find((o) => o.config.provisioning.git.access !== 'full');
|
|
7
|
+
return other ? { name: other.name, access: other.config.provisioning.git.access } : null;
|
|
8
|
+
}
|
|
9
|
+
/** github.shareToken is on and git access is "full" for every project this sandbox boots with. */
|
|
10
|
+
export function githubTokenAllowed(config, others = []) {
|
|
11
|
+
return config.github.shareToken && stricterProject(config, others) === null;
|
|
12
|
+
}
|
|
13
|
+
/** What this launch hands over. Never throws: a token that cannot be read is a note, and the launch goes on. */
|
|
14
|
+
export function prepareGithubToken(config, storageRoot, store, others = []) {
|
|
15
|
+
if (!config.github.shareToken)
|
|
16
|
+
return { notes: [] };
|
|
17
|
+
const stricter = stricterProject(config, others);
|
|
18
|
+
if (stricter)
|
|
19
|
+
return {
|
|
20
|
+
notes: [
|
|
21
|
+
'github.shareToken is on, but git access is "' +
|
|
22
|
+
stricter.access +
|
|
23
|
+
'"' +
|
|
24
|
+
(stricter.name ? ' for ' + stricter.name + ' (also in this sandbox)' : ' in this sandbox') +
|
|
25
|
+
', so your GitHub token was not handed over (it needs provisioning.git.access "full").',
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
const tokens = store ?? new GithubToken(storageRoot);
|
|
29
|
+
let token;
|
|
30
|
+
try {
|
|
31
|
+
token = tokens.read();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// The reason may come from DPAPI or a damaged file; a fixed text is enough (and can never hold the token).
|
|
35
|
+
return {
|
|
36
|
+
notes: [
|
|
37
|
+
'Your saved GitHub token could not be read (only the Windows user that saved it can), so it was not handed over. Save it again: sandbox github login',
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (!token)
|
|
42
|
+
return {
|
|
43
|
+
notes: ['github.shareToken is on, but no GitHub token is saved. Save one: sandbox github login'],
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
token,
|
|
47
|
+
notes: [
|
|
48
|
+
'Your GitHub token is handed to this sandbox (github.shareToken): the agent can read it and use it for whatever it allows.',
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type GithubTokenKind } from '../../shared/github/index.js';
|
|
2
|
+
import { SecretStore } from '../secrets/index.js';
|
|
3
|
+
/** What the status may say about the token: never its value. */
|
|
4
|
+
export type TokenState = 'none' | 'saved' | 'unreadable';
|
|
5
|
+
/** The token's kind, or a refusal that says what to do (the message never holds what was typed). */
|
|
6
|
+
export declare function checkGithubToken(token: string): GithubTokenKind;
|
|
7
|
+
export declare class GithubToken {
|
|
8
|
+
private readonly storageRoot;
|
|
9
|
+
private readonly secrets;
|
|
10
|
+
constructor(storageRoot: string, secrets?: SecretStore);
|
|
11
|
+
private file;
|
|
12
|
+
/** Checks, encrypts and saves the token (replacing an earlier one). Returns its kind. */
|
|
13
|
+
save(token: string): GithubTokenKind;
|
|
14
|
+
/** The token, decrypted. Only the launch calls this; throws when this Windows user cannot open it. */
|
|
15
|
+
read(): string | null;
|
|
16
|
+
/** Whether a token is saved and can be decrypted by this Windows user (the value is never returned). */
|
|
17
|
+
state(): TokenState;
|
|
18
|
+
savedAt(): string | null;
|
|
19
|
+
kind(): GithubTokenKind | null;
|
|
20
|
+
remove(): boolean;
|
|
21
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// The GitHub token you saved for sandboxes (github.shareToken), kept encrypted with Windows DPAPI in host-only storage
|
|
2
|
+
// (<storage>\github\token.json). It is read only when a sandbox starts, to hand to the sandbox in a one-shot file, and
|
|
3
|
+
// is never printed, logged or returned to a surface: they only learn its state, kind and when it was saved.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { JsonFile } from '../../shared/io/index.js';
|
|
8
|
+
import { GITHUB_TOKEN_KINDS, GithubTokenSchema, githubTokenKind, githubTokenProblem, } from '../../shared/github/index.js';
|
|
9
|
+
import { fail } from '../../core/util/index.js';
|
|
10
|
+
import { SecretStore } from '../secrets/index.js';
|
|
11
|
+
const SavedSchema = z.object({
|
|
12
|
+
savedAt: z.string().max(64),
|
|
13
|
+
kind: z.enum(GITHUB_TOKEN_KINDS),
|
|
14
|
+
sealed: z.string().max(10000),
|
|
15
|
+
});
|
|
16
|
+
/** The token's kind, or a refusal that says what to do (the message never holds what was typed). */
|
|
17
|
+
export function checkGithubToken(token) {
|
|
18
|
+
const problem = githubTokenProblem(token);
|
|
19
|
+
if (problem)
|
|
20
|
+
fail(problem.message, problem.fix);
|
|
21
|
+
return githubTokenKind(token) ?? fail('That is not a GitHub personal access token.', 'Run: sandbox github login');
|
|
22
|
+
}
|
|
23
|
+
export class GithubToken {
|
|
24
|
+
storageRoot;
|
|
25
|
+
secrets;
|
|
26
|
+
constructor(storageRoot, secrets = SecretStore.standard()) {
|
|
27
|
+
this.storageRoot = storageRoot;
|
|
28
|
+
this.secrets = secrets;
|
|
29
|
+
}
|
|
30
|
+
file() {
|
|
31
|
+
return new JsonFile(SavedSchema, path.join(this.storageRoot, 'github', 'token.json'));
|
|
32
|
+
}
|
|
33
|
+
/** Checks, encrypts and saves the token (replacing an earlier one). Returns its kind. */
|
|
34
|
+
save(token) {
|
|
35
|
+
const kind = checkGithubToken(token);
|
|
36
|
+
this.file().write({ savedAt: new Date().toISOString(), kind, sealed: this.secrets.sealJson({ token }) });
|
|
37
|
+
return kind;
|
|
38
|
+
}
|
|
39
|
+
/** The token, decrypted. Only the launch calls this; throws when this Windows user cannot open it. */
|
|
40
|
+
read() {
|
|
41
|
+
const saved = this.file().read();
|
|
42
|
+
if (!saved)
|
|
43
|
+
return null;
|
|
44
|
+
const value = this.secrets.openJson(saved.sealed);
|
|
45
|
+
const token = GithubTokenSchema.safeParse(value?.token);
|
|
46
|
+
if (!token.success)
|
|
47
|
+
fail('The saved GitHub token is damaged.', 'Save it again with: sandbox github login');
|
|
48
|
+
return token.data;
|
|
49
|
+
}
|
|
50
|
+
/** Whether a token is saved and can be decrypted by this Windows user (the value is never returned). */
|
|
51
|
+
state() {
|
|
52
|
+
if (!this.file().read())
|
|
53
|
+
return 'none';
|
|
54
|
+
try {
|
|
55
|
+
return this.read() ? 'saved' : 'none';
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return 'unreadable';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
savedAt() {
|
|
62
|
+
return this.file().read()?.savedAt ?? null;
|
|
63
|
+
}
|
|
64
|
+
kind() {
|
|
65
|
+
return this.file().read()?.kind ?? null;
|
|
66
|
+
}
|
|
67
|
+
remove() {
|
|
68
|
+
const file = this.file().file;
|
|
69
|
+
if (!fs.existsSync(file))
|
|
70
|
+
return false;
|
|
71
|
+
fs.rmSync(file, { force: true });
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -9,6 +9,7 @@ import { dirExists, ensureDir, fail, writeJson } from '../../core/util/index.js'
|
|
|
9
9
|
import { engineFor } from '../engines/index.js';
|
|
10
10
|
import { capturePersonalization, stageClaudeProfile } from '../profile/index.js';
|
|
11
11
|
import { clearSecrets, writeSecrets } from '../secrets/index.js';
|
|
12
|
+
import { githubTokenAllowed } from '../github/index.js';
|
|
12
13
|
import { folderAttachment } from '../storage/providers/index.js';
|
|
13
14
|
import { assertTestWritable } from '../test-guard.js';
|
|
14
15
|
import { writeLoadSettings } from '../life/watchdog.js';
|
|
@@ -20,7 +21,7 @@ import { guestSession, logonEnv } from './session-file.js';
|
|
|
20
21
|
import { copyRuntime, stageGitIdentity, stageHooks } from './stage.js';
|
|
21
22
|
import { decideProjectToolchains } from './toolchains.js';
|
|
22
23
|
import { buildLaunchPlan } from './launch-plan.js';
|
|
23
|
-
import { decideMemory, GUEST, memoryNote, resolveMemoryMb } from './wsb-file.js';
|
|
24
|
+
import { cappedMemoryMb, decideMemory, GUEST, memoryNote, residentNote, resolveMemoryMb } from './wsb-file.js';
|
|
24
25
|
const NO_PROFILE = { hostDir: null, staged: [], skipped: [], credentials: false, secretsRemoved: [] };
|
|
25
26
|
/** Moves the staged copy of this PC's Claude login out of the launch folder; its text goes in the one-shot file. */
|
|
26
27
|
function takeStagedCredentials(launchPath) {
|
|
@@ -59,7 +60,9 @@ opened) {
|
|
|
59
60
|
assertTestWritable(dataPath);
|
|
60
61
|
// Checked before anything is written, so a refusal leaves nothing behind.
|
|
61
62
|
const commitAvailableMb = o.commitAvailableMb ?? null;
|
|
62
|
-
const
|
|
63
|
+
const askedMb = Math.max(resolveMemoryMb(config.sandbox, o.freeMemoryBytes, undefined, commitAvailableMb), ...others.map((p) => (typeof p.config.sandbox.memoryMb === 'number' ? p.config.sandbox.memoryMb : 0)));
|
|
64
|
+
// Never more than this PC can keep in RAM for it, whatever was asked.
|
|
65
|
+
const memoryMb = cappedMemoryMb(askedMb, config.sandbox, Math.floor(os.totalmem() / 1048576));
|
|
63
66
|
const launchId = Date.now() + '-' + crypto.randomBytes(3).toString('hex');
|
|
64
67
|
const launchPath = path.join(storageRoot, 'launches', launchId);
|
|
65
68
|
// Whatever must stay secret goes in a one-shot file in the data folder, never into the launch folder (the sandbox
|
|
@@ -104,6 +107,8 @@ opened) {
|
|
|
104
107
|
...(claudeCredentials ? { claudeCredentials } : {}),
|
|
105
108
|
...(o.vpn?.credentials ? { vpn: o.vpn.credentials } : {}),
|
|
106
109
|
...(o.vpn?.wireguardConfig ? { wireguardConfig: o.vpn.wireguardConfig } : {}),
|
|
110
|
+
// Checked again on the configs the sandbox boots with: never when a project in it has git access below "full".
|
|
111
|
+
...(o.githubToken && githubTokenAllowed(config, others) ? { githubToken: o.githubToken } : {}),
|
|
107
112
|
});
|
|
108
113
|
fs.writeFileSync(path.join(launchPath, 'env.cmd'), logonEnv(session.workspace), 'utf8');
|
|
109
114
|
copyRuntime(o.packageRoot, launchPath);
|
|
@@ -127,9 +132,10 @@ opened) {
|
|
|
127
132
|
launchId,
|
|
128
133
|
sessionPath,
|
|
129
134
|
memoryMb,
|
|
130
|
-
memoryNote:
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
memoryNote: residentNote(askedMb, memoryMb) ??
|
|
136
|
+
(config.sandbox.memoryMb === 'auto'
|
|
137
|
+
? memoryNote(decideMemory(config.sandbox, Math.floor((o.freeMemoryBytes ?? os.freemem()) / 1048576), Math.floor(os.totalmem() / 1048576), commitAvailableMb))
|
|
138
|
+
: null),
|
|
133
139
|
bootConfig: config,
|
|
134
140
|
widened: wide.widened,
|
|
135
141
|
mappings,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { generateSandbox } from './generate.js';
|
|
2
2
|
export { decideProjectToolchains } from './toolchains.js';
|
|
3
3
|
export { hostToolsOf, logonEnv } from './session-file.js';
|
|
4
|
-
export {
|
|
4
|
+
export { cappedMemoryMb, decideMemory, GUEST, memoryNote, residentCapMb, residentNote, resolveMemoryMb, } from './wsb-file.js';
|
|
5
5
|
export { GENTLE_NOTE, vgpuSource, vgpuWarning } from './risk-notes.js';
|
|
6
6
|
export { buildLaunchPlan, buildWsb } from './launch-plan.js';
|
|
7
7
|
export { MAX_BOOT_PROJECTS, planBootProjects, shownByRoot, type BootProjects } from './boot-projects.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { generateSandbox } from './generate.js';
|
|
2
2
|
export { decideProjectToolchains } from './toolchains.js';
|
|
3
3
|
export { hostToolsOf, logonEnv } from './session-file.js';
|
|
4
|
-
export {
|
|
4
|
+
export { cappedMemoryMb, decideMemory, GUEST, memoryNote, residentCapMb, residentNote, resolveMemoryMb, } from './wsb-file.js';
|
|
5
5
|
export { GENTLE_NOTE, vgpuSource, vgpuWarning } from './risk-notes.js';
|
|
6
6
|
export { buildLaunchPlan, buildWsb } from './launch-plan.js';
|
|
7
7
|
export { MAX_BOOT_PROJECTS, planBootProjects, shownByRoot } from './boot-projects.js';
|
|
@@ -5,7 +5,6 @@ import { resolveToolchains } from '../../core/detect/index.js';
|
|
|
5
5
|
import { hubName, PROJECTS_GUEST, substDrives } from '../../shared/paths/index.js';
|
|
6
6
|
import { hostClaudeDir } from '../profile/index.js';
|
|
7
7
|
import { databaseCatalog } from './database-plan.js';
|
|
8
|
-
import { agentMemoryLimitMb } from './wsb-file.js';
|
|
9
8
|
import { effectiveCpuLimit } from '../../shared/memory/index.js';
|
|
10
9
|
import { loadProfileOf } from './risk-notes.js';
|
|
11
10
|
/** What was found on this PC to copy instead of download. */
|
|
@@ -65,7 +64,7 @@ export function guestSession(o, parts) {
|
|
|
65
64
|
cpuLimitPercent: effectiveCpuLimit(config.sandbox.cpuLimit, loadProfileOf(config)),
|
|
66
65
|
loadProfile: loadProfileOf(config),
|
|
67
66
|
pageFileMb: config.sandbox.pageFileMb,
|
|
68
|
-
|
|
67
|
+
agentBackstop: config.sandbox.agentMemoryHeadroomMb,
|
|
69
68
|
pressureResponse: config.sandbox.pressureResponse,
|
|
70
69
|
apps: config.apps,
|
|
71
70
|
auditProcesses: config.audit.enabled && config.audit.processes,
|
|
@@ -6,7 +6,7 @@ export declare const GUEST: {
|
|
|
6
6
|
readonly mirror: "C:\\Mirror";
|
|
7
7
|
};
|
|
8
8
|
type SandboxSettings = Config['sandbox'];
|
|
9
|
-
export type MemoryLimit = 'free' | 'share' | 'max' | 'commit';
|
|
9
|
+
export type MemoryLimit = 'free' | 'share' | 'max' | 'commit' | 'resident';
|
|
10
10
|
export interface MemoryDecision {
|
|
11
11
|
readonly mb: number;
|
|
12
12
|
/** What set the size. */
|
|
@@ -17,18 +17,22 @@ export interface MemoryDecision {
|
|
|
17
17
|
readonly freeMb: number;
|
|
18
18
|
readonly commitAvailableMb: number | null;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* The most memory this PC can keep in RAM for the sandbox (pure): its RAM minus max(reserveHostMb, 25 %), and 2 GB
|
|
22
|
+
* more with vGPU. Windows Sandbox's memory is this PC's memory taken as needed; beyond what the PC can keep resident it
|
|
23
|
+
* is paged to this PC's disk while the sandbox pages to its own, and that is what can freeze the whole PC.
|
|
24
|
+
*/
|
|
25
|
+
export declare function residentCapMb(sandbox: SandboxSettings, totalMb: number): number;
|
|
26
|
+
/** A fixed memoryMb above what this PC can keep resident is lowered to that (never below 4 GB). */
|
|
27
|
+
export declare function cappedMemoryMb(memoryMb: number, sandbox: SandboxSettings, totalMb: number): number;
|
|
28
|
+
/** The launch note when a fixed memoryMb was lowered. */
|
|
29
|
+
export declare function residentNote(asked: number, given: number): string | null;
|
|
20
30
|
/**
|
|
21
31
|
* "auto" (pure): free RAM minus the reserve, never more than maxMemoryMb or maxMemoryPercent of the PC's total RAM,
|
|
22
32
|
* and, when the PC's available commit (RAM plus paging file, what the sandbox's memory is charged against) is known,
|
|
23
33
|
* never more than that minus the reserve either.
|
|
24
34
|
*/
|
|
25
35
|
export declare function decideMemory(sandbox: SandboxSettings, freeMb: number, totalMb: number, commitAvailableMb: number | null): MemoryDecision;
|
|
26
|
-
/**
|
|
27
|
-
* The most memory everything the agents start may use together (pure): the sandbox's memory minus the headroom
|
|
28
|
-
* ("auto" =
|
|
29
|
-
* the larger of 2048 MB and 10 %), at least half of it so a tiny sandbox still works; null = no limit ("none").
|
|
30
|
-
*/
|
|
31
|
-
export declare function agentMemoryLimitMb(memoryMb: number, headroom: SandboxSettings['agentMemoryHeadroomMb']): number | null;
|
|
32
36
|
/** A plain-words note when the commit made the sandbox's memory much smaller (2 GB or more) than free RAM allowed. */
|
|
33
37
|
export declare function memoryNote(decision: MemoryDecision): string | null;
|
|
34
38
|
/** "auto": see decideMemory. commitAvailableMb: from HostTools memory-status (omitted = not checked). */
|
|
@@ -11,6 +11,34 @@ export const GUEST = {
|
|
|
11
11
|
const MB = 1048576;
|
|
12
12
|
const MIN_AUTO_MEMORY_MB = 4096;
|
|
13
13
|
const roundDown = (mb) => Math.floor(mb / 1024) * 1024;
|
|
14
|
+
/** GPU-PV (vGPU) needs room of its own on this PC: memory the graphics driver pins for the sandbox. */
|
|
15
|
+
const VGPU_RESERVE_MB = 2048;
|
|
16
|
+
/**
|
|
17
|
+
* The most memory this PC can keep in RAM for the sandbox (pure): its RAM minus max(reserveHostMb, 25 %), and 2 GB
|
|
18
|
+
* more with vGPU. Windows Sandbox's memory is this PC's memory taken as needed; beyond what the PC can keep resident it
|
|
19
|
+
* is paged to this PC's disk while the sandbox pages to its own, and that is what can freeze the whole PC.
|
|
20
|
+
*/
|
|
21
|
+
export function residentCapMb(sandbox, totalMb) {
|
|
22
|
+
const keep = Math.max(sandbox.reserveHostMb, Math.floor(totalMb / 4)) + (sandbox.vGpu ? VGPU_RESERVE_MB : 0);
|
|
23
|
+
return roundDown(totalMb - keep);
|
|
24
|
+
}
|
|
25
|
+
/** A fixed memoryMb above what this PC can keep resident is lowered to that (never below 4 GB). */
|
|
26
|
+
export function cappedMemoryMb(memoryMb, sandbox, totalMb) {
|
|
27
|
+
const cap = residentCapMb(sandbox, totalMb);
|
|
28
|
+
return cap >= MIN_AUTO_MEMORY_MB && memoryMb > cap ? cap : memoryMb;
|
|
29
|
+
}
|
|
30
|
+
/** The launch note when a fixed memoryMb was lowered. */
|
|
31
|
+
export function residentNote(asked, given) {
|
|
32
|
+
if (given >= asked)
|
|
33
|
+
return null;
|
|
34
|
+
return ('sandbox.memoryMb asks for ' +
|
|
35
|
+
asked +
|
|
36
|
+
' MB; this PC can keep ' +
|
|
37
|
+
given +
|
|
38
|
+
' MB in RAM for the sandbox (its RAM minus what it needs itself), so the sandbox gets ' +
|
|
39
|
+
given +
|
|
40
|
+
" MB. More would be paged to this PC's disk, which can freeze the whole PC.");
|
|
41
|
+
}
|
|
14
42
|
/**
|
|
15
43
|
* "auto" (pure): free RAM minus the reserve, never more than maxMemoryMb or maxMemoryPercent of the PC's total RAM,
|
|
16
44
|
* and, when the PC's available commit (RAM plus paging file, what the sandbox's memory is charged against) is known,
|
|
@@ -25,22 +53,12 @@ export function decideMemory(sandbox, freeMb, totalMb, commitAvailableMb) {
|
|
|
25
53
|
['share', shareMb],
|
|
26
54
|
['free', byFree],
|
|
27
55
|
['commit', byCommit],
|
|
56
|
+
['resident', residentCapMb(sandbox, totalMb)],
|
|
28
57
|
];
|
|
29
58
|
const lowest = candidates.reduce((a, b) => (b[1] < a[1] ? b : a));
|
|
30
|
-
const withoutCommit = roundDown(Math.min(sandbox.maxMemoryMb, shareMb, byFree));
|
|
59
|
+
const withoutCommit = roundDown(Math.min(sandbox.maxMemoryMb, shareMb, byFree, residentCapMb(sandbox, totalMb)));
|
|
31
60
|
return { mb: roundDown(lowest[1]), limitedBy: lowest[0], withoutCommitMb: withoutCommit, freeMb, commitAvailableMb };
|
|
32
61
|
}
|
|
33
|
-
/**
|
|
34
|
-
* The most memory everything the agents start may use together (pure): the sandbox's memory minus the headroom
|
|
35
|
-
* ("auto" =
|
|
36
|
-
* the larger of 2048 MB and 10 %), at least half of it so a tiny sandbox still works; null = no limit ("none").
|
|
37
|
-
*/
|
|
38
|
-
export function agentMemoryLimitMb(memoryMb, headroom) {
|
|
39
|
-
if (headroom === 'none')
|
|
40
|
-
return null;
|
|
41
|
-
const keep = headroom === 'auto' ? Math.max(2048, Math.floor(memoryMb / 10)) : headroom;
|
|
42
|
-
return Math.max(Math.floor(memoryMb / 2), memoryMb - keep);
|
|
43
|
-
}
|
|
44
62
|
/** A plain-words note when the commit made the sandbox's memory much smaller (2 GB or more) than free RAM allowed. */
|
|
45
63
|
export function memoryNote(decision) {
|
|
46
64
|
if (decision.limitedBy !== 'commit' || decision.withoutCommitMb - decision.mb < 2048)
|
|
@@ -10,6 +10,7 @@ export declare const HostMemorySchema: z.ZodObject<{
|
|
|
10
10
|
fixed: "fixed";
|
|
11
11
|
}>>;
|
|
12
12
|
pageFileMaxMb: z.ZodCatch<z.ZodNumber>;
|
|
13
|
+
diskQueue: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
13
14
|
}, z.core.$strip>;
|
|
14
15
|
export type HostMemory = z.output<typeof HostMemorySchema>;
|
|
15
16
|
/** The parsed answer of memory-status, or null (not Windows, unreadable, nonsense). */
|