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
|
@@ -19,6 +19,8 @@ export const HostMemorySchema = z.object({
|
|
|
19
19
|
pageFile: z.enum(['auto', 'fixed', 'none']).catch('auto'),
|
|
20
20
|
/** Fixed: the total of its maximum sizes (MB). */
|
|
21
21
|
pageFileMaxMb: z.number().int().nonnegative().catch(0),
|
|
22
|
+
/** The disks' queue length now (all disks together); absent when this PC has no such counter. */
|
|
23
|
+
diskQueue: z.number().nonnegative().max(100000).optional().catch(undefined),
|
|
22
24
|
});
|
|
23
25
|
const TIMEOUT_MS = 20000;
|
|
24
26
|
/** The parsed answer of memory-status, or null (not Windows, unreadable, nonsense). */
|
|
@@ -91,7 +93,8 @@ export function hostMemoryLine(memory, at = new Date()) {
|
|
|
91
93
|
'/' +
|
|
92
94
|
memory.commitLimitMb +
|
|
93
95
|
' | page file ' +
|
|
94
|
-
memory.pageFile
|
|
96
|
+
memory.pageFile +
|
|
97
|
+
(memory.diskQueue === undefined ? '' : ' | disk queue ' + memory.diskQueue));
|
|
95
98
|
}
|
|
96
99
|
/**
|
|
97
100
|
* Whether Windows Sandbox could push this PC past its commit limit (pure): the paging file is fixed or off and the
|
|
@@ -3,9 +3,13 @@ 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;
|
|
11
|
+
/** Seconds the disk queue has been above the limit. */
|
|
12
|
+
readonly diskHighSeconds: number;
|
|
9
13
|
/** Time (ms) the last request went out. */
|
|
10
14
|
readonly lastSentAt: number;
|
|
11
15
|
}
|
|
@@ -29,6 +33,8 @@ export interface WatchdogStep {
|
|
|
29
33
|
readonly send: boolean;
|
|
30
34
|
/** Why the PC is low (plain words), empty when it is not. */
|
|
31
35
|
readonly reason: string;
|
|
36
|
+
/** Nearly out of memory: builds go to the governor's last step. */
|
|
37
|
+
readonly severe: boolean;
|
|
32
38
|
/** The state changed (the log says so). */
|
|
33
39
|
readonly changed: boolean;
|
|
34
40
|
}
|
|
@@ -38,6 +44,11 @@ export interface WatchdogStep {
|
|
|
38
44
|
* `scale` widens the limits (1.25 = the level it must climb back above).
|
|
39
45
|
*/
|
|
40
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;
|
|
41
52
|
/** One watchdog step. Hysteresis: low starts at the limits and ends above 1.25 times them for calmSamples samples. */
|
|
42
53
|
export declare function watchdogStep(prev: WatchdogState, input: WatchdogInput): WatchdogStep;
|
|
43
54
|
/** Total CPU use between two os.cpus() readings (percent). */
|
|
@@ -3,7 +3,14 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { HOST_THRESHOLDS } from '../../shared/memory/index.js';
|
|
5
5
|
import { hostMemoryLine } from './host-memory.js';
|
|
6
|
-
export const WATCHDOG_START = {
|
|
6
|
+
export const WATCHDOG_START = {
|
|
7
|
+
low: false,
|
|
8
|
+
severe: false,
|
|
9
|
+
calm: 0,
|
|
10
|
+
cpuHighSeconds: 0,
|
|
11
|
+
diskHighSeconds: 0,
|
|
12
|
+
lastSentAt: 0,
|
|
13
|
+
};
|
|
7
14
|
/** The request is repeated this often while the PC stays low, and each one lasts a bit longer than that. */
|
|
8
15
|
export const RESEND_MS = 30000;
|
|
9
16
|
export const REQUEST_SECONDS = 90;
|
|
@@ -21,6 +28,17 @@ export function lowReason(memory, reserveHostMb, t, scale) {
|
|
|
21
28
|
return 'only ' + memory.commitAvailableMb + ' MB of memory (RAM plus paging file) is left on this PC';
|
|
22
29
|
return '';
|
|
23
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;
|
|
24
42
|
/** One watchdog step. Hysteresis: low starts at the limits and ends above 1.25 times them for calmSamples samples. */
|
|
25
43
|
export function watchdogStep(prev, input) {
|
|
26
44
|
const t = HOST_THRESHOLDS[input.profile];
|
|
@@ -30,6 +48,10 @@ export function watchdogStep(prev, input) {
|
|
|
30
48
|
const cpuHigh = input.cpuPercent !== null && input.cpuPercent >= t.cpuPercent ? prev.cpuHighSeconds + input.intervalSeconds : 0;
|
|
31
49
|
if (!reason && cpuHigh >= t.cpuSeconds)
|
|
32
50
|
reason = "this PC's CPU has been above " + t.cpuPercent + '% for ' + cpuHigh + ' seconds';
|
|
51
|
+
const diskQueue = input.memory?.diskQueue;
|
|
52
|
+
const diskHigh = diskQueue !== undefined && diskQueue >= t.diskQueue ? prev.diskHighSeconds + input.intervalSeconds : 0;
|
|
53
|
+
if (!reason && diskHigh >= t.diskSeconds)
|
|
54
|
+
reason = "this PC's disks have had a queue of " + diskQueue + ' for ' + diskHigh + ' seconds';
|
|
33
55
|
const calmNow = input.memory ? lowReason(input.memory, input.reserveHostMb, t, 1.25) === '' : true;
|
|
34
56
|
let low = prev.low;
|
|
35
57
|
let calm = prev.calm;
|
|
@@ -38,17 +60,27 @@ export function watchdogStep(prev, input) {
|
|
|
38
60
|
calm = 0;
|
|
39
61
|
}
|
|
40
62
|
else if (prev.low) {
|
|
41
|
-
calm = calmNow && cpuHigh === 0 ? prev.calm + 1 : 0;
|
|
63
|
+
calm = calmNow && cpuHigh === 0 && diskHigh === 0 ? prev.calm + 1 : 0;
|
|
42
64
|
if (calm >= t.calmSamples) {
|
|
43
65
|
low = false;
|
|
44
66
|
calm = 0;
|
|
45
67
|
}
|
|
46
68
|
}
|
|
47
|
-
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));
|
|
48
72
|
return {
|
|
49
|
-
state: {
|
|
73
|
+
state: {
|
|
74
|
+
low,
|
|
75
|
+
severe,
|
|
76
|
+
calm,
|
|
77
|
+
cpuHighSeconds: cpuHigh,
|
|
78
|
+
diskHighSeconds: diskHigh,
|
|
79
|
+
lastSentAt: send ? input.nowMs : prev.lastSentAt,
|
|
80
|
+
},
|
|
50
81
|
send,
|
|
51
82
|
reason: reason || (low ? 'this PC is still low' : ''),
|
|
83
|
+
severe,
|
|
52
84
|
changed: low !== prev.low,
|
|
53
85
|
};
|
|
54
86
|
}
|
|
@@ -7,6 +7,12 @@ export interface TasklistRunners {
|
|
|
7
7
|
export declare function useTasklistRunners(replacement?: TasklistRunners): void;
|
|
8
8
|
/** While set, a synchronous read returns this copy (undefined: none yet, run tasklist). Null removes it. */
|
|
9
9
|
export declare function useTasklistCache(read: (() => string | undefined) | null): void;
|
|
10
|
+
/**
|
|
11
|
+
* For a long-running server (the web UI): questions within `freshMs` of the last run share its list; an older one
|
|
12
|
+
* (up to `staleMs`) is still answered from it while a new list is fetched in the background, so no request waits for
|
|
13
|
+
* tasklist. Nothing runs while nobody asks. Returns the function that stops it.
|
|
14
|
+
*/
|
|
15
|
+
export declare function keepTasklistFresh(freshMs?: number, staleMs?: number): () => void;
|
|
10
16
|
/** The process list now: the cached copy when the menu keeps one, else a run of tasklist the caller waits for. */
|
|
11
17
|
export declare function readTasklist(): string;
|
|
12
18
|
/** The process list from a run of tasklist that does not block the program (the menu's background refresh). */
|
|
@@ -37,6 +37,43 @@ export function useTasklistRunners(replacement = REAL_RUNNERS) {
|
|
|
37
37
|
export function useTasklistCache(read) {
|
|
38
38
|
cache = read;
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* For a long-running server (the web UI): questions within `freshMs` of the last run share its list; an older one
|
|
42
|
+
* (up to `staleMs`) is still answered from it while a new list is fetched in the background, so no request waits for
|
|
43
|
+
* tasklist. Nothing runs while nobody asks. Returns the function that stops it.
|
|
44
|
+
*/
|
|
45
|
+
export function keepTasklistFresh(freshMs = 2000, staleMs = 15000) {
|
|
46
|
+
let copy;
|
|
47
|
+
let at = 0;
|
|
48
|
+
let fetching = false;
|
|
49
|
+
const refresh = () => {
|
|
50
|
+
if (fetching)
|
|
51
|
+
return;
|
|
52
|
+
fetching = true;
|
|
53
|
+
void runners
|
|
54
|
+
.async()
|
|
55
|
+
.then((list) => {
|
|
56
|
+
copy = list;
|
|
57
|
+
at = Date.now();
|
|
58
|
+
})
|
|
59
|
+
.finally(() => {
|
|
60
|
+
fetching = false;
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
useTasklistCache(() => {
|
|
64
|
+
const age = Date.now() - at;
|
|
65
|
+
if (copy !== undefined && age < freshMs)
|
|
66
|
+
return copy;
|
|
67
|
+
if (copy !== undefined && age < staleMs) {
|
|
68
|
+
refresh();
|
|
69
|
+
return copy;
|
|
70
|
+
}
|
|
71
|
+
copy = runners.sync();
|
|
72
|
+
at = Date.now();
|
|
73
|
+
return copy;
|
|
74
|
+
});
|
|
75
|
+
return () => useTasklistCache(null);
|
|
76
|
+
}
|
|
40
77
|
/** The process list now: the cached copy when the menu keeps one, else a run of tasklist the caller waits for. */
|
|
41
78
|
export function readTasklist() {
|
|
42
79
|
return cache?.() ?? runners.sync();
|
|
@@ -4,7 +4,8 @@ import type { GuestRequest } from '../../core/types.js';
|
|
|
4
4
|
export declare function writeSecrets(dataPath: string, id: string, secrets: SecretsFile): string | null;
|
|
5
5
|
/**
|
|
6
6
|
* Removes whatever one-shot files are left (the sandbox did not start, or did not get to read them) and, from older
|
|
7
|
-
* versions, a login copy in a launch folder. Called before a launch writes new ones and when
|
|
7
|
+
* versions, a login copy in a launch folder. Called before a launch writes new ones, and when a launch fails before
|
|
8
|
+
* its sandbox starts.
|
|
8
9
|
*/
|
|
9
10
|
export declare function clearSecrets(storageRoot: string, dataPath: string): number;
|
|
10
11
|
/**
|
|
@@ -12,7 +12,8 @@ export function writeSecrets(dataPath, id, secrets) {
|
|
|
12
12
|
!Object.keys(secrets.projectHarnessEnvironment ?? {}).length &&
|
|
13
13
|
!secrets.claudeCredentials &&
|
|
14
14
|
!secrets.vpn &&
|
|
15
|
-
!secrets.wireguardConfig
|
|
15
|
+
!secrets.wireguardConfig &&
|
|
16
|
+
!secrets.githubToken;
|
|
16
17
|
if (empty)
|
|
17
18
|
return null;
|
|
18
19
|
const run = new RunDir(dataPath);
|
|
@@ -23,7 +24,8 @@ export function writeSecrets(dataPath, id, secrets) {
|
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
25
26
|
* Removes whatever one-shot files are left (the sandbox did not start, or did not get to read them) and, from older
|
|
26
|
-
* versions, a login copy in a launch folder. Called before a launch writes new ones and when
|
|
27
|
+
* versions, a login copy in a launch folder. Called before a launch writes new ones, and when a launch fails before
|
|
28
|
+
* its sandbox starts.
|
|
27
29
|
*/
|
|
28
30
|
export function clearSecrets(storageRoot, dataPath) {
|
|
29
31
|
let removed = 0;
|
package/dist/host/vpn/status.js
CHANGED
|
@@ -17,8 +17,16 @@ export function vpnLine(status, hostIp = null) {
|
|
|
17
17
|
if (!status)
|
|
18
18
|
return { level: 'warn', text: 'The sandbox has not reported on the VPN.' };
|
|
19
19
|
const message = safeText(status.message).slice(0, 300);
|
|
20
|
+
const leaky = status.killSwitch && status.killSwitchHeld === false;
|
|
20
21
|
if (status.state === 'up') {
|
|
21
22
|
const same = hostIp !== null && status.publicIp === hostIp;
|
|
23
|
+
if (!same && leaky)
|
|
24
|
+
return {
|
|
25
|
+
level: 'warn',
|
|
26
|
+
text: 'VPN up' +
|
|
27
|
+
(status.publicIp ? ', the sandbox appears as ' + safeText(status.publicIp) : '') +
|
|
28
|
+
', but the kill switch did not hold: if the tunnel drops, traffic goes out through your normal connection.',
|
|
29
|
+
};
|
|
22
30
|
return same
|
|
23
31
|
? {
|
|
24
32
|
level: 'bad',
|
|
@@ -40,9 +48,11 @@ export function vpnLine(status, hostIp = null) {
|
|
|
40
48
|
level: 'bad',
|
|
41
49
|
text: 'VPN failed: ' +
|
|
42
50
|
message +
|
|
43
|
-
(
|
|
44
|
-
? ' The kill switch
|
|
45
|
-
:
|
|
51
|
+
(leaky
|
|
52
|
+
? ' The kill switch did not hold, so the sandbox is ONLINE through your normal connection, without the VPN.'
|
|
53
|
+
: status.killSwitch
|
|
54
|
+
? ' The kill switch is on, so the sandbox has no internet.'
|
|
55
|
+
: ' The kill switch is off, so the sandbox uses your normal connection.'),
|
|
46
56
|
};
|
|
47
57
|
return { level: 'warn', text: 'VPN ' + status.state + (message ? ': ' + message : '') };
|
|
48
58
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Endpoint } from '../../shared/pty/index.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { WebInfo, WebSession } from '../../shared/web/index.js';
|
|
3
3
|
export interface WebBackend {
|
|
4
4
|
info(): Promise<WebInfo>;
|
|
5
5
|
/** Sessions as the browser may see them: never an attach token or address. */
|
|
@@ -9,6 +9,4 @@ export interface WebBackend {
|
|
|
9
9
|
* running session, the sandbox is not shared with this user, or it has no address on this PC's sandbox network.
|
|
10
10
|
*/
|
|
11
11
|
endpoint(sessionId: string): Endpoint | null;
|
|
12
|
-
/** Starts an agent in a project of the running sandbox and waits until it runs. */
|
|
13
|
-
create(request: NewSessionRequest): Promise<WebSession>;
|
|
14
12
|
}
|
package/dist/host/web/index.d.ts
CHANGED
|
@@ -4,7 +4,8 @@ export { openBrowser, openIssuePage } from './browser.js';
|
|
|
4
4
|
export { TerminalBridge } from './bridge.js';
|
|
5
5
|
export { WebControl, type WebStatus } from './control.js';
|
|
6
6
|
export { WebRunner } from './runner.js';
|
|
7
|
+
export { LiveHub, type LiveListener } from './live.js';
|
|
7
8
|
export { RpcSession } from './rpc.js';
|
|
8
|
-
export { WebServer, CONTENT_SECURITY_POLICY, RPC_PATH, type WebServerOptions } from './server.js';
|
|
9
|
+
export { WebServer, CONTENT_SECURITY_POLICY, IDLE_EXIT_MS, RPC_PATH, type WebServerOptions } from './server.js';
|
|
9
10
|
export { StaticFiles } from './static-files.js';
|
|
10
11
|
export { WebStore, WebStateSchema, type WebState } from './state.js';
|
package/dist/host/web/index.js
CHANGED
|
@@ -3,7 +3,8 @@ export { openBrowser, openIssuePage } from './browser.js';
|
|
|
3
3
|
export { TerminalBridge } from './bridge.js';
|
|
4
4
|
export { WebControl } from './control.js';
|
|
5
5
|
export { WebRunner } from './runner.js';
|
|
6
|
+
export { LiveHub } from './live.js';
|
|
6
7
|
export { RpcSession } from './rpc.js';
|
|
7
|
-
export { WebServer, CONTENT_SECURITY_POLICY, RPC_PATH } from './server.js';
|
|
8
|
+
export { WebServer, CONTENT_SECURITY_POLICY, IDLE_EXIT_MS, RPC_PATH } from './server.js';
|
|
8
9
|
export { StaticFiles } from './static-files.js';
|
|
9
10
|
export { WebStore, WebStateSchema } from './state.js';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { RpcHost } from '../../app/kernel/index.js';
|
|
2
|
+
export type LiveListener = (event: {
|
|
3
|
+
readonly output: unknown;
|
|
4
|
+
} | {
|
|
5
|
+
readonly error: unknown;
|
|
6
|
+
}) => void;
|
|
7
|
+
export declare class LiveHub {
|
|
8
|
+
private readonly host;
|
|
9
|
+
private readonly polls;
|
|
10
|
+
constructor(host: RpcHost);
|
|
11
|
+
/** How many polls run (for tests and the idle check). */
|
|
12
|
+
get size(): number;
|
|
13
|
+
/**
|
|
14
|
+
* Starts listening; the listener gets the current answer (at once when one is known) and every change. Returns the
|
|
15
|
+
* function that stops listening.
|
|
16
|
+
*/
|
|
17
|
+
watch(query: string, input: unknown, listener: LiveListener): () => void;
|
|
18
|
+
/** Something changed (an operation ran): every query reads again now, at its own pace. */
|
|
19
|
+
poke(): void;
|
|
20
|
+
private schedule;
|
|
21
|
+
private isLive;
|
|
22
|
+
private read;
|
|
23
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { NO_QUESTIONS } from '../../app/kernel/index.js';
|
|
2
|
+
/** A query unchanged this many reads in a row is read less often... */
|
|
3
|
+
const CALM_READS = 4;
|
|
4
|
+
/** ...down to this many times its own interval. */
|
|
5
|
+
const SLOWEST = 4;
|
|
6
|
+
export class LiveHub {
|
|
7
|
+
host;
|
|
8
|
+
polls = new Map();
|
|
9
|
+
constructor(host) {
|
|
10
|
+
this.host = host;
|
|
11
|
+
}
|
|
12
|
+
/** How many polls run (for tests and the idle check). */
|
|
13
|
+
get size() {
|
|
14
|
+
return this.polls.size;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Starts listening; the listener gets the current answer (at once when one is known) and every change. Returns the
|
|
18
|
+
* function that stops listening.
|
|
19
|
+
*/
|
|
20
|
+
watch(query, input, listener) {
|
|
21
|
+
const key = query + '\u0000' + JSON.stringify(input ?? null);
|
|
22
|
+
let poll = this.polls.get(key);
|
|
23
|
+
if (!poll) {
|
|
24
|
+
poll = {
|
|
25
|
+
query,
|
|
26
|
+
input,
|
|
27
|
+
everyMs: this.host.liveMs(query) ?? 0,
|
|
28
|
+
listeners: new Set(),
|
|
29
|
+
timer: undefined,
|
|
30
|
+
busy: false,
|
|
31
|
+
last: undefined,
|
|
32
|
+
lastOutput: undefined,
|
|
33
|
+
calm: 0,
|
|
34
|
+
again: false,
|
|
35
|
+
};
|
|
36
|
+
this.polls.set(key, poll);
|
|
37
|
+
void this.read(poll);
|
|
38
|
+
}
|
|
39
|
+
else if (poll.last !== undefined)
|
|
40
|
+
listener({ output: poll.lastOutput });
|
|
41
|
+
else if (!poll.busy)
|
|
42
|
+
void this.read(poll); // the last read failed: try again for the newcomer
|
|
43
|
+
poll.listeners.add(listener);
|
|
44
|
+
const p = poll;
|
|
45
|
+
return () => {
|
|
46
|
+
p.listeners.delete(listener);
|
|
47
|
+
if (p.listeners.size)
|
|
48
|
+
return;
|
|
49
|
+
clearTimeout(p.timer);
|
|
50
|
+
this.polls.delete(key);
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Something changed (an operation ran): every query reads again now, at its own pace. */
|
|
54
|
+
poke() {
|
|
55
|
+
for (const poll of this.polls.values()) {
|
|
56
|
+
poll.calm = 0;
|
|
57
|
+
void this.read(poll);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
schedule(poll) {
|
|
61
|
+
clearTimeout(poll.timer);
|
|
62
|
+
if (!poll.everyMs || !this.isLive(poll))
|
|
63
|
+
return;
|
|
64
|
+
const factor = poll.calm >= CALM_READS ? Math.min(SLOWEST, 1 + Math.floor(poll.calm / CALM_READS)) : 1;
|
|
65
|
+
poll.timer = setTimeout(() => void this.read(poll), poll.everyMs * factor);
|
|
66
|
+
poll.timer.unref?.();
|
|
67
|
+
}
|
|
68
|
+
isLive(poll) {
|
|
69
|
+
return [...this.polls.values()].includes(poll);
|
|
70
|
+
}
|
|
71
|
+
async read(poll) {
|
|
72
|
+
if (poll.busy) {
|
|
73
|
+
poll.again = true;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
poll.busy = true;
|
|
77
|
+
clearTimeout(poll.timer);
|
|
78
|
+
try {
|
|
79
|
+
const output = await this.host.run(poll.query, poll.input, NO_QUESTIONS);
|
|
80
|
+
const text = JSON.stringify(output);
|
|
81
|
+
if (text === poll.last)
|
|
82
|
+
poll.calm++;
|
|
83
|
+
else {
|
|
84
|
+
poll.calm = 0;
|
|
85
|
+
poll.last = text;
|
|
86
|
+
poll.lastOutput = output;
|
|
87
|
+
for (const l of [...poll.listeners])
|
|
88
|
+
l({ output });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
poll.calm = 0;
|
|
93
|
+
poll.last = undefined;
|
|
94
|
+
for (const l of [...poll.listeners])
|
|
95
|
+
l({ error });
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
poll.busy = false;
|
|
99
|
+
}
|
|
100
|
+
if (poll.again) {
|
|
101
|
+
poll.again = false;
|
|
102
|
+
void this.read(poll);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
this.schedule(poll);
|
|
106
|
+
}
|
|
107
|
+
}
|
package/dist/host/web/rpc.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import type { WebSocket } from 'ws';
|
|
2
2
|
import { type RpcHost } from '../../app/kernel/index.js';
|
|
3
|
+
import { LiveHub } from './live.js';
|
|
3
4
|
export declare class RpcSession {
|
|
4
5
|
private readonly ws;
|
|
5
6
|
private readonly host;
|
|
7
|
+
/** Shared by every socket of the server (one poll per query); a socket of its own by default. */
|
|
8
|
+
private readonly live;
|
|
6
9
|
private readonly calls;
|
|
7
10
|
private readonly subscriptions;
|
|
8
11
|
private closed;
|
|
9
|
-
constructor(ws: WebSocket, host: RpcHost
|
|
12
|
+
constructor(ws: WebSocket, host: RpcHost,
|
|
13
|
+
/** Shared by every socket of the server (one poll per query); a socket of its own by default. */
|
|
14
|
+
live?: LiveHub);
|
|
10
15
|
start(): void;
|
|
11
16
|
private send;
|
|
12
17
|
/** A browser that sends something that is not one of our messages is closed. */
|
package/dist/host/web/rpc.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ANSWER_SCHEMAS, ClientMessageSchema, MAX_OPEN_PER_SOCKET,
|
|
1
|
+
import { ANSWER_SCHEMAS, ClientMessageSchema, MAX_OPEN_PER_SOCKET, ioFromAsker, toWireError, } from '../../app/kernel/index.js';
|
|
2
|
+
import { LiveHub } from './live.js';
|
|
2
3
|
/** What a question gets when the browser goes away before answering: no. */
|
|
3
4
|
function declined(q) {
|
|
4
5
|
return q.kind === 'confirm' ? false : undefined;
|
|
@@ -6,12 +7,16 @@ function declined(q) {
|
|
|
6
7
|
export class RpcSession {
|
|
7
8
|
ws;
|
|
8
9
|
host;
|
|
10
|
+
live;
|
|
9
11
|
calls = new Map();
|
|
10
12
|
subscriptions = new Map();
|
|
11
13
|
closed = false;
|
|
12
|
-
constructor(ws, host
|
|
14
|
+
constructor(ws, host,
|
|
15
|
+
/** Shared by every socket of the server (one poll per query); a socket of its own by default. */
|
|
16
|
+
live = new LiveHub(host)) {
|
|
13
17
|
this.ws = ws;
|
|
14
18
|
this.host = host;
|
|
19
|
+
this.live = live;
|
|
15
20
|
}
|
|
16
21
|
start() {
|
|
17
22
|
this.ws.on('message', (data, binary) => this.receive(data, binary));
|
|
@@ -86,7 +91,12 @@ export class RpcSession {
|
|
|
86
91
|
this.host
|
|
87
92
|
.run(op, input, io)
|
|
88
93
|
.then((output) => this.send({ t: 'result', id, output }), (e) => this.send({ t: 'error', id, error: toWireError(e) }))
|
|
89
|
-
.finally(() =>
|
|
94
|
+
.finally(() => {
|
|
95
|
+
this.calls.delete(id);
|
|
96
|
+
// An operation may have changed what the live queries show: they read again now.
|
|
97
|
+
if (this.host.kind(op) === 'operation')
|
|
98
|
+
this.live.poke();
|
|
99
|
+
});
|
|
90
100
|
}
|
|
91
101
|
answer(id, value) {
|
|
92
102
|
const waiting = this.calls.get(id);
|
|
@@ -103,38 +113,20 @@ export class RpcSession {
|
|
|
103
113
|
this.send({ t: 'error', id, error: { kind: 'invalid', message: 'There is no ' + query + ' here.' } });
|
|
104
114
|
return;
|
|
105
115
|
}
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
const read = async () => {
|
|
109
|
-
if (sub.busy)
|
|
116
|
+
const stop = this.live.watch(query, input, (event) => {
|
|
117
|
+
if (this.subscriptions.get(id) !== stop)
|
|
110
118
|
return;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.send({ t: 'data', id, output });
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
catch (e) {
|
|
121
|
-
sub.last = undefined;
|
|
122
|
-
this.send({ t: 'error', id, error: toWireError(e) });
|
|
123
|
-
}
|
|
124
|
-
finally {
|
|
125
|
-
sub.busy = false;
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
void read();
|
|
129
|
-
const every = this.host.liveMs(query);
|
|
130
|
-
if (every)
|
|
131
|
-
sub.timer = setInterval(() => void read(), every);
|
|
119
|
+
if ('output' in event)
|
|
120
|
+
this.send({ t: 'data', id, output: event.output });
|
|
121
|
+
else
|
|
122
|
+
this.send({ t: 'error', id, error: toWireError(event.error) });
|
|
123
|
+
});
|
|
124
|
+
this.subscriptions.set(id, stop);
|
|
132
125
|
}
|
|
133
126
|
unsubscribe(id) {
|
|
134
|
-
const
|
|
135
|
-
if (sub?.timer)
|
|
136
|
-
clearInterval(sub.timer);
|
|
127
|
+
const stop = this.subscriptions.get(id);
|
|
137
128
|
this.subscriptions.delete(id);
|
|
129
|
+
stop?.();
|
|
138
130
|
}
|
|
139
131
|
/** The browser went away: subscriptions stop, and questions still open are answered no. */
|
|
140
132
|
close() {
|
|
@@ -6,6 +6,10 @@ export interface WebRunOptions {
|
|
|
6
6
|
readonly backend: WebBackend;
|
|
7
7
|
readonly rpc?: RpcHost;
|
|
8
8
|
readonly staticRoot: string;
|
|
9
|
+
/** This program's version, kept in the state file. */
|
|
10
|
+
readonly version: string;
|
|
11
|
+
/** End after this long unused while no sandbox runs (WebServerOptions.idleExitMs). */
|
|
12
|
+
readonly idleExitMs?: number;
|
|
9
13
|
}
|
|
10
14
|
export declare class WebRunner {
|
|
11
15
|
private readonly options;
|
package/dist/host/web/runner.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { fail } from '../../core/util/index.js';
|
|
2
|
+
import { keepTasklistFresh } from '../sandbox/index.js';
|
|
2
3
|
import { newToken } from './auth.js';
|
|
3
4
|
import { WebServer } from './server.js';
|
|
4
5
|
import { WebStore } from './state.js';
|
|
@@ -20,6 +21,7 @@ export class WebRunner {
|
|
|
20
21
|
backend: this.options.backend,
|
|
21
22
|
rpc: this.options.rpc,
|
|
22
23
|
staticRoot: this.options.staticRoot,
|
|
24
|
+
idleExitMs: this.options.idleExitMs,
|
|
23
25
|
onStop: stop,
|
|
24
26
|
});
|
|
25
27
|
try {
|
|
@@ -30,13 +32,22 @@ export class WebRunner {
|
|
|
30
32
|
fail('Port ' + this.options.port + ' is taken by another program.', 'Pick another with the web.port setting, then run sandbox web start again.');
|
|
31
33
|
throw e;
|
|
32
34
|
}
|
|
33
|
-
this.store.write({
|
|
35
|
+
this.store.write({
|
|
36
|
+
pid: process.pid,
|
|
37
|
+
port: server.port,
|
|
38
|
+
token,
|
|
39
|
+
startedAt: new Date().toISOString(),
|
|
40
|
+
version: this.options.version,
|
|
41
|
+
});
|
|
34
42
|
process.once('SIGINT', stop);
|
|
35
43
|
process.once('SIGTERM', stop);
|
|
44
|
+
// Every browser's status reads share one process list instead of each running tasklist.
|
|
45
|
+
const stopTasklist = keepTasklistFresh();
|
|
36
46
|
try {
|
|
37
47
|
await stopped;
|
|
38
48
|
}
|
|
39
49
|
finally {
|
|
50
|
+
stopTasklist();
|
|
40
51
|
this.store.clear();
|
|
41
52
|
await server.stop();
|
|
42
53
|
}
|
|
@@ -4,6 +4,8 @@ import type { Endpoint } from '../../shared/pty/index.js';
|
|
|
4
4
|
import type { WebBackend } from './backend.js';
|
|
5
5
|
/** No inline script: the page and its script are files from this server. Inline styles are what xterm.js needs. */
|
|
6
6
|
export declare const CONTENT_SECURITY_POLICY: string;
|
|
7
|
+
/** The server ends after this long with no request, no socket and no sandbox running (it starts again with one). */
|
|
8
|
+
export declare const IDLE_EXIT_MS: number;
|
|
7
9
|
export declare const RPC_PATH = "/api/rpc";
|
|
8
10
|
export interface WebServerOptions {
|
|
9
11
|
/** 0 picks a free port (tests). */
|
|
@@ -18,6 +20,16 @@ export interface WebServerOptions {
|
|
|
18
20
|
readonly dial?: (endpoint: Endpoint) => Promise<net.Socket>;
|
|
19
21
|
/** The shared core's operations and queries the browser may run (none: /api/rpc is not served). */
|
|
20
22
|
readonly rpc?: RpcHost;
|
|
23
|
+
/** End by itself (onStop) after this long unused while no sandbox runs; 0 or missing: never (tests). */
|
|
24
|
+
readonly idleExitMs?: number;
|
|
25
|
+
/** Heartbeat period (tests make it short). */
|
|
26
|
+
readonly heartbeatMs?: number;
|
|
27
|
+
}
|
|
28
|
+
/** A browser's socket: which kind, whether it answered the last ping, and when it was opened. */
|
|
29
|
+
interface Tracked {
|
|
30
|
+
readonly kind: 'terminal' | 'rpc';
|
|
31
|
+
readonly opened: number;
|
|
32
|
+
alive: boolean;
|
|
21
33
|
}
|
|
22
34
|
export declare class WebServer {
|
|
23
35
|
private readonly options;
|
|
@@ -25,12 +37,27 @@ export declare class WebServer {
|
|
|
25
37
|
private readonly sockets;
|
|
26
38
|
private readonly auth;
|
|
27
39
|
private readonly files;
|
|
28
|
-
private
|
|
29
|
-
|
|
40
|
+
private readonly tracked;
|
|
41
|
+
/** One poll per live query for all browsers together. */
|
|
42
|
+
private readonly live;
|
|
30
43
|
private actualPort;
|
|
44
|
+
private lastUsed;
|
|
45
|
+
private heartbeat;
|
|
46
|
+
private idleCheck;
|
|
31
47
|
constructor(options: WebServerOptions);
|
|
32
48
|
get port(): number;
|
|
33
49
|
start(): Promise<void>;
|
|
50
|
+
/** How many browser sockets are open, by kind. */
|
|
51
|
+
openSockets(kind: Tracked['kind']): number;
|
|
52
|
+
/**
|
|
53
|
+
* Drops sockets that did not answer the last ping (a tab that went away without closing: sleep, a killed browser),
|
|
54
|
+
* so they never hold a slot or a session connection.
|
|
55
|
+
*/
|
|
56
|
+
private beat;
|
|
57
|
+
/** Nothing has used the server for a while and no sandbox runs: it ends (the next launch starts it again). */
|
|
58
|
+
private checkIdle;
|
|
59
|
+
/** Counts a new socket; at the limit, the oldest of its kind makes room (a new tab always works). */
|
|
60
|
+
private track;
|
|
34
61
|
stop(): Promise<void>;
|
|
35
62
|
private headers;
|
|
36
63
|
private json;
|
|
@@ -45,3 +72,4 @@ export declare class WebServer {
|
|
|
45
72
|
/** The RPC socket (after the same Origin and cookie checks as a terminal). */
|
|
46
73
|
private upgradeRpc;
|
|
47
74
|
}
|
|
75
|
+
export {};
|