windows-development-sandbox 1.10.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 +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 +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 +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 +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/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 +323 -176
- package/dist/runtime/db.js +30 -6
- package/dist/runtime/main.js +375 -76
- package/dist/runtime/netproxy.js +30 -6
- package/dist/runtime/view.js +495 -146
- 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/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.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/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,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 {};
|
package/dist/host/web/server.js
CHANGED
|
@@ -5,9 +5,10 @@ import fs from 'node:fs';
|
|
|
5
5
|
import http from 'node:http';
|
|
6
6
|
import { WebSocketServer } from 'ws';
|
|
7
7
|
import { UserError, safeText } from '../../core/util/index.js';
|
|
8
|
-
import { MAX_CLIENT_MESSAGE_BYTES, MAX_COLS, MAX_ROWS, MIN_COLS, MIN_ROWS
|
|
8
|
+
import { MAX_CLIENT_MESSAGE_BYTES, MAX_COLS, MAX_ROWS, MIN_COLS, MIN_ROWS } from '../../shared/web/index.js';
|
|
9
9
|
import { cookieHeader, csrfOk, hostAllowed, originAllowed, WebAuth } from './auth.js';
|
|
10
10
|
import { TerminalBridge } from './bridge.js';
|
|
11
|
+
import { LiveHub } from './live.js';
|
|
11
12
|
import { RpcSession } from './rpc.js';
|
|
12
13
|
import { StaticFiles } from './static-files.js';
|
|
13
14
|
/** No inline script: the page and its script are files from this server. Inline styles are what xterm.js needs. */
|
|
@@ -16,6 +17,10 @@ export const CONTENT_SECURITY_POLICY = "default-src 'self'; script-src 'self'; s
|
|
|
16
17
|
const MAX_BODY_BYTES = 16 * 1024;
|
|
17
18
|
const MAX_TERMINALS = 16;
|
|
18
19
|
const MAX_RPC_SOCKETS = 8;
|
|
20
|
+
/** Every socket is pinged this often; one that has not answered the last ping by the next is dropped. */
|
|
21
|
+
const HEARTBEAT_MS = 20_000;
|
|
22
|
+
/** The server ends after this long with no request, no socket and no sandbox running (it starts again with one). */
|
|
23
|
+
export const IDLE_EXIT_MS = 30 * 60_000;
|
|
19
24
|
const SESSION_PATH = /^\/api\/sessions\/([A-Za-z0-9_-]{1,64})\/terminal$/;
|
|
20
25
|
export const RPC_PATH = '/api/rpc';
|
|
21
26
|
class HttpFailure extends Error {
|
|
@@ -35,15 +40,23 @@ export class WebServer {
|
|
|
35
40
|
});
|
|
36
41
|
auth;
|
|
37
42
|
files;
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
tracked = new Map();
|
|
44
|
+
/** One poll per live query for all browsers together. */
|
|
45
|
+
live;
|
|
40
46
|
actualPort = 0;
|
|
47
|
+
lastUsed = Date.now();
|
|
48
|
+
heartbeat;
|
|
49
|
+
idleCheck;
|
|
41
50
|
constructor(options) {
|
|
42
51
|
this.options = options;
|
|
43
52
|
this.auth = new WebAuth(options.token);
|
|
44
53
|
this.files = new StaticFiles(options.staticRoot);
|
|
54
|
+
this.live = options.rpc ? new LiveHub(options.rpc) : undefined;
|
|
45
55
|
this.server = http.createServer((req, res) => void this.handle(req, res));
|
|
46
|
-
this.server.on('upgrade', (req, socket, head) =>
|
|
56
|
+
this.server.on('upgrade', (req, socket, head) => {
|
|
57
|
+
this.lastUsed = Date.now();
|
|
58
|
+
this.upgrade(req, socket, head);
|
|
59
|
+
});
|
|
47
60
|
this.server.headersTimeout = 15000;
|
|
48
61
|
this.server.requestTimeout = 0; // creating a session waits for the agent to start
|
|
49
62
|
}
|
|
@@ -56,11 +69,75 @@ export class WebServer {
|
|
|
56
69
|
this.server.listen(this.options.port, '127.0.0.1', () => {
|
|
57
70
|
this.server.off('error', reject);
|
|
58
71
|
this.actualPort = this.server.address().port;
|
|
72
|
+
this.heartbeat = setInterval(() => this.beat(), this.options.heartbeatMs ?? HEARTBEAT_MS);
|
|
73
|
+
this.heartbeat.unref();
|
|
74
|
+
if (this.options.idleExitMs) {
|
|
75
|
+
this.idleCheck = setInterval(() => void this.checkIdle(), Math.min(60_000, this.options.idleExitMs));
|
|
76
|
+
this.idleCheck.unref();
|
|
77
|
+
}
|
|
59
78
|
resolve();
|
|
60
79
|
});
|
|
61
80
|
});
|
|
62
81
|
}
|
|
82
|
+
/** How many browser sockets are open, by kind. */
|
|
83
|
+
openSockets(kind) {
|
|
84
|
+
return [...this.tracked.values()].filter((t) => t.kind === kind).length;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Drops sockets that did not answer the last ping (a tab that went away without closing: sleep, a killed browser),
|
|
88
|
+
* so they never hold a slot or a session connection.
|
|
89
|
+
*/
|
|
90
|
+
beat() {
|
|
91
|
+
for (const [ws, t] of this.tracked) {
|
|
92
|
+
if (!t.alive) {
|
|
93
|
+
ws.terminate();
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
t.alive = false;
|
|
97
|
+
try {
|
|
98
|
+
ws.ping();
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
ws.terminate(); // the socket broke between the check and the ping
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Nothing has used the server for a while and no sandbox runs: it ends (the next launch starts it again). */
|
|
106
|
+
async checkIdle() {
|
|
107
|
+
const limit = this.options.idleExitMs ?? 0;
|
|
108
|
+
if (!limit || this.tracked.size > 0 || Date.now() - this.lastUsed < limit)
|
|
109
|
+
return;
|
|
110
|
+
try {
|
|
111
|
+
if ((await this.options.backend.info()).sandbox === 'running')
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return; // cannot tell: keep running
|
|
116
|
+
}
|
|
117
|
+
this.options.onStop?.();
|
|
118
|
+
}
|
|
119
|
+
/** Counts a new socket; at the limit, the oldest of its kind makes room (a new tab always works). */
|
|
120
|
+
track(ws, kind, limit) {
|
|
121
|
+
const same = [...this.tracked.entries()]
|
|
122
|
+
.filter(([, t]) => t.kind === kind)
|
|
123
|
+
.sort((a, b) => a[1].opened - b[1].opened);
|
|
124
|
+
for (const [old] of same.slice(0, Math.max(0, same.length - limit + 1)))
|
|
125
|
+
old.close(4000, 'replaced by a newer page');
|
|
126
|
+
this.tracked.set(ws, { kind, opened: Date.now(), alive: true });
|
|
127
|
+
ws.on('pong', () => {
|
|
128
|
+
const t = this.tracked.get(ws);
|
|
129
|
+
if (t)
|
|
130
|
+
t.alive = true;
|
|
131
|
+
});
|
|
132
|
+
ws.on('message', () => (this.lastUsed = Date.now()));
|
|
133
|
+
ws.on('close', () => {
|
|
134
|
+
this.tracked.delete(ws);
|
|
135
|
+
this.lastUsed = Date.now();
|
|
136
|
+
});
|
|
137
|
+
}
|
|
63
138
|
stop() {
|
|
139
|
+
clearInterval(this.heartbeat);
|
|
140
|
+
clearInterval(this.idleCheck);
|
|
64
141
|
for (const client of this.sockets.clients)
|
|
65
142
|
client.close(1001, 'server stopping');
|
|
66
143
|
this.server.closeAllConnections();
|
|
@@ -82,6 +159,7 @@ export class WebServer {
|
|
|
82
159
|
res.end(JSON.stringify(body));
|
|
83
160
|
}
|
|
84
161
|
async handle(req, res) {
|
|
162
|
+
this.lastUsed = Date.now();
|
|
85
163
|
try {
|
|
86
164
|
if (!hostAllowed(req.headers.host, this.actualPort))
|
|
87
165
|
throw new HttpFailure(421, 'Wrong host name.');
|
|
@@ -141,13 +219,6 @@ export class WebServer {
|
|
|
141
219
|
return this.json(res, 200, await backend.info());
|
|
142
220
|
if (pathname === '/api/sessions' && method === 'GET')
|
|
143
221
|
return this.json(res, 200, await backend.sessions());
|
|
144
|
-
if (pathname === '/api/sessions' && method === 'POST') {
|
|
145
|
-
this.requireSameSite(req);
|
|
146
|
-
const request = newSessionRequest(await this.body(req));
|
|
147
|
-
if (!request)
|
|
148
|
-
throw new HttpFailure(400, 'Choose an agent and a project.');
|
|
149
|
-
return this.json(res, 201, await backend.create(request));
|
|
150
|
-
}
|
|
151
222
|
if (pathname === '/api/stop' && method === 'POST') {
|
|
152
223
|
this.requireSameSite(req);
|
|
153
224
|
this.json(res, 200, { ok: true });
|
|
@@ -198,30 +269,24 @@ export class WebServer {
|
|
|
198
269
|
if (!this.auth.signedIn(req.headers.cookie))
|
|
199
270
|
return refuse('401 Unauthorized');
|
|
200
271
|
if (rpc)
|
|
201
|
-
return this.upgradeRpc(req, socket, head, rpc
|
|
272
|
+
return this.upgradeRpc(req, socket, head, rpc);
|
|
202
273
|
if (!match)
|
|
203
274
|
return refuse('404 Not Found');
|
|
204
|
-
if (this.terminals >= MAX_TERMINALS)
|
|
205
|
-
return refuse('503 Service Unavailable');
|
|
206
275
|
const endpoint = this.options.backend.endpoint(match[1]);
|
|
207
276
|
if (!endpoint)
|
|
208
277
|
return refuse('404 Not Found');
|
|
209
278
|
const cols = bounded(url.searchParams.get('cols'), MIN_COLS, MAX_COLS, 120);
|
|
210
279
|
const rows = bounded(url.searchParams.get('rows'), MIN_ROWS, MAX_ROWS, 30);
|
|
211
280
|
this.sockets.handleUpgrade(req, socket, head, (ws) => {
|
|
212
|
-
this.
|
|
213
|
-
ws.on('close', () => this.terminals--);
|
|
281
|
+
this.track(ws, 'terminal', MAX_TERMINALS);
|
|
214
282
|
void new TerminalBridge(ws, endpoint, cols, rows, this.options.dial).start();
|
|
215
283
|
});
|
|
216
284
|
}
|
|
217
285
|
/** The RPC socket (after the same Origin and cookie checks as a terminal). */
|
|
218
|
-
upgradeRpc(req, socket, head, rpc
|
|
219
|
-
if (this.rpcSockets >= MAX_RPC_SOCKETS)
|
|
220
|
-
return refuse('503 Service Unavailable');
|
|
286
|
+
upgradeRpc(req, socket, head, rpc) {
|
|
221
287
|
this.sockets.handleUpgrade(req, socket, head, (ws) => {
|
|
222
|
-
this.
|
|
223
|
-
ws
|
|
224
|
-
new RpcSession(ws, rpc).start();
|
|
288
|
+
this.track(ws, 'rpc', MAX_RPC_SOCKETS);
|
|
289
|
+
new RpcSession(ws, rpc, this.live).start();
|
|
225
290
|
});
|
|
226
291
|
}
|
|
227
292
|
}
|
|
@@ -229,10 +294,3 @@ function bounded(text, min, max, fallback) {
|
|
|
229
294
|
const n = Number(text);
|
|
230
295
|
return text !== null && Number.isInteger(n) && n >= min && n <= max ? n : fallback;
|
|
231
296
|
}
|
|
232
|
-
function newSessionRequest(body) {
|
|
233
|
-
if (typeof body !== 'object' || body === null)
|
|
234
|
-
return null;
|
|
235
|
-
const { harness, project } = body;
|
|
236
|
-
const ok = (v) => typeof v === 'string' && /^[A-Za-z0-9_.-]{1,64}$/.test(v);
|
|
237
|
-
return ok(harness) && ok(project) ? { harness, project } : null;
|
|
238
|
-
}
|
package/dist/host/web/state.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export declare const WebStateSchema: z.ZodObject<{
|
|
|
4
4
|
port: z.ZodNumber;
|
|
5
5
|
token: z.ZodString;
|
|
6
6
|
startedAt: z.ZodString;
|
|
7
|
+
version: z.ZodOptional<z.ZodString>;
|
|
7
8
|
}, z.core.$strict>;
|
|
8
9
|
export type WebState = z.output<typeof WebStateSchema>;
|
|
9
10
|
export declare class WebStore {
|
package/dist/host/web/state.js
CHANGED
|
@@ -10,6 +10,8 @@ export const WebStateSchema = z.strictObject({
|
|
|
10
10
|
port: z.number().int().min(1).max(65535),
|
|
11
11
|
token: z.string().regex(/^[0-9a-f]{64}$/),
|
|
12
12
|
startedAt: z.string().max(64),
|
|
13
|
+
/** The version that started it (missing: an older one). Another version is replaced, never reused. */
|
|
14
|
+
version: z.string().max(64).optional(),
|
|
13
15
|
});
|
|
14
16
|
export class WebStore {
|
|
15
17
|
storageRoot;
|