windows-development-sandbox 0.0.0-stage → 1.1.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/.sandbox-config.example.json +14 -0
- package/LICENSE +21 -0
- package/README.md +94 -2
- package/config.example.json +349 -0
- package/dist/api.d.ts +31 -0
- package/dist/api.js +31 -0
- package/dist/bin/agent-sandbox.d.ts +2 -0
- package/dist/bin/agent-sandbox.js +3 -0
- package/dist/bin/claude-host.d.ts +2 -0
- package/dist/bin/claude-host.js +5 -0
- package/dist/bin/claude-sbx.d.ts +2 -0
- package/dist/bin/claude-sbx.js +3 -0
- package/dist/bin/codex-sbx.d.ts +2 -0
- package/dist/bin/codex-sbx.js +3 -0
- package/dist/bin/gemini-sbx.d.ts +2 -0
- package/dist/bin/gemini-sbx.js +3 -0
- package/dist/bin/sandbox.d.ts +2 -0
- package/dist/bin/sandbox.js +4 -0
- package/dist/bin/sbx-hide.d.ts +2 -0
- package/dist/bin/sbx-hide.js +3 -0
- package/dist/bin/sbx-show.d.ts +2 -0
- package/dist/bin/sbx-show.js +3 -0
- package/dist/cli/args.d.ts +52 -0
- package/dist/cli/args.js +143 -0
- package/dist/cli/colors.d.ts +24 -0
- package/dist/cli/colors.js +42 -0
- package/dist/cli/commands/accounts.d.ts +2 -0
- package/dist/cli/commands/accounts.js +52 -0
- package/dist/cli/commands/agent-commands.d.ts +6 -0
- package/dist/cli/commands/agent-commands.js +61 -0
- package/dist/cli/commands/context.d.ts +23 -0
- package/dist/cli/commands/context.js +57 -0
- package/dist/cli/commands/data/clean.d.ts +15 -0
- package/dist/cli/commands/data/clean.js +101 -0
- package/dist/cli/commands/data/collect.d.ts +11 -0
- package/dist/cli/commands/data/collect.js +54 -0
- package/dist/cli/commands/data/index.d.ts +4 -0
- package/dist/cli/commands/data/index.js +45 -0
- package/dist/cli/commands/data/remove.d.ts +2 -0
- package/dist/cli/commands/data/remove.js +30 -0
- package/dist/cli/commands/index.d.ts +10 -0
- package/dist/cli/commands/index.js +10 -0
- package/dist/cli/commands/launch.d.ts +29 -0
- package/dist/cli/commands/launch.js +333 -0
- package/dist/cli/commands/maintenance.d.ts +8 -0
- package/dist/cli/commands/maintenance.js +154 -0
- package/dist/cli/commands/project.d.ts +29 -0
- package/dist/cli/commands/project.js +63 -0
- package/dist/cli/commands/projects.d.ts +14 -0
- package/dist/cli/commands/projects.js +146 -0
- package/dist/cli/commands/repair/checks.d.ts +2 -0
- package/dist/cli/commands/repair/checks.js +170 -0
- package/dist/cli/commands/repair/finders.d.ts +30 -0
- package/dist/cli/commands/repair/finders.js +108 -0
- package/dist/cli/commands/repair/index.d.ts +12 -0
- package/dist/cli/commands/repair/index.js +61 -0
- package/dist/cli/commands/repair/types.d.ts +26 -0
- package/dist/cli/commands/repair/types.js +3 -0
- package/dist/cli/commands/restart.d.ts +6 -0
- package/dist/cli/commands/restart.js +85 -0
- package/dist/cli/commands/sessions.d.ts +12 -0
- package/dist/cli/commands/sessions.js +257 -0
- package/dist/cli/commands/setup/checks.d.ts +13 -0
- package/dist/cli/commands/setup/checks.js +71 -0
- package/dist/cli/commands/setup/facts.d.ts +31 -0
- package/dist/cli/commands/setup/facts.js +60 -0
- package/dist/cli/commands/setup/index.d.ts +2 -0
- package/dist/cli/commands/setup/index.js +43 -0
- package/dist/cli/commands/user-config.d.ts +8 -0
- package/dist/cli/commands/user-config.js +41 -0
- package/dist/cli/history.d.ts +11 -0
- package/dist/cli/history.js +21 -0
- package/dist/cli/index.d.ts +20 -0
- package/dist/cli/index.js +121 -0
- package/dist/cli/manifest.d.ts +103 -0
- package/dist/cli/manifest.js +107 -0
- package/dist/cli/menu-multi.d.ts +12 -0
- package/dist/cli/menu-multi.js +82 -0
- package/dist/cli/menu.d.ts +14 -0
- package/dist/cli/menu.js +290 -0
- package/dist/cli/progress.d.ts +9 -0
- package/dist/cli/progress.js +49 -0
- package/dist/cli/settings/edit.d.ts +13 -0
- package/dist/cli/settings/edit.js +19 -0
- package/dist/cli/settings/index.d.ts +8 -0
- package/dist/cli/settings/index.js +131 -0
- package/dist/cli/settings/prompts.d.ts +7 -0
- package/dist/cli/settings/prompts.js +48 -0
- package/dist/cli/settings/schema.d.ts +33 -0
- package/dist/cli/settings/schema.js +29 -0
- package/dist/cli/validate.d.ts +17 -0
- package/dist/cli/validate.js +89 -0
- package/dist/core/config.d.ts +17 -0
- package/dist/core/config.js +106 -0
- package/dist/core/detect-databases.d.ts +15 -0
- package/dist/core/detect-databases.js +147 -0
- package/dist/core/detect-pwsh.d.ts +18 -0
- package/dist/core/detect-pwsh.js +111 -0
- package/dist/core/detect-toolchains.d.ts +30 -0
- package/dist/core/detect-toolchains.js +58 -0
- package/dist/core/disk.d.ts +16 -0
- package/dist/core/disk.js +55 -0
- package/dist/core/local-config.d.ts +20 -0
- package/dist/core/local-config.js +77 -0
- package/dist/core/project-policy.d.ts +15 -0
- package/dist/core/project-policy.js +35 -0
- package/dist/core/prompt.d.ts +4 -0
- package/dist/core/prompt.js +15 -0
- package/dist/core/safe-paths.d.ts +4 -0
- package/dist/core/safe-paths.js +37 -0
- package/dist/core/schema/common.d.ts +17 -0
- package/dist/core/schema/common.js +15 -0
- package/dist/core/schema/config.d.ts +594 -0
- package/dist/core/schema/config.js +63 -0
- package/dist/core/schema/databases.d.ts +33 -0
- package/dist/core/schema/databases.js +35 -0
- package/dist/core/schema/harness.d.ts +12 -0
- package/dist/core/schema/harness.js +17 -0
- package/dist/core/schema/json-schema.d.ts +5 -0
- package/dist/core/schema/json-schema.js +25 -0
- package/dist/core/schema/profile.d.ts +27 -0
- package/dist/core/schema/profile.js +29 -0
- package/dist/core/schema/projects.d.ts +9 -0
- package/dist/core/schema/projects.js +11 -0
- package/dist/core/schema/sandbox.d.ts +50 -0
- package/dist/core/schema/sandbox.js +36 -0
- package/dist/core/schema/toolchains.d.ts +93 -0
- package/dist/core/schema/toolchains.js +83 -0
- package/dist/core/schema.d.ts +6 -0
- package/dist/core/schema.js +8 -0
- package/dist/core/types.d.ts +190 -0
- package/dist/core/types.js +1 -0
- package/dist/core/util.d.ts +60 -0
- package/dist/core/util.js +154 -0
- package/dist/core/workspace.d.ts +18 -0
- package/dist/core/workspace.js +128 -0
- package/dist/guest/boot-env.d.ts +4 -0
- package/dist/guest/boot-env.js +25 -0
- package/dist/guest/cmdline.d.ts +12 -0
- package/dist/guest/cmdline.js +23 -0
- package/dist/guest/daemon/exec.d.ts +5 -0
- package/dist/guest/daemon/exec.js +32 -0
- package/dist/guest/daemon/project.d.ts +14 -0
- package/dist/guest/daemon/project.js +132 -0
- package/dist/guest/daemon/session.d.ts +13 -0
- package/dist/guest/daemon/session.js +139 -0
- package/dist/guest/daemon/shutdown.d.ts +1 -0
- package/dist/guest/daemon/shutdown.js +21 -0
- package/dist/guest/daemon/state.d.ts +17 -0
- package/dist/guest/daemon/state.js +31 -0
- package/dist/guest/daemon.d.ts +1 -0
- package/dist/guest/daemon.js +53 -0
- package/dist/guest/db.d.ts +1 -0
- package/dist/guest/db.js +75 -0
- package/dist/guest/env.d.ts +51 -0
- package/dist/guest/env.js +101 -0
- package/dist/guest/gitguard.d.ts +7 -0
- package/dist/guest/gitguard.js +60 -0
- package/dist/guest/gitrules.d.ts +2 -0
- package/dist/guest/gitrules.js +11 -0
- package/dist/guest/guide.d.ts +16 -0
- package/dist/guest/guide.js +69 -0
- package/dist/guest/helpers.d.ts +3 -0
- package/dist/guest/helpers.js +26 -0
- package/dist/guest/main.d.ts +1 -0
- package/dist/guest/main.js +130 -0
- package/dist/guest/ports.d.ts +6 -0
- package/dist/guest/ports.js +22 -0
- package/dist/guest/project-context.d.ts +7 -0
- package/dist/guest/project-context.js +10 -0
- package/dist/guest/project-env.d.ts +20 -0
- package/dist/guest/project-env.js +20 -0
- package/dist/guest/project-status.d.ts +5 -0
- package/dist/guest/project-status.js +17 -0
- package/dist/guest/ptyhost.d.ts +22 -0
- package/dist/guest/ptyhost.js +36 -0
- package/dist/guest/setup/agents.d.ts +11 -0
- package/dist/guest/setup/agents.js +87 -0
- package/dist/guest/setup/claude.d.ts +21 -0
- package/dist/guest/setup/claude.js +146 -0
- package/dist/guest/setup/environment.d.ts +16 -0
- package/dist/guest/setup/environment.js +69 -0
- package/dist/guest/setup/hooks.d.ts +9 -0
- package/dist/guest/setup/hooks.js +50 -0
- package/dist/guest/setup/network.d.ts +10 -0
- package/dist/guest/setup/network.js +99 -0
- package/dist/guest/setup/policy.d.ts +3 -0
- package/dist/guest/setup/policy.js +18 -0
- package/dist/guest/setup/user.d.ts +7 -0
- package/dist/guest/setup/user.js +47 -0
- package/dist/guest/toolchains/catalogue.d.ts +55 -0
- package/dist/guest/toolchains/catalogue.js +31 -0
- package/dist/guest/toolchains/cli.d.ts +10 -0
- package/dist/guest/toolchains/cli.js +72 -0
- package/dist/guest/toolchains/common.d.ts +54 -0
- package/dist/guest/toolchains/common.js +147 -0
- package/dist/guest/toolchains/databases/dynamodb.d.ts +8 -0
- package/dist/guest/toolchains/databases/dynamodb.js +52 -0
- package/dist/guest/toolchains/databases/index.d.ts +6 -0
- package/dist/guest/toolchains/databases/index.js +62 -0
- package/dist/guest/toolchains/databases/mongodb.d.ts +3 -0
- package/dist/guest/toolchains/databases/mongodb.js +31 -0
- package/dist/guest/toolchains/databases/mysql.d.ts +3 -0
- package/dist/guest/toolchains/databases/mysql.js +38 -0
- package/dist/guest/toolchains/databases/postgres.d.ts +5 -0
- package/dist/guest/toolchains/databases/postgres.js +38 -0
- package/dist/guest/toolchains/databases/redis.d.ts +3 -0
- package/dist/guest/toolchains/databases/redis.js +16 -0
- package/dist/guest/toolchains/databases/server.d.ts +14 -0
- package/dist/guest/toolchains/databases/server.js +52 -0
- package/dist/guest/toolchains/git.d.ts +3 -0
- package/dist/guest/toolchains/git.js +121 -0
- package/dist/guest/toolchains/go.d.ts +3 -0
- package/dist/guest/toolchains/go.js +33 -0
- package/dist/guest/toolchains/index.d.ts +17 -0
- package/dist/guest/toolchains/index.js +80 -0
- package/dist/guest/toolchains/java.d.ts +8 -0
- package/dist/guest/toolchains/java.js +96 -0
- package/dist/guest/toolchains/leftovers.d.ts +2 -0
- package/dist/guest/toolchains/leftovers.js +26 -0
- package/dist/guest/toolchains/managers.d.ts +16 -0
- package/dist/guest/toolchains/managers.js +84 -0
- package/dist/guest/toolchains/native/binstall.d.ts +7 -0
- package/dist/guest/toolchains/native/binstall.js +52 -0
- package/dist/guest/toolchains/native/cpp.d.ts +4 -0
- package/dist/guest/toolchains/native/cpp.js +38 -0
- package/dist/guest/toolchains/native/dotnet.d.ts +4 -0
- package/dist/guest/toolchains/native/dotnet.js +103 -0
- package/dist/guest/toolchains/native/mesa.d.ts +7 -0
- package/dist/guest/toolchains/native/mesa.js +46 -0
- package/dist/guest/toolchains/native/msvc/environment.d.ts +16 -0
- package/dist/guest/toolchains/native/msvc/environment.js +57 -0
- package/dist/guest/toolchains/native/msvc/index.d.ts +6 -0
- package/dist/guest/toolchains/native/msvc/index.js +48 -0
- package/dist/guest/toolchains/native/msvc/packages.d.ts +6 -0
- package/dist/guest/toolchains/native/msvc/packages.js +156 -0
- package/dist/guest/toolchains/native/msvc/tree.d.ts +15 -0
- package/dist/guest/toolchains/native/msvc/tree.js +21 -0
- package/dist/guest/toolchains/native/rust.d.ts +3 -0
- package/dist/guest/toolchains/native/rust.js +45 -0
- package/dist/guest/toolchains/node.d.ts +3 -0
- package/dist/guest/toolchains/node.js +152 -0
- package/dist/guest/toolchains/plan.d.ts +22 -0
- package/dist/guest/toolchains/plan.js +96 -0
- package/dist/guest/toolchains/pwsh.d.ts +4 -0
- package/dist/guest/toolchains/pwsh.js +14 -0
- package/dist/guest/toolchains/python.d.ts +3 -0
- package/dist/guest/toolchains/python.js +54 -0
- package/dist/guest/toolchains/runtimes.d.ts +19 -0
- package/dist/guest/toolchains/runtimes.js +38 -0
- package/dist/guest/toolchains/scripting/php.d.ts +3 -0
- package/dist/guest/toolchains/scripting/php.js +71 -0
- package/dist/guest/toolchains/scripting/ruby.d.ts +3 -0
- package/dist/guest/toolchains/scripting/ruby.js +47 -0
- package/dist/guest/toolchains/scripting/vcruntime.d.ts +7 -0
- package/dist/guest/toolchains/scripting/vcruntime.js +45 -0
- package/dist/guest/toolchains/versions.d.ts +47 -0
- package/dist/guest/toolchains/versions.js +77 -0
- package/dist/guest/toolchains/vscode.d.ts +2 -0
- package/dist/guest/toolchains/vscode.js +53 -0
- package/dist/guest/view.d.ts +1 -0
- package/dist/guest/view.js +42 -0
- package/dist/host/accounts.d.ts +32 -0
- package/dist/host/accounts.js +128 -0
- package/dist/host/agent-commands.d.ts +25 -0
- package/dist/host/agent-commands.js +145 -0
- package/dist/host/attach.d.ts +17 -0
- package/dist/host/attach.js +69 -0
- package/dist/host/channel.d.ts +38 -0
- package/dist/host/channel.js +162 -0
- package/dist/host/claude-profile.d.ts +15 -0
- package/dist/host/claude-profile.js +107 -0
- package/dist/host/cleanup.d.ts +23 -0
- package/dist/host/cleanup.js +57 -0
- package/dist/host/database-plan.d.ts +12 -0
- package/dist/host/database-plan.js +52 -0
- package/dist/host/generate.d.ts +7 -0
- package/dist/host/generate.js +98 -0
- package/dist/host/join.d.ts +28 -0
- package/dist/host/join.js +76 -0
- package/dist/host/launch/mappings.d.ts +3 -0
- package/dist/host/launch/mappings.js +50 -0
- package/dist/host/launch/projects.d.ts +15 -0
- package/dist/host/launch/projects.js +56 -0
- package/dist/host/launch/stage.d.ts +11 -0
- package/dist/host/launch/stage.js +66 -0
- package/dist/host/launch/wsb.d.ts +10 -0
- package/dist/host/launch/wsb.js +34 -0
- package/dist/host/pc-scan.d.ts +6 -0
- package/dist/host/pc-scan.js +37 -0
- package/dist/host/personalize.d.ts +10 -0
- package/dist/host/personalize.js +97 -0
- package/dist/host/platform.d.ts +19 -0
- package/dist/host/platform.js +126 -0
- package/dist/host/project-scan.d.ts +7 -0
- package/dist/host/project-scan.js +89 -0
- package/dist/host/projects.d.ts +100 -0
- package/dist/host/projects.js +147 -0
- package/dist/host/repo-guard.d.ts +36 -0
- package/dist/host/repo-guard.js +219 -0
- package/dist/host/secrets.d.ts +14 -0
- package/dist/host/secrets.js +133 -0
- package/dist/host/terminal.d.ts +32 -0
- package/dist/host/terminal.js +130 -0
- package/dist/host/test-guard.d.ts +6 -0
- package/dist/host/test-guard.js +15 -0
- package/dist/host/window.d.ts +4 -0
- package/dist/host/window.js +72 -0
- package/dist/host/wsb.d.ts +37 -0
- package/dist/host/wsb.js +101 -0
- package/dist/runtime/daemon.js +28494 -0
- package/dist/runtime/db.js +26387 -0
- package/dist/runtime/gitguard.js +84 -0
- package/dist/runtime/main.js +28622 -0
- package/dist/runtime/view.js +25841 -0
- package/dist/selftest/accounts.test.d.ts +2 -0
- package/dist/selftest/accounts.test.js +44 -0
- package/dist/selftest/agent-commands.test.d.ts +2 -0
- package/dist/selftest/agent-commands.test.js +47 -0
- package/dist/selftest/channel.test.d.ts +2 -0
- package/dist/selftest/channel.test.js +37 -0
- package/dist/selftest/cli.test.d.ts +2 -0
- package/dist/selftest/cli.test.js +118 -0
- package/dist/selftest/cmdline.test.d.ts +2 -0
- package/dist/selftest/cmdline.test.js +45 -0
- package/dist/selftest/config.test.d.ts +2 -0
- package/dist/selftest/config.test.js +38 -0
- package/dist/selftest/data.test.d.ts +2 -0
- package/dist/selftest/data.test.js +80 -0
- package/dist/selftest/detect.test.d.ts +2 -0
- package/dist/selftest/detect.test.js +58 -0
- package/dist/selftest/fixtures.d.ts +272 -0
- package/dist/selftest/fixtures.js +80 -0
- package/dist/selftest/generate.test.d.ts +2 -0
- package/dist/selftest/generate.test.js +63 -0
- package/dist/selftest/git-access.test.d.ts +2 -0
- package/dist/selftest/git-access.test.js +26 -0
- package/dist/selftest/guest-boot.test.d.ts +2 -0
- package/dist/selftest/guest-boot.test.js +76 -0
- package/dist/selftest/harness.d.ts +51 -0
- package/dist/selftest/harness.js +77 -0
- package/dist/selftest/host-sim.test.d.ts +2 -0
- package/dist/selftest/host-sim.test.js +93 -0
- package/dist/selftest/hostile-input.test.d.ts +2 -0
- package/dist/selftest/hostile-input.test.js +54 -0
- package/dist/selftest/index.d.ts +8 -0
- package/dist/selftest/index.js +155 -0
- package/dist/selftest/links.test.d.ts +2 -0
- package/dist/selftest/links.test.js +50 -0
- package/dist/selftest/live-daemon.test.d.ts +2 -0
- package/dist/selftest/live-daemon.test.js +91 -0
- package/dist/selftest/live-git.test.d.ts +2 -0
- package/dist/selftest/live-git.test.js +60 -0
- package/dist/selftest/live-projects.test.d.ts +2 -0
- package/dist/selftest/live-projects.test.js +127 -0
- package/dist/selftest/live.d.ts +65 -0
- package/dist/selftest/live.js +109 -0
- package/dist/selftest/local-config.test.d.ts +2 -0
- package/dist/selftest/local-config.test.js +107 -0
- package/dist/selftest/mirror.test.d.ts +2 -0
- package/dist/selftest/mirror.test.js +101 -0
- package/dist/selftest/network.test.d.ts +2 -0
- package/dist/selftest/network.test.js +28 -0
- package/dist/selftest/personalization.test.d.ts +2 -0
- package/dist/selftest/personalization.test.js +53 -0
- package/dist/selftest/projects.test.d.ts +2 -0
- package/dist/selftest/projects.test.js +254 -0
- package/dist/selftest/repair.test.d.ts +2 -0
- package/dist/selftest/repair.test.js +42 -0
- package/dist/selftest/repo-guard.test.d.ts +2 -0
- package/dist/selftest/repo-guard.test.js +63 -0
- package/dist/selftest/secrets.test.d.ts +2 -0
- package/dist/selftest/secrets.test.js +71 -0
- package/dist/selftest/setup.test.d.ts +2 -0
- package/dist/selftest/setup.test.js +46 -0
- package/dist/selftest/suite.d.ts +13 -0
- package/dist/selftest/suite.js +1 -0
- package/dist/selftest/sync.test.d.ts +2 -0
- package/dist/selftest/sync.test.js +94 -0
- package/dist/selftest/test-guard.test.d.ts +2 -0
- package/dist/selftest/test-guard.test.js +49 -0
- package/dist/selftest/toolchains.test.d.ts +2 -0
- package/dist/selftest/toolchains.test.js +103 -0
- package/dist/selftest/workspace.test.d.ts +2 -0
- package/dist/selftest/workspace.test.js +53 -0
- package/dist/shared/download.d.ts +60 -0
- package/dist/shared/download.js +215 -0
- package/dist/shared/guest-paths.d.ts +30 -0
- package/dist/shared/guest-paths.js +61 -0
- package/dist/shared/netrules.d.ts +18 -0
- package/dist/shared/netrules.js +98 -0
- package/dist/shared/paths.d.ts +14 -0
- package/dist/shared/paths.js +28 -0
- package/dist/shared/projects.d.ts +53 -0
- package/dist/shared/projects.js +67 -0
- package/dist/shared/pty-client.d.ts +86 -0
- package/dist/shared/pty-client.js +227 -0
- package/dist/shared/rundir.d.ts +39 -0
- package/dist/shared/rundir.js +94 -0
- package/dist/shared/safe-remove.d.ts +10 -0
- package/dist/shared/safe-remove.js +66 -0
- package/dist/shared/schema.d.ts +527 -0
- package/dist/shared/schema.js +90 -0
- package/dist/shared/system.d.ts +15 -0
- package/dist/shared/system.js +19 -0
- package/dist/shared/test-guard.d.ts +4 -0
- package/dist/shared/test-guard.js +37 -0
- package/dist/sync/claude-mem.d.ts +8 -0
- package/dist/sync/claude-mem.js +79 -0
- package/dist/sync/history.d.ts +41 -0
- package/dist/sync/history.js +161 -0
- package/dist/sync/watcher.d.ts +21 -0
- package/dist/sync/watcher.js +135 -0
- package/hooks/post-agent/README.txt +1 -0
- package/hooks/pre-provision/README.txt +1 -0
- package/package.json +91 -4
- package/sandbox/bootstrap.ps1 +258 -0
- package/sandbox/cpucap.ps1 +51 -0
- package/sandbox/gitshim.cs +35 -0
- package/sandbox/logon.cmd +18 -0
- package/sandbox/openssl.cs +42 -0
- package/sandbox/ptyhost.cs +614 -0
- package/sandbox/trust.cmd +35 -0
- package/sandbox/vswhere.cs +83 -0
- package/schemas/config.schema.json +1161 -0
- package/schemas/sandbox-config.schema.json +968 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import type { Config as ConfigSchema, GitAccess as GitAccessSchema, Harness as HarnessSchema, LocalOverrides, Mapping as MappingSchema } from './schema.js';
|
|
3
|
+
export type { AttachEndpointT as AttachEndpoint, GuestRequestT as GuestRequest, GuestResponseT as GuestResponse, SandboxStatusT as SandboxStatus, SessionStatusT as SessionStatus } from '../shared/schema.js';
|
|
4
|
+
export type Config = z.output<typeof ConfigSchema>;
|
|
5
|
+
export type Overrides = z.output<typeof LocalOverrides>;
|
|
6
|
+
export type GitAccess = z.output<typeof GitAccessSchema>;
|
|
7
|
+
export type Harness = z.output<typeof HarnessSchema>;
|
|
8
|
+
export type Mapping = z.output<typeof MappingSchema>;
|
|
9
|
+
export type Toolchains = Config['toolchains'];
|
|
10
|
+
export type ClaudeProfileConfig = Config['claudeProfile'];
|
|
11
|
+
export interface ResolvedMapping {
|
|
12
|
+
host: string;
|
|
13
|
+
guest: string;
|
|
14
|
+
readOnly: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface LocalConfigFile {
|
|
17
|
+
path: string;
|
|
18
|
+
hash: string;
|
|
19
|
+
data: unknown;
|
|
20
|
+
}
|
|
21
|
+
export interface LoadedConfig {
|
|
22
|
+
config: Config;
|
|
23
|
+
configPath: string | null;
|
|
24
|
+
base: string;
|
|
25
|
+
}
|
|
26
|
+
export interface RunRequest {
|
|
27
|
+
packageRoot: string;
|
|
28
|
+
loaded: LoadedConfig;
|
|
29
|
+
cwd: string;
|
|
30
|
+
workspace?: string;
|
|
31
|
+
harness?: string;
|
|
32
|
+
noAgent: boolean;
|
|
33
|
+
noLocalConfig: boolean;
|
|
34
|
+
trustConfig: boolean;
|
|
35
|
+
scripts: string[];
|
|
36
|
+
headless?: boolean;
|
|
37
|
+
/** --database: start these whatever the config and detection say. */
|
|
38
|
+
databases?: string[];
|
|
39
|
+
nodeVersion?: string;
|
|
40
|
+
pythonVersion?: string;
|
|
41
|
+
ask?: (question: string) => Promise<boolean>;
|
|
42
|
+
}
|
|
43
|
+
export interface ResolvedRun {
|
|
44
|
+
config: Config;
|
|
45
|
+
storageRoot: string;
|
|
46
|
+
dataPath: string;
|
|
47
|
+
workspaceHost: string;
|
|
48
|
+
harnessName: string;
|
|
49
|
+
scripts: string[];
|
|
50
|
+
localConfigPath: string | null;
|
|
51
|
+
notes: string[];
|
|
52
|
+
versions: ToolVersions;
|
|
53
|
+
/** Your config asks for guestWorkspace "mirror" (project roots then appear at their PC paths too). */
|
|
54
|
+
mirror?: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** A requested version plus where the request came from (shown to the user). */
|
|
57
|
+
export interface VersionSpec {
|
|
58
|
+
spec: string;
|
|
59
|
+
source: string;
|
|
60
|
+
}
|
|
61
|
+
export interface ToolVersions {
|
|
62
|
+
node: VersionSpec;
|
|
63
|
+
nodeExtra: string[];
|
|
64
|
+
python: VersionSpec;
|
|
65
|
+
}
|
|
66
|
+
export interface HostProbe {
|
|
67
|
+
nodeDir: string | null;
|
|
68
|
+
harnessExe: {
|
|
69
|
+
dir: string;
|
|
70
|
+
file: string;
|
|
71
|
+
} | null;
|
|
72
|
+
gitDir: string | null;
|
|
73
|
+
nvmDir: string | null;
|
|
74
|
+
hostNodeVersion: string;
|
|
75
|
+
/** This PC's Node is one of nvm's versions, so it comes in with nvm and nodeDir stays unmapped. */
|
|
76
|
+
nvmNode?: boolean;
|
|
77
|
+
/** This PC has Volta / pyenv-win (VOLTA_HOME / PYENV set). */
|
|
78
|
+
volta?: boolean;
|
|
79
|
+
pyenv?: boolean;
|
|
80
|
+
}
|
|
81
|
+
/** A database the sandbox runs, and why ("compose.yaml: a compose service runs Postgres"). */
|
|
82
|
+
export interface DatabasePlan {
|
|
83
|
+
database: 'postgres' | 'mysql' | 'sqlserver' | 'mongodb' | 'redis' | 'dynamodb';
|
|
84
|
+
reasons: string[];
|
|
85
|
+
version: string;
|
|
86
|
+
port: number;
|
|
87
|
+
adminPort?: number;
|
|
88
|
+
}
|
|
89
|
+
/** databases / pcLanguages: use these instead of scanning (tests). */
|
|
90
|
+
export interface GenerateOptions {
|
|
91
|
+
packageRoot: string;
|
|
92
|
+
run: ResolvedRun;
|
|
93
|
+
harnessArgs: string[];
|
|
94
|
+
probe: HostProbe;
|
|
95
|
+
freeMemoryBytes?: number;
|
|
96
|
+
databases?: DatabasePlan[];
|
|
97
|
+
pcLanguages?: import('./detect-toolchains.js').AutoToolchain[];
|
|
98
|
+
/** More projects to map (a restart that adds one keeps the ones in use); knownProjects: instead of known-projects.json (tests). */
|
|
99
|
+
extraProjects?: string[];
|
|
100
|
+
knownProjects?: string[];
|
|
101
|
+
/** false: no agent session at boot. */
|
|
102
|
+
autoSession?: boolean;
|
|
103
|
+
}
|
|
104
|
+
export interface Generated {
|
|
105
|
+
wsbPath: string;
|
|
106
|
+
launchPath: string;
|
|
107
|
+
launchId: string;
|
|
108
|
+
sessionPath: string;
|
|
109
|
+
memoryMb: number;
|
|
110
|
+
mappings: ResolvedMapping[];
|
|
111
|
+
profile: ProfileResult;
|
|
112
|
+
databases: DatabasePlan[];
|
|
113
|
+
/** Project roots and other projects mapped with the launch folder. */
|
|
114
|
+
projects: {
|
|
115
|
+
roots: {
|
|
116
|
+
host: string;
|
|
117
|
+
guest: string;
|
|
118
|
+
}[];
|
|
119
|
+
projects: {
|
|
120
|
+
host: string;
|
|
121
|
+
guest: string;
|
|
122
|
+
}[];
|
|
123
|
+
notes: string[];
|
|
124
|
+
};
|
|
125
|
+
toolchains: Record<import('./detect-toolchains.js').AutoToolchain, import('./detect-toolchains.js').ToolchainDecision>;
|
|
126
|
+
}
|
|
127
|
+
/** secretsRemoved: what secrets.strip kept on this PC, e.g. "settings.json env.AWS_SECRET_ACCESS_KEY". */
|
|
128
|
+
export interface ProfileResult {
|
|
129
|
+
hostDir: string | null;
|
|
130
|
+
staged: string[];
|
|
131
|
+
skipped: string[];
|
|
132
|
+
credentials: boolean;
|
|
133
|
+
secretsRemoved: string[];
|
|
134
|
+
}
|
|
135
|
+
/** Written by the host to C:\AgentLaunch\session.json (read-only for the guest): everything the guest needs to know. */
|
|
136
|
+
export interface GuestSession {
|
|
137
|
+
launchId: string;
|
|
138
|
+
project: string;
|
|
139
|
+
workspace: string;
|
|
140
|
+
workspaceHost: string;
|
|
141
|
+
/** The launch folder's project id (file-name safe; status files and sessions use it). Older launch folders lack it. */
|
|
142
|
+
projectId?: string;
|
|
143
|
+
/** Where other projects appear (C:\Projects), and the project roots' guest folders. Older launch folders lack them. */
|
|
144
|
+
projectsDir?: string;
|
|
145
|
+
roots?: string[];
|
|
146
|
+
/** false: start no agent session at boot (a restart that adds a project opens its sessions itself). */
|
|
147
|
+
autoSession?: boolean;
|
|
148
|
+
harnessName: string;
|
|
149
|
+
harness: Harness | null;
|
|
150
|
+
harnessArguments: string[];
|
|
151
|
+
persistAgentState: boolean;
|
|
152
|
+
networking: boolean;
|
|
153
|
+
headless: boolean;
|
|
154
|
+
network: Config['network'];
|
|
155
|
+
provisioning: Config['provisioning'];
|
|
156
|
+
toolchains: Toolchains;
|
|
157
|
+
databases: DatabasePlan[];
|
|
158
|
+
versions: ToolVersions;
|
|
159
|
+
/** Every database `sbx-db start` can run later (older launch folders lack it). */
|
|
160
|
+
databaseCatalog?: DatabasePlan[];
|
|
161
|
+
attach: Config['attach'];
|
|
162
|
+
hooks: Config['hooks'];
|
|
163
|
+
downloads: Config['downloads'];
|
|
164
|
+
/** nodeVersion: this PC's Node (e.g. 22.11.0), found under nvm\v<version> when nvm is mapped, else under node. */
|
|
165
|
+
/** volta / pyenv: this PC uses them (their "auto" settings turn them on in the sandbox too). */
|
|
166
|
+
hostTools: {
|
|
167
|
+
node: boolean;
|
|
168
|
+
nodeVersion: string | null;
|
|
169
|
+
harnessExecutable: string | null;
|
|
170
|
+
git: boolean;
|
|
171
|
+
nvm: boolean;
|
|
172
|
+
volta?: boolean;
|
|
173
|
+
pyenv?: boolean;
|
|
174
|
+
};
|
|
175
|
+
claudeProfile: {
|
|
176
|
+
overwrite: boolean;
|
|
177
|
+
items: string[];
|
|
178
|
+
credentials: boolean;
|
|
179
|
+
hostConfigDir: string | null;
|
|
180
|
+
syncClaudeMem: boolean;
|
|
181
|
+
};
|
|
182
|
+
personalization: boolean;
|
|
183
|
+
/** Drives other than C: that bootstrap.ps1 makes with subst before anything uses them: letter D-Z onto C:\Mirror\<letter>. */
|
|
184
|
+
substDrives?: {
|
|
185
|
+
letter: string;
|
|
186
|
+
target: string;
|
|
187
|
+
}[];
|
|
188
|
+
/** sandbox.cpuLimit as a number (null = no cap); sandbox\cpucap.ps1 applies it before anything starts. */
|
|
189
|
+
cpuLimitPercent?: number | null;
|
|
190
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export declare const isWin: boolean;
|
|
2
|
+
/** An expected problem the user can act on: what went wrong, and (optionally) how to fix it. */
|
|
3
|
+
export declare class UserError extends Error {
|
|
4
|
+
readonly fix?: string | undefined;
|
|
5
|
+
constructor(message: string, fix?: string | undefined);
|
|
6
|
+
}
|
|
7
|
+
/** Throws a UserError. Typed explicitly so TypeScript narrows after calls (arrow functions need it to act as never). */
|
|
8
|
+
export declare const fail: (message: string, fix?: string) => never;
|
|
9
|
+
/**
|
|
10
|
+
* Parses JSON with comments and trailing commas (JSONC, like VS Code's settings.json). Errors name the file, line
|
|
11
|
+
* and column, and say what to fix.
|
|
12
|
+
*/
|
|
13
|
+
export declare const parseJsonc: <T>(text: string, source: string) => T;
|
|
14
|
+
export declare const readJsonc: <T>(file: string) => T;
|
|
15
|
+
export declare const errMsg: (e: unknown) => string;
|
|
16
|
+
export declare const ensureDir: (p: string) => void;
|
|
17
|
+
export declare const dirExists: (p: string) => boolean;
|
|
18
|
+
export declare const fileExists: (p: string) => boolean;
|
|
19
|
+
export declare const readJson: <T>(p: string) => T;
|
|
20
|
+
export declare const tryReadJson: <T>(p: string) => T | null;
|
|
21
|
+
/** Atomic (a crash or a second command never leaves half a file); refused outside temp during the self-test. */
|
|
22
|
+
export declare const writeJson: (p: string, value: unknown) => void;
|
|
23
|
+
export declare const realPath: (p: string) => string;
|
|
24
|
+
export declare const xmlEscape: (v: string) => string;
|
|
25
|
+
export declare const homeDir: () => string;
|
|
26
|
+
export declare const sleep: (ms: number) => Promise<void>;
|
|
27
|
+
/** Expands %VAR% and resolves relative paths against `base`. */
|
|
28
|
+
export declare const expandPath: (input: string, base: string) => string;
|
|
29
|
+
export declare const isRootPath: (p: string) => boolean;
|
|
30
|
+
export declare const isUserProfile: (p: string) => boolean;
|
|
31
|
+
export declare const isInsideOrEqual: (parent: string, child: string) => boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Text that came from the sandbox, made safe to print: control characters and escape sequences are removed except
|
|
34
|
+
* plain colours (SGR), so nothing written by the sandbox can drive your terminal (titles, clipboard, cursor tricks).
|
|
35
|
+
*/
|
|
36
|
+
export declare const safeText: (text: string) => string;
|
|
37
|
+
export declare const findOnPath: (names: string[]) => string | null;
|
|
38
|
+
/** True if `p` itself is a symlink or junction (lstat; never follows it). */
|
|
39
|
+
export declare const isLink: (p: string) => boolean;
|
|
40
|
+
/** Removes a symlink or junction itself (never its target). Junctions need rmdir on Windows. */
|
|
41
|
+
export declare const removeLink: (p: string) => void;
|
|
42
|
+
/**
|
|
43
|
+
* The sandbox can write anywhere in its data folder, so it could swap a folder for a junction that points at this
|
|
44
|
+
* PC's own disk. Before the host reads, writes or deletes anything under `root`, every existing path component from
|
|
45
|
+
* `root` down to `target` must be a plain folder/file. Throws otherwise.
|
|
46
|
+
*/
|
|
47
|
+
export declare const assertNoLinks: (root: string, target: string) => void;
|
|
48
|
+
/** Runs a PowerShell script and returns its exit code (and stdout when `capture`). */
|
|
49
|
+
export declare const runPowerShell: (script: string, capture?: boolean) => {
|
|
50
|
+
status: number;
|
|
51
|
+
stdout: string;
|
|
52
|
+
};
|
|
53
|
+
/** Runs a program (an absolute path: `SYSTEM.*`, never a bare name) and returns its stdout, or '' if it failed or timed out. */
|
|
54
|
+
export declare const captureOutput: (file: string, args: string[], options?: {
|
|
55
|
+
timeout?: number;
|
|
56
|
+
cwd?: string;
|
|
57
|
+
maxBuffer?: number;
|
|
58
|
+
}) => string;
|
|
59
|
+
/** Async variant (does not block the event loop), resolving with stdout. */
|
|
60
|
+
export declare const runPowerShellAsync: (script: string) => Promise<string>;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
5
|
+
import { SYSTEM } from '../shared/system.js';
|
|
6
|
+
import { writeJsonAtomic } from '../shared/rundir.js';
|
|
7
|
+
import { assertTestWritable } from '../shared/test-guard.js';
|
|
8
|
+
import { parse as parseJsoncText, printParseErrorCode } from 'jsonc-parser';
|
|
9
|
+
export const isWin = process.platform === 'win32';
|
|
10
|
+
/** An expected problem the user can act on: what went wrong, and (optionally) how to fix it. */
|
|
11
|
+
export class UserError extends Error {
|
|
12
|
+
fix;
|
|
13
|
+
constructor(message, fix) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.fix = fix;
|
|
16
|
+
this.name = 'UserError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Throws a UserError. Typed explicitly so TypeScript narrows after calls (arrow functions need it to act as never). */
|
|
20
|
+
export const fail = (message, fix) => { throw new UserError(message, fix); };
|
|
21
|
+
/**
|
|
22
|
+
* Parses JSON with comments and trailing commas (JSONC, like VS Code's settings.json). Errors name the file, line
|
|
23
|
+
* and column, and say what to fix.
|
|
24
|
+
*/
|
|
25
|
+
export const parseJsonc = (text, source) => {
|
|
26
|
+
const errors = [];
|
|
27
|
+
const value = parseJsoncText(text.replace(/^/, ''), errors, { allowTrailingComma: true, disallowComments: false });
|
|
28
|
+
if (errors.length) {
|
|
29
|
+
const { offset, error } = errors[0];
|
|
30
|
+
const before = text.slice(0, offset).split(/\r?\n/);
|
|
31
|
+
fail(source + ' is not valid JSON (line ' + before.length + ', column ' + (before[before.length - 1].length + 1) + ': ' + printParseErrorCode(error) + ').', 'Fix that spot in the file. Comments (// and /* */) and trailing commas are fine.');
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
};
|
|
35
|
+
export const readJsonc = (file) => parseJsonc(fs.readFileSync(file, 'utf8'), file);
|
|
36
|
+
export const errMsg = (e) => e instanceof Error ? e.message : String(e);
|
|
37
|
+
export const ensureDir = (p) => { fs.mkdirSync(p, { recursive: true }); };
|
|
38
|
+
export const dirExists = (p) => { try {
|
|
39
|
+
return fs.statSync(p).isDirectory();
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
} };
|
|
44
|
+
export const fileExists = (p) => { try {
|
|
45
|
+
return fs.statSync(p).isFile();
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
} };
|
|
50
|
+
export const readJson = (p) => JSON.parse(fs.readFileSync(p, 'utf8').replace(/^\uFEFF/, ''));
|
|
51
|
+
export const tryReadJson = (p) => { try {
|
|
52
|
+
return readJson(p);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
} };
|
|
57
|
+
/** Atomic (a crash or a second command never leaves half a file); refused outside temp during the self-test. */
|
|
58
|
+
export const writeJson = (p, value) => writeJsonAtomic(p, value);
|
|
59
|
+
export const realPath = (p) => { try {
|
|
60
|
+
return fs.realpathSync(p);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return p;
|
|
64
|
+
} };
|
|
65
|
+
export const xmlEscape = (v) => v.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
66
|
+
export const homeDir = () => process.env.USERPROFILE ?? os.homedir();
|
|
67
|
+
export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
68
|
+
/** Expands %VAR% and resolves relative paths against `base`. */
|
|
69
|
+
export const expandPath = (input, base) => {
|
|
70
|
+
const expanded = input.replace(/%([^%]+)%/g, (_m, name) => process.env[name] ?? '%' + name + '%').replace(/^~(?=$|[\\/])/, homeDir());
|
|
71
|
+
return path.resolve(path.isAbsolute(expanded) ? expanded : path.join(base, expanded));
|
|
72
|
+
};
|
|
73
|
+
export const isRootPath = (p) => { const n = path.resolve(p); return n.toLowerCase() === path.parse(n).root.toLowerCase(); };
|
|
74
|
+
export const isUserProfile = (p) => { const profile = process.env.USERPROFILE ?? process.env.HOME ?? ''; return profile !== '' && path.resolve(p).toLowerCase() === path.resolve(profile).toLowerCase(); };
|
|
75
|
+
export const isInsideOrEqual = (parent, child) => {
|
|
76
|
+
const rel = path.relative(path.resolve(parent).toLowerCase(), path.resolve(child).toLowerCase());
|
|
77
|
+
return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Text that came from the sandbox, made safe to print: control characters and escape sequences are removed except
|
|
81
|
+
* plain colours (SGR), so nothing written by the sandbox can drive your terminal (titles, clipboard, cursor tricks).
|
|
82
|
+
*/
|
|
83
|
+
export const safeText = (text) => text.replace(/\x1b\[[0-9;]*m|\x1b(\[[0-9;?]*[ -\/]*[@-~]|\][^\x07\x1b]*(\x07|\x1b\\)|[@-Z\\-_])|[\x00-\x08\x0b-\x1f\x7f-\x9f]/g, (m) => (/^\x1b\[[0-9;]*m$/.test(m) ? m : ''));
|
|
84
|
+
export const findOnPath = (names) => {
|
|
85
|
+
const dirs = (process.env.PATH ?? process.env.Path ?? '').split(path.delimiter).filter((d) => d !== '');
|
|
86
|
+
for (const name of names)
|
|
87
|
+
for (const dir of dirs) {
|
|
88
|
+
const candidate = path.join(dir, name);
|
|
89
|
+
if (fileExists(candidate))
|
|
90
|
+
return candidate;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
};
|
|
94
|
+
/** True if `p` itself is a symlink or junction (lstat; never follows it). */
|
|
95
|
+
export const isLink = (p) => { try {
|
|
96
|
+
return fs.lstatSync(p).isSymbolicLink();
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return false;
|
|
100
|
+
} };
|
|
101
|
+
/** Removes a symlink or junction itself (never its target). Junctions need rmdir on Windows. */
|
|
102
|
+
export const removeLink = (p) => { assertTestWritable(p); try {
|
|
103
|
+
fs.unlinkSync(p);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
fs.rmdirSync(p);
|
|
107
|
+
} };
|
|
108
|
+
/**
|
|
109
|
+
* The sandbox can write anywhere in its data folder, so it could swap a folder for a junction that points at this
|
|
110
|
+
* PC's own disk. Before the host reads, writes or deletes anything under `root`, every existing path component from
|
|
111
|
+
* `root` down to `target` must be a plain folder/file. Throws otherwise.
|
|
112
|
+
*/
|
|
113
|
+
export const assertNoLinks = (root, target) => {
|
|
114
|
+
const rel = path.relative(path.resolve(root), path.resolve(target));
|
|
115
|
+
if (rel.startsWith('..') || path.isAbsolute(rel))
|
|
116
|
+
fail('Refusing to touch ' + target + ': it is outside ' + root);
|
|
117
|
+
let current = path.resolve(root);
|
|
118
|
+
for (const part of ['', ...rel.split(path.sep).filter(Boolean)]) {
|
|
119
|
+
current = part ? path.join(current, part) : current;
|
|
120
|
+
if (isLink(current))
|
|
121
|
+
fail('Refusing to touch ' + target + ': ' + current + ' is a link (possible escape attempt from the sandbox).');
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* PowerShell arguments for a script passed encoded (no quoting issues, no progress-bar noise on stderr). Output is
|
|
126
|
+
* UTF-8: Windows PowerShell 5.1 otherwise writes redirected stdout in the OEM code page, mangling non-ASCII (regional
|
|
127
|
+
* date formats, wallpaper paths). UTF8Encoding($false): no byte-order mark in front of the output.
|
|
128
|
+
*/
|
|
129
|
+
const powerShellArgs = (script) => {
|
|
130
|
+
const prelude = "[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding $false\n$ProgressPreference = 'SilentlyContinue'\n";
|
|
131
|
+
const encoded = Buffer.from(prelude + script, 'utf16le').toString('base64');
|
|
132
|
+
// -OutputFormat Text: with redirected output PowerShell would otherwise print its progress stream as "#< CLIXML" XML.
|
|
133
|
+
return ['-NoLogo', '-NoProfile', '-NonInteractive', '-OutputFormat', 'Text', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', encoded];
|
|
134
|
+
};
|
|
135
|
+
/** Runs a PowerShell script and returns its exit code (and stdout when `capture`). */
|
|
136
|
+
export const runPowerShell = (script, capture = false) => {
|
|
137
|
+
const result = spawnSync(SYSTEM.powershell(), powerShellArgs(script), { stdio: capture ? ['ignore', 'pipe', 'inherit'] : 'inherit', encoding: 'utf8', windowsHide: true });
|
|
138
|
+
if (result.error)
|
|
139
|
+
throw result.error;
|
|
140
|
+
return { status: result.status ?? 1, stdout: result.stdout ?? '' };
|
|
141
|
+
};
|
|
142
|
+
/** Runs a program (an absolute path: `SYSTEM.*`, never a bare name) and returns its stdout, or '' if it failed or timed out. */
|
|
143
|
+
export const captureOutput = (file, args, options = {}) => {
|
|
144
|
+
const result = spawnSync(file, args, { encoding: 'utf8', windowsHide: true, timeout: 60000, ...options });
|
|
145
|
+
return result.status === 0 ? result.stdout ?? '' : '';
|
|
146
|
+
};
|
|
147
|
+
/** Async variant (does not block the event loop), resolving with stdout. */
|
|
148
|
+
export const runPowerShellAsync = (script) => new Promise((resolve, reject) => {
|
|
149
|
+
const child = spawn(SYSTEM.powershell(), powerShellArgs(script), { stdio: ['ignore', 'pipe', 'inherit'], windowsHide: true });
|
|
150
|
+
let out = '';
|
|
151
|
+
child.stdout.on('data', (d) => { out += d.toString('utf8'); });
|
|
152
|
+
child.on('error', reject);
|
|
153
|
+
child.on('close', () => resolve(out));
|
|
154
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Config, Overrides, ResolvedRun, RunRequest, ToolVersions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* 'auto' guest workspace: C:\Workspace\<folder name>. Keeping the real folder name means Claude's per-project history
|
|
4
|
+
* and claude-mem's project name match this PC instead of every project being "Workspace".
|
|
5
|
+
*/
|
|
6
|
+
export declare const autoGuestWorkspace: (workspaceHost: string) => string;
|
|
7
|
+
/** The Node version a Volta project pins ("volta": { "node": "20.11.1" } in package.json), so nvm defaults to it too. */
|
|
8
|
+
export declare const voltaNode: (workspace: string) => string | null;
|
|
9
|
+
/**
|
|
10
|
+
* Picks tool versions. Highest priority first: command line, the folder's .sandbox-config.json,
|
|
11
|
+
* version files in the workspace (.nvmrc, .node-version, .python-version), the tool config, this PC's node version.
|
|
12
|
+
*/
|
|
13
|
+
export declare const resolveVersions: (config: Config, workspace: string, local: Overrides, req: {
|
|
14
|
+
nodeVersion?: string;
|
|
15
|
+
pythonVersion?: string;
|
|
16
|
+
}, hostNodeVersion: string) => ToolVersions;
|
|
17
|
+
/** Precedence (lowest to highest): built-in defaults, user config, .sandbox-config.json in the workspace, command-line flags. */
|
|
18
|
+
export declare const resolveRun: (req: RunRequest, hostNodeVersion?: string) => Promise<ResolvedRun>;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { planMirrorWorkspace } from '../shared/guest-paths.js';
|
|
5
|
+
import { DATABASES } from './detect-databases.js';
|
|
6
|
+
import { applyOverrides, describeOverrides, isTrusted, normalizeOverrides, readLocalConfig, saveTrust, trustStorePath } from './local-config.js';
|
|
7
|
+
import { assertSafeWorkspace } from './safe-paths.js';
|
|
8
|
+
import { dirExists, expandPath, fail, fileExists } from './util.js';
|
|
9
|
+
/**
|
|
10
|
+
* 'auto' guest workspace: C:\Workspace\<folder name>. Keeping the real folder name means Claude's per-project history
|
|
11
|
+
* and claude-mem's project name match this PC instead of every project being "Workspace".
|
|
12
|
+
*/
|
|
13
|
+
export const autoGuestWorkspace = (workspaceHost) => {
|
|
14
|
+
const name = path.basename(workspaceHost).replace(/[^A-Za-z0-9._ -]/g, '_').replace(/^[. ]+|[. ]+$/g, '');
|
|
15
|
+
return 'C:\\Workspace\\' + (name || 'project');
|
|
16
|
+
};
|
|
17
|
+
const firstLine = (file) => {
|
|
18
|
+
if (!fileExists(file))
|
|
19
|
+
return null;
|
|
20
|
+
const line = fs.readFileSync(file, 'utf8').replace(/^/, '').split(/\r?\n/).map((l) => l.replace(/#.*$/, '').trim()).find((l) => l !== '');
|
|
21
|
+
return line ?? null;
|
|
22
|
+
};
|
|
23
|
+
const VOLTA_PIN = 'package.json volta.node';
|
|
24
|
+
const PackageJson = z.object({ volta: z.object({ node: z.string().regex(/^\d+(\.\d+){0,2}$/).optional() }).optional() });
|
|
25
|
+
/** The Node version a Volta project pins ("volta": { "node": "20.11.1" } in package.json), so nvm defaults to it too. */
|
|
26
|
+
export const voltaNode = (workspace) => {
|
|
27
|
+
const file = path.join(workspace, 'package.json');
|
|
28
|
+
if (!fileExists(file))
|
|
29
|
+
return null;
|
|
30
|
+
try {
|
|
31
|
+
return PackageJson.safeParse(JSON.parse(fs.readFileSync(file, 'utf8').replace(/^/, ''))).data?.volta?.node ?? null;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Picks tool versions. Highest priority first: command line, the folder's .sandbox-config.json,
|
|
39
|
+
* version files in the workspace (.nvmrc, .node-version, .python-version), the tool config, this PC's node version.
|
|
40
|
+
*/
|
|
41
|
+
export const resolveVersions = (config, workspace, local, req, hostNodeVersion) => {
|
|
42
|
+
const localTools = (local.toolchains ?? {});
|
|
43
|
+
const pick = (cli, localValue, files, configured) => {
|
|
44
|
+
if (cli)
|
|
45
|
+
return { spec: cli, source: 'command line' };
|
|
46
|
+
if (localValue)
|
|
47
|
+
return { spec: localValue, source: '.sandbox-config.json' };
|
|
48
|
+
for (const name of files) {
|
|
49
|
+
const value = name === VOLTA_PIN ? voltaNode(workspace) : firstLine(path.join(workspace, name));
|
|
50
|
+
if (value)
|
|
51
|
+
return { spec: value, source: name };
|
|
52
|
+
}
|
|
53
|
+
return { spec: configured, source: 'config' };
|
|
54
|
+
};
|
|
55
|
+
let node = pick(req.nodeVersion, localTools.node?.version, ['.nvmrc', '.node-version', VOLTA_PIN], config.toolchains.node.version);
|
|
56
|
+
if (node.spec === 'host')
|
|
57
|
+
node = { spec: hostNodeVersion.replace(/^v/, ''), source: 'this PC' };
|
|
58
|
+
const python = pick(req.pythonVersion, localTools.python?.version, ['.python-version'], config.toolchains.python.version);
|
|
59
|
+
return { node, nodeExtra: config.toolchains.node.extraVersions, python };
|
|
60
|
+
};
|
|
61
|
+
/** Precedence (lowest to highest): built-in defaults, user config, .sandbox-config.json in the workspace, command-line flags. */
|
|
62
|
+
export const resolveRun = async (req, hostNodeVersion = process.version) => {
|
|
63
|
+
const notes = [];
|
|
64
|
+
// Mappings from the user config are relative to that config file; expand them before anything else uses them.
|
|
65
|
+
const expand = (list) => list.map((p) => expandPath(p, req.loaded.base));
|
|
66
|
+
const projects = req.loaded.config.projects;
|
|
67
|
+
let config = {
|
|
68
|
+
...req.loaded.config,
|
|
69
|
+
mappings: req.loaded.config.mappings.map((m) => ({ ...m, host: expandPath(m.host, req.loaded.base) })),
|
|
70
|
+
projects: { ...projects, roots: expand(projects.roots), exclude: expand(projects.exclude), declinedRoots: expand(projects.declinedRoots) },
|
|
71
|
+
};
|
|
72
|
+
const mirror = config.guestWorkspace === 'mirror';
|
|
73
|
+
const storageRoot = expandPath(config.storage.root, req.loaded.base);
|
|
74
|
+
const workspaceHost = path.resolve(req.workspace ?? req.cwd);
|
|
75
|
+
if (!dirExists(workspaceHost))
|
|
76
|
+
fail('The workspace folder does not exist: ' + workspaceHost, 'Check the --workspace path, or cd into the project first.');
|
|
77
|
+
assertSafeWorkspace(workspaceHost, req.packageRoot, storageRoot);
|
|
78
|
+
const scripts = [];
|
|
79
|
+
let localConfigPath = null;
|
|
80
|
+
let overrides = {};
|
|
81
|
+
const local = req.noLocalConfig ? null : readLocalConfig(workspaceHost);
|
|
82
|
+
if (local) {
|
|
83
|
+
overrides = normalizeOverrides(local.data, workspaceHost, local.path);
|
|
84
|
+
const store = trustStorePath(storageRoot);
|
|
85
|
+
if (!isTrusted(store, local.path, local.hash)) {
|
|
86
|
+
const summary = describeOverrides(overrides);
|
|
87
|
+
let trusted = req.trustConfig;
|
|
88
|
+
if (!trusted && req.ask) {
|
|
89
|
+
console.log('\nFound ' + local.path + ' which is not trusted yet. It would apply:');
|
|
90
|
+
for (const line of summary)
|
|
91
|
+
console.log(' ' + line);
|
|
92
|
+
trusted = await req.ask('Trust and use this file?');
|
|
93
|
+
}
|
|
94
|
+
if (!trusted)
|
|
95
|
+
fail(local.path + ' has not been approved yet (a repo can ship this file, so it needs your OK). It would apply:\n ' + (summary.join('\n ') || '(no changes)'), 'Review it, then run again in an interactive terminal and answer y, or add --trust-config. To ignore the file: --no-local-config');
|
|
96
|
+
saveTrust(store, local.path, local.hash);
|
|
97
|
+
notes.push('Trusted ' + local.path + ' (you will only be asked again if the file changes).');
|
|
98
|
+
}
|
|
99
|
+
config = applyOverrides(config, overrides);
|
|
100
|
+
scripts.push(...(overrides.scripts ?? []));
|
|
101
|
+
localConfigPath = local.path;
|
|
102
|
+
notes.push('Using ' + local.path);
|
|
103
|
+
}
|
|
104
|
+
if (req.headless !== undefined)
|
|
105
|
+
config = { ...config, sandbox: { ...config.sandbox, headless: req.headless } };
|
|
106
|
+
for (const name of req.databases ?? []) {
|
|
107
|
+
const db = name.toLowerCase();
|
|
108
|
+
if (!DATABASES.includes(db))
|
|
109
|
+
fail('Unknown database "' + name + '".', 'Choose from: ' + DATABASES.join(', '));
|
|
110
|
+
config = { ...config, databases: { ...config.databases, [db]: { ...config.databases[db], enabled: true } } };
|
|
111
|
+
}
|
|
112
|
+
if (config.guestWorkspace === 'auto')
|
|
113
|
+
config = { ...config, guestWorkspace: autoGuestWorkspace(workspaceHost) };
|
|
114
|
+
else if (config.guestWorkspace === 'mirror') {
|
|
115
|
+
const plan = planMirrorWorkspace(workspaceHost, autoGuestWorkspace(workspaceHost));
|
|
116
|
+
if (plan.note)
|
|
117
|
+
notes.push(plan.note);
|
|
118
|
+
config = { ...config, guestWorkspace: plan.guest };
|
|
119
|
+
}
|
|
120
|
+
scripts.push(...req.scripts.map((s) => path.resolve(s)));
|
|
121
|
+
const harnessName = req.noAgent ? 'none' : (req.harness ?? config.defaultHarness);
|
|
122
|
+
if (harnessName !== 'none' && !config.harnesses[harnessName])
|
|
123
|
+
fail('Unknown agent "' + harnessName + '".', 'Configured agents: ' + Object.keys(config.harnesses).join(', ') + ' (add more under "harnesses" in your config).');
|
|
124
|
+
const versions = resolveVersions(config, workspaceHost, overrides, req, hostNodeVersion);
|
|
125
|
+
const dataPath = path.join(storageRoot, 'data', config.storage.dataName);
|
|
126
|
+
// A project file may ask for "mirror" too (it decides only its own path); project roots follow the user's choice.
|
|
127
|
+
return { config, storageRoot, dataPath, workspaceHost, harnessName, scripts, localConfigPath, notes, versions, mirror };
|
|
128
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// What the boot's provisioning left for projects that join later: the PATH before any toolchain was added, which
|
|
2
|
+
// variables the toolchains set, and what each step contributed. Kept on the sandbox's own disk (lost on close, as it
|
|
3
|
+
// should be). The session daemon builds every later project's environment from it instead of from the boot
|
|
4
|
+
// project's, so project A's Node, Rust target folder or database variables never leak into project B's sessions.
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { writeJsonAtomic } from '../shared/rundir.js';
|
|
8
|
+
import { localDir } from './env.js';
|
|
9
|
+
import { BootEnv } from './project-env.js';
|
|
10
|
+
export { baseEnvFor } from './project-env.js';
|
|
11
|
+
const file = () => path.join(localDir, 'boot-env.json');
|
|
12
|
+
export const saveBootEnv = (value) => {
|
|
13
|
+
try {
|
|
14
|
+
writeJsonAtomic(file(), value);
|
|
15
|
+
}
|
|
16
|
+
catch { /* projects added later then fall back to the boot environment */ }
|
|
17
|
+
};
|
|
18
|
+
export const readBootEnv = () => {
|
|
19
|
+
try {
|
|
20
|
+
return BootEnv.safeParse(JSON.parse(fs.readFileSync(file(), 'utf8'))).data ?? null;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const quoteArg: (arg: string) => string;
|
|
2
|
+
/**
|
|
3
|
+
* Arguments on a `cmd /d /c <agent>.cmd ...` line (npm agents). cmd parses them twice: the /c line, then the .cmd's %*.
|
|
4
|
+
* & | < > ^ are literal inside quotes, so arguments with them are quoted (https://x?a=1&b=2 survives). % expands even
|
|
5
|
+
* inside quotes, so each one sits between quoted pieces ("50"%" off"): every %name% cmd could see then starts with a
|
|
6
|
+
* quote, and no variable is called that. cmd's quote state runs across the whole line (an embedded \" toggles it too),
|
|
7
|
+
* so a metacharacter it would see outside quotes gets ^^^ (one caret left after each parse). Arguments without any
|
|
8
|
+
* of these are quoted as usual.
|
|
9
|
+
*/
|
|
10
|
+
export declare const cmdArgs: (args: string[]) => string;
|
|
11
|
+
/** The command line ptyhost starts; when the program is cmd.exe its arguments are quoted for cmd (cmdArgs). */
|
|
12
|
+
export declare const commandLine: (file: string, args: string[]) => string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Command lines for the agent: Windows argument quoting, and the extra layer cmd.exe needs for npm agents (.cmd files).
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const quoted = (arg) => '"' + arg.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/, '$1$1') + '"';
|
|
4
|
+
export const quoteArg = (arg) => (arg !== '' && !/[\s"]/.test(arg) ? arg : quoted(arg));
|
|
5
|
+
/**
|
|
6
|
+
* Arguments on a `cmd /d /c <agent>.cmd ...` line (npm agents). cmd parses them twice: the /c line, then the .cmd's %*.
|
|
7
|
+
* & | < > ^ are literal inside quotes, so arguments with them are quoted (https://x?a=1&b=2 survives). % expands even
|
|
8
|
+
* inside quotes, so each one sits between quoted pieces ("50"%" off"): every %name% cmd could see then starts with a
|
|
9
|
+
* quote, and no variable is called that. cmd's quote state runs across the whole line (an embedded \" toggles it too),
|
|
10
|
+
* so a metacharacter it would see outside quotes gets ^^^ (one caret left after each parse). Arguments without any
|
|
11
|
+
* of these are quoted as usual.
|
|
12
|
+
*/
|
|
13
|
+
export const cmdArgs = (args) => {
|
|
14
|
+
const pieces = args.map((arg) => (/[&|<>^%]/.test(arg) ? arg.split('%').map(quoted).join('%') : quoteArg(arg))).join(' ');
|
|
15
|
+
let inQuotes = false;
|
|
16
|
+
return [...pieces].map((c) => {
|
|
17
|
+
if (c === '"')
|
|
18
|
+
inQuotes = !inQuotes;
|
|
19
|
+
return !inQuotes && /[&|<>^]/.test(c) ? '^^^' + c : c;
|
|
20
|
+
}).join('');
|
|
21
|
+
};
|
|
22
|
+
/** The command line ptyhost starts; when the program is cmd.exe its arguments are quoted for cmd (cmdArgs). */
|
|
23
|
+
export const commandLine = (file, args) => path.win32.basename(file).toLowerCase() === 'cmd.exe' ? [quoteArg(file), cmdArgs(args)].filter(Boolean).join(' ') : [file, ...args].map(quoteArg).join(' ');
|