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,143 @@
1
+ // Command-line parsing with commander. parseCli() only parses (no side effects), so it is easy to test; cli/index.ts
2
+ // dispatches the result. sandbox claude & co parse as `launch`: every option launch does not own goes to the agent, in order.
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { Command, InvalidArgumentError, Option } from 'commander';
6
+ import { z } from 'zod';
7
+ import { fail, tryReadJson } from '../core/util.js';
8
+ import { c } from './colors.js';
9
+ import { COMMANDS } from './manifest.js';
10
+ /** Splits a command line like a shell: "quoted words" and 'quoted words' stay together. */
11
+ export const splitArgs = (text) => [...text.matchAll(/"([^"]*)"|'([^']*)'|(\S+)/g)].map((m) => m[1] ?? m[2] ?? m[3]);
12
+ /**
13
+ * `<agent>@<folder>` (the folder may contain '@': only the first one splits), `<agent>` alone = the current folder,
14
+ * and `|<arguments>` for that session's agent only ('|' cannot be in a Windows path). Folders become absolute against
15
+ * `cwd`. Pure; validate.ts checks that the agent and folder exist.
16
+ */
17
+ export const parseSessionWant = (input, cwd = process.cwd()) => {
18
+ const bar = input.indexOf('|');
19
+ const text = bar < 0 ? input : input.slice(0, bar);
20
+ const args = bar < 0 ? [] : splitArgs(input.slice(bar + 1));
21
+ const at = text.indexOf('@');
22
+ const harness = (at < 0 ? text : text.slice(0, at)).trim();
23
+ const folder = at < 0 ? '' : text.slice(at + 1).trim();
24
+ if (!harness)
25
+ fail('--session "' + text + '" names no agent.', 'Use --session <agent>@<folder>, e.g. --session claude@C:\\code\\api (the folder defaults to this one).');
26
+ return { harness, folder: path.resolve(cwd, folder || '.'), ...(args.length ? { args } : {}) };
27
+ };
28
+ /** This package's version (this file is dist/cli/args.js, two folders below package.json). */
29
+ export const packageVersion = () => {
30
+ const file = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'package.json');
31
+ return z.object({ version: z.string() }).safeParse(tryReadJson(file)).data?.version ?? 'unknown';
32
+ };
33
+ /** Reserved spelling for our launch options that can never clash with an agent's flags: --sbx-terminal, ... */
34
+ const SBX_PREFIX = '--sbx-';
35
+ const positiveNumber = (value) => {
36
+ const n = Number(value);
37
+ if (!Number.isFinite(n) || n <= 0)
38
+ throw new InvalidArgumentError('expected a positive number');
39
+ return n;
40
+ };
41
+ const collect = (value, previous = []) => [...previous, value];
42
+ /** One commander option from its manifest entry. */
43
+ const toOption = (o) => {
44
+ const flags = (o.short ? o.short + ', ' : '') + o.flag + (o.type === 'boolean' ? '' : ' <' + (o.value ?? 'value') + '>');
45
+ const option = new Option(flags, o.description);
46
+ if (o.type === 'number')
47
+ option.argParser(positiveNumber);
48
+ if (o.type === 'list')
49
+ option.argParser(collect);
50
+ if (o.default !== undefined)
51
+ option.default(o.default);
52
+ return option;
53
+ };
54
+ /** Every command, its options and help text, from manifest.ts. Actions only record what was parsed. */
55
+ export const buildProgram = (record) => {
56
+ const program = new Command('sandbox')
57
+ .description('A disposable Windows Sandbox for development, with or without an AI agent, for the folder you are in. Run it with no arguments for the menu.')
58
+ .version(packageVersion(), '-v, --version', 'show the version')
59
+ .enablePositionalOptions()
60
+ .showHelpAfterError(' Fix: run the command with --help to see how to use it.')
61
+ .configureOutput({ outputError: (text, write) => write(c.red('Error: ') + text.replace(/^error: /, '').replace(/^./, (ch) => ch.toUpperCase())) })
62
+ .addHelpText('after', '\nPut --config <file> before or after the command to use another settings file.\nExamples:\n sandbox the menu\n sandbox claude -t start Claude and attach this terminal\n sandbox data clean logs delete the saved logs\n sandbox exec -- npm test run a command in the running sandbox')
63
+ .exitOverride();
64
+ for (const spec of COMMANDS) {
65
+ const cmd = program.command(spec.name, { hidden: spec.hidden }).description(spec.summary).option('--config <file>', 'config file (default: ~/.agent-sandbox/config.json)');
66
+ for (const option of spec.options)
67
+ cmd.addOption(toOption(option));
68
+ if (spec.json)
69
+ cmd.option('--json', 'machine-readable output');
70
+ for (const arg of spec.args)
71
+ cmd.argument((arg.required ? '<' : '[') + arg.name + (arg.variadic ? '...' : '') + (arg.required ? '>' : ']'), arg.description);
72
+ if (spec.passthrough === 'agent')
73
+ cmd.argument('[agentArgs...]', 'passed to the agent').allowUnknownOption().allowExcessArguments();
74
+ if (spec.passthrough === 'command')
75
+ cmd.passThroughOptions();
76
+ cmd.action((...args) => record(spec.name, args[args.length - 1]));
77
+ }
78
+ return program;
79
+ };
80
+ /** Turns `--sbx-<option>` into `--<option>` (only for options launch knows; anything else is an error). */
81
+ const expandReserved = (argv, known) => {
82
+ const end = argv.indexOf('--');
83
+ return argv.map((token, i) => {
84
+ if ((end !== -1 && i > end) || !token.startsWith(SBX_PREFIX))
85
+ return token;
86
+ const [flag, value] = token.split(/=(.*)/s);
87
+ const option = '--' + flag.slice(SBX_PREFIX.length);
88
+ if (!known.has(option))
89
+ fail('Unknown option: ' + flag, 'Reserved --sbx- options: ' + [...known].map((o) => o.replace('--', SBX_PREFIX)).join(', '));
90
+ return value === undefined ? option : option + '=' + value;
91
+ });
92
+ };
93
+ /** Parses argv into the command name and options. Throws commander errors for unknown commands/options. */
94
+ export const parseCli = (argv, wrapper = false) => {
95
+ let parsed = null;
96
+ const program = buildProgram((name, cmd) => { parsed = { name, cmd }; });
97
+ const launchFlags = new Set(program.commands.find((c) => c.name() === 'launch').options.map((o) => o.long).filter(Boolean));
98
+ const args = expandReserved(wrapper ? ['launch', ...argv] : argv, launchFlags);
99
+ program.parse(args, { from: 'user' });
100
+ if (!parsed)
101
+ return { ...defaults(), command: '' };
102
+ const { name, cmd } = parsed;
103
+ const o = { ...program.opts(), ...cmd.opts() };
104
+ const forAgent = ['launch', 'start', 'generate', 'host-claude'].includes(name);
105
+ // Commander keeps our `--` separator among the operands when unknown options are allowed; it is ours, not the agent's.
106
+ const separator = forAgent ? cmd.args.indexOf('--') : -1;
107
+ const operands = separator === -1 ? cmd.args : [...cmd.args.slice(0, separator), ...cmd.args.slice(separator + 1)];
108
+ return {
109
+ ...defaults(),
110
+ // `start` is launch without an agent.
111
+ command: name === 'start' ? 'launch' : name,
112
+ positionals: forAgent || name === 'exec' ? [] : operands,
113
+ passthrough: forAgent || name === 'exec' ? operands : [],
114
+ config: o.config,
115
+ workspace: o.workspace,
116
+ harness: o.harness,
117
+ account: o.account,
118
+ node: o.node,
119
+ python: o.python,
120
+ scripts: o.script ?? [],
121
+ databases: (o.database ?? []).flatMap((d) => d.split(',')).map((d) => d.trim()).filter(Boolean),
122
+ headless: o.window ? false : o.headless ? true : undefined,
123
+ terminal: Boolean(o.terminal),
124
+ newSession: Boolean(o.new),
125
+ noAgent: o.agent === false || name === 'start',
126
+ trustConfig: Boolean(o.trustConfig),
127
+ noLocalConfig: o.localConfig === false,
128
+ personal: Boolean(o.personal),
129
+ fromHost: Boolean(o.fromHost),
130
+ noRestart: o.restart === false,
131
+ follow: Boolean(o.follow),
132
+ json: Boolean(o.json),
133
+ yes: Boolean(o.yes),
134
+ all: Boolean(o.all),
135
+ sessions: (o.session ?? []).map((s) => parseSessionWant(s)),
136
+ timeout: o.timeout ?? 600,
137
+ days: o.days ?? 7,
138
+ };
139
+ };
140
+ const defaults = () => ({
141
+ command: '', positionals: [], passthrough: [], scripts: [], databases: [], terminal: false, newSession: false, noAgent: false, trustConfig: false, noLocalConfig: false,
142
+ personal: false, fromHost: false, noRestart: false, follow: false, json: false, yes: false, all: false, sessions: [], timeout: 600, days: 7,
143
+ });
@@ -0,0 +1,24 @@
1
+ /** The colour policy as a pure function (tested); `isTTY` is that of the stream being written to. */
2
+ export declare const colorEnabled: (env: NodeJS.ProcessEnv, isTTY: boolean) => boolean;
3
+ export declare const c: {
4
+ bold: (text: string) => string;
5
+ dim: (text: string) => string;
6
+ red: (text: string) => string;
7
+ green: (text: string) => string;
8
+ yellow: (text: string) => string;
9
+ cyan: (text: string) => string;
10
+ };
11
+ /** Removes colour codes (for measuring or comparing text). */
12
+ export declare const stripAnsi: (text: string) => string;
13
+ /** Cuts `text` to `max` visible characters, ending in an ellipsis (plain text only). */
14
+ export declare const shorten: (text: string, max: number) => string;
15
+ /** Cuts the middle out of a long path or id so both ends stay readable. */
16
+ export declare const shortenMiddle: (text: string, max: number) => string;
17
+ /** 1536 -> "1.5 KB" is in the data command; this is "3 min ago" for the tables. */
18
+ export declare const ago: (then: number | string | Date, now?: number) => string;
19
+ /** Width to lay text out in (terminal columns, 80 when unknown, never absurdly small). */
20
+ export declare const termWidth: () => number;
21
+ /** Each menu page starts on a clean screen (answered prompts would otherwise pile up above it). */
22
+ export declare const clearScreen: () => void;
23
+ /** Rows of a list that fit the terminal (leaving room for the prompt and the help line). */
24
+ export declare const pageSizeFor: (wanted: number) => number;
@@ -0,0 +1,42 @@
1
+ // ANSI colours for terminal output (one place, so menus, errors and warnings agree).
2
+ // Colour is on only for a terminal that wants it: not with NO_COLOR, TERM=dumb or piped output; FORCE_COLOR turns it on.
3
+ /** The colour policy as a pure function (tested); `isTTY` is that of the stream being written to. */
4
+ export const colorEnabled = (env, isTTY) => {
5
+ if (env.NO_COLOR)
6
+ return false;
7
+ if (env.FORCE_COLOR !== undefined && env.FORCE_COLOR !== '')
8
+ return env.FORCE_COLOR !== '0' && env.FORCE_COLOR !== 'false';
9
+ return isTTY && env.TERM !== 'dumb';
10
+ };
11
+ const paint = (code) => (text) => (colorEnabled(process.env, Boolean(process.stdout.isTTY)) ? '\x1b[' + code + 'm' + text + '\x1b[0m' : text);
12
+ export const c = { bold: paint(1), dim: paint(2), red: paint(31), green: paint(32), yellow: paint(33), cyan: paint(36) };
13
+ /** Removes colour codes (for measuring or comparing text). */
14
+ export const stripAnsi = (text) => text.replace(/\x1b\[[0-9;]*m/g, '');
15
+ /** Cuts `text` to `max` visible characters, ending in an ellipsis (plain text only). */
16
+ export const shorten = (text, max) => (max < 2 || text.length <= max ? text : text.slice(0, max - 1) + '…');
17
+ /** Cuts the middle out of a long path or id so both ends stay readable. */
18
+ export const shortenMiddle = (text, max) => {
19
+ if (max < 5 || text.length <= max)
20
+ return text;
21
+ const keep = max - 1;
22
+ const head = Math.ceil(keep / 2);
23
+ return text.slice(0, head) + '…' + text.slice(text.length - (keep - head));
24
+ };
25
+ /** 1536 -> "1.5 KB" is in the data command; this is "3 min ago" for the tables. */
26
+ export const ago = (then, now = Date.now()) => {
27
+ const secs = Math.max(0, Math.round((now - new Date(then).getTime()) / 1000));
28
+ if (secs < 45)
29
+ return 'just now';
30
+ if (secs < 3600)
31
+ return Math.round(secs / 60) + ' min ago';
32
+ if (secs < 86400)
33
+ return Math.round(secs / 3600) + ' h ago';
34
+ return Math.round(secs / 86400) + ' d ago';
35
+ };
36
+ /** Width to lay text out in (terminal columns, 80 when unknown, never absurdly small). */
37
+ export const termWidth = () => Math.max(40, process.stdout.columns ?? 80);
38
+ /** Each menu page starts on a clean screen (answered prompts would otherwise pile up above it). */
39
+ export const clearScreen = () => { if (process.stdout.isTTY)
40
+ process.stdout.write('\x1b[2J\x1b[3J\x1b[H'); };
41
+ /** Rows of a list that fit the terminal (leaving room for the prompt and the help line). */
42
+ export const pageSizeFor = (wanted) => Math.max(5, Math.min(wanted, (process.stdout.rows ?? 30) - 6));
@@ -0,0 +1,2 @@
1
+ import { type Context } from './context.js';
2
+ export declare const account: (ctx: Context) => Promise<number>;
@@ -0,0 +1,52 @@
1
+ // `account list | save | use | default | remove`
2
+ import { activateAccount, listAccounts, removeAccount, saveAccount, setDefaultAccount } from '../../host/accounts.js';
3
+ import { restartSessions, runningSessions } from '../../host/channel.js';
4
+ import { hostClaudeDir, hostGlobalStateFile } from '../../host/claude-profile.js';
5
+ import { isSandboxRunning } from '../../host/platform.js';
6
+ import { fail } from '../../core/util.js';
7
+ import { printJson, sandboxAlive, table } from './context.js';
8
+ const label = (a) => '"' + a.name + '"' + (a.email ? ' (' + a.email + ')' : '');
9
+ /** This PC's Claude login location (config folder + global state file). */
10
+ const hostLogin = (ctx) => {
11
+ const profile = ctx.loaded.config.claudeProfile;
12
+ return { claudeDir: hostClaudeDir(profile), globalStateFile: hostGlobalStateFile(profile) };
13
+ };
14
+ const list = (ctx) => {
15
+ const rows = listAccounts(ctx.storageRoot, ctx.dataPath);
16
+ if (printJson(ctx, rows))
17
+ return 0;
18
+ if (rows.length === 0) {
19
+ console.log('No saved accounts. Sign in inside the sandbox, then: sandbox account save <name> (or --from-host to save this PC\'s login)');
20
+ return 0;
21
+ }
22
+ table([['', 'NAME', 'EMAIL', 'SAVED'], ...rows.map((a) => [(a.isActive ? '*' : ' ') + (a.isDefault ? 'D' : ' '), a.name, a.email ?? '-', a.savedAt.slice(0, 16).replace('T', ' ')])]);
23
+ console.log('\n* = active in the sandbox D = default for new launches');
24
+ return 0;
25
+ };
26
+ const use = async (ctx, name) => {
27
+ console.log('Sandbox now uses ' + label(activateAccount(ctx.storageRoot, ctx.dataPath, name)) + '.');
28
+ const claudeRunning = isSandboxRunning() && runningSessions(ctx.dataPath).some((s) => s.harness === 'claude');
29
+ if (claudeRunning && !ctx.options.noRestart)
30
+ console.log((await restartSessions(ctx.dataPath, 'claude', sandboxAlive)).message + ' with --continue; the conversation carries on under the new account.');
31
+ return 0;
32
+ };
33
+ export const account = async (ctx) => {
34
+ const [action = 'list', name] = ctx.options.positionals;
35
+ const needName = () => name ?? fail('Missing account name.', 'Usage: sandbox account ' + action + ' <name>');
36
+ switch (action) {
37
+ case 'list': return list(ctx);
38
+ case 'save':
39
+ console.log('Saved account ' + label(saveAccount(ctx.storageRoot, ctx.dataPath, needName(), ctx.options.fromHost ? hostLogin(ctx) : null)) + '.');
40
+ return 0;
41
+ case 'use': return use(ctx, needName());
42
+ case 'default':
43
+ setDefaultAccount(ctx.storageRoot, needName());
44
+ console.log('Default account: ' + name);
45
+ return 0;
46
+ case 'remove':
47
+ removeAccount(ctx.storageRoot, needName());
48
+ console.log('Removed account ' + name);
49
+ return 0;
50
+ default: fail('Unknown account action "' + action + '".', 'Use one of: sandbox account list | save <name> | use <name> | default <name> | remove <name>');
51
+ }
52
+ };
@@ -0,0 +1,6 @@
1
+ import type { Config } from '../../core/types.js';
2
+ import { type Reconciled } from '../../host/agent-commands.js';
3
+ import { type Context } from './context.js';
4
+ /** Brings this PC in line with agentCommands and says what changed (used after on/off, settings and launch). */
5
+ export declare const syncAgentCommands: (ctx: Context, config: Config, quiet?: boolean) => Reconciled | null;
6
+ export declare const agentCommandsCommand: (ctx: Context) => number;
@@ -0,0 +1,61 @@
1
+ // `sandbox agent-commands [status|on|off]` (Beta): typing claude, codex ... on this PC starts them in a hidden sandbox.
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { loadConfig, userConfigPath } from '../../core/config.js';
5
+ import { fail } from '../../core/util.js';
6
+ import { agentCommands, commandsDir, reconcileAgentCommands } from '../../host/agent-commands.js';
7
+ import { insideSandbox } from '../../host/platform.js';
8
+ import { writeFileAtomic } from '../../shared/rundir.js';
9
+ import { c } from '../colors.js';
10
+ import { editConfigText } from '../settings/edit.js';
11
+ import { table } from './context.js';
12
+ const entryOf = (ctx) => path.join(ctx.packageRoot, 'dist', 'bin', 'sandbox.js');
13
+ /** Brings this PC in line with agentCommands and says what changed (used after on/off, settings and launch). */
14
+ export const syncAgentCommands = (ctx, config, quiet = false) => {
15
+ if (insideSandbox())
16
+ return null;
17
+ const result = reconcileAgentCommands(config, ctx.storageRoot, process.execPath, entryOf(ctx));
18
+ if (!result.changed || quiet)
19
+ return result;
20
+ if (result.on) {
21
+ console.log('Agent commands (Beta) are on for: ' + (result.commands.map((a) => a.command).join(', ') || 'no installed agents found'));
22
+ console.log(c.dim('Open a new terminal, then type e.g. claude in any project folder. Run the real one: set SANDBOX_AGENT_DIRECT=1'));
23
+ }
24
+ else
25
+ console.log('Agent commands are off: claude, codex ... run on this PC again (open a new terminal).');
26
+ return result;
27
+ };
28
+ export const agentCommandsCommand = (ctx) => {
29
+ if (insideSandbox())
30
+ fail('Agent commands change your PC\'s PATH, so they are set up from your PC, not inside the sandbox.', 'Run sandbox agent-commands on in a terminal on your PC.');
31
+ const action = ctx.options.positionals[0] ?? 'status';
32
+ if (action === 'status') {
33
+ const config = ctx.loaded.config;
34
+ const found = agentCommands(config, ctx.storageRoot);
35
+ console.log('Agent commands (Beta): ' + (config.agentCommands.enabled ? c.green('on') : 'off') + c.dim(' (' + commandsDir(ctx.storageRoot) + ')'));
36
+ if (found.length)
37
+ table(found.map((a) => [a.command, 'sandboxed ' + a.harness + (config.agentCommands.enabled ? '' : ' when on'), c.dim('real: ' + a.real)]));
38
+ else
39
+ console.log(c.dim('No agents found on this PC (claude, codex, gemini ... on your PATH).'));
40
+ if (!config.agentCommands.enabled)
41
+ console.log(c.dim('Turn on with: sandbox agent-commands on'));
42
+ return 0;
43
+ }
44
+ if (action !== 'on' && action !== 'off')
45
+ fail('Unknown action "' + action + '".', 'Use: sandbox agent-commands [status|on|off]');
46
+ const file = ctx.loaded.configPath ?? userConfigPath();
47
+ const text = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
48
+ const edited = editConfigText(text, ['agentCommands', 'enabled'], action === 'on' ? true : undefined);
49
+ if ('problems' in edited)
50
+ fail('Could not change ' + file + ': ' + edited.problems.join('; '), 'Fix the file with sandbox config check, then try again.');
51
+ writeFileAtomic(file, edited.text);
52
+ const result = syncAgentCommands(ctx, loadConfig(file).config, true);
53
+ if (result?.on) {
54
+ console.log('Agent commands (Beta) are on: ' + (result.commands.map((a) => a.command).join(', ') || 'no installed agents found yet'));
55
+ console.log(c.dim('Open a new terminal, then type e.g. claude in a project folder: it starts in a hidden sandbox and attaches.'));
56
+ console.log(c.dim('Run the real one on this PC: set SANDBOX_AGENT_DIRECT=1 (PowerShell: $env:SANDBOX_AGENT_DIRECT=1). Turn off: sandbox agent-commands off'));
57
+ }
58
+ else
59
+ console.log('Agent commands are off: claude, codex ... run on this PC again. Open a new terminal.');
60
+ return 0;
61
+ };
@@ -0,0 +1,23 @@
1
+ import type { CliOptions } from '../args.js';
2
+ import type { LoadedConfig } from '../../core/types.js';
3
+ export interface Context {
4
+ packageRoot: string;
5
+ entry: string;
6
+ options: CliOptions;
7
+ loaded: LoadedConfig;
8
+ storageRoot: string;
9
+ dataPath: string;
10
+ }
11
+ export declare const createContext: (packageRoot: string, entry: string, options: CliOptions) => Context;
12
+ /** Prints rows as aligned columns (the last column is not padded). */
13
+ export declare const table: (rows: string[][]) => void;
14
+ /** Prints `value` as JSON when --json was given; returns whether it did. */
15
+ export declare const printJson: (ctx: Context, value: unknown) => boolean;
16
+ export declare const sandboxAlive: () => boolean;
17
+ /** For commands that control the sandbox from your PC: inside the sandbox they make no sense. */
18
+ export declare const requireOnPc: (what: string) => void;
19
+ export declare const requireRunning: (hint?: string) => void;
20
+ /** Maps a folder on this PC to its path inside the running sandbox (only folders inside the mapped workspace exist there). */
21
+ export declare const guestPathFor: (hostDir: string, workspaceHost: string, guestWorkspace: string) => string;
22
+ /** The guest folder for the current directory (or --workspace) in the running sandbox. */
23
+ export declare const currentGuestDir: (ctx: Context, workspaceHost: string, guestWorkspace: string) => string;
@@ -0,0 +1,57 @@
1
+ // What every command receives, plus the small helpers they share.
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import _ from 'lodash';
5
+ import { loadConfig, userDir } from '../../core/config.js';
6
+ import { expandPath, fail, isInsideOrEqual } from '../../core/util.js';
7
+ import { GUEST_DATA_DIR, insideSandbox, isSandboxRunning } from '../../host/platform.js';
8
+ /** Keeps ~/.agent-sandbox/*.schema.json in step with the installed version (local schema paths editors trust). */
9
+ const refreshSchemas = (packageRoot) => {
10
+ const source = path.join(packageRoot, 'schemas');
11
+ if (!fs.existsSync(source))
12
+ return;
13
+ try {
14
+ fs.mkdirSync(userDir(), { recursive: true });
15
+ for (const name of fs.readdirSync(source)) {
16
+ const target = path.join(userDir(), name);
17
+ const text = fs.readFileSync(path.join(source, name), 'utf8');
18
+ if (!fs.existsSync(target) || fs.readFileSync(target, 'utf8') !== text)
19
+ fs.writeFileSync(target, text, 'utf8');
20
+ }
21
+ }
22
+ catch { /* editor help only; never block a command over it */ }
23
+ };
24
+ export const createContext = (packageRoot, entry, options) => {
25
+ refreshSchemas(packageRoot);
26
+ const loaded = loadConfig(options.config);
27
+ const storageRoot = expandPath(loaded.config.storage.root, loaded.base);
28
+ // Inside a sandbox, the sandbox's own data folder is the one to talk to.
29
+ return { packageRoot, entry, options, loaded, storageRoot, dataPath: insideSandbox() ? GUEST_DATA_DIR : path.join(storageRoot, 'data', loaded.config.storage.dataName) };
30
+ };
31
+ /** Prints rows as aligned columns (the last column is not padded). */
32
+ export const table = (rows) => {
33
+ const widths = _.range(_.max(rows.map((r) => r.length)) ?? 0).map((i) => _.max(rows.map((r) => (r[i] ?? '').length)) ?? 0);
34
+ for (const row of rows)
35
+ console.log(row.map((cell, i) => (i === row.length - 1 ? cell : _.padEnd(cell, widths[i] + 2))).join(''));
36
+ };
37
+ /** Prints `value` as JSON when --json was given; returns whether it did. */
38
+ export const printJson = (ctx, value) => {
39
+ if (ctx.options.json)
40
+ console.log(JSON.stringify(value, null, 2));
41
+ return ctx.options.json;
42
+ };
43
+ export const sandboxAlive = () => isSandboxRunning();
44
+ /** For commands that control the sandbox from your PC: inside the sandbox they make no sense. */
45
+ export const requireOnPc = (what) => { if (insideSandbox())
46
+ fail('You are inside the sandbox already, so "sandbox ' + what + '" has nothing to control.', 'Run it from a terminal on your PC.'); };
47
+ export const requireRunning = (hint = '') => { if (!isSandboxRunning())
48
+ fail('No Windows Sandbox is running.', hint || 'Start one with sandbox claude.'); };
49
+ /** Maps a folder on this PC to its path inside the running sandbox (only folders inside the mapped workspace exist there). */
50
+ export const guestPathFor = (hostDir, workspaceHost, guestWorkspace) => {
51
+ if (!isInsideOrEqual(workspaceHost, hostDir))
52
+ fail('The running sandbox works on ' + workspaceHost + ', and Windows Sandbox cannot add folders while it runs.', 'Run from inside that folder, or stop the sandbox (sandbox stop) and launch again from ' + hostDir + '.');
53
+ const rel = path.relative(workspaceHost, hostDir);
54
+ return rel ? path.win32.join(guestWorkspace, rel) : guestWorkspace;
55
+ };
56
+ /** The guest folder for the current directory (or --workspace) in the running sandbox. */
57
+ export const currentGuestDir = (ctx, workspaceHost, guestWorkspace) => guestPathFor(path.resolve(ctx.options.workspace ?? process.cwd()), workspaceHost, guestWorkspace);
@@ -0,0 +1,15 @@
1
+ import { type Context } from '../context.js';
2
+ export interface CleanTarget {
3
+ /** The target as normalised (forward slashes), e.g. "tools/go". */
4
+ target: string;
5
+ path: string;
6
+ /** Folder the target must stay inside, with no links between the two. */
7
+ root: string;
8
+ warning?: string;
9
+ }
10
+ /**
11
+ * Turns `sandbox data clean` arguments into folders. Pure (no disk access); throws a UserError for anything that is
12
+ * not a plain relative target inside the data folder: absolute paths, drive letters, `..`, wildcards, unknown areas.
13
+ */
14
+ export declare const resolveCleanTargets: (inputs: string[], dataPath: string, storageRoot: string) => CleanTarget[];
15
+ export declare const dataClean: (ctx: Context) => Promise<number>;
@@ -0,0 +1,101 @@
1
+ // `sandbox data clean`: turns the targets you name into folders inside the data folder, and removes them.
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import _ from 'lodash';
5
+ import { humanBytes, sizeOf } from '../../../core/disk.js';
6
+ import { askTyped, askYesNo } from '../../../core/prompt.js';
7
+ import { assertNoLinks, fail, isInsideOrEqual, isLink } from '../../../core/util.js';
8
+ import { isSandboxRunning } from '../../../host/platform.js';
9
+ import { table } from '../context.js';
10
+ import { removeTree } from './remove.js';
11
+ const DATA_AREAS = ['tools', 'databases', 'state', 'logs'];
12
+ const SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._ +@-]*$/;
13
+ const LOGIN_WARNING = 'removes the agent logins, settings and history inside the sandbox (save a Claude login first with: sandbox account save <name>)';
14
+ const ALL_WARNING = 'removes EVERYTHING the sandbox keeps: toolchains, databases, agent logins and history, logs and launch folders';
15
+ /**
16
+ * Turns `sandbox data clean` arguments into folders. Pure (no disk access); throws a UserError for anything that is
17
+ * not a plain relative target inside the data folder: absolute paths, drive letters, `..`, wildcards, unknown areas.
18
+ */
19
+ export const resolveCleanTargets = (inputs, dataPath, storageRoot) => {
20
+ const usage = 'Targets: all | tools | tools/<name> | tools/agents/<name> | databases | databases/<name> | state | state/<agent> | logs | launches. See them with: sandbox data';
21
+ if (!inputs.length)
22
+ fail('Say what to remove.', usage);
23
+ const resolved = inputs.map((raw) => {
24
+ const input = raw.trim();
25
+ if (!input)
26
+ fail('Empty target.', usage);
27
+ if (path.win32.isAbsolute(input) || path.posix.isAbsolute(input) || /^[A-Za-z]:/.test(input) || input.includes(':'))
28
+ fail('Refusing "' + raw + '": targets are relative to the data folder, not paths on this PC.', usage);
29
+ const parts = input.replace(/\\/g, '/').split('/').filter(Boolean);
30
+ const bad = parts.find((p) => !SEGMENT.test(p) || /^\.+$/.test(p) || /[. ]$/.test(p));
31
+ if (bad !== undefined)
32
+ fail('Refusing "' + raw + '": "' + bad + '" is not a plain folder name (no .., wildcards or special characters).', usage);
33
+ const [head, ...rest] = parts;
34
+ const area = head.toLowerCase();
35
+ if (parts.length > 3)
36
+ fail('Refusing "' + raw + '": too deep.', usage);
37
+ const target = [area, ...rest].join('/');
38
+ if (area === 'all' && !rest.length)
39
+ return { target: 'all', path: dataPath, root: path.join(storageRoot, 'data'), warning: ALL_WARNING };
40
+ if (area === 'launches')
41
+ return { target, path: path.join(storageRoot, 'launches', ...rest), root: storageRoot };
42
+ if (area === 'accounts' || area === 'sync')
43
+ fail('"' + area + '" is kept on this PC on purpose and is not removed here.', area === 'accounts' ? 'Remove a saved login with: sandbox account remove <name>' : usage);
44
+ if (!DATA_AREAS.includes(area))
45
+ fail('Unknown target "' + raw + '".', usage);
46
+ if (area === 'logs' && rest.length)
47
+ fail('Remove all logs ("logs"), not single files.', usage);
48
+ if (rest.length > (area === 'tools' && rest[0]?.toLowerCase() === 'agents' ? 2 : 1))
49
+ fail('Refusing "' + raw + '": too deep.', usage);
50
+ return { target, path: path.join(dataPath, area, ...rest), root: dataPath, ...(area === 'state' ? { warning: LOGIN_WARNING } : {}) };
51
+ });
52
+ // "all" covers everything; otherwise drop targets inside another target (tools + tools/go = tools).
53
+ const all = resolved.find((t) => t.target === 'all');
54
+ if (all)
55
+ return [all, { target: 'launches', path: path.join(storageRoot, 'launches'), root: storageRoot }];
56
+ const unique = _.uniqBy(resolved, (t) => t.path.toLowerCase());
57
+ return unique.filter((t) => !unique.some((o) => o !== t && isInsideOrEqual(o.path, t.path)));
58
+ };
59
+ export const dataClean = async (ctx) => {
60
+ const targets = resolveCleanTargets(ctx.options.positionals.slice(1), ctx.dataPath, ctx.storageRoot);
61
+ if (isSandboxRunning())
62
+ fail('A Windows Sandbox is running, and it uses these folders.', 'Close it first (sandbox stop), then run this again.');
63
+ // Every folder from the root down to the target's parent must be real; a link in place of the target is removed itself.
64
+ for (const t of targets)
65
+ assertNoLinks(t.root, path.dirname(t.path));
66
+ const present = targets.filter((t) => fs.existsSync(t.path) || isLink(t.path));
67
+ if (!present.length) {
68
+ console.log('Nothing to remove: ' + targets.map((t) => t.target).join(', ') + ' do(es) not exist.');
69
+ return 0;
70
+ }
71
+ const sized = present.map((t) => ({ ...t, bytes: sizeOf(t.path) }));
72
+ const total = _.sumBy(sized, 'bytes');
73
+ table(sized.map((t) => [' ' + t.target, humanBytes(t.bytes), t.path]));
74
+ for (const t of sized)
75
+ if (t.warning)
76
+ console.log('Warning: ' + t.target + ' ' + t.warning + '.');
77
+ const yes = Boolean(ctx.options.yes);
78
+ if (!yes) {
79
+ const ok = sized.some((t) => t.target === 'all')
80
+ ? await askTyped('This cannot be undone; the next sandbox starts from scratch and reinstalls everything.', 'all')
81
+ : await askYesNo('Delete ' + sized.map((t) => t.target).join(', ') + ' (' + humanBytes(total) + ')? This cannot be undone.');
82
+ if (!ok) {
83
+ console.log('Nothing removed.' + (process.stdin.isTTY ? '' : ' Pass --yes to confirm without a prompt.'));
84
+ return 1;
85
+ }
86
+ }
87
+ const failed = [];
88
+ for (const t of sized) {
89
+ try {
90
+ removeTree(t.path, t.root);
91
+ }
92
+ catch (e) {
93
+ failed.push(t.target + ' (' + (e instanceof Error ? e.message : String(e)) + ')');
94
+ }
95
+ }
96
+ const freed = total - _.sumBy(sized, (t) => sizeOf(t.path));
97
+ console.log('Freed ' + humanBytes(freed) + '. ' + (sized.some((t) => t.root === ctx.dataPath || t.target === 'all') ? 'The next sandbox reinstalls what it needs.' : ''));
98
+ if (failed.length)
99
+ fail('Could not remove everything: ' + failed.join('; '), 'Close programs using those folders and run the same command again.');
100
+ return 0;
101
+ };
@@ -0,0 +1,11 @@
1
+ export type DataArea = 'tools' | 'databases' | 'state' | 'logs' | 'data' | 'host';
2
+ export interface DataEntry {
3
+ area: DataArea;
4
+ name: string;
5
+ path: string;
6
+ bytes: number;
7
+ }
8
+ /** Every row of `sandbox data`: the sandbox's data folder by area, then what this PC keeps for it. */
9
+ export declare const collectDataEntries: (dataPath: string, storageRoot: string) => DataEntry[];
10
+ /** The `sandbox data clean` target for a listed row, or null when that row is not removable (kept on this PC on purpose). */
11
+ export declare const cleanTargetOf: (entry: DataEntry) => string | null;
@@ -0,0 +1,54 @@
1
+ // What `sandbox data` lists: the data folder by area, then what this PC keeps for the sandbox.
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import _ from 'lodash';
5
+ import { sizeOf } from '../../../core/disk.js';
6
+ import { dirExists, isLink } from '../../../core/util.js';
7
+ const AREA_ORDER = ['tools', 'databases', 'state', 'logs', 'data', 'host'];
8
+ /** Folders of the data folder that get one row per child; anything else at the top level is listed as itself. */
9
+ const PER_CHILD = ['tools', 'databases', 'state'];
10
+ /** Real (non-link) children of a real folder; a link in place of the folder yields nothing. */
11
+ const entriesOf = (dir) => (dirExists(dir) && !isLink(dir) ? fs.readdirSync(dir, { withFileTypes: true }).filter((d) => !d.isSymbolicLink()) : []);
12
+ const toolRows = (toolsDir) => {
13
+ const entries = entriesOf(toolsDir);
14
+ const folders = entries.filter((d) => d.isDirectory());
15
+ const files = entries.filter((d) => !d.isDirectory());
16
+ const row = (name, p) => ({ area: 'tools', name, path: p, bytes: sizeOf(p) });
17
+ return [
18
+ ...folders.flatMap((d) => (d.name.toLowerCase() === 'agents'
19
+ ? entriesOf(path.join(toolsDir, d.name)).map((a) => row('agents/' + a.name, path.join(toolsDir, d.name, a.name)))
20
+ : [row(d.name, path.join(toolsDir, d.name))])),
21
+ ...(files.length ? [{ area: 'tools', name: '(files in tools)', path: toolsDir, bytes: _.sumBy(files, (f) => sizeOf(path.join(toolsDir, f.name))) }] : []),
22
+ ];
23
+ };
24
+ /** Every row of `sandbox data`: the sandbox's data folder by area, then what this PC keeps for it. */
25
+ export const collectDataEntries = (dataPath, storageRoot) => {
26
+ const inData = entriesOf(dataPath);
27
+ const rows = inData.flatMap((d) => {
28
+ const p = path.join(dataPath, d.name);
29
+ const area = d.name.toLowerCase();
30
+ if (area === 'tools')
31
+ return toolRows(p);
32
+ if (PER_CHILD.includes(area))
33
+ return entriesOf(p).map((c) => ({ area, name: area + '/' + c.name, path: path.join(p, c.name), bytes: sizeOf(path.join(p, c.name)) }));
34
+ return [{ area: area === 'logs' ? 'logs' : 'data', name: d.name, path: p, bytes: sizeOf(p) }];
35
+ });
36
+ const otherData = entriesOf(path.join(storageRoot, 'data')).filter((d) => d.isDirectory() && path.join(storageRoot, 'data', d.name).toLowerCase() !== path.resolve(dataPath).toLowerCase());
37
+ const host = [
38
+ ...['launches', 'accounts', 'sync', 'logs', 'run', 'host'].map((name) => ({ area: 'host', name, path: path.join(storageRoot, name), bytes: sizeOf(path.join(storageRoot, name)) })),
39
+ ...otherData.map((d) => ({ area: 'host', name: 'other data folder: ' + d.name, path: path.join(storageRoot, 'data', d.name), bytes: sizeOf(path.join(storageRoot, 'data', d.name)) })),
40
+ ].filter((r) => fs.existsSync(r.path));
41
+ return _.sortBy([...rows, ...host], (r) => AREA_ORDER.indexOf(r.area), (r) => -r.bytes, (r) => r.name.toLowerCase());
42
+ };
43
+ /** The `sandbox data clean` target for a listed row, or null when that row is not removable (kept on this PC on purpose). */
44
+ export const cleanTargetOf = (entry) => {
45
+ if (entry.area === 'tools')
46
+ return entry.name.startsWith('(') ? null : 'tools/' + entry.name;
47
+ if (entry.area === 'databases' || entry.area === 'state')
48
+ return entry.name;
49
+ if (entry.area === 'logs')
50
+ return 'logs';
51
+ if (entry.area === 'host' && entry.name === 'launches')
52
+ return 'launches';
53
+ return null;
54
+ };
@@ -0,0 +1,4 @@
1
+ import { type Context } from '../context.js';
2
+ export declare const dataInfo: (ctx: Context) => Promise<number>;
3
+ /** `sandbox data [info|clean] [targets...]`. */
4
+ export declare const data: (ctx: Context) => number | Promise<number>;