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,26 @@
1
+ import type { UserError } from '../../../core/util.js';
2
+ import type { Context } from '../context.js';
3
+ export type RepairStatus = 'ok' | 'fixed' | 'needs-you' | 'warn' | 'skipped';
4
+ export interface RepairResult {
5
+ name: string;
6
+ status: RepairStatus;
7
+ detail: string;
8
+ }
9
+ export interface RepairEnv {
10
+ ctx: Context;
11
+ running: boolean;
12
+ dataPath: string;
13
+ storageRoot: string;
14
+ toolsDir: string;
15
+ configError: UserError | null;
16
+ }
17
+ export interface RepairCheck {
18
+ name: string;
19
+ run: (env: RepairEnv) => Omit<RepairResult, 'name'>;
20
+ }
21
+ export declare const ok: (detail: string) => Omit<RepairResult, "name">;
22
+ /** For fixes that must not touch folders a running sandbox may be using. */
23
+ export declare const whileRunning: {
24
+ status: "skipped";
25
+ detail: string;
26
+ };
@@ -0,0 +1,3 @@
1
+ export const ok = (detail) => ({ status: 'ok', detail });
2
+ /** For fixes that must not touch folders a running sandbox may be using. */
3
+ export const whileRunning = { status: 'skipped', detail: 'a sandbox is running and may be using it; run repair again once it is closed' };
@@ -0,0 +1,6 @@
1
+ import type { SessionStatus } from '../../core/types.js';
2
+ import { type NeedsRestart } from '../../host/projects.js';
3
+ import { type Context } from './context.js';
4
+ /** "2 sessions (claude in api, codex in web)". */
5
+ export declare const describeSessions: (sessions: Pick<SessionStatus, "harness" | "cwd" | "project">[]) => string;
6
+ export declare const offerRestart: (ctx: Context, needs: NeedsRestart[], then: () => Promise<number>) => Promise<number>;
@@ -0,0 +1,85 @@
1
+ // The last way in for a folder the running sandbox cannot show (not in a project root, no `wsb share`): restart it
2
+ // with that folder mapped as well. Only ever with your consent (a prompt, or --yes): it lists what will end, stops
3
+ // the sandbox cleanly (history and claude-mem sync back as on any close), starts it again from the same launch
4
+ // folder with the projects it had plus the new one, reopens the sessions it ended (continuing their conversations
5
+ // where the agent supports it) and then starts the new project's session.
6
+ import path from 'node:path';
7
+ import _ from 'lodash';
8
+ import { askYesNo } from '../../core/prompt.js';
9
+ import { fail, safeText, sleep } from '../../core/util.js';
10
+ import { currentSessions, execInSandbox } from '../../host/channel.js';
11
+ import { isSandboxRunning } from '../../host/platform.js';
12
+ import { readRegistry } from '../../host/projects.js';
13
+ import { hostDirFor, projectForGuestDir } from '../../shared/projects.js';
14
+ import { readSandboxStatus } from '../../shared/rundir.js';
15
+ import { watcherRunning } from '../../sync/watcher.js';
16
+ import { c } from '../colors.js';
17
+ import { working } from '../progress.js';
18
+ import { sandboxAlive } from './context.js';
19
+ /** "2 sessions (claude in api, codex in web)". */
20
+ export const describeSessions = (sessions) => sessions.length === 0 ? 'no sessions' : sessions.length + ' session' + (sessions.length === 1 ? '' : 's') + ' (' + sessions.map((s) => safeText(s.harness) + ' in ' + safeText(s.project ?? path.win32.basename(s.cwd))).join(', ') + ')';
21
+ /** The command to run again with consent, for a non-interactive terminal. */
22
+ const againWithYes = (ctx) => 'sandbox ' + (ctx.options.noAgent ? 'start' : ctx.options.harness ?? ctx.loaded.config.defaultHarness) + (ctx.options.terminal ? ' -t' : '') + ' --yes';
23
+ /** Where each running session is on this PC, so it can be reopened after the restart. */
24
+ const sessionsToReopen = (registry, sessions) => sessions.flatMap((s) => {
25
+ const project = projectForGuestDir([...registry.projects, ...registry.roots.map((r) => ({ ...r }))], s.cwd);
26
+ return project ? [{ harness: s.harness, hostDir: hostDirFor(project, s.cwd) }] : [];
27
+ });
28
+ /** Shuts the sandbox down and waits until it (and the background sync's final pass) is gone. */
29
+ const stopAndWait = async (ctx) => {
30
+ const status = readSandboxStatus(ctx.dataPath);
31
+ await working('Stopping the sandbox (history syncs back)', async () => {
32
+ await execInSandbox(ctx.dataPath, ['C:\\Windows\\System32\\shutdown.exe', '/s', '/t', '0'], status?.guestWorkspace ?? 'C:\\', 30, sandboxAlive).catch(() => null);
33
+ for (let i = 0; i < 180 && isSandboxRunning(); i++)
34
+ await sleep(1000);
35
+ if (isSandboxRunning())
36
+ fail('The sandbox did not shut down within 3 minutes.', 'Close its window yourself, then run the command again.');
37
+ // The watcher syncs history back a few seconds after the window closes; the next start needs it gone.
38
+ for (let i = 0; i < 60 && watcherRunning(ctx.dataPath); i++)
39
+ await sleep(1000);
40
+ });
41
+ };
42
+ export const offerRestart = async (ctx, needs, then) => {
43
+ const folders = needs.map((n) => n.hostDir);
44
+ const registry = readRegistry(ctx.storageRoot) ?? fail('The running sandbox was started by an older version, so it cannot take another folder.', 'Stop it (sandbox stop) and start it again from ' + folders[0] + '.');
45
+ const primary = registry.projects.find((p) => p.primary) ?? fail('The running sandbox has no launch folder on record.', 'Stop it (sandbox stop) and start it again.');
46
+ const sessions = currentSessions(ctx.dataPath);
47
+ for (const need of needs)
48
+ console.log(c.yellow('Windows Sandbox cannot add ' + need.hostDir + ' while it runs: ' + need.reason + '.'));
49
+ console.log('A restart maps ' + (needs.length === 1 ? 'it' : 'them') + ' as well (and keeps ' + registry.projects.map((p) => p.host).join(', ') + '). ' +
50
+ (sessions.length ? describeSessions(sessions) + ' will end' : 'No session is running') + '; your files, tools, databases and logins are kept. It takes about a minute.');
51
+ for (const parent of _.uniq(folders.map((f) => path.dirname(f))))
52
+ console.log(c.dim('No restart next time for folders in ' + parent + ': sandbox projects roots add "' + parent + '"'));
53
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
54
+ if (!ctx.options.yes) {
55
+ if (!interactive)
56
+ fail('Not restarting without your consent.', 'Run it again with --yes to restart: ' + againWithYes(ctx));
57
+ if (!(await askYesNo('Restart the sandbox now?'))) {
58
+ console.log('Nothing changed.');
59
+ return 1;
60
+ }
61
+ }
62
+ const reopen = sessionsToReopen(registry, sessions);
63
+ const resume = reopen.length > 0 && (ctx.options.yes || (await askYesNo('Reopen the ' + reopen.length + ' session(s) afterwards (continuing their conversations where the agent supports it)?', true)));
64
+ await stopAndWait(ctx);
65
+ // Imported here: launch.ts imports this file.
66
+ const { launch, openSession, readySandbox } = await import('./launch.js');
67
+ const { joinHere } = await import('./project.js');
68
+ const keep = registry.projects.filter((p) => !p.primary && p.mechanism !== 'root').map((p) => p.host);
69
+ const relaunch = { ...ctx, options: { ...ctx.options, workspace: primary.host, harness: ctx.options.harness ?? ctx.options.sessions[0]?.harness, terminal: false, noAgent: false, newSession: true, passthrough: [], sessions: [] } };
70
+ await launch(relaunch, false, { extraProjects: [...keep, ...folders], autoSession: false, waitUntilReady: true });
71
+ const ready = await readySandbox(ctx.dataPath);
72
+ if (resume) {
73
+ for (const s of reopen) {
74
+ const at = { ...ctx, options: { ...ctx.options, workspace: s.hostDir, harness: s.harness, terminal: false, newSession: true, passthrough: [], sessions: [] } };
75
+ try {
76
+ const joined = await joinHere(at, ready);
77
+ await openSession(at, ready, joined, { attach: false, harness: s.harness, args: ctx.loaded.config.harnesses[s.harness]?.resumeArguments ?? [], reuse: false });
78
+ }
79
+ catch (e) {
80
+ console.warn(c.yellow('Could not reopen ' + s.harness + ' in ' + s.hostDir + ': ' + (e instanceof Error ? e.message : String(e))));
81
+ }
82
+ }
83
+ }
84
+ return then();
85
+ };
@@ -0,0 +1,12 @@
1
+ import { type Context } from './context.js';
2
+ export declare const attach: (ctx: Context) => Promise<number>;
3
+ export declare const sessions: (ctx: Context) => Promise<number>;
4
+ export declare const status: (ctx: Context) => Promise<number>;
5
+ export declare const exec: (ctx: Context) => Promise<number>;
6
+ /** `sandbox db`: runs the sandbox's own `sbx-db` in this folder's project, which knows its database versions and ports. */
7
+ export declare const db: (ctx: Context) => Promise<number>;
8
+ /** Restarts this folder's project's sessions (--all: every session). */
9
+ export declare const restart: (ctx: Context) => Promise<number>;
10
+ export declare const stop: (ctx: Context) => Promise<number>;
11
+ export declare const show: (ctx: Context) => number;
12
+ export declare const hide: (ctx: Context) => Promise<number>;
@@ -0,0 +1,257 @@
1
+ // Working with a running sandbox: attach, sessions, status, exec, restart, stop, show/hide. Each knows which project
2
+ // you run it from (project.ts): attach and sessions put that project's sessions first, exec and db run in it, restart
3
+ // restarts its sessions, stop warns about the other projects' sessions.
4
+ import path from 'node:path';
5
+ import { spawnSync } from 'node:child_process';
6
+ import { select } from '@inquirer/prompts';
7
+ import _ from 'lodash';
8
+ import { askYesNo } from '../../core/prompt.js';
9
+ import { activeAccount } from '../../host/accounts.js';
10
+ import { execInSandbox, pickSession, pingSandbox, restartSessions, currentSessions, runningSessions, sessionsFirstIn } from '../../host/channel.js';
11
+ import { sessionViewers } from '../../host/attach.js';
12
+ import { insideSandbox, isSandboxRunning } from '../../host/platform.js';
13
+ import { NeedsRestart, readRegistry } from '../../host/projects.js';
14
+ import { guardedProjects, repoFindings } from '../../host/repo-guard.js';
15
+ import { hideSandboxWindow, showSandboxWindow } from '../../host/window.js';
16
+ import { isWithin, projectForGuestDir } from '../../shared/projects.js';
17
+ import { listProjectStatus, listSessions, readSandboxStatus } from '../../shared/rundir.js';
18
+ import { watcherRunning } from '../../sync/watcher.js';
19
+ import { errMsg, fail, safeText, sleep } from '../../core/util.js';
20
+ import { printJson, requireOnPc, requireRunning, sandboxAlive, table } from './context.js';
21
+ import { attachHere, readySandbox } from './launch.js';
22
+ import { hereDir, joinHere, locateHere, provisionJoined } from './project.js';
23
+ import { working } from '../progress.js';
24
+ import { ago, c, shortenMiddle, termWidth } from '../colors.js';
25
+ const viewerText = (v) => (v === undefined ? '-' : v === null ? 'unknown (older session)' : v.remote + ' here, ' + (v.viewers - v.remote) + ' in sandbox');
26
+ const projectOf = (projects, s) => s.project ?? projectForGuestDir(projects, s.cwd)?.name ?? '-';
27
+ export const attach = async (ctx) => {
28
+ requireOnPc('attach');
29
+ requireRunning('Start one with: sandbox claude --terminal');
30
+ const ready = await readySandbox(ctx.dataPath);
31
+ const id = ctx.options.positionals[0];
32
+ if (!id && runningSessions(ctx.dataPath).length === 0)
33
+ await sleep(1500);
34
+ const here = locateHere(ctx, ready);
35
+ if (id)
36
+ return attachHere(ctx, pickSession(ctx.dataPath, id));
37
+ // Several sessions: choose, this folder's project first (the newest is the default).
38
+ const running = sessionsFirstIn(runningSessions(ctx.dataPath), here.guestDir);
39
+ if (running.length > 1 && process.stdin.isTTY && process.stdout.isTTY) {
40
+ const projects = readRegistry(ctx.storageRoot)?.projects ?? [];
41
+ const chosen = await select({
42
+ message: 'Which session?' + (here.project ? ' (' + here.project.name + ' first)' : ''),
43
+ choices: running.map((s) => ({ value: s.id, name: (here.guestDir && isWithin(here.guestDir, s.cwd) ? '* ' : ' ') + safeText(s.harness).padEnd(9) + ' ' + projectOf(projects, s).padEnd(16) + ' ' + ago(s.startedAt).padEnd(11) + ' ' + s.id })),
44
+ });
45
+ return attachHere(ctx, pickSession(ctx.dataPath, chosen));
46
+ }
47
+ return attachHere(ctx, pickSession(ctx.dataPath, undefined, here.guestDir));
48
+ };
49
+ /** Sessions with who is watching them; `here` marks the ones in the project of the current folder. */
50
+ const listedSessions = async (ctx) => {
51
+ // Who is attached comes from each session itself, so a terminal that was closed is never counted.
52
+ const live = isSandboxRunning();
53
+ // Only this sandbox's sessions are asked (and at most MAX_QUERIED_SESSIONS): the sandbox writes these files.
54
+ const ask = new Set(live ? currentSessions(ctx.dataPath).map((s) => s.id) : []);
55
+ const here = live ? locateHere(ctx, readSandboxStatus(ctx.dataPath)) : { guestDir: null };
56
+ const projects = readRegistry(ctx.storageRoot)?.projects ?? [];
57
+ return Promise.all(listSessions(ctx.dataPath).map(async (s) => ({
58
+ ...s, projectName: projectOf(projects, s), here: Boolean(here.guestDir && isWithin(here.guestDir, s.cwd)),
59
+ ...(ask.has(s.id) ? { viewers: await sessionViewers(s) } : {}),
60
+ })));
61
+ };
62
+ export const sessions = async (ctx) => {
63
+ const list = await listedSessions(ctx);
64
+ if (printJson(ctx, list.map((s) => _.omit(s, 'attach'))))
65
+ return 0;
66
+ if (list.length === 0) {
67
+ console.log('No sessions. Start one with: sandbox claude');
68
+ return 0;
69
+ }
70
+ // This folder's project first, newest first within each group; harness, cwd and times are written by the sandbox.
71
+ const ordered = [..._.sortBy(list.filter((s) => s.here), (s) => s.startedAt).reverse(), ..._.sortBy(list.filter((s) => !s.here), (s) => s.startedAt).reverse()];
72
+ const folderWidth = Math.max(16, termWidth() - 80);
73
+ table([['', 'ID', 'AGENT', 'PROJECT', 'STATE', 'ATTACHED', 'FOLDER', 'STARTED'], ...ordered.map((s) => [
74
+ s.here ? '*' : '', s.id, safeText(s.harness), safeText(s.projectName ?? '-'), s.phase + (s.exitCode !== undefined ? ' (' + s.exitCode + ')' : ''), viewerText(s.viewers), shortenMiddle(safeText(s.cwd), folderWidth), ago(s.startedAt),
75
+ ])]);
76
+ if (list.some((s) => s.here))
77
+ console.log(c.dim('* = the project of this folder'));
78
+ return 0;
79
+ };
80
+ /** Running databases by port, from the sandbox's own `sbx-db list` (best effort: unknown when it does not answer). */
81
+ const runningPorts = async (ctx, cwd) => {
82
+ try {
83
+ const r = await execInSandbox(ctx.dataPath, ['sbx-db', 'list'], cwd, 30, sandboxAlive);
84
+ return new Set((r.stdout ?? '').split(/\r?\n/).filter((l) => /\brunning\b/.test(l)).map((l) => Number(/port (\d+)/.exec(l)?.[1])).filter((n) => n > 0));
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ };
90
+ export const status = async (ctx) => {
91
+ const running = isSandboxRunning();
92
+ const sandbox = readSandboxStatus(ctx.dataPath);
93
+ const registry = running ? readRegistry(ctx.storageRoot) : null;
94
+ const ready = running && sandbox?.phase === 'ready';
95
+ const daemon = ready ? await pingSandbox(ctx.dataPath, sandboxAlive).then((r) => r.message, (e) => 'no answer (' + errMsg(e) + ')') : '-';
96
+ const here = running ? locateHere(ctx, sandbox) : { project: null, guestDir: null };
97
+ const sessionList = running ? await listedSessions(ctx) : [];
98
+ const provisioning = running ? listProjectStatus(ctx.dataPath) : [];
99
+ const ports = ready && sandbox ? await runningPorts(ctx, sandbox.guestWorkspace) : null;
100
+ const findings = repoFindings(ctx.dataPath);
101
+ const startedAt = Number(/^(\d+)-/.exec(registry?.launchId ?? sandbox?.launchId ?? '')?.[1]) || null;
102
+ const projects = (registry?.projects ?? []).map((p) => {
103
+ const prov = provisioning.find((s) => s.name.toLowerCase() === p.name.toLowerCase());
104
+ const own = sessionList.filter((s) => s.phase === 'running' && projectForGuestDir(registry?.projects ?? [], s.cwd)?.name === p.name);
105
+ return {
106
+ name: p.name, host: p.host, guest: p.guest, mechanism: p.mechanism, here: here.project?.name === p.name,
107
+ setup: prov ? { phase: prov.phase, message: safeText(prov.message) } : { phase: 'not set up yet', message: 'set up when you first start a session or command there' },
108
+ databases: (prov?.databases ?? []).map((d) => ({ database: safeText(d.database), port: d.port, state: ports ? (ports.has(d.port) ? 'running' : 'stopped') : 'unknown' })),
109
+ sessions: own.map((s) => ({ id: s.id, harness: safeText(s.harness), viewers: viewerText(s.viewers), started: s.startedAt })),
110
+ // With several guarded folders each finding starts with its folder.
111
+ repoGuard: findings.filter((f) => f.detail.toLowerCase().startsWith(p.host.toLowerCase() + ':')).map((f) => f.kind + ': ' + safeText(f.detail)),
112
+ };
113
+ });
114
+ const info = {
115
+ sandbox: {
116
+ running, phase: running ? sandbox?.phase ?? 'unknown' : 'stopped', message: sandbox?.message, headless: sandbox?.headless,
117
+ uptime: running && startedAt ? ago(startedAt).replace(' ago', '') : null, daemon, account: activeAccount(ctx.dataPath), syncWatcher: watcherRunning(ctx.dataPath),
118
+ },
119
+ here: { folder: hereDir(ctx), project: here.project?.name ?? null, guest: here.guestDir },
120
+ roots: registry?.roots ?? [],
121
+ projects,
122
+ otherSessions: sessionList.filter((s) => s.phase === 'running' && !projects.some((p) => p.sessions.some((x) => x.id === s.id))).map((s) => ({ id: s.id, harness: safeText(s.harness), cwd: safeText(s.cwd) })),
123
+ repoGuard: { guarded: guardedProjects(ctx.dataPath).length, findings: findings.map((f) => f.kind + ': ' + f.detail) },
124
+ };
125
+ if (printJson(ctx, info))
126
+ return 0;
127
+ const yesNo = (v) => (typeof v === 'boolean' ? (v ? 'yes' : 'no') : String(v ?? '-'));
128
+ table(Object.entries(info.sandbox).map(([k, v]) => [_.startCase(k), safeText(yesNo(v))]));
129
+ if (!running)
130
+ return 0;
131
+ console.log('\n' + c.bold('Projects') + c.dim(' (* = this folder: ' + info.here.folder + (info.here.project ? '' : ', not in this sandbox') + ')'));
132
+ for (const p of projects) {
133
+ console.log((p.here ? '* ' : ' ') + c.bold(p.name) + ' ' + p.host + ' -> ' + p.guest + c.dim(' (' + (p.mechanism === 'launch' ? 'started from here' : p.mechanism === 'root' ? 'in a project root' : p.mechanism === 'boot' ? 'mapped at start' : 'wsb share') + ')'));
134
+ console.log(' setup: ' + p.setup.phase + (p.setup.phase === 'ready' ? '' : ' - ' + shortenMiddle(p.setup.message, termWidth() - 20)));
135
+ if (p.databases.length)
136
+ console.log(' databases: ' + p.databases.map((d) => d.database + ' :' + d.port + ' ' + d.state).join(', '));
137
+ for (const s of p.sessions)
138
+ console.log(' session ' + s.id + ' ' + s.harness + ' ' + s.viewers + ' ' + ago(s.started));
139
+ for (const f of p.repoGuard)
140
+ console.log(' ' + c.yellow('guard: ' + f));
141
+ }
142
+ for (const r of info.roots)
143
+ console.log(' root ' + r.host + ' -> ' + r.guest + c.dim(' (every project in it opens without a restart)'));
144
+ for (const s of info.otherSessions)
145
+ console.log(' session ' + s.id + ' ' + s.harness + ' ' + s.cwd);
146
+ console.log('\n' + c.bold('Repo guard') + ' ' + info.repoGuard.guarded + ' folder(s) guarded; ' + (findings.length ? c.yellow(findings.length + ' change(s) neutralised:') : 'nothing from the sandbox needed neutralising'));
147
+ for (const f of info.repoGuard.findings.slice(-10))
148
+ console.log(' ' + c.yellow(safeText(f)));
149
+ return 0;
150
+ };
151
+ /** What a command run in the sandbox printed (text from the sandbox: through safeText), or why it failed. */
152
+ const printExecResult = (result) => {
153
+ if (result.stdout)
154
+ process.stdout.write(safeText(result.stdout));
155
+ if (result.stderr)
156
+ process.stderr.write(safeText(result.stderr));
157
+ if (!result.ok && !result.stdout && !result.stderr)
158
+ console.error(result.message);
159
+ };
160
+ /** The guest folder for this folder, joining the running sandbox (and setting the project up) when needed. */
161
+ const guestFolderHere = async (ctx, what) => {
162
+ const ready = await readySandbox(ctx.dataPath);
163
+ try {
164
+ const joined = await joinHere(ctx, ready);
165
+ for (const note of joined.notes)
166
+ console.error(note);
167
+ await provisionJoined(ctx, joined);
168
+ return joined.guestDir;
169
+ }
170
+ catch (e) {
171
+ if (!(e instanceof NeedsRestart))
172
+ throw e;
173
+ return fail(hereDir(ctx) + ' is not in the running sandbox (' + e.reason + '), so "sandbox ' + what + '" cannot run there.', 'sandbox claude (or sandbox start) from that folder offers a restart that adds it; or add its parent folder as a root: sandbox projects roots add "' + path.dirname(e.hostDir) + '"');
174
+ }
175
+ };
176
+ export const exec = async (ctx) => {
177
+ const command = [...ctx.options.positionals, ...ctx.options.passthrough];
178
+ if (command.length === 0)
179
+ fail('No command given.', 'Usage: sandbox exec [--timeout <s>] -- <command> [args...]');
180
+ requireOnPc('exec');
181
+ requireRunning();
182
+ const cwd = await guestFolderHere(ctx, 'exec');
183
+ const result = await execInSandbox(ctx.dataPath, command, cwd, ctx.options.timeout, sandboxAlive);
184
+ if (!printJson(ctx, result))
185
+ printExecResult(result);
186
+ return result.exitCode ?? 1;
187
+ };
188
+ /** `sandbox db`: runs the sandbox's own `sbx-db` in this folder's project, which knows its database versions and ports. */
189
+ export const db = async (ctx) => {
190
+ const [action = 'list', name] = ctx.options.positionals;
191
+ if (!['list', 'start', 'stop'].includes(action))
192
+ fail('Unknown db action "' + action + '".', 'Use: sandbox db (list) or sandbox db start|stop <postgres|mysql|mongodb|redis|dynamodb>');
193
+ if (action !== 'list' && !name)
194
+ fail('Which database?', 'Usage: sandbox db ' + action + ' <postgres|mysql|mongodb|redis|dynamodb>');
195
+ // Already inside the sandbox: run the database tool here instead of asking the sandbox from outside.
196
+ if (insideSandbox())
197
+ return spawnSync(process.execPath, [path.join(ctx.packageRoot, 'dist', 'runtime', 'db.js'), action, ...(name ? [name] : [])], { stdio: 'inherit', windowsHide: true }).status ?? 1;
198
+ requireRunning('Start one with: sandbox start --database <name>');
199
+ const cwd = await guestFolderHere(ctx, 'db');
200
+ // Starting can download the database the first time.
201
+ const result = await working((action === 'list' ? 'Asking' : action === 'start' ? 'Starting' : 'Stopping') + ' the database' + (name ? ' ' + name : 's') + ' in the sandbox', () => execInSandbox(ctx.dataPath, ['sbx-db', action, ...(name ? [name] : [])], cwd, 1800, sandboxAlive));
202
+ printExecResult(result);
203
+ return result.exitCode ?? 1;
204
+ };
205
+ /** Restarts this folder's project's sessions (--all: every session). */
206
+ export const restart = async (ctx) => {
207
+ requireOnPc('restart');
208
+ requireRunning();
209
+ const here = locateHere(ctx, readSandboxStatus(ctx.dataPath));
210
+ const scope = ctx.options.all || !here.project ? undefined : here.project.guest;
211
+ if (!scope && !ctx.options.all)
212
+ console.log(c.dim(hereDir(ctx) + ' is not a project of the running sandbox: restarting every session.'));
213
+ console.log((await working('Restarting the agents' + (scope ? ' of ' + here.project?.name : ''), () => restartSessions(ctx.dataPath, ctx.options.harness ?? null, sandboxAlive, scope))).message);
214
+ return 0;
215
+ };
216
+ export const stop = async (ctx) => {
217
+ requireOnPc('stop');
218
+ if (!isSandboxRunning()) {
219
+ console.log('No Windows Sandbox is running.');
220
+ return 0;
221
+ }
222
+ const sandbox = readSandboxStatus(ctx.dataPath) ?? fail('This sandbox was not started by this tool.', 'Close its window instead.');
223
+ // Stopping ends every project's sessions, not just this folder's: say which, and ask when others would end too.
224
+ const here = locateHere(ctx, sandbox);
225
+ const running = currentSessions(ctx.dataPath);
226
+ const others = running.filter((s) => !(here.guestDir && isWithin(here.project?.guest ?? here.guestDir, s.cwd)));
227
+ if (running.length)
228
+ console.log('Stopping ends ' + running.length + ' session(s): ' + running.map((s) => safeText(s.harness) + ' in ' + safeText(s.project ?? path.win32.basename(s.cwd))).join(', ') + '.');
229
+ if (others.length && !ctx.options.yes) {
230
+ // A script must never end other projects' sessions by accident: without a terminal to ask in, it needs --yes.
231
+ const interactive = process.stdin.isTTY && process.stdout.isTTY;
232
+ if (!interactive)
233
+ fail(others.length + ' of these sessions belong to other projects, so the sandbox was not stopped.', 'Stop it anyway with: sandbox stop --yes');
234
+ if (!(await askYesNo(others.length + ' of them belong to other projects. Stop the whole sandbox?'))) {
235
+ console.log('Nothing changed.');
236
+ return 1;
237
+ }
238
+ }
239
+ await working('Asking the sandbox to shut down', () => execInSandbox(ctx.dataPath, ['C:\\Windows\\System32\\shutdown.exe', '/s', '/t', '2'], sandbox.guestWorkspace, 30, sandboxAlive));
240
+ console.log('Sandbox is shutting down; history and claude-mem sync back when it closes.');
241
+ return 0;
242
+ };
243
+ export const show = (ctx) => {
244
+ requireOnPc('show');
245
+ requireRunning();
246
+ if (!showSandboxWindow(ctx.storageRoot))
247
+ fail('Could not find the Windows Sandbox window.', 'It may still be starting; try again in a few seconds.');
248
+ return 0;
249
+ };
250
+ export const hide = async (ctx) => {
251
+ requireOnPc('hide');
252
+ requireRunning();
253
+ if (!(await hideSandboxWindow(ctx.storageRoot, 5)))
254
+ fail('Could not find a visible Windows Sandbox window.', 'It may already be hidden (sbx-show brings it back).');
255
+ console.log('Sandbox window hidden. Bring it back with: sbx-show');
256
+ return 0;
257
+ };
@@ -0,0 +1,13 @@
1
+ import { type EditionSupport, type MachineFacts } from './facts.js';
2
+ export type CheckStatus = 'ok' | 'warn' | 'action';
3
+ export interface Check {
4
+ name: string;
5
+ status: CheckStatus;
6
+ detail: string;
7
+ }
8
+ /** Windows Sandbox needs Pro, Enterprise or Education (build 18305+); Home ("Core*") and Server cannot run it. */
9
+ export declare const editionSupport: (edition: string | null) => EditionSupport;
10
+ export declare const nodeMajor: (version: string) => number;
11
+ /** Offer to enable the feature only when it is off and this edition can have it. */
12
+ export declare const canEnableFeature: (f: MachineFacts) => boolean;
13
+ export declare const buildChecks: (f: MachineFacts) => Check[];
@@ -0,0 +1,71 @@
1
+ // Turns what `sandbox setup` learnt about the PC into a checklist. Pure, so it can be tested.
2
+ import { GB } from '../../../core/disk.js';
3
+ import { ENABLE_COMMAND } from './facts.js';
4
+ /** Windows Sandbox needs Pro, Enterprise or Education (build 18305+); Home ("Core*") and Server cannot run it. */
5
+ export const editionSupport = (edition) => {
6
+ if (!edition)
7
+ return 'unknown';
8
+ if (/^Core/i.test(edition))
9
+ return 'home';
10
+ if (/^Server/i.test(edition))
11
+ return 'unsupported';
12
+ return /^(Professional|Enterprise|Education|IoTEnterprise)/i.test(edition) ? 'supported' : 'unknown';
13
+ };
14
+ export const nodeMajor = (version) => Number(/^v?(\d+)/.exec(version.trim())?.[1] ?? 0);
15
+ /** Offer to enable the feature only when it is off and this edition can have it. */
16
+ export const canEnableFeature = (f) => f.feature !== 'enabled' && f.feature !== 'pending' && !f.sandboxExe && ['supported', 'unknown'].includes(editionSupport(f.edition));
17
+ const formatGb = (bytes) => (bytes / GB).toFixed(1) + ' GB';
18
+ const check = (name, status, detail) => ({ name, status, detail });
19
+ const editionCheck = (f) => {
20
+ const support = editionSupport(f.edition);
21
+ const edition = (f.edition ?? 'unknown edition') + (f.build ? ', build ' + f.build : '');
22
+ const name = 'Windows edition';
23
+ if (support === 'supported' && (f.build ?? 18305) >= 18305)
24
+ return check(name, 'ok', edition);
25
+ if (support === 'home')
26
+ return check(name, 'action', edition + ': Windows Home cannot run Windows Sandbox. Upgrade to Pro (Settings > System > Activation).');
27
+ if (support === 'unsupported')
28
+ return check(name, 'action', edition + ': Windows Sandbox needs Windows 10/11 Pro, Enterprise or Education.');
29
+ if (support === 'supported')
30
+ return check(name, 'action', edition + ': too old; Windows Sandbox needs Windows 10 version 1903 or newer.');
31
+ return check(name, 'warn', edition + ': not sure this edition has Windows Sandbox (it needs Pro, Enterprise or Education).');
32
+ };
33
+ const virtualizationCheck = (f) => {
34
+ const name = 'CPU virtualization';
35
+ const virt = f.hypervisorPresent || f.virtualizationFirmware;
36
+ if (virt)
37
+ return check(name, 'ok', f.hypervisorPresent ? 'on (hypervisor running)' : 'on');
38
+ if (virt === false)
39
+ return check(name, 'action', 'off. Turn on Intel VT-x / AMD-V (SVM) in your PC\'s BIOS/UEFI settings, then run this again.');
40
+ return check(name, 'warn', 'could not tell; if Windows Sandbox will not start, turn on VT-x / AMD-V in the BIOS/UEFI.');
41
+ };
42
+ const featureCheck = (f) => {
43
+ const name = 'Windows Sandbox feature';
44
+ if (f.feature === 'enabled' && f.sandboxExe)
45
+ return check(name, 'ok', 'enabled');
46
+ if (f.feature === 'pending' || f.feature === 'enabled')
47
+ return check(name, 'action', 'enabled, but Windows needs a restart to finish.');
48
+ if (f.sandboxExe)
49
+ return check(name, 'ok', 'installed');
50
+ return check(name, 'action', 'not enabled. In an admin PowerShell: ' + ENABLE_COMMAND + ', then restart.');
51
+ };
52
+ const memoryCheck = (f) => {
53
+ const gb = formatGb(f.totalRamBytes);
54
+ if (f.totalRamBytes < 4 * GB)
55
+ return check('Memory', 'action', gb + ': Windows Sandbox needs at least 4 GB.');
56
+ if (f.totalRamBytes < 8 * GB)
57
+ return check('Memory', 'warn', gb + ': works, but 8 GB or more is recommended.');
58
+ return check('Memory', 'ok', gb);
59
+ };
60
+ const diskCheck = (f) => {
61
+ if (f.freeDiskBytes === null)
62
+ return check('Free disk', 'warn', 'could not check the drive of ' + f.storageRoot);
63
+ const free = formatGb(f.freeDiskBytes) + ' free for ' + f.storageRoot;
64
+ if (f.freeDiskBytes < 5 * GB)
65
+ return check('Free disk', 'action', free + ': free up space (toolchains need several GB).');
66
+ if (f.freeDiskBytes < 20 * GB)
67
+ return check('Free disk', 'warn', free + ': getting low; toolchains and caches can use several GB.');
68
+ return check('Free disk', 'ok', free);
69
+ };
70
+ const nodeCheck = (f) => nodeMajor(f.nodeVersion) >= 20 ? check('Node.js', 'ok', f.nodeVersion) : check('Node.js', 'action', f.nodeVersion + ': needs 20 or newer (winget install OpenJS.NodeJS.LTS).');
71
+ export const buildChecks = (f) => [editionCheck(f), virtualizationCheck(f), featureCheck(f), memoryCheck(f), diskCheck(f), nodeCheck(f)];
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+ export type FeatureState = 'enabled' | 'pending' | 'disabled' | 'unknown';
3
+ export type EditionSupport = 'supported' | 'home' | 'unsupported' | 'unknown';
4
+ export interface MachineFacts {
5
+ edition: string | null;
6
+ build: number | null;
7
+ feature: FeatureState;
8
+ /** WindowsSandbox.exe exists (it appears only after the restart that follows enabling the feature). */
9
+ sandboxExe: boolean;
10
+ virtualizationFirmware: boolean | null;
11
+ hypervisorPresent: boolean | null;
12
+ totalRamBytes: number;
13
+ freeDiskBytes: number | null;
14
+ storageRoot: string;
15
+ nodeVersion: string;
16
+ }
17
+ export declare const ENABLE_COMMAND: string;
18
+ /** `reg query <key>` output to { valueName: data }. */
19
+ export declare const parseRegQuery: (text: string) => Record<string, string>;
20
+ /** The "State : ..." line of `Dism /Get-FeatureInfo` (English output only; anything else is unknown). */
21
+ export declare const parseDismState: (text: string) => FeatureState;
22
+ /** Win32_OptionalFeature.InstallState: 1 enabled, 2 disabled, 3 absent. */
23
+ export declare const featureFromInstallState: (state: number | null | undefined) => FeatureState;
24
+ declare const Probe: z.ZodObject<{
25
+ virtualizationFirmware: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
26
+ hypervisorPresent: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
27
+ installState: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
28
+ }, z.core.$strip>;
29
+ export declare const parseProbe: (text: string) => z.infer<typeof Probe>;
30
+ export declare const gatherFacts: (storageRoot: string) => MachineFacts;
31
+ export {};
@@ -0,0 +1,60 @@
1
+ // What `sandbox setup` learns about this PC: the Windows edition, CPU virtualization and the Windows Sandbox feature.
2
+ import os from 'node:os';
3
+ import _ from 'lodash';
4
+ import { z } from 'zod';
5
+ import { freeBytesAt } from '../../../core/disk.js';
6
+ import { captureOutput, fileExists } from '../../../core/util.js';
7
+ import { SYSTEM, system32 } from '../../../shared/system.js';
8
+ const FEATURE = 'Containers-DisposableClientVM';
9
+ export const ENABLE_COMMAND = 'Enable-WindowsOptionalFeature -Online -FeatureName ' + FEATURE + ' -All -NoRestart';
10
+ /** `reg query <key>` output to { valueName: data }. */
11
+ export const parseRegQuery = (text) => Object.fromEntries(_.compact(text.split(/\r?\n/).map((line) => /^\s+(\S+)\s+REG_\w+\s+(.*)$/.exec(line)?.slice(1, 3))));
12
+ /** The "State : ..." line of `Dism /Get-FeatureInfo` (English output only; anything else is unknown). */
13
+ export const parseDismState = (text) => {
14
+ const state = /^State\s*:\s*(.+?)\s*$/im.exec(text)?.[1].toLowerCase() ?? '';
15
+ if (state === 'enabled')
16
+ return 'enabled';
17
+ if (state.includes('pending'))
18
+ return 'pending';
19
+ return state.startsWith('disabled') ? 'disabled' : 'unknown';
20
+ };
21
+ /** Win32_OptionalFeature.InstallState: 1 enabled, 2 disabled, 3 absent. */
22
+ export const featureFromInstallState = (state) => state === 1 ? 'enabled' : state === 2 || state === 3 ? 'disabled' : 'unknown';
23
+ // Fixed script (no outside data): CPU/hypervisor facts and the feature state, readable without admin rights.
24
+ const PROBE = [
25
+ '$ErrorActionPreference = "SilentlyContinue"',
26
+ '$cpu = @(Get-CimInstance Win32_Processor)',
27
+ '$cs = Get-CimInstance Win32_ComputerSystem',
28
+ '$f = Get-CimInstance Win32_OptionalFeature -Filter "Name=\'' + FEATURE + '\'"',
29
+ '$v = $null; if ($cpu.Count) { $v = [bool]($cpu | Where-Object { $_.VirtualizationFirmwareEnabled }) }',
30
+ '$h = $null; if ($cs) { $h = [bool]$cs.HypervisorPresent }',
31
+ '$s = $null; if ($f) { $s = [int]$f.InstallState }',
32
+ '@{ virtualizationFirmware = $v; hypervisorPresent = $h; installState = $s } | ConvertTo-Json -Compress',
33
+ ].join('; ');
34
+ const Probe = z.object({ virtualizationFirmware: z.boolean().nullable(), hypervisorPresent: z.boolean().nullable(), installState: z.number().nullable() }).partial();
35
+ export const parseProbe = (text) => {
36
+ try {
37
+ return Probe.parse(JSON.parse(_.last(text.trim().split(/\r?\n/)) ?? ''));
38
+ }
39
+ catch {
40
+ return {};
41
+ }
42
+ };
43
+ export const gatherFacts = (storageRoot) => {
44
+ const reg = parseRegQuery(captureOutput(SYSTEM.reg(), ['query', 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion']));
45
+ const probe = parseProbe(captureOutput(SYSTEM.powershell(), ['-NoProfile', '-NonInteractive', '-Command', PROBE]));
46
+ // DISM knows about a pending restart but needs admin rights; the CIM state works for everyone.
47
+ const dism = parseDismState(captureOutput(SYSTEM.dism(), ['/English', '/Online', '/Get-FeatureInfo', '/FeatureName:' + FEATURE]));
48
+ return {
49
+ edition: reg.EditionID ?? null,
50
+ build: Number(reg.CurrentBuildNumber ?? reg.CurrentBuild) || null,
51
+ feature: dism === 'unknown' ? featureFromInstallState(probe.installState) : dism,
52
+ sandboxExe: fileExists(system32('WindowsSandbox.exe')),
53
+ virtualizationFirmware: probe.virtualizationFirmware ?? null,
54
+ hypervisorPresent: probe.hypervisorPresent ?? null,
55
+ totalRamBytes: os.totalmem(),
56
+ freeDiskBytes: freeBytesAt(storageRoot),
57
+ storageRoot,
58
+ nodeVersion: process.version,
59
+ };
60
+ };
@@ -0,0 +1,2 @@
1
+ import { type Context } from '../context.js';
2
+ export declare const setup: (ctx: Context) => Promise<number>;
@@ -0,0 +1,43 @@
1
+ // `sandbox setup`: checks this PC can run Windows Sandbox and offers to turn the feature on.
2
+ import { spawnSync } from 'node:child_process';
3
+ import { askYesNo } from '../../../core/prompt.js';
4
+ import { fail, isWin } from '../../../core/util.js';
5
+ import { refuseInSelfTest } from '../../../host/test-guard.js';
6
+ import { SYSTEM } from '../../../shared/system.js';
7
+ import { c } from '../../colors.js';
8
+ import { table } from '../context.js';
9
+ import { buildChecks, canEnableFeature } from './checks.js';
10
+ import { ENABLE_COMMAND, gatherFacts } from './facts.js';
11
+ const LABEL = { ok: c.green('OK'), warn: c.yellow('WARNING'), action: c.red('ACTION NEEDED') };
12
+ const print = (checks) => table(checks.map((check) => [LABEL[check.status], check.name, check.detail]));
13
+ /** Runs the enable command in an elevated PowerShell (Windows asks for permission); true if it worked. */
14
+ const enableFeature = () => {
15
+ refuseInSelfTest('turn on Windows features');
16
+ const inner = "'-NoProfile','-Command','" + ENABLE_COMMAND + " | Out-Null'";
17
+ const outer = "try { $p = Start-Process -FilePath '" + SYSTEM.powershell() + "' -Verb RunAs -Wait -PassThru -WindowStyle Hidden -ArgumentList " + inner + '; exit $p.ExitCode } catch { exit 1 }';
18
+ return spawnSync(SYSTEM.powershell(), ['-NoProfile', '-NonInteractive', '-Command', outer], { stdio: 'inherit' }).status === 0;
19
+ };
20
+ export const setup = async (ctx) => {
21
+ if (!isWin)
22
+ fail('Windows Sandbox only runs on Windows.', 'Run sandbox setup on a Windows 10/11 Pro, Enterprise or Education PC.');
23
+ console.log('Checking this PC for Windows Sandbox...\n');
24
+ const facts = gatherFacts(ctx.storageRoot);
25
+ let checks = buildChecks(facts);
26
+ print(checks);
27
+ if (canEnableFeature(facts)) {
28
+ console.log('');
29
+ if (await askYesNo('Turn on Windows Sandbox now? Windows will ask for admin permission.')) {
30
+ console.log('Enabling Windows Sandbox (this can take a minute)...');
31
+ if (!enableFeature())
32
+ fail('Windows Sandbox could not be enabled (or the admin prompt was declined).', 'In an admin PowerShell run: ' + ENABLE_COMMAND + ', then restart Windows.');
33
+ console.log('Done. Restart Windows to finish, then run sandbox setup again.\n');
34
+ checks = buildChecks(gatherFacts(ctx.storageRoot));
35
+ print(checks);
36
+ }
37
+ else
38
+ console.log('To do it yourself, in an admin PowerShell run:\n ' + ENABLE_COMMAND + '\nthen restart Windows.');
39
+ }
40
+ const todo = checks.filter((check) => check.status === 'action').length;
41
+ console.log('\n' + (todo ? todo + ' thing(s) to fix before the sandbox can start (see ACTION NEEDED above).' : 'This PC is ready. cd into a project and run sandbox claude (or sandbox for no agent).'));
42
+ return todo ? 1 : 0;
43
+ };