windows-development-sandbox 1.5.0 → 1.7.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 +17 -1
- package/dist/cli/args.d.ts +13 -2
- package/dist/cli/args.js +95 -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/index.d.ts +1 -0
- package/dist/cli/commands/index.js +1 -0
- 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 +57 -4
- package/dist/cli/commands/launch/prepare.js +3 -1
- 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.d.ts +1 -1
- package/dist/cli/commands/maintenance/config.js +112 -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/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 +7 -0
- 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/data.d.ts +2 -0
- package/dist/cli/commands/repair/checks/data.js +241 -0
- package/dist/cli/commands/repair/checks/index.d.ts +2 -0
- package/dist/cli/commands/repair/checks/index.js +20 -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 +55 -0
- package/dist/cli/commands/repair/checks/sandbox.d.ts +2 -0
- package/dist/cli/commands/repair/checks/sandbox.js +267 -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 +53 -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 +49 -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/list.js +5 -2
- package/dist/cli/commands/sessions/status.d.ts +2 -0
- package/dist/cli/commands/sessions/status.js +27 -6
- package/dist/cli/commands/setup/index.d.ts +5 -0
- package/dist/cli/commands/setup/index.js +25 -1
- 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 +85 -0
- package/dist/cli/commands/storage/storage.d.ts +8 -0
- package/dist/cli/commands/storage/storage.js +171 -0
- package/dist/cli/commands/user-config.js +1 -1
- package/dist/cli/commands/vpn.d.ts +4 -0
- package/dist/cli/commands/vpn.js +75 -0
- package/dist/cli/commands/web.d.ts +4 -0
- package/dist/cli/commands/web.js +95 -0
- package/dist/cli/history.js +1 -1
- package/dist/cli/index.js +46 -3
- package/dist/cli/manifest.d.ts +303 -29
- package/dist/cli/manifest.js +173 -43
- 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 +31 -2
- package/dist/cli/menu/apps.js +44 -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/menu.d.ts +1 -1
- package/dist/cli/menu/menu.js +11 -8
- package/dist/cli/menu/pages/accounts-page.js +55 -24
- package/dist/cli/menu/pages/agents-page.d.ts +66 -0
- package/dist/cli/menu/pages/agents-page.js +338 -0
- package/dist/cli/menu/pages/apps-page.js +1 -2
- package/dist/cli/menu/pages/list-page.d.ts +16 -3
- package/dist/cli/menu/pages/list-page.js +40 -8
- package/dist/cli/menu/pages/maintenance-page.d.ts +9 -0
- package/dist/cli/menu/pages/maintenance-page.js +161 -72
- package/dist/cli/menu/pages/sandbox-page.d.ts +3 -0
- package/dist/cli/menu/pages/sandbox-page.js +132 -43
- package/dist/cli/menu/pages/sessions-page.d.ts +4 -0
- package/dist/cli/menu/pages/sessions-page.js +41 -25
- package/dist/cli/menu/pages/settings-page.d.ts +8 -4
- package/dist/cli/menu/pages/settings-page.js +36 -13
- package/dist/cli/menu/settings/categories.d.ts +2 -0
- package/dist/cli/menu/settings/categories.js +19 -44
- package/dist/cli/menu/settings/collections/collection-editor.d.ts +7 -1
- 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 +11 -1
- package/dist/cli/menu/settings/collections/string-list-editor.js +23 -0
- package/dist/cli/menu/settings/index.d.ts +1 -1
- package/dist/cli/menu/settings/index.js +1 -1
- package/dist/cli/menu/settings/object-editor.d.ts +5 -1
- package/dist/cli/menu/settings/object-editor.js +15 -4
- package/dist/cli/menu/settings/path-kind.d.ts +20 -0
- package/dist/cli/menu/settings/path-kind.js +44 -0
- package/dist/cli/menu/settings/schema-node.d.ts +9 -0
- package/dist/cli/menu/settings/schema-node.js +26 -0
- package/dist/cli/menu/settings/value-editor.d.ts +5 -0
- package/dist/cli/menu/settings/value-editor.js +51 -18
- package/dist/cli/menu/several.js +7 -5
- package/dist/cli/menu/shell.d.ts +37 -10
- package/dist/cli/menu/shell.js +90 -10
- package/dist/cli/menu/vpn-login.d.ts +16 -0
- package/dist/cli/menu/vpn-login.js +66 -0
- package/dist/cli/output/print.d.ts +2 -0
- package/dist/cli/output/print.js +8 -1
- package/dist/cli/services/accounts-service.d.ts +2 -0
- package/dist/cli/services/accounts-service.js +2 -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/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 +6 -3
- package/dist/cli/tui/app.js +12 -5
- 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 +14 -2
- package/dist/cli/tui/list/list-screen.js +68 -32
- package/dist/cli/tui/list/list-types.d.ts +8 -4
- package/dist/cli/tui/nav.d.ts +29 -0
- package/dist/cli/tui/nav.js +67 -0
- 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 +5 -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 +23 -0
- package/dist/core/schema/config.js +10 -1
- package/dist/core/schema/harness.d.ts +1 -0
- package/dist/core/schema/harness.js +8 -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.js +1 -0
- package/dist/core/schema/vpn.d.ts +17 -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 +26 -0
- package/dist/guest/agents/agent-files.d.ts +49 -0
- package/dist/guest/agents/agent-files.js +63 -0
- package/dist/guest/agents/agent-installer.js +2 -9
- package/dist/guest/agents/local-state.d.ts +12 -0
- package/dist/guest/agents/local-state.js +198 -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 +50 -11
- package/dist/guest/daemon/daemon.js +4 -1
- package/dist/guest/daemon/exec.js +2 -1
- package/dist/guest/daemon/idle-shutdown.js +2 -0
- package/dist/guest/daemon/sessions.js +4 -1
- package/dist/guest/entries/agent.d.ts +1 -0
- package/dist/guest/entries/agent.js +10 -0
- package/dist/guest/entries/daemon.js +2 -0
- package/dist/guest/runtime/csc.d.ts +1 -0
- package/dist/guest/runtime/csc.js +1 -0
- package/dist/guest/runtime/secrets.d.ts +12 -0
- package/dist/guest/runtime/secrets.js +63 -0
- 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/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 +126 -0
- package/dist/host/channel/attach.d.ts +3 -1
- package/dist/host/channel/attach.js +1 -1
- package/dist/host/channel/channel.d.ts +7 -0
- package/dist/host/channel/channel.js +26 -2
- 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.js +25 -1
- package/dist/host/launch/session-file.js +4 -1
- package/dist/host/launch/stage.js +1 -1
- 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/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/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/platform.d.ts +2 -0
- package/dist/host/sandbox/platform.js +21 -2
- 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 +93 -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 +11 -2
- 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/history.d.ts +21 -0
- package/dist/host/storage/history.js +49 -0
- package/dist/host/storage/index.d.ts +4 -0
- package/dist/host/storage/index.js +4 -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/root.d.ts +18 -0
- package/dist/host/storage/root.js +77 -0
- package/dist/host/tools/windows-terminal.d.ts +2 -0
- package/dist/host/tools/windows-terminal.js +23 -5
- 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 +17 -0
- package/dist/host/vpn/prepare.js +77 -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 +1 -0
- package/dist/host/web/browser.js +10 -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 +9 -0
- package/dist/host/web/index.js +8 -0
- package/dist/host/web/runner.d.ts +14 -0
- package/dist/host/web/runner.js +45 -0
- package/dist/host/web/server.d.ts +40 -0
- package/dist/host/web/server.js +218 -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 +27614 -0
- package/dist/runtime/daemon.js +1012 -618
- package/dist/runtime/db.js +236 -106
- package/dist/runtime/main.js +1093 -250
- package/dist/runtime/netproxy.js +102 -3
- package/dist/runtime/view.js +102 -3
- 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 +115 -0
- package/dist/selftest/channel.test.js +5 -0
- package/dist/selftest/cli.test.js +34 -4
- package/dist/selftest/credentials.test.d.ts +2 -0
- package/dist/selftest/credentials.test.js +198 -0
- package/dist/selftest/generate.test.js +1 -1
- package/dist/selftest/health.test.d.ts +2 -0
- package/dist/selftest/health.test.js +470 -0
- package/dist/selftest/host-sim.test.js +2 -0
- package/dist/selftest/index.js +19 -0
- package/dist/selftest/layout.test.js +2 -0
- package/dist/selftest/menu-screens.d.ts +7 -0
- package/dist/selftest/menu-screens.js +65 -16
- package/dist/selftest/menu.test.js +574 -21
- 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/fixtures.d.ts +14 -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 +279 -0
- package/dist/selftest/web.test.d.ts +2 -0
- package/dist/selftest/web.test.js +304 -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 +6 -1
- package/dist/shared/channel/run-dir.js +12 -1
- package/dist/shared/channel/schema.d.ts +19 -0
- package/dist/shared/channel/schema.js +11 -0
- package/dist/shared/channel/secrets.d.ts +28 -0
- package/dist/shared/channel/secrets.js +44 -0
- package/dist/shared/vpn/download.d.ts +13 -0
- package/dist/shared/vpn/download.js +14 -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/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 +5 -1
- package/dist/sync/sync-state.d.ts +1 -0
- package/dist/sync/sync-state.js +1 -0
- package/dist/sync/watcher.js +10 -0
- package/dist/web/index.html +13 -0
- package/dist/web/main-CXESL347.js +45 -0
- package/dist/web/main-VLR7DVQ4.css +1 -0
- package/native/csharp/FolderSecurity.cs +29 -0
- package/native/scripts/protect-data.ps1 +29 -0
- package/native/scripts/read-folder-sddl.ps1 +10 -0
- package/native/scripts/set-folder-access.ps1 +49 -7
- 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 +15 -5
- package/sandbox/csharp/EnvBroadcast.cs +33 -0
- package/sandbox/csharp/PathSync.cs +90 -0
- package/sandbox/scripts/install-shell-path.ps1 +73 -0
- package/sandbox/scripts/set-machine-environment.ps1 +37 -4
- package/schemas/config.schema.json +79 -4
- package/schemas/sandbox-config.schema.json +14 -1
- package/dist/cli/commands/repair/checks.d.ts +0 -2
- package/dist/cli/commands/repair/checks.js +0 -246
- 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,198 @@
|
|
|
1
|
+
// Agents that keep SQLite databases (codex ...): SQLite cannot map its shared memory in a mapped folder (error 8714,
|
|
2
|
+
// "disk I/O error"), so they run from a copy on the sandbox's own disk. The copy comes from persistent storage at boot
|
|
3
|
+
// and goes back when no agent process holds the files, so the data lives on between sandboxes.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { SYSTEM } from '../../shared/system.js';
|
|
7
|
+
import { dryRun, isWin, localDir } from '../runtime/paths.js';
|
|
8
|
+
import { exists, mkdirp } from '../runtime/fs.js';
|
|
9
|
+
import { execOutput } from '../runtime/process.js';
|
|
10
|
+
import { log, warn } from '../runtime/log.js';
|
|
11
|
+
import { session, stateDir } from '../runtime/session.js';
|
|
12
|
+
import { errMsg } from '../../core/util/errors.js';
|
|
13
|
+
import { isCopyableDatabaseFile, isDatabaseFile, localStateEntries } from './agent-files.js';
|
|
14
|
+
const SQLITE_HEADER = 'SQLite format 3\0';
|
|
15
|
+
/** The sandbox-disk folder that holds the working copies. */
|
|
16
|
+
const localRoot = () => path.join(localDir, 'agent-state');
|
|
17
|
+
/** Every SQLite folder of every configured agent (none in launch folders that predate the agents list). */
|
|
18
|
+
export function agentLocalStates() {
|
|
19
|
+
return localStateEntries(session.agents ?? {}, stateDir, localRoot());
|
|
20
|
+
}
|
|
21
|
+
/** Variable -> working folder, for the agent environment (and the machine environment of new terminal tabs). */
|
|
22
|
+
export function localStateVars() {
|
|
23
|
+
return Object.fromEntries(agentLocalStates().map((s) => [s.env, s.local]));
|
|
24
|
+
}
|
|
25
|
+
const databaseFiles = (dir, keep) => exists(dir)
|
|
26
|
+
? fs.readdirSync(dir, { withFileTypes: true }).flatMap((e) => (e.isFile() && keep(e.name) ? [e.name] : []))
|
|
27
|
+
: [];
|
|
28
|
+
/** A database file that starts like SQLite (an empty or cut-off file must never replace a saved copy). */
|
|
29
|
+
const looksLikeSqlite = (file) => {
|
|
30
|
+
const fd = fs.openSync(file, 'r');
|
|
31
|
+
try {
|
|
32
|
+
const head = Buffer.alloc(16);
|
|
33
|
+
return fs.readSync(fd, head, 0, 16, 0) === 16 && head.toString('latin1') === SQLITE_HEADER;
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
fs.closeSync(fd);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const copyFiles = (from, to, names) => {
|
|
40
|
+
fs.rmSync(to, { recursive: true, force: true });
|
|
41
|
+
mkdirp(to);
|
|
42
|
+
for (const name of names)
|
|
43
|
+
fs.copyFileSync(path.join(from, name), path.join(to, name));
|
|
44
|
+
};
|
|
45
|
+
/** Replaces the folder `store` with the staged `incoming` folder; the old one is only removed once the new is in. */
|
|
46
|
+
const swapIn = (incoming, store) => {
|
|
47
|
+
const previous = store + '.previous';
|
|
48
|
+
fs.rmSync(previous, { recursive: true, force: true });
|
|
49
|
+
if (exists(store))
|
|
50
|
+
fs.renameSync(store, previous);
|
|
51
|
+
try {
|
|
52
|
+
fs.renameSync(incoming, store);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
if (exists(previous))
|
|
56
|
+
fs.renameSync(previous, store);
|
|
57
|
+
throw e;
|
|
58
|
+
}
|
|
59
|
+
fs.rmSync(previous, { recursive: true, force: true });
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Databases an agent kept in its state folder before (codex: state_5.sqlite ...) move into the saved copy. The
|
|
63
|
+
* originals
|
|
64
|
+
* go only after the copy is in place and has the same sizes.
|
|
65
|
+
*/
|
|
66
|
+
function adoptLegacy(s) {
|
|
67
|
+
if (exists(s.store))
|
|
68
|
+
return;
|
|
69
|
+
const names = databaseFiles(s.legacy, isDatabaseFile);
|
|
70
|
+
if (!names.length)
|
|
71
|
+
return;
|
|
72
|
+
const copyable = names.filter(isCopyableDatabaseFile);
|
|
73
|
+
const incoming = s.store + '.incoming';
|
|
74
|
+
copyFiles(s.legacy, incoming, copyable);
|
|
75
|
+
swapIn(incoming, s.store);
|
|
76
|
+
const same = copyable.every((n) => fs.statSync(path.join(s.store, n)).size === fs.statSync(path.join(s.legacy, n)).size);
|
|
77
|
+
if (!same) {
|
|
78
|
+
warn(s.agent + ': the copied databases differ in size from the originals; the originals were kept.');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
for (const n of names)
|
|
82
|
+
fs.rmSync(path.join(s.legacy, n), { force: true });
|
|
83
|
+
log(s.agent + ': moved ' + copyable.length + ' database file(s) to ' + s.store + '.');
|
|
84
|
+
}
|
|
85
|
+
/** Boot: the saved databases of every configured agent are copied to the sandbox's disk, where the agent runs them. */
|
|
86
|
+
export function restoreLocalState() {
|
|
87
|
+
if (dryRun)
|
|
88
|
+
return;
|
|
89
|
+
for (const s of agentLocalStates()) {
|
|
90
|
+
try {
|
|
91
|
+
adoptLegacy(s);
|
|
92
|
+
mkdirp(s.local);
|
|
93
|
+
const names = databaseFiles(s.store, isCopyableDatabaseFile);
|
|
94
|
+
for (const n of names)
|
|
95
|
+
fs.copyFileSync(path.join(s.store, n), path.join(s.local, n));
|
|
96
|
+
if (names.length)
|
|
97
|
+
log(s.agent + ': databases restored to ' + s.local + '.');
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
warn(s.agent + ': could not restore its databases (' + errMsg(e) + '); it starts with empty ones.');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** True while one of the programs that open the databases runs. */
|
|
105
|
+
function held(s) {
|
|
106
|
+
if (!isWin)
|
|
107
|
+
return false;
|
|
108
|
+
return s.processes.some((image) => execOutput(SYSTEM.tasklist(), ['/FI', 'IMAGENAME eq ' + image, '/NH', '/FO', 'CSV'])
|
|
109
|
+
.toLowerCase()
|
|
110
|
+
.includes(image.toLowerCase()));
|
|
111
|
+
}
|
|
112
|
+
const fingerprint = (dir) => databaseFiles(dir, isCopyableDatabaseFile)
|
|
113
|
+
.sort()
|
|
114
|
+
.map((n) => {
|
|
115
|
+
const st = fs.statSync(path.join(dir, n));
|
|
116
|
+
return n + ':' + st.size + ':' + st.mtimeMs;
|
|
117
|
+
})
|
|
118
|
+
.join('|');
|
|
119
|
+
/** The working copies as of the last save by this process. */
|
|
120
|
+
const saved = new Map();
|
|
121
|
+
/** Snapshots in progress (one per working folder) and whether the missing online backup was said already. */
|
|
122
|
+
const snapshotting = new Set();
|
|
123
|
+
let saidNoBackup = false;
|
|
124
|
+
/**
|
|
125
|
+
* While the agent runs its databases are never copied as files (the -wal and the database would not match): SQLite's
|
|
126
|
+
* online backup writes a consistent snapshot into the staging folder, which is then swapped in. Needs node:sqlite
|
|
127
|
+
* (newer Node); without it the save waits until the agent has exited.
|
|
128
|
+
*/
|
|
129
|
+
function snapshotWhileRunning(s, print) {
|
|
130
|
+
if (snapshotting.has(s.local))
|
|
131
|
+
return;
|
|
132
|
+
snapshotting.add(s.local);
|
|
133
|
+
void (async () => {
|
|
134
|
+
try {
|
|
135
|
+
const sqlite = await import('node:sqlite').catch(() => null);
|
|
136
|
+
if (!sqlite || typeof sqlite.backup !== 'function') {
|
|
137
|
+
if (!saidNoBackup)
|
|
138
|
+
log(s.agent + ': this Node has no SQLite online backup; its databases are saved when it exits.');
|
|
139
|
+
saidNoBackup = true;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const incoming = s.store + '.incoming';
|
|
143
|
+
fs.rmSync(incoming, { recursive: true, force: true });
|
|
144
|
+
mkdirp(incoming);
|
|
145
|
+
for (const name of databaseFiles(s.local, (n) => isCopyableDatabaseFile(n) && !/-wal$/i.test(n))) {
|
|
146
|
+
const db = new sqlite.DatabaseSync(path.join(s.local, name), { readOnly: true });
|
|
147
|
+
try {
|
|
148
|
+
await sqlite.backup(db, path.join(incoming, name));
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
db.close();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
swapIn(incoming, s.store);
|
|
155
|
+
saved.set(s.local, print);
|
|
156
|
+
}
|
|
157
|
+
catch (e) {
|
|
158
|
+
warn(s.agent + ': could not snapshot its running databases: ' + errMsg(e));
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
snapshotting.delete(s.local);
|
|
162
|
+
}
|
|
163
|
+
})();
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Copies the working databases back to persistent storage (staging folder + swap). While the agent runs they are
|
|
167
|
+
* snapshotted with SQLite's online backup instead (see snapshotWhileRunning); nothing is done when nothing changed.
|
|
168
|
+
*/
|
|
169
|
+
export function saveLocalState() {
|
|
170
|
+
if (dryRun)
|
|
171
|
+
return;
|
|
172
|
+
for (const s of agentLocalStates()) {
|
|
173
|
+
try {
|
|
174
|
+
if (!exists(s.local))
|
|
175
|
+
continue;
|
|
176
|
+
const print = fingerprint(s.local);
|
|
177
|
+
if (!print || (saved.get(s.local) === print && exists(s.store)))
|
|
178
|
+
continue;
|
|
179
|
+
if (held(s)) {
|
|
180
|
+
snapshotWhileRunning(s, print);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const names = databaseFiles(s.local, isCopyableDatabaseFile);
|
|
184
|
+
const bad = names.filter((n) => !/-wal$/i.test(n) && !looksLikeSqlite(path.join(s.local, n)));
|
|
185
|
+
if (bad.length) {
|
|
186
|
+
warn(s.agent + ': ' + bad.join(', ') + ' do not look like SQLite databases, so they were not saved.');
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const incoming = s.store + '.incoming';
|
|
190
|
+
copyFiles(s.local, incoming, names);
|
|
191
|
+
swapIn(incoming, s.store);
|
|
192
|
+
saved.set(s.local, print);
|
|
193
|
+
}
|
|
194
|
+
catch (e) {
|
|
195
|
+
warn(s.agent + ': could not save its databases: ' + errMsg(e));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// `sbx-agent`: install a configured agent from a terminal inside the sandbox (the agent commands call it the first
|
|
2
|
+
// time). Usage: sbx-agent list | sbx-agent install <agent>
|
|
3
|
+
import { agents } from './agent-installer.js';
|
|
4
|
+
import { agentCommand, agentPaths } from './agent-files.js';
|
|
5
|
+
import { toolsDir } from '../runtime/paths.js';
|
|
6
|
+
import { exists } from '../runtime/fs.js';
|
|
7
|
+
import { session } from '../runtime/session.js';
|
|
8
|
+
/** Runs one sbx-agent command; returns the exit code. Prints, as the command line tool it is. */
|
|
9
|
+
export async function runSbxAgent(argv) {
|
|
10
|
+
const known = session.agents ?? {};
|
|
11
|
+
const [verb, name] = argv;
|
|
12
|
+
if (verb === 'list' || verb === undefined) {
|
|
13
|
+
for (const [agent, harness] of Object.entries(known)) {
|
|
14
|
+
const p = agentPaths(toolsDir, agent, harness);
|
|
15
|
+
const installed = (p.exe !== null && exists(p.exe)) || (p.cmd !== null && exists(p.cmd));
|
|
16
|
+
console.log(agentCommand(agent, harness).padEnd(14) + (installed ? 'installed' : 'not installed yet'));
|
|
17
|
+
}
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
const harness = verb === 'install' && name ? known[name] : undefined;
|
|
21
|
+
if (!harness) {
|
|
22
|
+
console.error(verb === 'install' && name
|
|
23
|
+
? 'Unknown agent "' + name + '". Configured agents: ' + Object.keys(known).join(', ')
|
|
24
|
+
: 'Usage: sbx-agent list | sbx-agent install <agent>');
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
await agents.provision(name, harness, process.env, false);
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
console.error('Could not install ' + name + ': ' + (e instanceof Error ? e.message : String(e)));
|
|
32
|
+
return 1;
|
|
33
|
+
}
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
@@ -3,11 +3,15 @@ export declare class Boot {
|
|
|
3
3
|
private ptyHost;
|
|
4
4
|
private lan;
|
|
5
5
|
private readonly gitAccess;
|
|
6
|
+
/** What the host handed over in its one-shot file (read and deleted at the start of run()). */
|
|
7
|
+
private secrets;
|
|
6
8
|
run(): Promise<void>;
|
|
7
9
|
private networkAndSessionHost;
|
|
8
10
|
private hooksAndDesktop;
|
|
9
11
|
/** Toolchains, databases, apps, the network proxy and VS Code; then the agent environment, saved machine-wide. */
|
|
10
12
|
private developerTools;
|
|
13
|
+
/** vpn.enabled: the tunnel and its kill switch, before anything else uses the network (git, the agent). */
|
|
14
|
+
private vpn;
|
|
11
15
|
private gitIdentity;
|
|
12
16
|
private claude;
|
|
13
17
|
private agent;
|
package/dist/guest/boot/boot.js
CHANGED
|
@@ -5,6 +5,9 @@ import { spawn } from 'node:child_process';
|
|
|
5
5
|
import { writeGuides } from './guide.js';
|
|
6
6
|
import { setupToolchains } from '../toolchains/index.js';
|
|
7
7
|
import { agents } from '../agents/agent-installer.js';
|
|
8
|
+
import { agentShims, sbxAgentText, shimText } from '../agents/agent-files.js';
|
|
9
|
+
import { restoreLocalState } from '../agents/local-state.js';
|
|
10
|
+
import { installShellPath } from '../setup/shell-path.js';
|
|
8
11
|
import { linkProfileDirs } from '../agents/profile-links.js';
|
|
9
12
|
import { restoreClaudeMem } from '../setup/claude-mem.js';
|
|
10
13
|
import { syncClaudeProfile } from '../setup/claude-profile.js';
|
|
@@ -20,6 +23,7 @@ import { pruneToolLeftovers } from '../toolchains/leftovers.js';
|
|
|
20
23
|
import { wanted } from '../toolchains/detect.js';
|
|
21
24
|
import { configureVscode } from '../toolchains/tools/vscode.js';
|
|
22
25
|
import { enableProcessAudit } from '../audit/process-audit.js';
|
|
26
|
+
import { setupVpn } from '../vpn/index.js';
|
|
23
27
|
import { now } from '../../shared/channel/run-dir.js';
|
|
24
28
|
import { writeJsonAtomic } from '../../shared/io/json.js';
|
|
25
29
|
import { viewSession } from '../sessions/viewer.js';
|
|
@@ -29,9 +33,10 @@ import { dryRun, isWin, localDir, logsDir, run, toolsDir } from '../runtime/path
|
|
|
29
33
|
import { log, logPath, warn } from '../runtime/log.js';
|
|
30
34
|
import { mkdirp } from '../runtime/fs.js';
|
|
31
35
|
import { session, workspace } from '../runtime/session.js';
|
|
36
|
+
import { applyBootEnvironment, BOOT_SECRETS_VARIABLE, takeSecrets } from '../runtime/secrets.js';
|
|
32
37
|
import { setSandboxStatus } from '../runtime/status.js';
|
|
33
38
|
import { errMsg } from '../../core/util/errors.js';
|
|
34
|
-
const STEPS =
|
|
39
|
+
const STEPS = 9;
|
|
35
40
|
const PRIMARY_POLLS = 600;
|
|
36
41
|
const POLL_MS = 500;
|
|
37
42
|
const VSCODE_LANGUAGES = ['python', 'java', 'go', 'rust', 'cpp', 'msvc', 'dotnet', 'ruby', 'php', 'deno', 'pwsh'];
|
|
@@ -56,12 +61,19 @@ async function soft(what, work) {
|
|
|
56
61
|
warn(what + ' failed (continuing): ' + errMsg(e));
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
|
-
function startDaemon(env) {
|
|
64
|
+
function startDaemon(env, secrets) {
|
|
60
65
|
spawn(process.execPath, [path.join(path.dirname(process.argv[1]), 'daemon.js')], {
|
|
61
66
|
detached: true,
|
|
62
67
|
stdio: 'ignore',
|
|
63
68
|
windowsHide: true,
|
|
64
|
-
env: {
|
|
69
|
+
env: {
|
|
70
|
+
...env,
|
|
71
|
+
AGENT_LOG_PREFIX: 'daemon',
|
|
72
|
+
// The agent's environment (it may hold API keys): the daemon moves it into memory and removes this at once.
|
|
73
|
+
...(secrets.harnessEnvironment
|
|
74
|
+
? { [BOOT_SECRETS_VARIABLE]: JSON.stringify({ harnessEnvironment: secrets.harnessEnvironment }) }
|
|
75
|
+
: {}),
|
|
76
|
+
},
|
|
65
77
|
}).unref();
|
|
66
78
|
}
|
|
67
79
|
/**
|
|
@@ -72,8 +84,19 @@ function writeDbCommand() {
|
|
|
72
84
|
const dir = mkdirp(path.join(localDir, 'bin'));
|
|
73
85
|
const db = path.join(path.dirname(process.argv[1]), 'db.js');
|
|
74
86
|
fs.writeFileSync(path.join(dir, 'sbx-db.cmd'), '@setlocal\r\n@set AGENT_LOG_PREFIX=db\r\n@"' + process.execPath + '" "' + db + '" %*\r\n');
|
|
87
|
+
writeAgentCommands(dir);
|
|
75
88
|
return dir;
|
|
76
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* `claude`, `codex` ... by name in every terminal tab, installed yet or not (the command installs the agent on first
|
|
92
|
+
* use), and `sbx-agent`. Written to the sandbox's disk each boot, next to `sbx-db`.
|
|
93
|
+
*/
|
|
94
|
+
function writeAgentCommands(dir) {
|
|
95
|
+
const install = { node: process.execPath, script: path.join(path.dirname(process.argv[1]), 'agent.js') };
|
|
96
|
+
for (const shim of agentShims(toolsDir, session.agents ?? {}))
|
|
97
|
+
fs.writeFileSync(path.join(dir, shim.command + '.cmd'), shimText(shim, install));
|
|
98
|
+
fs.writeFileSync(path.join(dir, 'sbx-agent.cmd'), sbxAgentText(install));
|
|
99
|
+
}
|
|
77
100
|
async function waitForPrimary() {
|
|
78
101
|
for (let i = 0; i < PRIMARY_POLLS; i++) {
|
|
79
102
|
const s = run.readSession(session.launchId);
|
|
@@ -97,6 +120,8 @@ export class Boot {
|
|
|
97
120
|
ptyHost = null;
|
|
98
121
|
lan = session.networking ? 'open' : 'offline';
|
|
99
122
|
gitAccess = session.provisioning.git.access ?? 'full';
|
|
123
|
+
/** What the host handed over in its one-shot file (read and deleted at the start of run()). */
|
|
124
|
+
secrets = {};
|
|
100
125
|
async run() {
|
|
101
126
|
log('Agent sandbox: ' +
|
|
102
127
|
session.project +
|
|
@@ -110,9 +135,12 @@ export class Boot {
|
|
|
110
135
|
throw new Error('Workspace folder is missing in the sandbox: ' + workspace);
|
|
111
136
|
mkdirp(toolsDir);
|
|
112
137
|
mkdirp(run.root);
|
|
138
|
+
this.secrets = takeSecrets(session.launchId);
|
|
139
|
+
applyBootEnvironment(this.secrets.harnessEnvironment);
|
|
113
140
|
await this.networkAndSessionHost();
|
|
114
141
|
await this.hooksAndDesktop();
|
|
115
142
|
const { tools, env } = await this.developerTools();
|
|
143
|
+
await this.vpn();
|
|
116
144
|
await this.gitIdentity(env);
|
|
117
145
|
await this.claude(tools);
|
|
118
146
|
await this.agent(env);
|
|
@@ -165,6 +193,7 @@ export class Boot {
|
|
|
165
193
|
}));
|
|
166
194
|
const env = agentEnv(tools);
|
|
167
195
|
persistEnvironment(env, Object.keys(tools.vars));
|
|
196
|
+
await soft('Shell start-up PATH', installShellPath);
|
|
168
197
|
saveBootEnv({
|
|
169
198
|
basePath,
|
|
170
199
|
toolVars: Object.keys(tools.vars),
|
|
@@ -178,36 +207,46 @@ export class Boot {
|
|
|
178
207
|
await soft('Cleaning old tool builds', () => pruneToolLeftovers());
|
|
179
208
|
return { tools, env };
|
|
180
209
|
}
|
|
210
|
+
/** vpn.enabled: the tunnel and its kill switch, before anything else uses the network (git, the agent). */
|
|
211
|
+
async vpn() {
|
|
212
|
+
step(4, session.vpn ? 'VPN (' + session.vpn.server + ')' : 'VPN (off)');
|
|
213
|
+
if (!session.vpn)
|
|
214
|
+
return;
|
|
215
|
+
await soft('VPN', async () => {
|
|
216
|
+
await setupVpn(this.secrets, this.ptyHost);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
181
219
|
async gitIdentity(env) {
|
|
182
|
-
step(
|
|
220
|
+
step(5, 'Git identity');
|
|
183
221
|
await soft('Git config', () => setupGitConfig(env));
|
|
184
222
|
}
|
|
185
223
|
async claude(tools) {
|
|
186
|
-
step(
|
|
224
|
+
step(6, 'Claude settings, history and claude-mem');
|
|
187
225
|
if (session.harnessName === 'claude')
|
|
188
|
-
await soft('Claude settings', syncClaudeProfile);
|
|
226
|
+
await soft('Claude settings', () => syncClaudeProfile(this.secrets.claudeCredentials));
|
|
189
227
|
await soft('claude-mem restore', restoreClaudeMem);
|
|
228
|
+
await soft('Agent databases restore', restoreLocalState);
|
|
190
229
|
await soft('Sandbox guide for the agent', () => writeGuides(tools.summary, this.lan));
|
|
191
230
|
await soft('Claude managed settings', () => writeManagedSettings(this.gitAccess));
|
|
192
231
|
}
|
|
193
232
|
async agent(env) {
|
|
194
|
-
step(
|
|
233
|
+
step(7, 'Agent: ' + session.harnessName);
|
|
195
234
|
linkProfileDirs(session.agentProfileDirs ?? {});
|
|
196
235
|
if (session.harness)
|
|
197
236
|
await agents.provision(session.harnessName, session.harness, env);
|
|
198
237
|
}
|
|
199
238
|
preAgentHooks(env) {
|
|
200
|
-
step(
|
|
239
|
+
step(8, 'Pre-agent hooks');
|
|
201
240
|
runHooks('pre-agent', env);
|
|
202
241
|
}
|
|
203
242
|
/** Ready: starts the session daemon and, with an agent, its first session, shown in this window. */
|
|
204
243
|
async start(env) {
|
|
205
|
-
step(
|
|
244
|
+
step(9, 'Starting ' + session.harnessName);
|
|
206
245
|
setSandboxStatus('ready', 'Ready');
|
|
207
246
|
// No agent, or a restart that adds a project (the host opens its sessions itself once the sandbox is ready).
|
|
208
247
|
if (!session.harness || session.autoSession === false) {
|
|
209
248
|
if (!dryRun)
|
|
210
|
-
startDaemon(env);
|
|
249
|
+
startDaemon(env, this.secrets);
|
|
211
250
|
log('Ready' + (session.harness ? '' : ' (no agent requested)') + '. Workspace: ' + workspace);
|
|
212
251
|
return;
|
|
213
252
|
}
|
|
@@ -220,7 +259,7 @@ export class Boot {
|
|
|
220
259
|
runHooks('post-agent', env);
|
|
221
260
|
return;
|
|
222
261
|
}
|
|
223
|
-
startDaemon(env);
|
|
262
|
+
startDaemon(env, this.secrets);
|
|
224
263
|
writeJsonAtomic(run.requestFile(session.launchId), {
|
|
225
264
|
kind: 'session',
|
|
226
265
|
id: session.launchId,
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// The session daemon's polling loop: claims each valid request in run\requests and hands it to its handler.
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import { saveClaudeMem } from '../setup/claude-mem.js';
|
|
4
|
+
import { saveLocalState } from '../agents/local-state.js';
|
|
4
5
|
import { execCommand } from './exec.js';
|
|
5
6
|
import { sessions } from './sessions.js';
|
|
6
7
|
import { respond, safely } from './state.js';
|
|
7
8
|
import { run } from '../runtime/paths.js';
|
|
9
|
+
import { withRequestSecrets } from '../runtime/secrets.js';
|
|
8
10
|
import { warn } from '../runtime/log.js';
|
|
9
11
|
import { errMsg } from '../../core/util/errors.js';
|
|
10
12
|
const POLL_MS = 400;
|
|
@@ -33,11 +35,12 @@ export class Daemon {
|
|
|
33
35
|
if (run.readSession(req.id))
|
|
34
36
|
continue;
|
|
35
37
|
// Not awaited: starting a session or running a command must not hold up other requests.
|
|
36
|
-
void Promise.resolve(this.handle(req)).catch((e) => respond(req.id, { ok: false, message: errMsg(e) }));
|
|
38
|
+
void Promise.resolve(this.handle(withRequestSecrets(req))).catch((e) => respond(req.id, { ok: false, message: errMsg(e) }));
|
|
37
39
|
}
|
|
38
40
|
if (Date.now() - this.lastMemSave > MEM_SAVE_MS) {
|
|
39
41
|
this.lastMemSave = Date.now();
|
|
40
42
|
safely('claude-mem save', saveClaudeMem);
|
|
43
|
+
safely('agent databases save', saveLocalState);
|
|
41
44
|
}
|
|
42
45
|
}
|
|
43
46
|
handle(req) {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import { SYSTEM } from '../../shared/system.js';
|
|
4
4
|
import { exists } from '../runtime/fs.js';
|
|
5
|
+
import { withRegistryPath } from '../setup/environment.js';
|
|
5
6
|
import { errMsg } from '../../core/util/errors.js';
|
|
6
7
|
import { provisioner } from './project-provisioner.js';
|
|
7
8
|
import { insideWorkspace, respond } from './state.js';
|
|
@@ -20,7 +21,7 @@ export function execCommand(req) {
|
|
|
20
21
|
}
|
|
21
22
|
// chcp 65001 first: console programs then write UTF-8 (not the OEM code page) into the pipes.
|
|
22
23
|
// In a project registered after boot, its own environment (its Node, Python, database variables).
|
|
23
|
-
const env = provisioner.provisionedFor(req.cwd)?.env ?? process.env;
|
|
24
|
+
const env = withRegistryPath(provisioner.provisionedFor(req.cwd)?.env ?? process.env);
|
|
24
25
|
const child = spawn(SYSTEM.cmd(), ['/d', '/s', '/c', '"chcp 65001>nul & ' + req.command.map(quoteForCmd).join(' ') + '"'], { cwd: req.cwd, env, windowsHide: true, windowsVerbatimArguments: true });
|
|
25
26
|
const output = { stdout: '', stderr: '' };
|
|
26
27
|
// setEncoding decodes across chunks: a multi-byte character split between two reads is not mangled.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
3
|
import { SYSTEM } from '../../shared/system.js';
|
|
4
4
|
import { saveClaudeMem } from '../setup/claude-mem.js';
|
|
5
|
+
import { saveLocalState } from '../agents/local-state.js';
|
|
5
6
|
import { isWin, run } from '../runtime/paths.js';
|
|
6
7
|
import { log } from '../runtime/log.js';
|
|
7
8
|
import { session } from '../runtime/session.js';
|
|
@@ -25,6 +26,7 @@ export class IdleShutdown {
|
|
|
25
26
|
return;
|
|
26
27
|
log('Headless sandbox has no sessions left; shutting down.');
|
|
27
28
|
safely('claude-mem save', saveClaudeMem);
|
|
29
|
+
safely('agent databases save', saveLocalState);
|
|
28
30
|
spawnSync(SYSTEM.shutdown(), ['/s', '/t', '5'], { windowsHide: true });
|
|
29
31
|
}, GRACE_MS);
|
|
30
32
|
}
|
|
@@ -14,8 +14,10 @@ import { primaryProject } from '../boot/project-status.js';
|
|
|
14
14
|
import { ensurePtyHost, ptyHostLogTail, startPtyHost, waitForPtyHost } from '../sessions/pty-host.js';
|
|
15
15
|
import { agents } from '../agents/agent-installer.js';
|
|
16
16
|
import { saveClaudeMem } from '../setup/claude-mem.js';
|
|
17
|
+
import { saveLocalState } from '../agents/local-state.js';
|
|
17
18
|
import { projectHooks, runHooks, runHooksAsync } from '../setup/hooks.js';
|
|
18
19
|
import { guestAddresses } from '../network/addresses.js';
|
|
20
|
+
import { withRegistryPath } from '../setup/environment.js';
|
|
19
21
|
import { dataDir, toolsDir } from '../runtime/paths.js';
|
|
20
22
|
import { exists } from '../runtime/fs.js';
|
|
21
23
|
import { log, warn } from '../runtime/log.js';
|
|
@@ -64,7 +66,7 @@ const launchPtyHost = async (req, remote, env, say) => {
|
|
|
64
66
|
rows: 32,
|
|
65
67
|
id: req.id,
|
|
66
68
|
cwd: req.cwd,
|
|
67
|
-
env: { ...env, ...harness.environment },
|
|
69
|
+
env: { ...withRegistryPath(env), ...harness.environment },
|
|
68
70
|
remote,
|
|
69
71
|
command: commandLine(launch.file, [...base, ...req.args]),
|
|
70
72
|
restartCommand: commandLine(launch.file, [...base, ...(harness.resumeArguments ?? [])]),
|
|
@@ -257,6 +259,7 @@ export class SessionManager {
|
|
|
257
259
|
else
|
|
258
260
|
safely('post-agent hooks', () => runHooks('post-agent', process.env));
|
|
259
261
|
safely('claude-mem save', saveClaudeMem);
|
|
262
|
+
safely('agent databases save', saveLocalState);
|
|
260
263
|
this.idle.check();
|
|
261
264
|
}
|
|
262
265
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// `sbx-agent`: list agents and install a configured one inside the sandbox. Usage: sbx-agent list | install <agent>
|
|
2
|
+
import { dropInheritedModulePath } from '../../shared/system.js';
|
|
3
|
+
import { runSbxAgent } from '../agents/sbx-agent.js';
|
|
4
|
+
dropInheritedModulePath();
|
|
5
|
+
runSbxAgent(process.argv.slice(2)).then((code) => {
|
|
6
|
+
process.exitCode = code;
|
|
7
|
+
}, (e) => {
|
|
8
|
+
console.error(e instanceof Error ? e.message : String(e));
|
|
9
|
+
process.exitCode = 1;
|
|
10
|
+
});
|
|
@@ -6,10 +6,12 @@ import { startRelays } from '../daemon/relays.js';
|
|
|
6
6
|
import { startProcessCollector } from '../audit/process-audit.js';
|
|
7
7
|
import { safely } from '../daemon/state.js';
|
|
8
8
|
import { log, warn } from '../runtime/log.js';
|
|
9
|
+
import { adoptBootSecrets } from '../runtime/secrets.js';
|
|
9
10
|
import { errMsg } from '../../core/util/errors.js';
|
|
10
11
|
import { Daemon } from '../daemon/daemon.js';
|
|
11
12
|
process.on('uncaughtException', (e) => warn('daemon: unexpected error (continuing): ' + (e.stack ?? errMsg(e))));
|
|
12
13
|
process.on('unhandledRejection', (e) => warn('daemon: unexpected error (continuing): ' + errMsg(e)));
|
|
14
|
+
adoptBootSecrets();
|
|
13
15
|
log('Session daemon started (pid ' + process.pid + ').');
|
|
14
16
|
safely('port forwards', startRelays);
|
|
15
17
|
safely('audit', startProcessCollector);
|
|
@@ -6,6 +6,7 @@ declare const SOURCES: {
|
|
|
6
6
|
readonly openssl: "OpenSsl.cs";
|
|
7
7
|
readonly cargoshim: "CargoShim.cs";
|
|
8
8
|
readonly linkgate: "LinkGate.cs";
|
|
9
|
+
readonly pathsync: "PathSync.cs";
|
|
9
10
|
};
|
|
10
11
|
export type Helper = keyof typeof SOURCES;
|
|
11
12
|
/** %TEMP%\agent-sandbox\<name>-<hash>.exe, compiled from this launch's source the first time it is needed. */
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { GuestRequest } from '../../core/types.js';
|
|
2
|
+
import { type SecretsFile } from '../../shared/channel/secrets.js';
|
|
3
|
+
/** Where the boot's secrets travel from main.js to the daemon. */
|
|
4
|
+
export declare const BOOT_SECRETS_VARIABLE = "SBX_BOOT_SECRETS";
|
|
5
|
+
/** Reads and deletes <data>\secrets\<id>.json; nothing (or an unreadable file) gives {}. */
|
|
6
|
+
export declare function takeSecrets(id: string): SecretsFile;
|
|
7
|
+
/** The agent the sandbox booted with gets its environment (in memory only). */
|
|
8
|
+
export declare function applyBootEnvironment(environment: Record<string, string> | undefined): void;
|
|
9
|
+
/** The daemon takes what main.js passed it, and removes it from its own environment before any session starts. */
|
|
10
|
+
export declare function adoptBootSecrets(): void;
|
|
11
|
+
/** A request with the agent environment values that travelled in its own secrets file put back (in memory only). */
|
|
12
|
+
export declare function withRequestSecrets(req: GuestRequest): GuestRequest;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// The one-shot secrets the host handed over (shared/channel/secrets.ts): read once, the file deleted at once. What the
|
|
2
|
+
// boot read for the agent is passed to the daemon in its own environment (SBX_BOOT_SECRETS, removed there at start), so
|
|
3
|
+
// nothing waits on disk.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import { mergeRequestSecrets, SecretsFileSchema } from '../../shared/channel/secrets.js';
|
|
6
|
+
import { run } from './paths.js';
|
|
7
|
+
import { warn } from './log.js';
|
|
8
|
+
import { session } from './session.js';
|
|
9
|
+
/** Where the boot's secrets travel from main.js to the daemon. */
|
|
10
|
+
export const BOOT_SECRETS_VARIABLE = 'SBX_BOOT_SECRETS';
|
|
11
|
+
const MAX_BYTES = 4 * 1024 * 1024;
|
|
12
|
+
/** Reads and deletes <data>\secrets\<id>.json; nothing (or an unreadable file) gives {}. */
|
|
13
|
+
export function takeSecrets(id) {
|
|
14
|
+
const file = run.secretFile(id);
|
|
15
|
+
let text;
|
|
16
|
+
try {
|
|
17
|
+
if (fs.statSync(file).size > MAX_BYTES)
|
|
18
|
+
throw new Error('too big');
|
|
19
|
+
text = fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, '');
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
fs.rmSync(file, { force: true });
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const parsed = SecretsFileSchema.safeParse(JSON.parse(text));
|
|
29
|
+
if (parsed.success)
|
|
30
|
+
return parsed.data;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Not JSON: treated like an invalid file below.
|
|
34
|
+
}
|
|
35
|
+
warn('The secrets file for ' + id + ' was not valid and was ignored.');
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
/** The agent the sandbox booted with gets its environment (in memory only). */
|
|
39
|
+
export function applyBootEnvironment(environment) {
|
|
40
|
+
if (session.harness && environment)
|
|
41
|
+
session.harness.environment = { ...environment };
|
|
42
|
+
}
|
|
43
|
+
/** The daemon takes what main.js passed it, and removes it from its own environment before any session starts. */
|
|
44
|
+
export function adoptBootSecrets() {
|
|
45
|
+
const text = process.env[BOOT_SECRETS_VARIABLE];
|
|
46
|
+
delete process.env[BOOT_SECRETS_VARIABLE];
|
|
47
|
+
if (!text)
|
|
48
|
+
return;
|
|
49
|
+
try {
|
|
50
|
+
const parsed = SecretsFileSchema.pick({ harnessEnvironment: true }).safeParse(JSON.parse(text));
|
|
51
|
+
if (parsed.success)
|
|
52
|
+
applyBootEnvironment(parsed.data.harnessEnvironment);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
warn('The boot secrets given to the daemon were not valid.');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** A request with the agent environment values that travelled in its own secrets file put back (in memory only). */
|
|
59
|
+
export function withRequestSecrets(req) {
|
|
60
|
+
if (req.kind !== 'session' && req.kind !== 'project')
|
|
61
|
+
return req;
|
|
62
|
+
return mergeRequestSecrets(req, takeSecrets(req.id));
|
|
63
|
+
}
|
|
@@ -4,4 +4,5 @@
|
|
|
4
4
|
* Servers with the same name are replaced by this PC's definition; servers added inside the sandbox are kept.
|
|
5
5
|
*/
|
|
6
6
|
export declare function mergeMcpServers(hostFile: string, guestFile: string, hostWorkspace: string, guestWorkspace: string): number;
|
|
7
|
-
|
|
7
|
+
/** `credentials`: the text of this PC's .credentials.json, handed over in the one-shot secrets file. */
|
|
8
|
+
export declare function syncClaudeProfile(credentials?: string): void;
|