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,13 +8,19 @@ const KEPT_OSC = (body) => {
|
|
|
8
8
|
};
|
|
9
9
|
/** Longest OSC kept (a title or a link); anything longer is dropped whole. */
|
|
10
10
|
const MAX_KEPT_STRING = 4096;
|
|
11
|
+
/** Longest clipboard write kept (about 750 KB of text); anything longer is dropped whole. */
|
|
12
|
+
const MAX_CLIPBOARD_STRING = 1024 * 1024;
|
|
13
|
+
/** A clipboard write (OSC 52: selection, then base64): never a read ("?"), which would send the clipboard back. */
|
|
14
|
+
const CLIPBOARD_WRITE = /^0{0,2}52;[cpqs0-7]{0,8};[A-Za-z0-9+/=]*$/;
|
|
11
15
|
/**
|
|
12
16
|
* Removes the terminal "strings" an untrusted program could abuse from the sandbox's output before it reaches your
|
|
13
|
-
* terminal: clipboard writes (OSC 52), terminal macros (ConEmu's OSC 9), DCS / APC /
|
|
17
|
+
* terminal: clipboard writes (OSC 52, unless clipboardWrites) and reads, terminal macros (ConEmu's OSC 9), DCS / APC /
|
|
18
|
+
* PM / SOS, and their C1 forms.
|
|
14
19
|
* Titles, links, progress and colours pass. A byte-level state machine, so no split across chunks, leading zero
|
|
15
20
|
* (ESC]052) or huge payload slips through: a dropped string stays dropped until its terminator, however long it is.
|
|
16
21
|
*/
|
|
17
22
|
export class OutputFilter {
|
|
23
|
+
options;
|
|
18
24
|
/**
|
|
19
25
|
* text: normal output; esc: after ESC; c2: after a 0xC2 lead byte; string: inside an OSC/DCS/...; stringEsc /
|
|
20
26
|
* stringC2: a possible terminator inside one.
|
|
@@ -22,8 +28,20 @@ export class OutputFilter {
|
|
|
22
28
|
state = 'text';
|
|
23
29
|
/** The string being read: an OSC worth keeping is collected until its terminator; null = being dropped. */
|
|
24
30
|
body = null;
|
|
31
|
+
maxString;
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
this.options = options;
|
|
34
|
+
this.maxString = options.clipboardWrites ? MAX_CLIPBOARD_STRING : MAX_KEPT_STRING;
|
|
35
|
+
}
|
|
36
|
+
/** An OSC that may pass: a short title, link, progress or mark, or (when allowed) a clipboard write. */
|
|
37
|
+
kept(body) {
|
|
38
|
+
if (this.options.clipboardWrites && CLIPBOARD_WRITE.test(body))
|
|
39
|
+
return true;
|
|
40
|
+
return body.length <= MAX_KEPT_STRING && KEPT_OSC(body);
|
|
41
|
+
}
|
|
25
42
|
push(chunk) {
|
|
26
|
-
|
|
43
|
+
// At most: the string carried over from the last chunk, this chunk, and an OSC prefix and terminator.
|
|
44
|
+
const out = Buffer.allocUnsafe(chunk.length + (this.body?.length ?? 0) + 8);
|
|
27
45
|
let n = 0;
|
|
28
46
|
const put = (...bytes) => {
|
|
29
47
|
for (const b of bytes)
|
|
@@ -45,7 +63,7 @@ export class OutputFilter {
|
|
|
45
63
|
};
|
|
46
64
|
const endString = (terminator) => {
|
|
47
65
|
const body = this.body && Buffer.from(this.body).toString('latin1');
|
|
48
|
-
if (body !== null &&
|
|
66
|
+
if (body !== null && this.kept(body)) {
|
|
49
67
|
put(0x1b, 0x5d);
|
|
50
68
|
out.write(body, n, 'latin1');
|
|
51
69
|
n += body.length;
|
|
@@ -55,7 +73,7 @@ export class OutputFilter {
|
|
|
55
73
|
this.state = 'text';
|
|
56
74
|
};
|
|
57
75
|
const collect = (b) => {
|
|
58
|
-
if (this.body && this.body.push(b) >
|
|
76
|
+
if (this.body && this.body.push(b) > this.maxString)
|
|
59
77
|
this.body = null;
|
|
60
78
|
};
|
|
61
79
|
for (const b of chunk) {
|
|
@@ -22,6 +22,9 @@ export declare function fixWindowsKeys(chunk: Buffer): Buffer;
|
|
|
22
22
|
* Bridges this process's terminal to the remote program until it exits, the connection drops, or the user presses
|
|
23
23
|
* the detach key. Restores the terminal before resolving.
|
|
24
24
|
*/
|
|
25
|
-
export declare function runTerminalClient(socket: net.Socket, token: string,
|
|
25
|
+
export declare function runTerminalClient(socket: net.Socket, token: string,
|
|
26
|
+
/** clipboardWrites: the agent's "copy" (OSC 52) reaches the clipboard; see OutputFilterOptions. */
|
|
27
|
+
options?: {
|
|
26
28
|
detachKey?: number;
|
|
29
|
+
clipboardWrites?: boolean;
|
|
27
30
|
}): Promise<ClientResult>;
|
|
@@ -20,7 +20,9 @@ export function fixWindowsKeys(chunk) {
|
|
|
20
20
|
* Bridges this process's terminal to the remote program until it exits, the connection drops, or the user presses
|
|
21
21
|
* the detach key. Restores the terminal before resolving.
|
|
22
22
|
*/
|
|
23
|
-
export function runTerminalClient(socket, token,
|
|
23
|
+
export function runTerminalClient(socket, token,
|
|
24
|
+
/** clipboardWrites: the agent's "copy" (OSC 52) reaches the clipboard; see OutputFilterOptions. */
|
|
25
|
+
options = {}) {
|
|
24
26
|
const stdin = process.stdin;
|
|
25
27
|
const stdout = process.stdout;
|
|
26
28
|
// getWindowSize asks the console every time; columns/rows only change when Node noticed a resize.
|
|
@@ -33,7 +35,7 @@ export function runTerminalClient(socket, token, options = {}) {
|
|
|
33
35
|
return new Promise((resolve) => {
|
|
34
36
|
let done = false;
|
|
35
37
|
const reader = new FrameReader();
|
|
36
|
-
const filter = new OutputFilter();
|
|
38
|
+
const filter = new OutputFilter({ clipboardWrites: options.clipboardWrites });
|
|
37
39
|
let sent = size();
|
|
38
40
|
let resizeTimer;
|
|
39
41
|
// Set once raw mode is on (below); finish() may run before that.
|
package/dist/shared/system.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare const SYSTEM: {
|
|
|
15
15
|
readonly shutdown: () => string;
|
|
16
16
|
readonly dism: () => string;
|
|
17
17
|
readonly msiexec: () => string;
|
|
18
|
+
readonly sc: () => string;
|
|
18
19
|
/** The .NET Framework C# compiler that ships with Windows (C# 5). */
|
|
19
20
|
readonly csc: () => string;
|
|
20
21
|
};
|
package/dist/shared/system.js
CHANGED
|
@@ -21,6 +21,7 @@ export const SYSTEM = {
|
|
|
21
21
|
shutdown: () => system32('shutdown.exe'),
|
|
22
22
|
dism: () => system32('Dism.exe'),
|
|
23
23
|
msiexec: () => system32('msiexec.exe'),
|
|
24
|
+
sc: () => system32('sc.exe'),
|
|
24
25
|
/** The .NET Framework C# compiler that ships with Windows (C# 5). */
|
|
25
26
|
csc: () => path.join(systemRoot(), 'Microsoft.NET', 'Framework64', 'v4.0.30319', 'csc.exe'),
|
|
26
27
|
};
|
|
@@ -11,3 +11,19 @@ export declare const OPENVPN_MSI: {
|
|
|
11
11
|
};
|
|
12
12
|
readonly base: "https://swupdate.openvpn.org/community/releases/";
|
|
13
13
|
};
|
|
14
|
+
/**
|
|
15
|
+
* WireGuard for Windows (brings wireguard.exe and wg.exe). Hashes computed from the files on download.wireguard.com
|
|
16
|
+
* (https); both files carry a valid WireGuard LLC signature.
|
|
17
|
+
*/
|
|
18
|
+
export declare const WIREGUARD_MSI: {
|
|
19
|
+
readonly version: "1.1.1";
|
|
20
|
+
readonly x64: {
|
|
21
|
+
readonly file: "wireguard-amd64-1.1.1.msi";
|
|
22
|
+
readonly sha256: "7bfed60ad61b785c914b38b61555a975488e1d3ec472dbfb2fcdf498fca75242";
|
|
23
|
+
};
|
|
24
|
+
readonly arm64: {
|
|
25
|
+
readonly file: "wireguard-arm64-1.1.1.msi";
|
|
26
|
+
readonly sha256: "8336335c738d4aff040fa9d11b752da65a80b6f41d3262d6ba5e82f97ebd1bd4";
|
|
27
|
+
};
|
|
28
|
+
readonly base: "https://download.wireguard.com/windows-client/";
|
|
29
|
+
};
|
|
@@ -12,3 +12,19 @@ export const OPENVPN_MSI = {
|
|
|
12
12
|
},
|
|
13
13
|
base: 'https://swupdate.openvpn.org/community/releases/',
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* WireGuard for Windows (brings wireguard.exe and wg.exe). Hashes computed from the files on download.wireguard.com
|
|
17
|
+
* (https); both files carry a valid WireGuard LLC signature.
|
|
18
|
+
*/
|
|
19
|
+
export const WIREGUARD_MSI = {
|
|
20
|
+
version: '1.1.1',
|
|
21
|
+
x64: {
|
|
22
|
+
file: 'wireguard-amd64-1.1.1.msi',
|
|
23
|
+
sha256: '7bfed60ad61b785c914b38b61555a975488e1d3ec472dbfb2fcdf498fca75242',
|
|
24
|
+
},
|
|
25
|
+
arm64: {
|
|
26
|
+
file: 'wireguard-arm64-1.1.1.msi',
|
|
27
|
+
sha256: '8336335c738d4aff040fa9d11b752da65a80b6f41d3262d6ba5e82f97ebd1bd4',
|
|
28
|
+
},
|
|
29
|
+
base: 'https://download.wireguard.com/windows-client/',
|
|
30
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export declare const MAX_WIREGUARD_BYTES: number;
|
|
2
|
+
/** Keys that run a program, change the routes or write the config back. Refused with a clear reason. */
|
|
3
|
+
export declare const WIREGUARD_PROGRAM_KEYS: readonly ["preup", "postup", "predown", "postdown", "table", "saveconfig"];
|
|
4
|
+
export interface WireguardPeer {
|
|
5
|
+
readonly publicKey: string;
|
|
6
|
+
readonly presharedKey?: string;
|
|
7
|
+
readonly allowedIps: readonly string[];
|
|
8
|
+
readonly endpointHost: string;
|
|
9
|
+
readonly endpointPort: number;
|
|
10
|
+
readonly keepalive?: number;
|
|
11
|
+
}
|
|
12
|
+
/** A checked config. Holds secrets (privateKey, presharedKey): never log it or write it to session.json. */
|
|
13
|
+
export interface WireguardConfig {
|
|
14
|
+
readonly privateKey: string;
|
|
15
|
+
readonly addresses: readonly string[];
|
|
16
|
+
readonly dns: readonly string[];
|
|
17
|
+
readonly mtu?: number;
|
|
18
|
+
readonly listenPort?: number;
|
|
19
|
+
readonly peers: readonly WireguardPeer[];
|
|
20
|
+
}
|
|
21
|
+
export type WireguardReport = {
|
|
22
|
+
readonly ok: true;
|
|
23
|
+
readonly config: WireguardConfig;
|
|
24
|
+
} | {
|
|
25
|
+
readonly ok: false;
|
|
26
|
+
readonly problems: readonly string[];
|
|
27
|
+
};
|
|
28
|
+
/** Where the tunnel connects (the kill switch allows these, over udp). */
|
|
29
|
+
export interface WireguardEndpoint {
|
|
30
|
+
readonly host: string;
|
|
31
|
+
readonly port: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Checks a config. `ok: false` lists the problems (no values in them); `ok: true` gives the checked settings. It needs
|
|
35
|
+
* one [Interface] with a PrivateKey and an IPv4 Address, and at least one [Peer] with PublicKey, AllowedIPs and an
|
|
36
|
+
* Endpoint that is a name or an IPv4 address (the kill switch rules are IPv4).
|
|
37
|
+
*/
|
|
38
|
+
export declare function inspectWireguard(text: string): WireguardReport;
|
|
39
|
+
/** The checked config; throws, saying why (never with a key), when it is not safe. `what` names the file. */
|
|
40
|
+
export declare function assertSafeWireguard(text: string, what: string): WireguardConfig;
|
|
41
|
+
/** The config as text, written from the checked values. `endpointIps` puts an address in a peer's place. */
|
|
42
|
+
export declare function renderWireguard(config: WireguardConfig, endpointIps?: Readonly<Record<number, string>>): string;
|
|
43
|
+
export declare function wireguardEndpoints(config: WireguardConfig): WireguardEndpoint[];
|
|
44
|
+
/** The tunnel's own IPv4 address (what the kill switch lets through once the tunnel is up). */
|
|
45
|
+
export declare function wireguardTunnelAddress(config: WireguardConfig): string;
|
|
46
|
+
/** True when some peer sends all IPv4 traffic through the tunnel (AllowedIPs 0.0.0.0/0). */
|
|
47
|
+
export declare function routesEverything(config: WireguardConfig): boolean;
|
|
48
|
+
/** Whether `sc query` output says the tunnel service is running. */
|
|
49
|
+
export declare function serviceRunning(scOutput: string): boolean;
|
|
50
|
+
/** Whether `wg show <tunnel> latest-handshakes` says a peer has completed a handshake (a time that is not 0). */
|
|
51
|
+
export declare function handshakeDone(wgOutput: string): boolean;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// A WireGuard config (.conf) is untrusted data: it comes from a file you were given, and WireGuard for Windows runs the
|
|
2
|
+
// programs of PreUp/PostUp/PreDown/PostDown with administrator rights. Only the settings on a short list are accepted;
|
|
3
|
+
// anything else is refused by name, and the config that is handed on is written again from the checked values, never
|
|
4
|
+
// copied. Problem messages name keys and lines, never values (the private and preshared keys are secrets). Also answers
|
|
5
|
+
// where the tunnel connects and which address it has, which the kill switch needs. Pure: no files, no network.
|
|
6
|
+
import net from 'node:net';
|
|
7
|
+
export const MAX_WIREGUARD_BYTES = 16 * 1024;
|
|
8
|
+
const MAX_LINES = 300;
|
|
9
|
+
const MAX_PEERS = 16;
|
|
10
|
+
/** Keys that run a program, change the routes or write the config back. Refused with a clear reason. */
|
|
11
|
+
export const WIREGUARD_PROGRAM_KEYS = ['preup', 'postup', 'predown', 'postdown', 'table', 'saveconfig'];
|
|
12
|
+
const INTERFACE_KEYS = new Set(['privatekey', 'address', 'dns', 'mtu', 'listenport']);
|
|
13
|
+
const PEER_KEYS = new Set(['publickey', 'presharedkey', 'allowedips', 'endpoint', 'persistentkeepalive']);
|
|
14
|
+
/** Keys that may be given more than once (their values are joined). */
|
|
15
|
+
const LIST_KEYS = new Set(['address', 'dns', 'allowedips']);
|
|
16
|
+
const KEY = /^[A-Za-z0-9+/]{43}=$/;
|
|
17
|
+
const HOST = /^(?:\d{1,3}(?:\.\d{1,3}){3}|[A-Za-z0-9](?:[A-Za-z0-9.-]{0,251}[A-Za-z0-9])?)$/;
|
|
18
|
+
function validCidr(text) {
|
|
19
|
+
const [addr, prefix, ...rest] = text.split('/');
|
|
20
|
+
if (rest.length > 0 || !addr)
|
|
21
|
+
return false;
|
|
22
|
+
const version = net.isIP(addr);
|
|
23
|
+
if (version === 0)
|
|
24
|
+
return false;
|
|
25
|
+
if (prefix === undefined)
|
|
26
|
+
return true;
|
|
27
|
+
if (!/^\d{1,3}$/.test(prefix))
|
|
28
|
+
return false;
|
|
29
|
+
return Number(prefix) <= (version === 4 ? 32 : 128);
|
|
30
|
+
}
|
|
31
|
+
const inRange = (text, low, high) => {
|
|
32
|
+
if (!/^\d{1,5}$/.test(text))
|
|
33
|
+
return null;
|
|
34
|
+
const n = Number(text);
|
|
35
|
+
return n >= low && n <= high ? n : null;
|
|
36
|
+
};
|
|
37
|
+
const splitList = (text) => text
|
|
38
|
+
.split(',')
|
|
39
|
+
.map((s) => s.trim())
|
|
40
|
+
.filter(Boolean);
|
|
41
|
+
/**
|
|
42
|
+
* Checks a config. `ok: false` lists the problems (no values in them); `ok: true` gives the checked settings. It needs
|
|
43
|
+
* one [Interface] with a PrivateKey and an IPv4 Address, and at least one [Peer] with PublicKey, AllowedIPs and an
|
|
44
|
+
* Endpoint that is a name or an IPv4 address (the kill switch rules are IPv4).
|
|
45
|
+
*/
|
|
46
|
+
export function inspectWireguard(text) {
|
|
47
|
+
const problems = [];
|
|
48
|
+
const refuse = (message) => {
|
|
49
|
+
if (problems.length < 20 && !problems.includes(message))
|
|
50
|
+
problems.push(message);
|
|
51
|
+
};
|
|
52
|
+
if (Buffer.byteLength(text, 'utf8') > MAX_WIREGUARD_BYTES)
|
|
53
|
+
return { ok: false, problems: ['the file is too big'] };
|
|
54
|
+
if (text.includes('\0'))
|
|
55
|
+
return { ok: false, problems: ['the file is not text'] };
|
|
56
|
+
const lines = (text.charCodeAt(0) === 0xfeff ? text.slice(1) : text).split(/\r?\n/);
|
|
57
|
+
if (lines.length > MAX_LINES)
|
|
58
|
+
return { ok: false, problems: ['the file has too many lines'] };
|
|
59
|
+
const sections = [];
|
|
60
|
+
let current = null;
|
|
61
|
+
lines.forEach((raw, i) => {
|
|
62
|
+
const at = 'line ' + (i + 1) + ': ';
|
|
63
|
+
const line = raw.replace(/#.*$/, '').trim();
|
|
64
|
+
if (!line)
|
|
65
|
+
return;
|
|
66
|
+
const header = /^\[([A-Za-z]{1,20})\]$/.exec(line);
|
|
67
|
+
if (header) {
|
|
68
|
+
const name = header[1].toLowerCase();
|
|
69
|
+
if (name !== 'interface' && name !== 'peer') {
|
|
70
|
+
current = null;
|
|
71
|
+
return refuse(at + '[' + name + '] is not allowed');
|
|
72
|
+
}
|
|
73
|
+
current = { kind: name, values: new Map() };
|
|
74
|
+
sections.push(current);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const pair = /^([A-Za-z]{1,40})\s*=\s*(.*)$/.exec(line);
|
|
78
|
+
if (!pair)
|
|
79
|
+
return refuse(at + 'not a "Key = value" line');
|
|
80
|
+
const key = pair[1].toLowerCase();
|
|
81
|
+
if (WIREGUARD_PROGRAM_KEYS.includes(key))
|
|
82
|
+
return refuse(at + pair[1] + ' runs a program or changes how the tunnel is routed or saved');
|
|
83
|
+
if (!current)
|
|
84
|
+
return refuse(at + pair[1] + ' is outside a section');
|
|
85
|
+
if (!(current.kind === 'interface' ? INTERFACE_KEYS : PEER_KEYS).has(key))
|
|
86
|
+
return refuse(at + pair[1] + ' is not on the list of settings that are safe to accept');
|
|
87
|
+
const seen = current.values.get(key);
|
|
88
|
+
if (seen && !LIST_KEYS.has(key))
|
|
89
|
+
return refuse(at + pair[1] + ' is given twice');
|
|
90
|
+
current.values.set(key, [...(seen ?? []), pair[2].trim()]);
|
|
91
|
+
});
|
|
92
|
+
const one = (s, key) => s.values.get(key)?.[0];
|
|
93
|
+
const joined = (s, key) => (s.values.get(key) ?? []).flatMap(splitList);
|
|
94
|
+
const interfaces = sections.filter((s) => s.kind === 'interface');
|
|
95
|
+
const peers = sections.filter((s) => s.kind === 'peer');
|
|
96
|
+
if (interfaces.length !== 1)
|
|
97
|
+
refuse('it needs exactly one [Interface] section');
|
|
98
|
+
if (peers.length === 0)
|
|
99
|
+
refuse('it has no [Peer] section');
|
|
100
|
+
if (peers.length > MAX_PEERS)
|
|
101
|
+
refuse('it has too many [Peer] sections');
|
|
102
|
+
if (problems.length)
|
|
103
|
+
return { ok: false, problems };
|
|
104
|
+
const face = interfaces[0];
|
|
105
|
+
const privateKey = one(face, 'privatekey');
|
|
106
|
+
if (!privateKey)
|
|
107
|
+
refuse('[Interface] has no PrivateKey');
|
|
108
|
+
else if (!KEY.test(privateKey))
|
|
109
|
+
refuse('PrivateKey is not a WireGuard key (44 characters of base64)');
|
|
110
|
+
const addresses = joined(face, 'address');
|
|
111
|
+
if (addresses.length === 0 || !addresses.every(validCidr))
|
|
112
|
+
refuse('[Interface] needs valid Address values');
|
|
113
|
+
else if (!addresses.some((a) => net.isIPv4(a.split('/')[0])))
|
|
114
|
+
refuse('Address needs an IPv4 address (the kill switch works on IPv4)');
|
|
115
|
+
const dns = joined(face, 'dns');
|
|
116
|
+
if (!dns.every((d) => net.isIP(d) !== 0))
|
|
117
|
+
refuse('DNS may only list IP addresses');
|
|
118
|
+
const mtuText = one(face, 'mtu');
|
|
119
|
+
const mtu = mtuText === undefined ? undefined : inRange(mtuText, 576, 1500);
|
|
120
|
+
if (mtuText !== undefined && mtu === null)
|
|
121
|
+
refuse('MTU must be a number from 576 to 1500');
|
|
122
|
+
const portText = one(face, 'listenport');
|
|
123
|
+
const listenPort = portText === undefined ? undefined : inRange(portText, 1, 65535);
|
|
124
|
+
if (portText !== undefined && listenPort === null)
|
|
125
|
+
refuse('ListenPort must be a number from 1 to 65535');
|
|
126
|
+
const checked = [];
|
|
127
|
+
peers.forEach((p, n) => {
|
|
128
|
+
const who = '[Peer] ' + (n + 1) + ': ';
|
|
129
|
+
const publicKey = one(p, 'publickey');
|
|
130
|
+
if (!publicKey || !KEY.test(publicKey))
|
|
131
|
+
return refuse(who + 'PublicKey is missing or not a WireGuard key');
|
|
132
|
+
const preshared = one(p, 'presharedkey');
|
|
133
|
+
if (preshared !== undefined && !KEY.test(preshared))
|
|
134
|
+
return refuse(who + 'PresharedKey is not a WireGuard key');
|
|
135
|
+
const allowed = joined(p, 'allowedips');
|
|
136
|
+
if (allowed.length === 0 || !allowed.every(validCidr))
|
|
137
|
+
return refuse(who + 'AllowedIPs is missing or invalid');
|
|
138
|
+
const endpoint = one(p, 'endpoint');
|
|
139
|
+
const m = endpoint ? /^([^:[\]]+):(\d{1,5})$/.exec(endpoint) : null;
|
|
140
|
+
if (!m || !HOST.test(m[1]) || inRange(m[2], 1, 65535) === null)
|
|
141
|
+
return refuse(who + 'Endpoint must be host:port with a name or an IPv4 address');
|
|
142
|
+
if (/^\d+\.\d+\.\d+\.\d+$/.test(m[1]) && !net.isIPv4(m[1]))
|
|
143
|
+
return refuse(who + 'Endpoint is not a valid address');
|
|
144
|
+
const keepText = one(p, 'persistentkeepalive');
|
|
145
|
+
let keepalive;
|
|
146
|
+
if (keepText !== undefined && keepText.toLowerCase() !== 'off') {
|
|
147
|
+
const k = inRange(keepText, 0, 65535);
|
|
148
|
+
if (k === null)
|
|
149
|
+
return refuse(who + 'PersistentKeepalive must be a number or off');
|
|
150
|
+
keepalive = k;
|
|
151
|
+
}
|
|
152
|
+
checked.push({
|
|
153
|
+
publicKey,
|
|
154
|
+
...(preshared ? { presharedKey: preshared } : {}),
|
|
155
|
+
allowedIps: allowed,
|
|
156
|
+
endpointHost: m[1],
|
|
157
|
+
endpointPort: Number(m[2]),
|
|
158
|
+
...(keepalive !== undefined ? { keepalive } : {}),
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
if (problems.length || !privateKey)
|
|
162
|
+
return { ok: false, problems };
|
|
163
|
+
return {
|
|
164
|
+
ok: true,
|
|
165
|
+
config: {
|
|
166
|
+
privateKey,
|
|
167
|
+
addresses,
|
|
168
|
+
dns,
|
|
169
|
+
...(typeof mtu === 'number' ? { mtu } : {}),
|
|
170
|
+
...(typeof listenPort === 'number' ? { listenPort } : {}),
|
|
171
|
+
peers: checked,
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/** The checked config; throws, saying why (never with a key), when it is not safe. `what` names the file. */
|
|
176
|
+
export function assertSafeWireguard(text, what) {
|
|
177
|
+
const report = inspectWireguard(text);
|
|
178
|
+
if (report.ok)
|
|
179
|
+
return report.config;
|
|
180
|
+
throw new Error(what +
|
|
181
|
+
' was refused because it could run programs or is not a plain WireGuard config:\n - ' +
|
|
182
|
+
report.problems.slice(0, 5).join('\n - '));
|
|
183
|
+
}
|
|
184
|
+
/** The config as text, written from the checked values. `endpointIps` puts an address in a peer's place. */
|
|
185
|
+
export function renderWireguard(config, endpointIps = {}) {
|
|
186
|
+
const out = ['[Interface]', 'PrivateKey = ' + config.privateKey, 'Address = ' + config.addresses.join(', ')];
|
|
187
|
+
if (config.dns.length)
|
|
188
|
+
out.push('DNS = ' + config.dns.join(', '));
|
|
189
|
+
if (config.mtu !== undefined)
|
|
190
|
+
out.push('MTU = ' + config.mtu);
|
|
191
|
+
if (config.listenPort !== undefined)
|
|
192
|
+
out.push('ListenPort = ' + config.listenPort);
|
|
193
|
+
config.peers.forEach((p, i) => {
|
|
194
|
+
out.push('', '[Peer]', 'PublicKey = ' + p.publicKey);
|
|
195
|
+
if (p.presharedKey)
|
|
196
|
+
out.push('PresharedKey = ' + p.presharedKey);
|
|
197
|
+
out.push('AllowedIPs = ' + p.allowedIps.join(', '), 'Endpoint = ' + (endpointIps[i] ?? p.endpointHost) + ':' + p.endpointPort);
|
|
198
|
+
if (p.keepalive !== undefined)
|
|
199
|
+
out.push('PersistentKeepalive = ' + p.keepalive);
|
|
200
|
+
});
|
|
201
|
+
return out.join('\r\n') + '\r\n';
|
|
202
|
+
}
|
|
203
|
+
export function wireguardEndpoints(config) {
|
|
204
|
+
return config.peers.map((p) => ({ host: p.endpointHost, port: p.endpointPort }));
|
|
205
|
+
}
|
|
206
|
+
/** The tunnel's own IPv4 address (what the kill switch lets through once the tunnel is up). */
|
|
207
|
+
export function wireguardTunnelAddress(config) {
|
|
208
|
+
const found = config.addresses.map((a) => a.split('/')[0]).find((a) => net.isIPv4(a));
|
|
209
|
+
if (!found)
|
|
210
|
+
throw new Error('the config has no IPv4 address');
|
|
211
|
+
return found;
|
|
212
|
+
}
|
|
213
|
+
/** True when some peer sends all IPv4 traffic through the tunnel (AllowedIPs 0.0.0.0/0). */
|
|
214
|
+
export function routesEverything(config) {
|
|
215
|
+
return config.peers.some((p) => p.allowedIps.some((a) => a === '0.0.0.0/0'));
|
|
216
|
+
}
|
|
217
|
+
/** Whether `sc query` output says the tunnel service is running. */
|
|
218
|
+
export function serviceRunning(scOutput) {
|
|
219
|
+
return /STATE\s*:\s*\d+\s+RUNNING/i.test(scOutput);
|
|
220
|
+
}
|
|
221
|
+
/** Whether `wg show <tunnel> latest-handshakes` says a peer has completed a handshake (a time that is not 0). */
|
|
222
|
+
export function handshakeDone(wgOutput) {
|
|
223
|
+
return wgOutput.split(/\r?\n/).some((line) => {
|
|
224
|
+
const t = /\s(\d+)\s*$/.exec(line);
|
|
225
|
+
return t !== null && Number(t[1]) > 0;
|
|
226
|
+
});
|
|
227
|
+
}
|
package/dist/sync/sync-cycle.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { assertNoLinks } from '../core/util/index.js';
|
|
3
3
|
import { hostClaudeDir, PluginShare } from '../host/profile/index.js';
|
|
4
|
-
import { storageOf } from '../host/storage/index.js';
|
|
4
|
+
import { hostDataFolder, hostDataRoot, storageOf } from '../host/storage/index.js';
|
|
5
5
|
import { hostClaudeMemDir, syncClaudeMem } from './claude-mem.js';
|
|
6
6
|
import { readSyncState, writeSyncState } from './sync-state.js';
|
|
7
7
|
import { syncHistoryIn, syncHistoryOut } from './history.js';
|
|
8
|
-
const guestClaude = (dataPath) => path.join(dataPath, 'state', 'claude');
|
|
9
|
-
const guestMem = (dataPath) => path.join(dataPath, 'state', 'claude-mem');
|
|
8
|
+
const guestClaude = (dataPath) => path.join(hostDataFolder(dataPath, 'state'), 'claude');
|
|
9
|
+
const guestMem = (dataPath) => path.join(hostDataFolder(dataPath, 'state'), 'claude-mem');
|
|
10
10
|
/** Sync paths for the sandbox side, after checking nothing on the way there is a link planted by the sandbox. */
|
|
11
11
|
const toPaths = (state, dataPath) => {
|
|
12
|
-
assertNoLinks(dataPath, guestClaude(dataPath));
|
|
12
|
+
assertNoLinks(hostDataRoot(dataPath), guestClaude(dataPath));
|
|
13
13
|
return {
|
|
14
14
|
hostClaudeDir: state.hostClaudeDir,
|
|
15
15
|
guestClaudeDir: guestClaude(dataPath),
|
|
@@ -38,7 +38,7 @@ export function syncProjectIn(dataPath, project) {
|
|
|
38
38
|
: 0;
|
|
39
39
|
}
|
|
40
40
|
const memSync = (dataPath, state, direction) => {
|
|
41
|
-
assertNoLinks(dataPath, guestMem(dataPath));
|
|
41
|
+
assertNoLinks(hostDataRoot(dataPath), guestMem(dataPath));
|
|
42
42
|
return syncClaudeMem(hostClaudeMemDir(), guestMem(dataPath), state, direction);
|
|
43
43
|
};
|
|
44
44
|
/** Before launch: finish syncing the previous run out, then bring this PC's history (and claude-mem) in. */
|
package/dist/sync/watcher.d.ts
CHANGED
|
@@ -22,12 +22,25 @@ export declare class Watcher {
|
|
|
22
22
|
private awake;
|
|
23
23
|
private views;
|
|
24
24
|
private stops;
|
|
25
|
+
private watchdogTimer;
|
|
26
|
+
private watchdog;
|
|
27
|
+
private watchdogBusy;
|
|
28
|
+
private lastCpu;
|
|
29
|
+
private lastHostLog;
|
|
25
30
|
constructor(dataPath: string, resume?: WatcherResume);
|
|
26
31
|
/** Runs until the sandbox has closed and the final sync is done (or the end deadline passed). */
|
|
27
32
|
run(): Promise<void>;
|
|
33
|
+
/** Contained storage of this data set (only when a mounted image is on record), else null. */
|
|
34
|
+
private contained;
|
|
35
|
+
/** The mounted image stays mounted until the sandbox is gone: this watcher holds it. */
|
|
36
|
+
private holdContained;
|
|
37
|
+
/** The sandbox is gone and the last sync done: let go of the image, which dismounts it when nothing else holds it. */
|
|
38
|
+
private releaseContained;
|
|
28
39
|
private note;
|
|
29
40
|
/** Everything that runs alongside the sandbox, in this order. */
|
|
30
41
|
private start;
|
|
42
|
+
/** One watchdog sample: this PC's memory and CPU; asks the sandbox to slow down while the PC is low. Never throws. */
|
|
43
|
+
private watchHost;
|
|
31
44
|
/** The repo guard, then history and (at the end) claude-mem back to this PC. */
|
|
32
45
|
private sync;
|
|
33
46
|
/** The running sessions and their conversations, for the next launch (resume.sessions). */
|
package/dist/sync/watcher.js
CHANGED
|
@@ -7,24 +7,29 @@ import { writeFileAtomic } from '../shared/io/index.js';
|
|
|
7
7
|
import { refuseInSelfTest } from '../host/test-guard.js';
|
|
8
8
|
import { errMsg, sleep } from '../core/util/index.js';
|
|
9
9
|
import { RepoGuardStore } from '../host/guard/index.js';
|
|
10
|
-
import { isSandboxRunning, spawnDetached } from '../host/sandbox/index.js';
|
|
10
|
+
import { isSandboxRunning, realContainedDeps, spawnDetached } from '../host/sandbox/index.js';
|
|
11
11
|
import { readActiveViews, WorkspaceView } from '../host/workspace-view/index.js';
|
|
12
12
|
import { readForwards, sandboxAddress, startPortForwards } from '../host/network/index.js';
|
|
13
13
|
import { AuditCollector, readAuditSettings } from '../host/audit/collect.js';
|
|
14
14
|
import { readMarkedProjects, saveDataSize, watchAndMark } from '../host/guard/index.js';
|
|
15
15
|
import { sizeWithin } from '../core/disk.js';
|
|
16
|
-
import { autoRestartAllowed, causeSince, KeepAwake, LifeRecord, saveCrashReport, SessionLedger, } from '../host/life/index.js';
|
|
16
|
+
import { autoRestartAllowed, causeSince, appendHostMemory, cpuPercentBetween, KeepAwake, LifeRecord, readHostMemoryAsync, readLoadSettings, REQUEST_SECONDS, WATCHDOG_START, watchdogStep, saveCrashReport, SessionLedger, } from '../host/life/index.js';
|
|
17
17
|
import { SandboxChannel } from '../host/channel/index.js';
|
|
18
18
|
import { ProjectRegistry } from '../host/projects/registry.js';
|
|
19
|
-
import { storageOf } from '../host/storage/index.js';
|
|
19
|
+
import { ContainedStorage, setNameOf, storageOf } from '../host/storage/index.js';
|
|
20
20
|
import { clearSecrets } from '../host/secrets/index.js';
|
|
21
21
|
import { endSharing, sharingDeps } from '../host/sharing/index.js';
|
|
22
22
|
import { PACKAGE_ROOT } from '../core/package-info.js';
|
|
23
23
|
import { syncBack } from './sync-cycle.js';
|
|
24
|
+
import os from 'node:os';
|
|
24
25
|
const WATCH_INTERVAL_MS = 60 * 1000;
|
|
25
26
|
/** The first sync waits this long: the sandbox is still booting. */
|
|
26
27
|
const FIRST_SYNC_MS = 30000;
|
|
27
28
|
const AUDIT_FLUSH_MS = 10000;
|
|
29
|
+
/** How often this PC's memory and CPU are sampled (the sample runs asynchronously: it never holds anything up). */
|
|
30
|
+
const WATCHDOG_MS = 10000;
|
|
31
|
+
/** The host memory log gets a line this often, and at every sample while the PC is low. */
|
|
32
|
+
const HOST_LOG_EVERY_MS = 60000;
|
|
28
33
|
/** After the sandbox closed: a moment for its last writes to land before the final sync. */
|
|
29
34
|
const SETTLE_MS = 3000;
|
|
30
35
|
/** The longest the watcher may take after the sandbox closed (final syncs) before it stops anyway. */
|
|
@@ -79,6 +84,11 @@ export class Watcher {
|
|
|
79
84
|
awake = null;
|
|
80
85
|
views = [];
|
|
81
86
|
stops = [];
|
|
87
|
+
watchdogTimer = null;
|
|
88
|
+
watchdog = WATCHDOG_START;
|
|
89
|
+
watchdogBusy = false;
|
|
90
|
+
lastCpu = os.cpus();
|
|
91
|
+
lastHostLog = 0;
|
|
82
92
|
constructor(dataPath, resume = { restartAfterCrash: false }) {
|
|
83
93
|
this.dataPath = dataPath;
|
|
84
94
|
this.resume = resume;
|
|
@@ -95,6 +105,7 @@ export class Watcher {
|
|
|
95
105
|
fs.mkdirSync(path.dirname(watcherLog(this.dataPath)), { recursive: true });
|
|
96
106
|
writeFileAtomic(watcherPidFile(this.dataPath), String(process.pid));
|
|
97
107
|
this.start();
|
|
108
|
+
this.holdContained();
|
|
98
109
|
try {
|
|
99
110
|
await sleep(FIRST_SYNC_MS);
|
|
100
111
|
while (isSandboxRunning()) {
|
|
@@ -116,10 +127,44 @@ export class Watcher {
|
|
|
116
127
|
}
|
|
117
128
|
finally {
|
|
118
129
|
this.stop();
|
|
130
|
+
await this.releaseContained();
|
|
119
131
|
}
|
|
120
132
|
// After stop(): its pid file is gone, so the sandbox started again gets a watcher of its own.
|
|
121
133
|
this.restartAfterCrash();
|
|
122
134
|
}
|
|
135
|
+
/** Contained storage of this data set (only when a mounted image is on record), else null. */
|
|
136
|
+
contained() {
|
|
137
|
+
try {
|
|
138
|
+
const storage = new ContainedStorage(realContainedDeps(this.storageRoot, 64), setNameOf(this.dataPath));
|
|
139
|
+
return storage.isRecorded() ? storage : null;
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// Not a name contained storage can use: there is none.
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** The mounted image stays mounted until the sandbox is gone: this watcher holds it. */
|
|
147
|
+
holdContained() {
|
|
148
|
+
try {
|
|
149
|
+
this.contained()?.hold({ id: 'watcher', pid: process.pid });
|
|
150
|
+
}
|
|
151
|
+
catch (e) {
|
|
152
|
+
this.note('contained storage: could not record the watcher: ' + errMsg(e));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/** The sandbox is gone and the last sync done: let go of the image, which dismounts it when nothing else holds it. */
|
|
156
|
+
async releaseContained() {
|
|
157
|
+
try {
|
|
158
|
+
const result = await this.contained()?.release('watcher');
|
|
159
|
+
if (result)
|
|
160
|
+
this.note(result.dismounted
|
|
161
|
+
? 'contained storage dismounted'
|
|
162
|
+
: 'contained storage stays mounted: ' + (result.note ?? ''));
|
|
163
|
+
}
|
|
164
|
+
catch (e) {
|
|
165
|
+
this.note('contained storage: ' + errMsg(e));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
123
168
|
note(message) {
|
|
124
169
|
try {
|
|
125
170
|
fs.appendFileSync(watcherLog(this.dataPath), new Date().toISOString() + ' ' + message + '\n');
|
|
@@ -162,9 +207,51 @@ export class Watcher {
|
|
|
162
207
|
}
|
|
163
208
|
});
|
|
164
209
|
this.stops = [...viewStops, stopForwards, stopMarking];
|
|
210
|
+
// Memory and CPU of this PC: when low, the sandbox is asked to slow its builds down (host/life/watchdog.ts).
|
|
211
|
+
this.watchdogTimer = setInterval(() => void this.watchHost(), WATCHDOG_MS);
|
|
212
|
+
this.watchdogTimer.unref();
|
|
165
213
|
// sandbox.keepAwake, and the life record that tells the next launch how this run ended (host/life).
|
|
166
214
|
this.awake = this.life.read()?.keepAwake ? KeepAwake.start(process.pid, storageRoot) : null;
|
|
167
215
|
}
|
|
216
|
+
/** One watchdog sample: this PC's memory and CPU; asks the sandbox to slow down while the PC is low. Never throws. */
|
|
217
|
+
async watchHost() {
|
|
218
|
+
if (this.watchdogBusy)
|
|
219
|
+
return;
|
|
220
|
+
this.watchdogBusy = true;
|
|
221
|
+
try {
|
|
222
|
+
const settings = readLoadSettings(this.storageRoot, this.dataPath);
|
|
223
|
+
const memory = await readHostMemoryAsync(this.storageRoot);
|
|
224
|
+
const cpus = os.cpus();
|
|
225
|
+
const cpuPercent = cpuPercentBetween(this.lastCpu, cpus);
|
|
226
|
+
this.lastCpu = cpus;
|
|
227
|
+
const nowMs = Date.now();
|
|
228
|
+
const step = watchdogStep(this.watchdog, {
|
|
229
|
+
memory,
|
|
230
|
+
cpuPercent,
|
|
231
|
+
intervalSeconds: WATCHDOG_MS / 1000,
|
|
232
|
+
nowMs,
|
|
233
|
+
reserveHostMb: settings.reserveHostMb,
|
|
234
|
+
profile: settings.profile,
|
|
235
|
+
});
|
|
236
|
+
this.watchdog = step.state;
|
|
237
|
+
if (memory && (step.state.low || nowMs - this.lastHostLog >= HOST_LOG_EVERY_MS)) {
|
|
238
|
+
this.lastHostLog = nowMs;
|
|
239
|
+
appendHostMemory(path.join(this.storageRoot, 'logs', 'host-memory-' + path.basename(this.dataPath) + '.log'), memory, cpuPercent);
|
|
240
|
+
}
|
|
241
|
+
if (step.changed)
|
|
242
|
+
this.note(step.state.low
|
|
243
|
+
? 'this PC is low (' + step.reason + '): asking the sandbox to slow its builds down'
|
|
244
|
+
: 'this PC has recovered: no longer asking the sandbox to slow down');
|
|
245
|
+
if (step.send)
|
|
246
|
+
new SandboxChannel(this.dataPath).pressure(step.reason, REQUEST_SECONDS);
|
|
247
|
+
}
|
|
248
|
+
catch (e) {
|
|
249
|
+
this.note('host watchdog: ' + errMsg(e));
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
this.watchdogBusy = false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
168
255
|
/** The repo guard, then history and (at the end) claude-mem back to this PC. */
|
|
169
256
|
sync(final) {
|
|
170
257
|
try {
|
|
@@ -235,6 +322,8 @@ export class Watcher {
|
|
|
235
322
|
/** Stops what start() started, syncs the filtered workspaces a last time and records the data folder's size. */
|
|
236
323
|
stop() {
|
|
237
324
|
this.awake?.stop();
|
|
325
|
+
if (this.watchdogTimer)
|
|
326
|
+
clearInterval(this.watchdogTimer);
|
|
238
327
|
this.stops.forEach((stop) => stop());
|
|
239
328
|
if (this.auditTimer)
|
|
240
329
|
clearInterval(this.auditTimer);
|