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,55 @@
|
|
|
1
|
+
import type { GitAccess } from '../../core/types.js';
|
|
2
|
+
export type Arch = 'x64' | 'arm64';
|
|
3
|
+
/** PowerShell/PowerShell: the portable zip (PowerShell-7.6.6-win-x64.zip), never the MSI or the fxdependent builds. */
|
|
4
|
+
export declare const pwshAsset: (arch: Arch) => RegExp;
|
|
5
|
+
/** cargo-bins/cargo-binstall: the plain zip (the .full build adds nothing we use; .sig files are signatures). */
|
|
6
|
+
export declare const binstallAsset: (arch: Arch) => RegExp;
|
|
7
|
+
/**
|
|
8
|
+
* One portable command-line tool from a GitHub release. The archive is unpacked into tools\cli\<name>; without
|
|
9
|
+
* `subdir` only the folder holding `exe` is kept (and goes on PATH), with `subdir` the whole archive stays and that
|
|
10
|
+
* folder inside it goes on PATH (for tools whose exe needs files next to it).
|
|
11
|
+
*/
|
|
12
|
+
export interface CliTool {
|
|
13
|
+
name: string;
|
|
14
|
+
repo: string;
|
|
15
|
+
asset: (arch: Arch) => RegExp;
|
|
16
|
+
exe: string;
|
|
17
|
+
subdir?: string;
|
|
18
|
+
/** What the tool is for (summary and docs). */
|
|
19
|
+
about: string;
|
|
20
|
+
/** Rough download size for the progress line. */
|
|
21
|
+
size: string;
|
|
22
|
+
}
|
|
23
|
+
export declare const CLI_TOOLS: readonly [{
|
|
24
|
+
readonly name: "gh";
|
|
25
|
+
readonly repo: "cli/cli";
|
|
26
|
+
readonly asset: (arch: Arch) => RegExp;
|
|
27
|
+
readonly exe: "gh.exe";
|
|
28
|
+
readonly about: "GitHub CLI";
|
|
29
|
+
readonly size: "15 MB";
|
|
30
|
+
}, {
|
|
31
|
+
readonly name: "zstd";
|
|
32
|
+
readonly repo: "facebook/zstd";
|
|
33
|
+
readonly asset: () => RegExp;
|
|
34
|
+
readonly exe: "zstd.exe";
|
|
35
|
+
readonly about: "Zstandard compression";
|
|
36
|
+
readonly size: "2 MB";
|
|
37
|
+
}];
|
|
38
|
+
export type CliName = (typeof CLI_TOOLS)[number]['name'] | 'openssl';
|
|
39
|
+
export declare const CLI_NAMES: readonly CliName[];
|
|
40
|
+
/** toolchains.cli: each tool true, false or "auto" (gh: when the project has a .github folder; zstd: never by itself). */
|
|
41
|
+
export type CliSettings = Partial<Record<CliName, boolean | 'auto'>>;
|
|
42
|
+
export interface CliContext {
|
|
43
|
+
gitAccess: GitAccess;
|
|
44
|
+
/** The workspace has a .github folder (workflows, CODEOWNERS ...). */
|
|
45
|
+
hasGithubDir: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface CliDecision {
|
|
48
|
+
on: boolean;
|
|
49
|
+
reason: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Which catalogue tools a sandbox gets. gh is never installed unless git access is "full": gh pushes, merges and edits
|
|
53
|
+
* branches through the GitHub API, so it would walk straight past a "readonly" (or "none") git guard.
|
|
54
|
+
*/
|
|
55
|
+
export declare const decideCli: (settings: CliSettings, ctx: CliContext) => Record<CliName, CliDecision>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** PowerShell/PowerShell: the portable zip (PowerShell-7.6.6-win-x64.zip), never the MSI or the fxdependent builds. */
|
|
2
|
+
export const pwshAsset = (arch) => (arch === 'arm64' ? /^PowerShell-\d+\.\d+\.\d+-win-arm64\.zip$/ : /^PowerShell-\d+\.\d+\.\d+-win-x64\.zip$/);
|
|
3
|
+
/** cargo-bins/cargo-binstall: the plain zip (the .full build adds nothing we use; .sig files are signatures). */
|
|
4
|
+
export const binstallAsset = (arch) => (arch === 'arm64' ? /^cargo-binstall-aarch64-pc-windows-msvc\.zip$/ : /^cargo-binstall-x86_64-pc-windows-msvc\.zip$/);
|
|
5
|
+
export const CLI_TOOLS = [
|
|
6
|
+
// gh_2.102.0_windows_amd64.zip holds bin\gh.exe.
|
|
7
|
+
{ name: 'gh', repo: 'cli/cli', asset: (arch) => (arch === 'arm64' ? /^gh_\d+\.\d+\.\d+_windows_arm64\.zip$/ : /^gh_\d+\.\d+\.\d+_windows_amd64\.zip$/), exe: 'gh.exe', about: 'GitHub CLI', size: '15 MB' },
|
|
8
|
+
// zstd-v1.5.7-win64.zip; there is no arm64 build, and Windows on Arm runs the x64 one.
|
|
9
|
+
{ name: 'zstd', repo: 'facebook/zstd', asset: () => /^zstd-v\d+\.\d+\.\d+-win64\.zip$/, exe: 'zstd.exe', about: 'Zstandard compression', size: '2 MB' },
|
|
10
|
+
];
|
|
11
|
+
export const CLI_NAMES = [...CLI_TOOLS.map((t) => t.name), 'openssl'];
|
|
12
|
+
/**
|
|
13
|
+
* Which catalogue tools a sandbox gets. gh is never installed unless git access is "full": gh pushes, merges and edits
|
|
14
|
+
* branches through the GitHub API, so it would walk straight past a "readonly" (or "none") git guard.
|
|
15
|
+
*/
|
|
16
|
+
export const decideCli = (settings, ctx) => {
|
|
17
|
+
const decide = (name) => {
|
|
18
|
+
const setting = settings[name] ?? false;
|
|
19
|
+
if (name === 'gh' && setting !== false && ctx.gitAccess !== 'full')
|
|
20
|
+
return { on: false, reason: 'needs provisioning.git.access "full" (gh can push and merge through the GitHub API)' };
|
|
21
|
+
if (setting !== 'auto')
|
|
22
|
+
return { on: setting, reason: setting ? 'enabled in config' : 'disabled in config' };
|
|
23
|
+
if (name === 'gh' && ctx.hasGithubDir)
|
|
24
|
+
return { on: true, reason: '.github' };
|
|
25
|
+
// openssl is only a shim onto Git's own copy: free, so "auto" means on.
|
|
26
|
+
if (name === 'openssl')
|
|
27
|
+
return { on: true, reason: 'Git for Windows ships it' };
|
|
28
|
+
return { on: false, reason: 'not listed in toolchains.cli' };
|
|
29
|
+
};
|
|
30
|
+
return Object.fromEntries(CLI_NAMES.map((name) => [name, decide(name)]));
|
|
31
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { GitAccess } from '../../core/types.js';
|
|
2
|
+
import { type CliSettings } from './catalogue.js';
|
|
3
|
+
import { type ToolEnv } from './common.js';
|
|
4
|
+
/** gh keeps `gh auth login` here, under the agent state (persists like Claude's own login; never copied from the PC). */
|
|
5
|
+
export declare const ghConfigDir: () => string;
|
|
6
|
+
/**
|
|
7
|
+
* Installs the tools toolchains.cli asks for. Settings still "auto" here (normally the host decides) use the same
|
|
8
|
+
* rules on the workspace as the host would. The gh rule is enforced here again, whatever the host sent.
|
|
9
|
+
*/
|
|
10
|
+
export declare const setupCli: (env: ToolEnv, settings: CliSettings, gitAccess: GitAccess, refresh?: boolean) => Promise<void>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Small portable command-line tools (toolchains.cli): one table entry each instead of one step per binary, plus an
|
|
2
|
+
// `openssl` shim onto the copy Git for Windows already ships.
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { dryRun, errMsg, exists, log, mkdirp, stateDir, warn } from '../env.js';
|
|
6
|
+
import { projectWorkspace } from '../project-context.js';
|
|
7
|
+
import { CLI_TOOLS, decideCli } from './catalogue.js';
|
|
8
|
+
import { arch, tool } from './common.js';
|
|
9
|
+
import { setupSingleExe } from './runtimes.js';
|
|
10
|
+
import { ensureCompiled } from '../helpers.js';
|
|
11
|
+
/** gh keeps `gh auth login` here, under the agent state (persists like Claude's own login; never copied from the PC). */
|
|
12
|
+
export const ghConfigDir = () => path.join(stateDir, 'gh');
|
|
13
|
+
const toolVars = {
|
|
14
|
+
gh: () => ({ GH_CONFIG_DIR: mkdirp(ghConfigDir()), GH_NO_UPDATE_NOTIFIER: '1', GH_NO_EXTENSION_UPDATE_NOTIFIER: '1' }),
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* tools\shims\openssl.exe (sandbox/openssl.cs), a launcher for Git's mingw64\bin\openssl.exe. A real .exe so programs
|
|
18
|
+
* that call CreateProcess directly (Rust's Command::new("openssl")) find it, and only the shim is on PATH: mingw64\bin
|
|
19
|
+
* itself would bring Git's runtime DLLs along and could shadow MinGW's gcc from the C/C++ step.
|
|
20
|
+
*/
|
|
21
|
+
const shimOpenssl = (env) => {
|
|
22
|
+
if (!exists(tool('git', 'mingw64', 'bin', 'openssl.exe'))) {
|
|
23
|
+
env.summary.push('openssl: not in this Git for Windows (skipped)');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const shims = mkdirp(tool('shims'));
|
|
27
|
+
fs.copyFileSync(ensureCompiled('openssl'), path.join(shims, 'openssl.exe'));
|
|
28
|
+
// An earlier version wrote a .cmd; it would still win for shells that prefer it.
|
|
29
|
+
fs.rmSync(path.join(shims, 'openssl.cmd'), { force: true });
|
|
30
|
+
env.prepend.push(shims);
|
|
31
|
+
env.summary.push('openssl: Git for Windows\' copy');
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Installs the tools toolchains.cli asks for. Settings still "auto" here (normally the host decides) use the same
|
|
35
|
+
* rules on the workspace as the host would. The gh rule is enforced here again, whatever the host sent.
|
|
36
|
+
*/
|
|
37
|
+
export const setupCli = async (env, settings, gitAccess, refresh = false) => {
|
|
38
|
+
const decisions = decideCli(settings, { gitAccess, hasGithubDir: exists(path.join(projectWorkspace(), '.github')) });
|
|
39
|
+
if (settings.gh && !decisions.gh.on) {
|
|
40
|
+
env.summary.push('gh: off (' + decisions.gh.reason + ')');
|
|
41
|
+
// A login from an earlier full-access sandbox is still on disk; the agent could read the token from there.
|
|
42
|
+
if (exists(path.join(ghConfigDir(), 'hosts.yml')))
|
|
43
|
+
warn('gh is off (git access is not "full"), but an earlier gh login is still stored in ' + ghConfigDir() + ' and readable by the agent. Run `gh auth logout` in a full-access sandbox, or delete that folder.');
|
|
44
|
+
}
|
|
45
|
+
if (dryRun) {
|
|
46
|
+
log('(dry run) would provision CLI tools: ' + Object.entries(decisions).filter(([, d]) => d.on).map(([n]) => n).join(', '));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
for (const entry of CLI_TOOLS) {
|
|
50
|
+
const name = entry.name;
|
|
51
|
+
if (!decisions[name].on)
|
|
52
|
+
continue;
|
|
53
|
+
try {
|
|
54
|
+
await setupSingleExe(env, name, entry.repo, entry.asset(arch), entry.exe, tool('cli', name), { refresh, subdir: entry.subdir, size: entry.size });
|
|
55
|
+
Object.assign(env.vars, toolVars[name]?.() ?? {});
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
// One tool failing never takes the others down.
|
|
59
|
+
warn(name + ' could not be set up and is skipped; the other tools are not affected. Reason: ' + errMsg(e));
|
|
60
|
+
env.summary.push(entry.name + ': FAILED');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (decisions.openssl.on) {
|
|
64
|
+
try {
|
|
65
|
+
shimOpenssl(env);
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
warn('openssl could not be set up and is skipped (Git Bash still has its own). Reason: ' + errMsg(e));
|
|
69
|
+
env.summary.push('openssl: FAILED');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type ReleaseAsset } from '../../shared/download.js';
|
|
2
|
+
/** What the toolchains contribute to the agent's environment. */
|
|
3
|
+
export interface ToolEnv {
|
|
4
|
+
vars: Record<string, string>;
|
|
5
|
+
prepend: string[];
|
|
6
|
+
append: string[];
|
|
7
|
+
summary: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare const emptyEnv: () => ToolEnv;
|
|
10
|
+
export declare const arch: string;
|
|
11
|
+
export declare const tool: (...parts: string[]) => string;
|
|
12
|
+
/** First x.y.z in a tool's `--version` output. */
|
|
13
|
+
export declare const versionOf: (exe: string, args?: string[], options?: {}) => string;
|
|
14
|
+
/**
|
|
15
|
+
* Resolves a version online and remembers it for a day (provisioning.refresh looks again), so most boots make no
|
|
16
|
+
* lookups and an offline boot reuses what was installed last time. After a network failure the remaining lookups go
|
|
17
|
+
* straight to the remembered versions instead of each waiting for the network; other errors (no such version) do not.
|
|
18
|
+
*/
|
|
19
|
+
export declare const resolveCached: (key: string, resolve: () => Promise<string>) => Promise<string>;
|
|
20
|
+
/** A single file download that lands as `file` only when complete (and checksum-verified): a cut-off one never looks finished. */
|
|
21
|
+
export declare const downloadFile: (url: string, file: string, sha256?: string | null) => Promise<void>;
|
|
22
|
+
interface GithubInstall {
|
|
23
|
+
/** The folder the archive is unpacked into (replaced atomically). */
|
|
24
|
+
dest: string;
|
|
25
|
+
/** What the progress line says is being downloaded, with its size; a function gets the release asset. */
|
|
26
|
+
what: string | ((asset: ReleaseAsset) => string);
|
|
27
|
+
/** Look past the latest release for a matching asset. */
|
|
28
|
+
searchOlder?: boolean;
|
|
29
|
+
/** installArchive's `locate` / `up`: keep only the folder holding this file. */
|
|
30
|
+
locate?: string;
|
|
31
|
+
up?: number;
|
|
32
|
+
}
|
|
33
|
+
/** The newest GitHub release asset matching `pattern`, checksum-verified, unpacked into `dest`. */
|
|
34
|
+
export declare const installFromGithub: (repo: string, pattern: RegExp, o: GithubInstall) => Promise<void>;
|
|
35
|
+
/** Runs one installer; a failure is reported and skipped so it never blocks the other toolchains. */
|
|
36
|
+
export declare const step: (name: string, env: ToolEnv, work: () => Promise<void>) => Promise<void>;
|
|
37
|
+
/** File names in the project's folder and its direct sub-folders ("api/go.mod"); read once per project. */
|
|
38
|
+
export declare const workspaceEntries: () => string[];
|
|
39
|
+
/**
|
|
40
|
+
* true / false as configured. The host already turns "auto" into a decision at launch (core/detect-toolchains.ts);
|
|
41
|
+
* this only applies when a session file still says "auto": the workspace has one of the marker files.
|
|
42
|
+
*/
|
|
43
|
+
export declare const wanted: (setting: boolean | "auto", toolchain: string) => boolean;
|
|
44
|
+
/** The first of these version files in the workspace root, parsed (e.g. .ruby-version "3.3.1"). */
|
|
45
|
+
export declare const versionFromFiles: (files: string[], parse?: (text: string, name: string) => string | null) => {
|
|
46
|
+
spec: string;
|
|
47
|
+
source: string;
|
|
48
|
+
} | null;
|
|
49
|
+
/**
|
|
50
|
+
* Installs each package that is not installed yet (remembered in tools\installed-packages.json under `group`, e.g.
|
|
51
|
+
* "npm@22.11.0"). One failing package is reported and skipped; it is tried again next boot.
|
|
52
|
+
*/
|
|
53
|
+
export declare const installOnce: (env: ToolEnv, group: string, packages: string[], install: (pkg: string) => void | Promise<void>) => Promise<void>;
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Shared plumbing for the toolchain installers. Everything lives in C:\AgentData\tools (persistent), so only the first
|
|
2
|
+
// boot downloads; later boots only put things on PATH.
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { download, githubAsset, installArchive } from '../../shared/download.js';
|
|
7
|
+
import { writeJsonAtomic } from '../../shared/rundir.js';
|
|
8
|
+
import { errMsg, execOutput, exists, log, session, toolsDir, warn } from '../env.js';
|
|
9
|
+
import { projectWorkspace } from '../project-context.js';
|
|
10
|
+
import { MARKERS } from '../../core/detect-toolchains.js';
|
|
11
|
+
import { projectUses, specFromVersionFile } from './versions.js';
|
|
12
|
+
export const emptyEnv = () => ({ vars: {}, prepend: [], append: [], summary: [] });
|
|
13
|
+
export const arch = process.arch === 'arm64' ? 'arm64' : 'x64';
|
|
14
|
+
export const tool = (...parts) => path.join(toolsDir, ...parts);
|
|
15
|
+
/** First x.y.z in a tool's `--version` output. */
|
|
16
|
+
export const versionOf = (exe, args = ['--version'], options = {}) => /\d+\.\d+\.\d+\S*/.exec(execOutput(exe, args, options))?.[0] ?? 'installed';
|
|
17
|
+
/** Small JSON state files in tools\: read with zod (a half-written file is just empty), written atomically. */
|
|
18
|
+
const readState = (file, schema, empty) => {
|
|
19
|
+
try {
|
|
20
|
+
return schema.safeParse(JSON.parse(fs.readFileSync(file, 'utf8').replace(/^/, ''))).data ?? empty;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return empty;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
/** A remembered lookup; plain strings are what older versions wrote (treated as stale, still usable offline). */
|
|
27
|
+
const Resolved = z.record(z.string(), z.union([z.string(), z.strictObject({ value: z.string(), at: z.number() })]));
|
|
28
|
+
const resolvedFile = () => tool('resolved-versions.json');
|
|
29
|
+
const remembered = () => Object.fromEntries(Object.entries(readState(resolvedFile(), Resolved, {})).map(([k, v]) => [k, typeof v === 'string' ? { value: v, at: 0 } : v]));
|
|
30
|
+
/** How long a looked-up version is trusted: boots within a day make no version lookups at all. */
|
|
31
|
+
const FRESH_MS = 24 * 60 * 60 * 1000;
|
|
32
|
+
let offline = false;
|
|
33
|
+
const networkError = (e) => /fetch failed|timed? ?out|aborted|ENOTFOUND|EAI_AGAIN|ECONN|ENETUNREACH|EHOSTUNREACH|HTTP 5\d\d|socket/i.test(errMsg(e) + ' ' + String(e?.cause ?? ''));
|
|
34
|
+
/**
|
|
35
|
+
* Resolves a version online and remembers it for a day (provisioning.refresh looks again), so most boots make no
|
|
36
|
+
* lookups and an offline boot reuses what was installed last time. After a network failure the remaining lookups go
|
|
37
|
+
* straight to the remembered versions instead of each waiting for the network; other errors (no such version) do not.
|
|
38
|
+
*/
|
|
39
|
+
export const resolveCached = async (key, resolve) => {
|
|
40
|
+
const cached = remembered()[key];
|
|
41
|
+
if (cached && (offline || (!session.provisioning.refresh && Date.now() - cached.at < FRESH_MS)))
|
|
42
|
+
return cached.value;
|
|
43
|
+
try {
|
|
44
|
+
const value = await resolve();
|
|
45
|
+
fs.mkdirSync(toolsDir, { recursive: true });
|
|
46
|
+
writeJsonAtomic(resolvedFile(), { ...remembered(), [key]: { value, at: Date.now() } });
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
if (networkError(e))
|
|
51
|
+
offline = true;
|
|
52
|
+
if (!cached)
|
|
53
|
+
throw e;
|
|
54
|
+
warn('Could not look up the latest ' + key + ' online (' + errMsg(e) + '); using ' + cached.value + ' from an earlier start.');
|
|
55
|
+
return cached.value;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/** A single file download that lands as `file` only when complete (and checksum-verified): a cut-off one never looks finished. */
|
|
59
|
+
export const downloadFile = async (url, file, sha256 = null) => {
|
|
60
|
+
const part = file + '.part';
|
|
61
|
+
try {
|
|
62
|
+
await download(url, part, sha256);
|
|
63
|
+
fs.rmSync(file, { force: true });
|
|
64
|
+
fs.renameSync(part, file);
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
fs.rmSync(part, { force: true });
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
/** The newest GitHub release asset matching `pattern`, checksum-verified, unpacked into `dest`. */
|
|
71
|
+
export const installFromGithub = async (repo, pattern, o) => {
|
|
72
|
+
const asset = await githubAsset(repo, pattern, o.searchOlder);
|
|
73
|
+
log('Downloading ' + (typeof o.what === 'string' ? o.what : o.what(asset)) + ' ...');
|
|
74
|
+
await installArchive(asset.url, o.dest, { sha256: asset.sha256, fileName: asset.name, ...(o.locate ? { locate: o.locate } : {}), ...(o.up ? { up: o.up } : {}) });
|
|
75
|
+
};
|
|
76
|
+
/** Runs one installer; a failure is reported and skipped so it never blocks the other toolchains. */
|
|
77
|
+
export const step = async (name, env, work) => {
|
|
78
|
+
try {
|
|
79
|
+
await work();
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
warn(name + ' could not be set up and is skipped; everything else still works, and the next start tries again. Reason: ' + errMsg(e));
|
|
83
|
+
env.summary.push(name + ': FAILED');
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
// ---- which toolchains a project needs ------------------------------------------------------------------------------
|
|
87
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', 'target', 'bin', 'obj', 'dist', 'build', 'vendor', '.venv', 'venv']);
|
|
88
|
+
const entries = new Map();
|
|
89
|
+
/** File names in the project's folder and its direct sub-folders ("api/go.mod"); read once per project. */
|
|
90
|
+
export const workspaceEntries = () => {
|
|
91
|
+
const workspace = projectWorkspace();
|
|
92
|
+
const known = entries.get(workspace.toLowerCase());
|
|
93
|
+
if (known)
|
|
94
|
+
return known;
|
|
95
|
+
const list = (dir) => { try {
|
|
96
|
+
return fs.readdirSync(dir, { withFileTypes: true });
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return [];
|
|
100
|
+
} };
|
|
101
|
+
const top = list(workspace);
|
|
102
|
+
const below = top.filter((d) => d.isDirectory() && !d.name.startsWith('.') && !SKIP_DIRS.has(d.name.toLowerCase()))
|
|
103
|
+
.flatMap((d) => list(path.join(workspace, d.name)).filter((f) => f.isFile()).map((f) => d.name + '/' + f.name));
|
|
104
|
+
const found = [...top.filter((d) => d.isFile()).map((d) => d.name), ...below];
|
|
105
|
+
entries.set(workspace.toLowerCase(), found);
|
|
106
|
+
return found;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* true / false as configured. The host already turns "auto" into a decision at launch (core/detect-toolchains.ts);
|
|
110
|
+
* this only applies when a session file still says "auto": the workspace has one of the marker files.
|
|
111
|
+
*/
|
|
112
|
+
export const wanted = (setting, toolchain) => setting === 'auto' ? projectUses(workspaceEntries(), MARKERS[toolchain] ?? []) : setting;
|
|
113
|
+
/** The first of these version files in the workspace root, parsed (e.g. .ruby-version "3.3.1"). */
|
|
114
|
+
export const versionFromFiles = (files, parse = (t) => specFromVersionFile(t)) => {
|
|
115
|
+
for (const name of files) {
|
|
116
|
+
const file = path.join(projectWorkspace(), name);
|
|
117
|
+
if (!exists(file))
|
|
118
|
+
continue;
|
|
119
|
+
const spec = parse(fs.readFileSync(file, 'utf8'), name);
|
|
120
|
+
if (spec)
|
|
121
|
+
return { spec, source: name };
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
};
|
|
125
|
+
// ---- global packages, installed once -------------------------------------------------------------------------------
|
|
126
|
+
const installedFile = () => tool('installed-packages.json');
|
|
127
|
+
const installed = () => readState(installedFile(), z.record(z.string(), z.string()), {});
|
|
128
|
+
/**
|
|
129
|
+
* Installs each package that is not installed yet (remembered in tools\installed-packages.json under `group`, e.g.
|
|
130
|
+
* "npm@22.11.0"). One failing package is reported and skipped; it is tried again next boot.
|
|
131
|
+
*/
|
|
132
|
+
export const installOnce = async (env, group, packages, install) => {
|
|
133
|
+
const todo = packages.filter((p) => !installed()[group + ':' + p]);
|
|
134
|
+
for (const pkg of todo) {
|
|
135
|
+
try {
|
|
136
|
+
log('Installing ' + pkg + ' (' + group + ') ...');
|
|
137
|
+
await install(pkg);
|
|
138
|
+
writeJsonAtomic(installedFile(), { ...installed(), [group + ':' + pkg]: new Date().toISOString() });
|
|
139
|
+
}
|
|
140
|
+
catch (e) {
|
|
141
|
+
warn(pkg + ' (' + group + ') could not be installed and is skipped; the next start tries again. Reason: ' + errMsg(e));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const ok = packages.filter((p) => installed()[group + ':' + p]);
|
|
145
|
+
if (ok.length)
|
|
146
|
+
env.summary.push(group.split('@')[0] + ' packages: ' + ok.join(', '));
|
|
147
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DatabasePlan } from '../../../core/types.js';
|
|
2
|
+
import { type ToolEnv } from '../common.js';
|
|
3
|
+
/**
|
|
4
|
+
* Amazon's DynamoDB Local (a Java program; the Java toolchain step runs first, else this installs the LTS JDK) and
|
|
5
|
+
* dynamodb-admin, the web UI to browse and edit tables, installed with the runner's own npm. Every AWS SDK picks the
|
|
6
|
+
* local endpoint up from AWS_ENDPOINT_URL_DYNAMODB, with dummy credentials (real ones never reach the sandbox).
|
|
7
|
+
*/
|
|
8
|
+
export declare const setupDynamo: (env: ToolEnv, plan: DatabasePlan) => Promise<void>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// DynamoDB Local + dynamodb-admin.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
import { fetchText, installArchive } from '../../../shared/download.js';
|
|
6
|
+
import { execAsync, exists, log, logsDir, mkdirp } from '../../env.js';
|
|
7
|
+
import { ensureJdk } from '../java.js';
|
|
8
|
+
import { tool } from '../common.js';
|
|
9
|
+
import { portOpen, waitForPort } from '../../ports.js';
|
|
10
|
+
import { dataFor, startServer } from './server.js';
|
|
11
|
+
const DYNAMO_ZIP = 'https://d1ni2b6xgvw0s0.cloudfront.net/v2.x/dynamodb_local_latest.zip';
|
|
12
|
+
/**
|
|
13
|
+
* Amazon's DynamoDB Local (a Java program; the Java toolchain step runs first, else this installs the LTS JDK) and
|
|
14
|
+
* dynamodb-admin, the web UI to browse and edit tables, installed with the runner's own npm. Every AWS SDK picks the
|
|
15
|
+
* local endpoint up from AWS_ENDPOINT_URL_DYNAMODB, with dummy credentials (real ones never reach the sandbox).
|
|
16
|
+
*/
|
|
17
|
+
export const setupDynamo = async (env, plan) => {
|
|
18
|
+
const java = path.join(await ensureJdk(), 'bin', 'java.exe');
|
|
19
|
+
const home = tool('dynamodb-local');
|
|
20
|
+
if (!exists(path.join(home, 'DynamoDBLocal.jar'))) {
|
|
21
|
+
log('Downloading DynamoDB Local (~60 MB once) ...');
|
|
22
|
+
const sha256 = (await fetchText(DYNAMO_ZIP + '.sha256')).trim().split(/\s+/)[0];
|
|
23
|
+
await installArchive(DYNAMO_ZIP, home, { sha256, locate: 'DynamoDBLocal.jar', fileName: 'dynamodb_local.zip' });
|
|
24
|
+
}
|
|
25
|
+
const data = mkdirp(dataFor('dynamodb'));
|
|
26
|
+
// -sharedDb: one database whatever region/credentials a client uses, so the admin UI and the app see the same tables.
|
|
27
|
+
await startServer('dynamodb', java, ['-Djava.library.path=' + path.join(home, 'DynamoDBLocal_lib'), '-jar', path.join(home, 'DynamoDBLocal.jar'), '-sharedDb', '-dbPath', data, '-port', String(plan.port)], plan.port);
|
|
28
|
+
const endpoint = 'http://localhost:' + plan.port;
|
|
29
|
+
const aws = { AWS_ENDPOINT_URL_DYNAMODB: endpoint, DYNAMO_ENDPOINT: endpoint, AWS_ACCESS_KEY_ID: 'local', AWS_SECRET_ACCESS_KEY: 'local', AWS_REGION: 'us-east-1', AWS_DEFAULT_REGION: 'us-east-1' };
|
|
30
|
+
Object.assign(env.vars, aws);
|
|
31
|
+
env.summary.push('dynamodb (DynamoDB Local): ' + endpoint + ' (AWS SDKs use it automatically via AWS_ENDPOINT_URL_DYNAMODB; dummy credentials)');
|
|
32
|
+
if (!plan.adminPort)
|
|
33
|
+
return;
|
|
34
|
+
const prefix = tool('dynamodb-admin');
|
|
35
|
+
const script = path.join(prefix, 'node_modules', 'dynamodb-admin', 'dist', 'dynamodb-admin.js');
|
|
36
|
+
if (!exists(script)) {
|
|
37
|
+
log('Installing dynamodb-admin, the DynamoDB web UI (~20 MB once) ...');
|
|
38
|
+
const npmCli = path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
|
|
39
|
+
await execAsync(process.execPath, [npmCli, 'install', '--global', '--prefix', prefix, 'dynamodb-admin', '--no-fund', '--no-audit'], { stdio: 'ignore' });
|
|
40
|
+
}
|
|
41
|
+
// The database matters more than its UI: a taken port is reported, not fatal.
|
|
42
|
+
if (await portOpen(plan.adminPort)) {
|
|
43
|
+
env.summary.push('dynamodb-admin: NOT started, port ' + plan.adminPort + ' is in use (change databases.dynamodb.adminPort)');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const out = fs.openSync(path.join(logsDir, 'dynamodb-admin.log'), 'a');
|
|
47
|
+
spawn(process.execPath, [script, '--port', String(plan.adminPort), '--dynamo-endpoint', endpoint, '--skip-default-credentials'], { detached: true, stdio: ['ignore', out, out], windowsHide: true, env: { ...process.env, ...aws } }).unref();
|
|
48
|
+
fs.closeSync(out);
|
|
49
|
+
if (!(await waitForPort(plan.adminPort, 30)))
|
|
50
|
+
throw new Error('dynamodb-admin did not start; see ' + path.join(logsDir, 'dynamodb-admin.log'));
|
|
51
|
+
env.summary.push('dynamodb-admin: http://localhost:' + plan.adminPort + ' (browse and edit tables in the sandbox\'s browser)');
|
|
52
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DatabasePlan } from '../../../core/types.js';
|
|
2
|
+
import { type ToolEnv } from '../common.js';
|
|
3
|
+
export declare const DATABASE_NAMES: Record<DatabasePlan['database'], string>;
|
|
4
|
+
/** Stops a database (and DynamoDB's admin UI). PostgreSQL shuts down cleanly through pg_ctl; the others are ended. */
|
|
5
|
+
export declare const stopDatabase: (plan: DatabasePlan) => Promise<boolean>;
|
|
6
|
+
export declare const setupDatabase: (env: ToolEnv, plan: DatabasePlan) => Promise<void>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Databases inside the sandbox: PostgreSQL, MariaDB (MySQL-compatible), MongoDB, Redis and DynamoDB Local. Binaries
|
|
2
|
+
// live in C:\AgentData\tools, data in C:\AgentData\databases (both persist), and every server listens on localhost only.
|
|
3
|
+
// Which ones run was decided on the host (session.databases): only what the project needs.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { spawnSync } from 'node:child_process';
|
|
7
|
+
import { SYSTEM } from '../../../shared/system.js';
|
|
8
|
+
import { dryRun, execAsync, exists, log } from '../../env.js';
|
|
9
|
+
import { tool } from '../common.js';
|
|
10
|
+
import { setupDynamo } from './dynamodb.js';
|
|
11
|
+
import { setupMongo } from './mongodb.js';
|
|
12
|
+
import { setupMysql } from './mysql.js';
|
|
13
|
+
import { postgresData, setupPostgres } from './postgres.js';
|
|
14
|
+
import { setupRedis } from './redis.js';
|
|
15
|
+
import { portOpen } from '../../ports.js';
|
|
16
|
+
import { duringSession } from '../../project-context.js';
|
|
17
|
+
const SETUP = { postgres: setupPostgres, mysql: setupMysql, mongodb: setupMongo, redis: setupRedis, dynamodb: setupDynamo };
|
|
18
|
+
export const DATABASE_NAMES = { postgres: 'PostgreSQL', mysql: 'MySQL (MariaDB)', mongodb: 'MongoDB', redis: 'Redis', dynamodb: 'DynamoDB Local', sqlserver: 'SQL Server' };
|
|
19
|
+
/** The process listening on a local TCP port (netstat: no PowerShell start-up cost), or null. */
|
|
20
|
+
const listenerPid = (port) => {
|
|
21
|
+
const out = spawnSync(SYSTEM.netstat(), ['-ano', '-p', 'TCP'], { encoding: 'utf8', windowsHide: true }).stdout ?? '';
|
|
22
|
+
const hit = out.split(/\r?\n/).map((l) => /^\s*TCP\s+\S+:(\d+)\s+\S+\s+LISTENING\s+(\d+)/.exec(l)).find((m) => m && Number(m[1]) === port);
|
|
23
|
+
return hit ? Number(hit[2]) : null;
|
|
24
|
+
};
|
|
25
|
+
/** Stops a database (and DynamoDB's admin UI). PostgreSQL shuts down cleanly through pg_ctl; the others are ended. */
|
|
26
|
+
export const stopDatabase = async (plan) => {
|
|
27
|
+
if (!(await portOpen(plan.port)))
|
|
28
|
+
return false;
|
|
29
|
+
if (plan.database === 'postgres' && exists(tool('postgres'))) {
|
|
30
|
+
for (const version of fs.readdirSync(tool('postgres'))) {
|
|
31
|
+
const data = postgresData(version);
|
|
32
|
+
const pgCtl = path.join(tool('postgres', version), 'bin', 'pg_ctl.exe');
|
|
33
|
+
if (exists(path.join(data, 'postmaster.pid')) && exists(pgCtl))
|
|
34
|
+
await execAsync(pgCtl, ['stop', '-D', data, '-m', 'fast', '-w'], { stdio: 'ignore' });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
for (const port of [plan.port, plan.adminPort ?? 0]) {
|
|
38
|
+
const pid = port && listenerPid(port);
|
|
39
|
+
if (pid)
|
|
40
|
+
try {
|
|
41
|
+
process.kill(pid);
|
|
42
|
+
}
|
|
43
|
+
catch { /* already gone */ }
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
};
|
|
47
|
+
export const setupDatabase = async (env, plan) => {
|
|
48
|
+
if (plan.database === 'sqlserver') {
|
|
49
|
+
env.summary.push('sqlserver: NOT available (SQL Server cannot start inside Windows Sandbox). Switch the project to Postgres or SQLite for local work');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (dryRun) {
|
|
53
|
+
log('(dry run) would run ' + DATABASE_NAMES[plan.database] + ' on port ' + plan.port + ' (' + plan.reasons.join('; ') + ')');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// A project that joined later, while another project's server already listens there: they share it.
|
|
57
|
+
if (duringSession() && (await portOpen(plan.port))) {
|
|
58
|
+
env.summary.push(plan.database + ': shared, already running on port ' + plan.port);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
await SETUP[plan.database](env, plan);
|
|
62
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// MongoDB Community, with mongosh.
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fetchJson, installArchive } from '../../../shared/download.js';
|
|
4
|
+
import { exists, log, mkdirp } from '../../env.js';
|
|
5
|
+
import { installFromGithub, resolveCached, tool } from '../common.js';
|
|
6
|
+
import { clearStaleLocks, dataFor, startServer } from './server.js';
|
|
7
|
+
export const setupMongo = async (env, plan) => {
|
|
8
|
+
const find = async () => {
|
|
9
|
+
const { versions } = await fetchJson('https://downloads.mongodb.org/full.json');
|
|
10
|
+
const prod = versions.filter((v) => v.production_release);
|
|
11
|
+
const hit = plan.version === 'latest' ? prod[0] : prod.find((v) => v.version === plan.version || v.version.startsWith(plan.version + '.'));
|
|
12
|
+
const archive = hit?.downloads.find((d) => d.target === 'windows' && d.arch === 'x86_64' && d.edition === 'base')?.archive;
|
|
13
|
+
return hit && archive ? { version: hit.version, archive } : Promise.reject(new Error('No MongoDB ' + plan.version + ' for Windows'));
|
|
14
|
+
};
|
|
15
|
+
const version = await resolveCached('mongodb:' + plan.version, async () => (await find()).version);
|
|
16
|
+
const home = tool('mongodb', version);
|
|
17
|
+
if (!exists(path.join(home, 'bin', 'mongod.exe'))) {
|
|
18
|
+
const { archive } = await find();
|
|
19
|
+
log('Downloading MongoDB ' + version + ' (~400 MB once) ...');
|
|
20
|
+
await installArchive(archive.url, home, { sha256: archive.sha256, locate: 'mongod.exe', up: 1 });
|
|
21
|
+
}
|
|
22
|
+
const shell = tool('mongosh');
|
|
23
|
+
if (!exists(path.join(shell, 'mongosh.exe'))) {
|
|
24
|
+
await installFromGithub('mongodb-js/mongosh', /^mongosh-[\d.]+-win32-x64\.zip$/, { dest: shell, what: 'mongosh, the MongoDB shell (~50 MB once)', locate: 'mongosh.exe' });
|
|
25
|
+
}
|
|
26
|
+
const data = mkdirp(dataFor('mongodb'));
|
|
27
|
+
await clearStaleLocks(data, ['mongod.lock'], plan.port);
|
|
28
|
+
await startServer('mongodb', path.join(home, 'bin', 'mongod.exe'), ['--dbpath', data, '--bind_ip', '127.0.0.1', '--port', String(plan.port)], plan.port);
|
|
29
|
+
env.prepend.push(path.join(home, 'bin'), shell);
|
|
30
|
+
env.summary.push('mongodb ' + version + ': mongodb://127.0.0.1:' + plan.port + ' (mongosh on PATH)');
|
|
31
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// MariaDB, the MySQL-compatible server.
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fetchJson, installArchive } from '../../../shared/download.js';
|
|
4
|
+
import { execAsync, exists, log } from '../../env.js';
|
|
5
|
+
import { resolveCached, tool } from '../common.js';
|
|
6
|
+
import { createOnce, dataFor, startServer } from './server.js';
|
|
7
|
+
const MARIA = 'https://downloads.mariadb.org/rest-api/mariadb/';
|
|
8
|
+
export const setupMysql = async (env, plan) => {
|
|
9
|
+
const series = await resolveCached('mariadb:' + plan.version, async () => {
|
|
10
|
+
if (plan.version !== 'lts')
|
|
11
|
+
return plan.version;
|
|
12
|
+
const { major_releases: all } = await fetchJson(MARIA);
|
|
13
|
+
return all.find((r) => r.release_status === 'Stable' && r.release_support_type === 'Long Term Support')?.release_id ?? Promise.reject(new Error('No MariaDB LTS release found'));
|
|
14
|
+
});
|
|
15
|
+
const latest = async () => {
|
|
16
|
+
const { releases } = await fetchJson(MARIA + series + '/latest/');
|
|
17
|
+
const [version, release] = Object.entries(releases)[0] ?? [];
|
|
18
|
+
const zip = release?.files.find((f) => /-winx64\.zip$/.test(f.file_name));
|
|
19
|
+
return zip ? { version, zip } : Promise.reject(new Error('No MariaDB ' + series + ' zip for Windows'));
|
|
20
|
+
};
|
|
21
|
+
const version = await resolveCached('mariadb-version:' + series, async () => (await latest()).version);
|
|
22
|
+
const home = tool('mariadb', version);
|
|
23
|
+
const bin = path.join(home, 'bin');
|
|
24
|
+
if (!exists(path.join(bin, 'mariadbd.exe'))) {
|
|
25
|
+
const { zip } = await latest();
|
|
26
|
+
log('Downloading MariaDB ' + version + ' (~90 MB once) ...');
|
|
27
|
+
await installArchive(zip.file_download_url.replace(/^http:/, 'https:'), home, { sha256: zip.checksum?.sha256sum ?? null, locate: 'mariadbd.exe', up: 1, fileName: zip.file_name });
|
|
28
|
+
}
|
|
29
|
+
const data = dataFor('mariadb');
|
|
30
|
+
await createOnce(data, 'mysql', async () => {
|
|
31
|
+
log('Creating the MariaDB database (first start) ...');
|
|
32
|
+
await execAsync(path.join(bin, 'mariadb-install-db.exe'), ['--datadir=' + data, '--port=' + plan.port], { stdio: 'ignore' });
|
|
33
|
+
});
|
|
34
|
+
await startServer('mysql', path.join(bin, 'mariadbd.exe'), ['--datadir=' + data, '--port=' + plan.port, '--bind-address=127.0.0.1', '--console'], plan.port);
|
|
35
|
+
Object.assign(env.vars, { MYSQL_HOST: '127.0.0.1', MYSQL_TCP_PORT: String(plan.port) });
|
|
36
|
+
env.prepend.push(bin);
|
|
37
|
+
env.summary.push('mysql (MariaDB ' + version + '): 127.0.0.1:' + plan.port + ', user root, no password (mysql://root@127.0.0.1:' + plan.port + ')');
|
|
38
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DatabasePlan } from '../../../core/types.js';
|
|
2
|
+
import { type ToolEnv } from '../common.js';
|
|
3
|
+
/** The data folder of a PostgreSQL major version (one cluster per major). */
|
|
4
|
+
export declare const postgresData: (version: string) => string;
|
|
5
|
+
export declare const setupPostgres: (env: ToolEnv, plan: DatabasePlan) => Promise<void>;
|