windows-development-sandbox 1.0.5 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (453) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -8
  3. package/config.example.json +55 -5
  4. package/dist/api.js +2 -1
  5. package/dist/cli/args.d.ts +23 -0
  6. package/dist/cli/args.js +38 -4
  7. package/dist/cli/colors.d.ts +24 -0
  8. package/dist/cli/colors.js +42 -0
  9. package/dist/cli/commands/accounts.js +2 -2
  10. package/dist/cli/commands/agent-commands.d.ts +6 -0
  11. package/dist/cli/commands/agent-commands.js +61 -0
  12. package/dist/cli/commands/context.d.ts +2 -0
  13. package/dist/cli/commands/context.js +5 -1
  14. package/dist/cli/commands/data/clean.d.ts +15 -0
  15. package/dist/cli/commands/data/clean.js +106 -0
  16. package/dist/cli/commands/data/collect.d.ts +11 -0
  17. package/dist/cli/commands/data/collect.js +54 -0
  18. package/dist/cli/commands/data/index.d.ts +4 -0
  19. package/dist/cli/commands/data/index.js +45 -0
  20. package/dist/cli/commands/data/remove.d.ts +2 -0
  21. package/dist/cli/commands/data/remove.js +30 -0
  22. package/dist/cli/commands/index.d.ts +6 -4
  23. package/dist/cli/commands/index.js +6 -4
  24. package/dist/cli/commands/launch.d.ts +26 -2
  25. package/dist/cli/commands/launch.js +256 -42
  26. package/dist/cli/commands/maintenance.d.ts +2 -0
  27. package/dist/cli/commands/maintenance.js +67 -5
  28. package/dist/cli/commands/project.d.ts +29 -0
  29. package/dist/cli/commands/project.js +63 -0
  30. package/dist/cli/commands/projects.d.ts +14 -0
  31. package/dist/cli/commands/projects.js +146 -0
  32. package/dist/cli/commands/repair/checks.d.ts +2 -0
  33. package/dist/cli/commands/{repair.js → repair/checks.js} +19 -181
  34. package/dist/cli/commands/{repair.d.ts → repair/finders.d.ts} +0 -29
  35. package/dist/cli/commands/repair/finders.js +108 -0
  36. package/dist/cli/commands/repair/index.d.ts +12 -0
  37. package/dist/cli/commands/repair/index.js +61 -0
  38. package/dist/cli/commands/repair/types.d.ts +26 -0
  39. package/dist/cli/commands/repair/types.js +3 -0
  40. package/dist/cli/commands/restart.d.ts +6 -0
  41. package/dist/cli/commands/restart.js +85 -0
  42. package/dist/cli/commands/sessions.d.ts +2 -1
  43. package/dist/cli/commands/sessions.js +189 -45
  44. package/dist/cli/commands/setup/checks.d.ts +13 -0
  45. package/dist/cli/commands/setup/checks.js +71 -0
  46. package/dist/cli/commands/{setup.d.ts → setup/facts.d.ts} +1 -15
  47. package/dist/cli/commands/setup/facts.js +60 -0
  48. package/dist/cli/commands/setup/index.d.ts +2 -0
  49. package/dist/cli/commands/setup/index.js +43 -0
  50. package/dist/cli/commands/user-config.d.ts +8 -0
  51. package/dist/cli/commands/user-config.js +41 -0
  52. package/dist/cli/history.d.ts +11 -0
  53. package/dist/cli/history.js +21 -0
  54. package/dist/cli/index.d.ts +7 -1
  55. package/dist/cli/index.js +42 -15
  56. package/dist/cli/manifest.d.ts +8 -0
  57. package/dist/cli/manifest.js +40 -15
  58. package/dist/cli/menu/apps.d.ts +21 -0
  59. package/dist/cli/menu/apps.js +122 -0
  60. package/dist/cli/menu/command-form.d.ts +28 -0
  61. package/dist/cli/menu/command-form.js +219 -0
  62. package/dist/cli/menu/index.d.ts +26 -0
  63. package/dist/cli/menu/index.js +180 -0
  64. package/dist/cli/menu/settings/categories.d.ts +9 -0
  65. package/dist/cli/menu/settings/categories.js +28 -0
  66. package/dist/cli/menu/settings/config-file.d.ts +40 -0
  67. package/dist/cli/menu/settings/config-file.js +71 -0
  68. package/dist/cli/menu/settings/editors.d.ts +30 -0
  69. package/dist/cli/menu/settings/editors.js +305 -0
  70. package/dist/cli/menu/settings/index.d.ts +32 -0
  71. package/dist/cli/menu/settings/index.js +136 -0
  72. package/dist/cli/menu/settings/schema-node.d.ts +62 -0
  73. package/dist/cli/menu/settings/schema-node.js +104 -0
  74. package/dist/cli/menu/several.d.ts +20 -0
  75. package/dist/cli/menu/several.js +102 -0
  76. package/dist/cli/progress.d.ts +9 -0
  77. package/dist/cli/progress.js +49 -0
  78. package/dist/cli/tui/app.d.ts +39 -0
  79. package/dist/cli/tui/app.js +52 -0
  80. package/dist/cli/tui/dialogs.d.ts +57 -0
  81. package/dist/cli/tui/dialogs.js +162 -0
  82. package/dist/cli/tui/frame.d.ts +18 -0
  83. package/dist/cli/tui/frame.js +27 -0
  84. package/dist/cli/tui/index.d.ts +7 -0
  85. package/dist/cli/tui/index.js +8 -0
  86. package/dist/cli/tui/list-screen.d.ts +101 -0
  87. package/dist/cli/tui/list-screen.js +299 -0
  88. package/dist/cli/tui/terminal.d.ts +37 -0
  89. package/dist/cli/tui/terminal.js +89 -0
  90. package/dist/cli/tui/text.d.ts +20 -0
  91. package/dist/cli/tui/text.js +105 -0
  92. package/dist/cli/tui/theme.d.ts +45 -0
  93. package/dist/cli/tui/theme.js +30 -0
  94. package/dist/cli/validate.d.ts +17 -0
  95. package/dist/cli/validate.js +89 -0
  96. package/dist/core/config.js +12 -6
  97. package/dist/core/disk.d.ts +16 -0
  98. package/dist/core/disk.js +55 -0
  99. package/dist/core/local-config.d.ts +23 -0
  100. package/dist/core/local-config.js +79 -7
  101. package/dist/core/project-policy.d.ts +15 -0
  102. package/dist/core/project-policy.js +35 -0
  103. package/dist/core/prompt.d.ts +4 -0
  104. package/dist/core/prompt.js +15 -0
  105. package/dist/core/safe-paths.d.ts +4 -0
  106. package/dist/core/safe-paths.js +37 -0
  107. package/dist/core/schema/apps.d.ts +40 -0
  108. package/dist/core/schema/apps.js +27 -0
  109. package/dist/core/schema/common.d.ts +17 -0
  110. package/dist/core/schema/common.js +15 -0
  111. package/dist/core/schema/config.d.ts +659 -0
  112. package/dist/core/schema/config.js +75 -0
  113. package/dist/core/schema/databases.d.ts +33 -0
  114. package/dist/core/schema/databases.js +35 -0
  115. package/dist/core/schema/harness.d.ts +12 -0
  116. package/dist/core/schema/harness.js +17 -0
  117. package/dist/core/schema/json-schema.d.ts +5 -0
  118. package/dist/core/schema/json-schema.js +25 -0
  119. package/dist/core/schema/profile.d.ts +32 -0
  120. package/dist/core/schema/profile.js +31 -0
  121. package/dist/core/schema/projects.d.ts +9 -0
  122. package/dist/core/schema/projects.js +11 -0
  123. package/dist/core/schema/sandbox.d.ts +78 -0
  124. package/dist/core/schema/sandbox.js +64 -0
  125. package/dist/core/schema/toolchains.d.ts +96 -0
  126. package/dist/core/schema/toolchains.js +88 -0
  127. package/dist/core/schema.d.ts +6 -604
  128. package/dist/core/schema.js +8 -264
  129. package/dist/core/types.d.ts +35 -1
  130. package/dist/core/util.d.ts +9 -0
  131. package/dist/core/util.js +14 -3
  132. package/dist/core/workspace.d.ts +0 -5
  133. package/dist/core/workspace.js +33 -50
  134. package/dist/guest/apps/index.d.ts +10 -0
  135. package/dist/guest/apps/index.js +206 -0
  136. package/dist/guest/apps/resolvers.d.ts +8 -0
  137. package/dist/guest/apps/resolvers.js +90 -0
  138. package/dist/guest/audit-parse.d.ts +7 -0
  139. package/dist/guest/audit-parse.js +24 -0
  140. package/dist/guest/audit.d.ts +4 -0
  141. package/dist/guest/audit.js +47 -0
  142. package/dist/guest/boot-env.d.ts +4 -0
  143. package/dist/guest/boot-env.js +25 -0
  144. package/dist/guest/cmdline.d.ts +12 -0
  145. package/dist/guest/cmdline.js +23 -0
  146. package/dist/guest/daemon/exec.d.ts +5 -0
  147. package/dist/guest/daemon/exec.js +32 -0
  148. package/dist/guest/daemon/project.d.ts +14 -0
  149. package/dist/guest/daemon/project.js +132 -0
  150. package/dist/guest/daemon/relays.d.ts +1 -0
  151. package/dist/guest/daemon/relays.js +18 -0
  152. package/dist/guest/daemon/session.d.ts +13 -0
  153. package/dist/guest/daemon/session.js +139 -0
  154. package/dist/guest/daemon/shutdown.d.ts +1 -0
  155. package/dist/guest/daemon/shutdown.js +21 -0
  156. package/dist/guest/daemon/state.d.ts +17 -0
  157. package/dist/guest/daemon/state.js +31 -0
  158. package/dist/guest/daemon.js +12 -141
  159. package/dist/guest/db.js +18 -3
  160. package/dist/guest/env.d.ts +3 -0
  161. package/dist/guest/env.js +7 -3
  162. package/dist/guest/guide.d.ts +6 -2
  163. package/dist/guest/guide.js +5 -4
  164. package/dist/guest/helpers.d.ts +3 -0
  165. package/dist/guest/helpers.js +26 -0
  166. package/dist/guest/main.js +31 -8
  167. package/dist/guest/netproxy.d.ts +1 -0
  168. package/dist/guest/netproxy.js +100 -0
  169. package/dist/guest/ports.d.ts +6 -0
  170. package/dist/guest/ports.js +22 -0
  171. package/dist/guest/project-context.d.ts +7 -0
  172. package/dist/guest/project-context.js +10 -0
  173. package/dist/guest/project-env.d.ts +20 -0
  174. package/dist/guest/project-env.js +20 -0
  175. package/dist/guest/project-status.d.ts +5 -0
  176. package/dist/guest/project-status.js +17 -0
  177. package/dist/guest/ptyhost.d.ts +0 -18
  178. package/dist/guest/ptyhost.js +17 -65
  179. package/dist/guest/setup/agents.d.ts +5 -2
  180. package/dist/guest/setup/agents.js +22 -8
  181. package/dist/guest/setup/claude.d.ts +0 -4
  182. package/dist/guest/setup/claude.js +59 -5
  183. package/dist/guest/setup/environment.d.ts +13 -3
  184. package/dist/guest/setup/environment.js +14 -10
  185. package/dist/guest/setup/hooks.d.ts +9 -1
  186. package/dist/guest/setup/hooks.js +28 -14
  187. package/dist/guest/setup/network.d.ts +9 -0
  188. package/dist/guest/setup/network.js +97 -4
  189. package/dist/guest/setup/user.js +4 -0
  190. package/dist/guest/toolchains/catalogue.d.ts +4 -0
  191. package/dist/guest/toolchains/catalogue.js +2 -2
  192. package/dist/guest/toolchains/cli.js +25 -17
  193. package/dist/guest/toolchains/common.d.ts +18 -1
  194. package/dist/guest/toolchains/common.js +34 -11
  195. package/dist/guest/toolchains/databases/dynamodb.d.ts +8 -0
  196. package/dist/guest/toolchains/databases/dynamodb.js +52 -0
  197. package/dist/guest/toolchains/{databases.d.ts → databases/index.d.ts} +2 -3
  198. package/dist/guest/toolchains/databases/index.js +62 -0
  199. package/dist/guest/toolchains/databases/mongodb.d.ts +3 -0
  200. package/dist/guest/toolchains/databases/mongodb.js +31 -0
  201. package/dist/guest/toolchains/databases/mysql.d.ts +3 -0
  202. package/dist/guest/toolchains/databases/mysql.js +38 -0
  203. package/dist/guest/toolchains/databases/postgres.d.ts +5 -0
  204. package/dist/guest/toolchains/databases/postgres.js +38 -0
  205. package/dist/guest/toolchains/databases/redis.d.ts +3 -0
  206. package/dist/guest/toolchains/databases/redis.js +16 -0
  207. package/dist/guest/toolchains/databases/server.d.ts +14 -0
  208. package/dist/guest/toolchains/databases/server.js +52 -0
  209. package/dist/guest/toolchains/git.js +25 -16
  210. package/dist/guest/toolchains/go.js +1 -1
  211. package/dist/guest/toolchains/index.d.ts +13 -9
  212. package/dist/guest/toolchains/index.js +40 -82
  213. package/dist/guest/toolchains/java.js +5 -5
  214. package/dist/guest/toolchains/leftovers.d.ts +2 -0
  215. package/dist/guest/toolchains/leftovers.js +26 -0
  216. package/dist/guest/toolchains/managers.js +12 -10
  217. package/dist/guest/toolchains/{binstall.js → native/binstall.js} +6 -9
  218. package/dist/guest/toolchains/native/cpp.d.ts +4 -0
  219. package/dist/guest/toolchains/native/cpp.js +38 -0
  220. package/dist/guest/toolchains/native/dotnet.d.ts +4 -0
  221. package/dist/guest/toolchains/native/dotnet.js +103 -0
  222. package/dist/guest/toolchains/native/mesa.d.ts +7 -0
  223. package/dist/guest/toolchains/native/mesa.js +56 -0
  224. package/dist/guest/toolchains/native/msvc/environment.d.ts +16 -0
  225. package/dist/guest/toolchains/native/msvc/environment.js +57 -0
  226. package/dist/guest/toolchains/native/msvc/index.d.ts +6 -0
  227. package/dist/guest/toolchains/native/msvc/index.js +48 -0
  228. package/dist/guest/toolchains/native/msvc/packages.d.ts +6 -0
  229. package/dist/guest/toolchains/native/msvc/packages.js +156 -0
  230. package/dist/guest/toolchains/native/msvc/tree.d.ts +15 -0
  231. package/dist/guest/toolchains/native/msvc/tree.js +21 -0
  232. package/dist/guest/toolchains/native/rust.d.ts +3 -0
  233. package/dist/guest/toolchains/native/rust.js +45 -0
  234. package/dist/guest/toolchains/node.js +26 -6
  235. package/dist/guest/toolchains/plan.d.ts +24 -0
  236. package/dist/guest/toolchains/plan.js +140 -0
  237. package/dist/guest/toolchains/pwsh.js +1 -1
  238. package/dist/guest/toolchains/python.js +43 -12
  239. package/dist/guest/toolchains/runtimes.d.ts +2 -0
  240. package/dist/guest/toolchains/runtimes.js +6 -8
  241. package/dist/guest/toolchains/scripting/php.d.ts +3 -0
  242. package/dist/guest/toolchains/scripting/php.js +71 -0
  243. package/dist/guest/toolchains/scripting/ruby.d.ts +3 -0
  244. package/dist/guest/toolchains/scripting/ruby.js +47 -0
  245. package/dist/guest/toolchains/scripting/vcruntime.d.ts +7 -0
  246. package/dist/guest/toolchains/scripting/vcruntime.js +45 -0
  247. package/dist/guest/toolchains/vscode-profile.d.ts +30 -0
  248. package/dist/guest/toolchains/vscode-profile.js +69 -0
  249. package/dist/guest/toolchains/vscode.d.ts +17 -0
  250. package/dist/guest/toolchains/vscode.js +87 -1
  251. package/dist/guest/view.js +21 -1
  252. package/dist/host/accounts.js +14 -5
  253. package/dist/host/agent-commands.d.ts +25 -0
  254. package/dist/host/agent-commands.js +145 -0
  255. package/dist/host/attach.d.ts +17 -0
  256. package/dist/host/attach.js +70 -0
  257. package/dist/host/audit/collect.d.ts +34 -0
  258. package/dist/host/audit/collect.js +126 -0
  259. package/dist/host/audit/log.d.ts +46 -0
  260. package/dist/host/audit/log.js +143 -0
  261. package/dist/host/channel.d.ts +23 -22
  262. package/dist/host/channel.js +66 -84
  263. package/dist/host/claude-profile.js +7 -3
  264. package/dist/host/cleanup.js +2 -11
  265. package/dist/host/database-plan.d.ts +12 -0
  266. package/dist/host/database-plan.js +52 -0
  267. package/dist/host/generate.d.ts +10 -12
  268. package/dist/host/generate.js +58 -135
  269. package/dist/host/join.d.ts +28 -0
  270. package/dist/host/join.js +76 -0
  271. package/dist/host/launch/mappings.d.ts +3 -0
  272. package/dist/host/launch/mappings.js +54 -0
  273. package/dist/host/launch/projects.d.ts +15 -0
  274. package/dist/host/launch/projects.js +56 -0
  275. package/dist/host/launch/stage.d.ts +11 -0
  276. package/dist/host/launch/stage.js +66 -0
  277. package/dist/host/launch/wsb.d.ts +10 -0
  278. package/dist/host/launch/wsb.js +34 -0
  279. package/dist/host/motw.d.ts +17 -0
  280. package/dist/host/motw.js +74 -0
  281. package/dist/host/pc-scan.d.ts +6 -0
  282. package/dist/host/pc-scan.js +37 -0
  283. package/dist/host/platform.d.ts +2 -0
  284. package/dist/host/platform.js +16 -1
  285. package/dist/host/port-forward.d.ts +8 -0
  286. package/dist/host/port-forward.js +37 -0
  287. package/dist/host/project-scan.d.ts +1 -17
  288. package/dist/host/project-scan.js +2 -86
  289. package/dist/host/projects.d.ts +100 -0
  290. package/dist/host/projects.js +147 -0
  291. package/dist/host/repo-guard.d.ts +25 -5
  292. package/dist/host/repo-guard.js +242 -23
  293. package/dist/host/stops.d.ts +67 -0
  294. package/dist/host/stops.js +126 -0
  295. package/dist/host/terminal.d.ts +8 -0
  296. package/dist/host/terminal.js +48 -1
  297. package/dist/host/test-guard.d.ts +6 -0
  298. package/dist/host/test-guard.js +15 -0
  299. package/dist/host/window.js +7 -3
  300. package/dist/host/workspace-view/index.d.ts +20 -0
  301. package/dist/host/workspace-view/index.js +53 -0
  302. package/dist/host/workspace-view/rules.d.ts +26 -0
  303. package/dist/host/workspace-view/rules.js +93 -0
  304. package/dist/host/workspace-view/view.d.ts +50 -0
  305. package/dist/host/workspace-view/view.js +281 -0
  306. package/dist/host/wsb.d.ts +37 -0
  307. package/dist/host/wsb.js +101 -0
  308. package/dist/runtime/daemon.js +11791 -2821
  309. package/dist/runtime/db.js +6585 -466
  310. package/dist/runtime/main.js +10238 -1724
  311. package/dist/runtime/netproxy.js +25936 -0
  312. package/dist/runtime/view.js +6183 -59
  313. package/dist/selftest/accounts.test.d.ts +2 -0
  314. package/dist/selftest/accounts.test.js +44 -0
  315. package/dist/selftest/agent-commands.test.d.ts +2 -0
  316. package/dist/selftest/agent-commands.test.js +47 -0
  317. package/dist/selftest/audit.test.d.ts +2 -0
  318. package/dist/selftest/audit.test.js +57 -0
  319. package/dist/selftest/channel.test.d.ts +2 -0
  320. package/dist/selftest/channel.test.js +44 -0
  321. package/dist/selftest/cli.test.d.ts +2 -2
  322. package/dist/selftest/cli.test.js +145 -27
  323. package/dist/selftest/cmdline.test.d.ts +2 -0
  324. package/dist/selftest/cmdline.test.js +45 -0
  325. package/dist/selftest/config.test.d.ts +2 -3
  326. package/dist/selftest/config.test.js +36 -72
  327. package/dist/selftest/data.test.d.ts +2 -2
  328. package/dist/selftest/data.test.js +77 -70
  329. package/dist/selftest/detect.test.d.ts +2 -2
  330. package/dist/selftest/detect.test.js +54 -38
  331. package/dist/selftest/fixtures.d.ts +89 -4
  332. package/dist/selftest/fixtures.js +43 -24
  333. package/dist/selftest/generate.test.d.ts +2 -0
  334. package/dist/selftest/generate.test.js +68 -0
  335. package/dist/selftest/git-access.test.d.ts +2 -0
  336. package/dist/selftest/git-access.test.js +26 -0
  337. package/dist/selftest/guest-boot.test.d.ts +2 -0
  338. package/dist/selftest/guest-boot.test.js +76 -0
  339. package/dist/selftest/harness.d.ts +44 -7
  340. package/dist/selftest/harness.js +62 -21
  341. package/dist/selftest/host-sim.test.d.ts +2 -0
  342. package/dist/selftest/host-sim.test.js +93 -0
  343. package/dist/selftest/hostile-input.test.d.ts +2 -0
  344. package/dist/selftest/hostile-input.test.js +54 -0
  345. package/dist/selftest/index.d.ts +8 -1
  346. package/dist/selftest/index.js +137 -35
  347. package/dist/selftest/links.test.d.ts +2 -0
  348. package/dist/selftest/links.test.js +50 -0
  349. package/dist/selftest/live-daemon.test.d.ts +2 -0
  350. package/dist/selftest/live-daemon.test.js +91 -0
  351. package/dist/selftest/live-git.test.d.ts +2 -0
  352. package/dist/selftest/live-git.test.js +60 -0
  353. package/dist/selftest/live-network.test.d.ts +2 -0
  354. package/dist/selftest/live-network.test.js +63 -0
  355. package/dist/selftest/live-projects.test.d.ts +2 -0
  356. package/dist/selftest/live-projects.test.js +127 -0
  357. package/dist/selftest/live.d.ts +65 -0
  358. package/dist/selftest/live.js +109 -0
  359. package/dist/selftest/local-config.test.d.ts +2 -0
  360. package/dist/selftest/local-config.test.js +120 -0
  361. package/dist/selftest/mirror.test.d.ts +2 -3
  362. package/dist/selftest/mirror.test.js +92 -65
  363. package/dist/selftest/network.test.d.ts +2 -0
  364. package/dist/selftest/network.test.js +40 -0
  365. package/dist/selftest/personalization.test.d.ts +2 -0
  366. package/dist/selftest/personalization.test.js +53 -0
  367. package/dist/selftest/projects.test.d.ts +2 -0
  368. package/dist/selftest/projects.test.js +254 -0
  369. package/dist/selftest/repair.test.d.ts +2 -0
  370. package/dist/selftest/repair.test.js +42 -0
  371. package/dist/selftest/repo-guard.test.d.ts +2 -0
  372. package/dist/selftest/repo-guard.test.js +121 -0
  373. package/dist/selftest/secrets.test.d.ts +2 -0
  374. package/dist/selftest/secrets.test.js +71 -0
  375. package/dist/selftest/setup.test.d.ts +2 -2
  376. package/dist/selftest/setup.test.js +45 -22
  377. package/dist/selftest/suite.d.ts +13 -0
  378. package/dist/selftest/suite.js +1 -0
  379. package/dist/selftest/sync.test.d.ts +2 -3
  380. package/dist/selftest/sync.test.js +130 -78
  381. package/dist/selftest/test-guard.test.d.ts +2 -0
  382. package/dist/selftest/test-guard.test.js +49 -0
  383. package/dist/selftest/toolchains.test.d.ts +2 -0
  384. package/dist/selftest/toolchains.test.js +133 -0
  385. package/dist/selftest/workspace-view.test.d.ts +2 -0
  386. package/dist/selftest/workspace-view.test.js +89 -0
  387. package/dist/selftest/workspace.test.d.ts +2 -0
  388. package/dist/selftest/workspace.test.js +53 -0
  389. package/dist/shared/apps.d.ts +49 -0
  390. package/dist/shared/apps.js +114 -0
  391. package/dist/shared/download.d.ts +3 -1
  392. package/dist/shared/download.js +19 -4
  393. package/dist/shared/netpolicy.d.ts +27 -0
  394. package/dist/shared/netpolicy.js +51 -0
  395. package/dist/shared/paths.d.ts +5 -0
  396. package/dist/shared/paths.js +6 -0
  397. package/dist/shared/projects.d.ts +53 -0
  398. package/dist/shared/projects.js +67 -0
  399. package/dist/shared/pty-client.d.ts +9 -4
  400. package/dist/shared/pty-client.js +139 -17
  401. package/dist/shared/rundir.d.ts +12 -1
  402. package/dist/shared/rundir.js +14 -4
  403. package/dist/shared/safe-remove.js +11 -0
  404. package/dist/shared/schema.d.ts +453 -0
  405. package/dist/shared/schema.js +43 -2
  406. package/dist/shared/system.d.ts +1 -0
  407. package/dist/shared/system.js +1 -0
  408. package/dist/shared/test-guard.d.ts +4 -0
  409. package/dist/shared/test-guard.js +37 -0
  410. package/dist/sync/claude-mem.js +6 -3
  411. package/dist/sync/history.d.ts +29 -12
  412. package/dist/sync/history.js +72 -25
  413. package/dist/sync/watcher.d.ts +8 -0
  414. package/dist/sync/watcher.js +99 -8
  415. package/package.json +7 -6
  416. package/sandbox/bootstrap.ps1 +258 -258
  417. package/sandbox/cargoshim.cs +56 -0
  418. package/sandbox/cpucap.cs +33 -0
  419. package/sandbox/cpucap.ps1 +20 -0
  420. package/sandbox/logon.cmd +24 -16
  421. package/sandbox/memlog.ps1 +34 -0
  422. package/sandbox/openssl.cs +42 -0
  423. package/sandbox/pagefile.cs +31 -0
  424. package/sandbox/pagefile.ps1 +21 -0
  425. package/sandbox/ptyhost.cs +120 -11
  426. package/sandbox/trust.cmd +35 -35
  427. package/schemas/config.schema.json +403 -3
  428. package/schemas/sandbox-config.schema.json +143 -13
  429. package/dist/cli/commands/data.d.ts +0 -46
  430. package/dist/cli/commands/data.js +0 -249
  431. package/dist/cli/commands/setup.js +0 -152
  432. package/dist/cli/menu.d.ts +0 -22
  433. package/dist/cli/menu.js +0 -187
  434. package/dist/cli/settings.d.ts +0 -11
  435. package/dist/cli/settings.js +0 -156
  436. package/dist/guest/toolchains/databases.js +0 -226
  437. package/dist/guest/toolchains/msvc.d.ts +0 -10
  438. package/dist/guest/toolchains/msvc.js +0 -216
  439. package/dist/guest/toolchains/native.d.ts +0 -12
  440. package/dist/guest/toolchains/native.js +0 -220
  441. package/dist/guest/toolchains/scripting.d.ts +0 -10
  442. package/dist/guest/toolchains/scripting.js +0 -137
  443. package/dist/selftest/guest.test.d.ts +0 -3
  444. package/dist/selftest/guest.test.js +0 -61
  445. package/dist/selftest/helpers.test.d.ts +0 -2
  446. package/dist/selftest/helpers.test.js +0 -125
  447. package/dist/selftest/host.test.d.ts +0 -2
  448. package/dist/selftest/host.test.js +0 -71
  449. package/dist/selftest/live.test.d.ts +0 -7
  450. package/dist/selftest/live.test.js +0 -127
  451. package/dist/selftest/security.test.d.ts +0 -3
  452. package/dist/selftest/security.test.js +0 -143
  453. /package/dist/guest/toolchains/{binstall.d.ts → native/binstall.d.ts} +0 -0
@@ -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
+ };
@@ -0,0 +1,8 @@
1
+ import type { Context } from './context.js';
2
+ export declare const configFile: (ctx: Context) => string;
3
+ /** The value as written in your file (not merged with the defaults). */
4
+ export declare const fileSetting: <T>(ctx: Context, key: string[], fallback: T) => T;
5
+ export declare const setSetting: (ctx: Context, key: string[], value: unknown) => void;
6
+ /** Adds a folder to a list setting (projects.roots, projects.exclude, ...) unless it is there already. */
7
+ export declare const addToList: (ctx: Context, key: string[], folder: string) => boolean;
8
+ export declare const removeFromList: (ctx: Context, key: string[], folder: string) => boolean;
@@ -0,0 +1,41 @@
1
+ // Changing your own config file from a command (sandbox projects ..., the "add this root?" question): one setting at
2
+ // a time through the settings editor, so comments and formatting stay and an invalid result is never written.
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { parse } from 'jsonc-parser';
6
+ import _ from 'lodash';
7
+ import { userConfigPath } from '../../core/config.js';
8
+ import { fail } from '../../core/util.js';
9
+ import { editConfigText } from '../menu/settings/config-file.js';
10
+ export const configFile = (ctx) => ctx.loaded.configPath ?? userConfigPath();
11
+ /** The value as written in your file (not merged with the defaults). */
12
+ export const fileSetting = (ctx, key, fallback) => {
13
+ const file = configFile(ctx);
14
+ const text = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
15
+ return _.get(parse(text) ?? {}, key) ?? fallback;
16
+ };
17
+ export const setSetting = (ctx, key, value) => {
18
+ const file = configFile(ctx);
19
+ const text = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
20
+ const result = editConfigText(text, key, value);
21
+ if ('problems' in result)
22
+ fail('Could not change ' + key.join('.') + ':\n ' + result.problems.join('\n '));
23
+ fs.mkdirSync(path.dirname(file), { recursive: true });
24
+ fs.writeFileSync(file, result.text, 'utf8');
25
+ };
26
+ /** Adds a folder to a list setting (projects.roots, projects.exclude, ...) unless it is there already. */
27
+ export const addToList = (ctx, key, folder) => {
28
+ const list = fileSetting(ctx, key, []);
29
+ if (list.some((p) => path.resolve(p).toLowerCase() === path.resolve(folder).toLowerCase()))
30
+ return false;
31
+ setSetting(ctx, key, [...list, folder]);
32
+ return true;
33
+ };
34
+ export const removeFromList = (ctx, key, folder) => {
35
+ const list = fileSetting(ctx, key, []);
36
+ const rest = list.filter((p) => path.resolve(p).toLowerCase() !== path.resolve(folder).toLowerCase());
37
+ if (rest.length === list.length)
38
+ return false;
39
+ setSetting(ctx, key, rest);
40
+ return true;
41
+ };
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod';
2
+ declare const History: z.ZodArray<z.ZodObject<{
3
+ argv: z.ZodArray<z.ZodString>;
4
+ at: z.ZodString;
5
+ }, z.core.$strict>>;
6
+ export declare const readHistory: () => z.output<typeof History>;
7
+ /** Remembers a command (menu or typed) so the menu can offer it again. Never fails the command itself. */
8
+ export declare const recordHistory: (argv: string[]) => void;
9
+ /** argv as you would type it (quoted where needed). */
10
+ export declare const commandLine: (argv: string[]) => string;
11
+ export {};
@@ -0,0 +1,21 @@
1
+ // The last commands run (menu or typed), kept in ~/.agent-sandbox/history.json so the menu can offer them again.
2
+ import path from 'node:path';
3
+ import { z } from 'zod';
4
+ import { userDir } from '../core/config.js';
5
+ import { writeJson } from '../core/util.js';
6
+ import { readValid } from '../shared/rundir.js';
7
+ const History = z.array(z.strictObject({ argv: z.array(z.string()).max(200), at: z.string() })).max(20);
8
+ const historyFile = () => path.join(userDir(), 'history.json');
9
+ const HIDDEN = new Set(['menu', '_watch', 'self-test']);
10
+ export const readHistory = () => readValid(History, historyFile()) ?? [];
11
+ /** Remembers a command (menu or typed) so the menu can offer it again. Never fails the command itself. */
12
+ export const recordHistory = (argv) => {
13
+ if (!argv.length || HIDDEN.has(argv[0]) || argv.includes('--help'))
14
+ return;
15
+ try {
16
+ writeJson(historyFile(), [{ argv, at: new Date().toISOString() }, ...readHistory().filter((h) => h.argv.join('\0') !== argv.join('\0'))].slice(0, 10));
17
+ }
18
+ catch { /* history is a convenience */ }
19
+ };
20
+ /** argv as you would type it (quoted where needed). */
21
+ export const commandLine = (argv) => ['sandbox', ...argv].map((a) => (/[\s"]/.test(a) || a === '' ? '"' + a.replace(/"/g, '\\"') + '"' : a)).join(' ');
@@ -4,11 +4,17 @@ export interface Preset {
4
4
  harness?: string;
5
5
  command?: string;
6
6
  }
7
+ /** `--config <file>` right after the command name (where every command accepts it); argv unchanged without a config. */
8
+ export declare const withConfig: (argv: string[], config: string | undefined) => string[];
9
+ /** `sandbox --config f.json status` works like `sandbox status --config f.json`: a leading --config moves behind the command. */
10
+ export declare const hoistConfig: (argv: string[]) => string[];
7
11
  /**
8
12
  * CLI entry. `preset` lets wrapper binaries fix the command/agent (sandbox claude = launch --harness claude). No arguments
9
13
  * opens the menu; `sandbox <agent> [agent args]` starts that agent.
10
14
  */
11
- export declare const main: (argv: string[], preset?: Preset) => Promise<number>;
15
+ export declare const main: (rawArgv: string[], preset?: Preset) => Promise<number>;
16
+ /** argv without `--config <file>` / `--config=<file>`. */
17
+ export declare const withoutConfig: (argv: string[]) => string[];
12
18
  /** Prints a problem the way a user can act on it: what happened, how to fix it; stack traces only for real bugs. */
13
19
  export declare const report: (e: unknown) => number;
14
20
  export declare const run: (argv: string[], preset?: Preset) => void;
package/dist/cli/index.js CHANGED
@@ -3,10 +3,13 @@ import { fileURLToPath } from 'node:url';
3
3
  import { CommanderError } from 'commander';
4
4
  import { buildProgram, parseCli } from './args.js';
5
5
  import { commandSpec } from './manifest.js';
6
- import { menu, recordHistory } from './menu.js';
6
+ import { c } from './colors.js';
7
+ import { recordHistory } from './history.js';
8
+ import { menu } from './menu/index.js';
9
+ import { validateOptions } from './validate.js';
7
10
  import { loadConfig } from '../core/config.js';
8
11
  import * as commands from './commands/index.js';
9
- import { UserError, errMsg, fail } from '../core/util.js';
12
+ import { UserError, errMsg, fail, plainText } from '../core/util.js';
10
13
  /** The package folder (this file is dist/cli/index.js). */
11
14
  export const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
12
15
  const entry = path.join(packageRoot, 'dist', 'bin', 'agent-sandbox.js');
@@ -17,14 +20,17 @@ const HANDLERS = {
17
20
  sessions: commands.sessions,
18
21
  status: commands.status,
19
22
  exec: commands.exec,
23
+ 'agent-commands': commands.agentCommandsCommand,
20
24
  db: commands.db,
21
25
  restart: commands.restart,
22
26
  stop: commands.stop,
23
27
  show: commands.show,
24
28
  hide: commands.hide,
25
29
  account: commands.account,
30
+ projects: commands.projects,
26
31
  sync: commands.sync,
27
32
  logs: commands.logs,
33
+ audit: commands.audit,
28
34
  clean: commands.clean,
29
35
  config: commands.config,
30
36
  doctor: commands.doctor,
@@ -33,15 +39,27 @@ const HANDLERS = {
33
39
  'host-claude': commands.claudeHost,
34
40
  _watch: commands.watcher,
35
41
  };
36
- const red = (s) => '\x1b[31m' + s + '\x1b[0m';
37
- const dim = (s) => '\x1b[2m' + s + '\x1b[0m';
38
42
  /** Runs an argv from the menu exactly as if it had been typed, reporting errors instead of throwing. */
39
- const runFromMenu = (argv) => main(argv).catch((e) => report(e));
43
+ const runFromMenu = (config) => (argv) => main(withConfig(argv, config)).catch((e) => report(e));
44
+ /** `--config <file>` right after the command name (where every command accepts it); argv unchanged without a config. */
45
+ export const withConfig = (argv, config) => (config && argv.length ? [argv[0], '--config', config, ...argv.slice(1)] : argv);
46
+ /** `sandbox --config f.json status` works like `sandbox status --config f.json`: a leading --config moves behind the command. */
47
+ export const hoistConfig = (argv) => {
48
+ const eq = argv[0]?.startsWith('--config=');
49
+ if (argv[0] !== '--config' && !eq)
50
+ return argv;
51
+ const value = eq ? argv[0].slice('--config='.length) : argv[1];
52
+ const rest = argv.slice(eq ? 1 : 2);
53
+ if (value === undefined || value === '')
54
+ return argv;
55
+ return rest.length ? withConfig(rest, value) : ['menu', '--config', value];
56
+ };
40
57
  /**
41
58
  * CLI entry. `preset` lets wrapper binaries fix the command/agent (sandbox claude = launch --harness claude). No arguments
42
59
  * opens the menu; `sandbox <agent> [agent args]` starts that agent.
43
60
  */
44
- export const main = async (argv, preset = {}) => {
61
+ export const main = async (rawArgv, preset = {}) => {
62
+ const argv = preset.command || preset.harness ? rawArgv : hoistConfig(rawArgv);
45
63
  const isAgent = (name) => {
46
64
  if (!name || name.startsWith('-') || commandSpec(name))
47
65
  return false;
@@ -62,32 +80,41 @@ export const main = async (argv, preset = {}) => {
62
80
  const options = parseCli(preset.command ? [preset.command, ...argv] : argv, Boolean(preset.harness));
63
81
  if (preset.harness)
64
82
  options.harness = preset.harness;
65
- recordHistory(preset.harness ? ['launch', '--harness', preset.harness, ...argv] : preset.command ? [preset.command, ...argv] : argv);
83
+ const typed = preset.harness ? ['launch', '--harness', preset.harness, ...argv] : preset.command ? [preset.command, ...argv] : argv;
66
84
  if (options.command === 'menu')
67
- return menu(commands.createContext(packageRoot, entry, options), runFromMenu);
85
+ return menu(commands.createContext(packageRoot, entry, options), runFromMenu(options.config));
68
86
  // Loaded only when asked for: normal commands never load test code.
69
87
  if (options.command === 'self-test')
70
- return (await import('../selftest/index.js')).selfTest(packageRoot);
88
+ return (await import('../selftest/index.js')).selfTest(packageRoot, options.positionals);
71
89
  // repair must run even when the config file is broken (createContext would refuse it).
72
- if (options.command === 'repair')
90
+ if (options.command === 'repair') {
91
+ recordHistory(withoutConfig(typed));
73
92
  return commands.repairCommand(packageRoot, entry, options);
93
+ }
74
94
  const handler = HANDLERS[options.command];
75
95
  if (!handler)
76
96
  return fail('Unknown command "' + options.command + '".', 'Run sandbox --help for the list of commands, or sandbox for the menu.');
77
- return (await handler(commands.createContext(packageRoot, entry, options))) ?? 0;
97
+ const ctx = commands.createContext(packageRoot, entry, options);
98
+ validateOptions(ctx);
99
+ // Only what parsed and validated is offered again by the menu, without the --config (it is the same every time).
100
+ recordHistory(withoutConfig(typed));
101
+ return (await handler(ctx)) ?? 0;
78
102
  };
103
+ /** argv without `--config <file>` / `--config=<file>`. */
104
+ export const withoutConfig = (argv) => argv.filter((a, i) => a !== '--config' && argv[i - 1] !== '--config' && !a.startsWith('--config='));
79
105
  /** Prints a problem the way a user can act on it: what happened, how to fix it; stack traces only for real bugs. */
80
106
  export const report = (e) => {
81
107
  if (e instanceof CommanderError)
82
108
  return ['commander.helpDisplayed', 'commander.help', 'commander.version'].includes(e.code) ? 0 : e.exitCode || 1;
83
109
  if (e instanceof UserError) {
84
- console.error(red('Error: ') + e.message);
110
+ // Messages can quote what the sandbox wrote (a session's status, an address): never with its control codes.
111
+ console.error(c.red('Error: ') + plainText(e.message));
85
112
  if (e.fix)
86
- console.error(' Fix: ' + e.fix);
113
+ console.error(' Fix: ' + plainText(e.fix));
87
114
  return 1;
88
115
  }
89
- console.error(red('Unexpected error: ') + errMsg(e));
90
- console.error(dim('This is a bug in windows-development-sandbox. Details: ' + (e instanceof Error && e.stack ? e.stack.split('\n').slice(1, 4).join(' | ').trim() : 'none')));
116
+ console.error(c.red('Unexpected error: ') + errMsg(e));
117
+ console.error(c.dim('This is a bug in windows-development-sandbox. Details: ' + (e instanceof Error && e.stack ? e.stack.split('\n').slice(1, 4).join(' | ').trim() : 'none')));
91
118
  console.error(' Please report it with the output of: sandbox doctor');
92
119
  return 1;
93
120
  };
@@ -17,6 +17,8 @@ export declare const OptionSpec: z.ZodObject<{
17
17
  accounts: "accounts";
18
18
  }>]>>;
19
19
  advanced: z.ZodOptional<z.ZodBoolean>;
20
+ when: z.ZodOptional<z.ZodString>;
21
+ noMenu: z.ZodOptional<z.ZodBoolean>;
20
22
  }, z.core.$strict>;
21
23
  export declare const ArgSpec: z.ZodObject<{
22
24
  name: z.ZodString;
@@ -28,7 +30,10 @@ export declare const ArgSpec: z.ZodObject<{
28
30
  sessions: "sessions";
29
31
  accounts: "accounts";
30
32
  }>]>>;
33
+ when: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
31
34
  }, z.core.$strict>;
35
+ /** Is an argument or option with this `when` asked, given the first argument's answer? */
36
+ export declare const askedWhen: (when: string | string[] | undefined, first: string) => boolean;
32
37
  export declare const Group: z.ZodEnum<{
33
38
  Agents: "Agents";
34
39
  Sandbox: "Sandbox";
@@ -60,6 +65,7 @@ export declare const CommandSpec: z.ZodObject<{
60
65
  sessions: "sessions";
61
66
  accounts: "accounts";
62
67
  }>]>>;
68
+ when: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
63
69
  }, z.core.$strict>>;
64
70
  options: z.ZodArray<z.ZodObject<{
65
71
  flag: z.ZodString;
@@ -79,6 +85,8 @@ export declare const CommandSpec: z.ZodObject<{
79
85
  accounts: "accounts";
80
86
  }>]>>;
81
87
  advanced: z.ZodOptional<z.ZodBoolean>;
88
+ when: z.ZodOptional<z.ZodString>;
89
+ noMenu: z.ZodOptional<z.ZodBoolean>;
82
90
  }, z.core.$strict>>;
83
91
  passthrough: z.ZodOptional<z.ZodEnum<{
84
92
  command: "command";
@@ -13,6 +13,10 @@ export const OptionSpec = z.strictObject({
13
13
  choices: Choices.optional(),
14
14
  /** Advanced options are only asked in the menu after "Show advanced options". */
15
15
  advanced: z.boolean().optional(),
16
+ /** Only asked in the menu when the command's first argument has this value (account save: --from-host). */
17
+ when: z.string().optional(),
18
+ /** Never offered in the menu (dangerous shortcuts such as --yes); typing it on the command line still works. */
19
+ noMenu: z.boolean().optional(),
16
20
  });
17
21
  export const ArgSpec = z.strictObject({
18
22
  name: z.string(),
@@ -20,7 +24,11 @@ export const ArgSpec = z.strictObject({
20
24
  required: z.boolean(),
21
25
  variadic: z.boolean().optional(),
22
26
  choices: Choices.optional(),
27
+ /** Only asked in the menu when the command's first argument has this value, or one of these (data clean: targets). */
28
+ when: z.union([z.string(), z.array(z.string())]).optional(),
23
29
  });
30
+ /** Is an argument or option with this `when` asked, given the first argument's answer? */
31
+ export const askedWhen = (when, first) => when === undefined || (Array.isArray(when) ? when.includes(first) : when === first);
24
32
  export const Group = z.enum(['Agents', 'Sandbox', 'Sessions', 'Accounts', 'Data', 'Health', 'Config']);
25
33
  export const CommandSpec = z.strictObject({
26
34
  name: z.string(),
@@ -38,49 +46,66 @@ export const CommandSpec = z.strictObject({
38
46
  const bool = (flag, description, extra = {}) => ({ flag, type: 'boolean', description, ...extra });
39
47
  const text = (flag, value, description, extra = {}) => ({ flag, type: 'string', value, description, ...extra });
40
48
  const SANDBOX_OPTIONS = [
41
- bool('--terminal', 'run in THIS terminal (reattaches to this folder\'s session if nobody is watching it)', { short: '-t' }),
49
+ bool('--terminal', 'attach this terminal (reuses this folder\'s session if nobody is watching it)', { short: '-t' }),
42
50
  bool('--headless', 'hide the sandbox window (sbx-show brings it back)'),
43
- bool('--new', 'always start a new session, even if this folder has one nobody is attached to'),
51
+ bool('--new', 'always start a new session, even if this folder has an unattached one'),
44
52
  text('--workspace', 'dir', 'use this folder instead of the current one'),
45
53
  text('--node', 'version', 'Node version for nvm (default: .sandbox-config.json > .nvmrc > this PC)', { advanced: true }),
46
54
  text('--python', 'version', 'Python 3 version (default: .sandbox-config.json > .python-version > config)', { advanced: true }),
47
- { flag: '--database', type: 'list', value: 'name', description: 'also start this database (postgres, mysql, mongodb, redis, dynamodb; repeatable)', advanced: true },
55
+ { flag: '--database', type: 'list', value: 'name', description: 'also start this database (postgres, mysql, mongodb, redis, dynamodb; repeatable)', choices: ['postgres', 'mysql', 'mongodb', 'redis', 'dynamodb'], advanced: true },
48
56
  { flag: '--script', type: 'list', value: 'file', description: 'run this .ps1/.mjs/.cmd inside the sandbox first (repeatable)', advanced: true },
49
57
  bool('--window', 'show the sandbox window even if config says headless', { advanced: true }),
50
58
  bool('--trust-config', 'approve the folder\'s .sandbox-config.json without asking', { advanced: true }),
51
59
  bool('--no-local-config', 'ignore the folder\'s .sandbox-config.json', { advanced: true }),
60
+ bool('--yes', 'when this folder is not in the running sandbox and adding it needs a restart, restart without asking', { noMenu: true }),
52
61
  ];
53
62
  const AGENT_OPTIONS = [
54
63
  text('--harness', 'name', 'agent to run', { choices: 'harnesses' }),
64
+ { flag: '--session', type: 'list', value: 'agent@folder', description: 'start several sessions at once, e.g. --session claude@C:\\code\\api --session "codex@C:\\code\\web|--model o3" (repeatable; after | = that agent\'s own arguments; -t attaches the first)', noMenu: true },
55
65
  text('--account', 'name', 'Claude account for this sandbox', { choices: 'accounts' }),
56
66
  ...SANDBOX_OPTIONS,
57
- bool('--no-agent', 'provision the sandbox without starting an agent', { advanced: true }),
67
+ bool('--no-agent', 'set up the sandbox but do not start an agent', { advanced: true }),
58
68
  ];
59
69
  const json = true;
60
70
  export const COMMANDS = z.array(CommandSpec).parse([
61
71
  { name: 'launch', summary: 'start the sandbox and an agent (or another session in the running sandbox)', group: 'Agents', args: [], options: AGENT_OPTIONS, passthrough: 'agent', interactive: true },
62
- { name: 'start', summary: 'start the sandbox WITHOUT an agent: just the machine, toolchains and databases', group: 'Sandbox', args: [], options: SANDBOX_OPTIONS.filter((o) => o.flag !== '--new'), interactive: true },
63
- { name: 'generate', summary: 'dry run: write the .wsb and launch folder and show what would happen, start nothing', group: 'Health', args: [], options: AGENT_OPTIONS, passthrough: 'agent' },
72
+ { name: 'start', summary: 'start a sandbox with no agent (tools and databases only)', group: 'Sandbox', args: [], options: SANDBOX_OPTIONS.filter((o) => o.flag !== '--new'), interactive: true },
73
+ { name: 'generate', summary: 'show what a start would do, without starting anything', group: 'Health', args: [], options: AGENT_OPTIONS, passthrough: 'agent' },
64
74
  { name: 'attach', summary: 'show a running session in this terminal (Ctrl+] detaches)', group: 'Sessions', args: [{ name: 'id', description: 'session (default: the newest)', required: false, choices: 'sessions' }], options: [], interactive: true },
65
75
  { name: 'sessions', summary: 'list agent sessions and who is attached', group: 'Sessions', args: [], options: [], json },
66
- { name: 'status', summary: 'sandbox overview', group: 'Sandbox', args: [], options: [], json },
76
+ { name: 'status', summary: 'show what the sandbox is doing', group: 'Sandbox', args: [], options: [], json },
67
77
  { name: 'db', summary: 'list, start or stop databases in the running sandbox', group: 'Sandbox', args: [{ name: 'action', description: 'list, start or stop', required: false, choices: ['list', 'start', 'stop'] }, { name: 'name', description: 'postgres, mysql, mongodb, redis or dynamodb', required: false, choices: ['postgres', 'mysql', 'mongodb', 'redis', 'dynamodb'] }], options: [] },
68
78
  { name: 'exec', summary: 'run a command inside the sandbox and print its output', group: 'Sandbox', args: [{ name: 'command', description: 'command and its arguments', required: true, variadic: true }], options: [{ flag: '--timeout', type: 'number', value: 'seconds', description: 'give up after this many seconds', default: 600 }, text('--workspace', 'dir', 'folder (inside the mapped workspace) to run in')], passthrough: 'command', json },
69
- { name: 'restart', summary: 'restart running agents in place; they resume their conversation', group: 'Sessions', args: [], options: [text('--harness', 'name', 'only this agent', { choices: 'harnesses' })] },
70
- { name: 'stop', summary: 'shut the sandbox down', group: 'Sandbox', args: [], options: [] },
79
+ { name: 'restart', summary: 'restart this project\'s running agents in place; they resume their conversation', group: 'Sessions', args: [], options: [text('--harness', 'name', 'only this agent', { choices: 'harnesses' }), bool('--all', 'every project\'s sessions, not only this folder\'s')] },
80
+ { name: 'stop', summary: 'shut the sandbox down (every project\'s sessions end)', group: 'Sandbox', args: [], options: [bool('--yes', 'do not ask when other projects\' sessions would end', { noMenu: true })] },
81
+ {
82
+ name: 'projects', summary: 'the projects a sandbox can open without a restart: list, choose, roots, all/exclude', group: 'Sandbox', json,
83
+ args: [
84
+ { name: 'action', description: 'list, choose, add, remove, include, exclude, all, roots', required: false, choices: ['list', 'choose', 'add', 'remove', 'include', 'exclude', 'all', 'roots'] },
85
+ { name: 'value', description: 'a folder (add, remove, include, exclude), on/off (all), or list/add/remove (roots)', required: false, when: ['add', 'remove', 'include', 'exclude', 'all', 'roots'] },
86
+ { name: 'folder', description: '(roots add/remove) the folder', required: false, when: 'roots' },
87
+ ],
88
+ options: [],
89
+ },
71
90
  { name: 'show', summary: 'show the sandbox window', group: 'Sandbox', args: [], options: [] },
72
91
  { name: 'hide', summary: 'hide the sandbox window (it keeps running)', group: 'Sandbox', args: [], options: [] },
73
- { name: 'account', summary: 'Claude accounts: list, save, use, set the default, remove', group: 'Accounts', args: [{ name: 'action', description: 'what to do', required: false, choices: ['list', 'save', 'use', 'default', 'remove'] }, { name: 'name', description: 'account name', required: false, choices: 'accounts' }], options: [bool('--from-host', '(save) save this PC\'s login instead of the sandbox\'s'), bool('--no-restart', '(use) do not restart running sessions')], json },
74
- { name: 'data', summary: 'what persists between sandboxes (tools, databases, logins, logs), and cleaning it', group: 'Data', args: [{ name: 'action', description: 'show or clean', required: false, choices: ['show', 'clean'] }, { name: 'targets', description: 'what to clean, e.g. tools/go databases logs', required: false, variadic: true }], options: [bool('--yes', 'do not ask before deleting')], json },
92
+ { name: 'account', summary: 'Claude logins: list, save, use, set the default, remove', group: 'Accounts', args: [{ name: 'action', description: 'what to do', required: false, choices: ['list', 'save', 'use', 'default', 'remove'] }, { name: 'name', description: 'account name', required: false, choices: 'accounts' }], options: [bool('--from-host', '(save) save this PC\'s login instead of the sandbox\'s', { when: 'save' }), bool('--no-restart', '(use) do not restart running sessions', { when: 'use' })], json },
93
+ { name: 'data', summary: 'show what is kept between sandboxes (tools, databases, logins, logs), or delete some of it', group: 'Data', args: [{ name: 'action', description: 'show or clean', required: false, choices: ['show', 'clean'] }, { name: 'targets', description: 'what to clean, e.g. tools/go databases logs', required: false, variadic: true, when: 'clean' }], options: [bool('--yes', 'do not ask before deleting', { noMenu: true })], json },
75
94
  { name: 'clean', summary: 'delete old launch folders, logs and leftovers', group: 'Data', args: [], options: [{ flag: '--days', type: 'number', value: 'n', description: 'launch folders older than this many days', default: 7 }] },
76
- { name: 'sync', summary: 'copy history (and claude-mem, once closed) back to this PC now', group: 'Data', args: [], options: [] },
95
+ { name: 'sync', summary: 'copy conversation history back to this PC now', group: 'Data', args: [], options: [] },
96
+ {
97
+ name: 'audit', summary: 'what happened in your sandboxes: programs started, files changed, sessions, repo guard (audit.enabled)', group: 'Health', json,
98
+ args: [],
99
+ options: [{ flag: '--days', type: 'number', value: 'n', description: 'the last n days (default: today)', default: 1 }, text('--kind', 'kind', 'only proc, file, session or guard events', { choices: ['proc', 'file', 'session', 'guard'] }), text('--grep', 'text', 'only events containing this text')],
100
+ },
77
101
  { name: 'logs', summary: 'print the newest sandbox log', group: 'Health', args: [], options: [bool('--follow', 'keep printing new lines', { short: '-f' })] },
78
102
  { name: 'setup', summary: 'check this PC can run Windows Sandbox and offer to turn it on', group: 'Health', args: [], options: [] },
79
103
  { name: 'doctor', summary: 'check this PC and show what will be copied or downloaded', group: 'Health', args: [], options: [text('--harness', 'name', 'agent to check', { choices: 'harnesses' })] },
80
104
  { name: 'repair', summary: 'find and fix common problems (stale state, broken downloads, half-installed tools)', group: 'Health', args: [], options: [] },
81
- { name: 'self-test', summary: 'run the built-in tests', group: 'Health', args: [], options: [] },
82
- { name: 'config', summary: 'show the effective config, its path, create it, editor schemas, or check a project\'s .sandbox-config.json', group: 'Config', args: [{ name: 'action', description: 'what to do', required: false, choices: ['show', 'path', 'init', 'schema', 'check'] }], options: [text('--workspace', 'dir', '(check) the project folder; default: the current one')] },
83
- { name: 'host-claude', summary: 'Claude on this PC (NOT sandboxed) with a separate profile', group: 'Agents', args: [], options: [bool('--personal', 'use your normal Claude profile')], passthrough: 'agent', interactive: true },
105
+ { name: 'self-test', summary: 'run the built-in tests', group: 'Health', args: [{ name: 'suite', description: 'run only these test suites, e.g. config live-daemon (default: all)', required: false, variadic: true }], options: [] },
106
+ { name: 'config', summary: 'show or create your settings file, or check a project\'s .sandbox-config.json', group: 'Config', args: [{ name: 'action', description: 'what to do', required: false, choices: ['show', 'path', 'init', 'schema', 'check'] }], options: [text('--workspace', 'dir', '(check) the project folder; default: the current one')] },
107
+ { name: 'agent-commands', summary: '(Beta) make claude, codex ... on this PC start in a hidden sandbox and attach', group: 'Agents', args: [{ name: 'action', description: 'on, off or status', required: false, choices: ['status', 'on', 'off'] }], options: [] },
108
+ { name: 'host-claude', summary: 'run Claude on this PC, NOT sandboxed, with its own profile', group: 'Agents', args: [], options: [bool('--personal', 'use your normal Claude profile')], passthrough: 'agent', interactive: true },
84
109
  { name: 'menu', summary: 'the interactive menu (what `sandbox` opens with no arguments)', group: 'Config', args: [], options: [], hidden: true, interactive: true },
85
110
  { name: '_watch', summary: 'background history sync', group: 'Data', args: [], options: [], hidden: true },
86
111
  ]);
@@ -0,0 +1,21 @@
1
+ import { App } from '../tui/index.js';
2
+ import type { ConfigFile } from './settings/config-file.js';
3
+ export declare class AppsCatalogue {
4
+ private readonly app;
5
+ private readonly file;
6
+ private readonly dataPath;
7
+ constructor(app: App, file: ConfigFile, dataPath: string);
8
+ /** Downloaded already (into this data folder), so the next sandbox starts it without a download. */
9
+ private installed;
10
+ /** Is this entry on now (its setting, or listed in apps.install)? */
11
+ private chosen;
12
+ private item;
13
+ /** Shows the catalogue until you save (Enter) or leave it (Esc); returns the notice to show afterwards. */
14
+ run(): Promise<string | undefined>;
15
+ private save;
16
+ /** Your own programs: the apps.custom map, each entry a form (GitHub release or link, program, command line or not). */
17
+ private editCustom;
18
+ }
19
+ /** For the home screen: "12 apps chosen". */
20
+ export declare const appsSummary: (file: ConfigFile) => string;
21
+ export declare const appLabel: (id: string) => string;
@@ -0,0 +1,122 @@
1
+ // The apps catalogue in the menu: every app by category, searchable, ticked = installed in every sandbox. VS Code and
2
+ // Windows Terminal are listed too (they have settings of their own, which ticking changes). Your own programs
3
+ // (apps.custom) are edited as a list of small forms. Saved to your config file (or, for catalogue apps only, the
4
+ // project's .sandbox-config.json).
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import _ from 'lodash';
8
+ import { APP_CATEGORIES, APPS } from '../../shared/apps.js';
9
+ import { Text, Theme } from '../tui/index.js';
10
+ import { ValueEditor } from './settings/editors.js';
11
+ const CUSTOM = '@custom';
12
+ export class AppsCatalogue {
13
+ app;
14
+ file;
15
+ dataPath;
16
+ constructor(app, file, dataPath) {
17
+ this.app = app;
18
+ this.file = file;
19
+ this.dataPath = dataPath;
20
+ }
21
+ /** Downloaded already (into this data folder), so the next sandbox starts it without a download. */
22
+ installed(entry) {
23
+ if (entry.id === 'vscode')
24
+ return fs.existsSync(path.join(this.dataPath, 'tools', 'vscode', 'Code.exe'));
25
+ if (entry.id === 'windows-terminal')
26
+ return fs.existsSync(path.join(this.dataPath, 'tools', 'terminal', 'WindowsTerminal.exe'));
27
+ return fs.existsSync(path.join(this.dataPath, 'tools', 'apps', entry.id, '.agent-sandbox-app'));
28
+ }
29
+ /** Is this entry on now (its setting, or listed in apps.install)? */
30
+ chosen(entry, install) {
31
+ return entry.setting ? _.get(this.file.userConfig(), entry.setting) === true : install.includes(entry.id);
32
+ }
33
+ item(entry, install) {
34
+ const notes = [
35
+ Theme.muted('Download ') + '~' + entry.size + ' once, kept in C:\\AgentData\\tools\\' + (entry.setting ? '' : 'apps\\') + entry.id,
36
+ Theme.muted('Starts ') + (entry.cli ? 'from a terminal (on PATH)' : 'from the desktop or Start > Sandbox apps'),
37
+ ...(entry.needs?.includes('mesa') ? [Theme.muted('Graphics ') + 'OpenGL on the CPU (Mesa) is added for it'] : []),
38
+ ...(entry.fullGitOnly ? [Theme.warn('Needs provisioning.git.access "full" (it pushes with its own git).')] : []),
39
+ ...(entry.setting ? [Theme.muted('Setting ') + entry.setting] : []),
40
+ ];
41
+ return {
42
+ value: entry.id, label: entry.name, category: entry.category, description: entry.about, details: notes, keywords: entry.id,
43
+ hint: (this.installed(entry) ? Theme.ok('installed ') : '') + Theme.muted(entry.size), checked: this.chosen(entry, install),
44
+ ...(this.file.isProject && entry.setting ? { disabled: 'A project cannot change this; it is in your own settings.' } : {}),
45
+ };
46
+ }
47
+ /** Shows the catalogue until you save (Enter) or leave it (Esc); returns the notice to show afterwards. */
48
+ async run() {
49
+ for (;;) {
50
+ const install = (this.file.isProject ? this.file.valueInFile(['apps', 'install']) : this.file.userConfig().apps.install) ?? [];
51
+ const custom = this.file.isProject ? {} : this.file.userConfig().apps.custom;
52
+ const entries = APPS.filter((a) => !(this.file.isProject && a.setting));
53
+ const picked = await this.app.list({
54
+ path: [this.file.isProject ? 'Project settings' : 'Apps'],
55
+ intro: 'Tick the apps every sandbox should have (space), then Enter to save. Each is downloaded once and kept; c = your own programs.',
56
+ multi: true,
57
+ categories: [...APP_CATEGORIES, ...(this.file.isProject ? [] : [{ id: 'custom', label: 'Your own programs', description: 'Programs you added yourself (c to add or change them).' }])],
58
+ items: [
59
+ ...entries.map((a) => this.item(a, install)),
60
+ ...Object.entries(custom).map(([id, a]) => ({ value: CUSTOM + id, label: a.name, category: 'custom', description: a.github ? 'From the latest ' + a.github + ' release.' : 'From ' + a.url, hint: Theme.muted(a.cli ? 'command line' : 'desktop'), checked: true, disabled: 'Your own programs are always installed; c to change or remove them.' })),
61
+ ],
62
+ actions: this.file.isProject ? [] : [{ key: 'c', label: 'your own programs' }],
63
+ });
64
+ if (picked.kind === 'back')
65
+ return undefined;
66
+ if (picked.kind === 'action') {
67
+ const notice = await this.editCustom();
68
+ if (notice)
69
+ return notice;
70
+ continue;
71
+ }
72
+ if (picked.kind !== 'multi')
73
+ continue;
74
+ return this.save(picked.values.filter((v) => !v.startsWith(CUSTOM)));
75
+ }
76
+ }
77
+ save(ids) {
78
+ const problems = [];
79
+ let changed = 0;
80
+ for (const entry of APPS.filter((a) => a.setting && !this.file.isProject)) {
81
+ const on = ids.includes(entry.id);
82
+ if (on === this.chosen(entry, []))
83
+ continue;
84
+ const result = this.file.set(entry.setting.split('.'), on);
85
+ if (typeof result === 'object')
86
+ problems.push(...result.problems);
87
+ else if (result === 'saved')
88
+ changed++;
89
+ }
90
+ const install = APPS.filter((a) => !a.setting && ids.includes(a.id)).map((a) => a.id);
91
+ const result = this.file.set(['apps', 'install'], install.length || this.file.isProject ? install : undefined);
92
+ if (typeof result === 'object')
93
+ problems.push(...result.problems);
94
+ else if (result === 'saved')
95
+ changed++;
96
+ if (problems.length)
97
+ return Theme.error(Theme.glyph.cross + ' Not saved: ' + problems.join('; '));
98
+ if (!changed)
99
+ return Theme.muted('Apps unchanged.');
100
+ return Theme.ok(Theme.glyph.ok + ' Saved: ' + (install.length ? install.length + ' app' + (install.length === 1 ? '' : 's') : 'no apps') + '.') + Theme.muted(this.file.isProject ? ' The next launch asks you to approve the project file again.' : ' The next sandbox installs what it does not have yet.');
101
+ }
102
+ /** Your own programs: the apps.custom map, each entry a form (GitHub release or link, program, command line or not). */
103
+ async editCustom() {
104
+ const node = this.file.schema.child('apps')?.child('custom');
105
+ if (!node)
106
+ return undefined;
107
+ const edited = await new ValueEditor(this.app).edit(['Apps', 'Your own programs'], ['apps', 'custom'], node, this.file.userConfig().apps.custom);
108
+ if (!edited)
109
+ return undefined;
110
+ const result = this.file.set(['apps', 'custom'], edited.value);
111
+ if (typeof result === 'object')
112
+ return Theme.error(Theme.glyph.cross + ' Not saved: ' + result.problems.join('; '));
113
+ return result === 'saved' ? Theme.ok(Theme.glyph.ok + ' Saved your programs (' + Object.keys(edited.value).length + ').') : undefined;
114
+ }
115
+ }
116
+ /** For the home screen: "12 apps chosen". */
117
+ export const appsSummary = (file) => {
118
+ const config = file.userConfig();
119
+ const n = config.apps.install.length + Object.keys(config.apps.custom).length;
120
+ return n ? n + ' chosen' : 'none chosen';
121
+ };
122
+ export const appLabel = (id) => APPS.find((a) => a.id === id)?.name ?? Text.humanize(id);
@@ -0,0 +1,28 @@
1
+ import type { Context } from '../commands/context.js';
2
+ import { type CommandSpecT, type OptionSpecT } from '../manifest.js';
3
+ import { App } from '../tui/index.js';
4
+ /** What an option accepts, checked while typing (undefined = fine). */
5
+ export declare const optionProblem: (o: OptionSpecT, v: string) => string | undefined;
6
+ export declare class CommandForm {
7
+ private readonly app;
8
+ private readonly ctx;
9
+ private readonly spec;
10
+ /** Answers by field id (arg:<name>, opt:<flag>, json, extra). */
11
+ private readonly values;
12
+ constructor(app: App, ctx: Context, spec: CommandSpecT, preset?: string[]);
13
+ /** Values a choices list names, filled in from config and the running sandbox. */
14
+ private choices;
15
+ private first;
16
+ private fields;
17
+ private id;
18
+ /** The command line the form stands for now. */
19
+ argv(): string[];
20
+ /** The name of a required argument that has no value yet. */
21
+ private missing;
22
+ private item;
23
+ /** Shows the form until you run the command (its argv) or go back (null). */
24
+ run(): Promise<string[] | null>;
25
+ private edit;
26
+ /** `data clean` targets, ticked from a list with their sizes (never "all": that is typed on purpose). */
27
+ private dataTargets;
28
+ }