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,15 @@
|
|
|
1
|
+
import { type ContainedDeps, type HolderId } from '../contained/contained-storage.js';
|
|
2
|
+
import type { DataSet, PreparedStorage, ProviderCheck, StorageProvider } from './types.js';
|
|
3
|
+
export declare class ContainedStorageProvider implements StorageProvider {
|
|
4
|
+
private readonly deps;
|
|
5
|
+
private readonly holder;
|
|
6
|
+
private readonly availability;
|
|
7
|
+
readonly id = "vhdx-mounted";
|
|
8
|
+
private storage;
|
|
9
|
+
constructor(deps: ContainedDeps, holder?: HolderId, availability?: () => ProviderCheck);
|
|
10
|
+
check(): Promise<ProviderCheck>;
|
|
11
|
+
/** Mounts the image (the one Windows permission prompt of a launch, when it is not mounted yet). */
|
|
12
|
+
prepare(dataSet: DataSet): Promise<PreparedStorage>;
|
|
13
|
+
/** Lets go of the mount (a launch that stopped before the sandbox started). The watcher holds it afterwards. */
|
|
14
|
+
release(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Contained storage for the Windows Sandbox engine (beta, untested on a real PC): the data set splits. `run\` stays
|
|
2
|
+
// the plain data folder shared in as before; tools, state, logs, databases and cache live in a disk image that is
|
|
3
|
+
// mounted at <storage>\mounts\<set> (no drive letter) and shared in from there, to the same guest paths.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { containedAvailability } from '../contained/check.js';
|
|
7
|
+
import { ContainedStorage } from '../contained/contained-storage.js';
|
|
8
|
+
import { containedMappings, setNameOf } from '../contained/layout.js';
|
|
9
|
+
export class ContainedStorageProvider {
|
|
10
|
+
deps;
|
|
11
|
+
holder;
|
|
12
|
+
availability;
|
|
13
|
+
id = 'vhdx-mounted';
|
|
14
|
+
storage = null;
|
|
15
|
+
constructor(deps, holder = { id: 'launch', pid: process.pid }, availability = containedAvailability) {
|
|
16
|
+
this.deps = deps;
|
|
17
|
+
this.holder = holder;
|
|
18
|
+
this.availability = availability;
|
|
19
|
+
}
|
|
20
|
+
check() {
|
|
21
|
+
return Promise.resolve(this.availability());
|
|
22
|
+
}
|
|
23
|
+
/** Mounts the image (the one Windows permission prompt of a launch, when it is not mounted yet). */
|
|
24
|
+
async prepare(dataSet) {
|
|
25
|
+
const storage = new ContainedStorage(this.deps, setNameOf(dataSet.dataPath));
|
|
26
|
+
this.storage = storage;
|
|
27
|
+
const mount = await storage.acquire(this.holder);
|
|
28
|
+
fs.mkdirSync(path.join(dataSet.dataPath, 'run'), { recursive: true });
|
|
29
|
+
const mappings = containedMappings(dataSet.dataPath, mount, dataSet.guest);
|
|
30
|
+
return {
|
|
31
|
+
attachments: mappings.map((m) => ({ kind: 'folder', host: m.host, guest: m.guest, readOnly: m.readOnly })),
|
|
32
|
+
mappings,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Lets go of the mount (a launch that stopped before the sandbox started). The watcher holds it afterwards. */
|
|
36
|
+
async release() {
|
|
37
|
+
await this.storage?.release(this.holder.id);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ResolvedMapping } from '../../../core/types.js';
|
|
2
|
+
import type { DataSet, PreparedStorage, ProviderCheck, StorageAttachment, StorageProvider } from './types.js';
|
|
3
|
+
/** The data folder as a read/write attachment. */
|
|
4
|
+
export declare function folderAttachment(dataSet: DataSet): StorageAttachment;
|
|
5
|
+
/** The data folder as a mapping (the launch builds its mapping list synchronously, so this is the plain function). */
|
|
6
|
+
export declare function dataMapping(dataSet: DataSet): ResolvedMapping;
|
|
7
|
+
export declare class FolderStorageProvider implements StorageProvider {
|
|
8
|
+
readonly id = "folder";
|
|
9
|
+
check(): Promise<ProviderCheck>;
|
|
10
|
+
prepare(dataSet: DataSet): Promise<PreparedStorage>;
|
|
11
|
+
release(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** The data folder as a read/write attachment. */
|
|
2
|
+
export function folderAttachment(dataSet) {
|
|
3
|
+
return { kind: 'folder', host: dataSet.dataPath, guest: dataSet.guest, readOnly: false };
|
|
4
|
+
}
|
|
5
|
+
/** The data folder as a mapping (the launch builds its mapping list synchronously, so this is the plain function). */
|
|
6
|
+
export function dataMapping(dataSet) {
|
|
7
|
+
return { host: dataSet.dataPath, guest: dataSet.guest, readOnly: false };
|
|
8
|
+
}
|
|
9
|
+
export class FolderStorageProvider {
|
|
10
|
+
id = 'folder';
|
|
11
|
+
check() {
|
|
12
|
+
return Promise.resolve({ available: true, reasons: [], fixes: [] });
|
|
13
|
+
}
|
|
14
|
+
prepare(dataSet) {
|
|
15
|
+
return Promise.resolve({ attachments: [folderAttachment(dataSet)], mappings: [dataMapping(dataSet)] });
|
|
16
|
+
}
|
|
17
|
+
release() {
|
|
18
|
+
return Promise.resolve();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { FolderStorageProvider, dataMapping, folderAttachment } from './folder.js';
|
|
2
|
+
export { ContainedStorageProvider } from './contained.js';
|
|
3
|
+
export { providerFor, type ProviderOptions } from './registry.js';
|
|
4
|
+
export type { DataSet, PreparedStorage, ProviderCheck, StorageAttachment, StorageProvider } from './types.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Config } from '../../../core/types.js';
|
|
2
|
+
import type { ContainedDeps } from '../contained/contained-storage.js';
|
|
3
|
+
import type { ProviderCheck, StorageProvider } from './types.js';
|
|
4
|
+
export interface ProviderOptions {
|
|
5
|
+
readonly contained?: ContainedDeps;
|
|
6
|
+
/** Tests: the answer of the availability check. */
|
|
7
|
+
readonly availability?: () => ProviderCheck;
|
|
8
|
+
}
|
|
9
|
+
export declare function providerFor(config: Config, options?: ProviderOptions): StorageProvider;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { fail } from '../../../core/util/index.js';
|
|
2
|
+
import { containedAvailability } from '../contained/check.js';
|
|
3
|
+
import { FolderStorageProvider } from './folder.js';
|
|
4
|
+
import { ContainedStorageProvider } from './contained.js';
|
|
5
|
+
export function providerFor(config, options = {}) {
|
|
6
|
+
if (config.storage.contained === 'off')
|
|
7
|
+
return new FolderStorageProvider();
|
|
8
|
+
const check = (options.availability ?? containedAvailability)();
|
|
9
|
+
if (!check.available)
|
|
10
|
+
fail('Contained storage cannot be used: ' + check.reasons.join(' '), check.fixes.join(' ') || 'Set storage.contained to "off" (the default) in your settings.');
|
|
11
|
+
if (!options.contained)
|
|
12
|
+
fail('Contained storage was not set up for this command.', 'Set storage.contained to "off" and report this.');
|
|
13
|
+
return new ContainedStorageProvider(options.contained, undefined, options.availability);
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ResolvedMapping } from '../../../core/types.js';
|
|
2
|
+
/** `folder`: a host folder shared in (it is also one of the plan's mappings). `disk`: an image the engine attaches. */
|
|
3
|
+
export type StorageAttachment = {
|
|
4
|
+
readonly kind: 'folder';
|
|
5
|
+
readonly host: string;
|
|
6
|
+
readonly guest: string;
|
|
7
|
+
readonly readOnly: boolean;
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: 'disk';
|
|
10
|
+
readonly image: string;
|
|
11
|
+
readonly guest: string;
|
|
12
|
+
readonly maxGb: number;
|
|
13
|
+
};
|
|
14
|
+
export interface DataSet {
|
|
15
|
+
readonly dataPath: string;
|
|
16
|
+
readonly guest: string;
|
|
17
|
+
}
|
|
18
|
+
export interface PreparedStorage {
|
|
19
|
+
readonly attachments: readonly StorageAttachment[];
|
|
20
|
+
/** The folder attachments as mappings, in the order they go in the launch. */
|
|
21
|
+
readonly mappings: readonly ResolvedMapping[];
|
|
22
|
+
}
|
|
23
|
+
export interface ProviderCheck {
|
|
24
|
+
readonly available: boolean;
|
|
25
|
+
readonly reasons: readonly string[];
|
|
26
|
+
readonly fixes: readonly string[];
|
|
27
|
+
}
|
|
28
|
+
export interface StorageProvider {
|
|
29
|
+
readonly id: 'folder' | 'vhdx-mounted' | 'vhdx-attached';
|
|
30
|
+
check(): Promise<ProviderCheck>;
|
|
31
|
+
prepare(dataSet: DataSet): Promise<PreparedStorage>;
|
|
32
|
+
release(): Promise<void>;
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -3,12 +3,13 @@ import path from 'node:path';
|
|
|
3
3
|
import { downloader, extractArchive, fetchJson, moveIntoPlace, stagingFolder } from '../../shared/download/index.js';
|
|
4
4
|
import { safeRemove } from '../../shared/io/index.js';
|
|
5
5
|
import { assertTestWritable } from '../test-guard.js';
|
|
6
|
+
import { hostDataFolder, hostDataRoot } from '../storage/index.js';
|
|
6
7
|
import { z } from 'zod';
|
|
7
8
|
import { assertNoLinks, errMsg, fileExists } from '../../core/util/index.js';
|
|
8
9
|
// ---- conpty.dll -----------------------------------------------------------------------------------------------------
|
|
9
10
|
/** Where the sandbox finds conpty.dll + OpenConsole.exe (guest/ptyhost.ts points ptyhost at it). */
|
|
10
11
|
export function conptyDir(dataPath) {
|
|
11
|
-
return path.join(dataPath, 'tools', 'conpty');
|
|
12
|
+
return path.join(hostDataFolder(dataPath, 'tools'), 'conpty');
|
|
12
13
|
}
|
|
13
14
|
const NUGET_CONPTY = 'https://api.nuget.org/v3-flatcontainer/microsoft.windows.console.conpty/';
|
|
14
15
|
const NugetVersionsSchema = z.object({ versions: z.array(z.string()) });
|
|
@@ -22,7 +23,7 @@ const NugetCatalogSchema = z.object({ packageHash: z.string(), packageHashAlgori
|
|
|
22
23
|
export async function ensureConpty(dataPath, refresh) {
|
|
23
24
|
const dir = conptyDir(dataPath);
|
|
24
25
|
assertTestWritable(dir);
|
|
25
|
-
assertNoLinks(dataPath, dir);
|
|
26
|
+
assertNoLinks(hostDataRoot(dataPath), dir);
|
|
26
27
|
if (fileExists(path.join(dir, 'conpty.dll')) && fileExists(path.join(dir, 'OpenConsole.exe')) && !refresh)
|
|
27
28
|
return 'cached';
|
|
28
29
|
const arch = process.arch === 'arm64' ? 'arm64' : 'x64';
|
|
@@ -5,11 +5,12 @@ import path from 'node:path';
|
|
|
5
5
|
import { downloader, githubAsset } from '../../shared/download/index.js';
|
|
6
6
|
import { safeRemove, writeFileAtomic } from '../../shared/io/index.js';
|
|
7
7
|
import { assertTestWritable } from '../test-guard.js';
|
|
8
|
+
import { hostDataFolder, hostDataRoot } from '../storage/index.js';
|
|
8
9
|
import { applyEdits, modify } from 'jsonc-parser';
|
|
9
10
|
import { z } from 'zod';
|
|
10
11
|
import { assertNoLinks, errMsg, fileExists, parseJsonc, writeJson } from '../../core/util/index.js';
|
|
11
12
|
export function terminalDir(dataPath) {
|
|
12
|
-
return path.join(dataPath, 'tools', 'terminal');
|
|
13
|
+
return path.join(hostDataFolder(dataPath, 'tools'), 'terminal');
|
|
13
14
|
}
|
|
14
15
|
export function terminalExe(dataPath) {
|
|
15
16
|
return path.join(terminalDir(dataPath), 'WindowsTerminal.exe');
|
|
@@ -82,10 +83,10 @@ export function withReloadEnvironment(settings) {
|
|
|
82
83
|
export function syncPwshProfile(dataPath, allowed) {
|
|
83
84
|
const file = path.join(terminalDir(dataPath), 'settings', 'settings.json');
|
|
84
85
|
assertTestWritable(file);
|
|
85
|
-
assertNoLinks(dataPath, file);
|
|
86
|
+
assertNoLinks(hostDataRoot(dataPath), file);
|
|
86
87
|
if (!fileExists(file) || fs.statSync(file).size > MAX_SETTINGS_BYTES)
|
|
87
88
|
return;
|
|
88
|
-
const visible = allowed && fileExists(path.join(dataPath, 'tools', 'pwsh', 'pwsh.exe'));
|
|
89
|
+
const visible = allowed && fileExists(path.join(hostDataFolder(dataPath, 'tools'), 'pwsh', 'pwsh.exe'));
|
|
89
90
|
let parsed;
|
|
90
91
|
const text = fs.readFileSync(file, 'utf8');
|
|
91
92
|
try {
|
|
@@ -130,9 +131,9 @@ const installTerminal = async (dataPath, refresh) => {
|
|
|
130
131
|
const settings = path.join(dir, 'settings');
|
|
131
132
|
// downloader.archive replaces tools\terminal: the portable settings folder waits here meanwhile (and goes back also
|
|
132
133
|
// when the download fails). One still parked from an interrupted earlier run is the one to keep.
|
|
133
|
-
const parked = path.join(dataPath, 'tools', 'terminal-settings.parked');
|
|
134
|
-
assertNoLinks(dataPath, settings);
|
|
135
|
-
assertNoLinks(dataPath, parked);
|
|
134
|
+
const parked = path.join(hostDataFolder(dataPath, 'tools'), 'terminal-settings.parked');
|
|
135
|
+
assertNoLinks(hostDataRoot(dataPath), settings);
|
|
136
|
+
assertNoLinks(hostDataRoot(dataPath), parked);
|
|
136
137
|
if (fileExists(exe) && !refresh)
|
|
137
138
|
return { exe, message: 'cached' };
|
|
138
139
|
try {
|
|
@@ -158,7 +159,7 @@ const installTerminal = async (dataPath, refresh) => {
|
|
|
158
159
|
fs.renameSync(parked, settings);
|
|
159
160
|
}
|
|
160
161
|
const settingsFile = path.join(settings, 'settings.json');
|
|
161
|
-
assertNoLinks(dataPath, settingsFile);
|
|
162
|
+
assertNoLinks(hostDataRoot(dataPath), settingsFile);
|
|
162
163
|
if (fileExists(path.join(dir, '.portable')) && !fs.existsSync(settingsFile))
|
|
163
164
|
writeJson(settingsFile, SETTINGS);
|
|
164
165
|
}
|
package/dist/host/vpn/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { VpnLogin, loginProblem, type LoginState, type VpnCredentials } from './login.js';
|
|
2
2
|
export { fetchBounded, findCountry, nordProfile, recommendedServer, type Fetcher, type NordServer } from './nordvpn.js';
|
|
3
|
-
export { prepareVpn, vpnNotes, type PrepareOptions } from './prepare.js';
|
|
3
|
+
export { prepareVpn, vpnNotes, wireguardFileProblem, type PrepareOptions } from './prepare.js';
|
|
4
4
|
export { hostPublicIp, readVpnLine, vpnLine, type VpnLine } from './status.js';
|
package/dist/host/vpn/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { VpnLogin, loginProblem } from './login.js';
|
|
2
2
|
export { fetchBounded, findCountry, nordProfile, recommendedServer } from './nordvpn.js';
|
|
3
|
-
export { prepareVpn, vpnNotes } from './prepare.js';
|
|
3
|
+
export { prepareVpn, vpnNotes, wireguardFileProblem } from './prepare.js';
|
|
4
4
|
export { hostPublicIp, readVpnLine, vpnLine } from './status.js';
|
|
@@ -13,5 +13,7 @@ export interface PrepareOptions {
|
|
|
13
13
|
}
|
|
14
14
|
/** Null when the VPN is off. Throws a UserError that says what to do when it cannot be set up. */
|
|
15
15
|
export declare function prepareVpn(o: PrepareOptions): Promise<PreparedVpn | null>;
|
|
16
|
+
/** For doctor: null when vpn.configFile is a readable, safe WireGuard config, else a short reason (never a key). */
|
|
17
|
+
export declare function wireguardFileProblem(configFile: string, base: string): string | null;
|
|
16
18
|
/** What the launch says about the VPN: the warnings the user must see before trusting it. */
|
|
17
19
|
export declare function vpnNotes(vpn: Config['vpn']): string[];
|
package/dist/host/vpn/prepare.js
CHANGED
|
@@ -4,6 +4,7 @@ import fs from 'node:fs';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { expandPath, fail } from '../../core/util/index.js';
|
|
6
6
|
import { assertSafeOvpn, MAX_OVPN_BYTES } from '../../shared/vpn/ovpn.js';
|
|
7
|
+
import { assertSafeWireguard, inspectWireguard, MAX_WIREGUARD_BYTES, renderWireguard, } from '../../shared/vpn/wireguard.js';
|
|
7
8
|
import { fetchBounded, nordProfile, recommendedServer } from './nordvpn.js';
|
|
8
9
|
import { VpnLogin } from './login.js';
|
|
9
10
|
/** Null when the VPN is off. Throws a UserError that says what to do when it cannot be set up. */
|
|
@@ -15,6 +16,8 @@ export async function prepareVpn(o) {
|
|
|
15
16
|
fail('The VPN is on, but sandbox.networking is off: there is no network to put it on.', 'Turn one of them off.');
|
|
16
17
|
if (o.sandboxRunning)
|
|
17
18
|
fail('A sandbox is running already, so the VPN login is not handed over.', 'Start the VPN sandbox after it stops.');
|
|
19
|
+
if (vpn.provider === 'wireguard-file')
|
|
20
|
+
return prepareWireguard(o);
|
|
18
21
|
const login = o.login ?? new VpnLogin(o.storageRoot);
|
|
19
22
|
const credentials = login.read(vpn.provider) ??
|
|
20
23
|
fail('The VPN is on, but no ' + (vpn.provider === 'nordvpn' ? 'NordVPN' : 'VPN') + ' login is saved.', 'Save it with: sandbox vpn login (or turn the VPN off: sandbox config set vpn.enabled false)');
|
|
@@ -62,16 +65,74 @@ export async function prepareVpn(o) {
|
|
|
62
65
|
credentials,
|
|
63
66
|
};
|
|
64
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* wireguard-file: reads vpn.configFile on this PC (size capped), checks it as untrusted data and returns it written
|
|
70
|
+
* again from the checked values. It holds the private key, so it only ever goes in the one-shot secrets file. There is
|
|
71
|
+
* no saved login: the keys are in the file.
|
|
72
|
+
*/
|
|
73
|
+
function prepareWireguard(o) {
|
|
74
|
+
const { vpn } = o.config;
|
|
75
|
+
if (!vpn.configFile.trim())
|
|
76
|
+
fail('vpn.provider is wireguard-file but vpn.configFile is empty.', 'Set it: sandbox config set vpn.configFile <path to your .conf>');
|
|
77
|
+
const file = expandPath(vpn.configFile, o.base ?? process.cwd());
|
|
78
|
+
let size;
|
|
79
|
+
try {
|
|
80
|
+
const stat = fs.statSync(file);
|
|
81
|
+
if (!stat.isFile())
|
|
82
|
+
throw new Error('not a file');
|
|
83
|
+
size = stat.size;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return fail('The WireGuard config ' + file + ' does not exist.', 'Fix vpn.configFile.');
|
|
87
|
+
}
|
|
88
|
+
if (size > MAX_WIREGUARD_BYTES)
|
|
89
|
+
fail('The WireGuard config ' + file + ' is too big.', 'Use a plain .conf file.');
|
|
90
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
91
|
+
try {
|
|
92
|
+
const config = assertSafeWireguard(text, path.basename(file));
|
|
93
|
+
return {
|
|
94
|
+
session: { provider: vpn.provider, protocol: 'udp', killSwitch: vpn.killSwitch, server: path.basename(file) },
|
|
95
|
+
wireguardConfig: renderWireguard(config),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
return fail(e instanceof Error ? e.message : String(e), 'Nothing was started. Use a config without PostUp, PreUp or other script lines.');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** For doctor: null when vpn.configFile is a readable, safe WireGuard config, else a short reason (never a key). */
|
|
103
|
+
export function wireguardFileProblem(configFile, base) {
|
|
104
|
+
if (!configFile.trim())
|
|
105
|
+
return 'vpn.configFile is empty';
|
|
106
|
+
const file = expandPath(configFile, base);
|
|
107
|
+
try {
|
|
108
|
+
const stat = fs.statSync(file);
|
|
109
|
+
if (!stat.isFile())
|
|
110
|
+
return file + ' is not a file';
|
|
111
|
+
if (stat.size > MAX_WIREGUARD_BYTES)
|
|
112
|
+
return file + ' is too big for a WireGuard config';
|
|
113
|
+
const report = inspectWireguard(fs.readFileSync(file, 'utf8'));
|
|
114
|
+
return report.ok ? null : 'refused: ' + report.problems.slice(0, 3).join('; ');
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return file + ' cannot be read';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
65
120
|
/** What the launch says about the VPN: the warnings the user must see before trusting it. */
|
|
66
121
|
export function vpnNotes(vpn) {
|
|
67
122
|
if (!vpn.enabled)
|
|
68
123
|
return [];
|
|
69
124
|
return [
|
|
70
125
|
'VPN: ' +
|
|
71
|
-
(vpn.provider === 'nordvpn'
|
|
126
|
+
(vpn.provider === 'nordvpn'
|
|
127
|
+
? 'NordVPN' + (vpn.country ? ' (' + vpn.country + ')' : '')
|
|
128
|
+
: vpn.provider === 'wireguard-file'
|
|
129
|
+
? 'your WireGuard config'
|
|
130
|
+
: 'your profile') +
|
|
72
131
|
(vpn.killSwitch
|
|
73
132
|
? ', kill switch on: if the tunnel fails the sandbox stays offline.'
|
|
74
133
|
: ', kill switch OFF: if the tunnel fails the sandbox uses your normal connection.'),
|
|
75
|
-
|
|
134
|
+
vpn.provider === 'wireguard-file'
|
|
135
|
+
? 'The agent in the sandbox can read the WireGuard private key from your config and can turn the tunnel off, because it is the administrator there. Use a key you can revoke.'
|
|
136
|
+
: 'The agent in the sandbox can read the VPN login (a service login you can revoke) and can turn the tunnel off, because it is the administrator there.',
|
|
76
137
|
];
|
|
77
138
|
}
|
package/dist/host/web/browser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Opens an address in the default browser (through cmd's start; the address is one we built, hex and digits only).
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
|
-
import { SYSTEM } from '../../shared/system.js';
|
|
3
|
+
import { SYSTEM, system32 } from '../../shared/system.js';
|
|
4
4
|
import { refuseInSelfTest } from '../test-guard.js';
|
|
5
5
|
export function openBrowser(url) {
|
|
6
6
|
refuseInSelfTest('open a browser');
|
|
@@ -8,3 +8,16 @@ export function openBrowser(url) {
|
|
|
8
8
|
throw new Error('Not a web UI address.');
|
|
9
9
|
spawn(SYSTEM.cmd(), ['/c', 'start', '', url], { detached: true, stdio: 'ignore', windowsHide: true }).unref();
|
|
10
10
|
}
|
|
11
|
+
/** The one other page we open: a GitHub new-issue form (letters, digits and URL escapes only). */
|
|
12
|
+
const ISSUE_URL = /^https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/issues\/new\?[\w%.~&=+-]*$/;
|
|
13
|
+
/** Opens a GitHub new-issue address in the default browser (rundll32, so no shell reads the & in it). */
|
|
14
|
+
export function openIssuePage(url) {
|
|
15
|
+
refuseInSelfTest('open a browser');
|
|
16
|
+
if (!ISSUE_URL.test(url) || url.length > 2000)
|
|
17
|
+
throw new Error('Not a GitHub new-issue address.');
|
|
18
|
+
spawn(system32('rundll32.exe'), ['url.dll,FileProtocolHandler', url], {
|
|
19
|
+
detached: true,
|
|
20
|
+
stdio: 'ignore',
|
|
21
|
+
windowsHide: true,
|
|
22
|
+
}).unref();
|
|
23
|
+
}
|
package/dist/host/web/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { WebAuth, newToken, sameSecret, hostAllowed, originAllowed, csrfOk, cookieValue, cookieHeader, } from './auth.js';
|
|
2
2
|
export type { WebBackend } from './backend.js';
|
|
3
|
-
export { openBrowser } from './browser.js';
|
|
3
|
+
export { openBrowser, openIssuePage } from './browser.js';
|
|
4
4
|
export { TerminalBridge } from './bridge.js';
|
|
5
5
|
export { WebControl, type WebStatus } from './control.js';
|
|
6
6
|
export { WebRunner } from './runner.js';
|
|
7
|
-
export {
|
|
7
|
+
export { RpcSession } from './rpc.js';
|
|
8
|
+
export { WebServer, CONTENT_SECURITY_POLICY, RPC_PATH, type WebServerOptions } from './server.js';
|
|
8
9
|
export { StaticFiles } from './static-files.js';
|
|
9
10
|
export { WebStore, WebStateSchema, type WebState } from './state.js';
|
package/dist/host/web/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { WebAuth, newToken, sameSecret, hostAllowed, originAllowed, csrfOk, cookieValue, cookieHeader, } from './auth.js';
|
|
2
|
-
export { openBrowser } from './browser.js';
|
|
2
|
+
export { openBrowser, openIssuePage } from './browser.js';
|
|
3
3
|
export { TerminalBridge } from './bridge.js';
|
|
4
4
|
export { WebControl } from './control.js';
|
|
5
5
|
export { WebRunner } from './runner.js';
|
|
6
|
-
export {
|
|
6
|
+
export { RpcSession } from './rpc.js';
|
|
7
|
+
export { WebServer, CONTENT_SECURITY_POLICY, RPC_PATH } from './server.js';
|
|
7
8
|
export { StaticFiles } from './static-files.js';
|
|
8
9
|
export { WebStore, WebStateSchema } from './state.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { WebSocket } from 'ws';
|
|
2
|
+
import { type RpcHost } from '../../app/kernel/index.js';
|
|
3
|
+
export declare class RpcSession {
|
|
4
|
+
private readonly ws;
|
|
5
|
+
private readonly host;
|
|
6
|
+
private readonly calls;
|
|
7
|
+
private readonly subscriptions;
|
|
8
|
+
private closed;
|
|
9
|
+
constructor(ws: WebSocket, host: RpcHost);
|
|
10
|
+
start(): void;
|
|
11
|
+
private send;
|
|
12
|
+
/** A browser that sends something that is not one of our messages is closed. */
|
|
13
|
+
private refuse;
|
|
14
|
+
private receive;
|
|
15
|
+
private handle;
|
|
16
|
+
/** The id is free and the socket has room for one more call or subscription. */
|
|
17
|
+
private admit;
|
|
18
|
+
private call;
|
|
19
|
+
private answer;
|
|
20
|
+
private subscribe;
|
|
21
|
+
private unsubscribe;
|
|
22
|
+
/** The browser went away: subscriptions stop, and questions still open are answered no. */
|
|
23
|
+
private close;
|
|
24
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { ANSWER_SCHEMAS, ClientMessageSchema, MAX_OPEN_PER_SOCKET, NO_QUESTIONS, ioFromAsker, toWireError, } from '../../app/kernel/index.js';
|
|
2
|
+
/** What a question gets when the browser goes away before answering: no. */
|
|
3
|
+
function declined(q) {
|
|
4
|
+
return q.kind === 'confirm' ? false : undefined;
|
|
5
|
+
}
|
|
6
|
+
export class RpcSession {
|
|
7
|
+
ws;
|
|
8
|
+
host;
|
|
9
|
+
calls = new Map();
|
|
10
|
+
subscriptions = new Map();
|
|
11
|
+
closed = false;
|
|
12
|
+
constructor(ws, host) {
|
|
13
|
+
this.ws = ws;
|
|
14
|
+
this.host = host;
|
|
15
|
+
}
|
|
16
|
+
start() {
|
|
17
|
+
this.ws.on('message', (data, binary) => this.receive(data, binary));
|
|
18
|
+
this.ws.on('close', () => this.close());
|
|
19
|
+
this.ws.on('error', () => this.close());
|
|
20
|
+
}
|
|
21
|
+
send(message) {
|
|
22
|
+
if (!this.closed && this.ws.readyState === this.ws.OPEN)
|
|
23
|
+
this.ws.send(JSON.stringify(message));
|
|
24
|
+
}
|
|
25
|
+
/** A browser that sends something that is not one of our messages is closed. */
|
|
26
|
+
refuse() {
|
|
27
|
+
this.ws.close(1008, 'bad message');
|
|
28
|
+
this.close();
|
|
29
|
+
}
|
|
30
|
+
receive(data, binary) {
|
|
31
|
+
if (binary)
|
|
32
|
+
return this.refuse();
|
|
33
|
+
let value;
|
|
34
|
+
try {
|
|
35
|
+
value = JSON.parse(data.toString());
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return this.refuse();
|
|
39
|
+
}
|
|
40
|
+
const parsed = ClientMessageSchema.safeParse(value);
|
|
41
|
+
if (!parsed.success)
|
|
42
|
+
return this.refuse();
|
|
43
|
+
this.handle(parsed.data);
|
|
44
|
+
}
|
|
45
|
+
handle(m) {
|
|
46
|
+
switch (m.t) {
|
|
47
|
+
case 'call':
|
|
48
|
+
return this.call(m.id, m.op, m.input);
|
|
49
|
+
case 'answer':
|
|
50
|
+
return this.answer(m.id, m.value);
|
|
51
|
+
case 'subscribe':
|
|
52
|
+
return this.subscribe(m.id, m.query, m.input);
|
|
53
|
+
case 'unsubscribe':
|
|
54
|
+
return this.unsubscribe(m.id);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** The id is free and the socket has room for one more call or subscription. */
|
|
58
|
+
admit(id) {
|
|
59
|
+
if (this.calls.has(id) || this.subscriptions.has(id)) {
|
|
60
|
+
this.send({ t: 'error', id, error: { kind: 'invalid', message: 'That id is in use.' } });
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (this.calls.size + this.subscriptions.size >= MAX_OPEN_PER_SOCKET) {
|
|
64
|
+
this.send({ t: 'error', id, error: { kind: 'invalid', message: 'Too many at once.' } });
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
call(id, op, input) {
|
|
70
|
+
if (!this.admit(id))
|
|
71
|
+
return;
|
|
72
|
+
if (!this.host.kind(op)) {
|
|
73
|
+
this.send({ t: 'error', id, error: { kind: 'invalid', message: 'There is no ' + op + ' here.' } });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.calls.set(id, null);
|
|
77
|
+
const io = ioFromAsker((question) => new Promise((resolve) => {
|
|
78
|
+
if (this.closed)
|
|
79
|
+
return resolve(declined(question));
|
|
80
|
+
this.calls.set(id, { question, resolve: resolve });
|
|
81
|
+
this.send({ t: 'ask', id, question });
|
|
82
|
+
}), {
|
|
83
|
+
progress: (text) => this.send({ t: 'progress', id, text }),
|
|
84
|
+
log: (text, tone) => this.send({ t: 'log', id, text, ...(tone ? { tone } : {}) }),
|
|
85
|
+
});
|
|
86
|
+
this.host
|
|
87
|
+
.run(op, input, io)
|
|
88
|
+
.then((output) => this.send({ t: 'result', id, output }), (e) => this.send({ t: 'error', id, error: toWireError(e) }))
|
|
89
|
+
.finally(() => this.calls.delete(id));
|
|
90
|
+
}
|
|
91
|
+
answer(id, value) {
|
|
92
|
+
const waiting = this.calls.get(id);
|
|
93
|
+
if (!waiting)
|
|
94
|
+
return;
|
|
95
|
+
const parsed = ANSWER_SCHEMAS[waiting.question.kind].safeParse(value);
|
|
96
|
+
this.calls.set(id, null);
|
|
97
|
+
waiting.resolve(parsed.success ? parsed.data : declined(waiting.question));
|
|
98
|
+
}
|
|
99
|
+
subscribe(id, query, input) {
|
|
100
|
+
if (!this.admit(id))
|
|
101
|
+
return;
|
|
102
|
+
if (this.host.kind(query) !== 'query') {
|
|
103
|
+
this.send({ t: 'error', id, error: { kind: 'invalid', message: 'There is no ' + query + ' here.' } });
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const sub = { timer: undefined, last: undefined, busy: false };
|
|
107
|
+
this.subscriptions.set(id, sub);
|
|
108
|
+
const read = async () => {
|
|
109
|
+
if (sub.busy)
|
|
110
|
+
return;
|
|
111
|
+
sub.busy = true;
|
|
112
|
+
try {
|
|
113
|
+
const output = await this.host.run(query, input, NO_QUESTIONS);
|
|
114
|
+
const text = JSON.stringify(output);
|
|
115
|
+
if (text !== sub.last && this.subscriptions.get(id) === sub) {
|
|
116
|
+
sub.last = text;
|
|
117
|
+
this.send({ t: 'data', id, output });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
sub.last = undefined;
|
|
122
|
+
this.send({ t: 'error', id, error: toWireError(e) });
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
sub.busy = false;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
void read();
|
|
129
|
+
const every = this.host.liveMs(query);
|
|
130
|
+
if (every)
|
|
131
|
+
sub.timer = setInterval(() => void read(), every);
|
|
132
|
+
}
|
|
133
|
+
unsubscribe(id) {
|
|
134
|
+
const sub = this.subscriptions.get(id);
|
|
135
|
+
if (sub?.timer)
|
|
136
|
+
clearInterval(sub.timer);
|
|
137
|
+
this.subscriptions.delete(id);
|
|
138
|
+
}
|
|
139
|
+
/** The browser went away: subscriptions stop, and questions still open are answered no. */
|
|
140
|
+
close() {
|
|
141
|
+
if (this.closed)
|
|
142
|
+
return;
|
|
143
|
+
this.closed = true;
|
|
144
|
+
for (const id of [...this.subscriptions.keys()])
|
|
145
|
+
this.unsubscribe(id);
|
|
146
|
+
for (const waiting of this.calls.values())
|
|
147
|
+
waiting?.resolve(declined(waiting.question));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { RpcHost } from '../../app/kernel/index.js';
|
|
1
2
|
import type { WebBackend } from './backend.js';
|
|
2
3
|
export interface WebRunOptions {
|
|
3
4
|
readonly storageRoot: string;
|
|
4
5
|
readonly port: number;
|
|
5
6
|
readonly backend: WebBackend;
|
|
7
|
+
readonly rpc?: RpcHost;
|
|
6
8
|
readonly staticRoot: string;
|
|
7
9
|
}
|
|
8
10
|
export declare class WebRunner {
|
package/dist/host/web/runner.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// The background process behind `sandbox web start`: starts the server, records pid, port and token in the private
|
|
2
|
-
// state file, and removes it again when the server stops.
|
|
3
1
|
import { fail } from '../../core/util/index.js';
|
|
4
2
|
import { newToken } from './auth.js';
|
|
5
3
|
import { WebServer } from './server.js';
|
|
@@ -20,6 +18,7 @@ export class WebRunner {
|
|
|
20
18
|
port: this.options.port,
|
|
21
19
|
token,
|
|
22
20
|
backend: this.options.backend,
|
|
21
|
+
rpc: this.options.rpc,
|
|
23
22
|
staticRoot: this.options.staticRoot,
|
|
24
23
|
onStop: stop,
|
|
25
24
|
});
|