windows-development-sandbox 1.7.0 → 1.10.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 +12 -1
- 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 +49 -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 +20 -3
- package/dist/cli/commands/web.d.ts +5 -0
- package/dist/cli/commands/web.js +40 -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 +9 -1
- package/dist/core/schema/config.d.ts +14 -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 +4 -4
- package/dist/core/schema/web.d.ts +1 -0
- package/dist/core/schema/web.js +4 -1
- 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 +5 -2
- 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 +71 -0
- package/dist/guest/governor/decide.js +211 -0
- package/dist/guest/governor/governor.d.ts +45 -0
- package/dist/guest/governor/governor.js +230 -0
- package/dist/guest/governor/index.d.ts +2 -0
- package/dist/guest/governor/index.js +2 -0
- package/dist/guest/network/firewall.d.ts +1 -0
- package/dist/guest/network/firewall.js +65 -23
- package/dist/guest/runtime/csc.d.ts +1 -0
- package/dist/guest/runtime/csc.js +1 -0
- package/dist/guest/runtime/process.js +53 -5
- package/dist/guest/sessions/viewer.js +3 -1
- package/dist/guest/setup/claude-mem.js +12 -4
- package/dist/guest/toolchains/languages/node.js +29 -3
- 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 +41 -0
- package/dist/host/life/host-memory.js +140 -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 +56 -0
- package/dist/host/life/watchdog.js +124 -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 +83 -18
- package/dist/runtime/daemon.js +1805 -1221
- package/dist/runtime/db.js +83 -18
- package/dist/runtime/main.js +779 -219
- package/dist/runtime/netproxy.js +73 -11
- package/dist/runtime/view.js +91 -16
- 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/guest-boot.test.js +47 -1
- 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 +355 -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/network.test.js +20 -1
- 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 +8 -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/io/copy-tree.d.ts +7 -0
- package/dist/shared/io/copy-tree.js +45 -0
- package/dist/shared/io/index.d.ts +2 -0
- package/dist/shared/io/index.js +2 -0
- package/dist/shared/io/robocopy-result.d.ts +6 -0
- package/dist/shared/io/robocopy-result.js +23 -0
- package/dist/shared/memory/index.d.ts +1 -0
- package/dist/shared/memory/index.js +1 -0
- package/dist/shared/memory/policy.d.ts +38 -0
- package/dist/shared/memory/policy.js +63 -0
- package/dist/shared/network/firewall-rules.d.ts +8 -0
- package/dist/shared/network/firewall-rules.js +28 -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 +128 -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 +42 -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 +466 -0
- package/sandbox/csharp/PtyHost.cs +111 -5
- package/sandbox/scripts/new-shortcuts.ps1 +3 -1
- package/schemas/config.schema.json +66 -8
- 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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './policy.js';
|
|
@@ -0,0 +1,38 @@
|
|
|
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
|
+
/** The disks' queue length (all together) at or above this for diskSeconds: builds writing through mapped folders. */
|
|
27
|
+
readonly diskQueue: number;
|
|
28
|
+
readonly diskSeconds: number;
|
|
29
|
+
}
|
|
30
|
+
export declare const HOST_THRESHOLDS: Readonly<Record<LoadProfile, HostThresholds>>;
|
|
31
|
+
/** The CPU cap (percent of the PC) the gentle profile uses: 15 points lower, never below 50. "none" = 100. */
|
|
32
|
+
export declare function gentleCpuLimit(cpuLimit: 'none' | number): number;
|
|
33
|
+
/** The CPU cap the sandbox runs with (percent; null = none) for a profile. */
|
|
34
|
+
export declare function effectiveCpuLimit(cpuLimit: 'none' | number, profile: LoadProfile): number | null;
|
|
35
|
+
/** Cargo jobs that fit the memory (about 4 GB per rustc) and the CPUs, fewer when several agent sessions build. */
|
|
36
|
+
export declare function cargoJobs(cpus: number, memoryGb: number, sessions: number): number;
|
|
37
|
+
/** The most heavy compilers (rustc, clippy-driver, link ...) that run at once in the agents' job: only with vGPU on. */
|
|
38
|
+
export declare function maxCompilers(cpus: number, profile: LoadProfile): number | null;
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
diskQueue: 32,
|
|
29
|
+
diskSeconds: 40,
|
|
30
|
+
},
|
|
31
|
+
gentle: {
|
|
32
|
+
availableMinMb: 6144,
|
|
33
|
+
availablePercent: 15,
|
|
34
|
+
commitAvailablePercent: 20,
|
|
35
|
+
calmSamples: 3,
|
|
36
|
+
cpuPercent: 90,
|
|
37
|
+
cpuSeconds: 45,
|
|
38
|
+
diskQueue: 16,
|
|
39
|
+
diskSeconds: 30,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
/** The CPU cap (percent of the PC) the gentle profile uses: 15 points lower, never below 50. "none" = 100. */
|
|
43
|
+
export function gentleCpuLimit(cpuLimit) {
|
|
44
|
+
return Math.max(50, (cpuLimit === 'none' ? 100 : cpuLimit) - 15);
|
|
45
|
+
}
|
|
46
|
+
/** The CPU cap the sandbox runs with (percent; null = none) for a profile. */
|
|
47
|
+
export function effectiveCpuLimit(cpuLimit, profile) {
|
|
48
|
+
if (profile === 'gentle')
|
|
49
|
+
return Math.min(gentleCpuLimit(cpuLimit), cpuLimit === 'none' ? 100 : cpuLimit);
|
|
50
|
+
return cpuLimit === 'none' ? null : cpuLimit;
|
|
51
|
+
}
|
|
52
|
+
/** Cargo jobs that fit the memory (about 4 GB per rustc) and the CPUs, fewer when several agent sessions build. */
|
|
53
|
+
export function cargoJobs(cpus, memoryGb, sessions) {
|
|
54
|
+
const byCpu = Math.max(2, Math.min(4, Math.floor(cpus / 4)));
|
|
55
|
+
const byMemory = Math.max(1, Math.floor(memoryGb / 4));
|
|
56
|
+
const jobs = Math.min(byCpu, byMemory);
|
|
57
|
+
// All the agents' builds share the sandbox: about 8 jobs in total however many sessions there are.
|
|
58
|
+
return Math.max(1, Math.min(jobs, Math.floor(8 / Math.max(1, sessions))));
|
|
59
|
+
}
|
|
60
|
+
/** The most heavy compilers (rustc, clippy-driver, link ...) that run at once in the agents' job: only with vGPU on. */
|
|
61
|
+
export function maxCompilers(cpus, profile) {
|
|
62
|
+
return profile === 'gentle' ? Math.max(2, Math.floor(cpus / 4)) : null;
|
|
63
|
+
}
|
|
@@ -22,3 +22,11 @@ export declare function parseDnsServers(ipconfigAll: string): string[];
|
|
|
22
22
|
* keeps working.
|
|
23
23
|
*/
|
|
24
24
|
export declare function buildFirewallCommands(exemptIps: string[]): string[][];
|
|
25
|
+
export declare const ATTACH_RULE = "AgentSandbox-Attach";
|
|
26
|
+
/**
|
|
27
|
+
* True when a Windows path has an 8.3 short name in it (`C:\Users\WDAGUT~1\...`). netsh refuses `program=` paths
|
|
28
|
+
* like that ("The application name could not be resolved"), so they are expanded first.
|
|
29
|
+
*/
|
|
30
|
+
export declare function hasShortName(file: string): boolean;
|
|
31
|
+
/** Inbound rule for ptyhost only, and only from the sandbox's own subnet (where the host's Hyper-V adapter is). */
|
|
32
|
+
export declare function attachRuleCommands(ptyHostExe: string): string[][];
|
|
@@ -135,3 +135,31 @@ export function buildFirewallCommands(exemptIps) {
|
|
|
135
135
|
}
|
|
136
136
|
return commands;
|
|
137
137
|
}
|
|
138
|
+
export const ATTACH_RULE = 'AgentSandbox-Attach';
|
|
139
|
+
/**
|
|
140
|
+
* True when a Windows path has an 8.3 short name in it (`C:\Users\WDAGUT~1\...`). netsh refuses `program=` paths
|
|
141
|
+
* like that ("The application name could not be resolved"), so they are expanded first.
|
|
142
|
+
*/
|
|
143
|
+
export function hasShortName(file) {
|
|
144
|
+
return file.split(/[/\\]/).some((part) => /~\d/.test(part));
|
|
145
|
+
}
|
|
146
|
+
/** Inbound rule for ptyhost only, and only from the sandbox's own subnet (where the host's Hyper-V adapter is). */
|
|
147
|
+
export function attachRuleCommands(ptyHostExe) {
|
|
148
|
+
return [
|
|
149
|
+
['advfirewall', 'firewall', 'delete', 'rule', 'name=' + ATTACH_RULE],
|
|
150
|
+
[
|
|
151
|
+
'advfirewall',
|
|
152
|
+
'firewall',
|
|
153
|
+
'add',
|
|
154
|
+
'rule',
|
|
155
|
+
'name=' + ATTACH_RULE,
|
|
156
|
+
'dir=in',
|
|
157
|
+
'action=allow',
|
|
158
|
+
'enable=yes',
|
|
159
|
+
'profile=any',
|
|
160
|
+
'protocol=tcp',
|
|
161
|
+
'remoteip=LocalSubnet',
|
|
162
|
+
'program=' + ptyHostExe,
|
|
163
|
+
],
|
|
164
|
+
];
|
|
165
|
+
}
|
|
@@ -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
|
}
|
|
@@ -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. */
|