windows-development-sandbox 1.5.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/config.example.json +27 -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} +4 -0
- package/dist/{cli/menu/settings/categories.js → app/features/settings/categories.def.js} +40 -46
- 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/{cli/menu/settings → app/kernel}/schema-node.d.ts +9 -0
- package/dist/{cli/menu/settings → app/kernel}/schema-node.js +26 -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 +18 -2
- package/dist/cli/args.js +101 -24
- package/dist/cli/commands/accounts.js +45 -17
- package/dist/cli/commands/context.d.ts +2 -0
- package/dist/cli/commands/context.js +9 -2
- package/dist/cli/commands/data/clean.js +32 -9
- package/dist/cli/commands/engine.d.ts +2 -0
- package/dist/cli/commands/engine.js +60 -0
- package/dist/cli/commands/index.d.ts +2 -1
- package/dist/cli/commands/index.js +2 -1
- package/dist/cli/commands/launch/add-root.d.ts +3 -2
- package/dist/cli/commands/launch/add-root.js +11 -10
- package/dist/cli/commands/launch/launch.js +102 -9
- package/dist/cli/commands/launch/prepare.js +6 -4
- package/dist/cli/commands/launch/running.js +2 -1
- package/dist/cli/commands/launch/summary.d.ts +2 -0
- package/dist/cli/commands/maintenance/audit.js +1 -1
- package/dist/cli/commands/maintenance/clean.d.ts +1 -1
- package/dist/cli/commands/maintenance/clean.js +17 -3
- 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.d.ts +1 -1
- package/dist/cli/commands/maintenance/config.js +38 -10
- package/dist/cli/commands/maintenance/doctor.d.ts +1 -1
- package/dist/cli/commands/maintenance/doctor.js +2 -4
- package/dist/cli/commands/maintenance/logs.js +14 -0
- package/dist/cli/commands/maintenance/migrate.d.ts +2 -1
- package/dist/cli/commands/maintenance/migrate.js +19 -4
- package/dist/cli/commands/maintenance/sync.js +2 -0
- package/dist/cli/commands/maintenance/update.d.ts +2 -0
- package/dist/cli/commands/maintenance/update.js +27 -4
- package/dist/cli/commands/projects/here.js +12 -7
- package/dist/cli/commands/projects/projects.js +26 -17
- package/dist/cli/commands/registry.js +10 -7
- package/dist/cli/commands/repair/checks/agents.d.ts +2 -0
- package/dist/cli/commands/repair/checks/agents.js +70 -0
- package/dist/cli/commands/repair/checks/common.d.ts +8 -0
- package/dist/cli/commands/repair/checks/common.js +22 -0
- 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/data.d.ts +2 -0
- package/dist/cli/commands/repair/checks/data.js +241 -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.d.ts +2 -0
- package/dist/cli/commands/repair/checks/index.js +24 -0
- package/dist/cli/commands/repair/checks/install.d.ts +5 -0
- package/dist/cli/commands/repair/checks/install.js +318 -0
- package/dist/cli/commands/repair/checks/machine.d.ts +2 -0
- package/dist/cli/commands/repair/checks/machine.js +232 -0
- package/dist/cli/commands/repair/checks/network.d.ts +6 -0
- package/dist/cli/commands/repair/checks/network.js +60 -0
- package/dist/cli/commands/repair/checks/pc.d.ts +4 -0
- package/dist/cli/commands/repair/checks/pc.js +86 -0
- package/dist/cli/commands/repair/checks/sandbox.d.ts +2 -0
- package/dist/cli/commands/repair/checks/sandbox.js +282 -0
- package/dist/cli/commands/repair/checks/settings.d.ts +2 -0
- package/dist/cli/commands/repair/checks/settings.js +169 -0
- package/dist/cli/commands/repair/checks/vpn.d.ts +2 -0
- package/dist/cli/commands/repair/checks/vpn.js +78 -0
- package/dist/cli/commands/repair/engine.d.ts +32 -0
- package/dist/cli/commands/repair/engine.js +127 -0
- package/dist/cli/commands/repair/env.d.ts +7 -0
- package/dist/cli/commands/repair/env.js +50 -0
- package/dist/cli/commands/repair/index.d.ts +1 -9
- package/dist/cli/commands/repair/index.js +2 -54
- package/dist/cli/commands/repair/log.d.ts +17 -0
- package/dist/cli/commands/repair/log.js +82 -0
- package/dist/cli/commands/repair/printer.d.ts +32 -0
- package/dist/cli/commands/repair/printer.js +132 -0
- package/dist/cli/commands/repair/run.d.ts +33 -0
- package/dist/cli/commands/repair/run.js +133 -0
- package/dist/cli/commands/repair/types.d.ts +97 -9
- package/dist/cli/commands/repair/types.js +11 -7
- package/dist/cli/commands/resume.js +2 -2
- package/dist/cli/commands/sessions/index.d.ts +0 -4
- package/dist/cli/commands/sessions/index.js +3 -7
- package/dist/cli/commands/setup/index.d.ts +5 -0
- package/dist/cli/commands/setup/index.js +25 -1
- 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/index.d.ts +2 -0
- package/dist/cli/commands/storage/index.js +2 -0
- package/dist/cli/commands/storage/offer.d.ts +3 -0
- package/dist/cli/commands/storage/offer.js +91 -0
- package/dist/cli/commands/storage/storage.d.ts +8 -0
- package/dist/cli/commands/storage/storage.js +179 -0
- package/dist/cli/commands/user-config.js +2 -2
- package/dist/cli/commands/vpn.d.ts +4 -0
- package/dist/cli/commands/vpn.js +91 -0
- package/dist/cli/commands/web.d.ts +4 -0
- package/dist/cli/commands/web.js +97 -0
- package/dist/cli/history.js +1 -1
- package/dist/cli/index.js +48 -4
- package/dist/cli/manifest.d.ts +339 -97
- package/dist/cli/manifest.js +222 -67
- package/dist/cli/menu/add-agent.d.ts +47 -0
- package/dist/cli/menu/add-agent.js +168 -0
- package/dist/cli/menu/apps.d.ts +38 -3
- package/dist/cli/menu/apps.js +80 -5
- package/dist/cli/menu/command-fields.d.ts +18 -0
- package/dist/cli/menu/command-fields.js +61 -0
- package/dist/cli/menu/command-form.d.ts +22 -0
- package/dist/cli/menu/command-form.js +169 -46
- package/dist/cli/menu/data-folder.d.ts +18 -0
- package/dist/cli/menu/data-folder.js +99 -0
- 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 +11 -2
- package/dist/cli/menu/menu.js +36 -15
- package/dist/cli/menu/pages/accounts-page.js +55 -24
- package/dist/cli/menu/pages/agents-page.d.ts +68 -0
- package/dist/cli/menu/pages/agents-page.js +345 -0
- package/dist/cli/menu/pages/apps-page.d.ts +3 -1
- package/dist/cli/menu/pages/apps-page.js +20 -4
- package/dist/cli/menu/pages/list-page.d.ts +43 -3
- package/dist/cli/menu/pages/list-page.js +146 -12
- package/dist/cli/menu/pages/maintenance-page.d.ts +9 -5
- package/dist/cli/menu/pages/maintenance-page.js +172 -85
- package/dist/cli/menu/pages/sandbox-page.d.ts +7 -6
- package/dist/cli/menu/pages/sandbox-page.js +117 -120
- package/dist/cli/menu/pages/scopes.d.ts +1 -1
- package/dist/cli/menu/pages/sessions-page.d.ts +6 -8
- package/dist/cli/menu/pages/sessions-page.js +22 -75
- package/dist/cli/menu/pages/settings-page.d.ts +10 -14
- package/dist/cli/menu/pages/settings-page.js +95 -102
- package/dist/cli/menu/render-view.d.ts +28 -0
- package/dist/cli/menu/render-view.js +112 -0
- package/dist/cli/menu/settings/ask-path.d.ts +14 -0
- package/dist/cli/menu/settings/ask-path.js +14 -0
- package/dist/cli/menu/settings/collections/collection-editor.d.ts +8 -2
- package/dist/cli/menu/settings/collections/collection-editor.js +12 -8
- package/dist/cli/menu/settings/collections/map-editor.js +5 -1
- package/dist/cli/menu/settings/collections/object-list-editor.js +2 -2
- package/dist/cli/menu/settings/collections/object-map-editor.js +2 -2
- package/dist/cli/menu/settings/collections/string-list-editor.d.ts +10 -1
- package/dist/cli/menu/settings/collections/string-list-editor.js +23 -0
- 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 +6 -2
- package/dist/cli/menu/settings/object-editor.js +15 -4
- 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 +10 -1
- package/dist/cli/menu/settings/value-editor.js +99 -63
- package/dist/cli/menu/several.js +7 -5
- 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 +53 -10
- package/dist/cli/menu/shell.js +115 -10
- package/dist/cli/menu/views.d.ts +7 -0
- package/dist/cli/menu/views.js +11 -0
- package/dist/cli/menu/vpn-login.d.ts +16 -0
- package/dist/cli/menu/vpn-login.js +74 -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 +5 -1
- package/dist/cli/output/print.js +15 -4
- 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/accounts-service.d.ts +2 -0
- package/dist/cli/services/accounts-service.js +2 -2
- 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/health-service.d.ts +0 -6
- package/dist/cli/services/health-service.js +5 -42
- package/dist/cli/services/index.d.ts +3 -1
- package/dist/cli/services/index.js +3 -1
- package/dist/cli/services/logs-service.js +4 -3
- package/dist/cli/services/migrate-service.js +1 -1
- package/dist/cli/services/update-service.d.ts +2 -0
- package/dist/cli/services/update-service.js +13 -6
- package/dist/cli/services/vpn-service.d.ts +28 -0
- package/dist/cli/services/vpn-service.js +46 -0
- package/dist/cli/services/web-service.d.ts +22 -0
- package/dist/cli/services/web-service.js +111 -0
- package/dist/cli/tui/app.d.ts +12 -4
- package/dist/cli/tui/app.js +38 -9
- package/dist/cli/tui/dialogs/path-listing.d.ts +43 -0
- package/dist/cli/tui/dialogs/path-listing.js +127 -0
- package/dist/cli/tui/dialogs/path-picker.d.ts +67 -0
- package/dist/cli/tui/dialogs/path-picker.js +423 -0
- package/dist/cli/tui/dialogs/text-screen.d.ts +6 -0
- package/dist/cli/tui/dialogs/text-screen.js +10 -3
- package/dist/cli/tui/frame.d.ts +7 -10
- package/dist/cli/tui/frame.js +19 -10
- package/dist/cli/tui/index.d.ts +4 -3
- package/dist/cli/tui/index.js +2 -1
- package/dist/cli/tui/list/list-screen.d.ts +28 -2
- package/dist/cli/tui/list/list-screen.js +130 -66
- package/dist/cli/tui/list/list-types.d.ts +10 -4
- package/dist/cli/tui/nav.d.ts +29 -0
- package/dist/cli/tui/nav.js +67 -0
- package/dist/cli/tui/terminal.d.ts +7 -0
- package/dist/cli/tui/terminal.js +30 -10
- package/dist/cli/validate.d.ts +1 -1
- package/dist/cli/validate.js +3 -3
- package/dist/cli/version.d.ts +2 -0
- package/dist/cli/version.js +11 -0
- package/dist/core/config/defaults.js +13 -1
- package/dist/core/config/loader.js +12 -2
- package/dist/core/local-config/overrides.js +1 -0
- package/dist/core/prompt.d.ts +19 -0
- package/dist/core/prompt.js +43 -3
- package/dist/core/schema/common.d.ts +6 -0
- package/dist/core/schema/common.js +8 -0
- package/dist/core/schema/config.d.ts +36 -0
- package/dist/core/schema/config.js +26 -2
- package/dist/core/schema/harness.d.ts +2 -0
- package/dist/core/schema/harness.js +13 -2
- package/dist/core/schema/index.d.ts +3 -1
- package/dist/core/schema/index.js +3 -1
- package/dist/core/schema/profile.d.ts +1 -0
- package/dist/core/schema/profile.js +3 -0
- package/dist/core/schema/redact.d.ts +13 -0
- package/dist/core/schema/redact.js +75 -0
- package/dist/core/schema/sandbox.d.ts +6 -0
- package/dist/core/schema/sandbox.js +21 -3
- package/dist/core/schema/vpn.d.ts +18 -0
- package/dist/core/schema/vpn.js +22 -0
- package/dist/core/schema/web.d.ts +6 -0
- package/dist/core/schema/web.js +14 -0
- package/dist/core/types.d.ts +44 -0
- 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 +54 -0
- package/dist/guest/agents/agent-files.js +67 -0
- package/dist/guest/agents/agent-installer.js +2 -9
- package/dist/guest/agents/local-state.d.ts +14 -0
- package/dist/guest/agents/local-state.js +218 -0
- package/dist/guest/agents/sbx-agent.d.ts +2 -0
- package/dist/guest/agents/sbx-agent.js +35 -0
- package/dist/guest/boot/boot.d.ts +4 -0
- package/dist/guest/boot/boot.js +51 -11
- package/dist/guest/daemon/daemon.js +8 -1
- package/dist/guest/daemon/exec.js +2 -1
- package/dist/guest/daemon/idle-shutdown.js +2 -0
- package/dist/guest/daemon/sessions.d.ts +5 -0
- package/dist/guest/daemon/sessions.js +20 -3
- package/dist/guest/entries/agent.d.ts +1 -0
- package/dist/guest/entries/agent.js +10 -0
- package/dist/guest/entries/daemon.js +5 -0
- package/dist/guest/governor/decide.d.ts +61 -0
- package/dist/guest/governor/decide.js +182 -0
- package/dist/guest/governor/governor.d.ts +42 -0
- package/dist/guest/governor/governor.js +214 -0
- package/dist/guest/governor/index.d.ts +2 -0
- package/dist/guest/governor/index.js +2 -0
- package/dist/guest/runtime/csc.d.ts +2 -0
- package/dist/guest/runtime/csc.js +2 -0
- package/dist/guest/runtime/secrets.d.ts +12 -0
- package/dist/guest/runtime/secrets.js +63 -0
- package/dist/guest/sessions/viewer.js +3 -1
- package/dist/guest/setup/claude-profile.d.ts +2 -1
- package/dist/guest/setup/claude-profile.js +12 -1
- package/dist/guest/setup/environment.d.ts +6 -0
- package/dist/guest/setup/environment.js +56 -6
- package/dist/guest/setup/path-plan.d.ts +32 -0
- package/dist/guest/setup/path-plan.js +67 -0
- package/dist/guest/setup/shell-path.d.ts +1 -0
- package/dist/guest/setup/shell-path.js +22 -0
- package/dist/guest/toolchains/languages/python.js +1 -1
- package/dist/guest/toolchains/native/rust-extras.js +4 -2
- package/dist/guest/toolchains/runtimes.js +2 -2
- package/dist/guest/vpn/index.d.ts +1 -0
- package/dist/guest/vpn/index.js +1 -0
- package/dist/guest/vpn/openvpn.d.ts +9 -0
- package/dist/guest/vpn/openvpn.js +47 -0
- package/dist/guest/vpn/tunnel.d.ts +17 -0
- package/dist/guest/vpn/tunnel.js +78 -0
- package/dist/guest/vpn/vpn.d.ts +6 -0
- package/dist/guest/vpn/vpn.js +174 -0
- 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 +6 -2
- package/dist/host/channel/attach.js +6 -3
- package/dist/host/channel/channel.d.ts +9 -0
- package/dist/host/channel/channel.js +30 -2
- 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/health/acl-read.d.ts +17 -0
- package/dist/host/health/acl-read.js +45 -0
- package/dist/host/health/config-backup.d.ts +11 -0
- package/dist/host/health/config-backup.js +73 -0
- package/dist/host/health/index.d.ts +6 -0
- package/dist/host/health/index.js +7 -0
- package/dist/host/health/install-state.d.ts +33 -0
- package/dist/host/health/install-state.js +110 -0
- package/dist/host/health/path-copies.d.ts +32 -0
- package/dist/host/health/path-copies.js +82 -0
- package/dist/host/health/stale.d.ts +45 -0
- package/dist/host/health/stale.js +120 -0
- package/dist/host/health/system.d.ts +37 -0
- package/dist/host/health/system.js +87 -0
- package/dist/host/install/copies.d.ts +13 -0
- package/dist/host/install/copies.js +63 -0
- package/dist/host/install/index.d.ts +1 -0
- package/dist/host/install/index.js +1 -0
- package/dist/host/launch/generate.d.ts +5 -1
- package/dist/host/launch/generate.js +73 -7
- 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 +11 -2
- package/dist/host/launch/stage.js +1 -1
- package/dist/host/launch/wsb-file.d.ts +28 -4
- package/dist/host/launch/wsb-file.js +61 -42
- package/dist/host/life/crash-report.d.ts +27 -1
- package/dist/host/life/crash-report.js +173 -7
- package/dist/host/life/host-memory.d.ts +40 -0
- package/dist/host/life/host-memory.js +137 -0
- package/dist/host/life/index.d.ts +2 -0
- package/dist/host/life/index.js +2 -0
- package/dist/host/life/stop-events.d.ts +2 -0
- package/dist/host/life/stop-events.js +20 -3
- package/dist/host/life/stop-warning.js +1 -1
- package/dist/host/life/watchdog.d.ts +54 -0
- package/dist/host/life/watchdog.js +108 -0
- package/dist/host/migrate/backup.js +3 -0
- package/dist/host/migrate/migrations/config-file.d.ts +2 -0
- package/dist/host/migrate/migrations/config-file.js +7 -0
- package/dist/host/migrate/migrations/follow-machine-storage.js +9 -1
- package/dist/host/migrate/migrations/pin-storage-root.d.ts +4 -0
- package/dist/host/migrate/migrations/pin-storage-root.js +46 -0
- package/dist/host/migrate/migrations/unpin-config-defaults.js +4 -1
- package/dist/host/migrate/registry.js +2 -1
- package/dist/host/native.d.ts +3 -2
- package/dist/host/native.js +22 -3
- package/dist/host/profile/accounts.d.ts +25 -1
- package/dist/host/profile/accounts.js +96 -13
- package/dist/host/profile/agent-accounts.d.ts +5 -1
- package/dist/host/profile/agent-accounts.js +35 -5
- package/dist/host/profile/claude-login.js +3 -2
- package/dist/host/profile/index.d.ts +2 -1
- package/dist/host/profile/index.js +1 -0
- package/dist/host/profile/plugin-share.d.ts +26 -0
- package/dist/host/profile/plugin-share.js +146 -0
- package/dist/host/projects/join.d.ts +7 -1
- package/dist/host/projects/join.js +21 -4
- package/dist/host/projects/registry.d.ts +16 -10
- package/dist/host/projects/registry.js +74 -6
- 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 +4 -0
- package/dist/host/sandbox/platform.js +23 -4
- package/dist/host/sandbox/tasklist.d.ts +13 -0
- package/dist/host/sandbox/tasklist.js +47 -0
- package/dist/host/secrets/index.d.ts +2 -0
- package/dist/host/secrets/index.js +2 -0
- package/dist/host/secrets/launch-secrets.d.ts +17 -0
- package/dist/host/secrets/launch-secrets.js +94 -0
- package/dist/host/secrets/secret-store.d.ts +25 -0
- package/dist/host/secrets/secret-store.js +89 -0
- package/dist/host/sharing/acl.d.ts +16 -2
- package/dist/host/sharing/acl.js +35 -10
- package/dist/host/sharing/current.js +8 -3
- package/dist/host/sharing/doctor.d.ts +2 -0
- package/dist/host/sharing/doctor.js +44 -0
- package/dist/host/sharing/group-access.d.ts +6 -0
- package/dist/host/sharing/group-access.js +24 -0
- package/dist/host/sharing/index.d.ts +2 -1
- package/dist/host/sharing/index.js +2 -1
- package/dist/host/sharing/lifecycle.d.ts +8 -2
- package/dist/host/sharing/lifecycle.js +16 -7
- package/dist/host/storage/cleanup.d.ts +2 -0
- package/dist/host/storage/cleanup.js +2 -1
- 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/history.d.ts +21 -0
- package/dist/host/storage/history.js +49 -0
- package/dist/host/storage/index.d.ts +6 -0
- package/dist/host/storage/index.js +6 -0
- package/dist/host/storage/move.d.ts +95 -0
- package/dist/host/storage/move.js +384 -0
- package/dist/host/storage/offer.d.ts +11 -0
- package/dist/host/storage/offer.js +36 -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/storage/root.d.ts +18 -0
- package/dist/host/storage/root.js +77 -0
- package/dist/host/tools/conpty.js +3 -2
- package/dist/host/tools/windows-terminal.d.ts +2 -0
- package/dist/host/tools/windows-terminal.js +31 -12
- package/dist/host/update/index.d.ts +1 -1
- package/dist/host/update/index.js +1 -1
- package/dist/host/update/notice.d.ts +2 -0
- package/dist/host/update/notice.js +13 -1
- package/dist/host/update/update-state.d.ts +6 -0
- package/dist/host/update/update-state.js +2 -0
- package/dist/host/update/updater.d.ts +19 -0
- package/dist/host/update/updater.js +88 -12
- package/dist/host/vpn/index.d.ts +4 -0
- package/dist/host/vpn/index.js +4 -0
- package/dist/host/vpn/login.d.ts +24 -0
- package/dist/host/vpn/login.js +79 -0
- package/dist/host/vpn/nordvpn.d.ts +21 -0
- package/dist/host/vpn/nordvpn.js +77 -0
- package/dist/host/vpn/prepare.d.ts +19 -0
- package/dist/host/vpn/prepare.js +138 -0
- package/dist/host/vpn/status.d.ts +12 -0
- package/dist/host/vpn/status.js +53 -0
- package/dist/host/web/auth.d.ts +38 -0
- package/dist/host/web/auth.js +96 -0
- package/dist/host/web/backend.d.ts +14 -0
- package/dist/host/web/backend.js +1 -0
- package/dist/host/web/bridge.d.ts +27 -0
- package/dist/host/web/bridge.js +144 -0
- package/dist/host/web/browser.d.ts +3 -0
- package/dist/host/web/browser.js +23 -0
- package/dist/host/web/control.d.ts +17 -0
- package/dist/host/web/control.js +67 -0
- package/dist/host/web/index.d.ts +10 -0
- package/dist/host/web/index.js +9 -0
- package/dist/host/web/rpc.d.ts +24 -0
- package/dist/host/web/rpc.js +149 -0
- package/dist/host/web/runner.d.ts +16 -0
- package/dist/host/web/runner.js +44 -0
- package/dist/host/web/server.d.ts +47 -0
- package/dist/host/web/server.js +238 -0
- package/dist/host/web/state.d.ts +20 -0
- package/dist/host/web/state.js +41 -0
- package/dist/host/web/static-files.d.ts +13 -0
- package/dist/host/web/static-files.js +57 -0
- package/dist/runtime/agent.js +27675 -0
- package/dist/runtime/daemon.js +1950 -1122
- package/dist/runtime/db.js +302 -111
- package/dist/runtime/main.js +1560 -275
- package/dist/runtime/netproxy.js +168 -8
- package/dist/runtime/view.js +186 -13
- package/dist/selftest/accounts.test.js +40 -0
- package/dist/selftest/agent-paths.test.d.ts +2 -0
- package/dist/selftest/agent-paths.test.js +125 -0
- package/dist/selftest/app.test.d.ts +2 -0
- package/dist/selftest/app.test.js +324 -0
- package/dist/selftest/channel.test.js +18 -0
- package/dist/selftest/cli.test.js +35 -4
- 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.d.ts +2 -0
- package/dist/selftest/credentials.test.js +198 -0
- package/dist/selftest/engine.test.d.ts +2 -0
- package/dist/selftest/engine.test.js +127 -0
- package/dist/selftest/generate.test.js +1 -1
- package/dist/selftest/hcs.test.d.ts +2 -0
- package/dist/selftest/hcs.test.js +482 -0
- package/dist/selftest/health.test.d.ts +2 -0
- package/dist/selftest/health.test.js +470 -0
- package/dist/selftest/heavy-load.test.d.ts +2 -0
- package/dist/selftest/heavy-load.test.js +329 -0
- package/dist/selftest/host-sim.test.js +2 -0
- package/dist/selftest/index.js +31 -0
- package/dist/selftest/layout.test.js +14 -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.d.ts +7 -0
- package/dist/selftest/menu-screens.js +68 -17
- package/dist/selftest/menu.test.js +603 -22
- package/dist/selftest/migrate.test.js +1 -1
- package/dist/selftest/personalization.test.js +3 -1
- package/dist/selftest/projects.test.js +39 -1
- package/dist/selftest/shared-use.test.js +64 -2
- package/dist/selftest/sharing.test.js +50 -1
- package/dist/selftest/storage.test.d.ts +2 -0
- package/dist/selftest/storage.test.js +363 -0
- 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 +20 -0
- package/dist/selftest/support/hcs-probes.d.ts +17 -0
- package/dist/selftest/support/hcs-probes.js +41 -0
- package/dist/selftest/support/releases.js +1 -1
- package/dist/selftest/toolchains.test.js +21 -0
- package/dist/selftest/update-service.test.js +87 -3
- package/dist/selftest/vpn.test.d.ts +2 -0
- package/dist/selftest/vpn.test.js +412 -0
- package/dist/selftest/web.test.d.ts +2 -0
- package/dist/selftest/web.test.js +402 -0
- package/dist/shared/apps/catalogue.js +31 -0
- package/dist/shared/channel/index.d.ts +1 -0
- package/dist/shared/channel/index.js +1 -0
- package/dist/shared/channel/run-dir.d.ts +9 -1
- package/dist/shared/channel/run-dir.js +18 -1
- package/dist/shared/channel/schema.d.ts +53 -0
- package/dist/shared/channel/schema.js +38 -0
- package/dist/shared/channel/secrets.d.ts +29 -0
- package/dist/shared/channel/secrets.js +50 -0
- package/dist/shared/memory/index.d.ts +1 -0
- package/dist/shared/memory/index.js +1 -0
- package/dist/shared/memory/policy.d.ts +33 -0
- package/dist/shared/memory/policy.js +55 -0
- package/dist/shared/pty/output-filter.d.ts +14 -1
- package/dist/shared/pty/output-filter.js +22 -4
- package/dist/shared/pty/terminal.d.ts +4 -1
- package/dist/shared/pty/terminal.js +4 -2
- package/dist/shared/system.d.ts +1 -0
- package/dist/shared/system.js +1 -0
- package/dist/shared/vpn/download.d.ts +29 -0
- package/dist/shared/vpn/download.js +30 -0
- package/dist/shared/vpn/kill-switch.d.ts +18 -0
- package/dist/shared/vpn/kill-switch.js +81 -0
- package/dist/shared/vpn/ovpn.d.ts +23 -0
- package/dist/shared/vpn/ovpn.js +244 -0
- package/dist/shared/vpn/wireguard.d.ts +51 -0
- package/dist/shared/vpn/wireguard.js +227 -0
- package/dist/shared/web/index.d.ts +1 -0
- package/dist/shared/web/index.js +1 -0
- package/dist/shared/web/protocol.d.ts +61 -0
- package/dist/shared/web/protocol.js +35 -0
- package/dist/sync/sync-cycle.js +9 -5
- package/dist/sync/sync-state.d.ts +1 -0
- package/dist/sync/sync-state.js +1 -0
- package/dist/sync/watcher.d.ts +13 -0
- package/dist/sync/watcher.js +102 -3
- package/dist/web/index.html +14 -0
- package/dist/web/main-NRRS5O6Z.js +157 -0
- package/dist/web/main-T5DMCHYV.css +1 -0
- package/native/csharp/FolderSecurity.cs +29 -0
- package/native/csharp/HcsHost/AssemblyInfo.cs +14 -0
- package/native/csharp/HcsHost/Guard.cs +100 -0
- package/native/csharp/HcsHost/Hcs.cs +256 -0
- package/native/csharp/HcsHost/Json.cs +78 -0
- package/native/csharp/HcsHost/Program.cs +185 -0
- package/native/csharp/HcsHost/VirtualDisk.cs +95 -0
- package/native/csharp/HostTools/AssemblyInfo.cs +14 -0
- package/native/csharp/HostTools/MemoryStatus.cs +107 -0
- package/native/csharp/HostTools/Program.cs +58 -17
- package/native/scripts/check-contained.ps1 +28 -0
- package/native/scripts/contained-common.ps1 +241 -0
- package/native/scripts/dismount-contained.ps1 +66 -0
- package/native/scripts/memory-status.ps1 +34 -0
- package/native/scripts/mount-contained.ps1 +139 -0
- package/native/scripts/protect-data.ps1 +29 -0
- package/native/scripts/read-folder-sddl.ps1 +10 -0
- package/native/scripts/run-elevated.ps1 +17 -4
- package/native/scripts/set-folder-access.ps1 +49 -7
- package/native/scripts/stop-events.ps1 +23 -8
- package/node_modules/ws/LICENSE +20 -0
- package/node_modules/ws/README.md +548 -0
- package/node_modules/ws/browser.js +8 -0
- package/node_modules/ws/index.js +22 -0
- package/node_modules/ws/lib/buffer-util.js +131 -0
- package/node_modules/ws/lib/constants.js +19 -0
- package/node_modules/ws/lib/event-target.js +292 -0
- package/node_modules/ws/lib/extension.js +203 -0
- package/node_modules/ws/lib/limiter.js +55 -0
- package/node_modules/ws/lib/permessage-deflate.js +530 -0
- package/node_modules/ws/lib/receiver.js +743 -0
- package/node_modules/ws/lib/sender.js +607 -0
- package/node_modules/ws/lib/stream.js +161 -0
- package/node_modules/ws/lib/subprotocol.js +62 -0
- package/node_modules/ws/lib/validation.js +152 -0
- package/node_modules/ws/lib/websocket-server.js +562 -0
- package/node_modules/ws/lib/websocket.js +1421 -0
- package/node_modules/ws/package.json +74 -0
- package/node_modules/ws/wrapper.mjs +21 -0
- package/package.json +22 -5
- package/sandbox/csharp/EnvBroadcast.cs +33 -0
- package/sandbox/csharp/MemGov.cs +398 -0
- package/sandbox/csharp/PathSync.cs +90 -0
- package/sandbox/csharp/PtyHost.cs +111 -5
- package/sandbox/scripts/install-shell-path.ps1 +73 -0
- package/sandbox/scripts/set-machine-environment.ps1 +37 -4
- package/schemas/config.schema.json +136 -7
- package/schemas/sandbox-config.schema.json +17 -4
- package/dist/cli/commands/repair/checks.d.ts +0 -2
- package/dist/cli/commands/repair/checks.js +0 -246
- 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 -45
- package/dist/cli/commands/sessions/status.d.ts +0 -2
- package/dist/cli/commands/sessions/status.js +0 -93
- package/dist/cli/commands/sessions/window.d.ts +0 -3
- package/dist/cli/commands/sessions/window.js +0 -18
- package/dist/cli/menu/pages/start-page.d.ts +0 -43
- package/dist/cli/menu/pages/start-page.js +0 -192
- package/dist/cli/tui/tab-bar.d.ts +0 -9
- package/dist/cli/tui/tab-bar.js +0 -33
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
// The web UI's server: login and cookie, the Host / Origin / CSRF checks, static files that cannot leave their folder,
|
|
2
|
+
// sessions without tokens, the terminal bridge against a fake ptyhost on loopback (the real address check is the
|
|
3
|
+
// backend's, so the fake is only reachable here), and the RPC socket of the shared core against a fake RpcHost.
|
|
4
|
+
import http from 'node:http';
|
|
5
|
+
import net from 'node:net';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import WebSocket from 'ws';
|
|
8
|
+
import { cancelled } from '../app/kernel/index.js';
|
|
9
|
+
import { webSession } from '../cli/services/index.js';
|
|
10
|
+
import { fail } from '../core/util/index.js';
|
|
11
|
+
import { cookieValue, csrfOk, hostAllowed, originAllowed, sameSecret, StaticFiles, WebAuth, WebServer, WebStore, } from '../host/web/index.js';
|
|
12
|
+
import { encodeFrame, FrameReader, FrameType } from '../shared/pty/index.js';
|
|
13
|
+
import { CSRF_HEADER, CSRF_VALUE, parseClientMessage } from '../shared/web/index.js';
|
|
14
|
+
const TOKEN = 'a'.repeat(64);
|
|
15
|
+
const ATTACH_TOKEN = 'SECRET-ATTACH-TOKEN';
|
|
16
|
+
/** One HTTP request with exactly the headers given (Host included). */
|
|
17
|
+
function send(port, method, url, headers = {}, body) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const req = http.request({ host: '127.0.0.1', port, method, path: url, headers: { Host: '127.0.0.1:' + port, ...headers } }, (res) => {
|
|
20
|
+
const chunks = [];
|
|
21
|
+
res.on('data', (c) => chunks.push(c));
|
|
22
|
+
res.on('end', () => resolve({ status: res.statusCode ?? 0, headers: res.headers, body: Buffer.concat(chunks).toString('utf8') }));
|
|
23
|
+
});
|
|
24
|
+
req.on('error', reject);
|
|
25
|
+
req.end(body);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/** A ptyhost stand-in: records what the bridge sends, plays output, ends with EXIT when it gets the letter x. */
|
|
29
|
+
class FakePtyHost {
|
|
30
|
+
hellos = [];
|
|
31
|
+
inputs = [];
|
|
32
|
+
resizes = [];
|
|
33
|
+
pings = 0;
|
|
34
|
+
closed = 0;
|
|
35
|
+
server;
|
|
36
|
+
open = new Set();
|
|
37
|
+
port = 0;
|
|
38
|
+
constructor() {
|
|
39
|
+
this.server = net.createServer((socket) => {
|
|
40
|
+
this.open.add(socket);
|
|
41
|
+
const reader = new FrameReader();
|
|
42
|
+
socket.on('error', () => undefined);
|
|
43
|
+
socket.on('close', () => this.closed++);
|
|
44
|
+
socket.on('data', (chunk) => {
|
|
45
|
+
for (const frame of reader.push(chunk)) {
|
|
46
|
+
if (frame.type === FrameType.Hello) {
|
|
47
|
+
this.hellos.push(frame.payload);
|
|
48
|
+
// Output with a clipboard write (OSC 52) inside: the bridge must drop that part.
|
|
49
|
+
socket.write(encodeFrame(FrameType.Output, Buffer.from('hello \x1b]52;c;ZXZpbA==\x07world')));
|
|
50
|
+
}
|
|
51
|
+
else if (frame.type === FrameType.Input) {
|
|
52
|
+
this.inputs.push(frame.payload);
|
|
53
|
+
if (frame.payload.toString() === 'x') {
|
|
54
|
+
const code = Buffer.alloc(4);
|
|
55
|
+
code.writeInt32LE(3, 0);
|
|
56
|
+
socket.write(encodeFrame(FrameType.Exit, code));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (frame.type === FrameType.Resize)
|
|
60
|
+
this.resizes.push(frame.payload);
|
|
61
|
+
else if (frame.type === FrameType.Ping)
|
|
62
|
+
this.pings++;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
start() {
|
|
68
|
+
return new Promise((resolve) => this.server.listen(0, '127.0.0.1', () => {
|
|
69
|
+
this.port = this.server.address().port;
|
|
70
|
+
resolve();
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
stop() {
|
|
74
|
+
this.server.close();
|
|
75
|
+
for (const socket of this.open)
|
|
76
|
+
socket.destroy();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** What a browser tab sees on the terminal socket. */
|
|
80
|
+
class Tab {
|
|
81
|
+
texts = [];
|
|
82
|
+
binary = [];
|
|
83
|
+
closeCode = null;
|
|
84
|
+
opened = false;
|
|
85
|
+
failed = false;
|
|
86
|
+
socket;
|
|
87
|
+
constructor(url, headers) {
|
|
88
|
+
this.socket = new WebSocket(url, { headers });
|
|
89
|
+
this.socket.on('open', () => (this.opened = true));
|
|
90
|
+
this.socket.on('unexpected-response', () => (this.failed = true));
|
|
91
|
+
this.socket.on('error', () => (this.failed = true));
|
|
92
|
+
this.socket.on('message', (data, isBinary) => {
|
|
93
|
+
if (isBinary)
|
|
94
|
+
this.binary.push(data);
|
|
95
|
+
else
|
|
96
|
+
this.texts.push(data.toString('utf8'));
|
|
97
|
+
});
|
|
98
|
+
this.socket.on('close', (code) => (this.closeCode = code));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** A stand-in for the shared core: one operation that reports, asks and may fail, and one live query. */
|
|
102
|
+
function fakeRpc() {
|
|
103
|
+
const ran = [];
|
|
104
|
+
const live = { value: 1 };
|
|
105
|
+
const host = {
|
|
106
|
+
kind: (id) => (id === 'test.work' ? 'operation' : id === 'test.live' ? 'query' : undefined),
|
|
107
|
+
liveMs: (id) => (id === 'test.live' ? 50 : undefined),
|
|
108
|
+
run: async (id, input, io) => {
|
|
109
|
+
ran.push(id);
|
|
110
|
+
if (id === 'test.live')
|
|
111
|
+
return { value: live.value };
|
|
112
|
+
const how = (input ?? {});
|
|
113
|
+
io.progress('Working');
|
|
114
|
+
io.log('one line', 'muted');
|
|
115
|
+
if (!(await io.confirm({ title: 'Go on?' })))
|
|
116
|
+
cancelled();
|
|
117
|
+
if (how.fail)
|
|
118
|
+
fail('It broke.', 'Fix it like this.');
|
|
119
|
+
if (how.bug)
|
|
120
|
+
throw new Error('internal detail C:\\Users\\secret');
|
|
121
|
+
return { done: true };
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
return { host, ran, live };
|
|
125
|
+
}
|
|
126
|
+
/** The server's messages on an RPC socket, parsed. */
|
|
127
|
+
function rpcMessages(tab) {
|
|
128
|
+
return tab.texts.map((text) => JSON.parse(text));
|
|
129
|
+
}
|
|
130
|
+
const session = (id, attach) => ({
|
|
131
|
+
id,
|
|
132
|
+
launchId: 'launch1',
|
|
133
|
+
harness: 'claude',
|
|
134
|
+
cwd: 'C:\\Projects\\app',
|
|
135
|
+
phase: 'running',
|
|
136
|
+
message: 'running\x1b[31m',
|
|
137
|
+
updatedAt: '2026-01-01T00:00:00.000Z',
|
|
138
|
+
startedAt: '2026-01-01T00:00:00.000Z',
|
|
139
|
+
...(attach ? { attach } : {}),
|
|
140
|
+
});
|
|
141
|
+
export const webSuite = {
|
|
142
|
+
name: 'web',
|
|
143
|
+
title: 'web UI server: login, Host / Origin / CSRF checks, static files, sessions, terminal bridge',
|
|
144
|
+
run: async (t) => {
|
|
145
|
+
t.section('the token and the cookie');
|
|
146
|
+
t.check(sameSecret(TOKEN, TOKEN) && !sameSecret(TOKEN, 'b'.repeat(64)) && !sameSecret(TOKEN, ''), 'secrets compare');
|
|
147
|
+
let clock = 1000;
|
|
148
|
+
const auth = new WebAuth(TOKEN, () => clock);
|
|
149
|
+
const bad = auth.login('nope');
|
|
150
|
+
t.check(!bad.ok && bad.status === 401, 'a wrong token is refused');
|
|
151
|
+
t.check(!auth.login(42).ok && !auth.login({}).ok, 'a token that is not text is refused');
|
|
152
|
+
const good = auth.login(TOKEN);
|
|
153
|
+
t.check(good.ok, 'the right token logs in');
|
|
154
|
+
const cookie = good.ok ? good.cookie : '';
|
|
155
|
+
t.check(auth.signedIn('x=1; sbxweb=' + cookie), 'the cookie signs in');
|
|
156
|
+
t.check(!auth.signedIn('sbxweb=' + cookie.replace(/.$/, '0')) && !auth.signedIn(undefined), 'another cookie does not');
|
|
157
|
+
clock += 13 * 60 * 60 * 1000;
|
|
158
|
+
t.check(!auth.signedIn('sbxweb=' + cookie), 'a cookie unused for 12 hours expires');
|
|
159
|
+
const limited = new WebAuth(TOKEN, () => clock);
|
|
160
|
+
for (let i = 0; i < 8; i++)
|
|
161
|
+
limited.login('wrong');
|
|
162
|
+
const locked = limited.login(TOKEN);
|
|
163
|
+
t.check(!locked.ok && locked.status === 429, 'too many wrong tokens lock logging in, even for the right one');
|
|
164
|
+
clock += 61 * 1000;
|
|
165
|
+
t.check(limited.login(TOKEN).ok, 'the lock ends after a minute');
|
|
166
|
+
t.equal(cookieValue('a=1; sbxweb=abc; b=2'), 'abc', 'the cookie is found among others');
|
|
167
|
+
t.section('Host, Origin and CSRF checks');
|
|
168
|
+
t.check(hostAllowed('127.0.0.1:47800', 47800) && hostAllowed('localhost:47800', 47800), 'loopback names are allowed');
|
|
169
|
+
t.check(!hostAllowed('evil.example:47800', 47800) &&
|
|
170
|
+
!hostAllowed('127.0.0.1:1', 47800) &&
|
|
171
|
+
!hostAllowed(undefined, 47800) &&
|
|
172
|
+
!hostAllowed('127.0.0.1.evil.example:47800', 47800), 'any other Host is refused (DNS rebinding)');
|
|
173
|
+
t.check(originAllowed('http://127.0.0.1:47800', '127.0.0.1:47800', 47800) &&
|
|
174
|
+
!originAllowed('http://evil.example', '127.0.0.1:47800', 47800) &&
|
|
175
|
+
!originAllowed(undefined, '127.0.0.1:47800', 47800) &&
|
|
176
|
+
!originAllowed('http://localhost:47800', '127.0.0.1:47800', 47800), 'the Origin must be the page itself');
|
|
177
|
+
t.check(csrfOk({ [CSRF_HEADER]: CSRF_VALUE }) && !csrfOk({}) && !csrfOk({ [CSRF_HEADER]: '0' }), 'the CSRF header');
|
|
178
|
+
t.section('browser messages');
|
|
179
|
+
t.check(parseClientMessage('{"t":"resize","cols":80,"rows":24}')?.t === 'resize', 'a resize is read');
|
|
180
|
+
t.check([
|
|
181
|
+
'nope',
|
|
182
|
+
'{"t":"resize","cols":0,"rows":24}',
|
|
183
|
+
'{"t":"resize","cols":80.5,"rows":24}',
|
|
184
|
+
'{"t":"resize","cols":80,"rows":99999}',
|
|
185
|
+
'{"t":"input","data":"x"}',
|
|
186
|
+
'null',
|
|
187
|
+
].every((m) => parseClientMessage(m) === null), 'garbage, odd numbers and unknown kinds are refused');
|
|
188
|
+
t.section('static files');
|
|
189
|
+
const root = t.dir('web-root');
|
|
190
|
+
t.write(path.join(root, 'index.html'), '<!doctype html><title>x</title>');
|
|
191
|
+
t.write(path.join(root, 'main-ABCD1234.js'), 'console.log(1)');
|
|
192
|
+
t.write(path.join(root, 'notes.exe'), 'MZ');
|
|
193
|
+
t.write(path.join(t.temp, 'secret.txt'), 'TOP SECRET');
|
|
194
|
+
const files = new StaticFiles(root);
|
|
195
|
+
t.check(files.find('/')?.type.startsWith('text/html') === true, '/ is the app');
|
|
196
|
+
t.check(files.find('/main-ABCD1234.js')?.immutable === true, 'a hashed file may be cached for good');
|
|
197
|
+
t.check(files.find('/some/page')?.file === path.join(root, 'index.html'), 'a page of the app is the app');
|
|
198
|
+
t.check([
|
|
199
|
+
'/../secret.txt',
|
|
200
|
+
'/%2e%2e/secret.txt',
|
|
201
|
+
'/a/../../secret.txt',
|
|
202
|
+
'/..%2fsecret.txt',
|
|
203
|
+
'/..\\secret.txt',
|
|
204
|
+
'/x%00.js',
|
|
205
|
+
].every((p) => files.find(p) === null), 'paths that leave the folder find nothing');
|
|
206
|
+
t.check(files.find('/notes.exe') === null && files.find('/missing.js') === null, 'unknown types and missing files find nothing');
|
|
207
|
+
t.section('sessions never carry a token');
|
|
208
|
+
const attach = { addresses: ['127.0.0.1'], port: 1234, token: ATTACH_TOKEN };
|
|
209
|
+
const shown = JSON.stringify(webSession(session('s1', attach), 2));
|
|
210
|
+
t.excludes(shown, ATTACH_TOKEN, 'the attach token is not in what the browser gets');
|
|
211
|
+
t.excludes(shown, '1234', 'nor the port');
|
|
212
|
+
t.excludes(shown, '\x1b', 'text from the sandbox is made safe');
|
|
213
|
+
t.section('the server');
|
|
214
|
+
const fake = new FakePtyHost();
|
|
215
|
+
await fake.start();
|
|
216
|
+
const endpoint = { addresses: ['127.0.0.1'], port: fake.port, token: ATTACH_TOKEN };
|
|
217
|
+
const listed = webSession(session('abc123'), 0);
|
|
218
|
+
const created = [];
|
|
219
|
+
const backend = {
|
|
220
|
+
info: () => Promise.resolve({
|
|
221
|
+
sandbox: 'running',
|
|
222
|
+
owner: null,
|
|
223
|
+
canUse: true,
|
|
224
|
+
harnesses: ['claude'],
|
|
225
|
+
defaultHarness: 'claude',
|
|
226
|
+
projects: [],
|
|
227
|
+
}),
|
|
228
|
+
sessions: () => Promise.resolve([listed]),
|
|
229
|
+
endpoint: (id) => (id === 'abc123' ? endpoint : null),
|
|
230
|
+
create: (request) => {
|
|
231
|
+
created.push(request);
|
|
232
|
+
return Promise.resolve(listed);
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
let stopped = false;
|
|
236
|
+
const rpc = fakeRpc();
|
|
237
|
+
const server = new WebServer({
|
|
238
|
+
port: 0,
|
|
239
|
+
token: TOKEN,
|
|
240
|
+
backend,
|
|
241
|
+
staticRoot: root,
|
|
242
|
+
rpc: rpc.host,
|
|
243
|
+
onStop: () => (stopped = true),
|
|
244
|
+
});
|
|
245
|
+
await server.start();
|
|
246
|
+
const { port } = server;
|
|
247
|
+
const origin = 'http://127.0.0.1:' + port;
|
|
248
|
+
const json = { 'Content-Type': 'application/json' };
|
|
249
|
+
const ajax = { ...json, Origin: origin, [CSRF_HEADER]: CSRF_VALUE };
|
|
250
|
+
try {
|
|
251
|
+
const page = await send(port, 'GET', '/');
|
|
252
|
+
t.check(page.status === 200 && page.headers['content-type']?.startsWith('text/html') === true, 'the app is served');
|
|
253
|
+
t.includes(String(page.headers['content-security-policy']), "script-src 'self'", 'the page has a CSP without inline scripts');
|
|
254
|
+
t.check(!String(page.headers['content-security-policy']).includes("script-src 'self' 'unsafe-inline'") &&
|
|
255
|
+
page.headers['x-content-type-options'] === 'nosniff' &&
|
|
256
|
+
page.headers['x-frame-options'] === 'DENY', 'and the other protective headers');
|
|
257
|
+
t.equal((await send(port, 'GET', '/', { Host: 'evil.example:' + port })).status, 421, 'a foreign Host name is refused');
|
|
258
|
+
t.equal((await send(port, 'GET', '/..%2f..%2fsecret.txt')).status, 404, 'a path out of the folder is a 404');
|
|
259
|
+
t.equal((await send(port, 'GET', '/%2e%2e/secret.txt')).status, 404, 'an encoded path out of the folder is a 404');
|
|
260
|
+
t.equal((await send(port, 'POST', '/', {}, 'x')).status, 405, 'the files take only GET');
|
|
261
|
+
t.equal((await send(port, 'GET', '/api/sessions')).status, 401, 'the API needs the login');
|
|
262
|
+
t.equal((await send(port, 'GET', '/api/health')).status, 200, 'health needs none');
|
|
263
|
+
const noCsrf = await send(port, 'POST', '/api/login', { ...json, Origin: origin }, JSON.stringify({ token: TOKEN }));
|
|
264
|
+
t.equal(noCsrf.status, 403, 'login without the CSRF header is refused');
|
|
265
|
+
const foreign = await send(port, 'POST', '/api/login', { ...ajax, Origin: 'http://evil.example' }, JSON.stringify({ token: TOKEN }));
|
|
266
|
+
t.equal(foreign.status, 403, 'login from another origin is refused');
|
|
267
|
+
const noOrigin = await send(port, 'POST', '/api/login', { ...json, [CSRF_HEADER]: CSRF_VALUE }, JSON.stringify({ token: TOKEN }));
|
|
268
|
+
t.equal(noOrigin.status, 403, 'login without an Origin is refused');
|
|
269
|
+
t.equal((await send(port, 'POST', '/api/login', ajax, JSON.stringify({ token: 'wrong' }))).status, 401, 'a wrong token');
|
|
270
|
+
t.equal((await send(port, 'POST', '/api/login', { ...ajax, 'Content-Type': 'text/plain' }, '{}')).status, 415, 'JSON only');
|
|
271
|
+
const login = await send(port, 'POST', '/api/login', ajax, JSON.stringify({ token: TOKEN }));
|
|
272
|
+
const setCookie = String(login.headers['set-cookie']?.[0] ?? '');
|
|
273
|
+
t.check(login.status === 200 && /HttpOnly/.test(setCookie) && /SameSite=Strict/.test(setCookie), 'the right token gets a safe cookie');
|
|
274
|
+
const Cookie = setCookie.split(';')[0];
|
|
275
|
+
const list = await send(port, 'GET', '/api/sessions', { Cookie });
|
|
276
|
+
t.check(list.status === 200 && JSON.parse(list.body).length === 1, 'a signed-in browser lists sessions');
|
|
277
|
+
t.excludes(list.body, ATTACH_TOKEN, 'the list has no attach token');
|
|
278
|
+
t.equal((await send(port, 'GET', '/api/info', { Cookie })).status, 200, 'and reads the info');
|
|
279
|
+
t.equal((await send(port, 'POST', '/api/sessions', { ...json, Cookie }, '{}')).status, 403, 'a new session needs CSRF and Origin');
|
|
280
|
+
t.equal((await send(port, 'POST', '/api/sessions', { ...ajax, Cookie }, '{"harness":"../x","project":"p"}')).status, 400, 'odd names are refused');
|
|
281
|
+
const made = await send(port, 'POST', '/api/sessions', { ...ajax, Cookie }, '{"harness":"claude","project":"app"}');
|
|
282
|
+
t.check(made.status === 201 && created.length === 1, 'a new session is requested');
|
|
283
|
+
t.section('the terminal bridge');
|
|
284
|
+
const url = 'ws://127.0.0.1:' + port + '/api/sessions/abc123/terminal?cols=100&rows=40';
|
|
285
|
+
const attempts = [
|
|
286
|
+
new Tab(url, { Cookie }),
|
|
287
|
+
new Tab(url, { Origin: 'http://evil.example', Cookie }),
|
|
288
|
+
new Tab(url, { Origin: origin }),
|
|
289
|
+
new Tab('ws://127.0.0.1:' + port + '/api/sessions/nosuch/terminal', { Origin: origin, Cookie }),
|
|
290
|
+
];
|
|
291
|
+
await t.waitFor(() => attempts.every((a) => a.failed), 5000);
|
|
292
|
+
t.check(attempts.every((a) => !a.opened && a.failed), 'no Origin, a foreign Origin, no cookie and an unknown session are all refused');
|
|
293
|
+
t.check(fake.hellos.length === 0, 'and the session was never contacted');
|
|
294
|
+
const tab = new Tab(url, { Origin: origin, Cookie });
|
|
295
|
+
await t.waitFor(() => tab.binary.length > 0, 5000);
|
|
296
|
+
t.check(tab.texts.includes('{"t":"ready"}'), 'the browser is told when it is connected');
|
|
297
|
+
const hello = fake.hellos[0];
|
|
298
|
+
t.check(hello !== undefined &&
|
|
299
|
+
hello.readInt16LE(0) === 100 &&
|
|
300
|
+
hello.readInt16LE(2) === 40 &&
|
|
301
|
+
hello.subarray(5).toString() === ATTACH_TOKEN, 'HELLO carries the attach token and the size');
|
|
302
|
+
const output = Buffer.concat(tab.binary).toString();
|
|
303
|
+
t.check(output.includes('hello ') && output.includes('world'), 'output is relayed as binary');
|
|
304
|
+
t.excludes(output, ']52', 'and a clipboard write (OSC 52) in it is dropped');
|
|
305
|
+
tab.socket.send(Buffer.from('abc'));
|
|
306
|
+
tab.socket.send(JSON.stringify({ t: 'resize', cols: 90, rows: 30 }));
|
|
307
|
+
await t.waitFor(() => fake.inputs.length > 0 && fake.resizes.length > 0, 5000);
|
|
308
|
+
t.check(fake.inputs[0]?.toString() === 'abc', 'keystrokes are forwarded as INPUT');
|
|
309
|
+
t.check(fake.resizes[0]?.readInt16LE(0) === 90 && fake.resizes[0]?.readInt16LE(2) === 30, 'a resize is forwarded');
|
|
310
|
+
tab.socket.send(Buffer.from('x'));
|
|
311
|
+
await t.waitFor(() => tab.closeCode !== null, 5000);
|
|
312
|
+
t.check(tab.texts.includes('{"t":"exit","code":3}'), 'EXIT reaches the browser with its code');
|
|
313
|
+
t.equal(tab.closeCode, 1000, 'and the socket closes cleanly');
|
|
314
|
+
const rude = new Tab(url, { Origin: origin, Cookie });
|
|
315
|
+
await t.waitFor(() => rude.binary.length > 0, 5000);
|
|
316
|
+
rude.socket.send('this is not json');
|
|
317
|
+
await t.waitFor(() => rude.closeCode !== null, 5000);
|
|
318
|
+
t.equal(rude.closeCode, 1008, 'a browser that sends garbage is closed');
|
|
319
|
+
const leaving = new Tab(url, { Origin: origin, Cookie });
|
|
320
|
+
await t.waitFor(() => leaving.binary.length > 0, 5000);
|
|
321
|
+
leaving.socket.close();
|
|
322
|
+
await t.waitFor(() => fake.closed >= 3, 5000);
|
|
323
|
+
t.check(fake.closed >= 3, 'closing the browser side closes the connection to the session');
|
|
324
|
+
t.section('the RPC socket');
|
|
325
|
+
const rpcUrl = 'ws://127.0.0.1:' + port + '/api/rpc';
|
|
326
|
+
const refusedRpc = [
|
|
327
|
+
new Tab(rpcUrl, { Cookie }),
|
|
328
|
+
new Tab(rpcUrl, { Origin: 'http://evil.example', Cookie }),
|
|
329
|
+
new Tab(rpcUrl, { Origin: origin }),
|
|
330
|
+
];
|
|
331
|
+
await t.waitFor(() => refusedRpc.every((a) => a.failed), 5000);
|
|
332
|
+
t.check(refusedRpc.every((a) => !a.opened), 'the RPC socket needs the cookie and this page as its Origin, like the terminal');
|
|
333
|
+
const browser = new Tab(rpcUrl, { Origin: origin, Cookie });
|
|
334
|
+
await t.waitFor(() => browser.opened, 5000);
|
|
335
|
+
const reply = (id, kind) => rpcMessages(browser).find((m) => m.id === id && m.t === kind);
|
|
336
|
+
browser.socket.send(JSON.stringify({ t: 'call', id: 'c1', op: 'test.work', input: {} }));
|
|
337
|
+
await t.waitFor(() => reply('c1', 'ask'), 5000);
|
|
338
|
+
t.check(reply('c1', 'progress') !== undefined && reply('c1', 'log') !== undefined, 'a call streams its progress and log lines before it asks');
|
|
339
|
+
browser.socket.send(JSON.stringify({ t: 'answer', id: 'c1', value: true }));
|
|
340
|
+
await t.waitFor(() => reply('c1', 'result'), 5000);
|
|
341
|
+
t.equal(reply('c1', 'result'), { t: 'result', id: 'c1', output: { done: true } }, 'the answer lets it finish');
|
|
342
|
+
browser.socket.send(JSON.stringify({ t: 'call', id: 'c2', op: 'test.work', input: {} }));
|
|
343
|
+
await t.waitFor(() => reply('c2', 'ask'), 5000);
|
|
344
|
+
browser.socket.send(JSON.stringify({ t: 'answer', id: 'c2', value: 'yes please' }));
|
|
345
|
+
await t.waitFor(() => reply('c2', 'error'), 5000);
|
|
346
|
+
const declinedCall = reply('c2', 'error');
|
|
347
|
+
t.check(declinedCall?.t === 'error' && declinedCall.error.kind === 'cancelled', 'an answer of the wrong kind counts as no: nothing changes');
|
|
348
|
+
browser.socket.send(JSON.stringify({ t: 'call', id: 'c3', op: 'test.work', input: { fail: true } }));
|
|
349
|
+
browser.socket.send(JSON.stringify({ t: 'call', id: 'c4', op: 'test.work', input: { bug: true } }));
|
|
350
|
+
await t.waitFor(() => reply('c3', 'ask') && reply('c4', 'ask'), 5000);
|
|
351
|
+
browser.socket.send(JSON.stringify({ t: 'answer', id: 'c3', value: true }));
|
|
352
|
+
browser.socket.send(JSON.stringify({ t: 'answer', id: 'c4', value: true }));
|
|
353
|
+
await t.waitFor(() => reply('c3', 'error') && reply('c4', 'error'), 5000);
|
|
354
|
+
t.equal(reply('c3', 'error'), { t: 'error', id: 'c3', error: { kind: 'user', message: 'It broke.', fix: 'Fix it like this.' } }, 'a failure arrives as its message and fix');
|
|
355
|
+
t.equal(reply('c4', 'error'), { t: 'error', id: 'c4', error: { kind: 'bug', message: 'Something went wrong.' } }, "a bug's details stay on this PC");
|
|
356
|
+
const before = rpc.ran.length;
|
|
357
|
+
browser.socket.send(JSON.stringify({ t: 'call', id: 'c5', op: 'sandbox.notThere', input: {} }));
|
|
358
|
+
await t.waitFor(() => reply('c5', 'error'), 5000);
|
|
359
|
+
const unknown = reply('c5', 'error');
|
|
360
|
+
t.check(unknown?.t === 'error' && unknown.error.kind === 'invalid' && rpc.ran.length === before, 'an operation that is not allowed on the web is never run');
|
|
361
|
+
browser.socket.send(JSON.stringify({ t: 'subscribe', id: 's1', query: 'test.live', input: {} }));
|
|
362
|
+
await t.waitFor(() => reply('s1', 'data'), 5000);
|
|
363
|
+
rpc.live.value = 2;
|
|
364
|
+
await t.waitFor(() => rpcMessages(browser).filter((m) => m.id === 's1' && m.t === 'data').length === 2, 5000);
|
|
365
|
+
const updates = rpcMessages(browser).filter((m) => m.id === 's1' && m.t === 'data');
|
|
366
|
+
t.equal(updates.map((m) => (m.t === 'data' ? m.output : null)), [{ value: 1 }, { value: 2 }], 'a live query sends its result, then again only when it changed');
|
|
367
|
+
browser.socket.send(JSON.stringify({ t: 'unsubscribe', id: 's1' }));
|
|
368
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
369
|
+
const reads = rpc.ran.filter((id) => id === 'test.live').length;
|
|
370
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
371
|
+
t.equal(rpc.ran.filter((id) => id === 'test.live').length, reads, 'unsubscribing stops the reads');
|
|
372
|
+
browser.socket.send(JSON.stringify({ t: 'call', id: 'bad id!', op: 'test.work', input: {} }));
|
|
373
|
+
await t.waitFor(() => browser.closeCode !== null, 5000);
|
|
374
|
+
t.equal(browser.closeCode, 1008, 'a message that does not fit the schema closes the socket');
|
|
375
|
+
const leaver = new Tab(rpcUrl, { Origin: origin, Cookie });
|
|
376
|
+
await t.waitFor(() => leaver.opened, 5000);
|
|
377
|
+
const runs = rpc.ran.length;
|
|
378
|
+
leaver.socket.send(JSON.stringify({ t: 'call', id: 'c1', op: 'test.work', input: {} }));
|
|
379
|
+
await t.waitFor(() => leaver.texts.some((m) => m.includes('"ask"')), 5000);
|
|
380
|
+
leaver.socket.close();
|
|
381
|
+
await t.waitFor(() => leaver.closeCode !== null, 5000);
|
|
382
|
+
t.check(rpc.ran.length === runs + 1, 'a browser that leaves mid-question answers no (the call ends)');
|
|
383
|
+
t.section('stopping');
|
|
384
|
+
t.equal((await send(port, 'POST', '/api/stop', { ...json, Cookie }, '{}')).status, 403, 'stop needs CSRF and Origin too');
|
|
385
|
+
t.equal((await send(port, 'POST', '/api/stop', { ...ajax, Cookie }, '{}')).status, 200, 'a signed-in browser can stop it');
|
|
386
|
+
await t.waitFor(() => stopped, 3000);
|
|
387
|
+
t.check(stopped, 'the server is told to stop');
|
|
388
|
+
}
|
|
389
|
+
finally {
|
|
390
|
+
await server.stop();
|
|
391
|
+
fake.stop();
|
|
392
|
+
}
|
|
393
|
+
t.section('the state file');
|
|
394
|
+
const store = new WebStore(t.dir('web-state'), () => undefined);
|
|
395
|
+
store.write({ pid: 1234, port: 47800, token: TOKEN, startedAt: '2026-01-01T00:00:00.000Z' });
|
|
396
|
+
t.equal(store.read()?.port, 47800, 'pid, port and token are kept');
|
|
397
|
+
t.write(path.join(store.dir, 'server.json'), '{"pid":1,"port":1,"token":"short","startedAt":"x"}');
|
|
398
|
+
t.check(store.read() === null, 'a state file that does not fit the schema is ignored');
|
|
399
|
+
store.clear();
|
|
400
|
+
t.check(store.read() === null, 'and it can be removed');
|
|
401
|
+
},
|
|
402
|
+
};
|
|
@@ -235,6 +235,17 @@ export const APPS = [
|
|
|
235
235
|
exe: 'lazygit.exe',
|
|
236
236
|
cli: true,
|
|
237
237
|
},
|
|
238
|
+
{
|
|
239
|
+
id: 'jj',
|
|
240
|
+
name: 'Jujutsu (jj)',
|
|
241
|
+
category: 'git',
|
|
242
|
+
about: 'Git-compatible version control; conflicts do not block a rebase. Pilot, untested with worktrees.',
|
|
243
|
+
size: '10 MB',
|
|
244
|
+
source: gh('jj-vcs/jj', '^jj-v[\\d.]+-x86_64-pc-windows-msvc\\.zip$'),
|
|
245
|
+
exe: 'jj.exe',
|
|
246
|
+
cli: true,
|
|
247
|
+
fullGitOnly: true,
|
|
248
|
+
},
|
|
238
249
|
// ---- utilities
|
|
239
250
|
{
|
|
240
251
|
id: '7zip',
|
|
@@ -764,6 +775,26 @@ export const APPS = [
|
|
|
764
775
|
exe: 'gitleaks.exe',
|
|
765
776
|
cli: true,
|
|
766
777
|
},
|
|
778
|
+
{
|
|
779
|
+
id: 'ast-grep',
|
|
780
|
+
name: 'ast-grep',
|
|
781
|
+
category: 'cli',
|
|
782
|
+
about: 'Search and rewrite code by its syntax tree (ast-grep, sg); prints only the matches.',
|
|
783
|
+
size: '15 MB',
|
|
784
|
+
source: gh('ast-grep/ast-grep', '^app-x86_64-pc-windows-msvc\\.zip$'),
|
|
785
|
+
exe: 'ast-grep.exe',
|
|
786
|
+
cli: true,
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
id: 'rust-analyzer',
|
|
790
|
+
name: 'rust-analyzer',
|
|
791
|
+
category: 'cli',
|
|
792
|
+
about: 'Rust language server: type errors and go-to-definition without cargo (for editors and the Claude LSP plugin).',
|
|
793
|
+
size: '18 MB',
|
|
794
|
+
source: gh('rust-lang/rust-analyzer', '^rust-analyzer-x86_64-pc-windows-msvc\\.zip$'),
|
|
795
|
+
exe: 'rust-analyzer.exe',
|
|
796
|
+
cli: true,
|
|
797
|
+
},
|
|
767
798
|
];
|
|
768
799
|
export function appById(id) {
|
|
769
800
|
return APPS.find((a) => a.id === id);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GuestRequest, GuestResponse, ProjectStatus, SandboxStatus, SessionStatus } from './schema.js';
|
|
1
|
+
import type { GuestRequest, GuestResponse, PressureStatus, ProjectStatus, SandboxStatus, SessionStatus, VpnStatus } from './schema.js';
|
|
2
2
|
/** Ids become file names, so only a strict alphabet is allowed (no separators, no dots). */
|
|
3
3
|
export declare function safeId(id: string): string;
|
|
4
4
|
export declare function now(): string;
|
|
@@ -6,18 +6,26 @@ export declare function now(): string;
|
|
|
6
6
|
export declare class RunDir {
|
|
7
7
|
readonly root: string;
|
|
8
8
|
readonly sandboxFile: string;
|
|
9
|
+
readonly vpnFile: string;
|
|
10
|
+
/** What the memory governor in the sandbox is doing (pressure.json). */
|
|
11
|
+
readonly pressureFile: string;
|
|
9
12
|
readonly requestsDir: string;
|
|
10
13
|
readonly responsesDir: string;
|
|
11
14
|
readonly sessionsDir: string;
|
|
12
15
|
readonly projectsDir: string;
|
|
13
16
|
/** audit.processes: programs the sandbox started, in small .jsonl files the host takes in and deletes. */
|
|
14
17
|
readonly auditDir: string;
|
|
18
|
+
/** One-shot secrets the host hands over (see secrets.ts). Next to run, not in it: shared-use users can change run. */
|
|
19
|
+
readonly secretsDir: string;
|
|
15
20
|
constructor(dataDir: string);
|
|
16
21
|
requestFile(id: string): string;
|
|
17
22
|
responseFile(id: string): string;
|
|
18
23
|
sessionFile(id: string): string;
|
|
19
24
|
projectFile(name: string): string;
|
|
25
|
+
secretFile(id: string): string;
|
|
20
26
|
readSandboxStatus(): SandboxStatus | null;
|
|
27
|
+
readVpnStatus(): VpnStatus | null;
|
|
28
|
+
readPressure(): PressureStatus | null;
|
|
21
29
|
readSession(id: string): SessionStatus | null;
|
|
22
30
|
readResponse(id: string): GuestResponse | null;
|
|
23
31
|
listSessions(): SessionStatus[];
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// written atomically and parsed with the channel schemas on read. Anything that does not parse is ignored.
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
-
import { GuestRequestSchema, GuestResponseSchema, IdSchema, ProjectStatusSchema, SandboxStatusSchema, SessionStatusSchema, } from './schema.js';
|
|
6
|
+
import { GuestRequestSchema, GuestResponseSchema, IdSchema, PressureStatusSchema, ProjectStatusSchema, SandboxStatusSchema, SessionStatusSchema, VpnStatusSchema, } from './schema.js';
|
|
7
7
|
import { readValid } from '../io/index.js';
|
|
8
8
|
/** Ids become file names, so only a strict alphabet is allowed (no separators, no dots). */
|
|
9
9
|
export function safeId(id) {
|
|
@@ -33,20 +33,28 @@ const scan = (schema, dir) => {
|
|
|
33
33
|
export class RunDir {
|
|
34
34
|
root;
|
|
35
35
|
sandboxFile;
|
|
36
|
+
vpnFile;
|
|
37
|
+
/** What the memory governor in the sandbox is doing (pressure.json). */
|
|
38
|
+
pressureFile;
|
|
36
39
|
requestsDir;
|
|
37
40
|
responsesDir;
|
|
38
41
|
sessionsDir;
|
|
39
42
|
projectsDir;
|
|
40
43
|
/** audit.processes: programs the sandbox started, in small .jsonl files the host takes in and deletes. */
|
|
41
44
|
auditDir;
|
|
45
|
+
/** One-shot secrets the host hands over (see secrets.ts). Next to run, not in it: shared-use users can change run. */
|
|
46
|
+
secretsDir;
|
|
42
47
|
constructor(dataDir) {
|
|
43
48
|
this.root = path.join(dataDir, 'run');
|
|
44
49
|
this.sandboxFile = path.join(this.root, 'sandbox.json');
|
|
50
|
+
this.vpnFile = path.join(this.root, 'vpn.json');
|
|
51
|
+
this.pressureFile = path.join(this.root, 'pressure.json');
|
|
45
52
|
this.requestsDir = path.join(this.root, 'requests');
|
|
46
53
|
this.responsesDir = path.join(this.root, 'responses');
|
|
47
54
|
this.sessionsDir = path.join(this.root, 'sessions');
|
|
48
55
|
this.projectsDir = path.join(this.root, 'projects');
|
|
49
56
|
this.auditDir = path.join(this.root, 'audit');
|
|
57
|
+
this.secretsDir = path.join(dataDir, 'secrets');
|
|
50
58
|
}
|
|
51
59
|
requestFile(id) {
|
|
52
60
|
return path.join(this.requestsDir, safeId(id) + '.json');
|
|
@@ -60,9 +68,18 @@ export class RunDir {
|
|
|
60
68
|
projectFile(name) {
|
|
61
69
|
return path.join(this.projectsDir, safeId(name) + '.json');
|
|
62
70
|
}
|
|
71
|
+
secretFile(id) {
|
|
72
|
+
return path.join(this.secretsDir, safeId(id) + '.json');
|
|
73
|
+
}
|
|
63
74
|
readSandboxStatus() {
|
|
64
75
|
return readValid(SandboxStatusSchema, this.sandboxFile);
|
|
65
76
|
}
|
|
77
|
+
readVpnStatus() {
|
|
78
|
+
return readValid(VpnStatusSchema, this.vpnFile);
|
|
79
|
+
}
|
|
80
|
+
readPressure() {
|
|
81
|
+
return readValid(PressureStatusSchema, this.pressureFile);
|
|
82
|
+
}
|
|
66
83
|
readSession(id) {
|
|
67
84
|
return readValid(SessionStatusSchema, this.sessionFile(id));
|
|
68
85
|
}
|
|
@@ -193,6 +193,8 @@ export declare const ProjectSpecSchema: z.ZodObject<{
|
|
|
193
193
|
loginFiles: z.ZodArray<z.ZodString>;
|
|
194
194
|
hostDir: z.ZodOptional<z.ZodString>;
|
|
195
195
|
}, z.core.$strict>>;
|
|
196
|
+
localState: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
197
|
+
bootReset: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
196
198
|
resumeArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
197
199
|
resumeOneArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
198
200
|
}, z.core.$strict>>;
|
|
@@ -349,6 +351,8 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
349
351
|
loginFiles: z.ZodArray<z.ZodString>;
|
|
350
352
|
hostDir: z.ZodOptional<z.ZodString>;
|
|
351
353
|
}, z.core.$strict>>;
|
|
354
|
+
localState: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
355
|
+
bootReset: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
352
356
|
resumeArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
353
357
|
resumeOneArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
354
358
|
}, z.core.$strict>>;
|
|
@@ -369,6 +373,8 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
369
373
|
loginFiles: z.ZodArray<z.ZodString>;
|
|
370
374
|
hostDir: z.ZodOptional<z.ZodString>;
|
|
371
375
|
}, z.core.$strict>>;
|
|
376
|
+
localState: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
377
|
+
bootReset: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
372
378
|
resumeArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
373
379
|
resumeOneArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
374
380
|
}, z.core.$strict>>;
|
|
@@ -520,6 +526,8 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
520
526
|
loginFiles: z.ZodArray<z.ZodString>;
|
|
521
527
|
hostDir: z.ZodOptional<z.ZodString>;
|
|
522
528
|
}, z.core.$strict>>;
|
|
529
|
+
localState: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
530
|
+
bootReset: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
523
531
|
resumeArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
524
532
|
resumeOneArguments: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
525
533
|
}, z.core.$strict>>;
|
|
@@ -543,7 +551,37 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
543
551
|
kind: z.ZodLiteral<"ping">;
|
|
544
552
|
id: z.ZodString;
|
|
545
553
|
createdAt: z.ZodString;
|
|
554
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
555
|
+
kind: z.ZodLiteral<"pressure">;
|
|
556
|
+
id: z.ZodString;
|
|
557
|
+
reason: z.ZodString;
|
|
558
|
+
seconds: z.ZodNumber;
|
|
559
|
+
createdAt: z.ZodString;
|
|
546
560
|
}, z.core.$strict>], "kind">;
|
|
561
|
+
/** C:\AgentData\run\pressure.json: what the memory governor in the sandbox is doing (guest-written, capped). */
|
|
562
|
+
export declare const PressureStatusSchema: z.ZodObject<{
|
|
563
|
+
level: z.ZodEnum<{
|
|
564
|
+
ok: "ok";
|
|
565
|
+
throttled: "throttled";
|
|
566
|
+
suspending: "suspending";
|
|
567
|
+
}>;
|
|
568
|
+
profile: z.ZodEnum<{
|
|
569
|
+
normal: "normal";
|
|
570
|
+
gentle: "gentle";
|
|
571
|
+
}>;
|
|
572
|
+
ramFreePercent: z.ZodNumber;
|
|
573
|
+
commitPercent: z.ZodNumber;
|
|
574
|
+
lowered: z.ZodNumber;
|
|
575
|
+
paused: z.ZodArray<z.ZodObject<{
|
|
576
|
+
name: z.ZodString;
|
|
577
|
+
pid: z.ZodNumber;
|
|
578
|
+
seconds: z.ZodNumber;
|
|
579
|
+
}, z.core.$strip>>;
|
|
580
|
+
pcLow: z.ZodBoolean;
|
|
581
|
+
lastAction: z.ZodString;
|
|
582
|
+
updatedAt: z.ZodString;
|
|
583
|
+
}, z.core.$strip>;
|
|
584
|
+
export type PressureStatus = z.output<typeof PressureStatusSchema>;
|
|
547
585
|
/** C:\AgentData\run\responses\<id>.json: the guest's reply. */
|
|
548
586
|
export declare const GuestResponseSchema: z.ZodObject<{
|
|
549
587
|
id: z.ZodString;
|
|
@@ -597,6 +635,21 @@ export declare const ProjectStatusSchema: z.ZodObject<{
|
|
|
597
635
|
}, z.core.$strip>>;
|
|
598
636
|
summary: z.ZodArray<z.ZodString>;
|
|
599
637
|
}, z.core.$strip>;
|
|
638
|
+
/** The guest writes run/vpn.json: whether the VPN tunnel is up (validated and capped on the host). */
|
|
639
|
+
export declare const VpnStatusSchema: z.ZodObject<{
|
|
640
|
+
state: z.ZodEnum<{
|
|
641
|
+
off: "off";
|
|
642
|
+
failed: "failed";
|
|
643
|
+
starting: "starting";
|
|
644
|
+
up: "up";
|
|
645
|
+
}>;
|
|
646
|
+
message: z.ZodString;
|
|
647
|
+
killSwitch: z.ZodBoolean;
|
|
648
|
+
server: z.ZodOptional<z.ZodString>;
|
|
649
|
+
publicIp: z.ZodOptional<z.ZodString>;
|
|
650
|
+
updatedAt: z.ZodString;
|
|
651
|
+
}, z.core.$strip>;
|
|
652
|
+
export type VpnStatus = z.output<typeof VpnStatusSchema>;
|
|
600
653
|
export type ProjectSpec = z.output<typeof ProjectSpecSchema>;
|
|
601
654
|
export type ProjectStatus = z.output<typeof ProjectStatusSchema>;
|
|
602
655
|
export type AttachEndpoint = z.output<typeof AttachEndpointSchema>;
|