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,8 @@
|
|
|
1
|
+
import type { Context } from './context.js';
|
|
2
|
+
export declare const configFile: (ctx: Context) => string;
|
|
3
|
+
/** The value as written in your file (not merged with the defaults). */
|
|
4
|
+
export declare const fileSetting: <T>(ctx: Context, key: string[], fallback: T) => T;
|
|
5
|
+
export declare const setSetting: (ctx: Context, key: string[], value: unknown) => void;
|
|
6
|
+
/** Adds a folder to a list setting (projects.roots, projects.exclude, ...) unless it is there already. */
|
|
7
|
+
export declare const addToList: (ctx: Context, key: string[], folder: string) => boolean;
|
|
8
|
+
export declare const removeFromList: (ctx: Context, key: string[], folder: string) => boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Changing your own config file from a command (sandbox projects ..., the "add this root?" question): one setting at
|
|
2
|
+
// a time through the settings editor, so comments and formatting stay and an invalid result is never written.
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { parse } from 'jsonc-parser';
|
|
6
|
+
import _ from 'lodash';
|
|
7
|
+
import { userConfigPath } from '../../core/config.js';
|
|
8
|
+
import { fail } from '../../core/util.js';
|
|
9
|
+
import { editConfigText } from '../settings/edit.js';
|
|
10
|
+
export const configFile = (ctx) => ctx.loaded.configPath ?? userConfigPath();
|
|
11
|
+
/** The value as written in your file (not merged with the defaults). */
|
|
12
|
+
export const fileSetting = (ctx, key, fallback) => {
|
|
13
|
+
const file = configFile(ctx);
|
|
14
|
+
const text = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
|
|
15
|
+
return _.get(parse(text) ?? {}, key) ?? fallback;
|
|
16
|
+
};
|
|
17
|
+
export const setSetting = (ctx, key, value) => {
|
|
18
|
+
const file = configFile(ctx);
|
|
19
|
+
const text = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
|
|
20
|
+
const result = editConfigText(text, key, value);
|
|
21
|
+
if ('problems' in result)
|
|
22
|
+
fail('Could not change ' + key.join('.') + ':\n ' + result.problems.join('\n '));
|
|
23
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
24
|
+
fs.writeFileSync(file, result.text, 'utf8');
|
|
25
|
+
};
|
|
26
|
+
/** Adds a folder to a list setting (projects.roots, projects.exclude, ...) unless it is there already. */
|
|
27
|
+
export const addToList = (ctx, key, folder) => {
|
|
28
|
+
const list = fileSetting(ctx, key, []);
|
|
29
|
+
if (list.some((p) => path.resolve(p).toLowerCase() === path.resolve(folder).toLowerCase()))
|
|
30
|
+
return false;
|
|
31
|
+
setSetting(ctx, key, [...list, folder]);
|
|
32
|
+
return true;
|
|
33
|
+
};
|
|
34
|
+
export const removeFromList = (ctx, key, folder) => {
|
|
35
|
+
const list = fileSetting(ctx, key, []);
|
|
36
|
+
const rest = list.filter((p) => path.resolve(p).toLowerCase() !== path.resolve(folder).toLowerCase());
|
|
37
|
+
if (rest.length === list.length)
|
|
38
|
+
return false;
|
|
39
|
+
setSetting(ctx, key, rest);
|
|
40
|
+
return true;
|
|
41
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const History: z.ZodArray<z.ZodObject<{
|
|
3
|
+
argv: z.ZodArray<z.ZodString>;
|
|
4
|
+
at: z.ZodString;
|
|
5
|
+
}, z.core.$strict>>;
|
|
6
|
+
export declare const readHistory: () => z.output<typeof History>;
|
|
7
|
+
/** Remembers a command (menu or typed) so the menu can offer it again. Never fails the command itself. */
|
|
8
|
+
export declare const recordHistory: (argv: string[]) => void;
|
|
9
|
+
/** argv as you would type it (quoted where needed). */
|
|
10
|
+
export declare const commandLine: (argv: string[]) => string;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// The last commands run (menu or typed), kept in ~/.agent-sandbox/history.json so the menu can offer them again.
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { userDir } from '../core/config.js';
|
|
5
|
+
import { writeJson } from '../core/util.js';
|
|
6
|
+
import { readValid } from '../shared/rundir.js';
|
|
7
|
+
const History = z.array(z.strictObject({ argv: z.array(z.string()).max(200), at: z.string() })).max(20);
|
|
8
|
+
const historyFile = () => path.join(userDir(), 'history.json');
|
|
9
|
+
const HIDDEN = new Set(['menu', '_watch', 'self-test']);
|
|
10
|
+
export const readHistory = () => readValid(History, historyFile()) ?? [];
|
|
11
|
+
/** Remembers a command (menu or typed) so the menu can offer it again. Never fails the command itself. */
|
|
12
|
+
export const recordHistory = (argv) => {
|
|
13
|
+
if (!argv.length || HIDDEN.has(argv[0]) || argv.includes('--help'))
|
|
14
|
+
return;
|
|
15
|
+
try {
|
|
16
|
+
writeJson(historyFile(), [{ argv, at: new Date().toISOString() }, ...readHistory().filter((h) => h.argv.join('\0') !== argv.join('\0'))].slice(0, 10));
|
|
17
|
+
}
|
|
18
|
+
catch { /* history is a convenience */ }
|
|
19
|
+
};
|
|
20
|
+
/** argv as you would type it (quoted where needed). */
|
|
21
|
+
export const commandLine = (argv) => ['sandbox', ...argv].map((a) => (/[\s"]/.test(a) || a === '' ? '"' + a.replace(/"/g, '\\"') + '"' : a)).join(' ');
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** The package folder (this file is dist/cli/index.js). */
|
|
2
|
+
export declare const packageRoot: string;
|
|
3
|
+
export interface Preset {
|
|
4
|
+
harness?: string;
|
|
5
|
+
command?: string;
|
|
6
|
+
}
|
|
7
|
+
/** `--config <file>` right after the command name (where every command accepts it); argv unchanged without a config. */
|
|
8
|
+
export declare const withConfig: (argv: string[], config: string | undefined) => string[];
|
|
9
|
+
/** `sandbox --config f.json status` works like `sandbox status --config f.json`: a leading --config moves behind the command. */
|
|
10
|
+
export declare const hoistConfig: (argv: string[]) => string[];
|
|
11
|
+
/**
|
|
12
|
+
* CLI entry. `preset` lets wrapper binaries fix the command/agent (sandbox claude = launch --harness claude). No arguments
|
|
13
|
+
* opens the menu; `sandbox <agent> [agent args]` starts that agent.
|
|
14
|
+
*/
|
|
15
|
+
export declare const main: (rawArgv: string[], preset?: Preset) => Promise<number>;
|
|
16
|
+
/** argv without `--config <file>` / `--config=<file>`. */
|
|
17
|
+
export declare const withoutConfig: (argv: string[]) => string[];
|
|
18
|
+
/** Prints a problem the way a user can act on it: what happened, how to fix it; stack traces only for real bugs. */
|
|
19
|
+
export declare const report: (e: unknown) => number;
|
|
20
|
+
export declare const run: (argv: string[], preset?: Preset) => void;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { CommanderError } from 'commander';
|
|
4
|
+
import { buildProgram, parseCli } from './args.js';
|
|
5
|
+
import { commandSpec } from './manifest.js';
|
|
6
|
+
import { c } from './colors.js';
|
|
7
|
+
import { recordHistory } from './history.js';
|
|
8
|
+
import { menu } from './menu.js';
|
|
9
|
+
import { validateOptions } from './validate.js';
|
|
10
|
+
import { loadConfig } from '../core/config.js';
|
|
11
|
+
import * as commands from './commands/index.js';
|
|
12
|
+
import { UserError, errMsg, fail } from '../core/util.js';
|
|
13
|
+
/** The package folder (this file is dist/cli/index.js). */
|
|
14
|
+
export const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
15
|
+
const entry = path.join(packageRoot, 'dist', 'bin', 'agent-sandbox.js');
|
|
16
|
+
const HANDLERS = {
|
|
17
|
+
launch: (ctx) => commands.launch(ctx),
|
|
18
|
+
generate: (ctx) => commands.launch(ctx, true),
|
|
19
|
+
attach: commands.attach,
|
|
20
|
+
sessions: commands.sessions,
|
|
21
|
+
status: commands.status,
|
|
22
|
+
exec: commands.exec,
|
|
23
|
+
'agent-commands': commands.agentCommandsCommand,
|
|
24
|
+
db: commands.db,
|
|
25
|
+
restart: commands.restart,
|
|
26
|
+
stop: commands.stop,
|
|
27
|
+
show: commands.show,
|
|
28
|
+
hide: commands.hide,
|
|
29
|
+
account: commands.account,
|
|
30
|
+
projects: commands.projects,
|
|
31
|
+
sync: commands.sync,
|
|
32
|
+
logs: commands.logs,
|
|
33
|
+
clean: commands.clean,
|
|
34
|
+
config: commands.config,
|
|
35
|
+
doctor: commands.doctor,
|
|
36
|
+
setup: commands.setup,
|
|
37
|
+
data: commands.data,
|
|
38
|
+
'host-claude': commands.claudeHost,
|
|
39
|
+
_watch: commands.watcher,
|
|
40
|
+
};
|
|
41
|
+
/** Runs an argv from the menu exactly as if it had been typed, reporting errors instead of throwing. */
|
|
42
|
+
const runFromMenu = (config) => (argv) => main(withConfig(argv, config)).catch((e) => report(e));
|
|
43
|
+
/** `--config <file>` right after the command name (where every command accepts it); argv unchanged without a config. */
|
|
44
|
+
export const withConfig = (argv, config) => (config && argv.length ? [argv[0], '--config', config, ...argv.slice(1)] : argv);
|
|
45
|
+
/** `sandbox --config f.json status` works like `sandbox status --config f.json`: a leading --config moves behind the command. */
|
|
46
|
+
export const hoistConfig = (argv) => {
|
|
47
|
+
const eq = argv[0]?.startsWith('--config=');
|
|
48
|
+
if (argv[0] !== '--config' && !eq)
|
|
49
|
+
return argv;
|
|
50
|
+
const value = eq ? argv[0].slice('--config='.length) : argv[1];
|
|
51
|
+
const rest = argv.slice(eq ? 1 : 2);
|
|
52
|
+
if (value === undefined || value === '')
|
|
53
|
+
return argv;
|
|
54
|
+
return rest.length ? withConfig(rest, value) : ['menu', '--config', value];
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* CLI entry. `preset` lets wrapper binaries fix the command/agent (sandbox claude = launch --harness claude). No arguments
|
|
58
|
+
* opens the menu; `sandbox <agent> [agent args]` starts that agent.
|
|
59
|
+
*/
|
|
60
|
+
export const main = async (rawArgv, preset = {}) => {
|
|
61
|
+
const argv = preset.command || preset.harness ? rawArgv : hoistConfig(rawArgv);
|
|
62
|
+
const isAgent = (name) => {
|
|
63
|
+
if (!name || name.startsWith('-') || commandSpec(name))
|
|
64
|
+
return false;
|
|
65
|
+
const at = argv.indexOf('--config');
|
|
66
|
+
try {
|
|
67
|
+
return Boolean(loadConfig(at >= 0 ? argv[at + 1] : undefined).config.harnesses[name]);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
if (!preset.command && !preset.harness) {
|
|
74
|
+
if (argv.length === 0)
|
|
75
|
+
return process.stdin.isTTY ? main(['menu']) : (buildProgram(() => undefined).outputHelp(), 0);
|
|
76
|
+
if (isAgent(argv[0]))
|
|
77
|
+
return main(argv.slice(1), { harness: argv[0] });
|
|
78
|
+
}
|
|
79
|
+
const options = parseCli(preset.command ? [preset.command, ...argv] : argv, Boolean(preset.harness));
|
|
80
|
+
if (preset.harness)
|
|
81
|
+
options.harness = preset.harness;
|
|
82
|
+
const typed = preset.harness ? ['launch', '--harness', preset.harness, ...argv] : preset.command ? [preset.command, ...argv] : argv;
|
|
83
|
+
if (options.command === 'menu')
|
|
84
|
+
return menu(commands.createContext(packageRoot, entry, options), runFromMenu(options.config));
|
|
85
|
+
// Loaded only when asked for: normal commands never load test code.
|
|
86
|
+
if (options.command === 'self-test')
|
|
87
|
+
return (await import('../selftest/index.js')).selfTest(packageRoot, options.positionals);
|
|
88
|
+
// repair must run even when the config file is broken (createContext would refuse it).
|
|
89
|
+
if (options.command === 'repair') {
|
|
90
|
+
recordHistory(withoutConfig(typed));
|
|
91
|
+
return commands.repairCommand(packageRoot, entry, options);
|
|
92
|
+
}
|
|
93
|
+
const handler = HANDLERS[options.command];
|
|
94
|
+
if (!handler)
|
|
95
|
+
return fail('Unknown command "' + options.command + '".', 'Run sandbox --help for the list of commands, or sandbox for the menu.');
|
|
96
|
+
const ctx = commands.createContext(packageRoot, entry, options);
|
|
97
|
+
validateOptions(ctx);
|
|
98
|
+
// Only what parsed and validated is offered again by the menu, without the --config (it is the same every time).
|
|
99
|
+
recordHistory(withoutConfig(typed));
|
|
100
|
+
return (await handler(ctx)) ?? 0;
|
|
101
|
+
};
|
|
102
|
+
/** argv without `--config <file>` / `--config=<file>`. */
|
|
103
|
+
export const withoutConfig = (argv) => argv.filter((a, i) => a !== '--config' && argv[i - 1] !== '--config' && !a.startsWith('--config='));
|
|
104
|
+
/** Prints a problem the way a user can act on it: what happened, how to fix it; stack traces only for real bugs. */
|
|
105
|
+
export const report = (e) => {
|
|
106
|
+
if (e instanceof CommanderError)
|
|
107
|
+
return ['commander.helpDisplayed', 'commander.help', 'commander.version'].includes(e.code) ? 0 : e.exitCode || 1;
|
|
108
|
+
if (e instanceof UserError) {
|
|
109
|
+
console.error(c.red('Error: ') + e.message);
|
|
110
|
+
if (e.fix)
|
|
111
|
+
console.error(' Fix: ' + e.fix);
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
114
|
+
console.error(c.red('Unexpected error: ') + errMsg(e));
|
|
115
|
+
console.error(c.dim('This is a bug in windows-development-sandbox. Details: ' + (e instanceof Error && e.stack ? e.stack.split('\n').slice(1, 4).join(' | ').trim() : 'none')));
|
|
116
|
+
console.error(' Please report it with the output of: sandbox doctor');
|
|
117
|
+
return 1;
|
|
118
|
+
};
|
|
119
|
+
export const run = (argv, preset) => {
|
|
120
|
+
main(argv, preset).then((code) => { process.exitCode = code; }, (e) => { process.exitCode = report(e); });
|
|
121
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const OptionSpec: z.ZodObject<{
|
|
3
|
+
flag: z.ZodString;
|
|
4
|
+
short: z.ZodOptional<z.ZodString>;
|
|
5
|
+
type: z.ZodEnum<{
|
|
6
|
+
string: "string";
|
|
7
|
+
number: "number";
|
|
8
|
+
boolean: "boolean";
|
|
9
|
+
list: "list";
|
|
10
|
+
}>;
|
|
11
|
+
value: z.ZodOptional<z.ZodString>;
|
|
12
|
+
description: z.ZodString;
|
|
13
|
+
default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
14
|
+
choices: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodEnum<{
|
|
15
|
+
harnesses: "harnesses";
|
|
16
|
+
sessions: "sessions";
|
|
17
|
+
accounts: "accounts";
|
|
18
|
+
}>]>>;
|
|
19
|
+
advanced: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
when: z.ZodOptional<z.ZodString>;
|
|
21
|
+
noMenu: z.ZodOptional<z.ZodBoolean>;
|
|
22
|
+
}, z.core.$strict>;
|
|
23
|
+
export declare const ArgSpec: z.ZodObject<{
|
|
24
|
+
name: z.ZodString;
|
|
25
|
+
description: z.ZodString;
|
|
26
|
+
required: z.ZodBoolean;
|
|
27
|
+
variadic: z.ZodOptional<z.ZodBoolean>;
|
|
28
|
+
choices: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodEnum<{
|
|
29
|
+
harnesses: "harnesses";
|
|
30
|
+
sessions: "sessions";
|
|
31
|
+
accounts: "accounts";
|
|
32
|
+
}>]>>;
|
|
33
|
+
when: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
34
|
+
}, z.core.$strict>;
|
|
35
|
+
/** Is an argument or option with this `when` asked, given the first argument's answer? */
|
|
36
|
+
export declare const askedWhen: (when: string | string[] | undefined, first: string) => boolean;
|
|
37
|
+
export declare const Group: z.ZodEnum<{
|
|
38
|
+
Agents: "Agents";
|
|
39
|
+
Sandbox: "Sandbox";
|
|
40
|
+
Sessions: "Sessions";
|
|
41
|
+
Accounts: "Accounts";
|
|
42
|
+
Data: "Data";
|
|
43
|
+
Health: "Health";
|
|
44
|
+
Config: "Config";
|
|
45
|
+
}>;
|
|
46
|
+
export declare const CommandSpec: z.ZodObject<{
|
|
47
|
+
name: z.ZodString;
|
|
48
|
+
summary: z.ZodString;
|
|
49
|
+
group: z.ZodEnum<{
|
|
50
|
+
Agents: "Agents";
|
|
51
|
+
Sandbox: "Sandbox";
|
|
52
|
+
Sessions: "Sessions";
|
|
53
|
+
Accounts: "Accounts";
|
|
54
|
+
Data: "Data";
|
|
55
|
+
Health: "Health";
|
|
56
|
+
Config: "Config";
|
|
57
|
+
}>;
|
|
58
|
+
args: z.ZodArray<z.ZodObject<{
|
|
59
|
+
name: z.ZodString;
|
|
60
|
+
description: z.ZodString;
|
|
61
|
+
required: z.ZodBoolean;
|
|
62
|
+
variadic: z.ZodOptional<z.ZodBoolean>;
|
|
63
|
+
choices: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodEnum<{
|
|
64
|
+
harnesses: "harnesses";
|
|
65
|
+
sessions: "sessions";
|
|
66
|
+
accounts: "accounts";
|
|
67
|
+
}>]>>;
|
|
68
|
+
when: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
69
|
+
}, z.core.$strict>>;
|
|
70
|
+
options: z.ZodArray<z.ZodObject<{
|
|
71
|
+
flag: z.ZodString;
|
|
72
|
+
short: z.ZodOptional<z.ZodString>;
|
|
73
|
+
type: z.ZodEnum<{
|
|
74
|
+
string: "string";
|
|
75
|
+
number: "number";
|
|
76
|
+
boolean: "boolean";
|
|
77
|
+
list: "list";
|
|
78
|
+
}>;
|
|
79
|
+
value: z.ZodOptional<z.ZodString>;
|
|
80
|
+
description: z.ZodString;
|
|
81
|
+
default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
82
|
+
choices: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodEnum<{
|
|
83
|
+
harnesses: "harnesses";
|
|
84
|
+
sessions: "sessions";
|
|
85
|
+
accounts: "accounts";
|
|
86
|
+
}>]>>;
|
|
87
|
+
advanced: z.ZodOptional<z.ZodBoolean>;
|
|
88
|
+
when: z.ZodOptional<z.ZodString>;
|
|
89
|
+
noMenu: z.ZodOptional<z.ZodBoolean>;
|
|
90
|
+
}, z.core.$strict>>;
|
|
91
|
+
passthrough: z.ZodOptional<z.ZodEnum<{
|
|
92
|
+
command: "command";
|
|
93
|
+
agent: "agent";
|
|
94
|
+
}>>;
|
|
95
|
+
json: z.ZodOptional<z.ZodBoolean>;
|
|
96
|
+
interactive: z.ZodOptional<z.ZodBoolean>;
|
|
97
|
+
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
98
|
+
}, z.core.$strict>;
|
|
99
|
+
export type OptionSpecT = z.output<typeof OptionSpec>;
|
|
100
|
+
export type ArgSpecT = z.output<typeof ArgSpec>;
|
|
101
|
+
export type CommandSpecT = z.output<typeof CommandSpec>;
|
|
102
|
+
export declare const COMMANDS: CommandSpecT[];
|
|
103
|
+
export declare const commandSpec: (name: string) => CommandSpecT | undefined;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Every command, defined once: name, group, arguments and options with their types. args.ts builds the commander
|
|
2
|
+
// parser from it and menu.ts builds the interactive menu from it, so a command added here works both ways.
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
const Choices = z.union([z.array(z.string()), z.enum(['harnesses', 'accounts', 'sessions'])])
|
|
5
|
+
.describe('Fixed values, or a list the menu fills in at run time.');
|
|
6
|
+
export const OptionSpec = z.strictObject({
|
|
7
|
+
flag: z.string().regex(/^--[a-z][a-z0-9-]*$/).describe('Long flag, e.g. --terminal.'),
|
|
8
|
+
short: z.string().regex(/^-[a-zA-Z]$/).optional(),
|
|
9
|
+
type: z.enum(['boolean', 'string', 'number', 'list']).describe('list = repeatable string option.'),
|
|
10
|
+
value: z.string().optional().describe('Placeholder shown in help, e.g. name for --account <name>.'),
|
|
11
|
+
description: z.string(),
|
|
12
|
+
default: z.union([z.string(), z.number()]).optional(),
|
|
13
|
+
choices: Choices.optional(),
|
|
14
|
+
/** Advanced options are only asked in the menu after "Show advanced options". */
|
|
15
|
+
advanced: z.boolean().optional(),
|
|
16
|
+
/** Only asked in the menu when the command's first argument has this value (account save: --from-host). */
|
|
17
|
+
when: z.string().optional(),
|
|
18
|
+
/** Never offered in the menu (dangerous shortcuts such as --yes); typing it on the command line still works. */
|
|
19
|
+
noMenu: z.boolean().optional(),
|
|
20
|
+
});
|
|
21
|
+
export const ArgSpec = z.strictObject({
|
|
22
|
+
name: z.string(),
|
|
23
|
+
description: z.string(),
|
|
24
|
+
required: z.boolean(),
|
|
25
|
+
variadic: z.boolean().optional(),
|
|
26
|
+
choices: Choices.optional(),
|
|
27
|
+
/** Only asked in the menu when the command's first argument has this value, or one of these (data clean: targets). */
|
|
28
|
+
when: z.union([z.string(), z.array(z.string())]).optional(),
|
|
29
|
+
});
|
|
30
|
+
/** Is an argument or option with this `when` asked, given the first argument's answer? */
|
|
31
|
+
export const askedWhen = (when, first) => when === undefined || (Array.isArray(when) ? when.includes(first) : when === first);
|
|
32
|
+
export const Group = z.enum(['Agents', 'Sandbox', 'Sessions', 'Accounts', 'Data', 'Health', 'Config']);
|
|
33
|
+
export const CommandSpec = z.strictObject({
|
|
34
|
+
name: z.string(),
|
|
35
|
+
summary: z.string(),
|
|
36
|
+
group: Group,
|
|
37
|
+
args: z.array(ArgSpec),
|
|
38
|
+
options: z.array(OptionSpec),
|
|
39
|
+
/** Unknown options and extra arguments are passed on to the agent (launch) or the program (exec). */
|
|
40
|
+
passthrough: z.enum(['agent', 'command']).optional(),
|
|
41
|
+
json: z.boolean().optional(),
|
|
42
|
+
/** Takes over the terminal or ends the session: the menu does not come back afterwards. */
|
|
43
|
+
interactive: z.boolean().optional(),
|
|
44
|
+
hidden: z.boolean().optional(),
|
|
45
|
+
});
|
|
46
|
+
const bool = (flag, description, extra = {}) => ({ flag, type: 'boolean', description, ...extra });
|
|
47
|
+
const text = (flag, value, description, extra = {}) => ({ flag, type: 'string', value, description, ...extra });
|
|
48
|
+
const SANDBOX_OPTIONS = [
|
|
49
|
+
bool('--terminal', 'attach this terminal (reuses this folder\'s session if nobody is watching it)', { short: '-t' }),
|
|
50
|
+
bool('--headless', 'hide the sandbox window (sbx-show brings it back)'),
|
|
51
|
+
bool('--new', 'always start a new session, even if this folder has an unattached one'),
|
|
52
|
+
text('--workspace', 'dir', 'use this folder instead of the current one'),
|
|
53
|
+
text('--node', 'version', 'Node version for nvm (default: .sandbox-config.json > .nvmrc > this PC)', { advanced: true }),
|
|
54
|
+
text('--python', 'version', 'Python 3 version (default: .sandbox-config.json > .python-version > config)', { advanced: true }),
|
|
55
|
+
{ flag: '--database', type: 'list', value: 'name', description: 'also start this database (postgres, mysql, mongodb, redis, dynamodb; repeatable)', choices: ['postgres', 'mysql', 'mongodb', 'redis', 'dynamodb'], advanced: true },
|
|
56
|
+
{ flag: '--script', type: 'list', value: 'file', description: 'run this .ps1/.mjs/.cmd inside the sandbox first (repeatable)', advanced: true },
|
|
57
|
+
bool('--window', 'show the sandbox window even if config says headless', { advanced: true }),
|
|
58
|
+
bool('--trust-config', 'approve the folder\'s .sandbox-config.json without asking', { advanced: true }),
|
|
59
|
+
bool('--no-local-config', 'ignore the folder\'s .sandbox-config.json', { advanced: true }),
|
|
60
|
+
bool('--yes', 'when this folder is not in the running sandbox and adding it needs a restart, restart without asking', { noMenu: true }),
|
|
61
|
+
];
|
|
62
|
+
const AGENT_OPTIONS = [
|
|
63
|
+
text('--harness', 'name', 'agent to run', { choices: 'harnesses' }),
|
|
64
|
+
{ flag: '--session', type: 'list', value: 'agent@folder', description: 'start several sessions at once, e.g. --session claude@C:\\code\\api --session "codex@C:\\code\\web|--model o3" (repeatable; after | = that agent\'s own arguments; -t attaches the first)', noMenu: true },
|
|
65
|
+
text('--account', 'name', 'Claude account for this sandbox', { choices: 'accounts' }),
|
|
66
|
+
...SANDBOX_OPTIONS,
|
|
67
|
+
bool('--no-agent', 'set up the sandbox but do not start an agent', { advanced: true }),
|
|
68
|
+
];
|
|
69
|
+
const json = true;
|
|
70
|
+
export const COMMANDS = z.array(CommandSpec).parse([
|
|
71
|
+
{ name: 'launch', summary: 'start the sandbox and an agent (or another session in the running sandbox)', group: 'Agents', args: [], options: AGENT_OPTIONS, passthrough: 'agent', interactive: true },
|
|
72
|
+
{ name: 'start', summary: 'start a sandbox with no agent (tools and databases only)', group: 'Sandbox', args: [], options: SANDBOX_OPTIONS.filter((o) => o.flag !== '--new'), interactive: true },
|
|
73
|
+
{ name: 'generate', summary: 'show what a start would do, without starting anything', group: 'Health', args: [], options: AGENT_OPTIONS, passthrough: 'agent' },
|
|
74
|
+
{ name: 'attach', summary: 'show a running session in this terminal (Ctrl+] detaches)', group: 'Sessions', args: [{ name: 'id', description: 'session (default: the newest)', required: false, choices: 'sessions' }], options: [], interactive: true },
|
|
75
|
+
{ name: 'sessions', summary: 'list agent sessions and who is attached', group: 'Sessions', args: [], options: [], json },
|
|
76
|
+
{ name: 'status', summary: 'show what the sandbox is doing', group: 'Sandbox', args: [], options: [], json },
|
|
77
|
+
{ name: 'db', summary: 'list, start or stop databases in the running sandbox', group: 'Sandbox', args: [{ name: 'action', description: 'list, start or stop', required: false, choices: ['list', 'start', 'stop'] }, { name: 'name', description: 'postgres, mysql, mongodb, redis or dynamodb', required: false, choices: ['postgres', 'mysql', 'mongodb', 'redis', 'dynamodb'] }], options: [] },
|
|
78
|
+
{ name: 'exec', summary: 'run a command inside the sandbox and print its output', group: 'Sandbox', args: [{ name: 'command', description: 'command and its arguments', required: true, variadic: true }], options: [{ flag: '--timeout', type: 'number', value: 'seconds', description: 'give up after this many seconds', default: 600 }, text('--workspace', 'dir', 'folder (inside the mapped workspace) to run in')], passthrough: 'command', json },
|
|
79
|
+
{ name: 'restart', summary: 'restart this project\'s running agents in place; they resume their conversation', group: 'Sessions', args: [], options: [text('--harness', 'name', 'only this agent', { choices: 'harnesses' }), bool('--all', 'every project\'s sessions, not only this folder\'s')] },
|
|
80
|
+
{ name: 'stop', summary: 'shut the sandbox down (every project\'s sessions end)', group: 'Sandbox', args: [], options: [bool('--yes', 'do not ask when other projects\' sessions would end', { noMenu: true })] },
|
|
81
|
+
{
|
|
82
|
+
name: 'projects', summary: 'the projects a sandbox can open without a restart: list, choose, roots, all/exclude', group: 'Sandbox', json,
|
|
83
|
+
args: [
|
|
84
|
+
{ name: 'action', description: 'list, choose, add, remove, include, exclude, all, roots', required: false, choices: ['list', 'choose', 'add', 'remove', 'include', 'exclude', 'all', 'roots'] },
|
|
85
|
+
{ name: 'value', description: 'a folder (add, remove, include, exclude), on/off (all), or list/add/remove (roots)', required: false, when: ['add', 'remove', 'include', 'exclude', 'all', 'roots'] },
|
|
86
|
+
{ name: 'folder', description: '(roots add/remove) the folder', required: false, when: 'roots' },
|
|
87
|
+
],
|
|
88
|
+
options: [],
|
|
89
|
+
},
|
|
90
|
+
{ name: 'show', summary: 'show the sandbox window', group: 'Sandbox', args: [], options: [] },
|
|
91
|
+
{ name: 'hide', summary: 'hide the sandbox window (it keeps running)', group: 'Sandbox', args: [], options: [] },
|
|
92
|
+
{ name: 'account', summary: 'Claude logins: list, save, use, set the default, remove', group: 'Accounts', args: [{ name: 'action', description: 'what to do', required: false, choices: ['list', 'save', 'use', 'default', 'remove'] }, { name: 'name', description: 'account name', required: false, choices: 'accounts' }], options: [bool('--from-host', '(save) save this PC\'s login instead of the sandbox\'s', { when: 'save' }), bool('--no-restart', '(use) do not restart running sessions', { when: 'use' })], json },
|
|
93
|
+
{ name: 'data', summary: 'show what is kept between sandboxes (tools, databases, logins, logs), or delete some of it', group: 'Data', args: [{ name: 'action', description: 'show or clean', required: false, choices: ['show', 'clean'] }, { name: 'targets', description: 'what to clean, e.g. tools/go databases logs', required: false, variadic: true, when: 'clean' }], options: [bool('--yes', 'do not ask before deleting', { noMenu: true })], json },
|
|
94
|
+
{ name: 'clean', summary: 'delete old launch folders, logs and leftovers', group: 'Data', args: [], options: [{ flag: '--days', type: 'number', value: 'n', description: 'launch folders older than this many days', default: 7 }] },
|
|
95
|
+
{ name: 'sync', summary: 'copy conversation history back to this PC now', group: 'Data', args: [], options: [] },
|
|
96
|
+
{ name: 'logs', summary: 'print the newest sandbox log', group: 'Health', args: [], options: [bool('--follow', 'keep printing new lines', { short: '-f' })] },
|
|
97
|
+
{ name: 'setup', summary: 'check this PC can run Windows Sandbox and offer to turn it on', group: 'Health', args: [], options: [] },
|
|
98
|
+
{ name: 'doctor', summary: 'check this PC and show what will be copied or downloaded', group: 'Health', args: [], options: [text('--harness', 'name', 'agent to check', { choices: 'harnesses' })] },
|
|
99
|
+
{ name: 'repair', summary: 'find and fix common problems (stale state, broken downloads, half-installed tools)', group: 'Health', args: [], options: [] },
|
|
100
|
+
{ name: 'self-test', summary: 'run the built-in tests', group: 'Health', args: [{ name: 'suite', description: 'run only these test suites, e.g. config live-daemon (default: all)', required: false, variadic: true }], options: [] },
|
|
101
|
+
{ name: 'config', summary: 'show or create your settings file, or check a project\'s .sandbox-config.json', group: 'Config', args: [{ name: 'action', description: 'what to do', required: false, choices: ['show', 'path', 'init', 'schema', 'check'] }], options: [text('--workspace', 'dir', '(check) the project folder; default: the current one')] },
|
|
102
|
+
{ name: 'agent-commands', summary: '(Beta) make claude, codex ... on this PC start in a hidden sandbox and attach', group: 'Agents', args: [{ name: 'action', description: 'on, off or status', required: false, choices: ['status', 'on', 'off'] }], options: [] },
|
|
103
|
+
{ name: 'host-claude', summary: 'run Claude on this PC, NOT sandboxed, with its own profile', group: 'Agents', args: [], options: [bool('--personal', 'use your normal Claude profile')], passthrough: 'agent', interactive: true },
|
|
104
|
+
{ name: 'menu', summary: 'the interactive menu (what `sandbox` opens with no arguments)', group: 'Config', args: [], options: [], hidden: true, interactive: true },
|
|
105
|
+
{ name: '_watch', summary: 'background history sync', group: 'Data', args: [], options: [], hidden: true },
|
|
106
|
+
]);
|
|
107
|
+
export const commandSpec = (name) => COMMANDS.find((c) => c.name === name);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Context } from './commands/context.js';
|
|
2
|
+
/** Folders worth offering: this one, the projects started before, and the folders inside your roots. */
|
|
3
|
+
export declare const candidateFolders: (ctx: Context, cwd?: string) => string[];
|
|
4
|
+
/** The command line for several sessions: one --session per (agent, folder), with the shared options. */
|
|
5
|
+
export declare const severalAgentsArgv: (sessions: {
|
|
6
|
+
harness: string;
|
|
7
|
+
folder: string;
|
|
8
|
+
}[], o: {
|
|
9
|
+
headless: boolean;
|
|
10
|
+
attachFirst: boolean;
|
|
11
|
+
}) => string[];
|
|
12
|
+
export declare const askSeveralAgents: (ctx: Context) => Promise<string[] | null>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// The menu's "Start several agents": projects, then agents for each, then options; it only builds a command line
|
|
2
|
+
// (sandbox launch --session <agent>@<folder> ...), so "repeat last" works and typing it does the same.
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { checkbox, confirm, input, select } from '@inquirer/prompts';
|
|
6
|
+
import { assertSafeWorkspace } from '../core/safe-paths.js';
|
|
7
|
+
import { expandPath, errMsg } from '../core/util.js';
|
|
8
|
+
import { readKnown } from '../host/projects.js';
|
|
9
|
+
import { isWithin } from '../shared/projects.js';
|
|
10
|
+
import { c, pageSizeFor, shortenMiddle, termWidth } from './colors.js';
|
|
11
|
+
import { commandLine } from './history.js';
|
|
12
|
+
const ADD = '@add';
|
|
13
|
+
/** Folders worth offering: this one, the projects started before, and the folders inside your roots. */
|
|
14
|
+
export const candidateFolders = (ctx, cwd = process.cwd()) => {
|
|
15
|
+
const roots = ctx.loaded.config.projects.roots.map((r) => expandPath(r, ctx.loaded.base));
|
|
16
|
+
const inRoots = roots.flatMap((r) => { try {
|
|
17
|
+
return fs.readdirSync(r, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.isSymbolicLink() && !e.name.startsWith('.')).slice(0, 200).map((e) => path.join(r, e.name));
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return [];
|
|
21
|
+
} });
|
|
22
|
+
const all = [path.resolve(cwd), ...readKnown(ctx.storageRoot).map((k) => k.host), ...inRoots];
|
|
23
|
+
return all.filter((f, i) => all.findIndex((g) => g.toLowerCase() === f.toLowerCase()) === i && fs.existsSync(f));
|
|
24
|
+
};
|
|
25
|
+
/** The command line for several sessions: one --session per (agent, folder), with the shared options. */
|
|
26
|
+
export const severalAgentsArgv = (sessions, o) => ['launch', ...sessions.flatMap((s) => ['--session', s.harness + '@' + s.folder]), ...(o.headless ? ['--headless'] : []), ...(o.attachFirst ? ['-t'] : [])];
|
|
27
|
+
export const askSeveralAgents = async (ctx) => {
|
|
28
|
+
const here = path.resolve(process.cwd());
|
|
29
|
+
const width = Math.max(20, termWidth() - 14);
|
|
30
|
+
let folders = await checkbox({
|
|
31
|
+
message: 'Which projects? (each gets its sessions; all of them are mapped into one sandbox)',
|
|
32
|
+
pageSize: pageSizeFor(16),
|
|
33
|
+
choices: [
|
|
34
|
+
...candidateFolders(ctx).map((f) => ({ value: f, name: shortenMiddle(f, width) + (isWithin(f, here) && isWithin(here, f) ? c.dim(' (this folder)') : ''), checked: f.toLowerCase() === here.toLowerCase() })),
|
|
35
|
+
{ value: ADD, name: 'Add a folder...' },
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
if (folders.includes(ADD)) {
|
|
39
|
+
const extra = await input({
|
|
40
|
+
message: 'Folder to add:',
|
|
41
|
+
validate: (v) => {
|
|
42
|
+
const dir = path.resolve(v.trim());
|
|
43
|
+
if (!v.trim() || !fs.existsSync(dir) || !fs.statSync(dir).isDirectory())
|
|
44
|
+
return 'Not a folder: ' + dir;
|
|
45
|
+
try {
|
|
46
|
+
assertSafeWorkspace(dir, ctx.packageRoot, ctx.storageRoot);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
return errMsg(e);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
folders = [...folders.filter((f) => f !== ADD), path.resolve(extra.trim())];
|
|
55
|
+
}
|
|
56
|
+
if (!folders.length)
|
|
57
|
+
return null;
|
|
58
|
+
const agents = Object.keys(ctx.loaded.config.harnesses);
|
|
59
|
+
const sessions = [];
|
|
60
|
+
for (const folder of folders) {
|
|
61
|
+
const picked = await checkbox({
|
|
62
|
+
message: 'Agents for ' + shortenMiddle(folder, width - 10) + ' (one session each):',
|
|
63
|
+
pageSize: pageSizeFor(agents.length),
|
|
64
|
+
choices: agents.map((a) => ({ value: a, name: a, checked: a === ctx.loaded.config.defaultHarness })),
|
|
65
|
+
required: true,
|
|
66
|
+
});
|
|
67
|
+
sessions.push(...picked.map((harness) => ({ harness, folder })));
|
|
68
|
+
}
|
|
69
|
+
const headless = await confirm({ message: 'Hide the sandbox window (headless)?', default: ctx.loaded.config.sandbox.headless });
|
|
70
|
+
const attach = await select({
|
|
71
|
+
message: 'Attach this terminal to:',
|
|
72
|
+
choices: [{ name: 'none (each has a tab inside; sandbox attach shows any of them)', value: -1 }, ...sessions.map((s, i) => ({ name: s.harness + ' in ' + shortenMiddle(s.folder, width - 20), value: i }))],
|
|
73
|
+
default: 0,
|
|
74
|
+
});
|
|
75
|
+
// The one to attach goes first: -t attaches the first session.
|
|
76
|
+
const ordered = attach >= 0 ? [sessions[attach], ...sessions.filter((_s, i) => i !== attach)] : sessions;
|
|
77
|
+
const projects = [...new Set(sessions.map((s) => path.basename(s.folder)))];
|
|
78
|
+
console.log('Start 1 sandbox with ' + sessions.length + ' session' + (sessions.length === 1 ? '' : 's') + ': ' + ordered.map((s) => s.harness + ' in ' + path.basename(s.folder)).join(', ') + ' - map ' + projects.join(' and '));
|
|
79
|
+
const argv = severalAgentsArgv(ordered, { headless, attachFirst: attach >= 0 });
|
|
80
|
+
console.log(c.dim(' ' + commandLine(argv)));
|
|
81
|
+
return await confirm({ message: 'Start them?', default: true }) ? argv : null;
|
|
82
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Context } from './commands/context.js';
|
|
2
|
+
import { splitArgs } from './args.js';
|
|
3
|
+
import { type CommandSpecT, type OptionSpecT } from './manifest.js';
|
|
4
|
+
/** Splits "a b 'c d'" like a shell would (enough for agent arguments and exec commands). */
|
|
5
|
+
export { splitArgs };
|
|
6
|
+
/** What this option accepts, checked while typing (undefined = anything). */
|
|
7
|
+
export declare const optionProblem: (o: OptionSpecT, v: string) => string | undefined;
|
|
8
|
+
/** Asks everything a command takes and returns its argv (or null when the user backs out). */
|
|
9
|
+
export declare const askCommand: (ctx: Context, spec: CommandSpecT, preset?: string[]) => Promise<string[] | null>;
|
|
10
|
+
/**
|
|
11
|
+
* Runs the menu until Exit, or until a command that takes over the terminal (launch, attach) has run. `run` executes
|
|
12
|
+
* an argv exactly like typing it.
|
|
13
|
+
*/
|
|
14
|
+
export declare const menu: (ctx: Context, run: (argv: string[]) => Promise<number>) => Promise<number>;
|