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.
Files changed (426) hide show
  1. package/.sandbox-config.example.json +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +94 -2
  4. package/config.example.json +349 -0
  5. package/dist/api.d.ts +31 -0
  6. package/dist/api.js +31 -0
  7. package/dist/bin/agent-sandbox.d.ts +2 -0
  8. package/dist/bin/agent-sandbox.js +3 -0
  9. package/dist/bin/claude-host.d.ts +2 -0
  10. package/dist/bin/claude-host.js +5 -0
  11. package/dist/bin/claude-sbx.d.ts +2 -0
  12. package/dist/bin/claude-sbx.js +3 -0
  13. package/dist/bin/codex-sbx.d.ts +2 -0
  14. package/dist/bin/codex-sbx.js +3 -0
  15. package/dist/bin/gemini-sbx.d.ts +2 -0
  16. package/dist/bin/gemini-sbx.js +3 -0
  17. package/dist/bin/sandbox.d.ts +2 -0
  18. package/dist/bin/sandbox.js +4 -0
  19. package/dist/bin/sbx-hide.d.ts +2 -0
  20. package/dist/bin/sbx-hide.js +3 -0
  21. package/dist/bin/sbx-show.d.ts +2 -0
  22. package/dist/bin/sbx-show.js +3 -0
  23. package/dist/cli/args.d.ts +52 -0
  24. package/dist/cli/args.js +143 -0
  25. package/dist/cli/colors.d.ts +24 -0
  26. package/dist/cli/colors.js +42 -0
  27. package/dist/cli/commands/accounts.d.ts +2 -0
  28. package/dist/cli/commands/accounts.js +52 -0
  29. package/dist/cli/commands/agent-commands.d.ts +6 -0
  30. package/dist/cli/commands/agent-commands.js +61 -0
  31. package/dist/cli/commands/context.d.ts +23 -0
  32. package/dist/cli/commands/context.js +57 -0
  33. package/dist/cli/commands/data/clean.d.ts +15 -0
  34. package/dist/cli/commands/data/clean.js +101 -0
  35. package/dist/cli/commands/data/collect.d.ts +11 -0
  36. package/dist/cli/commands/data/collect.js +54 -0
  37. package/dist/cli/commands/data/index.d.ts +4 -0
  38. package/dist/cli/commands/data/index.js +45 -0
  39. package/dist/cli/commands/data/remove.d.ts +2 -0
  40. package/dist/cli/commands/data/remove.js +30 -0
  41. package/dist/cli/commands/index.d.ts +10 -0
  42. package/dist/cli/commands/index.js +10 -0
  43. package/dist/cli/commands/launch.d.ts +29 -0
  44. package/dist/cli/commands/launch.js +333 -0
  45. package/dist/cli/commands/maintenance.d.ts +8 -0
  46. package/dist/cli/commands/maintenance.js +154 -0
  47. package/dist/cli/commands/project.d.ts +29 -0
  48. package/dist/cli/commands/project.js +63 -0
  49. package/dist/cli/commands/projects.d.ts +14 -0
  50. package/dist/cli/commands/projects.js +146 -0
  51. package/dist/cli/commands/repair/checks.d.ts +2 -0
  52. package/dist/cli/commands/repair/checks.js +170 -0
  53. package/dist/cli/commands/repair/finders.d.ts +30 -0
  54. package/dist/cli/commands/repair/finders.js +108 -0
  55. package/dist/cli/commands/repair/index.d.ts +12 -0
  56. package/dist/cli/commands/repair/index.js +61 -0
  57. package/dist/cli/commands/repair/types.d.ts +26 -0
  58. package/dist/cli/commands/repair/types.js +3 -0
  59. package/dist/cli/commands/restart.d.ts +6 -0
  60. package/dist/cli/commands/restart.js +85 -0
  61. package/dist/cli/commands/sessions.d.ts +12 -0
  62. package/dist/cli/commands/sessions.js +257 -0
  63. package/dist/cli/commands/setup/checks.d.ts +13 -0
  64. package/dist/cli/commands/setup/checks.js +71 -0
  65. package/dist/cli/commands/setup/facts.d.ts +31 -0
  66. package/dist/cli/commands/setup/facts.js +60 -0
  67. package/dist/cli/commands/setup/index.d.ts +2 -0
  68. package/dist/cli/commands/setup/index.js +43 -0
  69. package/dist/cli/commands/user-config.d.ts +8 -0
  70. package/dist/cli/commands/user-config.js +41 -0
  71. package/dist/cli/history.d.ts +11 -0
  72. package/dist/cli/history.js +21 -0
  73. package/dist/cli/index.d.ts +20 -0
  74. package/dist/cli/index.js +121 -0
  75. package/dist/cli/manifest.d.ts +103 -0
  76. package/dist/cli/manifest.js +107 -0
  77. package/dist/cli/menu-multi.d.ts +12 -0
  78. package/dist/cli/menu-multi.js +82 -0
  79. package/dist/cli/menu.d.ts +14 -0
  80. package/dist/cli/menu.js +290 -0
  81. package/dist/cli/progress.d.ts +9 -0
  82. package/dist/cli/progress.js +49 -0
  83. package/dist/cli/settings/edit.d.ts +13 -0
  84. package/dist/cli/settings/edit.js +19 -0
  85. package/dist/cli/settings/index.d.ts +8 -0
  86. package/dist/cli/settings/index.js +131 -0
  87. package/dist/cli/settings/prompts.d.ts +7 -0
  88. package/dist/cli/settings/prompts.js +48 -0
  89. package/dist/cli/settings/schema.d.ts +33 -0
  90. package/dist/cli/settings/schema.js +29 -0
  91. package/dist/cli/validate.d.ts +17 -0
  92. package/dist/cli/validate.js +89 -0
  93. package/dist/core/config.d.ts +17 -0
  94. package/dist/core/config.js +106 -0
  95. package/dist/core/detect-databases.d.ts +15 -0
  96. package/dist/core/detect-databases.js +147 -0
  97. package/dist/core/detect-pwsh.d.ts +18 -0
  98. package/dist/core/detect-pwsh.js +111 -0
  99. package/dist/core/detect-toolchains.d.ts +30 -0
  100. package/dist/core/detect-toolchains.js +58 -0
  101. package/dist/core/disk.d.ts +16 -0
  102. package/dist/core/disk.js +55 -0
  103. package/dist/core/local-config.d.ts +20 -0
  104. package/dist/core/local-config.js +77 -0
  105. package/dist/core/project-policy.d.ts +15 -0
  106. package/dist/core/project-policy.js +35 -0
  107. package/dist/core/prompt.d.ts +4 -0
  108. package/dist/core/prompt.js +15 -0
  109. package/dist/core/safe-paths.d.ts +4 -0
  110. package/dist/core/safe-paths.js +37 -0
  111. package/dist/core/schema/common.d.ts +17 -0
  112. package/dist/core/schema/common.js +15 -0
  113. package/dist/core/schema/config.d.ts +594 -0
  114. package/dist/core/schema/config.js +63 -0
  115. package/dist/core/schema/databases.d.ts +33 -0
  116. package/dist/core/schema/databases.js +35 -0
  117. package/dist/core/schema/harness.d.ts +12 -0
  118. package/dist/core/schema/harness.js +17 -0
  119. package/dist/core/schema/json-schema.d.ts +5 -0
  120. package/dist/core/schema/json-schema.js +25 -0
  121. package/dist/core/schema/profile.d.ts +27 -0
  122. package/dist/core/schema/profile.js +29 -0
  123. package/dist/core/schema/projects.d.ts +9 -0
  124. package/dist/core/schema/projects.js +11 -0
  125. package/dist/core/schema/sandbox.d.ts +50 -0
  126. package/dist/core/schema/sandbox.js +36 -0
  127. package/dist/core/schema/toolchains.d.ts +93 -0
  128. package/dist/core/schema/toolchains.js +83 -0
  129. package/dist/core/schema.d.ts +6 -0
  130. package/dist/core/schema.js +8 -0
  131. package/dist/core/types.d.ts +190 -0
  132. package/dist/core/types.js +1 -0
  133. package/dist/core/util.d.ts +60 -0
  134. package/dist/core/util.js +154 -0
  135. package/dist/core/workspace.d.ts +18 -0
  136. package/dist/core/workspace.js +128 -0
  137. package/dist/guest/boot-env.d.ts +4 -0
  138. package/dist/guest/boot-env.js +25 -0
  139. package/dist/guest/cmdline.d.ts +12 -0
  140. package/dist/guest/cmdline.js +23 -0
  141. package/dist/guest/daemon/exec.d.ts +5 -0
  142. package/dist/guest/daemon/exec.js +32 -0
  143. package/dist/guest/daemon/project.d.ts +14 -0
  144. package/dist/guest/daemon/project.js +132 -0
  145. package/dist/guest/daemon/session.d.ts +13 -0
  146. package/dist/guest/daemon/session.js +139 -0
  147. package/dist/guest/daemon/shutdown.d.ts +1 -0
  148. package/dist/guest/daemon/shutdown.js +21 -0
  149. package/dist/guest/daemon/state.d.ts +17 -0
  150. package/dist/guest/daemon/state.js +31 -0
  151. package/dist/guest/daemon.d.ts +1 -0
  152. package/dist/guest/daemon.js +53 -0
  153. package/dist/guest/db.d.ts +1 -0
  154. package/dist/guest/db.js +75 -0
  155. package/dist/guest/env.d.ts +51 -0
  156. package/dist/guest/env.js +101 -0
  157. package/dist/guest/gitguard.d.ts +7 -0
  158. package/dist/guest/gitguard.js +60 -0
  159. package/dist/guest/gitrules.d.ts +2 -0
  160. package/dist/guest/gitrules.js +11 -0
  161. package/dist/guest/guide.d.ts +16 -0
  162. package/dist/guest/guide.js +69 -0
  163. package/dist/guest/helpers.d.ts +3 -0
  164. package/dist/guest/helpers.js +26 -0
  165. package/dist/guest/main.d.ts +1 -0
  166. package/dist/guest/main.js +130 -0
  167. package/dist/guest/ports.d.ts +6 -0
  168. package/dist/guest/ports.js +22 -0
  169. package/dist/guest/project-context.d.ts +7 -0
  170. package/dist/guest/project-context.js +10 -0
  171. package/dist/guest/project-env.d.ts +20 -0
  172. package/dist/guest/project-env.js +20 -0
  173. package/dist/guest/project-status.d.ts +5 -0
  174. package/dist/guest/project-status.js +17 -0
  175. package/dist/guest/ptyhost.d.ts +22 -0
  176. package/dist/guest/ptyhost.js +36 -0
  177. package/dist/guest/setup/agents.d.ts +11 -0
  178. package/dist/guest/setup/agents.js +87 -0
  179. package/dist/guest/setup/claude.d.ts +21 -0
  180. package/dist/guest/setup/claude.js +146 -0
  181. package/dist/guest/setup/environment.d.ts +16 -0
  182. package/dist/guest/setup/environment.js +69 -0
  183. package/dist/guest/setup/hooks.d.ts +9 -0
  184. package/dist/guest/setup/hooks.js +50 -0
  185. package/dist/guest/setup/network.d.ts +10 -0
  186. package/dist/guest/setup/network.js +99 -0
  187. package/dist/guest/setup/policy.d.ts +3 -0
  188. package/dist/guest/setup/policy.js +18 -0
  189. package/dist/guest/setup/user.d.ts +7 -0
  190. package/dist/guest/setup/user.js +47 -0
  191. package/dist/guest/toolchains/catalogue.d.ts +55 -0
  192. package/dist/guest/toolchains/catalogue.js +31 -0
  193. package/dist/guest/toolchains/cli.d.ts +10 -0
  194. package/dist/guest/toolchains/cli.js +72 -0
  195. package/dist/guest/toolchains/common.d.ts +54 -0
  196. package/dist/guest/toolchains/common.js +147 -0
  197. package/dist/guest/toolchains/databases/dynamodb.d.ts +8 -0
  198. package/dist/guest/toolchains/databases/dynamodb.js +52 -0
  199. package/dist/guest/toolchains/databases/index.d.ts +6 -0
  200. package/dist/guest/toolchains/databases/index.js +62 -0
  201. package/dist/guest/toolchains/databases/mongodb.d.ts +3 -0
  202. package/dist/guest/toolchains/databases/mongodb.js +31 -0
  203. package/dist/guest/toolchains/databases/mysql.d.ts +3 -0
  204. package/dist/guest/toolchains/databases/mysql.js +38 -0
  205. package/dist/guest/toolchains/databases/postgres.d.ts +5 -0
  206. package/dist/guest/toolchains/databases/postgres.js +38 -0
  207. package/dist/guest/toolchains/databases/redis.d.ts +3 -0
  208. package/dist/guest/toolchains/databases/redis.js +16 -0
  209. package/dist/guest/toolchains/databases/server.d.ts +14 -0
  210. package/dist/guest/toolchains/databases/server.js +52 -0
  211. package/dist/guest/toolchains/git.d.ts +3 -0
  212. package/dist/guest/toolchains/git.js +121 -0
  213. package/dist/guest/toolchains/go.d.ts +3 -0
  214. package/dist/guest/toolchains/go.js +33 -0
  215. package/dist/guest/toolchains/index.d.ts +17 -0
  216. package/dist/guest/toolchains/index.js +80 -0
  217. package/dist/guest/toolchains/java.d.ts +8 -0
  218. package/dist/guest/toolchains/java.js +96 -0
  219. package/dist/guest/toolchains/leftovers.d.ts +2 -0
  220. package/dist/guest/toolchains/leftovers.js +26 -0
  221. package/dist/guest/toolchains/managers.d.ts +16 -0
  222. package/dist/guest/toolchains/managers.js +84 -0
  223. package/dist/guest/toolchains/native/binstall.d.ts +7 -0
  224. package/dist/guest/toolchains/native/binstall.js +52 -0
  225. package/dist/guest/toolchains/native/cpp.d.ts +4 -0
  226. package/dist/guest/toolchains/native/cpp.js +38 -0
  227. package/dist/guest/toolchains/native/dotnet.d.ts +4 -0
  228. package/dist/guest/toolchains/native/dotnet.js +103 -0
  229. package/dist/guest/toolchains/native/mesa.d.ts +7 -0
  230. package/dist/guest/toolchains/native/mesa.js +46 -0
  231. package/dist/guest/toolchains/native/msvc/environment.d.ts +16 -0
  232. package/dist/guest/toolchains/native/msvc/environment.js +57 -0
  233. package/dist/guest/toolchains/native/msvc/index.d.ts +6 -0
  234. package/dist/guest/toolchains/native/msvc/index.js +48 -0
  235. package/dist/guest/toolchains/native/msvc/packages.d.ts +6 -0
  236. package/dist/guest/toolchains/native/msvc/packages.js +156 -0
  237. package/dist/guest/toolchains/native/msvc/tree.d.ts +15 -0
  238. package/dist/guest/toolchains/native/msvc/tree.js +21 -0
  239. package/dist/guest/toolchains/native/rust.d.ts +3 -0
  240. package/dist/guest/toolchains/native/rust.js +45 -0
  241. package/dist/guest/toolchains/node.d.ts +3 -0
  242. package/dist/guest/toolchains/node.js +152 -0
  243. package/dist/guest/toolchains/plan.d.ts +22 -0
  244. package/dist/guest/toolchains/plan.js +96 -0
  245. package/dist/guest/toolchains/pwsh.d.ts +4 -0
  246. package/dist/guest/toolchains/pwsh.js +14 -0
  247. package/dist/guest/toolchains/python.d.ts +3 -0
  248. package/dist/guest/toolchains/python.js +54 -0
  249. package/dist/guest/toolchains/runtimes.d.ts +19 -0
  250. package/dist/guest/toolchains/runtimes.js +38 -0
  251. package/dist/guest/toolchains/scripting/php.d.ts +3 -0
  252. package/dist/guest/toolchains/scripting/php.js +71 -0
  253. package/dist/guest/toolchains/scripting/ruby.d.ts +3 -0
  254. package/dist/guest/toolchains/scripting/ruby.js +47 -0
  255. package/dist/guest/toolchains/scripting/vcruntime.d.ts +7 -0
  256. package/dist/guest/toolchains/scripting/vcruntime.js +45 -0
  257. package/dist/guest/toolchains/versions.d.ts +47 -0
  258. package/dist/guest/toolchains/versions.js +77 -0
  259. package/dist/guest/toolchains/vscode.d.ts +2 -0
  260. package/dist/guest/toolchains/vscode.js +53 -0
  261. package/dist/guest/view.d.ts +1 -0
  262. package/dist/guest/view.js +42 -0
  263. package/dist/host/accounts.d.ts +32 -0
  264. package/dist/host/accounts.js +128 -0
  265. package/dist/host/agent-commands.d.ts +25 -0
  266. package/dist/host/agent-commands.js +145 -0
  267. package/dist/host/attach.d.ts +17 -0
  268. package/dist/host/attach.js +69 -0
  269. package/dist/host/channel.d.ts +38 -0
  270. package/dist/host/channel.js +162 -0
  271. package/dist/host/claude-profile.d.ts +15 -0
  272. package/dist/host/claude-profile.js +107 -0
  273. package/dist/host/cleanup.d.ts +23 -0
  274. package/dist/host/cleanup.js +57 -0
  275. package/dist/host/database-plan.d.ts +12 -0
  276. package/dist/host/database-plan.js +52 -0
  277. package/dist/host/generate.d.ts +7 -0
  278. package/dist/host/generate.js +98 -0
  279. package/dist/host/join.d.ts +28 -0
  280. package/dist/host/join.js +76 -0
  281. package/dist/host/launch/mappings.d.ts +3 -0
  282. package/dist/host/launch/mappings.js +50 -0
  283. package/dist/host/launch/projects.d.ts +15 -0
  284. package/dist/host/launch/projects.js +56 -0
  285. package/dist/host/launch/stage.d.ts +11 -0
  286. package/dist/host/launch/stage.js +66 -0
  287. package/dist/host/launch/wsb.d.ts +10 -0
  288. package/dist/host/launch/wsb.js +34 -0
  289. package/dist/host/pc-scan.d.ts +6 -0
  290. package/dist/host/pc-scan.js +37 -0
  291. package/dist/host/personalize.d.ts +10 -0
  292. package/dist/host/personalize.js +97 -0
  293. package/dist/host/platform.d.ts +19 -0
  294. package/dist/host/platform.js +126 -0
  295. package/dist/host/project-scan.d.ts +7 -0
  296. package/dist/host/project-scan.js +89 -0
  297. package/dist/host/projects.d.ts +100 -0
  298. package/dist/host/projects.js +147 -0
  299. package/dist/host/repo-guard.d.ts +36 -0
  300. package/dist/host/repo-guard.js +219 -0
  301. package/dist/host/secrets.d.ts +14 -0
  302. package/dist/host/secrets.js +133 -0
  303. package/dist/host/terminal.d.ts +32 -0
  304. package/dist/host/terminal.js +130 -0
  305. package/dist/host/test-guard.d.ts +6 -0
  306. package/dist/host/test-guard.js +15 -0
  307. package/dist/host/window.d.ts +4 -0
  308. package/dist/host/window.js +72 -0
  309. package/dist/host/wsb.d.ts +37 -0
  310. package/dist/host/wsb.js +101 -0
  311. package/dist/runtime/daemon.js +28494 -0
  312. package/dist/runtime/db.js +26387 -0
  313. package/dist/runtime/gitguard.js +84 -0
  314. package/dist/runtime/main.js +28622 -0
  315. package/dist/runtime/view.js +25841 -0
  316. package/dist/selftest/accounts.test.d.ts +2 -0
  317. package/dist/selftest/accounts.test.js +44 -0
  318. package/dist/selftest/agent-commands.test.d.ts +2 -0
  319. package/dist/selftest/agent-commands.test.js +47 -0
  320. package/dist/selftest/channel.test.d.ts +2 -0
  321. package/dist/selftest/channel.test.js +37 -0
  322. package/dist/selftest/cli.test.d.ts +2 -0
  323. package/dist/selftest/cli.test.js +118 -0
  324. package/dist/selftest/cmdline.test.d.ts +2 -0
  325. package/dist/selftest/cmdline.test.js +45 -0
  326. package/dist/selftest/config.test.d.ts +2 -0
  327. package/dist/selftest/config.test.js +38 -0
  328. package/dist/selftest/data.test.d.ts +2 -0
  329. package/dist/selftest/data.test.js +80 -0
  330. package/dist/selftest/detect.test.d.ts +2 -0
  331. package/dist/selftest/detect.test.js +58 -0
  332. package/dist/selftest/fixtures.d.ts +272 -0
  333. package/dist/selftest/fixtures.js +80 -0
  334. package/dist/selftest/generate.test.d.ts +2 -0
  335. package/dist/selftest/generate.test.js +63 -0
  336. package/dist/selftest/git-access.test.d.ts +2 -0
  337. package/dist/selftest/git-access.test.js +26 -0
  338. package/dist/selftest/guest-boot.test.d.ts +2 -0
  339. package/dist/selftest/guest-boot.test.js +76 -0
  340. package/dist/selftest/harness.d.ts +51 -0
  341. package/dist/selftest/harness.js +77 -0
  342. package/dist/selftest/host-sim.test.d.ts +2 -0
  343. package/dist/selftest/host-sim.test.js +93 -0
  344. package/dist/selftest/hostile-input.test.d.ts +2 -0
  345. package/dist/selftest/hostile-input.test.js +54 -0
  346. package/dist/selftest/index.d.ts +8 -0
  347. package/dist/selftest/index.js +155 -0
  348. package/dist/selftest/links.test.d.ts +2 -0
  349. package/dist/selftest/links.test.js +50 -0
  350. package/dist/selftest/live-daemon.test.d.ts +2 -0
  351. package/dist/selftest/live-daemon.test.js +91 -0
  352. package/dist/selftest/live-git.test.d.ts +2 -0
  353. package/dist/selftest/live-git.test.js +60 -0
  354. package/dist/selftest/live-projects.test.d.ts +2 -0
  355. package/dist/selftest/live-projects.test.js +127 -0
  356. package/dist/selftest/live.d.ts +65 -0
  357. package/dist/selftest/live.js +109 -0
  358. package/dist/selftest/local-config.test.d.ts +2 -0
  359. package/dist/selftest/local-config.test.js +107 -0
  360. package/dist/selftest/mirror.test.d.ts +2 -0
  361. package/dist/selftest/mirror.test.js +101 -0
  362. package/dist/selftest/network.test.d.ts +2 -0
  363. package/dist/selftest/network.test.js +28 -0
  364. package/dist/selftest/personalization.test.d.ts +2 -0
  365. package/dist/selftest/personalization.test.js +53 -0
  366. package/dist/selftest/projects.test.d.ts +2 -0
  367. package/dist/selftest/projects.test.js +254 -0
  368. package/dist/selftest/repair.test.d.ts +2 -0
  369. package/dist/selftest/repair.test.js +42 -0
  370. package/dist/selftest/repo-guard.test.d.ts +2 -0
  371. package/dist/selftest/repo-guard.test.js +63 -0
  372. package/dist/selftest/secrets.test.d.ts +2 -0
  373. package/dist/selftest/secrets.test.js +71 -0
  374. package/dist/selftest/setup.test.d.ts +2 -0
  375. package/dist/selftest/setup.test.js +46 -0
  376. package/dist/selftest/suite.d.ts +13 -0
  377. package/dist/selftest/suite.js +1 -0
  378. package/dist/selftest/sync.test.d.ts +2 -0
  379. package/dist/selftest/sync.test.js +94 -0
  380. package/dist/selftest/test-guard.test.d.ts +2 -0
  381. package/dist/selftest/test-guard.test.js +49 -0
  382. package/dist/selftest/toolchains.test.d.ts +2 -0
  383. package/dist/selftest/toolchains.test.js +103 -0
  384. package/dist/selftest/workspace.test.d.ts +2 -0
  385. package/dist/selftest/workspace.test.js +53 -0
  386. package/dist/shared/download.d.ts +60 -0
  387. package/dist/shared/download.js +215 -0
  388. package/dist/shared/guest-paths.d.ts +30 -0
  389. package/dist/shared/guest-paths.js +61 -0
  390. package/dist/shared/netrules.d.ts +18 -0
  391. package/dist/shared/netrules.js +98 -0
  392. package/dist/shared/paths.d.ts +14 -0
  393. package/dist/shared/paths.js +28 -0
  394. package/dist/shared/projects.d.ts +53 -0
  395. package/dist/shared/projects.js +67 -0
  396. package/dist/shared/pty-client.d.ts +86 -0
  397. package/dist/shared/pty-client.js +227 -0
  398. package/dist/shared/rundir.d.ts +39 -0
  399. package/dist/shared/rundir.js +94 -0
  400. package/dist/shared/safe-remove.d.ts +10 -0
  401. package/dist/shared/safe-remove.js +66 -0
  402. package/dist/shared/schema.d.ts +527 -0
  403. package/dist/shared/schema.js +90 -0
  404. package/dist/shared/system.d.ts +15 -0
  405. package/dist/shared/system.js +19 -0
  406. package/dist/shared/test-guard.d.ts +4 -0
  407. package/dist/shared/test-guard.js +37 -0
  408. package/dist/sync/claude-mem.d.ts +8 -0
  409. package/dist/sync/claude-mem.js +79 -0
  410. package/dist/sync/history.d.ts +41 -0
  411. package/dist/sync/history.js +161 -0
  412. package/dist/sync/watcher.d.ts +21 -0
  413. package/dist/sync/watcher.js +135 -0
  414. package/hooks/post-agent/README.txt +1 -0
  415. package/hooks/pre-provision/README.txt +1 -0
  416. package/package.json +91 -4
  417. package/sandbox/bootstrap.ps1 +258 -0
  418. package/sandbox/cpucap.ps1 +51 -0
  419. package/sandbox/gitshim.cs +35 -0
  420. package/sandbox/logon.cmd +18 -0
  421. package/sandbox/openssl.cs +42 -0
  422. package/sandbox/ptyhost.cs +614 -0
  423. package/sandbox/trust.cmd +35 -0
  424. package/sandbox/vswhere.cs +83 -0
  425. package/schemas/config.schema.json +1161 -0
  426. package/schemas/sandbox-config.schema.json +968 -0
@@ -0,0 +1,45 @@
1
+ // Rust: rustup + cargo, with crates installed prebuilt where possible (binstall.ts).
2
+ import path from 'node:path';
3
+ import { dryRun, execAsync, exists, log } from '../../env.js';
4
+ import { arch, downloadFile, installOnce, tool, versionOf } from '../common.js';
5
+ import { crateInstaller } from './binstall.js';
6
+ import { installedMsvcEnv } from './msvc/index.js';
7
+ export const setupRust = async (env, config, refresh) => {
8
+ const RUSTUP_HOME = tool('rust', 'rustup');
9
+ const CARGO_HOME = tool('rust', 'cargo');
10
+ const rustEnv = { env: { ...process.env, RUSTUP_HOME, CARGO_HOME } };
11
+ const rustup = path.join(CARGO_HOME, 'bin', 'rustup.exe');
12
+ if (dryRun) {
13
+ log('(dry run) would provision Rust ' + config.toolchain + ' (' + config.host + ')');
14
+ return;
15
+ }
16
+ if (!exists(rustup)) {
17
+ const init = tool('rust', 'rustup-init.exe');
18
+ log('Installing Rust with rustup (~300 MB once; the toolchain downloads after rustup itself) ...');
19
+ await downloadFile('https://static.rust-lang.org/rustup/dist/' + (arch === 'arm64' ? 'aarch64' : 'x86_64') + '-pc-windows-msvc/rustup-init.exe', init);
20
+ await execAsync(init, ['-y', '--no-modify-path', '--profile', 'default', '--default-host', config.host, '--default-toolchain', config.toolchain], rustEnv);
21
+ }
22
+ else {
23
+ // The host can change (GNU <-> msvc with toolchains.msvc): install the toolchain for it; a no-op when it is there.
24
+ await execAsync(rustup, ['set', 'default-host', config.host], { ...rustEnv, stdio: 'ignore' });
25
+ await execAsync(rustup, ['toolchain', 'install', config.toolchain, '--profile', 'default', '--no-self-update'], { ...rustEnv, stdio: 'ignore' });
26
+ await execAsync(rustup, ['default', config.toolchain], { ...rustEnv, stdio: 'ignore' });
27
+ if (refresh)
28
+ await execAsync(rustup, ['update'], rustEnv);
29
+ }
30
+ Object.assign(env.vars, { RUSTUP_HOME, CARGO_HOME });
31
+ env.prepend.push(path.join(CARGO_HOME, 'bin'));
32
+ env.summary.push('rust: ' + versionOf(path.join(CARGO_HOME, 'bin', 'rustc.exe'), ['--version'], rustEnv) + ' [' + config.host + ']');
33
+ const toolchainKey = 'rustup@' + config.toolchain;
34
+ await installOnce(env, toolchainKey + ' components', config.components, (c) => execAsync(rustup, ['component', 'add', c], rustEnv));
35
+ await installOnce(env, toolchainKey + ' targets', config.targets, (t) => execAsync(rustup, ['target', 'add', t], rustEnv));
36
+ // cargo install links with MSVC's link.exe (msvc host) or MinGW's gcc (GNU host). Those steps ran first, but their
37
+ // PATH only reaches the agent at the end, so it is put together here.
38
+ const msvc = config.host.endsWith('-msvc') ? installedMsvcEnv() : null;
39
+ const mingw = tool('mingw', 'mingw64', 'bin');
40
+ const cargoEnv = msvc
41
+ ? { env: { ...rustEnv.env, ...msvc.vars, PATH: [path.join(CARGO_HOME, 'bin'), ...msvc.path, process.env.PATH ?? ''].join(path.delimiter) } }
42
+ : { env: { ...rustEnv.env, CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER: path.join(mingw, 'gcc.exe'), PATH: [path.join(CARGO_HOME, 'bin'), mingw, process.env.PATH ?? ''].join(path.delimiter) } };
43
+ // Prebuilt through cargo-binstall where the crate publishes a Windows build, else compiled (same "cargo" keys as before).
44
+ await installOnce(env, 'cargo', config.crates, crateInstaller(CARGO_HOME, cargoEnv));
45
+ };
@@ -0,0 +1,3 @@
1
+ import type { Toolchains, ToolVersions } from '../../core/types.js';
2
+ import { type ToolEnv } from './common.js';
3
+ export declare const setupNode: (env: ToolEnv, versions: ToolVersions, config: Toolchains["node"]) => Promise<void>;
@@ -0,0 +1,152 @@
1
+ // Node through nvm-windows. Versions live in C:\AgentData\tools\nvm (persistent); the active-version symlink lives on
2
+ // the sandbox's own disk (C:\nvm\nodejs), because a symlink inside a mapped folder would point at the host's paths.
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { spawn } from 'node:child_process';
6
+ import { fetchJson, githubAsset, installArchive } from '../../shared/download.js';
7
+ import { safeRemove } from '../../shared/safe-remove.js';
8
+ import { copyNode, dryRun, errMsg, execAsync, exists, hostToolsDir, localDir, log, mkdirp, warn } from '../env.js';
9
+ import { arch, installOnce, resolveCached, tool } from './common.js';
10
+ import { duringSession } from '../project-context.js';
11
+ import { pickNodeVersion, pickSpreadVersions } from './versions.js';
12
+ const NVM_HOME = tool('nvm');
13
+ const NVM_SYMLINK = path.join(localDir, 'nodejs');
14
+ const HOST_NVM = path.join(hostToolsDir, 'nvm');
15
+ /** Most Node versions taken from this PC's nvm, the selected one included. */
16
+ const HOST_VERSIONS_MAX = 3;
17
+ const hostNvmVersions = () => exists(HOST_NVM)
18
+ ? fs.readdirSync(HOST_NVM).filter((d) => /^v\d+\.\d+\.\d+$/.test(d) && exists(path.join(HOST_NVM, d, 'node.exe'))).map((d) => d.slice(1))
19
+ : [];
20
+ const resolveNode = (spec) => resolveCached('node:' + spec, async () => {
21
+ const exact = spec.replace(/^v/, '');
22
+ if (/^\d+\.\d+\.\d+$/.test(exact))
23
+ return exact;
24
+ const releases = await fetchJson('https://nodejs.org/dist/index.json');
25
+ return pickNodeVersion(releases, spec) ?? Promise.reject(new Error('No Node.js release matches "' + spec + '"'));
26
+ });
27
+ /** nvm unpacks here and then moves the version into NVM_HOME: a move from the sandbox's own disk into a mapped folder
28
+ * fails silently, so it must be on the same (mapped) disk. */
29
+ const NVM_TEMP = path.join(NVM_HOME, '.tmp');
30
+ /** nvm, without blocking the event loop (`nvm install` downloads). Resolves with its output. */
31
+ const nvm = (args) => new Promise((resolve) => {
32
+ const child = spawn(path.join(NVM_HOME, 'nvm.exe'), args, { windowsHide: true, env: { ...process.env, NVM_HOME, NVM_SYMLINK, TEMP: NVM_TEMP, TMP: NVM_TEMP } });
33
+ let out = '';
34
+ child.stdout.on('data', (d) => { out += d.toString(); });
35
+ child.stderr.on('data', (d) => { out += d.toString(); });
36
+ child.once('error', (e) => resolve(out + e.message));
37
+ child.once('close', () => resolve(out));
38
+ });
39
+ /** nvm itself: copied from the host's nvm when there is one, else the portable build. */
40
+ const ensureNvm = async () => {
41
+ if (exists(path.join(NVM_HOME, 'nvm.exe')))
42
+ return;
43
+ if (exists(path.join(HOST_NVM, 'nvm.exe'))) {
44
+ log('Copying nvm from this PC ...');
45
+ mkdirp(NVM_HOME);
46
+ // nvm.exe last: it is what marks the install as done, so an interrupted copy is redone.
47
+ const files = fs.readdirSync(HOST_NVM).filter((f) => /\.(exe|cmd|vbs)$/i.test(f)).sort((a, b) => Number(a.toLowerCase() === 'nvm.exe') - Number(b.toLowerCase() === 'nvm.exe'));
48
+ for (const file of files)
49
+ fs.copyFileSync(path.join(HOST_NVM, file), path.join(NVM_HOME, file));
50
+ return;
51
+ }
52
+ // nvm-windows 2.x only ships installers; the portable build is the newest 1.x release that has nvm-noinstall.zip.
53
+ const asset = await githubAsset('coreybutler/nvm-windows', /^nvm-noinstall\.zip$/, true);
54
+ log('Downloading nvm-windows (' + asset.url.split('/').slice(-2, -1)[0] + ') ...');
55
+ await installArchive(asset.url, NVM_HOME, { sha256: asset.sha256, locate: 'nvm.exe', fileName: asset.name });
56
+ };
57
+ /** A Node version: from the host's nvm when it has it (fast, offline), else `nvm install`. */
58
+ const installNodeVersion = async (version) => {
59
+ const dir = path.join(NVM_HOME, 'v' + version);
60
+ const fromHost = path.join(HOST_NVM, 'v' + version);
61
+ if (exists(path.join(dir, 'node.exe')))
62
+ return;
63
+ if (exists(path.join(fromHost, 'node.exe'))) {
64
+ log('Copying Node ' + version + ' from this PC\'s nvm (without global packages) ...');
65
+ copyNode(fromHost, dir);
66
+ return;
67
+ }
68
+ log('Downloading Node ' + version + ' (~30 MB once) ...');
69
+ mkdirp(NVM_TEMP);
70
+ let out;
71
+ try {
72
+ out = await nvm(['install', version]);
73
+ }
74
+ finally {
75
+ safeRemove(NVM_TEMP, NVM_HOME);
76
+ }
77
+ if (!exists(path.join(dir, 'node.exe')))
78
+ throw new Error('nvm install ' + version + ' failed: ' + out.trim().split('\n').slice(-2).join(' '));
79
+ };
80
+ const activate = async (version) => {
81
+ await nvm(['use', version]);
82
+ if (exists(path.join(NVM_SYMLINK, 'node.exe')))
83
+ return;
84
+ fs.rmSync(NVM_SYMLINK, { recursive: true, force: true });
85
+ fs.symlinkSync(path.join(NVM_HOME, 'v' + version), NVM_SYMLINK, 'junction');
86
+ };
87
+ export const setupNode = async (env, versions, config) => {
88
+ if (dryRun) {
89
+ log('(dry run) would provision nvm and Node ' + versions.node.spec + ' (from ' + versions.node.source + ')');
90
+ return;
91
+ }
92
+ await ensureNvm();
93
+ fs.writeFileSync(path.join(NVM_HOME, 'settings.txt'), ['root: ' + NVM_HOME, 'path: ' + NVM_SYMLINK, 'arch: ' + (arch === 'arm64' ? 'arm64' : '64'), 'proxy: none', ''].join('\r\n'));
94
+ mkdirp(localDir);
95
+ for (const extra of versions.nodeExtra) {
96
+ try {
97
+ await installNodeVersion(await resolveNode(extra));
98
+ }
99
+ catch (e) {
100
+ warn('Extra Node ' + extra + ': ' + errMsg(e));
101
+ }
102
+ }
103
+ const main = await resolveNode(versions.node.spec);
104
+ await installNodeVersion(main);
105
+ // A few more of this PC's versions (copied, never downloaded) so `nvm use` works offline; capped to keep boot fast.
106
+ for (const version of pickSpreadVersions(hostNvmVersions(), main, HOST_VERSIONS_MAX).slice(1)) {
107
+ try {
108
+ await installNodeVersion(version);
109
+ }
110
+ catch (e) {
111
+ warn('Node ' + version + ' from this PC: ' + errMsg(e));
112
+ }
113
+ }
114
+ Object.assign(env.vars, { NVM_HOME, NVM_SYMLINK });
115
+ // At boot, nvm's symlink selects the version. A project added later must not switch it under the sessions already
116
+ // running: its own version's folder goes first on its sessions' PATH instead.
117
+ if (duringSession())
118
+ env.prepend.push(path.join(NVM_HOME, 'v' + main), NVM_HOME, NVM_SYMLINK);
119
+ else {
120
+ await activate(main);
121
+ env.prepend.push(NVM_HOME, NVM_SYMLINK);
122
+ }
123
+ env.summary.push('node: ' + main + ' (from ' + versions.node.source + ', switch with nvm use)');
124
+ await packageManagers(env, config, main);
125
+ };
126
+ /** Global npm packages (per Node version, like nvm keeps them) and pnpm/yarn through corepack. */
127
+ const packageManagers = async (env, config, version) => {
128
+ const dir = path.join(NVM_HOME, 'v' + version);
129
+ const nodeEnv = { ...process.env, PATH: dir + path.delimiter + (process.env.PATH ?? '') };
130
+ const npm = (args) => execAsync(path.join(dir, 'node.exe'), [path.join(dir, 'node_modules', 'npm', 'bin', 'npm-cli.js'), ...args], { env: nodeEnv, stdio: 'ignore' });
131
+ const corepackJs = path.join(dir, 'node_modules', 'corepack', 'dist', 'corepack.js');
132
+ // Versions copied from this PC come without their global packages, corepack included, and Node 25+ dropped it.
133
+ const extra = config.corepack && !exists(corepackJs) ? ['corepack'] : [];
134
+ await installOnce(env, 'npm@' + version, [...extra, ...config.globalPackages], (pkg) => npm(['install', '--global', pkg, '--no-fund', '--no-audit']));
135
+ if (!config.corepack)
136
+ return;
137
+ // Without a packageManager pin, corepack's own known-good versions: the newest pnpm can be one this corepack cannot
138
+ // start. Mapped folders allow no symlinks, so pnpm uses a flat (hoisted) node_modules instead of its linked layout.
139
+ Object.assign(env.vars, { COREPACK_HOME: tool('corepack'), COREPACK_ENABLE_DOWNLOAD_PROMPT: '0', COREPACK_DEFAULT_TO_LATEST: '0', npm_config_node_linker: 'hoisted' });
140
+ // The shims are shared by every session: a project added later keeps the ones made at boot.
141
+ if (duringSession() && exists(tool('bin', 'pnpm.cmd'))) {
142
+ env.summary.push('pnpm, yarn: via corepack');
143
+ return;
144
+ }
145
+ try {
146
+ await execAsync(path.join(dir, 'node.exe'), [corepackJs, 'enable', '--install-directory', mkdirp(tool('bin')), 'pnpm', 'yarn'], { env: nodeEnv, stdio: 'ignore' });
147
+ env.summary.push('pnpm, yarn: via corepack (package.json "packageManager" picks the version)');
148
+ }
149
+ catch (e) {
150
+ warn('corepack enable failed: ' + errMsg(e));
151
+ }
152
+ };
@@ -0,0 +1,22 @@
1
+ import type { DatabasePlan, GitAccess, Toolchains, ToolVersions } from '../../core/types.js';
2
+ import { type ToolEnv } from './common.js';
3
+ export interface ToolchainOptions {
4
+ gitEnabled: boolean;
5
+ gitAccess: GitAccess;
6
+ hostGit: boolean;
7
+ refresh: boolean;
8
+ databases: DatabasePlan[];
9
+ }
10
+ /**
11
+ * shared: the step's effect is machine-wide and the same for every project (an installer, a registry entry, a
12
+ * shortcut, the git guard). A project added after boot reuses what the boot's run contributed instead of running it
13
+ * again under the sessions that already use it.
14
+ */
15
+ export interface Step {
16
+ name: string;
17
+ on: boolean;
18
+ after?: string[];
19
+ shared?: boolean;
20
+ run: (env: ToolEnv) => Promise<void> | void;
21
+ }
22
+ export declare const plan: (config: Toolchains, versions: ToolVersions, o: ToolchainOptions) => Step[];
@@ -0,0 +1,96 @@
1
+ // Which toolchain and database steps this sandbox runs, in the order their PATH entries are listed, and what each
2
+ // step depends on. The scheduler (index.ts) runs them.
3
+ import path from 'node:path';
4
+ import { dataDir, execAsync, exists, localDir } from '../env.js';
5
+ import { projectWorkspace } from '../project-context.js';
6
+ import { arch, installOnce, tool, wanted } from './common.js';
7
+ import { DATABASE_NAMES, setupDatabase } from './databases/index.js';
8
+ import { setupGit } from './git.js';
9
+ import { setupGo } from './go.js';
10
+ import { setupJava } from './java.js';
11
+ import { setupCli } from './cli.js';
12
+ import { setupPyenv, setupVolta, wantsPyenv, wantsVolta } from './managers.js';
13
+ import { setupCpp } from './native/cpp.js';
14
+ import { setupDotnet } from './native/dotnet.js';
15
+ import { setupMesa } from './native/mesa.js';
16
+ import { setupMsvc } from './native/msvc/index.js';
17
+ import { setupRust } from './native/rust.js';
18
+ import { setupNode } from './node.js';
19
+ import { setupPwsh } from './pwsh.js';
20
+ import { setupPython } from './python.js';
21
+ import { setupBun, setupDeno, setupUv, uvVars } from './runtimes.js';
22
+ import { setupPhp } from './scripting/php.js';
23
+ import { setupRuby } from './scripting/ruby.js';
24
+ import { setupVcRuntime } from './scripting/vcruntime.js';
25
+ import { setupVscode } from './vscode.js';
26
+ /** Python command-line tools through uv (each in its own environment, commands in uv's bin folder on PATH). */
27
+ const setupPythonTools = async (env, tools) => {
28
+ const uv = tool('uv', 'uv.exe');
29
+ if (!exists(uv))
30
+ throw new Error('toolchains.python.tools needs uv (toolchains.uv.enabled)');
31
+ await installOnce(env, 'uv tool', tools, (pkg) => execAsync(uv, ['tool', 'install', pkg], { env: { ...process.env, ...uvVars() } }));
32
+ };
33
+ /** Where cargo builds: shared with the PC's target\, a separate folder next to it, one on the sandbox's disk, or a path. */
34
+ const cargoTarget = (targetDir) => {
35
+ if (targetDir === 'shared')
36
+ return null;
37
+ if (targetDir === 'separate')
38
+ return path.join(projectWorkspace(), 'target', 'sandbox');
39
+ if (targetDir === 'local')
40
+ return path.join(localDir, 'cargo-target');
41
+ return targetDir;
42
+ };
43
+ /**
44
+ * Where cargo builds and where sccache keeps its cache. Cargo fingerprints hold absolute paths and the host toolchain,
45
+ * so a target\\ shared with builds on the PC rebuilds every time the side changes: "separate" (the default) keeps a
46
+ * sandbox one next to it. sccache's cache persists unless the project sets SCCACHE_DIR itself.
47
+ */
48
+ const rustCaches = (rust) => {
49
+ const target = cargoTarget(rust.targetDir);
50
+ return { ...(target ? { CARGO_TARGET_DIR: target } : {}), ...(process.env.SCCACHE_DIR ? {} : { SCCACHE_DIR: path.join(dataDir, 'cache', 'sccache') }) };
51
+ };
52
+ /** "auto": the msvc host when the MSVC step runs (as on most PCs), else GNU with MinGW. */
53
+ const rustHost = (config) => {
54
+ if (config.rust.host !== 'auto')
55
+ return config.rust.host;
56
+ const cpu = arch === 'arm64' ? 'aarch64' : 'x86_64';
57
+ return cpu + (wanted(config.msvc.enabled, 'msvc') ? '-pc-windows-msvc' : '-pc-windows-gnu');
58
+ };
59
+ export const plan = (config, versions, o) => {
60
+ const java = wanted(config.java.enabled, 'java');
61
+ const python = wanted(config.python.enabled, 'python');
62
+ return [
63
+ { name: 'Git', on: o.gitEnabled, shared: true, run: (env) => setupGit(env, o.hostGit, o.refresh, o.gitAccess) },
64
+ // Before Node: with package.json pins, Volta's node/npm/pnpm/yarn come first on PATH.
65
+ { name: 'Volta', on: wantsVolta(config.node.volta), run: (env) => setupVolta(env) },
66
+ { name: 'Node ' + versions.node.spec, on: true, run: (env) => setupNode(env, versions, config.node) },
67
+ { name: 'Python', on: python, run: (env) => setupPython(env, config.python, versions) },
68
+ { name: 'pyenv-win', on: python && wantsPyenv(config.python.pyenv), run: (env) => setupPyenv(env) },
69
+ { name: 'C/C++', on: wanted(config.cpp.enabled, 'cpp') || wanted(config.rust.enabled, 'rust'), run: (env) => setupCpp(env, config.cpp) },
70
+ { name: 'MSVC', on: wanted(config.msvc.enabled, 'msvc'), run: (env) => setupMsvc(env, config.msvc) },
71
+ // Rust links with MSVC (msvc host) or MinGW's gcc (GNU host): whichever it uses goes first.
72
+ {
73
+ name: 'Rust', on: wanted(config.rust.enabled, 'rust'), after: ['C/C++', 'MSVC'],
74
+ run: async (env) => { await setupRust(env, { ...config.rust, host: rustHost(config) }, o.refresh); Object.assign(env.vars, rustCaches(config.rust)); },
75
+ },
76
+ { name: '.NET', on: wanted(config.dotnet.enabled, 'dotnet'), run: (env) => setupDotnet(env, config.dotnet, o.refresh) },
77
+ { name: 'Go', on: wanted(config.go.enabled, 'go'), run: (env) => setupGo(env, config.go) },
78
+ { name: 'Java', on: java, run: (env) => setupJava(env, config.java) },
79
+ { name: 'Ruby', on: wanted(config.ruby.enabled, 'ruby'), run: (env) => setupRuby(env, config.ruby) },
80
+ // PHP installs the Visual C++ runtime itself: one installer at a time.
81
+ { name: 'Visual C++ runtime', on: config.vcRuntime.enabled, shared: true, run: async (env) => { await setupVcRuntime(env); } },
82
+ { name: 'PHP', on: wanted(config.php.enabled, 'php'), after: ['Visual C++ runtime'], run: (env) => setupPhp(env, config.php) },
83
+ { name: 'Deno', on: wanted(config.deno.enabled, 'deno'), run: (env) => setupDeno(env) },
84
+ { name: 'Bun', on: config.bun.enabled, run: (env) => setupBun(env) },
85
+ { name: 'uv', on: config.uv.enabled, run: (env) => setupUv(env) },
86
+ { name: 'PowerShell 7', on: wanted(config.pwsh.enabled, 'pwsh'), run: (env) => setupPwsh(env, o.refresh) },
87
+ // The openssl tool comes from Git's install: after Git.
88
+ { name: 'CLI tools', on: Object.values(config.cli).some((v) => v !== false), after: ['Git'], run: (env) => setupCli(env, config.cli, o.gitAccess, o.refresh) },
89
+ { name: 'Mesa 3D', on: config.mesa.enabled, shared: true, run: (env) => setupMesa(env, config.mesa) },
90
+ { name: 'VS Code', on: config.vscode.enabled, shared: true, run: (env) => setupVscode(env, o.refresh) },
91
+ { name: 'Python tools', on: python && config.python.tools.length > 0, after: ['Python', 'uv'], run: (env) => setupPythonTools(env, config.python.tools) },
92
+ // DynamoDB Local runs on Java: the Java step installs the JDK first instead of racing it.
93
+ // One server per database in the sandbox: a project that joins later shares the one the boot started.
94
+ ...o.databases.map((db) => ({ name: DATABASE_NAMES[db.database], on: true, shared: true, after: db.database === 'dynamodb' && java ? ['Java'] : [], run: (env) => setupDatabase(env, db) })),
95
+ ];
96
+ };
@@ -0,0 +1,4 @@
1
+ import { type ToolEnv } from './common.js';
2
+ /** Where pwsh.exe lives (the host's Windows Terminal profile points at the same folder under C:\AgentData). */
3
+ export declare const pwshDir: () => string;
4
+ export declare const setupPwsh: (env: ToolEnv, refresh?: boolean) => Promise<void>;
@@ -0,0 +1,14 @@
1
+ // PowerShell 7 (pwsh): the portable zip from PowerShell/PowerShell releases (no MSI: Windows Sandbox would lose it on
2
+ // every boot). Only `pwsh` is added; Windows PowerShell 5.1 (`powershell`, SYSTEM.powershell()) stays what the
3
+ // sandbox's own hooks and scripts use.
4
+ import { pwshAsset } from './catalogue.js';
5
+ import { arch, tool } from './common.js';
6
+ import { setupSingleExe } from './runtimes.js';
7
+ /** Where pwsh.exe lives (the host's Windows Terminal profile points at the same folder under C:\AgentData). */
8
+ export const pwshDir = () => tool('pwsh');
9
+ export const setupPwsh = async (env, refresh = false) => {
10
+ // pwsh.exe sits at the zip's root next to its runtime and modules: the whole folder is kept.
11
+ await setupSingleExe(env, 'pwsh', 'PowerShell/PowerShell', pwshAsset(arch), 'pwsh.exe', pwshDir(), { refresh, size: '100 MB', versionArgs: ['-Version'], note: ' (PowerShell 7; powershell is still 5.1)' });
12
+ // No update banner or telemetry from a copy that provisioning.refresh replaces anyway.
13
+ Object.assign(env.vars, { POWERSHELL_UPDATECHECK: 'Off', POWERSHELL_TELEMETRY_OPTOUT: '1' });
14
+ };
@@ -0,0 +1,3 @@
1
+ import type { Toolchains, ToolVersions } from '../../core/types.js';
2
+ import { type ToolEnv } from './common.js';
3
+ export declare const setupPython: (env: ToolEnv, config: Toolchains["python"], versions: ToolVersions) => Promise<void>;
@@ -0,0 +1,54 @@
1
+ // Python 3 and Python 2: full CPython from the official NuGet packages (portable, with pip).
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fetchJson, installArchive } from '../../shared/download.js';
5
+ import { dryRun, execAsync, exists, log, mkdirp } from '../env.js';
6
+ import { resolveCached, tool } from './common.js';
7
+ import { pickPackageVersion } from './versions.js';
8
+ import { duringSession } from '../project-context.js';
9
+ const installPython = async (pkg, spec) => {
10
+ const version = await resolveCached(pkg + ':' + spec, async () => {
11
+ const { versions } = await fetchJson('https://api.nuget.org/v3-flatcontainer/' + pkg + '/index.json');
12
+ return pickPackageVersion(versions, spec) ?? Promise.reject(new Error('No ' + pkg + ' release matches "' + spec + '"'));
13
+ });
14
+ const dir = tool('python', pkg + '-' + version);
15
+ const exe = path.join(dir, 'python.exe');
16
+ if (!exists(exe)) {
17
+ log('Downloading Python ' + version + (pkg === 'python2' ? ' (Python 2, ' : ' (') + '~30 MB once) ...');
18
+ await installArchive('https://api.nuget.org/v3-flatcontainer/' + pkg + '/' + version + '/' + pkg + '.' + version + '.nupkg', dir, { locate: 'python.exe', fileName: pkg + '.' + version + '.zip' });
19
+ }
20
+ if (!exists(path.join(dir, 'Scripts', 'pip.exe')) && !exists(path.join(dir, 'Scripts', 'pip2.exe')))
21
+ await execAsync(exe, ['-m', 'ensurepip', '--default-pip'], { stdio: 'ignore' });
22
+ return dir;
23
+ };
24
+ /**
25
+ * The shims folder: tools\shims at boot. A project added later gets one per Python version (tools\shims\<version>)
26
+ * so the boot project's python3.cmd keeps pointing at its own version.
27
+ */
28
+ const shimDir = (dir) => (duringSession() ? tool('shims', path.basename(dir)) : tool('shims'));
29
+ /** Writes `<name>.cmd` into the shims folder, e.g. python2.cmd. */
30
+ const shim = (folder, name, command) => {
31
+ fs.writeFileSync(path.join(mkdirp(folder), name + '.cmd'), '@echo off\r\n' + command + ' %*\r\n');
32
+ };
33
+ const shimPair = (folder, version, dir) => {
34
+ const python = '"' + path.join(dir, 'python.exe') + '"';
35
+ shim(folder, 'python' + version, python);
36
+ shim(folder, 'pip' + version, python + ' -m pip');
37
+ };
38
+ export const setupPython = async (env, config, versions) => {
39
+ if (dryRun) {
40
+ log('(dry run) would provision Python ' + versions.python.spec + (config.python2 ? ' and Python 2.7' : ''));
41
+ return;
42
+ }
43
+ const py3 = await installPython('python', versions.python.spec);
44
+ const shims = shimDir(py3);
45
+ shimPair(shims, '3', py3);
46
+ env.prepend.push(py3, path.join(py3, 'Scripts'), shims);
47
+ env.vars.PYTHONIOENCODING = 'utf-8';
48
+ env.summary.push('python: ' + path.basename(py3).replace('python-', '') + ' (from ' + versions.python.source + ')');
49
+ if (!config.python2)
50
+ return;
51
+ const py2 = await installPython('python2', '2.7');
52
+ shimPair(shims, '2', py2);
53
+ env.summary.push('python2: ' + path.basename(py2).replace('python2-', ''));
54
+ };
@@ -0,0 +1,19 @@
1
+ import { type ToolEnv } from './common.js';
2
+ export interface SingleExeOptions {
3
+ /** Download again even when installed (provisioning.refresh). */
4
+ refresh?: boolean;
5
+ /** Keep the whole archive and put this folder inside it on PATH (default: keep only the folder holding the exe). */
6
+ subdir?: string;
7
+ versionArgs?: string[];
8
+ /** Appended to the summary line, e.g. " (PowerShell 7)". */
9
+ note?: string;
10
+ /** Rough download size for the progress line, e.g. "35 MB". */
11
+ size?: string;
12
+ }
13
+ /** A tool from one GitHub release archive, into `dir` (atomically), on PATH. */
14
+ export declare const setupSingleExe: (env: ToolEnv, name: string, repo: string, pattern: RegExp, exeName: string, dir: string, options?: SingleExeOptions) => Promise<void>;
15
+ export declare const setupBun: (env: ToolEnv) => Promise<void>;
16
+ export declare const setupDeno: (env: ToolEnv) => Promise<void>;
17
+ /** Where uv keeps its cache, Pythons and tools (persistent). */
18
+ export declare const uvVars: () => Record<string, string>;
19
+ export declare const setupUv: (env: ToolEnv) => Promise<void>;
@@ -0,0 +1,38 @@
1
+ // Bun, uv and Deno: single-binary tools from GitHub releases (claude-mem needs Bun and uv).
2
+ import path from 'node:path';
3
+ import { dryRun, exists, localDir, log } from '../env.js';
4
+ import { arch, installFromGithub, tool, versionOf } from './common.js';
5
+ /** A tool from one GitHub release archive, into `dir` (atomically), on PATH. */
6
+ export const setupSingleExe = async (env, name, repo, pattern, exeName, dir, options = {}) => {
7
+ if (dryRun) {
8
+ log('(dry run) would provision ' + name);
9
+ return;
10
+ }
11
+ const bin = options.subdir ? path.join(dir, options.subdir) : dir;
12
+ const exe = path.join(bin, exeName);
13
+ if (!exists(exe) || options.refresh) {
14
+ const size = options.size ? ', ~' + options.size : '';
15
+ await installFromGithub(repo, pattern, { dest: dir, what: (a) => name + ' (' + a.name + size + ' once)', ...(options.subdir ? {} : { locate: exeName }) });
16
+ }
17
+ if (!exists(exe))
18
+ throw new Error(exeName + ' missing after installing ' + name);
19
+ env.prepend.push(bin);
20
+ env.summary.push(name + ': ' + versionOf(exe, options.versionArgs) + (options.note ?? ''));
21
+ };
22
+ export const setupBun = async (env) => {
23
+ await setupSingleExe(env, 'bun', 'oven-sh/bun', arch === 'arm64' ? /^bun-windows-aarch64\.zip$/ : /^bun-windows-x64\.zip$/, 'bun.exe', tool('bun', 'bin'), { size: '35 MB' });
24
+ // Bun cannot keep its cache or temp files on a mapped folder (EINVAL); they go to the sandbox's own disk.
25
+ Object.assign(env.vars, { BUN_INSTALL: tool('bun'), BUN_INSTALL_CACHE_DIR: path.join(localDir, 'bun-cache'), BUN_TMPDIR: path.join(localDir, 'bun-tmp') });
26
+ };
27
+ export const setupDeno = async (env) => {
28
+ await setupSingleExe(env, 'deno', 'denoland/deno', arch === 'arm64' ? /^deno-aarch64-pc-windows-msvc[.]zip$/ : /^deno-x86_64-pc-windows-msvc[.]zip$/, 'deno.exe', tool('deno', 'bin'), { size: '40 MB' });
29
+ // `deno install` puts commands in DENO_INSTALL_ROOT/bin: the folder deno.exe is in, already on PATH.
30
+ Object.assign(env.vars, { DENO_DIR: tool('deno-cache'), DENO_INSTALL_ROOT: tool('deno'), DENO_NO_UPDATE_CHECK: '1' });
31
+ };
32
+ /** Where uv keeps its cache, Pythons and tools (persistent). */
33
+ export const uvVars = () => ({ UV_CACHE_DIR: tool('uv-cache'), UV_PYTHON_INSTALL_DIR: tool('uv-python'), UV_TOOL_DIR: tool('uv-tools'), UV_TOOL_BIN_DIR: tool('uv-bin') });
34
+ export const setupUv = async (env) => {
35
+ await setupSingleExe(env, 'uv', 'astral-sh/uv', arch === 'arm64' ? /^uv-aarch64-pc-windows-msvc\.zip$/ : /^uv-x86_64-pc-windows-msvc\.zip$/, 'uv.exe', tool('uv'), { size: '20 MB' });
36
+ Object.assign(env.vars, uvVars());
37
+ env.prepend.push(tool('uv-bin'));
38
+ };
@@ -0,0 +1,3 @@
1
+ import type { Toolchains } from '../../../core/types.js';
2
+ import { type ToolEnv } from '../common.js';
3
+ export declare const setupPhp: (env: ToolEnv, config: Toolchains["php"]) => Promise<void>;
@@ -0,0 +1,71 @@
1
+ // PHP from windows.php.net, with Composer. Composer's global packages live in persistent storage.
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fetchJson, fetchText, installArchive } from '../../../shared/download.js';
5
+ import { dryRun, execOutput, exists, log, mkdirp } from '../../env.js';
6
+ import { downloadFile, resolveCached, tool, versionFromFiles } from '../common.js';
7
+ import { pickPackageVersion } from '../versions.js';
8
+ import { setupVcRuntime } from './vcruntime.js';
9
+ const PHP_EXTENSIONS = ['curl', 'fileinfo', 'gd', 'intl', 'mbstring', 'openssl', 'pdo_mysql', 'pdo_pgsql', 'pdo_sqlite', 'sodium', 'sqlite3', 'zip'];
10
+ const COMPOSER = 'https://getcomposer.org/download/latest-stable/composer.phar';
11
+ /** The non-thread-safe x64 zip of the newest release matching `spec` ("8" -> newest 8.x, "8.3" -> newest 8.3.x). */
12
+ const phpBuild = async (spec) => {
13
+ const releases = await fetchJson('https://windows.php.net/downloads/releases/releases.json');
14
+ const pick = pickPackageVersion(Object.values(releases).map((r) => r.version), spec);
15
+ const release = Object.values(releases).find((r) => r.version === pick);
16
+ const build = release && Object.entries(release).find(([key, value]) => /^nts-vs\d+-x64$/.test(key) && typeof value === 'object' && value.zip)?.[1];
17
+ if (!release || typeof build !== 'object' || !build.zip)
18
+ throw new Error('No PHP build for Windows matches "' + spec + '"');
19
+ return { version: release.version, ...build.zip };
20
+ };
21
+ /** php.ini from the development template with the common extensions switched on (written whole, then renamed). */
22
+ const writePhpIni = (home) => {
23
+ const ini = path.join(home, 'php.ini');
24
+ if (exists(ini) || !exists(path.join(home, 'php.ini-development')))
25
+ return;
26
+ let text = fs.readFileSync(path.join(home, 'php.ini-development'), 'utf8')
27
+ .replace(/^;\s*extension_dir\s*=\s*"ext"/m, 'extension_dir = "' + path.join(home, 'ext') + '"');
28
+ for (const ext of PHP_EXTENSIONS.filter((e) => exists(path.join(home, 'ext', 'php_' + e + '.dll'))))
29
+ text = text.replace(new RegExp('^;(extension=' + ext + ')\\s*$', 'm'), '$1');
30
+ fs.writeFileSync(ini + '.tmp', text);
31
+ fs.renameSync(ini + '.tmp', ini);
32
+ };
33
+ /** Writes `<name>.cmd` into tools\shims (on PATH). */
34
+ const shim = (env, name, command) => {
35
+ fs.writeFileSync(path.join(mkdirp(tool('shims')), name + '.cmd'), '@echo off\r\n' + command + ' %*\r\n');
36
+ env.prepend.push(tool('shims'));
37
+ };
38
+ const setupComposer = async (env, php) => {
39
+ const phar = tool('composer', 'composer.phar');
40
+ if (!exists(phar)) {
41
+ log('Downloading Composer (~3 MB once) ...');
42
+ const sha256 = (await fetchText(COMPOSER + '.sha256sum')).trim().split(/\s+/)[0];
43
+ await downloadFile(COMPOSER, phar, sha256);
44
+ }
45
+ const composerHome = tool('composer-home');
46
+ env.vars.COMPOSER_HOME = composerHome;
47
+ env.prepend.push(path.join(composerHome, 'vendor', 'bin'));
48
+ shim(env, 'composer', '"' + php + '" "' + phar + '"');
49
+ env.summary.push('composer: ' + (/\d+\.\d+\.\d+/.exec(execOutput(php, [phar, '--version', '--no-ansi']))?.[0] ?? 'installed'));
50
+ };
51
+ export const setupPhp = async (env, config) => {
52
+ const wantedSpec = versionFromFiles(['.php-version']) ?? { spec: config.version, source: 'config' };
53
+ if (dryRun) {
54
+ log('(dry run) would provision PHP ' + wantedSpec.spec + (config.composer ? ' + Composer' : ''));
55
+ return;
56
+ }
57
+ // No summary line of its own: the Visual C++ runtime step (which runs first) already reports it.
58
+ await setupVcRuntime(env, false);
59
+ const version = await resolveCached('php:' + wantedSpec.spec, async () => (await phpBuild(wantedSpec.spec)).version);
60
+ const home = tool('php', version);
61
+ if (!exists(path.join(home, 'php.exe'))) {
62
+ const build = await phpBuild(version);
63
+ log('Downloading PHP ' + version + ' (~30 MB once) ...');
64
+ await installArchive('https://windows.php.net/downloads/releases/' + build.path, home, { sha256: build.sha256, locate: 'php.exe', fileName: build.path });
65
+ }
66
+ writePhpIni(home);
67
+ env.prepend.push(home);
68
+ env.summary.push('php: ' + version + ' (from ' + wantedSpec.source + ')');
69
+ if (config.composer)
70
+ await setupComposer(env, path.join(home, 'php.exe'));
71
+ };
@@ -0,0 +1,3 @@
1
+ import type { Toolchains } from '../../../core/types.js';
2
+ import { type ToolEnv } from '../common.js';
3
+ export declare const setupRuby: (env: ToolEnv, config: Toolchains["ruby"]) => Promise<void>;
@@ -0,0 +1,47 @@
1
+ // Ruby from RubyInstaller. Gems live in persistent storage.
2
+ import path from 'node:path';
3
+ import { fetchJson, installArchive } from '../../../shared/download.js';
4
+ import { SYSTEM } from '../../../shared/system.js';
5
+ import { dryRun, execAsync, exists, log } from '../../env.js';
6
+ import { installOnce, resolveCached, tool, versionFromFiles, versionOf } from '../common.js';
7
+ import { pickPackageVersion } from '../versions.js';
8
+ const RUBY_ASSET = /^rubyinstaller-(\d+\.\d+\.\d+)-(\d+)-x64\.7z$/;
9
+ /** The build number after the version in a RubyInstaller file name (rubyinstaller-3.3.1-1-x64.7z -> 1). */
10
+ const buildNumber = (asset) => Number(RUBY_ASSET.exec(asset.name)?.[2] ?? 0);
11
+ /** RubyInstaller builds per Ruby version (newest build of each), from its GitHub releases. */
12
+ const rubyBuilds = async () => {
13
+ const releases = await fetchJson('https://api.github.com/repos/oneclick/rubyinstaller2/releases?per_page=100');
14
+ const builds = new Map();
15
+ for (const asset of releases.filter((r) => !r.draft && !r.prerelease).flatMap((r) => r.assets)) {
16
+ const version = RUBY_ASSET.exec(asset.name)?.[1];
17
+ if (!version)
18
+ continue;
19
+ const known = builds.get(version);
20
+ if (!known || buildNumber(known) < buildNumber(asset))
21
+ builds.set(version, asset);
22
+ }
23
+ return builds;
24
+ };
25
+ const sha256Of = (asset) => (asset.digest?.startsWith('sha256:') ? asset.digest.slice(7) : null);
26
+ export const setupRuby = async (env, config) => {
27
+ const wantedSpec = versionFromFiles(['.ruby-version']) ?? { spec: config.version, source: 'config' };
28
+ if (dryRun) {
29
+ log('(dry run) would provision Ruby ' + wantedSpec.spec);
30
+ return;
31
+ }
32
+ const version = await resolveCached('ruby:' + wantedSpec.spec, async () => pickPackageVersion([...(await rubyBuilds()).keys()], wantedSpec.spec) ?? Promise.reject(new Error('No RubyInstaller build matches "' + wantedSpec.spec + '"')));
33
+ const home = tool('ruby', version);
34
+ if (!exists(path.join(home, 'bin', 'ruby.exe'))) {
35
+ const asset = (await rubyBuilds()).get(version);
36
+ if (!asset)
37
+ throw new Error('RubyInstaller ' + version + ' is no longer published');
38
+ log('Downloading Ruby ' + version + ' (~100 MB once) ...');
39
+ await installArchive(asset.browser_download_url, home, { sha256: sha256Of(asset), locate: 'ruby.exe', up: 1, fileName: asset.name });
40
+ }
41
+ const gems = tool('ruby-gems', version.split('.').slice(0, 2).join('.'));
42
+ Object.assign(env.vars, { GEM_HOME: gems, BUNDLE_USER_HOME: tool('bundle-home') });
43
+ env.prepend.push(path.join(home, 'bin'), path.join(gems, 'bin'));
44
+ env.summary.push('ruby: ' + versionOf(path.join(home, 'bin', 'ruby.exe')) + ' (from ' + wantedSpec.source + '; gems with C extensions need MSYS2, not included)');
45
+ const gem = path.join(home, 'bin', 'gem.cmd');
46
+ await installOnce(env, 'gem@' + version, config.gems, (pkg) => execAsync(SYSTEM.cmd(), ['/d', '/c', gem, 'install', pkg, '--no-document'], { env: { ...process.env, GEM_HOME: gems } }));
47
+ };
@@ -0,0 +1,7 @@
1
+ import { type ToolEnv } from '../common.js';
2
+ /**
3
+ * A real installer (it writes to System32), so it runs on every boot; the installer itself is kept in persistent
4
+ * storage and only run when Microsoft's signature on it checks out. Resolves true when this call ran the installer.
5
+ * Only the "Visual C++ runtime" step reports it (`summary`); PHP calls it again just to be sure.
6
+ */
7
+ export declare const setupVcRuntime: (env: ToolEnv, summary?: boolean) => Promise<boolean>;