windows-development-sandbox 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config.example.json +10 -0
- package/dist/app/context.d.ts +8 -0
- package/dist/app/context.js +12 -0
- package/dist/app/features/defs.d.ts +322 -0
- package/dist/app/features/defs.js +28 -0
- package/dist/app/features/sandbox/index.d.ts +2 -0
- package/dist/app/features/sandbox/index.js +3 -0
- package/dist/app/features/sandbox/sandbox.def.d.ts +193 -0
- package/dist/app/features/sandbox/sandbox.def.js +245 -0
- package/dist/app/features/sandbox/sandbox.run.d.ts +72 -0
- package/dist/app/features/sandbox/sandbox.run.js +98 -0
- package/dist/app/features/sessions/index.d.ts +2 -0
- package/dist/app/features/sessions/index.js +3 -0
- package/dist/app/features/sessions/sessions.def.d.ts +141 -0
- package/dist/app/features/sessions/sessions.def.js +176 -0
- package/dist/app/features/sessions/sessions.run.d.ts +39 -0
- package/dist/app/features/sessions/sessions.run.js +58 -0
- package/dist/{cli/menu/settings/categories.d.ts → app/features/settings/categories.def.d.ts} +2 -0
- package/dist/{cli/menu/settings/categories.js → app/features/settings/categories.def.js} +24 -5
- package/dist/app/features/settings/index.d.ts +5 -0
- package/dist/app/features/settings/index.js +6 -0
- package/dist/{cli/menu/settings/config-file.d.ts → app/features/settings/settings-file.d.ts} +1 -1
- package/dist/{cli/menu/settings/config-file.js → app/features/settings/settings-file.js} +1 -1
- package/dist/app/features/settings/settings-rows.d.ts +35 -0
- package/dist/app/features/settings/settings-rows.js +94 -0
- package/dist/app/features/settings/settings.def.d.ts +157 -0
- package/dist/app/features/settings/settings.def.js +179 -0
- package/dist/app/features/settings/settings.run.d.ts +67 -0
- package/dist/app/features/settings/settings.run.js +84 -0
- package/dist/app/kernel/define.d.ts +143 -0
- package/dist/app/kernel/define.js +42 -0
- package/dist/app/kernel/errors.d.ts +34 -0
- package/dist/app/kernel/errors.js +45 -0
- package/dist/app/kernel/fields.d.ts +70 -0
- package/dist/app/kernel/fields.js +243 -0
- package/dist/app/kernel/index.d.ts +8 -0
- package/dist/app/kernel/index.js +10 -0
- package/dist/app/kernel/io.d.ts +193 -0
- package/dist/app/kernel/io.js +126 -0
- package/dist/app/kernel/rpc.d.ts +156 -0
- package/dist/app/kernel/rpc.js +47 -0
- package/dist/app/kernel/text.d.ts +6 -0
- package/dist/app/kernel/text.js +57 -0
- package/dist/app/kernel/view.d.ts +146 -0
- package/dist/app/kernel/view.js +28 -0
- package/dist/app/registry.d.ts +10 -0
- package/dist/app/registry.js +50 -0
- package/dist/cli/args.d.ts +5 -0
- package/dist/cli/args.js +6 -0
- package/dist/cli/commands/engine.d.ts +2 -0
- package/dist/cli/commands/engine.js +60 -0
- package/dist/cli/commands/index.d.ts +1 -1
- package/dist/cli/commands/index.js +1 -1
- package/dist/cli/commands/launch/launch.js +47 -7
- package/dist/cli/commands/launch/prepare.js +3 -3
- package/dist/cli/commands/launch/running.js +2 -1
- package/dist/cli/commands/launch/summary.d.ts +2 -0
- package/dist/cli/commands/maintenance/config-share.d.ts +12 -0
- package/dist/cli/commands/maintenance/config-share.js +80 -0
- package/dist/cli/commands/maintenance/config.js +8 -82
- package/dist/cli/commands/maintenance/logs.js +14 -0
- package/dist/cli/commands/registry.js +3 -7
- package/dist/cli/commands/repair/checks/contained.d.ts +2 -0
- package/dist/cli/commands/repair/checks/contained.js +139 -0
- package/dist/cli/commands/repair/checks/hcs.d.ts +7 -0
- package/dist/cli/commands/repair/checks/hcs.js +28 -0
- package/dist/cli/commands/repair/checks/index.js +4 -0
- package/dist/cli/commands/repair/checks/pc.js +32 -1
- package/dist/cli/commands/repair/checks/sandbox.js +16 -1
- package/dist/cli/commands/repair/checks/vpn.js +34 -9
- package/dist/cli/commands/repair/env.js +2 -1
- package/dist/cli/commands/sessions/index.d.ts +0 -4
- package/dist/cli/commands/sessions/index.js +3 -7
- package/dist/cli/commands/shared-use.d.ts +5 -2
- package/dist/cli/commands/shared-use.js +11 -8
- package/dist/cli/commands/storage/contained.d.ts +3 -0
- package/dist/cli/commands/storage/contained.js +179 -0
- package/dist/cli/commands/storage/offer.js +7 -1
- package/dist/cli/commands/storage/storage.js +10 -2
- package/dist/cli/commands/user-config.js +1 -1
- package/dist/cli/commands/vpn.js +19 -3
- package/dist/cli/commands/web.js +2 -0
- package/dist/cli/index.js +2 -1
- package/dist/cli/manifest.d.ts +72 -104
- package/dist/cli/manifest.js +64 -39
- package/dist/cli/menu/add-agent.d.ts +1 -1
- package/dist/cli/menu/add-agent.js +1 -1
- package/dist/cli/menu/apps.d.ts +7 -1
- package/dist/cli/menu/apps.js +36 -0
- package/dist/cli/menu/command-fields.d.ts +1 -1
- package/dist/cli/menu/command-form.js +2 -2
- package/dist/cli/menu/io-menu.d.ts +29 -0
- package/dist/cli/menu/io-menu.js +126 -0
- package/dist/cli/menu/live.d.ts +27 -0
- package/dist/cli/menu/live.js +69 -0
- package/dist/cli/menu/memory.d.ts +19 -0
- package/dist/cli/menu/memory.js +53 -0
- package/dist/cli/menu/menu.d.ts +10 -1
- package/dist/cli/menu/menu.js +27 -9
- package/dist/cli/menu/pages/agents-page.d.ts +3 -1
- package/dist/cli/menu/pages/agents-page.js +12 -5
- package/dist/cli/menu/pages/apps-page.d.ts +3 -1
- package/dist/cli/menu/pages/apps-page.js +19 -2
- package/dist/cli/menu/pages/list-page.d.ts +27 -0
- package/dist/cli/menu/pages/list-page.js +109 -7
- package/dist/cli/menu/pages/maintenance-page.d.ts +0 -5
- package/dist/cli/menu/pages/maintenance-page.js +17 -19
- package/dist/cli/menu/pages/sandbox-page.d.ts +4 -6
- package/dist/cli/menu/pages/sandbox-page.js +26 -118
- package/dist/cli/menu/pages/scopes.d.ts +1 -1
- package/dist/cli/menu/pages/sessions-page.d.ts +5 -11
- package/dist/cli/menu/pages/sessions-page.js +19 -88
- package/dist/cli/menu/pages/settings-page.d.ts +2 -10
- package/dist/cli/menu/pages/settings-page.js +64 -94
- package/dist/cli/menu/render-view.d.ts +28 -0
- package/dist/cli/menu/render-view.js +112 -0
- package/dist/cli/menu/settings/{path-kind.d.ts → ask-path.d.ts} +1 -7
- package/dist/cli/menu/settings/ask-path.js +14 -0
- package/dist/cli/menu/settings/collections/collection-editor.d.ts +1 -1
- package/dist/cli/menu/settings/collections/string-list-editor.d.ts +1 -2
- package/dist/cli/menu/settings/collections/string-list-editor.js +1 -1
- package/dist/cli/menu/settings/draft.d.ts +1 -1
- package/dist/cli/menu/settings/format.d.ts +1 -3
- package/dist/cli/menu/settings/format.js +6 -36
- package/dist/cli/menu/settings/index.d.ts +1 -3
- package/dist/cli/menu/settings/index.js +3 -3
- package/dist/cli/menu/settings/object-editor.d.ts +1 -1
- package/dist/cli/menu/settings/share.d.ts +36 -0
- package/dist/cli/menu/settings/share.js +131 -0
- package/dist/cli/menu/settings/value-editor.d.ts +5 -1
- package/dist/cli/menu/settings/value-editor.js +57 -54
- package/dist/cli/menu/several.js +1 -1
- package/dist/cli/menu/share-settings.d.ts +21 -0
- package/dist/cli/menu/share-settings.js +112 -0
- package/dist/cli/menu/shell.d.ts +21 -5
- package/dist/cli/menu/shell.js +31 -6
- package/dist/cli/menu/views.d.ts +7 -0
- package/dist/cli/menu/views.js +11 -0
- package/dist/cli/menu/vpn-login.js +8 -0
- package/dist/cli/operations/handler.d.ts +6 -0
- package/dist/cli/operations/handler.js +61 -0
- package/dist/cli/operations/index.d.ts +4 -0
- package/dist/cli/operations/index.js +5 -0
- package/dist/cli/operations/input.d.ts +6 -0
- package/dist/cli/operations/input.js +37 -0
- package/dist/cli/operations/io-cli.d.ts +7 -0
- package/dist/cli/operations/io-cli.js +62 -0
- package/dist/cli/operations/spec.d.ts +18 -0
- package/dist/cli/operations/spec.js +108 -0
- package/dist/cli/output/colors.d.ts +2 -2
- package/dist/cli/output/colors.js +2 -11
- package/dist/cli/output/index.d.ts +1 -0
- package/dist/cli/output/index.js +1 -0
- package/dist/cli/output/print.d.ts +3 -1
- package/dist/cli/output/print.js +7 -3
- package/dist/cli/output/progress.d.ts +15 -0
- package/dist/cli/output/progress.js +44 -0
- package/dist/cli/output/render-view.d.ts +4 -0
- package/dist/cli/output/render-view.js +63 -0
- package/dist/cli/services/agent-commands-service.d.ts +2 -0
- package/dist/cli/services/agent-commands-service.js +11 -2
- package/dist/cli/services/logs-service.js +4 -3
- package/dist/cli/tui/app.d.ts +6 -1
- package/dist/cli/tui/app.js +26 -4
- package/dist/cli/tui/list/list-screen.d.ts +14 -0
- package/dist/cli/tui/list/list-screen.js +64 -36
- package/dist/cli/tui/list/list-types.d.ts +2 -0
- package/dist/cli/tui/terminal.d.ts +7 -0
- package/dist/cli/tui/terminal.js +30 -10
- package/dist/core/config/defaults.js +8 -0
- package/dist/core/schema/config.d.ts +13 -0
- package/dist/core/schema/config.js +16 -1
- package/dist/core/schema/harness.d.ts +1 -0
- package/dist/core/schema/harness.js +5 -0
- package/dist/core/schema/sandbox.d.ts +6 -0
- package/dist/core/schema/sandbox.js +20 -3
- package/dist/core/schema/vpn.d.ts +2 -1
- package/dist/core/schema/vpn.js +3 -3
- package/dist/core/types.d.ts +23 -5
- package/dist/core/util/fs.d.ts +2 -0
- package/dist/core/util/fs.js +9 -0
- package/dist/core/util/index.d.ts +1 -1
- package/dist/core/util/index.js +1 -1
- package/dist/guest/agents/agent-files.d.ts +5 -0
- package/dist/guest/agents/agent-files.js +4 -0
- package/dist/guest/agents/local-state.d.ts +2 -0
- package/dist/guest/agents/local-state.js +21 -1
- package/dist/guest/boot/boot.js +2 -1
- package/dist/guest/daemon/daemon.js +4 -0
- package/dist/guest/daemon/sessions.d.ts +5 -0
- package/dist/guest/daemon/sessions.js +17 -3
- package/dist/guest/entries/daemon.js +3 -0
- package/dist/guest/governor/decide.d.ts +61 -0
- package/dist/guest/governor/decide.js +182 -0
- package/dist/guest/governor/governor.d.ts +42 -0
- package/dist/guest/governor/governor.js +214 -0
- package/dist/guest/governor/index.d.ts +2 -0
- package/dist/guest/governor/index.js +2 -0
- package/dist/guest/runtime/csc.d.ts +1 -0
- package/dist/guest/runtime/csc.js +1 -0
- package/dist/guest/sessions/viewer.js +3 -1
- package/dist/guest/toolchains/native/rust-extras.js +4 -2
- package/dist/guest/vpn/vpn.js +69 -21
- package/dist/guest/vpn/wireguard.d.ts +32 -0
- package/dist/guest/vpn/wireguard.js +132 -0
- package/dist/host/agent-commands/commands.d.ts +4 -0
- package/dist/host/agent-commands/commands.js +31 -7
- package/dist/host/audit/log.d.ts +1 -1
- package/dist/host/channel/attach.d.ts +3 -1
- package/dist/host/channel/attach.js +5 -2
- package/dist/host/channel/channel.d.ts +2 -0
- package/dist/host/channel/channel.js +4 -0
- package/dist/host/engines/engine.d.ts +12 -0
- package/dist/host/engines/engine.js +1 -0
- package/dist/host/engines/hcs/check.d.ts +25 -0
- package/dist/host/engines/hcs/check.js +95 -0
- package/dist/host/engines/hcs/documents.d.ts +44 -0
- package/dist/host/engines/hcs/documents.js +155 -0
- package/dist/host/engines/hcs/engine.d.ts +39 -0
- package/dist/host/engines/hcs/engine.js +171 -0
- package/dist/host/engines/hcs/helper.d.ts +43 -0
- package/dist/host/engines/hcs/helper.js +173 -0
- package/dist/host/engines/hcs/images.d.ts +37 -0
- package/dist/host/engines/hcs/images.js +152 -0
- package/dist/host/engines/index.d.ts +7 -0
- package/dist/host/engines/index.js +5 -0
- package/dist/host/engines/registry.d.ts +16 -0
- package/dist/host/engines/registry.js +30 -0
- package/dist/host/engines/types.d.ts +58 -0
- package/dist/host/engines/types.js +1 -0
- package/dist/host/engines/windows-sandbox/engine.d.ts +16 -0
- package/dist/host/engines/windows-sandbox/engine.js +63 -0
- package/dist/host/engines/windows-sandbox/wsb-text.d.ts +2 -0
- package/dist/host/engines/windows-sandbox/wsb-text.js +29 -0
- package/dist/host/launch/generate.d.ts +5 -1
- package/dist/host/launch/generate.js +50 -8
- package/dist/host/launch/index.d.ts +3 -1
- package/dist/host/launch/index.js +3 -1
- package/dist/host/launch/launch-plan.d.ts +13 -0
- package/dist/host/launch/launch-plan.js +26 -0
- package/dist/host/launch/mappings.js +2 -1
- package/dist/host/launch/risk-notes.d.ts +10 -0
- package/dist/host/launch/risk-notes.js +18 -0
- package/dist/host/launch/session-file.d.ts +2 -0
- package/dist/host/launch/session-file.js +7 -1
- package/dist/host/launch/wsb-file.d.ts +28 -4
- package/dist/host/launch/wsb-file.js +61 -42
- package/dist/host/life/crash-report.d.ts +27 -1
- package/dist/host/life/crash-report.js +173 -7
- package/dist/host/life/host-memory.d.ts +40 -0
- package/dist/host/life/host-memory.js +137 -0
- package/dist/host/life/index.d.ts +2 -0
- package/dist/host/life/index.js +2 -0
- package/dist/host/life/stop-events.d.ts +2 -0
- package/dist/host/life/stop-events.js +20 -3
- package/dist/host/life/stop-warning.js +1 -1
- package/dist/host/life/watchdog.d.ts +54 -0
- package/dist/host/life/watchdog.js +108 -0
- package/dist/host/native.d.ts +3 -2
- package/dist/host/native.js +22 -3
- package/dist/host/profile/claude-login.js +3 -2
- package/dist/host/sandbox/contained-deps.d.ts +2 -0
- package/dist/host/sandbox/contained-deps.js +17 -0
- package/dist/host/sandbox/index.d.ts +2 -0
- package/dist/host/sandbox/index.js +2 -0
- package/dist/host/sandbox/platform.d.ts +2 -0
- package/dist/host/sandbox/platform.js +7 -7
- package/dist/host/sandbox/tasklist.d.ts +13 -0
- package/dist/host/sandbox/tasklist.js +47 -0
- package/dist/host/secrets/launch-secrets.js +2 -1
- package/dist/host/sharing/current.js +2 -6
- package/dist/host/storage/contained/check.d.ts +9 -0
- package/dist/host/storage/contained/check.js +37 -0
- package/dist/host/storage/contained/contained-storage.d.ts +66 -0
- package/dist/host/storage/contained/contained-storage.js +168 -0
- package/dist/host/storage/contained/disk-port.d.ts +43 -0
- package/dist/host/storage/contained/disk-port.js +122 -0
- package/dist/host/storage/contained/index.d.ts +9 -0
- package/dist/host/storage/contained/index.js +8 -0
- package/dist/host/storage/contained/layout.d.ts +28 -0
- package/dist/host/storage/contained/layout.js +45 -0
- package/dist/host/storage/contained/resolve.d.ts +4 -0
- package/dist/host/storage/contained/resolve.js +30 -0
- package/dist/host/storage/contained/setting.d.ts +1 -0
- package/dist/host/storage/contained/setting.js +5 -0
- package/dist/host/storage/contained/state.d.ts +89 -0
- package/dist/host/storage/contained/state.js +166 -0
- package/dist/host/storage/contained/transfer.d.ts +24 -0
- package/dist/host/storage/contained/transfer.js +121 -0
- package/dist/host/storage/index.d.ts +2 -0
- package/dist/host/storage/index.js +2 -0
- package/dist/host/storage/providers/contained.d.ts +15 -0
- package/dist/host/storage/providers/contained.js +39 -0
- package/dist/host/storage/providers/folder.d.ts +12 -0
- package/dist/host/storage/providers/folder.js +20 -0
- package/dist/host/storage/providers/index.d.ts +4 -0
- package/dist/host/storage/providers/index.js +3 -0
- package/dist/host/storage/providers/registry.d.ts +9 -0
- package/dist/host/storage/providers/registry.js +14 -0
- package/dist/host/storage/providers/types.d.ts +33 -0
- package/dist/host/storage/providers/types.js +1 -0
- package/dist/host/tools/conpty.js +3 -2
- package/dist/host/tools/windows-terminal.js +8 -7
- package/dist/host/vpn/index.d.ts +1 -1
- package/dist/host/vpn/index.js +1 -1
- package/dist/host/vpn/prepare.d.ts +2 -0
- package/dist/host/vpn/prepare.js +63 -2
- package/dist/host/web/browser.d.ts +2 -0
- package/dist/host/web/browser.js +14 -1
- package/dist/host/web/index.d.ts +3 -2
- package/dist/host/web/index.js +3 -2
- package/dist/host/web/rpc.d.ts +24 -0
- package/dist/host/web/rpc.js +149 -0
- package/dist/host/web/runner.d.ts +2 -0
- package/dist/host/web/runner.js +1 -2
- package/dist/host/web/server.d.ts +7 -0
- package/dist/host/web/server.js +23 -3
- package/dist/runtime/agent.js +69 -8
- package/dist/runtime/daemon.js +1058 -624
- package/dist/runtime/db.js +69 -8
- package/dist/runtime/main.js +624 -182
- package/dist/runtime/netproxy.js +69 -8
- package/dist/runtime/view.js +87 -13
- package/dist/selftest/agent-paths.test.js +11 -1
- package/dist/selftest/app.test.d.ts +2 -0
- package/dist/selftest/app.test.js +324 -0
- package/dist/selftest/channel.test.js +13 -0
- package/dist/selftest/cli.test.js +2 -1
- package/dist/selftest/config.test.js +59 -0
- package/dist/selftest/contained.test.d.ts +2 -0
- package/dist/selftest/contained.test.js +544 -0
- package/dist/selftest/credentials.test.js +1 -1
- package/dist/selftest/engine.test.d.ts +2 -0
- package/dist/selftest/engine.test.js +127 -0
- package/dist/selftest/hcs.test.d.ts +2 -0
- package/dist/selftest/hcs.test.js +482 -0
- package/dist/selftest/heavy-load.test.d.ts +2 -0
- package/dist/selftest/heavy-load.test.js +329 -0
- package/dist/selftest/index.js +12 -0
- package/dist/selftest/layout.test.js +12 -0
- package/dist/selftest/menu-perf.test.d.ts +2 -0
- package/dist/selftest/menu-perf.test.js +248 -0
- package/dist/selftest/menu-screens.js +4 -2
- package/dist/selftest/menu.test.js +36 -8
- package/dist/selftest/support/engine-cases.d.ts +8 -0
- package/dist/selftest/support/engine-cases.js +65 -0
- package/dist/selftest/support/engine-golden.d.ts +3 -0
- package/dist/selftest/support/engine-golden.js +66 -0
- package/dist/selftest/support/fixtures.d.ts +7 -1
- package/dist/selftest/support/hcs-probes.d.ts +17 -0
- package/dist/selftest/support/hcs-probes.js +41 -0
- package/dist/selftest/vpn.test.js +136 -3
- package/dist/selftest/web.test.js +101 -3
- package/dist/shared/channel/run-dir.d.ts +4 -1
- package/dist/shared/channel/run-dir.js +7 -1
- package/dist/shared/channel/schema.d.ts +35 -1
- package/dist/shared/channel/schema.js +27 -0
- package/dist/shared/channel/secrets.d.ts +1 -0
- package/dist/shared/channel/secrets.js +6 -0
- package/dist/shared/memory/index.d.ts +1 -0
- package/dist/shared/memory/index.js +1 -0
- package/dist/shared/memory/policy.d.ts +33 -0
- package/dist/shared/memory/policy.js +55 -0
- package/dist/shared/pty/output-filter.d.ts +14 -1
- package/dist/shared/pty/output-filter.js +22 -4
- package/dist/shared/pty/terminal.d.ts +4 -1
- package/dist/shared/pty/terminal.js +4 -2
- package/dist/shared/system.d.ts +1 -0
- package/dist/shared/system.js +1 -0
- package/dist/shared/vpn/download.d.ts +16 -0
- package/dist/shared/vpn/download.js +16 -0
- package/dist/shared/vpn/wireguard.d.ts +51 -0
- package/dist/shared/vpn/wireguard.js +227 -0
- package/dist/sync/sync-cycle.js +5 -5
- package/dist/sync/watcher.d.ts +13 -0
- package/dist/sync/watcher.js +92 -3
- package/dist/web/index.html +4 -3
- package/dist/web/main-NRRS5O6Z.js +157 -0
- package/dist/web/main-T5DMCHYV.css +1 -0
- package/native/csharp/HcsHost/AssemblyInfo.cs +14 -0
- package/native/csharp/HcsHost/Guard.cs +100 -0
- package/native/csharp/HcsHost/Hcs.cs +256 -0
- package/native/csharp/HcsHost/Json.cs +78 -0
- package/native/csharp/HcsHost/Program.cs +185 -0
- package/native/csharp/HcsHost/VirtualDisk.cs +95 -0
- package/native/csharp/HostTools/AssemblyInfo.cs +14 -0
- package/native/csharp/HostTools/MemoryStatus.cs +107 -0
- package/native/csharp/HostTools/Program.cs +58 -17
- package/native/scripts/check-contained.ps1 +28 -0
- package/native/scripts/contained-common.ps1 +241 -0
- package/native/scripts/dismount-contained.ps1 +66 -0
- package/native/scripts/memory-status.ps1 +34 -0
- package/native/scripts/mount-contained.ps1 +139 -0
- package/native/scripts/run-elevated.ps1 +17 -4
- package/native/scripts/stop-events.ps1 +23 -8
- package/package.json +9 -2
- package/sandbox/csharp/MemGov.cs +398 -0
- package/sandbox/csharp/PtyHost.cs +111 -5
- package/schemas/config.schema.json +60 -6
- package/schemas/sandbox-config.schema.json +3 -3
- package/dist/cli/commands/sessions/control.d.ts +0 -4
- package/dist/cli/commands/sessions/control.js +0 -66
- package/dist/cli/commands/sessions/list.d.ts +0 -2
- package/dist/cli/commands/sessions/list.js +0 -48
- package/dist/cli/commands/sessions/status.d.ts +0 -4
- package/dist/cli/commands/sessions/status.js +0 -114
- package/dist/cli/commands/sessions/window.d.ts +0 -3
- package/dist/cli/commands/sessions/window.js +0 -18
- package/dist/cli/menu/settings/path-kind.js +0 -44
- package/dist/web/main-CXESL347.js +0 -45
- package/dist/web/main-VLR7DVQ4.css +0 -1
- /package/dist/{cli/menu/settings → app/kernel}/schema-node.d.ts +0 -0
- /package/dist/{cli/menu/settings → app/kernel}/schema-node.js +0 -0
|
@@ -8,11 +8,12 @@ import { ConfigSchema, LocalOverridesSchema } from '../core/schema/index.js';
|
|
|
8
8
|
import { SecretStore } from '../host/secrets/index.js';
|
|
9
9
|
import { clearSecrets } from '../host/secrets/index.js';
|
|
10
10
|
import { generateSandbox } from '../host/launch/index.js';
|
|
11
|
-
import { findCountry, nordProfile, prepareVpn, recommendedServer, VpnLogin, vpnLine, vpnNotes, } from '../host/vpn/index.js';
|
|
11
|
+
import { findCountry, nordProfile, prepareVpn, wireguardFileProblem, recommendedServer, VpnLogin, vpnLine, vpnNotes, } from '../host/vpn/index.js';
|
|
12
12
|
import { RunDir } from '../shared/channel/index.js';
|
|
13
|
-
import { OPENVPN_MSI } from '../shared/vpn/download.js';
|
|
13
|
+
import { OPENVPN_MSI, WIREGUARD_MSI } from '../shared/vpn/download.js';
|
|
14
14
|
import { cleanServers, killSwitchCommands, removeKillSwitchCommands, tunnelAddressFromLog, tunnelCommands, } from '../shared/vpn/kill-switch.js';
|
|
15
15
|
import { assertSafeOvpn, inspectOvpn, PROGRAM_DIRECTIVES } from '../shared/vpn/ovpn.js';
|
|
16
|
+
import { assertSafeWireguard, handshakeDone, inspectWireguard, renderWireguard, serviceRunning, WIREGUARD_PROGRAM_KEYS, wireguardEndpoints, wireguardTunnelAddress, } from '../shared/vpn/wireguard.js';
|
|
16
17
|
const PROFILE = [
|
|
17
18
|
'client',
|
|
18
19
|
'dev tun',
|
|
@@ -51,6 +52,24 @@ const PROFILE = [
|
|
|
51
52
|
'</tls-auth>',
|
|
52
53
|
'',
|
|
53
54
|
].join('\r\n');
|
|
55
|
+
const PRIVATE_KEY = 'PrivPlanted' + 'A'.repeat(32) + '=';
|
|
56
|
+
const PUBLIC_KEY = 'PubPlanted' + 'B'.repeat(33) + '=';
|
|
57
|
+
const PRESHARED_KEY = 'PskPlanted' + 'C'.repeat(33) + '=';
|
|
58
|
+
const WG_CONFIG = [
|
|
59
|
+
'[Interface]',
|
|
60
|
+
'PrivateKey = ' + PRIVATE_KEY,
|
|
61
|
+
'Address = 10.64.0.2/32, fd00::2/128',
|
|
62
|
+
'DNS = 10.64.0.1',
|
|
63
|
+
'MTU = 1380',
|
|
64
|
+
'',
|
|
65
|
+
'[Peer] # the office',
|
|
66
|
+
'PublicKey = ' + PUBLIC_KEY,
|
|
67
|
+
'PresharedKey = ' + PRESHARED_KEY,
|
|
68
|
+
'AllowedIPs = 0.0.0.0/0, ::/0',
|
|
69
|
+
'Endpoint = vpn.example.com:51820',
|
|
70
|
+
'PersistentKeepalive = 25',
|
|
71
|
+
'',
|
|
72
|
+
].join('\r\n');
|
|
54
73
|
const PASSWORD = 'vpn-pass-planted-91c2d7';
|
|
55
74
|
const USERNAME = 'vpn-user-planted-5e8a40';
|
|
56
75
|
export const vpnSuite = {
|
|
@@ -206,7 +225,7 @@ export const vpnSuite = {
|
|
|
206
225
|
});
|
|
207
226
|
t.check(prepared !== null &&
|
|
208
227
|
prepared.session.server === 'uk2201.nordvpn.com' &&
|
|
209
|
-
prepared.credentials
|
|
228
|
+
prepared.credentials?.password === PASSWORD, 'a launch picks the server, checks its profile and holds the login in memory');
|
|
210
229
|
await t.throws(() => prepareVpn({
|
|
211
230
|
config: run.config,
|
|
212
231
|
storageRoot: config.storage.root,
|
|
@@ -275,5 +294,119 @@ export const vpnSuite = {
|
|
|
275
294
|
killSwitch: true,
|
|
276
295
|
updatedAt: 'n',
|
|
277
296
|
}).text.includes('\u001b'), 'text the sandbox wrote loses its escape sequences before it is printed');
|
|
297
|
+
t.section('WireGuard: your own config is untrusted data');
|
|
298
|
+
const wg = inspectWireguard(WG_CONFIG);
|
|
299
|
+
t.check(wg.ok &&
|
|
300
|
+
wg.config.peers.length === 1 &&
|
|
301
|
+
wg.config.peers[0].endpointHost === 'vpn.example.com' &&
|
|
302
|
+
wg.config.peers[0].endpointPort === 51820 &&
|
|
303
|
+
wireguardTunnelAddress(wg.config) === '10.64.0.2' &&
|
|
304
|
+
wireguardEndpoints(wg.config)[0].port === 51820, 'a plain config is accepted and says where it connects and which address the tunnel has', JSON.stringify(wg));
|
|
305
|
+
for (const key of WIREGUARD_PROGRAM_KEYS) {
|
|
306
|
+
const bad = inspectWireguard(WG_CONFIG.replace('MTU = 1380', key + ' = calc.exe'));
|
|
307
|
+
t.check(!bad.ok && bad.problems.some((p) => p.toLowerCase().includes(key)), key + ' is refused by name', JSON.stringify(bad));
|
|
308
|
+
}
|
|
309
|
+
const wgOdd = [
|
|
310
|
+
['FwMark = 7', 'an unknown [Interface] key is refused, not passed on'],
|
|
311
|
+
['postup = calc', 'lower case does not hide PostUp'],
|
|
312
|
+
['[Socks]', 'an unknown section is refused'],
|
|
313
|
+
['DNS = evil.example.com', 'a DNS name (a search domain) is refused: only addresses'],
|
|
314
|
+
['MTU = 99999', 'an MTU out of range is refused'],
|
|
315
|
+
['PrivateKey = ' + PRIVATE_KEY, 'a second PrivateKey is refused'],
|
|
316
|
+
];
|
|
317
|
+
for (const [line, why] of wgOdd) {
|
|
318
|
+
const text = line.startsWith('[') ? WG_CONFIG + line + '\r\n' : WG_CONFIG.replace('MTU = 1380', line);
|
|
319
|
+
t.check(!inspectWireguard(text).ok, why, line);
|
|
320
|
+
}
|
|
321
|
+
t.check(!inspectWireguard(WG_CONFIG.replace(PRIVATE_KEY, 'tooshort')).ok &&
|
|
322
|
+
!inspectWireguard(WG_CONFIG.replace('10.64.0.2/32, fd00::2/128', 'fd00::2/128')).ok &&
|
|
323
|
+
!inspectWireguard(WG_CONFIG.replace('vpn.example.com:51820', '[2001:db8::1]:51820')).ok &&
|
|
324
|
+
!inspectWireguard(WG_CONFIG.replace('Endpoint = vpn.example.com:51820', '')).ok &&
|
|
325
|
+
!inspectWireguard(WG_CONFIG.replace('vpn.example.com:51820', 'vpn.example.com:0')).ok &&
|
|
326
|
+
!inspectWireguard(WG_CONFIG.replace('[Peer] # the office', '')).ok &&
|
|
327
|
+
!inspectWireguard('x'.repeat(20 * 1024)).ok &&
|
|
328
|
+
!inspectWireguard(WG_CONFIG.replace('AllowedIPs = 0.0.0.0/0, ::/0', 'AllowedIPs = 300.0.0.0/0')).ok, 'a bad key, an IPv6-only address or endpoint, a missing endpoint or peer, a bad port, a huge file and a bad range are refused');
|
|
329
|
+
t.check(inspectWireguard(WG_CONFIG.replace('MTU = 1380', '# PostUp = calc\r\nMTU = 1380 # PreUp = calc')).ok, 'comments are comments (a commented-out PostUp is not a key)');
|
|
330
|
+
const refusals = [
|
|
331
|
+
WG_CONFIG.replace('MTU = 1380', 'PostUp = ' + PRIVATE_KEY),
|
|
332
|
+
WG_CONFIG.replace('MTU = 1380', 'Bogus = ' + PRESHARED_KEY),
|
|
333
|
+
WG_CONFIG.replace(PUBLIC_KEY, 'bad' + PRESHARED_KEY),
|
|
334
|
+
WG_CONFIG.replace(PRIVATE_KEY, PRIVATE_KEY + 'x'),
|
|
335
|
+
].map(inspectWireguard);
|
|
336
|
+
t.check(refusals.every((r) => !r.ok && !JSON.stringify(r).includes('PrivPlanted') && !JSON.stringify(r).includes('PskPlanted')), 'what a refusal says never contains a key');
|
|
337
|
+
await t.throws(() => assertSafeWireguard(WG_CONFIG.replace('MTU = 1380', 'PostUp = calc'), 'office.conf'), 'office.conf was refused', 'a refused config names its file and the reason');
|
|
338
|
+
if (wg.ok) {
|
|
339
|
+
const text = renderWireguard(wg.config, { 0: '203.0.113.7' });
|
|
340
|
+
t.check(text.includes('Endpoint = 203.0.113.7:51820') &&
|
|
341
|
+
!text.includes('vpn.example.com') &&
|
|
342
|
+
!text.includes('the office') &&
|
|
343
|
+
inspectWireguard(text).ok, 'the config handed on is written again from the checked values, with the endpoint as an address, and is itself valid');
|
|
344
|
+
}
|
|
345
|
+
const wgRules = killSwitchCommands([{ ip: '203.0.113.7', port: 51820, proto: 'udp' }], null).map((c) => c.join(' '));
|
|
346
|
+
t.check(wgRules.some((c) => c.includes('name=AgentSandbox-VPN-Server-1') &&
|
|
347
|
+
c.includes('protocol=udp') &&
|
|
348
|
+
c.includes('remoteip=203.0.113.7') &&
|
|
349
|
+
c.includes('remoteport=51820')) && wgRules.findIndex((c) => c.includes('blockoutbound')) < wgRules.findIndex((c) => c.includes('action=allow')), 'the kill switch blocks first and lets only the endpoint host:port through, over udp');
|
|
350
|
+
t.check(tunnelCommands('10.64.0.2').some((c) => c.join(' ').includes('localip=10.64.0.2') && c[2] === 'add'), "the tunnel's own address (from Address) is let through once the service runs");
|
|
351
|
+
t.check(WIREGUARD_MSI.x64.sha256.length === 64 &&
|
|
352
|
+
WIREGUARD_MSI.arm64.sha256.length === 64 &&
|
|
353
|
+
WIREGUARD_MSI.base.startsWith('https://download.wireguard.com/') &&
|
|
354
|
+
serviceRunning('STATE : 4 RUNNING') &&
|
|
355
|
+
!serviceRunning('STATE : 1 STOPPED') &&
|
|
356
|
+
handshakeDone('abc=\t1700000000\r\n') &&
|
|
357
|
+
!handshakeDone('abc=\t0\r\n') &&
|
|
358
|
+
!handshakeDone(''), 'the WireGuard download is pinned by checksum; the service state and handshake are read from the tools output');
|
|
359
|
+
t.section('WireGuard: the keys never land in session.json or the launch folder');
|
|
360
|
+
const wgDir = t.dir('wg-config');
|
|
361
|
+
const wgFile = path.join(wgDir, 'office.conf');
|
|
362
|
+
fs.writeFileSync(wgFile, WG_CONFIG);
|
|
363
|
+
const wgSettings = f.makeConfig();
|
|
364
|
+
wgSettings.vpn = { ...wgSettings.vpn, enabled: true, provider: 'wireguard-file', configFile: wgFile };
|
|
365
|
+
const wgRun = await f.run({}, wgSettings);
|
|
366
|
+
const wgPrepared = await prepareVpn({
|
|
367
|
+
config: wgRun.config,
|
|
368
|
+
storageRoot: wgSettings.storage.root,
|
|
369
|
+
login: new VpnLogin(t.dir('wg-nologin'), SecretStore.memory()),
|
|
370
|
+
});
|
|
371
|
+
t.check(wgPrepared !== null &&
|
|
372
|
+
wgPrepared.session.server === 'office.conf' &&
|
|
373
|
+
wgPrepared.session.profile === undefined &&
|
|
374
|
+
wgPrepared.credentials === undefined &&
|
|
375
|
+
(wgPrepared.wireguardConfig ?? '').includes(PRIVATE_KEY), 'a launch needs no saved login; it holds the checked config (with the private key) in memory only');
|
|
376
|
+
fs.writeFileSync(wgFile, WG_CONFIG.replace('MTU = 1380', 'PostUp = calc.exe'));
|
|
377
|
+
await t.throws(() => prepareVpn({ config: wgRun.config, storageRoot: wgSettings.storage.root }), 'refused', 'a config with PostUp stops the launch before anything is written');
|
|
378
|
+
t.check(wireguardFileProblem(wgFile, wgDir)?.includes('refused') === true &&
|
|
379
|
+
wireguardFileProblem(path.join(wgDir, 'missing.conf'), wgDir) !== null &&
|
|
380
|
+
!(wireguardFileProblem(wgFile, wgDir) ?? '').includes('PrivPlanted'), 'doctor says a bad or missing config is a problem, without a key');
|
|
381
|
+
fs.writeFileSync(wgFile, WG_CONFIG);
|
|
382
|
+
t.check(wireguardFileProblem('office.conf', wgDir) === null, 'a good config, even by a relative path, is fine');
|
|
383
|
+
clearSecrets(wgSettings.storage.root, wgRun.dataPath);
|
|
384
|
+
const wgLaunch = generateSandbox({
|
|
385
|
+
packageRoot: t.packageRoot,
|
|
386
|
+
run: wgRun,
|
|
387
|
+
harnessArgs: [],
|
|
388
|
+
probe: f.probe,
|
|
389
|
+
pcLanguages: ['python', 'rust', 'cpp', 'dotnet'],
|
|
390
|
+
...(wgPrepared ? { vpn: wgPrepared } : {}),
|
|
391
|
+
});
|
|
392
|
+
const wgSession = fs.readFileSync(wgLaunch.sessionPath, 'utf8');
|
|
393
|
+
const wgAll = [];
|
|
394
|
+
const wgWalk = (d) => {
|
|
395
|
+
for (const e of fs.readdirSync(d, { withFileTypes: true })) {
|
|
396
|
+
const p = path.join(d, e.name);
|
|
397
|
+
if (e.isDirectory())
|
|
398
|
+
wgWalk(p);
|
|
399
|
+
else
|
|
400
|
+
wgAll.push(fs.readFileSync(p, 'latin1'));
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
wgWalk(wgLaunch.launchPath);
|
|
404
|
+
const hasKey = (text) => text.includes(PRIVATE_KEY) || text.includes(PRESHARED_KEY) || text.includes('PrivPlanted');
|
|
405
|
+
t.check(!hasKey(wgSession) && !wgAll.some(hasKey) && !fs.existsSync(path.join(wgLaunch.launchPath, 'vpn')), 'neither the private key nor the preshared key is in session.json or any file of the launch folder');
|
|
406
|
+
const wgHanded = f.readJson(new RunDir(wgRun.dataPath).secretFile(wgLaunch.launchId));
|
|
407
|
+
t.check((wgHanded.wireguardConfig ?? '').includes(PRIVATE_KEY) && wgHanded.vpn === undefined, 'the config is only in the one-shot file for the sandbox');
|
|
408
|
+
clearSecrets(wgSettings.storage.root, wgRun.dataPath);
|
|
409
|
+
t.check(!fs.existsSync(new RunDir(wgRun.dataPath).secretFile(wgLaunch.launchId)) &&
|
|
410
|
+
vpnNotes(wgSettings.vpn).some((n) => n.includes('WireGuard private key') && n.includes('turn the tunnel off')), 'the launch warns that the agent can read the key and turn the tunnel off');
|
|
278
411
|
},
|
|
279
412
|
};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// The web UI's server: login and cookie, the Host / Origin / CSRF checks, static files that cannot leave their folder,
|
|
2
|
-
// sessions without tokens,
|
|
3
|
-
// backend's, so the fake is only reachable here).
|
|
2
|
+
// sessions without tokens, the terminal bridge against a fake ptyhost on loopback (the real address check is the
|
|
3
|
+
// backend's, so the fake is only reachable here), and the RPC socket of the shared core against a fake RpcHost.
|
|
4
4
|
import http from 'node:http';
|
|
5
5
|
import net from 'node:net';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import WebSocket from 'ws';
|
|
8
|
+
import { cancelled } from '../app/kernel/index.js';
|
|
8
9
|
import { webSession } from '../cli/services/index.js';
|
|
10
|
+
import { fail } from '../core/util/index.js';
|
|
9
11
|
import { cookieValue, csrfOk, hostAllowed, originAllowed, sameSecret, StaticFiles, WebAuth, WebServer, WebStore, } from '../host/web/index.js';
|
|
10
12
|
import { encodeFrame, FrameReader, FrameType } from '../shared/pty/index.js';
|
|
11
13
|
import { CSRF_HEADER, CSRF_VALUE, parseClientMessage } from '../shared/web/index.js';
|
|
@@ -96,6 +98,35 @@ class Tab {
|
|
|
96
98
|
this.socket.on('close', (code) => (this.closeCode = code));
|
|
97
99
|
}
|
|
98
100
|
}
|
|
101
|
+
/** A stand-in for the shared core: one operation that reports, asks and may fail, and one live query. */
|
|
102
|
+
function fakeRpc() {
|
|
103
|
+
const ran = [];
|
|
104
|
+
const live = { value: 1 };
|
|
105
|
+
const host = {
|
|
106
|
+
kind: (id) => (id === 'test.work' ? 'operation' : id === 'test.live' ? 'query' : undefined),
|
|
107
|
+
liveMs: (id) => (id === 'test.live' ? 50 : undefined),
|
|
108
|
+
run: async (id, input, io) => {
|
|
109
|
+
ran.push(id);
|
|
110
|
+
if (id === 'test.live')
|
|
111
|
+
return { value: live.value };
|
|
112
|
+
const how = (input ?? {});
|
|
113
|
+
io.progress('Working');
|
|
114
|
+
io.log('one line', 'muted');
|
|
115
|
+
if (!(await io.confirm({ title: 'Go on?' })))
|
|
116
|
+
cancelled();
|
|
117
|
+
if (how.fail)
|
|
118
|
+
fail('It broke.', 'Fix it like this.');
|
|
119
|
+
if (how.bug)
|
|
120
|
+
throw new Error('internal detail C:\\Users\\secret');
|
|
121
|
+
return { done: true };
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
return { host, ran, live };
|
|
125
|
+
}
|
|
126
|
+
/** The server's messages on an RPC socket, parsed. */
|
|
127
|
+
function rpcMessages(tab) {
|
|
128
|
+
return tab.texts.map((text) => JSON.parse(text));
|
|
129
|
+
}
|
|
99
130
|
const session = (id, attach) => ({
|
|
100
131
|
id,
|
|
101
132
|
launchId: 'launch1',
|
|
@@ -202,7 +233,15 @@ export const webSuite = {
|
|
|
202
233
|
},
|
|
203
234
|
};
|
|
204
235
|
let stopped = false;
|
|
205
|
-
const
|
|
236
|
+
const rpc = fakeRpc();
|
|
237
|
+
const server = new WebServer({
|
|
238
|
+
port: 0,
|
|
239
|
+
token: TOKEN,
|
|
240
|
+
backend,
|
|
241
|
+
staticRoot: root,
|
|
242
|
+
rpc: rpc.host,
|
|
243
|
+
onStop: () => (stopped = true),
|
|
244
|
+
});
|
|
206
245
|
await server.start();
|
|
207
246
|
const { port } = server;
|
|
208
247
|
const origin = 'http://127.0.0.1:' + port;
|
|
@@ -282,6 +321,65 @@ export const webSuite = {
|
|
|
282
321
|
leaving.socket.close();
|
|
283
322
|
await t.waitFor(() => fake.closed >= 3, 5000);
|
|
284
323
|
t.check(fake.closed >= 3, 'closing the browser side closes the connection to the session');
|
|
324
|
+
t.section('the RPC socket');
|
|
325
|
+
const rpcUrl = 'ws://127.0.0.1:' + port + '/api/rpc';
|
|
326
|
+
const refusedRpc = [
|
|
327
|
+
new Tab(rpcUrl, { Cookie }),
|
|
328
|
+
new Tab(rpcUrl, { Origin: 'http://evil.example', Cookie }),
|
|
329
|
+
new Tab(rpcUrl, { Origin: origin }),
|
|
330
|
+
];
|
|
331
|
+
await t.waitFor(() => refusedRpc.every((a) => a.failed), 5000);
|
|
332
|
+
t.check(refusedRpc.every((a) => !a.opened), 'the RPC socket needs the cookie and this page as its Origin, like the terminal');
|
|
333
|
+
const browser = new Tab(rpcUrl, { Origin: origin, Cookie });
|
|
334
|
+
await t.waitFor(() => browser.opened, 5000);
|
|
335
|
+
const reply = (id, kind) => rpcMessages(browser).find((m) => m.id === id && m.t === kind);
|
|
336
|
+
browser.socket.send(JSON.stringify({ t: 'call', id: 'c1', op: 'test.work', input: {} }));
|
|
337
|
+
await t.waitFor(() => reply('c1', 'ask'), 5000);
|
|
338
|
+
t.check(reply('c1', 'progress') !== undefined && reply('c1', 'log') !== undefined, 'a call streams its progress and log lines before it asks');
|
|
339
|
+
browser.socket.send(JSON.stringify({ t: 'answer', id: 'c1', value: true }));
|
|
340
|
+
await t.waitFor(() => reply('c1', 'result'), 5000);
|
|
341
|
+
t.equal(reply('c1', 'result'), { t: 'result', id: 'c1', output: { done: true } }, 'the answer lets it finish');
|
|
342
|
+
browser.socket.send(JSON.stringify({ t: 'call', id: 'c2', op: 'test.work', input: {} }));
|
|
343
|
+
await t.waitFor(() => reply('c2', 'ask'), 5000);
|
|
344
|
+
browser.socket.send(JSON.stringify({ t: 'answer', id: 'c2', value: 'yes please' }));
|
|
345
|
+
await t.waitFor(() => reply('c2', 'error'), 5000);
|
|
346
|
+
const declinedCall = reply('c2', 'error');
|
|
347
|
+
t.check(declinedCall?.t === 'error' && declinedCall.error.kind === 'cancelled', 'an answer of the wrong kind counts as no: nothing changes');
|
|
348
|
+
browser.socket.send(JSON.stringify({ t: 'call', id: 'c3', op: 'test.work', input: { fail: true } }));
|
|
349
|
+
browser.socket.send(JSON.stringify({ t: 'call', id: 'c4', op: 'test.work', input: { bug: true } }));
|
|
350
|
+
await t.waitFor(() => reply('c3', 'ask') && reply('c4', 'ask'), 5000);
|
|
351
|
+
browser.socket.send(JSON.stringify({ t: 'answer', id: 'c3', value: true }));
|
|
352
|
+
browser.socket.send(JSON.stringify({ t: 'answer', id: 'c4', value: true }));
|
|
353
|
+
await t.waitFor(() => reply('c3', 'error') && reply('c4', 'error'), 5000);
|
|
354
|
+
t.equal(reply('c3', 'error'), { t: 'error', id: 'c3', error: { kind: 'user', message: 'It broke.', fix: 'Fix it like this.' } }, 'a failure arrives as its message and fix');
|
|
355
|
+
t.equal(reply('c4', 'error'), { t: 'error', id: 'c4', error: { kind: 'bug', message: 'Something went wrong.' } }, "a bug's details stay on this PC");
|
|
356
|
+
const before = rpc.ran.length;
|
|
357
|
+
browser.socket.send(JSON.stringify({ t: 'call', id: 'c5', op: 'sandbox.notThere', input: {} }));
|
|
358
|
+
await t.waitFor(() => reply('c5', 'error'), 5000);
|
|
359
|
+
const unknown = reply('c5', 'error');
|
|
360
|
+
t.check(unknown?.t === 'error' && unknown.error.kind === 'invalid' && rpc.ran.length === before, 'an operation that is not allowed on the web is never run');
|
|
361
|
+
browser.socket.send(JSON.stringify({ t: 'subscribe', id: 's1', query: 'test.live', input: {} }));
|
|
362
|
+
await t.waitFor(() => reply('s1', 'data'), 5000);
|
|
363
|
+
rpc.live.value = 2;
|
|
364
|
+
await t.waitFor(() => rpcMessages(browser).filter((m) => m.id === 's1' && m.t === 'data').length === 2, 5000);
|
|
365
|
+
const updates = rpcMessages(browser).filter((m) => m.id === 's1' && m.t === 'data');
|
|
366
|
+
t.equal(updates.map((m) => (m.t === 'data' ? m.output : null)), [{ value: 1 }, { value: 2 }], 'a live query sends its result, then again only when it changed');
|
|
367
|
+
browser.socket.send(JSON.stringify({ t: 'unsubscribe', id: 's1' }));
|
|
368
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
369
|
+
const reads = rpc.ran.filter((id) => id === 'test.live').length;
|
|
370
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
371
|
+
t.equal(rpc.ran.filter((id) => id === 'test.live').length, reads, 'unsubscribing stops the reads');
|
|
372
|
+
browser.socket.send(JSON.stringify({ t: 'call', id: 'bad id!', op: 'test.work', input: {} }));
|
|
373
|
+
await t.waitFor(() => browser.closeCode !== null, 5000);
|
|
374
|
+
t.equal(browser.closeCode, 1008, 'a message that does not fit the schema closes the socket');
|
|
375
|
+
const leaver = new Tab(rpcUrl, { Origin: origin, Cookie });
|
|
376
|
+
await t.waitFor(() => leaver.opened, 5000);
|
|
377
|
+
const runs = rpc.ran.length;
|
|
378
|
+
leaver.socket.send(JSON.stringify({ t: 'call', id: 'c1', op: 'test.work', input: {} }));
|
|
379
|
+
await t.waitFor(() => leaver.texts.some((m) => m.includes('"ask"')), 5000);
|
|
380
|
+
leaver.socket.close();
|
|
381
|
+
await t.waitFor(() => leaver.closeCode !== null, 5000);
|
|
382
|
+
t.check(rpc.ran.length === runs + 1, 'a browser that leaves mid-question answers no (the call ends)');
|
|
285
383
|
t.section('stopping');
|
|
286
384
|
t.equal((await send(port, 'POST', '/api/stop', { ...json, Cookie }, '{}')).status, 403, 'stop needs CSRF and Origin too');
|
|
287
385
|
t.equal((await send(port, 'POST', '/api/stop', { ...ajax, Cookie }, '{}')).status, 200, 'a signed-in browser can stop it');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GuestRequest, GuestResponse, ProjectStatus, SandboxStatus, SessionStatus, VpnStatus } from './schema.js';
|
|
1
|
+
import type { GuestRequest, GuestResponse, PressureStatus, ProjectStatus, SandboxStatus, SessionStatus, VpnStatus } from './schema.js';
|
|
2
2
|
/** Ids become file names, so only a strict alphabet is allowed (no separators, no dots). */
|
|
3
3
|
export declare function safeId(id: string): string;
|
|
4
4
|
export declare function now(): string;
|
|
@@ -7,6 +7,8 @@ export declare class RunDir {
|
|
|
7
7
|
readonly root: string;
|
|
8
8
|
readonly sandboxFile: string;
|
|
9
9
|
readonly vpnFile: string;
|
|
10
|
+
/** What the memory governor in the sandbox is doing (pressure.json). */
|
|
11
|
+
readonly pressureFile: string;
|
|
10
12
|
readonly requestsDir: string;
|
|
11
13
|
readonly responsesDir: string;
|
|
12
14
|
readonly sessionsDir: string;
|
|
@@ -23,6 +25,7 @@ export declare class RunDir {
|
|
|
23
25
|
secretFile(id: string): string;
|
|
24
26
|
readSandboxStatus(): SandboxStatus | null;
|
|
25
27
|
readVpnStatus(): VpnStatus | null;
|
|
28
|
+
readPressure(): PressureStatus | null;
|
|
26
29
|
readSession(id: string): SessionStatus | null;
|
|
27
30
|
readResponse(id: string): GuestResponse | null;
|
|
28
31
|
listSessions(): SessionStatus[];
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// written atomically and parsed with the channel schemas on read. Anything that does not parse is ignored.
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
-
import { GuestRequestSchema, GuestResponseSchema, IdSchema, ProjectStatusSchema, SandboxStatusSchema, SessionStatusSchema, VpnStatusSchema, } from './schema.js';
|
|
6
|
+
import { GuestRequestSchema, GuestResponseSchema, IdSchema, PressureStatusSchema, ProjectStatusSchema, SandboxStatusSchema, SessionStatusSchema, VpnStatusSchema, } from './schema.js';
|
|
7
7
|
import { readValid } from '../io/index.js';
|
|
8
8
|
/** Ids become file names, so only a strict alphabet is allowed (no separators, no dots). */
|
|
9
9
|
export function safeId(id) {
|
|
@@ -34,6 +34,8 @@ export class RunDir {
|
|
|
34
34
|
root;
|
|
35
35
|
sandboxFile;
|
|
36
36
|
vpnFile;
|
|
37
|
+
/** What the memory governor in the sandbox is doing (pressure.json). */
|
|
38
|
+
pressureFile;
|
|
37
39
|
requestsDir;
|
|
38
40
|
responsesDir;
|
|
39
41
|
sessionsDir;
|
|
@@ -46,6 +48,7 @@ export class RunDir {
|
|
|
46
48
|
this.root = path.join(dataDir, 'run');
|
|
47
49
|
this.sandboxFile = path.join(this.root, 'sandbox.json');
|
|
48
50
|
this.vpnFile = path.join(this.root, 'vpn.json');
|
|
51
|
+
this.pressureFile = path.join(this.root, 'pressure.json');
|
|
49
52
|
this.requestsDir = path.join(this.root, 'requests');
|
|
50
53
|
this.responsesDir = path.join(this.root, 'responses');
|
|
51
54
|
this.sessionsDir = path.join(this.root, 'sessions');
|
|
@@ -74,6 +77,9 @@ export class RunDir {
|
|
|
74
77
|
readVpnStatus() {
|
|
75
78
|
return readValid(VpnStatusSchema, this.vpnFile);
|
|
76
79
|
}
|
|
80
|
+
readPressure() {
|
|
81
|
+
return readValid(PressureStatusSchema, this.pressureFile);
|
|
82
|
+
}
|
|
77
83
|
readSession(id) {
|
|
78
84
|
return readValid(SessionStatusSchema, this.sessionFile(id));
|
|
79
85
|
}
|
|
@@ -194,6 +194,7 @@ export declare const ProjectSpecSchema: z.ZodObject<{
|
|
|
194
194
|
hostDir: z.ZodOptional<z.ZodString>;
|
|
195
195
|
}, z.core.$strict>>;
|
|
196
196
|
localState: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
197
|
+
bootReset: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
197
198
|
resumeArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
198
199
|
resumeOneArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
199
200
|
}, z.core.$strict>>;
|
|
@@ -351,6 +352,7 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
351
352
|
hostDir: z.ZodOptional<z.ZodString>;
|
|
352
353
|
}, z.core.$strict>>;
|
|
353
354
|
localState: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
355
|
+
bootReset: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
354
356
|
resumeArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
355
357
|
resumeOneArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
356
358
|
}, z.core.$strict>>;
|
|
@@ -372,6 +374,7 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
372
374
|
hostDir: z.ZodOptional<z.ZodString>;
|
|
373
375
|
}, z.core.$strict>>;
|
|
374
376
|
localState: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
377
|
+
bootReset: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
375
378
|
resumeArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
376
379
|
resumeOneArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
377
380
|
}, z.core.$strict>>;
|
|
@@ -524,6 +527,7 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
524
527
|
hostDir: z.ZodOptional<z.ZodString>;
|
|
525
528
|
}, z.core.$strict>>;
|
|
526
529
|
localState: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
530
|
+
bootReset: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
527
531
|
resumeArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
528
532
|
resumeOneArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
529
533
|
}, z.core.$strict>>;
|
|
@@ -547,7 +551,37 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
547
551
|
kind: z.ZodLiteral<"ping">;
|
|
548
552
|
id: z.ZodString;
|
|
549
553
|
createdAt: z.ZodString;
|
|
554
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
555
|
+
kind: z.ZodLiteral<"pressure">;
|
|
556
|
+
id: z.ZodString;
|
|
557
|
+
reason: z.ZodString;
|
|
558
|
+
seconds: z.ZodNumber;
|
|
559
|
+
createdAt: z.ZodString;
|
|
550
560
|
}, z.core.$strict>], "kind">;
|
|
561
|
+
/** C:\AgentData\run\pressure.json: what the memory governor in the sandbox is doing (guest-written, capped). */
|
|
562
|
+
export declare const PressureStatusSchema: z.ZodObject<{
|
|
563
|
+
level: z.ZodEnum<{
|
|
564
|
+
ok: "ok";
|
|
565
|
+
throttled: "throttled";
|
|
566
|
+
suspending: "suspending";
|
|
567
|
+
}>;
|
|
568
|
+
profile: z.ZodEnum<{
|
|
569
|
+
normal: "normal";
|
|
570
|
+
gentle: "gentle";
|
|
571
|
+
}>;
|
|
572
|
+
ramFreePercent: z.ZodNumber;
|
|
573
|
+
commitPercent: z.ZodNumber;
|
|
574
|
+
lowered: z.ZodNumber;
|
|
575
|
+
paused: z.ZodArray<z.ZodObject<{
|
|
576
|
+
name: z.ZodString;
|
|
577
|
+
pid: z.ZodNumber;
|
|
578
|
+
seconds: z.ZodNumber;
|
|
579
|
+
}, z.core.$strip>>;
|
|
580
|
+
pcLow: z.ZodBoolean;
|
|
581
|
+
lastAction: z.ZodString;
|
|
582
|
+
updatedAt: z.ZodString;
|
|
583
|
+
}, z.core.$strip>;
|
|
584
|
+
export type PressureStatus = z.output<typeof PressureStatusSchema>;
|
|
551
585
|
/** C:\AgentData\run\responses\<id>.json: the guest's reply. */
|
|
552
586
|
export declare const GuestResponseSchema: z.ZodObject<{
|
|
553
587
|
id: z.ZodString;
|
|
@@ -604,9 +638,9 @@ export declare const ProjectStatusSchema: z.ZodObject<{
|
|
|
604
638
|
/** The guest writes run/vpn.json: whether the VPN tunnel is up (validated and capped on the host). */
|
|
605
639
|
export declare const VpnStatusSchema: z.ZodObject<{
|
|
606
640
|
state: z.ZodEnum<{
|
|
641
|
+
off: "off";
|
|
607
642
|
failed: "failed";
|
|
608
643
|
starting: "starting";
|
|
609
|
-
off: "off";
|
|
610
644
|
up: "up";
|
|
611
645
|
}>;
|
|
612
646
|
message: z.ZodString;
|
|
@@ -91,7 +91,34 @@ export const GuestRequestSchema = z.discriminatedUnion('kind', [
|
|
|
91
91
|
createdAt: TimeSchema,
|
|
92
92
|
}),
|
|
93
93
|
z.strictObject({ kind: z.literal('ping'), id: IdSchema, createdAt: TimeSchema }),
|
|
94
|
+
/**
|
|
95
|
+
* The PC's own memory or CPU is running low (the host watchdog): the sandbox slows its heaviest builds down for
|
|
96
|
+
* `seconds` (it never stops them) so Windows does not have to close it. Repeated while the PC stays low.
|
|
97
|
+
*/
|
|
98
|
+
z.strictObject({
|
|
99
|
+
kind: z.literal('pressure'),
|
|
100
|
+
id: IdSchema,
|
|
101
|
+
reason: Text(300),
|
|
102
|
+
seconds: z.number().int().min(10).max(300),
|
|
103
|
+
createdAt: TimeSchema,
|
|
104
|
+
}),
|
|
94
105
|
]);
|
|
106
|
+
/** C:\AgentData\run\pressure.json: what the memory governor in the sandbox is doing (guest-written, capped). */
|
|
107
|
+
export const PressureStatusSchema = z.object({
|
|
108
|
+
level: z.enum(['ok', 'throttled', 'suspending']),
|
|
109
|
+
profile: z.enum(['normal', 'gentle']),
|
|
110
|
+
/** Share of the sandbox's RAM still free and of its commit limit in use (percent). */
|
|
111
|
+
ramFreePercent: z.number().int().min(0).max(100),
|
|
112
|
+
commitPercent: z.number().int().min(0).max(100),
|
|
113
|
+
/** Programs running at lower priority because of it. */
|
|
114
|
+
lowered: z.number().int().min(0).max(1024),
|
|
115
|
+
/** Programs paused right now (name, process id, seconds left). */
|
|
116
|
+
paused: z.array(z.object({ name: Text(64), pid: z.number().int(), seconds: z.number().int().min(0).max(60) })).max(8),
|
|
117
|
+
/** The PC itself asked for slowing down (its memory or CPU is low). */
|
|
118
|
+
pcLow: z.boolean(),
|
|
119
|
+
lastAction: Text(300),
|
|
120
|
+
updatedAt: TimeSchema,
|
|
121
|
+
});
|
|
95
122
|
/** C:\AgentData\run\responses\<id>.json: the guest's reply. */
|
|
96
123
|
export const GuestResponseSchema = z.object({
|
|
97
124
|
id: IdSchema,
|
|
@@ -8,6 +8,7 @@ export declare const SecretsFileSchema: z.ZodObject<{
|
|
|
8
8
|
username: z.ZodString;
|
|
9
9
|
password: z.ZodString;
|
|
10
10
|
}, z.core.$strict>>;
|
|
11
|
+
wireguardConfig: z.ZodOptional<z.ZodString>;
|
|
11
12
|
}, z.core.$strict>;
|
|
12
13
|
export type SecretsFile = z.output<typeof SecretsFileSchema>;
|
|
13
14
|
interface WithHarnesses {
|
|
@@ -19,6 +19,12 @@ export const SecretsFileSchema = z.strictObject({
|
|
|
19
19
|
.optional(),
|
|
20
20
|
/** VPN service credentials (vpn.provider's login), only for the launch's own sandbox. */
|
|
21
21
|
vpn: z.strictObject({ username: z.string().min(1).max(256), password: z.string().min(1).max(1024) }).optional(),
|
|
22
|
+
/** The checked WireGuard config of vpn.provider wireguard-file (holds the private key). */
|
|
23
|
+
wireguardConfig: z
|
|
24
|
+
.string()
|
|
25
|
+
.min(1)
|
|
26
|
+
.max(32 * 1024)
|
|
27
|
+
.optional(),
|
|
22
28
|
});
|
|
23
29
|
/** The environment a harness gets: its own, then the values that travelled in the secrets file. */
|
|
24
30
|
const withEnvironment = (harness, environment) => (environment ? { ...harness, environment: { ...harness.environment, ...environment } } : harness);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './policy.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './policy.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type LoadProfile = 'normal' | 'gentle';
|
|
2
|
+
/** The guest governor: when memory inside the sandbox counts as under pressure, and when it has recovered. */
|
|
3
|
+
export interface GuestThresholds {
|
|
4
|
+
/** Pressure starts when available RAM is below this share of the sandbox's RAM (percent)... */
|
|
5
|
+
readonly enterAvailablePercent: number;
|
|
6
|
+
/** ...or commit is above this share of its limit. */
|
|
7
|
+
readonly enterCommitPercent: number;
|
|
8
|
+
/** Pressure ends only when available RAM is above this and commit is below the next, for `calmSamples` in a row. */
|
|
9
|
+
readonly exitAvailablePercent: number;
|
|
10
|
+
readonly exitCommitPercent: number;
|
|
11
|
+
readonly calmSamples: number;
|
|
12
|
+
}
|
|
13
|
+
export declare const GUEST_THRESHOLDS: Readonly<Record<LoadProfile, GuestThresholds>>;
|
|
14
|
+
/** The PC watchdog: when this PC itself is low. */
|
|
15
|
+
export interface HostThresholds {
|
|
16
|
+
/** Low when available RAM is below max(this, availableShare of total RAM)... */
|
|
17
|
+
readonly availableMinMb: number;
|
|
18
|
+
readonly availablePercent: number;
|
|
19
|
+
/** ...or available commit is below this share of the commit limit (percent)... */
|
|
20
|
+
readonly commitAvailablePercent: number;
|
|
21
|
+
/** ...or below half the reserveHostMb. Recovered above 1.25 times each limit for `calmSamples` samples. */
|
|
22
|
+
readonly calmSamples: number;
|
|
23
|
+
/** Total CPU above this (percent) for cpuSeconds. */
|
|
24
|
+
readonly cpuPercent: number;
|
|
25
|
+
readonly cpuSeconds: number;
|
|
26
|
+
}
|
|
27
|
+
export declare const HOST_THRESHOLDS: Readonly<Record<LoadProfile, HostThresholds>>;
|
|
28
|
+
/** The CPU cap (percent of the PC) the gentle profile uses: 15 points lower, never below 50. "none" = 100. */
|
|
29
|
+
export declare function gentleCpuLimit(cpuLimit: 'none' | number): number;
|
|
30
|
+
/** The CPU cap the sandbox runs with (percent; null = none) for a profile. */
|
|
31
|
+
export declare function effectiveCpuLimit(cpuLimit: 'none' | number, profile: LoadProfile): number | null;
|
|
32
|
+
/** Cargo jobs that fit the memory (about 4 GB per rustc) and the CPUs, fewer when several agent sessions build. */
|
|
33
|
+
export declare function cargoJobs(cpus: number, memoryGb: number, sessions: number): number;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// What "heavy load" means, decided once for the host watchdog and the guest governor (pure, no I/O). Every
|
|
2
|
+
// protection is on by default; the gentle profile (vGPU on: a graphics driver hang can freeze the whole PC) triggers
|
|
3
|
+
// earlier and runs the sandbox with less CPU.
|
|
4
|
+
export const GUEST_THRESHOLDS = {
|
|
5
|
+
normal: {
|
|
6
|
+
enterAvailablePercent: 8,
|
|
7
|
+
enterCommitPercent: 92,
|
|
8
|
+
exitAvailablePercent: 12,
|
|
9
|
+
exitCommitPercent: 88,
|
|
10
|
+
calmSamples: 3,
|
|
11
|
+
},
|
|
12
|
+
gentle: {
|
|
13
|
+
enterAvailablePercent: 15,
|
|
14
|
+
enterCommitPercent: 85,
|
|
15
|
+
exitAvailablePercent: 20,
|
|
16
|
+
exitCommitPercent: 80,
|
|
17
|
+
calmSamples: 3,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
export const HOST_THRESHOLDS = {
|
|
21
|
+
normal: {
|
|
22
|
+
availableMinMb: 4096,
|
|
23
|
+
availablePercent: 12,
|
|
24
|
+
commitAvailablePercent: 15,
|
|
25
|
+
calmSamples: 3,
|
|
26
|
+
cpuPercent: 95,
|
|
27
|
+
cpuSeconds: 60,
|
|
28
|
+
},
|
|
29
|
+
gentle: {
|
|
30
|
+
availableMinMb: 6144,
|
|
31
|
+
availablePercent: 15,
|
|
32
|
+
commitAvailablePercent: 20,
|
|
33
|
+
calmSamples: 3,
|
|
34
|
+
cpuPercent: 90,
|
|
35
|
+
cpuSeconds: 45,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
/** The CPU cap (percent of the PC) the gentle profile uses: 15 points lower, never below 50. "none" = 100. */
|
|
39
|
+
export function gentleCpuLimit(cpuLimit) {
|
|
40
|
+
return Math.max(50, (cpuLimit === 'none' ? 100 : cpuLimit) - 15);
|
|
41
|
+
}
|
|
42
|
+
/** The CPU cap the sandbox runs with (percent; null = none) for a profile. */
|
|
43
|
+
export function effectiveCpuLimit(cpuLimit, profile) {
|
|
44
|
+
if (profile === 'gentle')
|
|
45
|
+
return Math.min(gentleCpuLimit(cpuLimit), cpuLimit === 'none' ? 100 : cpuLimit);
|
|
46
|
+
return cpuLimit === 'none' ? null : cpuLimit;
|
|
47
|
+
}
|
|
48
|
+
/** Cargo jobs that fit the memory (about 4 GB per rustc) and the CPUs, fewer when several agent sessions build. */
|
|
49
|
+
export function cargoJobs(cpus, memoryGb, sessions) {
|
|
50
|
+
const byCpu = Math.max(2, Math.min(4, Math.floor(cpus / 4)));
|
|
51
|
+
const byMemory = Math.max(1, Math.floor(memoryGb / 4));
|
|
52
|
+
const jobs = Math.min(byCpu, byMemory);
|
|
53
|
+
// All the agents' builds share the sandbox: about 8 jobs in total however many sessions there are.
|
|
54
|
+
return Math.max(1, Math.min(jobs, Math.floor(8 / Math.max(1, sessions))));
|
|
55
|
+
}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
+
export interface OutputFilterOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Let clipboard writes (OSC 52) through: the agent's "copy" reaches your clipboard. Only where the sandbox can reach
|
|
4
|
+
* that clipboard anyway (its own terminal, or sandbox.clipboard on). Clipboard reads are always dropped.
|
|
5
|
+
*/
|
|
6
|
+
readonly clipboardWrites?: boolean;
|
|
7
|
+
}
|
|
1
8
|
/**
|
|
2
9
|
* Removes the terminal "strings" an untrusted program could abuse from the sandbox's output before it reaches your
|
|
3
|
-
* terminal: clipboard writes (OSC 52), terminal macros (ConEmu's OSC 9), DCS / APC /
|
|
10
|
+
* terminal: clipboard writes (OSC 52, unless clipboardWrites) and reads, terminal macros (ConEmu's OSC 9), DCS / APC /
|
|
11
|
+
* PM / SOS, and their C1 forms.
|
|
4
12
|
* Titles, links, progress and colours pass. A byte-level state machine, so no split across chunks, leading zero
|
|
5
13
|
* (ESC]052) or huge payload slips through: a dropped string stays dropped until its terminator, however long it is.
|
|
6
14
|
*/
|
|
7
15
|
export declare class OutputFilter {
|
|
16
|
+
private readonly options;
|
|
8
17
|
/**
|
|
9
18
|
* text: normal output; esc: after ESC; c2: after a 0xC2 lead byte; string: inside an OSC/DCS/...; stringEsc /
|
|
10
19
|
* stringC2: a possible terminator inside one.
|
|
@@ -12,5 +21,9 @@ export declare class OutputFilter {
|
|
|
12
21
|
private state;
|
|
13
22
|
/** The string being read: an OSC worth keeping is collected until its terminator; null = being dropped. */
|
|
14
23
|
private body;
|
|
24
|
+
private readonly maxString;
|
|
25
|
+
constructor(options?: OutputFilterOptions);
|
|
26
|
+
/** An OSC that may pass: a short title, link, progress or mark, or (when allowed) a clipboard write. */
|
|
27
|
+
private kept;
|
|
15
28
|
push(chunk: Buffer): Buffer;
|
|
16
29
|
}
|