windows-development-sandbox 1.10.0 → 1.11.4
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 +5 -1
- 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 +194 -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 +959 -4
- package/dist/app/features/defs.js +151 -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 +297 -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 +220 -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 +224 -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 +21 -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 -89
- package/dist/cli/commands/web.d.ts +2 -1
- package/dist/cli/commands/web.js +14 -78
- 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 +2 -1
- package/dist/core/schema/config.d.ts +5 -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/types.d.ts +6 -3
- package/dist/guest/boot/boot.js +8 -0
- 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 +46 -18
- package/dist/guest/governor/decide.js +136 -82
- package/dist/guest/governor/governor.d.ts +2 -38
- package/dist/guest/governor/governor.js +33 -113
- package/dist/guest/governor/index.d.ts +1 -0
- package/dist/guest/governor/index.js +1 -0
- package/dist/guest/network/firewall.d.ts +2 -0
- package/dist/guest/network/firewall.js +3 -3
- package/dist/guest/sessions/viewer.js +5 -1
- 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 +11 -1
- 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 +35 -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 +18 -0
- package/dist/host/life/host-memory.js +66 -0
- package/dist/host/life/watchdog.d.ts +9 -0
- package/dist/host/life/watchdog.js +17 -1
- 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 +30 -6
- package/dist/runtime/daemon.js +337 -177
- package/dist/runtime/db.js +30 -6
- package/dist/runtime/main.js +389 -77
- package/dist/runtime/netproxy.js +30 -6
- package/dist/runtime/view.js +495 -146
- package/dist/selftest/app.test.js +632 -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/hcs.test.js +105 -8
- package/dist/selftest/health.test.js +71 -0
- package/dist/selftest/heavy-load.test.js +132 -72
- 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/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 +4 -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/memory/policy.d.ts +33 -12
- package/dist/shared/memory/policy.js +50 -19
- 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.d.ts +2 -0
- package/dist/sync/watcher.js +8 -3
- package/dist/web/index.html +2 -2
- package/dist/web/{main-T5DMCHYV.css → main-MU5XWZMS.css} +1 -1
- package/dist/web/main-SLQNJRXD.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 +58 -2
- package/native/csharp/HostTools/Program.cs +7 -1
- package/package.json +10 -3
- package/sandbox/csharp/MemGov.cs +240 -38
- 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/repair-wmi-services.ps1 +26 -0
- package/schemas/config.schema.json +19 -5
- 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
|
@@ -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)
|
|
@@ -17,6 +17,24 @@ export type HostMemory = z.output<typeof HostMemorySchema>;
|
|
|
17
17
|
export declare function parseHostMemory(text: string): HostMemory | null;
|
|
18
18
|
/** This PC's memory now, or null when it cannot be read (and always during the self-test: it never asks Windows). */
|
|
19
19
|
export declare function readHostMemory(storageRoot: string): HostMemory | null;
|
|
20
|
+
/**
|
|
21
|
+
* One long-running `HostTools memory-status --serve` for the watcher: a reading every `seconds`, instead of starting a
|
|
22
|
+
* program (and building a disk counter) for each one. Without HostTools.exe it does nothing and `latest()` stays null.
|
|
23
|
+
*/
|
|
24
|
+
export declare class HostMemoryStream {
|
|
25
|
+
private readonly storageRoot;
|
|
26
|
+
private readonly seconds;
|
|
27
|
+
private child;
|
|
28
|
+
private last;
|
|
29
|
+
private startedAt;
|
|
30
|
+
private stopped;
|
|
31
|
+
constructor(storageRoot: string, seconds: number);
|
|
32
|
+
start(): void;
|
|
33
|
+
/** The newest reading while it is fresh; starts the stream again when it ended (at most once a minute). */
|
|
34
|
+
latest(): HostMemory | null;
|
|
35
|
+
/** Ends it (closing its input ends HostTools). */
|
|
36
|
+
stop(): void;
|
|
37
|
+
}
|
|
20
38
|
/** The same without blocking: for the watcher, which must never wait on it. */
|
|
21
39
|
export declare function readHostMemoryAsync(storageRoot: string): Promise<HostMemory | null>;
|
|
22
40
|
/** How low this PC's memory is: what the host watchdog acts on (pure). */
|
|
@@ -47,6 +47,72 @@ export function readHostMemory(storageRoot) {
|
|
|
47
47
|
const result = spawnSync(file, args, { encoding: 'utf8', windowsHide: true, timeout: TIMEOUT_MS });
|
|
48
48
|
return parseHostMemory(result.stdout || '');
|
|
49
49
|
}
|
|
50
|
+
/** A reading older than this is not used (the stream stopped answering): the watcher asks once instead. */
|
|
51
|
+
const STREAM_FRESH_MS = 30000;
|
|
52
|
+
/** A stream that ended is started again at most this often. */
|
|
53
|
+
const STREAM_RESTART_MS = 60000;
|
|
54
|
+
/**
|
|
55
|
+
* One long-running `HostTools memory-status --serve` for the watcher: a reading every `seconds`, instead of starting a
|
|
56
|
+
* program (and building a disk counter) for each one. Without HostTools.exe it does nothing and `latest()` stays null.
|
|
57
|
+
*/
|
|
58
|
+
export class HostMemoryStream {
|
|
59
|
+
storageRoot;
|
|
60
|
+
seconds;
|
|
61
|
+
child = null;
|
|
62
|
+
last = null;
|
|
63
|
+
startedAt = 0;
|
|
64
|
+
stopped = false;
|
|
65
|
+
constructor(storageRoot, seconds) {
|
|
66
|
+
this.storageRoot = storageRoot;
|
|
67
|
+
this.seconds = seconds;
|
|
68
|
+
}
|
|
69
|
+
start() {
|
|
70
|
+
if (!isWin || selfTestRunning() || this.stopped || this.child)
|
|
71
|
+
return;
|
|
72
|
+
const tools = hostToolsExe(this.storageRoot);
|
|
73
|
+
if (!tools)
|
|
74
|
+
return;
|
|
75
|
+
this.startedAt = Date.now();
|
|
76
|
+
try {
|
|
77
|
+
const child = spawn(tools, ['memory-status', '--serve', String(this.seconds)], {
|
|
78
|
+
windowsHide: true,
|
|
79
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
80
|
+
});
|
|
81
|
+
this.child = child;
|
|
82
|
+
let buffer = '';
|
|
83
|
+
child.stdout.setEncoding('utf8');
|
|
84
|
+
child.stdout.on('data', (chunk) => {
|
|
85
|
+
buffer = (buffer + chunk).slice(-16384);
|
|
86
|
+
let end;
|
|
87
|
+
while ((end = buffer.indexOf('\n')) >= 0) {
|
|
88
|
+
const memory = parseHostMemory(buffer.slice(0, end).trim());
|
|
89
|
+
buffer = buffer.slice(end + 1);
|
|
90
|
+
if (memory)
|
|
91
|
+
this.last = { at: Date.now(), memory };
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
child.stdin.on('error', () => undefined);
|
|
95
|
+
child.on('error', () => (this.child = null));
|
|
96
|
+
child.on('exit', () => (this.child = null));
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Could not start it: the watcher asks the old way.
|
|
100
|
+
this.child = null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** The newest reading while it is fresh; starts the stream again when it ended (at most once a minute). */
|
|
104
|
+
latest() {
|
|
105
|
+
if (!this.child && Date.now() - this.startedAt >= STREAM_RESTART_MS)
|
|
106
|
+
this.start();
|
|
107
|
+
return this.last && Date.now() - this.last.at < STREAM_FRESH_MS ? this.last.memory : null;
|
|
108
|
+
}
|
|
109
|
+
/** Ends it (closing its input ends HostTools). */
|
|
110
|
+
stop() {
|
|
111
|
+
this.stopped = true;
|
|
112
|
+
this.child?.stdin?.end();
|
|
113
|
+
this.child = null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
50
116
|
/** The same without blocking: for the watcher, which must never wait on it. */
|
|
51
117
|
export function readHostMemoryAsync(storageRoot) {
|
|
52
118
|
if (!isWin || selfTestRunning())
|
|
@@ -3,6 +3,8 @@ import { type HostThresholds, type LoadProfile } from '../../shared/memory/index
|
|
|
3
3
|
import { type HostMemory } from './host-memory.js';
|
|
4
4
|
export interface WatchdogState {
|
|
5
5
|
readonly low: boolean;
|
|
6
|
+
/** Nearly out of memory: the request asks for the governor's last step. */
|
|
7
|
+
readonly severe: boolean;
|
|
6
8
|
readonly calm: number;
|
|
7
9
|
/** Seconds the CPU has been above the limit. */
|
|
8
10
|
readonly cpuHighSeconds: number;
|
|
@@ -31,6 +33,8 @@ export interface WatchdogStep {
|
|
|
31
33
|
readonly send: boolean;
|
|
32
34
|
/** Why the PC is low (plain words), empty when it is not. */
|
|
33
35
|
readonly reason: string;
|
|
36
|
+
/** Nearly out of memory: builds go to the governor's last step. */
|
|
37
|
+
readonly severe: boolean;
|
|
34
38
|
/** The state changed (the log says so). */
|
|
35
39
|
readonly changed: boolean;
|
|
36
40
|
}
|
|
@@ -40,6 +44,11 @@ export interface WatchdogStep {
|
|
|
40
44
|
* `scale` widens the limits (1.25 = the level it must climb back above).
|
|
41
45
|
*/
|
|
42
46
|
export declare function lowReason(memory: HostMemory, reserveHostMb: number, t: HostThresholds, scale: number): string;
|
|
47
|
+
/**
|
|
48
|
+
* Nearly out of memory on this PC: under 1.5 GB of RAM free, or commit within max(2 GB, 4 %) of its limit. Then the
|
|
49
|
+
* sandbox's builds go to the governor's last step at once (one compiler at a time, 15 % CPU) until it passes.
|
|
50
|
+
*/
|
|
51
|
+
export declare function severeHost(memory: HostMemory): boolean;
|
|
43
52
|
/** One watchdog step. Hysteresis: low starts at the limits and ends above 1.25 times them for calmSamples samples. */
|
|
44
53
|
export declare function watchdogStep(prev: WatchdogState, input: WatchdogInput): WatchdogStep;
|
|
45
54
|
/** Total CPU use between two os.cpus() readings (percent). */
|
|
@@ -5,6 +5,7 @@ import { HOST_THRESHOLDS } from '../../shared/memory/index.js';
|
|
|
5
5
|
import { hostMemoryLine } from './host-memory.js';
|
|
6
6
|
export const WATCHDOG_START = {
|
|
7
7
|
low: false,
|
|
8
|
+
severe: false,
|
|
8
9
|
calm: 0,
|
|
9
10
|
cpuHighSeconds: 0,
|
|
10
11
|
diskHighSeconds: 0,
|
|
@@ -27,6 +28,17 @@ export function lowReason(memory, reserveHostMb, t, scale) {
|
|
|
27
28
|
return 'only ' + memory.commitAvailableMb + ' MB of memory (RAM plus paging file) is left on this PC';
|
|
28
29
|
return '';
|
|
29
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Nearly out of memory on this PC: under 1.5 GB of RAM free, or commit within max(2 GB, 4 %) of its limit. Then the
|
|
33
|
+
* sandbox's builds go to the governor's last step at once (one compiler at a time, 15 % CPU) until it passes.
|
|
34
|
+
*/
|
|
35
|
+
export function severeHost(memory) {
|
|
36
|
+
return (memory.availableMb < SEVERE_AVAILABLE_MB ||
|
|
37
|
+
memory.commitAvailableMb < Math.max(SEVERE_COMMIT_MB, (memory.commitLimitMb * SEVERE_COMMIT_PERCENT) / 100));
|
|
38
|
+
}
|
|
39
|
+
const SEVERE_AVAILABLE_MB = 1536;
|
|
40
|
+
const SEVERE_COMMIT_MB = 2048;
|
|
41
|
+
const SEVERE_COMMIT_PERCENT = 4;
|
|
30
42
|
/** One watchdog step. Hysteresis: low starts at the limits and ends above 1.25 times them for calmSamples samples. */
|
|
31
43
|
export function watchdogStep(prev, input) {
|
|
32
44
|
const t = HOST_THRESHOLDS[input.profile];
|
|
@@ -54,10 +66,13 @@ export function watchdogStep(prev, input) {
|
|
|
54
66
|
calm = 0;
|
|
55
67
|
}
|
|
56
68
|
}
|
|
57
|
-
const
|
|
69
|
+
const severe = low && input.memory !== null && severeHost(input.memory);
|
|
70
|
+
// Getting severe is sent at once; otherwise the request is repeated every RESEND_MS while the PC stays low.
|
|
71
|
+
const send = low && (input.nowMs - prev.lastSentAt >= RESEND_MS || (severe && !prev.severe));
|
|
58
72
|
return {
|
|
59
73
|
state: {
|
|
60
74
|
low,
|
|
75
|
+
severe,
|
|
61
76
|
calm,
|
|
62
77
|
cpuHighSeconds: cpuHigh,
|
|
63
78
|
diskHighSeconds: diskHigh,
|
|
@@ -65,6 +80,7 @@ export function watchdogStep(prev, input) {
|
|
|
65
80
|
},
|
|
66
81
|
send,
|
|
67
82
|
reason: reason || (low ? 'this PC is still low' : ''),
|
|
83
|
+
severe,
|
|
68
84
|
changed: low !== prev.low,
|
|
69
85
|
};
|
|
70
86
|
}
|