windows-development-sandbox 1.0.5 → 1.4.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/LICENSE +21 -0
- package/README.md +25 -8
- package/config.example.json +55 -5
- package/dist/api.js +2 -1
- package/dist/cli/args.d.ts +23 -0
- package/dist/cli/args.js +38 -4
- package/dist/cli/colors.d.ts +24 -0
- package/dist/cli/colors.js +42 -0
- package/dist/cli/commands/accounts.js +2 -2
- 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 +2 -0
- package/dist/cli/commands/context.js +5 -1
- package/dist/cli/commands/data/clean.d.ts +15 -0
- package/dist/cli/commands/data/clean.js +106 -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 +6 -4
- package/dist/cli/commands/index.js +6 -4
- package/dist/cli/commands/launch.d.ts +26 -2
- package/dist/cli/commands/launch.js +256 -42
- package/dist/cli/commands/maintenance.d.ts +2 -0
- package/dist/cli/commands/maintenance.js +67 -5
- 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.js → repair/checks.js} +19 -181
- package/dist/cli/commands/{repair.d.ts → repair/finders.d.ts} +0 -29
- 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 +2 -1
- package/dist/cli/commands/sessions.js +189 -45
- package/dist/cli/commands/setup/checks.d.ts +13 -0
- package/dist/cli/commands/setup/checks.js +71 -0
- package/dist/cli/commands/{setup.d.ts → setup/facts.d.ts} +1 -15
- 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 +7 -1
- package/dist/cli/index.js +42 -15
- package/dist/cli/manifest.d.ts +8 -0
- package/dist/cli/manifest.js +40 -15
- package/dist/cli/menu/apps.d.ts +21 -0
- package/dist/cli/menu/apps.js +122 -0
- package/dist/cli/menu/command-form.d.ts +28 -0
- package/dist/cli/menu/command-form.js +219 -0
- package/dist/cli/menu/index.d.ts +26 -0
- package/dist/cli/menu/index.js +180 -0
- package/dist/cli/menu/settings/categories.d.ts +9 -0
- package/dist/cli/menu/settings/categories.js +28 -0
- package/dist/cli/menu/settings/config-file.d.ts +40 -0
- package/dist/cli/menu/settings/config-file.js +71 -0
- package/dist/cli/menu/settings/editors.d.ts +30 -0
- package/dist/cli/menu/settings/editors.js +305 -0
- package/dist/cli/menu/settings/index.d.ts +32 -0
- package/dist/cli/menu/settings/index.js +136 -0
- package/dist/cli/menu/settings/schema-node.d.ts +62 -0
- package/dist/cli/menu/settings/schema-node.js +104 -0
- package/dist/cli/menu/several.d.ts +20 -0
- package/dist/cli/menu/several.js +102 -0
- package/dist/cli/progress.d.ts +9 -0
- package/dist/cli/progress.js +49 -0
- package/dist/cli/tui/app.d.ts +39 -0
- package/dist/cli/tui/app.js +52 -0
- package/dist/cli/tui/dialogs.d.ts +57 -0
- package/dist/cli/tui/dialogs.js +162 -0
- package/dist/cli/tui/frame.d.ts +18 -0
- package/dist/cli/tui/frame.js +27 -0
- package/dist/cli/tui/index.d.ts +7 -0
- package/dist/cli/tui/index.js +8 -0
- package/dist/cli/tui/list-screen.d.ts +101 -0
- package/dist/cli/tui/list-screen.js +299 -0
- package/dist/cli/tui/terminal.d.ts +37 -0
- package/dist/cli/tui/terminal.js +89 -0
- package/dist/cli/tui/text.d.ts +20 -0
- package/dist/cli/tui/text.js +105 -0
- package/dist/cli/tui/theme.d.ts +45 -0
- package/dist/cli/tui/theme.js +30 -0
- package/dist/cli/validate.d.ts +17 -0
- package/dist/cli/validate.js +89 -0
- package/dist/core/config.js +12 -6
- package/dist/core/disk.d.ts +16 -0
- package/dist/core/disk.js +55 -0
- package/dist/core/local-config.d.ts +23 -0
- package/dist/core/local-config.js +79 -7
- 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/apps.d.ts +40 -0
- package/dist/core/schema/apps.js +27 -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 +659 -0
- package/dist/core/schema/config.js +75 -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 +32 -0
- package/dist/core/schema/profile.js +31 -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 +78 -0
- package/dist/core/schema/sandbox.js +64 -0
- package/dist/core/schema/toolchains.d.ts +96 -0
- package/dist/core/schema/toolchains.js +88 -0
- package/dist/core/schema.d.ts +6 -604
- package/dist/core/schema.js +8 -264
- package/dist/core/types.d.ts +35 -1
- package/dist/core/util.d.ts +9 -0
- package/dist/core/util.js +14 -3
- package/dist/core/workspace.d.ts +0 -5
- package/dist/core/workspace.js +33 -50
- package/dist/guest/apps/index.d.ts +10 -0
- package/dist/guest/apps/index.js +206 -0
- package/dist/guest/apps/resolvers.d.ts +8 -0
- package/dist/guest/apps/resolvers.js +90 -0
- package/dist/guest/audit-parse.d.ts +7 -0
- package/dist/guest/audit-parse.js +24 -0
- package/dist/guest/audit.d.ts +4 -0
- package/dist/guest/audit.js +47 -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/relays.d.ts +1 -0
- package/dist/guest/daemon/relays.js +18 -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.js +12 -141
- package/dist/guest/db.js +18 -3
- package/dist/guest/env.d.ts +3 -0
- package/dist/guest/env.js +7 -3
- package/dist/guest/guide.d.ts +6 -2
- package/dist/guest/guide.js +5 -4
- package/dist/guest/helpers.d.ts +3 -0
- package/dist/guest/helpers.js +26 -0
- package/dist/guest/main.js +31 -8
- package/dist/guest/netproxy.d.ts +1 -0
- package/dist/guest/netproxy.js +100 -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 +0 -18
- package/dist/guest/ptyhost.js +17 -65
- package/dist/guest/setup/agents.d.ts +5 -2
- package/dist/guest/setup/agents.js +22 -8
- package/dist/guest/setup/claude.d.ts +0 -4
- package/dist/guest/setup/claude.js +59 -5
- package/dist/guest/setup/environment.d.ts +13 -3
- package/dist/guest/setup/environment.js +14 -10
- package/dist/guest/setup/hooks.d.ts +9 -1
- package/dist/guest/setup/hooks.js +28 -14
- package/dist/guest/setup/network.d.ts +9 -0
- package/dist/guest/setup/network.js +97 -4
- package/dist/guest/setup/user.js +4 -0
- package/dist/guest/toolchains/catalogue.d.ts +4 -0
- package/dist/guest/toolchains/catalogue.js +2 -2
- package/dist/guest/toolchains/cli.js +25 -17
- package/dist/guest/toolchains/common.d.ts +18 -1
- package/dist/guest/toolchains/common.js +34 -11
- package/dist/guest/toolchains/databases/dynamodb.d.ts +8 -0
- package/dist/guest/toolchains/databases/dynamodb.js +52 -0
- package/dist/guest/toolchains/{databases.d.ts → databases/index.d.ts} +2 -3
- 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.js +25 -16
- package/dist/guest/toolchains/go.js +1 -1
- package/dist/guest/toolchains/index.d.ts +13 -9
- package/dist/guest/toolchains/index.js +40 -82
- package/dist/guest/toolchains/java.js +5 -5
- package/dist/guest/toolchains/leftovers.d.ts +2 -0
- package/dist/guest/toolchains/leftovers.js +26 -0
- package/dist/guest/toolchains/managers.js +12 -10
- package/dist/guest/toolchains/{binstall.js → native/binstall.js} +6 -9
- 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 +56 -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.js +26 -6
- package/dist/guest/toolchains/plan.d.ts +24 -0
- package/dist/guest/toolchains/plan.js +140 -0
- package/dist/guest/toolchains/pwsh.js +1 -1
- package/dist/guest/toolchains/python.js +43 -12
- package/dist/guest/toolchains/runtimes.d.ts +2 -0
- package/dist/guest/toolchains/runtimes.js +6 -8
- 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/vscode-profile.d.ts +30 -0
- package/dist/guest/toolchains/vscode-profile.js +69 -0
- package/dist/guest/toolchains/vscode.d.ts +17 -0
- package/dist/guest/toolchains/vscode.js +87 -1
- package/dist/guest/view.js +21 -1
- package/dist/host/accounts.js +14 -5
- 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 +70 -0
- package/dist/host/audit/collect.d.ts +34 -0
- package/dist/host/audit/collect.js +126 -0
- package/dist/host/audit/log.d.ts +46 -0
- package/dist/host/audit/log.js +143 -0
- package/dist/host/channel.d.ts +23 -22
- package/dist/host/channel.js +66 -84
- package/dist/host/claude-profile.js +7 -3
- package/dist/host/cleanup.js +2 -11
- package/dist/host/database-plan.d.ts +12 -0
- package/dist/host/database-plan.js +52 -0
- package/dist/host/generate.d.ts +10 -12
- package/dist/host/generate.js +58 -135
- 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 +54 -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/motw.d.ts +17 -0
- package/dist/host/motw.js +74 -0
- package/dist/host/pc-scan.d.ts +6 -0
- package/dist/host/pc-scan.js +37 -0
- package/dist/host/platform.d.ts +2 -0
- package/dist/host/platform.js +16 -1
- package/dist/host/port-forward.d.ts +8 -0
- package/dist/host/port-forward.js +37 -0
- package/dist/host/project-scan.d.ts +1 -17
- package/dist/host/project-scan.js +2 -86
- package/dist/host/projects.d.ts +100 -0
- package/dist/host/projects.js +147 -0
- package/dist/host/repo-guard.d.ts +25 -5
- package/dist/host/repo-guard.js +242 -23
- package/dist/host/stops.d.ts +67 -0
- package/dist/host/stops.js +126 -0
- package/dist/host/terminal.d.ts +8 -0
- package/dist/host/terminal.js +48 -1
- package/dist/host/test-guard.d.ts +6 -0
- package/dist/host/test-guard.js +15 -0
- package/dist/host/window.js +7 -3
- package/dist/host/workspace-view/index.d.ts +20 -0
- package/dist/host/workspace-view/index.js +53 -0
- package/dist/host/workspace-view/rules.d.ts +26 -0
- package/dist/host/workspace-view/rules.js +93 -0
- package/dist/host/workspace-view/view.d.ts +50 -0
- package/dist/host/workspace-view/view.js +281 -0
- package/dist/host/wsb.d.ts +37 -0
- package/dist/host/wsb.js +101 -0
- package/dist/runtime/daemon.js +11791 -2821
- package/dist/runtime/db.js +6585 -466
- package/dist/runtime/main.js +10238 -1724
- package/dist/runtime/netproxy.js +25936 -0
- package/dist/runtime/view.js +6183 -59
- 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/audit.test.d.ts +2 -0
- package/dist/selftest/audit.test.js +57 -0
- package/dist/selftest/channel.test.d.ts +2 -0
- package/dist/selftest/channel.test.js +44 -0
- package/dist/selftest/cli.test.d.ts +2 -2
- package/dist/selftest/cli.test.js +145 -27
- 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 -3
- package/dist/selftest/config.test.js +36 -72
- package/dist/selftest/data.test.d.ts +2 -2
- package/dist/selftest/data.test.js +77 -70
- package/dist/selftest/detect.test.d.ts +2 -2
- package/dist/selftest/detect.test.js +54 -38
- package/dist/selftest/fixtures.d.ts +89 -4
- package/dist/selftest/fixtures.js +43 -24
- package/dist/selftest/generate.test.d.ts +2 -0
- package/dist/selftest/generate.test.js +68 -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 +44 -7
- package/dist/selftest/harness.js +62 -21
- 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 -1
- package/dist/selftest/index.js +137 -35
- 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-network.test.d.ts +2 -0
- package/dist/selftest/live-network.test.js +63 -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 +120 -0
- package/dist/selftest/mirror.test.d.ts +2 -3
- package/dist/selftest/mirror.test.js +92 -65
- package/dist/selftest/network.test.d.ts +2 -0
- package/dist/selftest/network.test.js +40 -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 +121 -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 -2
- package/dist/selftest/setup.test.js +45 -22
- package/dist/selftest/suite.d.ts +13 -0
- package/dist/selftest/suite.js +1 -0
- package/dist/selftest/sync.test.d.ts +2 -3
- package/dist/selftest/sync.test.js +130 -78
- 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 +133 -0
- package/dist/selftest/workspace-view.test.d.ts +2 -0
- package/dist/selftest/workspace-view.test.js +89 -0
- package/dist/selftest/workspace.test.d.ts +2 -0
- package/dist/selftest/workspace.test.js +53 -0
- package/dist/shared/apps.d.ts +49 -0
- package/dist/shared/apps.js +114 -0
- package/dist/shared/download.d.ts +3 -1
- package/dist/shared/download.js +19 -4
- package/dist/shared/netpolicy.d.ts +27 -0
- package/dist/shared/netpolicy.js +51 -0
- package/dist/shared/paths.d.ts +5 -0
- package/dist/shared/paths.js +6 -0
- package/dist/shared/projects.d.ts +53 -0
- package/dist/shared/projects.js +67 -0
- package/dist/shared/pty-client.d.ts +9 -4
- package/dist/shared/pty-client.js +139 -17
- package/dist/shared/rundir.d.ts +12 -1
- package/dist/shared/rundir.js +14 -4
- package/dist/shared/safe-remove.js +11 -0
- package/dist/shared/schema.d.ts +453 -0
- package/dist/shared/schema.js +43 -2
- package/dist/shared/system.d.ts +1 -0
- package/dist/shared/system.js +1 -0
- package/dist/shared/test-guard.d.ts +4 -0
- package/dist/shared/test-guard.js +37 -0
- package/dist/sync/claude-mem.js +6 -3
- package/dist/sync/history.d.ts +29 -12
- package/dist/sync/history.js +72 -25
- package/dist/sync/watcher.d.ts +8 -0
- package/dist/sync/watcher.js +99 -8
- package/package.json +7 -6
- package/sandbox/bootstrap.ps1 +258 -258
- package/sandbox/cargoshim.cs +56 -0
- package/sandbox/cpucap.cs +33 -0
- package/sandbox/cpucap.ps1 +20 -0
- package/sandbox/logon.cmd +24 -16
- package/sandbox/memlog.ps1 +34 -0
- package/sandbox/openssl.cs +42 -0
- package/sandbox/pagefile.cs +31 -0
- package/sandbox/pagefile.ps1 +21 -0
- package/sandbox/ptyhost.cs +120 -11
- package/sandbox/trust.cmd +35 -35
- package/schemas/config.schema.json +403 -3
- package/schemas/sandbox-config.schema.json +143 -13
- package/dist/cli/commands/data.d.ts +0 -46
- package/dist/cli/commands/data.js +0 -249
- package/dist/cli/commands/setup.js +0 -152
- package/dist/cli/menu.d.ts +0 -22
- package/dist/cli/menu.js +0 -187
- package/dist/cli/settings.d.ts +0 -11
- package/dist/cli/settings.js +0 -156
- package/dist/guest/toolchains/databases.js +0 -226
- package/dist/guest/toolchains/msvc.d.ts +0 -10
- package/dist/guest/toolchains/msvc.js +0 -216
- package/dist/guest/toolchains/native.d.ts +0 -12
- package/dist/guest/toolchains/native.js +0 -220
- package/dist/guest/toolchains/scripting.d.ts +0 -10
- package/dist/guest/toolchains/scripting.js +0 -137
- package/dist/selftest/guest.test.d.ts +0 -3
- package/dist/selftest/guest.test.js +0 -61
- package/dist/selftest/helpers.test.d.ts +0 -2
- package/dist/selftest/helpers.test.js +0 -125
- package/dist/selftest/host.test.d.ts +0 -2
- package/dist/selftest/host.test.js +0 -71
- package/dist/selftest/live.test.d.ts +0 -7
- package/dist/selftest/live.test.js +0 -127
- package/dist/selftest/security.test.d.ts +0 -3
- package/dist/selftest/security.test.js +0 -143
- /package/dist/guest/toolchains/{binstall.d.ts → native/binstall.d.ts} +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// workspaceFilter: the sandbox works on a copy of the project without what git ignores, kept in step both ways.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { IgnoreRules } from '../host/workspace-view/rules.js';
|
|
5
|
+
import { WorkspaceView } from '../host/workspace-view/view.js';
|
|
6
|
+
export const workspaceViewSuite = {
|
|
7
|
+
name: 'workspace-view',
|
|
8
|
+
title: 'git-ignored files kept out of the sandbox (filtered view, two-way sync)',
|
|
9
|
+
run: async (t, f) => {
|
|
10
|
+
t.section('a filtered launch');
|
|
11
|
+
const project = t.dir('filtered-app');
|
|
12
|
+
fs.mkdirSync(path.join(project, '.git'));
|
|
13
|
+
const filtered = { ...f.baseConfig, workspaceFilter: { enabled: true, exclude: [], include: [] } };
|
|
14
|
+
const gen = await f.launch({ cwd: project }, { config: filtered });
|
|
15
|
+
const workspace = gen.mappings.find((m) => m.guest.toLowerCase().endsWith('\\filtered-app'));
|
|
16
|
+
const git = gen.mappings.find((m) => m.guest.toLowerCase().endsWith('\\filtered-app.git'));
|
|
17
|
+
t.check(Boolean(workspace) && !workspace.host.toLowerCase().startsWith(project.toLowerCase()) && workspace.host.includes('views'), 'the sandbox maps the filtered view, not the project folder');
|
|
18
|
+
t.check(git?.host === path.join(project, '.git') && !git.readOnly, '... and the project\'s .git beside it, so git works');
|
|
19
|
+
const noGit = await f.launch({ cwd: project }, { config: { ...filtered, provisioning: { ...filtered.provisioning, git: { ...filtered.provisioning.git, access: 'none' } } } });
|
|
20
|
+
t.check(!noGit.mappings.some((m) => m.guest.toLowerCase().endsWith('.git')), 'with git access "none" no .git is mapped at all');
|
|
21
|
+
t.section('which files git ignores');
|
|
22
|
+
const rulesRoot = t.at('rules');
|
|
23
|
+
t.write(path.join(rulesRoot, '.gitignore'), 'node_modules/\n*.log\n!keep.log\n.env*\n!.env.example\ndist\n');
|
|
24
|
+
t.write(path.join(rulesRoot, 'sub', '.gitignore'), 'secret.txt\n!*.log\n');
|
|
25
|
+
t.write(path.join(rulesRoot, '.git', 'info', 'exclude'), 'local-notes.md\n');
|
|
26
|
+
const rules = new IgnoreRules(rulesRoot, ['*.pem'], ['dist/keep-me.js']);
|
|
27
|
+
const verdicts = Object.fromEntries(['src/a.ts', 'node_modules/x/index.js', 'a.log', 'keep.log', '.env', '.env.local', '.env.example', 'dist/out.js', 'sub/secret.txt', 'sub/b.log', 'local-notes.md', 'key.pem', 'dist/keep-me.js', '.git/config'].map((p) => [p, rules.isIgnored(p)]));
|
|
28
|
+
t.equal(verdicts, {
|
|
29
|
+
'src/a.ts': false, 'node_modules/x/index.js': true, 'a.log': true, 'keep.log': false, '.env': true, '.env.local': true, '.env.example': false,
|
|
30
|
+
'dist/out.js': true, 'sub/secret.txt': true, 'sub/b.log': false, 'local-notes.md': true, 'key.pem': true, 'dist/keep-me.js': false, '.git/config': true,
|
|
31
|
+
}, 'rules: nested .gitignore files, ! exceptions, .git/info/exclude, your own exclude and include patterns');
|
|
32
|
+
t.section('the view');
|
|
33
|
+
const host = t.at('project');
|
|
34
|
+
t.write(path.join(host, '.gitignore'), 'node_modules/\n.env\ndist/\n');
|
|
35
|
+
t.write(path.join(host, 'src', 'main.ts'), 'console.log(1);\n');
|
|
36
|
+
t.write(path.join(host, 'README.md'), '# hi\n');
|
|
37
|
+
t.write(path.join(host, '.env'), 'API_KEY=secret\n');
|
|
38
|
+
t.write(path.join(host, 'node_modules', 'pkg', 'index.js'), 'host build');
|
|
39
|
+
fs.mkdirSync(path.join(host, '.git'), { recursive: true });
|
|
40
|
+
const paths = WorkspaceView.pathsFor(t.at('storage'), host);
|
|
41
|
+
const view = new WorkspaceView({ ...paths, guestGitDir: 'C:\\Workspace\\project.git' });
|
|
42
|
+
view.prepare();
|
|
43
|
+
const inView = (rel) => fs.existsSync(path.join(paths.view, ...rel.split('/')));
|
|
44
|
+
t.check(inView('src/main.ts') && inView('README.md') && inView('.gitignore'), 'view: what git tracks is there');
|
|
45
|
+
t.check(!inView('.env') && !inView('node_modules'), 'view: ignored files (.env, node_modules) are not');
|
|
46
|
+
t.equal(fs.readFileSync(path.join(paths.view, '.git'), 'utf8'), 'gitdir: C:/Workspace/project.git\n', 'view: its .git points at the project\'s real .git (mapped separately)');
|
|
47
|
+
t.check(!paths.view.toLowerCase().startsWith(host.toLowerCase()), 'view: it lives outside the project');
|
|
48
|
+
t.section('both ways');
|
|
49
|
+
t.write(path.join(host, 'src', 'main.ts'), 'console.log(2);\n');
|
|
50
|
+
t.touch(path.join(host, 'src', 'main.ts'), 5);
|
|
51
|
+
view.syncChanged(['src/main.ts']);
|
|
52
|
+
t.equal(fs.readFileSync(path.join(paths.view, 'src', 'main.ts'), 'utf8'), 'console.log(2);\n', 'your edit reaches the sandbox');
|
|
53
|
+
t.write(path.join(paths.view, 'src', 'new.ts'), 'export {};\n');
|
|
54
|
+
t.write(path.join(paths.view, 'node_modules', 'pkg', 'index.js'), 'sandbox build');
|
|
55
|
+
t.write(path.join(paths.view, 'dist', 'bundle.js'), 'built');
|
|
56
|
+
view.syncAll();
|
|
57
|
+
t.check(fs.existsSync(path.join(host, 'src', 'new.ts')), 'a file the agent creates reaches your project');
|
|
58
|
+
t.equal(fs.readFileSync(path.join(host, 'node_modules', 'pkg', 'index.js'), 'utf8'), 'host build', 'the sandbox\'s own node_modules never overwrite yours');
|
|
59
|
+
t.check(!fs.existsSync(path.join(host, 'dist')), 'its build output stays in the sandbox');
|
|
60
|
+
fs.rmSync(path.join(paths.view, 'README.md'));
|
|
61
|
+
view.syncAll();
|
|
62
|
+
t.check(!fs.existsSync(path.join(host, 'README.md')), 'a deletion in the sandbox is repeated in your project');
|
|
63
|
+
fs.rmSync(path.join(host, 'src', 'new.ts'));
|
|
64
|
+
view.syncAll();
|
|
65
|
+
t.check(!inView('src/new.ts'), 'a deletion in your project is repeated in the sandbox');
|
|
66
|
+
t.section('conflicts and changing rules');
|
|
67
|
+
t.write(path.join(host, 'src', 'main.ts'), 'yours\n');
|
|
68
|
+
t.touch(path.join(host, 'src', 'main.ts'), 20);
|
|
69
|
+
t.write(path.join(paths.view, 'src', 'main.ts'), 'sandbox\n');
|
|
70
|
+
t.touch(path.join(paths.view, 'src', 'main.ts'), 30);
|
|
71
|
+
const stats = view.syncAll();
|
|
72
|
+
const conflicts = fs.readdirSync(path.join(host, 'src')).filter((n) => n.includes('.sandbox-conflict-'));
|
|
73
|
+
t.check(stats.conflicts === 1 && fs.readFileSync(path.join(host, 'src', 'main.ts'), 'utf8') === 'yours\n' && conflicts.length === 1 && fs.readFileSync(path.join(host, 'src', conflicts[0]), 'utf8') === 'sandbox\n', 'changed on both sides: yours stays, the sandbox\'s version is kept next to it');
|
|
74
|
+
t.equal(fs.readFileSync(path.join(paths.view, 'src', 'main.ts'), 'utf8'), 'yours\n', '... and the sandbox sees yours');
|
|
75
|
+
t.write(path.join(host, '.gitignore'), 'node_modules/\ndist/\n');
|
|
76
|
+
view.syncChanged(['.gitignore']);
|
|
77
|
+
t.check(inView('.env'), 'a file no longer ignored comes in when .gitignore changes');
|
|
78
|
+
t.equal(view.syncAll(), { toView: 0, toHost: 0, deleted: 0, conflicts: 0, skipped: 0 }, 'a pass with nothing changed copies nothing');
|
|
79
|
+
t.check(view.syncChanged(['../outside.txt', 'C:/Windows/x', '']).toHost === 0, 'paths from outside the project are ignored');
|
|
80
|
+
t.section('live');
|
|
81
|
+
const stop = view.watch(60000);
|
|
82
|
+
t.write(path.join(host, 'live.txt'), 'from you');
|
|
83
|
+
const arrived = await t.waitFor(() => inView('live.txt'), 5000);
|
|
84
|
+
t.write(path.join(paths.view, 'live-back.txt'), 'from the sandbox');
|
|
85
|
+
const back = await t.waitFor(() => fs.existsSync(path.join(host, 'live-back.txt')), 5000);
|
|
86
|
+
stop();
|
|
87
|
+
t.check(Boolean(arrived) && Boolean(back), 'changes on either side arrive within a second or two while it watches');
|
|
88
|
+
},
|
|
89
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Turning the folder you run in into a sandbox run: guest path, tool versions from version files, folders that are refused.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { voltaNode, autoGuestWorkspace } from '../core/workspace.js';
|
|
5
|
+
export const workspaceSuite = {
|
|
6
|
+
name: 'workspace',
|
|
7
|
+
title: 'workspace resolution, version files (.nvmrc ...), refused folders',
|
|
8
|
+
run: async (t, f) => {
|
|
9
|
+
t.section('guest folder and defaults');
|
|
10
|
+
const run = await f.run();
|
|
11
|
+
t.equal({ host: run.workspaceHost, guest: run.config.guestWorkspace, harness: run.harnessName, dataFolder: path.basename(run.dataPath) }, { host: f.workA, guest: 'C:\\Workspace\\work-a', harness: 'claude', dataFolder: 'testdata' }, 'any folder is the workspace; its guest path is C:\\Workspace\\<folder name>');
|
|
12
|
+
t.equal(autoGuestWorkspace('C:\\x\\My Proj (2)'), 'C:\\Workspace\\My Proj _2_', 'auto guest folder names are sanitised');
|
|
13
|
+
t.equal(autoGuestWorkspace('D:\\...'), 'C:\\Workspace\\project', 'a folder name with nothing usable becomes "project"');
|
|
14
|
+
t.equal({ node: run.versions.node.spec, nodeSource: run.versions.node.source, python: run.versions.python.spec }, { node: '22.3.0', nodeSource: 'this PC', python: '3' }, 'the default Node version is this PC\'s; Python defaults to the latest 3.x');
|
|
15
|
+
t.section('version files');
|
|
16
|
+
const nvmrc = path.join(f.workA, '.nvmrc');
|
|
17
|
+
const pythonVersion = path.join(f.workA, '.python-version');
|
|
18
|
+
t.write(nvmrc, 'lts/iron\n');
|
|
19
|
+
t.write(pythonVersion, '3.11\n');
|
|
20
|
+
const withFiles = await f.run();
|
|
21
|
+
t.equal({ node: withFiles.versions.node.spec, source: withFiles.versions.node.source, python: withFiles.versions.python.spec }, { node: 'lts/iron', source: '.nvmrc', python: '3.11' }, '.nvmrc and .python-version are respected');
|
|
22
|
+
t.equal((await f.run({ nodeVersion: '18' })).versions.node.source, 'command line', '--node beats .nvmrc');
|
|
23
|
+
fs.rmSync(nvmrc);
|
|
24
|
+
fs.rmSync(pythonVersion);
|
|
25
|
+
const nodeVersion = path.join(f.workA, '.node-version');
|
|
26
|
+
t.write(nodeVersion, 'v20.11.1');
|
|
27
|
+
t.equal((await f.run()).versions.node.spec, 'v20.11.1', '.node-version is respected');
|
|
28
|
+
fs.rmSync(nodeVersion);
|
|
29
|
+
const voltaProject = t.write(t.at('volta-ws', 'package.json'), '{"name":"x","volta":{"node":"20.11.1","pnpm":"9.1.0"}}');
|
|
30
|
+
t.write(t.at('no-volta-ws', 'package.json'), '{"name":"y","volta":{"node":"latest; rm -rf"}}');
|
|
31
|
+
t.equal(voltaNode(path.dirname(voltaProject)), '20.11.1', 'Volta: package.json "volta.node" picks the Node version');
|
|
32
|
+
t.equal(voltaNode(t.at('no-volta-ws')), null, 'Volta: only a plain version is accepted');
|
|
33
|
+
t.equal(voltaNode(t.at('missing')), null, 'Volta: a folder without package.json has no version');
|
|
34
|
+
t.section('folders that are refused');
|
|
35
|
+
await t.throws(() => f.run({ cwd: f.install }), 'install folder', 'the install folder is refused');
|
|
36
|
+
await t.throws(() => f.run({ cwd: t.temp }), 'must not be able to change', 'a parent of the install folder is refused');
|
|
37
|
+
await t.throws(() => f.run({ cwd: t.at('storage') }), 'storage folder', 'the storage folder is refused');
|
|
38
|
+
const originalWindir = process.env.WINDIR;
|
|
39
|
+
const fakeWindows = t.dir('winroot');
|
|
40
|
+
const systemFolder = t.dir('winroot', 'sys');
|
|
41
|
+
process.env.WINDIR = fakeWindows;
|
|
42
|
+
try {
|
|
43
|
+
await t.throws(() => f.run({ cwd: systemFolder }), 'system folder', 'a folder inside the Windows folder is refused');
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
if (originalWindir === undefined)
|
|
47
|
+
delete process.env.WINDIR;
|
|
48
|
+
else
|
|
49
|
+
process.env.WINDIR = originalWindir;
|
|
50
|
+
}
|
|
51
|
+
await t.throws(() => f.run({ cwd: path.parse(t.temp).root }), 'whole drive', 'a whole drive is refused');
|
|
52
|
+
},
|
|
53
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Config, GitAccess } from '../core/types.js';
|
|
2
|
+
export type AppCategory = 'editors' | 'databases' | 'api' | 'git' | 'utilities' | 'browsers' | 'gamedev' | 'cloud' | 'diagrams' | 'network' | 'cli';
|
|
3
|
+
export declare const APP_CATEGORIES: {
|
|
4
|
+
id: AppCategory;
|
|
5
|
+
label: string;
|
|
6
|
+
description: string;
|
|
7
|
+
}[];
|
|
8
|
+
/** Where a download comes from. Resolvers (sublime, kubectl ...) are code in guest/apps, named here by id. */
|
|
9
|
+
export type AppSource = {
|
|
10
|
+
github: string;
|
|
11
|
+
asset: string;
|
|
12
|
+
} | {
|
|
13
|
+
url: string;
|
|
14
|
+
} | {
|
|
15
|
+
resolve: string;
|
|
16
|
+
} | {
|
|
17
|
+
npm: string;
|
|
18
|
+
};
|
|
19
|
+
export interface AppEntry {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
category: AppCategory;
|
|
23
|
+
about: string;
|
|
24
|
+
size: string;
|
|
25
|
+
source: AppSource;
|
|
26
|
+
/** How the download is unpacked: an archive (zip/7z/tar, the default), the file itself, an NSIS installer or an MSI. */
|
|
27
|
+
unpack?: 'archive' | 'file' | 'nsis' | 'msi';
|
|
28
|
+
/** The program to start (or the tool on PATH): a file name, or a regex (/.../) for names with a version in them. */
|
|
29
|
+
exe: string;
|
|
30
|
+
/** A command-line tool: its folder goes on PATH (no desktop shortcut). */
|
|
31
|
+
cli?: boolean;
|
|
32
|
+
/** Needs these first: 7-Zip to unpack an installer, Mesa for OpenGL without a GPU. */
|
|
33
|
+
needs?: ('7zip' | 'mesa')[];
|
|
34
|
+
/** Settings elsewhere that this entry stands for (VS Code, Windows Terminal): shown in the catalogue, not installed by it. */
|
|
35
|
+
setting?: string;
|
|
36
|
+
/** Only with full git access (it pushes on its own, past the sandbox's git guard). */
|
|
37
|
+
fullGitOnly?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export declare const APPS: AppEntry[];
|
|
40
|
+
export declare const appById: (id: string) => AppEntry | undefined;
|
|
41
|
+
/** A catalogue `exe` that is a regex ("/.../") matches names with a version in them. */
|
|
42
|
+
export declare const exeMatcher: (exe: string) => ((name: string) => boolean);
|
|
43
|
+
/** The apps to install, in order: 7-Zip first when anything needs it to unpack. */
|
|
44
|
+
export declare const appsToInstall: (apps: Config["apps"], gitAccess: GitAccess) => {
|
|
45
|
+
entries: AppEntry[];
|
|
46
|
+
skipped: string[];
|
|
47
|
+
};
|
|
48
|
+
/** Does any chosen app draw with OpenGL (so the Mesa step must run first)? */
|
|
49
|
+
export declare const appsNeedMesa: (apps: Config["apps"] | undefined) => boolean;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export const APP_CATEGORIES = [
|
|
2
|
+
{ id: 'editors', label: 'Editors & IDEs', description: 'Code and text editors (VS Code is a setting of its own and on by default).' },
|
|
3
|
+
{ id: 'databases', label: 'Database clients', description: 'GUIs for the databases the sandbox runs (PostgreSQL, MySQL, MongoDB, Redis, SQLite, DynamoDB).' },
|
|
4
|
+
{ id: 'api', label: 'API clients', description: 'Send HTTP, REST and GraphQL requests to what you are building.' },
|
|
5
|
+
{ id: 'git', label: 'Git tools', description: 'Git GUIs and terminal UIs. They use the sandbox\'s git, so its git access rules apply.' },
|
|
6
|
+
{ id: 'utilities', label: 'Utilities', description: 'Archives, comparing files, finding files, process tools, hex editing.' },
|
|
7
|
+
{ id: 'browsers', label: 'Browsers', description: 'Browsers for testing web apps (Edge is already in Windows).' },
|
|
8
|
+
{ id: 'gamedev', label: 'Game development', description: 'Engines and editors. They draw with OpenGL on the CPU (Mesa) when the sandbox has no GPU.' },
|
|
9
|
+
{ id: 'cloud', label: 'Cloud & Kubernetes', description: 'Command-line tools for clouds, Kubernetes and infrastructure as code.' },
|
|
10
|
+
{ id: 'diagrams', label: 'Diagrams & docs', description: 'Diagrams and document conversion.' },
|
|
11
|
+
{ id: 'network', label: 'Network & HTTP', description: 'Inspect and test HTTP traffic and APIs.' },
|
|
12
|
+
{ id: 'cli', label: 'Command-line tools', description: 'Small tools developers reach for: JSON and YAML, search, diff, builds, media, load testing ...' },
|
|
13
|
+
];
|
|
14
|
+
const gh = (github, asset) => ({ github, asset });
|
|
15
|
+
export const APPS = [
|
|
16
|
+
// ---- editors
|
|
17
|
+
{ id: 'vscode', name: 'Visual Studio Code', category: 'editors', about: 'Code editor with debugging, set up for the sandbox\'s languages.', size: '150 MB', source: { resolve: 'setting' }, exe: 'Code.exe', setting: 'toolchains.vscode.enabled' },
|
|
18
|
+
{ id: 'windows-terminal', name: 'Windows Terminal', category: 'utilities', about: 'The sandbox window: tabs, panes, PowerShell 7 profile.', size: '40 MB', source: { resolve: 'setting' }, exe: 'WindowsTerminal.exe', setting: 'provisioning.terminal.enabled' },
|
|
19
|
+
{ id: 'notepadpp', name: 'Notepad++', category: 'editors', about: 'Fast text and code editor.', size: '8 MB', source: gh('notepad-plus-plus/notepad-plus-plus', '^npp\\.[\\d.]+\\.portable\\.x64\\.zip$'), exe: 'notepad++.exe' },
|
|
20
|
+
{ id: 'sublime', name: 'Sublime Text', category: 'editors', about: 'Fast code editor (paid licence, free to evaluate).', size: '28 MB', source: { resolve: 'sublime' }, exe: 'sublime_text.exe' },
|
|
21
|
+
{ id: 'neovim', name: 'Neovim', category: 'editors', about: 'Terminal text editor (nvim).', size: '12 MB', source: gh('neovim/neovim', '^nvim-win64\\.zip$'), exe: 'nvim.exe', cli: true },
|
|
22
|
+
// ---- database clients
|
|
23
|
+
{ id: 'dbeaver', name: 'DBeaver Community', category: 'databases', about: 'Universal database client: PostgreSQL, MySQL, SQLite, SQL Server and more.', size: '120 MB', source: gh('dbeaver/dbeaver', '^dbeaver-ce-[\\d.]+-windows-x86_64\\.zip$'), exe: 'dbeaver.exe' },
|
|
24
|
+
{ id: 'pgadmin', name: 'pgAdmin 4', category: 'databases', about: 'The PostgreSQL admin GUI (from the PostgreSQL binaries zip).', size: '330 MB', source: { resolve: 'pgadmin' }, exe: 'pgAdmin4.exe' },
|
|
25
|
+
{ id: 'heidisql', name: 'HeidiSQL', category: 'databases', about: 'Light client for MySQL, MariaDB, PostgreSQL and SQL Server.', size: '28 MB', source: gh('HeidiSQL/HeidiSQL', '^HeidiSQL_[\\d.]+_64_Portable\\.zip$'), exe: 'heidisql.exe' },
|
|
26
|
+
{ id: 'sqlitebrowser', name: 'DB Browser for SQLite', category: 'databases', about: 'Open, browse and edit SQLite files.', size: '21 MB', source: gh('sqlitebrowser/sqlitebrowser', '^DB\\.Browser\\.for\\.SQLite-v?[\\d.]+-win64\\.zip$'), exe: 'DB Browser for SQLite.exe' },
|
|
27
|
+
{ id: 'compass', name: 'MongoDB Compass', category: 'databases', about: 'The MongoDB GUI.', size: '220 MB', source: gh('mongodb-js/compass', '^mongodb-compass-[\\d.]+-win32-x64\\.zip$'), exe: 'MongoDBCompass.exe' },
|
|
28
|
+
{ id: 'redisinsight', name: 'Redis Insight', category: 'databases', about: 'The Redis GUI.', size: '110 MB', source: gh('redis/RedisInsight', '^Redis-Insight-win-installer\\.exe$'), unpack: 'nsis', exe: 'Redis Insight.exe', needs: ['7zip'] },
|
|
29
|
+
{ id: 'dbgate', name: 'DbGate', category: 'databases', about: 'Database manager for SQL and NoSQL databases.', size: '180 MB', source: gh('dbgate/dbgate', '^dbgate-[\\d.]+-win_x64\\.zip$'), exe: 'DbGate.exe' },
|
|
30
|
+
{ id: 'beekeeper', name: 'Beekeeper Studio', category: 'databases', about: 'SQL editor and database manager.', size: '245 MB', source: gh('beekeeper-studio/beekeeper-studio', '^Beekeeper-Studio-[\\d.]+-portable\\.exe$'), unpack: 'nsis', exe: 'Beekeeper Studio.exe', needs: ['7zip'] },
|
|
31
|
+
{ id: 'dynamodb-admin', name: 'dynamodb-admin', category: 'databases', about: 'Web GUI for DynamoDB Local: run dynamodb-admin, then open http://localhost:8001.', size: '5 MB', source: { npm: 'dynamodb-admin' }, exe: 'dynamodb-admin.cmd', cli: true },
|
|
32
|
+
// ---- API clients
|
|
33
|
+
{ id: 'bruno', name: 'Bruno', category: 'api', about: 'Offline, git-friendly API client (collections are files in your repo).', size: '175 MB', source: gh('usebruno/bruno', '^bruno_[\\d.]+_x64_win\\.zip$'), exe: 'Bruno.exe' },
|
|
34
|
+
{ id: 'insomnia', name: 'Insomnia', category: 'api', about: 'REST, GraphQL and gRPC client.', size: '590 MB', source: gh('Kong/insomnia', '^insomnia-[\\d.]+-full\\.nupkg$'), exe: 'Insomnia.exe' },
|
|
35
|
+
// ---- git
|
|
36
|
+
{ id: 'sourcegit', name: 'SourceGit', category: 'git', about: 'Fast open-source Git GUI.', size: '26 MB', source: gh('sourcegit-scm/sourcegit', '^sourcegit_[\\d.]+\\.win-x64\\.zip$'), exe: 'SourceGit.exe' },
|
|
37
|
+
{ id: 'githubdesktop', name: 'GitHub Desktop', category: 'git', about: 'Simple Git GUI from GitHub. It has its own git, so it needs provisioning.git.access "full".', size: '300 MB', source: gh('desktop/desktop', '^GitHubDesktop-[\\d.]+-x64-full\\.nupkg$'), exe: 'GitHubDesktop.exe', fullGitOnly: true },
|
|
38
|
+
{ id: 'lazygit', name: 'lazygit', category: 'git', about: 'Git in the terminal.', size: '7 MB', source: gh('jesseduffield/lazygit', '^lazygit_[\\d.]+_windows_x86_64\\.zip$'), exe: 'lazygit.exe', cli: true },
|
|
39
|
+
// ---- utilities
|
|
40
|
+
{ id: '7zip', name: '7-Zip', category: 'utilities', about: 'Archive manager (7z, zip, rar, tar ...). Also unpacks installers for other apps.', size: '2 MB', source: { resolve: '7zip' }, exe: '7zFM.exe' },
|
|
41
|
+
{ id: 'winmerge', name: 'WinMerge', category: 'utilities', about: 'Compare and merge files and folders.', size: '23 MB', source: gh('WinMerge/winmerge', '^winmerge-[\\d.]+-x64-exe\\.zip$'), exe: 'WinMergeU.exe' },
|
|
42
|
+
{ id: 'everything', name: 'Everything', category: 'utilities', about: 'Finds files by name instantly.', size: '2 MB', source: { resolve: 'everything' }, exe: 'Everything.exe' },
|
|
43
|
+
{ id: 'sysinternals', name: 'Sysinternals Suite', category: 'utilities', about: 'Process Explorer, Process Monitor, Autoruns, TCPView and more (on PATH).', size: '200 MB', source: { url: 'https://download.sysinternals.com/files/SysinternalsSuite.zip' }, exe: 'procexp64.exe' },
|
|
44
|
+
{ id: 'imhex', name: 'ImHex', category: 'utilities', about: 'Hex editor for reverse engineering and binary formats.', size: '80 MB', source: gh('WerWolv/ImHex', '^imhex-[\\d.]+-Windows-Portable-NoGPU-x86_64\\.zip$'), exe: 'imhex-gui.exe' },
|
|
45
|
+
// ---- browsers
|
|
46
|
+
{ id: 'chrome', name: 'Chrome for Testing', category: 'browsers', about: 'Chrome build made for automated tests (with chromedriver on PATH).', size: '170 MB', source: { resolve: 'chrome' }, exe: 'chrome.exe' },
|
|
47
|
+
{ id: 'firefox', name: 'Firefox', category: 'browsers', about: 'Firefox for testing.', size: '75 MB', source: { resolve: 'firefox' }, unpack: 'nsis', exe: 'firefox.exe', needs: ['7zip'] },
|
|
48
|
+
// ---- game development
|
|
49
|
+
{ id: 'godot', name: 'Godot', category: 'gamedev', about: 'Open-source 2D and 3D game engine.', size: '80 MB', source: gh('godotengine/godot', '^Godot_v[\\d.]+-stable_win64\\.exe\\.zip$'), exe: '/^Godot_v[\\d.]+-stable_win64\\.exe$/', needs: ['mesa'] },
|
|
50
|
+
{ id: 'blender', name: 'Blender', category: 'gamedev', about: '3D modelling, animation and rendering.', size: '400 MB', source: { resolve: 'blender' }, exe: 'blender.exe', needs: ['mesa'] },
|
|
51
|
+
{ id: 'love', name: 'LÖVE', category: 'gamedev', about: 'Lua framework for 2D games.', size: '4 MB', source: gh('love2d/love', '^love-[\\d.]+-win64\\.zip$'), exe: 'love.exe', needs: ['mesa'] },
|
|
52
|
+
{ id: 'defold', name: 'Defold', category: 'gamedev', about: 'Free 2D/3D game engine and editor.', size: '330 MB', source: gh('defold/defold', '^Defold-x86_64-win32\\.zip$'), exe: 'Defold.exe', needs: ['mesa'] },
|
|
53
|
+
{ id: 'pixelorama', name: 'Pixelorama', category: 'gamedev', about: 'Pixel-art and sprite editor.', size: '40 MB', source: gh('Orama-Interactive/Pixelorama', '^Pixelorama-Windows-64bit\\.zip$'), exe: 'Pixelorama.exe', needs: ['mesa'] },
|
|
54
|
+
{ id: 'tiled', name: 'Tiled', category: 'gamedev', about: 'Tile map editor.', size: '22 MB', source: gh('mapeditor/tiled', '^Tiled-[\\d.]+_Windows-10\\+_x86_64\\.msi$'), unpack: 'msi', exe: 'tiled.exe' },
|
|
55
|
+
// ---- cloud
|
|
56
|
+
{ id: 'kubectl', name: 'kubectl', category: 'cloud', about: 'Kubernetes command line.', size: '60 MB', source: { resolve: 'kubectl' }, unpack: 'file', exe: 'kubectl.exe', cli: true },
|
|
57
|
+
{ id: 'helm', name: 'Helm', category: 'cloud', about: 'Kubernetes package manager.', size: '20 MB', source: { resolve: 'helm' }, exe: 'helm.exe', cli: true },
|
|
58
|
+
{ id: 'k9s', name: 'k9s', category: 'cloud', about: 'Kubernetes cluster UI in the terminal.', size: '40 MB', source: gh('derailed/k9s', '^k9s_Windows_amd64\\.zip$'), exe: 'k9s.exe', cli: true },
|
|
59
|
+
{ id: 'terraform', name: 'Terraform', category: 'cloud', about: 'Infrastructure as code.', size: '30 MB', source: { resolve: 'terraform' }, exe: 'terraform.exe', cli: true },
|
|
60
|
+
{ id: 'azure-cli', name: 'Azure CLI', category: 'cloud', about: 'Manage Azure (az).', size: '90 MB', source: { url: 'https://aka.ms/installazurecliwindowszipx64' }, exe: 'az.cmd', cli: true },
|
|
61
|
+
{ id: 'gcloud', name: 'Google Cloud CLI', category: 'cloud', about: 'Manage Google Cloud (gcloud, gsutil, bq).', size: '120 MB', source: { url: 'https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-windows-x86_64-bundled-python.zip' }, exe: 'gcloud.cmd', cli: true },
|
|
62
|
+
{ id: 'aws-cli', name: 'AWS CLI', category: 'cloud', about: 'Manage AWS (aws).', size: '50 MB', source: { url: 'https://awscli.amazonaws.com/AWSCLIV2.msi' }, unpack: 'msi', exe: 'aws.exe', cli: true },
|
|
63
|
+
// ---- diagrams and docs
|
|
64
|
+
{ id: 'drawio', name: 'draw.io', category: 'diagrams', about: 'Diagram editor (flowcharts, architecture, UML).', size: '170 MB', source: gh('jgraph/drawio-desktop', '^draw\\.io-[\\d.]+-windows\\.zip$'), exe: 'draw.io.exe' },
|
|
65
|
+
{ id: 'pandoc', name: 'Pandoc', category: 'diagrams', about: 'Convert documents between Markdown, HTML, Word, PDF ...', size: '35 MB', source: gh('jgm/pandoc', '^pandoc-[\\d.]+-windows-x86_64\\.zip$'), exe: 'pandoc.exe', cli: true },
|
|
66
|
+
// ---- network
|
|
67
|
+
{ id: 'mitmproxy', name: 'mitmproxy', category: 'network', about: 'Intercepting HTTPS proxy (mitmweb opens a web UI).', size: '90 MB', source: { resolve: 'mitmproxy' }, exe: 'mitmweb.exe', cli: true },
|
|
68
|
+
{ id: 'xh', name: 'xh', category: 'network', about: 'Friendly HTTP requests from the terminal (like HTTPie).', size: '5 MB', source: gh('ducaale/xh', '^xh-v[\\d.]+-x86_64-pc-windows-msvc\\.zip$'), exe: 'xh.exe', cli: true },
|
|
69
|
+
{ id: 'grpcurl', name: 'grpcurl', category: 'network', about: 'curl for gRPC servers.', size: '10 MB', source: gh('fullstorydev/grpcurl', '^grpcurl_[\\d.]+_windows_x86_64\\.zip$'), exe: 'grpcurl.exe', cli: true },
|
|
70
|
+
{ id: 'k6', name: 'k6', category: 'network', about: 'Load testing with JavaScript scripts.', size: '30 MB', source: gh('grafana/k6', '^k6-v[\\d.]+-windows-amd64\\.zip$'), exe: 'k6.exe', cli: true },
|
|
71
|
+
// ---- command-line tools
|
|
72
|
+
{ id: 'jq', name: 'jq', category: 'cli', about: 'Query and reshape JSON.', size: '1 MB', source: gh('jqlang/jq', '^jq-windows-amd64\\.exe$'), unpack: 'file', exe: 'jq.exe', cli: true },
|
|
73
|
+
{ id: 'yq', name: 'yq', category: 'cli', about: 'Query and edit YAML, JSON, XML and TOML.', size: '10 MB', source: gh('mikefarah/yq', '^yq_windows_amd64\\.exe$'), unpack: 'file', exe: 'yq.exe', cli: true },
|
|
74
|
+
{ id: 'ripgrep', name: 'ripgrep', category: 'cli', about: 'Very fast recursive search (rg).', size: '2 MB', source: gh('BurntSushi/ripgrep', '^ripgrep-[\\d.]+-x86_64-pc-windows-msvc\\.zip$'), exe: 'rg.exe', cli: true },
|
|
75
|
+
{ id: 'fd', name: 'fd', category: 'cli', about: 'Fast, friendly alternative to find.', size: '2 MB', source: gh('sharkdp/fd', '^fd-v[\\d.]+-x86_64-pc-windows-msvc\\.zip$'), exe: 'fd.exe', cli: true },
|
|
76
|
+
{ id: 'bat', name: 'bat', category: 'cli', about: 'cat with syntax highlighting and git changes.', size: '3 MB', source: gh('sharkdp/bat', '^bat-v[\\d.]+-x86_64-pc-windows-msvc\\.zip$'), exe: 'bat.exe', cli: true },
|
|
77
|
+
{ id: 'fzf', name: 'fzf', category: 'cli', about: 'Fuzzy finder for the terminal.', size: '2 MB', source: gh('junegunn/fzf', '^fzf-[\\d.]+-windows_amd64\\.zip$'), exe: 'fzf.exe', cli: true },
|
|
78
|
+
{ id: 'delta', name: 'delta', category: 'cli', about: 'Readable git diffs with syntax highlighting.', size: '3 MB', source: gh('dandavison/delta', '^delta-[\\d.]+-x86_64-pc-windows-msvc\\.zip$'), exe: 'delta.exe', cli: true },
|
|
79
|
+
{ id: 'just', name: 'just', category: 'cli', about: 'Command runner (a simpler make for project tasks).', size: '2 MB', source: gh('casey/just', '^just-[\\d.]+-x86_64-pc-windows-msvc\\.zip$'), exe: 'just.exe', cli: true },
|
|
80
|
+
{ id: 'hyperfine', name: 'hyperfine', category: 'cli', about: 'Benchmark commands.', size: '1 MB', source: gh('sharkdp/hyperfine', '^hyperfine-v[\\d.]+-x86_64-pc-windows-msvc\\.zip$'), exe: 'hyperfine.exe', cli: true },
|
|
81
|
+
{ id: 'watchexec', name: 'watchexec', category: 'cli', about: 'Run a command when files change.', size: '5 MB', source: gh('watchexec/watchexec', '^watchexec-[\\d.]+-x86_64-pc-windows-msvc\\.zip$'), exe: 'watchexec.exe', cli: true },
|
|
82
|
+
{ id: 'protoc', name: 'protoc', category: 'cli', about: 'Protocol Buffers compiler.', size: '3 MB', source: gh('protocolbuffers/protobuf', '^protoc-[\\d.]+-win64\\.zip$'), exe: 'protoc.exe', cli: true },
|
|
83
|
+
{ id: 'ninja', name: 'Ninja', category: 'cli', about: 'Small, fast build system (CMake\'s favourite generator).', size: '1 MB', source: gh('ninja-build/ninja', '^ninja-win\\.zip$'), exe: 'ninja.exe', cli: true },
|
|
84
|
+
{ id: 'ffmpeg', name: 'FFmpeg', category: 'cli', about: 'Convert and process audio and video.', size: '200 MB', source: gh('BtbN/FFmpeg-Builds', '^ffmpeg-master-latest-win64-gpl\\.zip$'), exe: 'ffmpeg.exe', cli: true },
|
|
85
|
+
{ id: 'sqlite', name: 'SQLite tools', category: 'cli', about: 'sqlite3 command-line shell.', size: '5 MB', source: { resolve: 'sqlite' }, exe: 'sqlite3.exe', cli: true },
|
|
86
|
+
{ id: 'shellcheck', name: 'ShellCheck', category: 'cli', about: 'Finds bugs in shell scripts.', size: '10 MB', source: gh('koalaman/shellcheck', '^shellcheck-v[\\d.]+\\.zip$'), exe: 'shellcheck.exe', cli: true },
|
|
87
|
+
{ id: 'gitleaks', name: 'Gitleaks', category: 'cli', about: 'Finds secrets committed to git.', size: '8 MB', source: gh('gitleaks/gitleaks', '^gitleaks_[\\d.]+_windows_x64\\.zip$'), exe: 'gitleaks.exe', cli: true },
|
|
88
|
+
];
|
|
89
|
+
export const appById = (id) => APPS.find((a) => a.id === id);
|
|
90
|
+
/** A catalogue `exe` that is a regex ("/.../") matches names with a version in them. */
|
|
91
|
+
export const exeMatcher = (exe) => {
|
|
92
|
+
const regex = /^\/(.+)\/$/.exec(exe);
|
|
93
|
+
if (regex) {
|
|
94
|
+
const r = new RegExp(regex[1], 'i');
|
|
95
|
+
return (name) => r.test(name);
|
|
96
|
+
}
|
|
97
|
+
return (name) => name.toLowerCase() === exe.toLowerCase();
|
|
98
|
+
};
|
|
99
|
+
/** Your own program as a catalogue entry (its folder is apps\custom\<id>). */
|
|
100
|
+
const customEntry = (id, app) => ({
|
|
101
|
+
id: 'custom/' + id, name: app.name, category: 'utilities', about: 'Your own program.', size: '?',
|
|
102
|
+
source: app.github && app.asset ? { github: app.github, asset: app.asset } : { url: app.url },
|
|
103
|
+
unpack: app.unpack, exe: app.exe, cli: app.cli,
|
|
104
|
+
});
|
|
105
|
+
/** The apps to install, in order: 7-Zip first when anything needs it to unpack. */
|
|
106
|
+
export const appsToInstall = (apps, gitAccess) => {
|
|
107
|
+
const chosen = apps.install.flatMap((id) => { const e = appById(id); return e && !e.setting ? [e] : []; });
|
|
108
|
+
const skipped = chosen.filter((e) => e.fullGitOnly && gitAccess !== 'full').map((e) => e.name + ' (needs provisioning.git.access "full")');
|
|
109
|
+
const allowed = [...chosen.filter((e) => !(e.fullGitOnly && gitAccess !== 'full')), ...Object.entries(apps.custom).map(([id, app]) => customEntry(id, app))];
|
|
110
|
+
const needs7zip = allowed.some((e) => e.needs?.includes('7zip') || e.unpack === 'nsis') && !allowed.some((e) => e.id === '7zip');
|
|
111
|
+
return { entries: [...(needs7zip ? [appById('7zip')] : []), ...allowed], skipped };
|
|
112
|
+
};
|
|
113
|
+
/** Does any chosen app draw with OpenGL (so the Mesa step must run first)? */
|
|
114
|
+
export const appsNeedMesa = (apps) => Boolean(apps?.install.some((id) => appById(id)?.needs?.includes('mesa')));
|
|
@@ -15,9 +15,11 @@ export interface Checksums {
|
|
|
15
15
|
sha256?: string | null;
|
|
16
16
|
sha512?: string | null;
|
|
17
17
|
}
|
|
18
|
+
/** downloads.requireChecksum: from then on download() refuses anything it has no expected hash for. The guest sets it from session.json. */
|
|
19
|
+
export declare const setChecksumRequired: (on: boolean) => void;
|
|
18
20
|
/**
|
|
19
21
|
* Streams a download to disk, hashing as it goes: big archives (PostgreSQL is ~330 MB) never sit in memory, and the
|
|
20
|
-
* event loop stays free so other toolchains keep downloading meanwhile. A mismatch
|
|
22
|
+
* event loop stays free so other toolchains keep downloading meanwhile. A mismatch (or any failure) leaves no file behind.
|
|
21
23
|
*/
|
|
22
24
|
export declare const download: (url: string, destination: string, expected?: string | null | Checksums) => Promise<void>;
|
|
23
25
|
/** Runs a program without blocking the event loop (parallel downloads keep going); resolves with its exit code. */
|
package/dist/shared/download.js
CHANGED
|
@@ -43,12 +43,18 @@ export const fetchRetry = async (url, init = {}, attempts = 5, timeoutMs = 60000
|
|
|
43
43
|
/** Small lookups fail fast (2 tries, 20 s each) so an offline boot falls back to cached versions quickly. */
|
|
44
44
|
export const fetchJson = async (url) => await (await fetchRetry(url, { headers: { Accept: 'application/json' } }, 2, 20000)).json();
|
|
45
45
|
export const fetchText = async (url) => await (await fetchRetry(url)).text();
|
|
46
|
+
let checksumRequired = false;
|
|
47
|
+
/** downloads.requireChecksum: from then on download() refuses anything it has no expected hash for. The guest sets it from session.json. */
|
|
48
|
+
export const setChecksumRequired = (on) => { checksumRequired = on; };
|
|
46
49
|
/**
|
|
47
50
|
* Streams a download to disk, hashing as it goes: big archives (PostgreSQL is ~330 MB) never sit in memory, and the
|
|
48
|
-
* event loop stays free so other toolchains keep downloading meanwhile. A mismatch
|
|
51
|
+
* event loop stays free so other toolchains keep downloading meanwhile. A mismatch (or any failure) leaves no file behind.
|
|
49
52
|
*/
|
|
50
53
|
export const download = async (url, destination, expected = null) => {
|
|
51
54
|
const sums = typeof expected === 'string' || expected === null ? { sha256: expected } : expected;
|
|
55
|
+
if (checksumRequired && !sums.sha256?.trim() && !sums.sha512?.trim()) {
|
|
56
|
+
throw new Error('Refusing to download ' + path.basename(destination) + ' (' + url + '): no checksum is published for it, and downloads.requireChecksum is on. Turn that setting off to allow downloads that cannot be verified.');
|
|
57
|
+
}
|
|
52
58
|
const response = await fetchRetry(url, {}, 3, 20 * 60 * 1000);
|
|
53
59
|
if (!response.body)
|
|
54
60
|
throw new Error('Empty response for ' + url);
|
|
@@ -60,10 +66,19 @@ export const download = async (url, destination, expected = null) => {
|
|
|
60
66
|
});
|
|
61
67
|
const tap = new Transform({ transform(chunk, _encoding, done) { for (const c of checks)
|
|
62
68
|
c.hash.update(chunk); done(null, chunk); } });
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
// Written to <destination>.part and moved into place only once complete and verified, so an interrupted download never
|
|
70
|
+
// leaves a partial file under the real name.
|
|
71
|
+
const partial = destination + '.part';
|
|
72
|
+
try {
|
|
73
|
+
await pipeline(Readable.fromWeb(response.body), tap, fs.createWriteStream(partial));
|
|
74
|
+
if (checks.some((c) => c.hash.digest('hex') !== c.want))
|
|
75
|
+
throw new Error('Checksum mismatch for ' + url);
|
|
76
|
+
// Mapped folders refuse rename-over, so the old file goes first.
|
|
65
77
|
fs.rmSync(destination, { force: true });
|
|
66
|
-
|
|
78
|
+
fs.renameSync(partial, destination);
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
fs.rmSync(partial, { force: true });
|
|
67
82
|
}
|
|
68
83
|
};
|
|
69
84
|
/** Runs a program without blocking the event loop (parallel downloads keep going); resolves with its exit code. */
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** A host name or a "*.example.com" pattern (lower case, no scheme, no port). */
|
|
2
|
+
export declare const HOST_PATTERN: RegExp;
|
|
3
|
+
/**
|
|
4
|
+
* What "@developer" stands for in allowOnly: package registries, source hosts, toolchain downloads and the AI
|
|
5
|
+
* providers the agents talk to. Enough for an agent to install dependencies, push to GitHub and reach its model.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEVELOPER_HOSTS: string[];
|
|
8
|
+
/** The allowOnly list with "@developer" expanded, lower case, without duplicates. */
|
|
9
|
+
export declare const expandAllowList: (list: string[]) => string[];
|
|
10
|
+
/** Does `host` match one of the patterns? "*.example.com" matches its subdomains (not example.com itself). */
|
|
11
|
+
export declare const hostAllowed: (host: string, patterns: string[]) => boolean;
|
|
12
|
+
export declare const HOSTS_BEGIN = "# >>> windows-development-sandbox network.blockHosts";
|
|
13
|
+
export declare const HOSTS_END = "# <<< windows-development-sandbox network.blockHosts";
|
|
14
|
+
/** The hosts file with our block section replaced by `hosts` (each sent to 0.0.0.0 and ::); everything else stays. */
|
|
15
|
+
export declare const withBlockedHosts: (hostsFile: string, hosts: string[]) => string;
|
|
16
|
+
/** One forwarded port: <pc> on this PC (localhost only) reaches <sandbox> inside. */
|
|
17
|
+
export interface PortForward {
|
|
18
|
+
pc: number;
|
|
19
|
+
sandbox: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The port the sandbox's relay for forward `index` listens on (it connects on to localhost:<sandbox> inside, so a
|
|
23
|
+
* server that only listens on localhost is reachable too). Out of the range Windows hands out for outgoing connections.
|
|
24
|
+
*/
|
|
25
|
+
export declare const relayPort: (index: number) => number;
|
|
26
|
+
/** The address the sandbox's built-in proxy listens on (allowOnly). */
|
|
27
|
+
export declare const PROXY_PORT = 38080;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// The network settings as rules: which hosts the sandbox may reach (network.allowOnly), which never (blockHosts), and
|
|
2
|
+
// the ports forwarded from this PC into the sandbox. Pure (tested); the guest applies them (guest/setup/network.ts,
|
|
3
|
+
// guest/netproxy.ts) and the host runs the forwarders (host/port-forward.ts).
|
|
4
|
+
/** A host name or a "*.example.com" pattern (lower case, no scheme, no port). */
|
|
5
|
+
export const HOST_PATTERN = /^(\*\.)?([a-z0-9-]+\.)*[a-z0-9-]+$/i;
|
|
6
|
+
/**
|
|
7
|
+
* What "@developer" stands for in allowOnly: package registries, source hosts, toolchain downloads and the AI
|
|
8
|
+
* providers the agents talk to. Enough for an agent to install dependencies, push to GitHub and reach its model.
|
|
9
|
+
*/
|
|
10
|
+
export const DEVELOPER_HOSTS = [
|
|
11
|
+
// AI providers
|
|
12
|
+
'api.anthropic.com', 'claude.ai', '*.claude.ai', 'console.anthropic.com', 'statsig.anthropic.com', 'sentry.io', '*.sentry.io',
|
|
13
|
+
'api.openai.com', 'chatgpt.com', '*.openai.com', 'generativelanguage.googleapis.com', 'oauth2.googleapis.com', 'accounts.google.com',
|
|
14
|
+
// source hosts
|
|
15
|
+
'github.com', '*.github.com', '*.githubusercontent.com', 'codeload.github.com', 'gitlab.com', '*.gitlab.com', 'bitbucket.org',
|
|
16
|
+
// package registries
|
|
17
|
+
'registry.npmjs.org', '*.npmjs.org', 'registry.yarnpkg.com', 'pypi.org', 'files.pythonhosted.org', 'crates.io', 'index.crates.io', 'static.crates.io',
|
|
18
|
+
'proxy.golang.org', 'sum.golang.org', 'repo1.maven.org', 'repo.maven.apache.org', 'plugins.gradle.org', 'services.gradle.org', 'api.nuget.org',
|
|
19
|
+
'*.nuget.org', 'rubygems.org', 'index.rubygems.org', 'repo.packagist.org', 'packagist.org', 'jsr.io', 'deno.land',
|
|
20
|
+
// toolchains
|
|
21
|
+
'nodejs.org', '*.nodejs.org', 'static.rust-lang.org', 'go.dev', 'dl.google.com', 'aka.ms', 'download.visualstudio.microsoft.com',
|
|
22
|
+
'dotnetcli.azureedge.net', 'builds.dotnet.microsoft.com', 'api.adoptium.net', 'objects.githubusercontent.com', 'marketplace.visualstudio.com',
|
|
23
|
+
'*.gallerycdn.vsassets.io', '*.vscode-cdn.net', 'update.code.visualstudio.com',
|
|
24
|
+
];
|
|
25
|
+
/** The allowOnly list with "@developer" expanded, lower case, without duplicates. */
|
|
26
|
+
export const expandAllowList = (list) => [...new Set(list.flatMap((h) => (h.trim().toLowerCase() === '@developer' ? DEVELOPER_HOSTS : [h])).map((h) => h.trim().toLowerCase()).filter((h) => HOST_PATTERN.test(h)))];
|
|
27
|
+
/** Does `host` match one of the patterns? "*.example.com" matches its subdomains (not example.com itself). */
|
|
28
|
+
export const hostAllowed = (host, patterns) => {
|
|
29
|
+
const h = host.toLowerCase().replace(/\.$/, '');
|
|
30
|
+
return patterns.some((p) => (p.startsWith('*.') ? h.endsWith(p.slice(1)) && h.length > p.length - 1 : h === p));
|
|
31
|
+
};
|
|
32
|
+
export const HOSTS_BEGIN = '# >>> windows-development-sandbox network.blockHosts';
|
|
33
|
+
export const HOSTS_END = '# <<< windows-development-sandbox network.blockHosts';
|
|
34
|
+
/** The hosts file with our block section replaced by `hosts` (each sent to 0.0.0.0 and ::); everything else stays. */
|
|
35
|
+
export const withBlockedHosts = (hostsFile, hosts) => {
|
|
36
|
+
const begin = hostsFile.indexOf(HOSTS_BEGIN);
|
|
37
|
+
const end = hostsFile.indexOf(HOSTS_END);
|
|
38
|
+
const outside = begin >= 0 && end > begin ? hostsFile.slice(0, begin) + hostsFile.slice(end + HOSTS_END.length) : hostsFile;
|
|
39
|
+
const clean = [...new Set(hosts.map((h) => h.trim().toLowerCase()).filter((h) => HOST_PATTERN.test(h) && !h.startsWith('*.')))];
|
|
40
|
+
const base = outside.replace(/\s+$/, '');
|
|
41
|
+
if (!clean.length)
|
|
42
|
+
return base + '\r\n';
|
|
43
|
+
return [base, HOSTS_BEGIN, ...clean.flatMap((h) => ['0.0.0.0 ' + h, ':: ' + h]), HOSTS_END, ''].join('\r\n');
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The port the sandbox's relay for forward `index` listens on (it connects on to localhost:<sandbox> inside, so a
|
|
47
|
+
* server that only listens on localhost is reachable too). Out of the range Windows hands out for outgoing connections.
|
|
48
|
+
*/
|
|
49
|
+
export const relayPort = (index) => 38100 + index;
|
|
50
|
+
/** The address the sandbox's built-in proxy listens on (allowOnly). */
|
|
51
|
+
export const PROXY_PORT = 38080;
|
package/dist/shared/paths.d.ts
CHANGED
|
@@ -5,5 +5,10 @@ export declare const projectFolderName: (workspace: string) => string;
|
|
|
5
5
|
* C:\Mirror\F\A\b, and programs that resolve real paths (realpath, GetFinalPathNameByHandle) report that one.
|
|
6
6
|
*/
|
|
7
7
|
export declare const guestWorkspaceAliases: (guestWorkspace: string) => string[];
|
|
8
|
+
/**
|
|
9
|
+
* A file or folder name that is safe to recreate on this PC. The sandbox can create names Windows would treat as devices
|
|
10
|
+
* (CON.jsonl opens the console), as streams (a:b), or that end in a dot or space (silently renamed); none of them are copied.
|
|
11
|
+
*/
|
|
12
|
+
export declare const isSafeFileName: (name: string) => boolean;
|
|
8
13
|
/** Rewrites `from` to `to` wherever it appears as a whole path (raw, JSON-escaped or with forward slashes). */
|
|
9
14
|
export declare const translatePaths: (text: string, from: string, to: string) => string;
|
package/dist/shared/paths.js
CHANGED
|
@@ -10,6 +10,12 @@ export const guestWorkspaceAliases = (guestWorkspace) => {
|
|
|
10
10
|
const real = sandboxFolder(guestWorkspace);
|
|
11
11
|
return real.toLowerCase() === guestWorkspace.toLowerCase() ? [guestWorkspace] : [real, guestWorkspace];
|
|
12
12
|
};
|
|
13
|
+
const DEVICE_NAME = /^(con|prn|aux|nul|conin\$|conout\$|com[0-9¹²³]|lpt[0-9¹²³])(\..*)?$/i;
|
|
14
|
+
/**
|
|
15
|
+
* A file or folder name that is safe to recreate on this PC. The sandbox can create names Windows would treat as devices
|
|
16
|
+
* (CON.jsonl opens the console), as streams (a:b), or that end in a dot or space (silently renamed); none of them are copied.
|
|
17
|
+
*/
|
|
18
|
+
export const isSafeFileName = (name) => name !== '' && name.length <= 200 && name !== '.' && name !== '..' && !/[<>:"|?*/\\\x00-\x1f]/.test(name) && !/[. ]$/.test(name) && !DEVICE_NAME.test(name);
|
|
13
19
|
const jsonEscaped = (p) => JSON.stringify(p).slice(1, -1);
|
|
14
20
|
const escapeRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
15
21
|
const forward = (p) => p.replace(/\\/g, '/');
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** Where roots, boot-mapped projects and shared folders appear inside the sandbox. */
|
|
2
|
+
export declare const PROJECTS_GUEST = "C:\\Projects";
|
|
3
|
+
/** How a project got into the sandbox. */
|
|
4
|
+
export type Mechanism = 'launch' | 'root' | 'boot' | 'wsb';
|
|
5
|
+
/** One project the running sandbox works on. */
|
|
6
|
+
export interface ProjectEntry {
|
|
7
|
+
name: string;
|
|
8
|
+
host: string;
|
|
9
|
+
guest: string;
|
|
10
|
+
primary: boolean;
|
|
11
|
+
mechanism: Mechanism;
|
|
12
|
+
}
|
|
13
|
+
/** `child` is `parent` or somewhere below it (Windows paths, case-insensitive). */
|
|
14
|
+
export declare const isWithin: (parent: string, child: string) => boolean;
|
|
15
|
+
/**
|
|
16
|
+
* A project id (also its folder name under C:\Projects) for a folder: its name with only letters, digits, '-' and
|
|
17
|
+
* '_' (ids become file names on both sides), at most 48 characters, made unique against `taken` with -2, -3, ...
|
|
18
|
+
*/
|
|
19
|
+
export declare const hubName: (hostDir: string, taken: Iterable<string>) => string;
|
|
20
|
+
/** The project a folder on this PC belongs to: the most specific one that contains it (a project inside another wins). */
|
|
21
|
+
export declare const projectForHostDir: <P extends {
|
|
22
|
+
host: string;
|
|
23
|
+
}>(projects: P[], hostDir: string) => P | null;
|
|
24
|
+
/** The same for a folder inside the sandbox. */
|
|
25
|
+
export declare const projectForGuestDir: <P extends {
|
|
26
|
+
guest: string;
|
|
27
|
+
}>(projects: P[], guestDir: string) => P | null;
|
|
28
|
+
/** Where a folder on this PC appears inside the sandbox, given the mapped folder (project or root) that contains it. */
|
|
29
|
+
export declare const guestDirFor: (mapped: {
|
|
30
|
+
host: string;
|
|
31
|
+
guest: string;
|
|
32
|
+
}, hostDir: string) => string;
|
|
33
|
+
/** The other way: where a folder inside the sandbox is on this PC, given the mapped folder that contains it. */
|
|
34
|
+
export declare const hostDirFor: (mapped: {
|
|
35
|
+
host: string;
|
|
36
|
+
guest: string;
|
|
37
|
+
}, guestDir: string) => string;
|
|
38
|
+
/** Strictly below `parent` (never `parent` itself). */
|
|
39
|
+
export declare const isBelow: (parent: string, child: string) => boolean;
|
|
40
|
+
/** A guest folder some project can be in: below C:\Projects, or inside one of the roots (their guest paths). */
|
|
41
|
+
export declare const insideProjects: (guestDir: string, roots?: string[], projectsDir?: string) => boolean;
|
|
42
|
+
export interface RootMapping {
|
|
43
|
+
host: string;
|
|
44
|
+
guest: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Guest folders for the project roots and the projects mapped at boot: C:\Projects\<name>, unique; with
|
|
48
|
+
* guestWorkspace "mirror" a root appears at its PC path when the sandbox can show it there.
|
|
49
|
+
*/
|
|
50
|
+
export declare const planProjectMappings: (roots: string[], projects: string[], mirror: boolean, taken?: string[]) => {
|
|
51
|
+
roots: RootMapping[];
|
|
52
|
+
projects: RootMapping[];
|
|
53
|
+
};
|