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
@@ -1,152 +0,0 @@
1
- // `sandbox setup`: checks this PC can run Windows Sandbox and offers to turn the feature on.
2
- import fs from 'node:fs';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import { spawnSync } from 'node:child_process';
6
- import _ from 'lodash';
7
- import { z } from 'zod';
8
- import { askYesNo } from '../../core/workspace.js';
9
- import { fail, fileExists, isWin } from '../../core/util.js';
10
- import { SYSTEM, system32 } from '../../shared/system.js';
11
- import { table } from './context.js';
12
- const FEATURE = 'Containers-DisposableClientVM';
13
- export const ENABLE_COMMAND = 'Enable-WindowsOptionalFeature -Online -FeatureName ' + FEATURE + ' -All -NoRestart';
14
- const GB = 1024 ** 3;
15
- /** `reg query <key>` output to { valueName: data }. */
16
- export const parseRegQuery = (text) => Object.fromEntries(_.compact(text.split(/\r?\n/).map((line) => /^\s+(\S+)\s+REG_\w+\s+(.*)$/.exec(line)?.slice(1, 3))));
17
- /** Windows Sandbox needs Pro, Enterprise or Education (build 18305+); Home ("Core*") and Server cannot run it. */
18
- export const editionSupport = (edition) => {
19
- if (!edition)
20
- return 'unknown';
21
- if (/^Core/i.test(edition))
22
- return 'home';
23
- if (/^Server/i.test(edition))
24
- return 'unsupported';
25
- return /^(Professional|Enterprise|Education|IoTEnterprise)/i.test(edition) ? 'supported' : 'unknown';
26
- };
27
- /** The "State : ..." line of `Dism /Get-FeatureInfo` (English output only; anything else is unknown). */
28
- export const parseDismState = (text) => {
29
- const state = /^State\s*:\s*(.+?)\s*$/im.exec(text)?.[1].toLowerCase() ?? '';
30
- if (state === 'enabled')
31
- return 'enabled';
32
- if (state.includes('pending'))
33
- return 'pending';
34
- return state.startsWith('disabled') ? 'disabled' : 'unknown';
35
- };
36
- /** Win32_OptionalFeature.InstallState: 1 enabled, 2 disabled, 3 absent. */
37
- export const featureFromInstallState = (state) => state === 1 ? 'enabled' : state === 2 || state === 3 ? 'disabled' : 'unknown';
38
- export const nodeMajor = (version) => Number(/^v?(\d+)/.exec(version.trim())?.[1] ?? 0);
39
- /** Offer to enable the feature only when it is off and this edition can have it. */
40
- export const canEnableFeature = (f) => f.feature !== 'enabled' && f.feature !== 'pending' && !f.sandboxExe && ['supported', 'unknown'].includes(editionSupport(f.edition));
41
- const formatGb = (bytes) => (bytes / GB).toFixed(1) + ' GB';
42
- /** Turns what we learnt about the PC into the checklist (pure, so it can be tested). */
43
- export const buildChecks = (f) => {
44
- const support = editionSupport(f.edition);
45
- const edition = (f.edition ?? 'unknown edition') + (f.build ? ', build ' + f.build : '');
46
- const virt = f.hypervisorPresent || f.virtualizationFirmware;
47
- const featureReady = f.feature === 'enabled' && f.sandboxExe;
48
- return [
49
- support === 'supported' && (f.build ?? 18305) >= 18305 ? { name: 'Windows edition', status: 'ok', detail: edition }
50
- : support === 'home' ? { name: 'Windows edition', status: 'action', detail: edition + ': Windows Home cannot run Windows Sandbox. Upgrade to Pro (Settings > System > Activation).' }
51
- : support === 'unsupported' ? { name: 'Windows edition', status: 'action', detail: edition + ': Windows Sandbox needs Windows 10/11 Pro, Enterprise or Education.' }
52
- : support === 'supported' ? { name: 'Windows edition', status: 'action', detail: edition + ': too old; Windows Sandbox needs Windows 10 version 1903 or newer.' }
53
- : { name: 'Windows edition', status: 'warn', detail: edition + ': not sure this edition has Windows Sandbox (it needs Pro, Enterprise or Education).' },
54
- virt ? { name: 'CPU virtualization', status: 'ok', detail: f.hypervisorPresent ? 'on (hypervisor running)' : 'on' }
55
- : virt === false ? { name: 'CPU virtualization', status: 'action', detail: 'off. Turn on Intel VT-x / AMD-V (SVM) in your PC\'s BIOS/UEFI settings, then run this again.' }
56
- : { name: 'CPU virtualization', status: 'warn', detail: 'could not tell; if Windows Sandbox will not start, turn on VT-x / AMD-V in the BIOS/UEFI.' },
57
- featureReady ? { name: 'Windows Sandbox feature', status: 'ok', detail: 'enabled' }
58
- : f.feature === 'pending' || (f.feature === 'enabled' && !f.sandboxExe) ? { name: 'Windows Sandbox feature', status: 'action', detail: 'enabled, but Windows needs a restart to finish.' }
59
- : f.sandboxExe ? { name: 'Windows Sandbox feature', status: 'ok', detail: 'installed' }
60
- : { name: 'Windows Sandbox feature', status: 'action', detail: 'not enabled. In an admin PowerShell: ' + ENABLE_COMMAND + ', then restart.' },
61
- { name: 'Memory', status: f.totalRamBytes < 4 * GB ? 'action' : f.totalRamBytes < 8 * GB ? 'warn' : 'ok', detail: formatGb(f.totalRamBytes) + (f.totalRamBytes < 4 * GB ? ': Windows Sandbox needs at least 4 GB.' : f.totalRamBytes < 8 * GB ? ': works, but 8 GB or more is recommended.' : '') },
62
- f.freeDiskBytes === null ? { name: 'Free disk', status: 'warn', detail: 'could not check the drive of ' + f.storageRoot }
63
- : { name: 'Free disk', status: f.freeDiskBytes < 5 * GB ? 'action' : f.freeDiskBytes < 20 * GB ? 'warn' : 'ok', detail: formatGb(f.freeDiskBytes) + ' free for ' + f.storageRoot + (f.freeDiskBytes < 5 * GB ? ': free up space (toolchains need several GB).' : f.freeDiskBytes < 20 * GB ? ': getting low; toolchains and caches can use several GB.' : '') },
64
- nodeMajor(f.nodeVersion) >= 20 ? { name: 'Node.js', status: 'ok', detail: f.nodeVersion } : { name: 'Node.js', status: 'action', detail: f.nodeVersion + ': needs 20 or newer (winget install OpenJS.NodeJS.LTS).' },
65
- ];
66
- };
67
- // Fixed script (no outside data): CPU/hypervisor facts and the feature state, readable without admin rights.
68
- const PROBE = [
69
- '$ErrorActionPreference = "SilentlyContinue"',
70
- '$cpu = @(Get-CimInstance Win32_Processor)',
71
- '$cs = Get-CimInstance Win32_ComputerSystem',
72
- '$f = Get-CimInstance Win32_OptionalFeature -Filter "Name=\'' + FEATURE + '\'"',
73
- '$v = $null; if ($cpu.Count) { $v = [bool]($cpu | Where-Object { $_.VirtualizationFirmwareEnabled }) }',
74
- '$h = $null; if ($cs) { $h = [bool]$cs.HypervisorPresent }',
75
- '$s = $null; if ($f) { $s = [int]$f.InstallState }',
76
- '@{ virtualizationFirmware = $v; hypervisorPresent = $h; installState = $s } | ConvertTo-Json -Compress',
77
- ].join('; ');
78
- const Probe = z.object({ virtualizationFirmware: z.boolean().nullable(), hypervisorPresent: z.boolean().nullable(), installState: z.number().nullable() }).partial();
79
- export const parseProbe = (text) => {
80
- try {
81
- return Probe.parse(JSON.parse(_.last(text.trim().split(/\r?\n/)) ?? ''));
82
- }
83
- catch {
84
- return {};
85
- }
86
- };
87
- const run = (exe, args) => {
88
- const r = spawnSync(exe, args, { encoding: 'utf8', windowsHide: true, timeout: 60000 });
89
- return r.status === 0 ? r.stdout : '';
90
- };
91
- const freeDisk = (dir) => {
92
- let at = path.resolve(dir);
93
- while (!fs.existsSync(at) && path.dirname(at) !== at)
94
- at = path.dirname(at);
95
- try {
96
- const s = fs.statfsSync(at);
97
- return s.bavail * s.bsize;
98
- }
99
- catch {
100
- return null;
101
- }
102
- };
103
- const gatherFacts = (storageRoot) => {
104
- const reg = parseRegQuery(run(SYSTEM.reg(), ['query', 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion']));
105
- const probe = parseProbe(run(SYSTEM.powershell(), ['-NoProfile', '-NonInteractive', '-Command', PROBE]));
106
- // DISM knows about a pending restart but needs admin rights; the CIM state works for everyone.
107
- const dism = parseDismState(run(SYSTEM.dism(), ['/English', '/Online', '/Get-FeatureInfo', '/FeatureName:' + FEATURE]));
108
- return {
109
- edition: reg.EditionID ?? null,
110
- build: Number(reg.CurrentBuildNumber ?? reg.CurrentBuild) || null,
111
- feature: dism === 'unknown' ? featureFromInstallState(probe.installState) : dism,
112
- sandboxExe: fileExists(system32('WindowsSandbox.exe')),
113
- virtualizationFirmware: probe.virtualizationFirmware ?? null,
114
- hypervisorPresent: probe.hypervisorPresent ?? null,
115
- totalRamBytes: os.totalmem(),
116
- freeDiskBytes: freeDisk(storageRoot),
117
- storageRoot,
118
- nodeVersion: process.version,
119
- };
120
- };
121
- const LABEL = { ok: '\x1b[32mOK\x1b[0m', warn: '\x1b[33mWARNING\x1b[0m', action: '\x1b[31mACTION NEEDED\x1b[0m' };
122
- const print = (checks) => table(checks.map((c) => [LABEL[c.status], c.name, c.detail]));
123
- /** Runs the enable command in an elevated PowerShell (Windows asks for permission); true if it worked. */
124
- const enableFeature = () => {
125
- const inner = "'-NoProfile','-Command','" + ENABLE_COMMAND + " | Out-Null'";
126
- const outer = "try { $p = Start-Process -FilePath '" + SYSTEM.powershell() + "' -Verb RunAs -Wait -PassThru -WindowStyle Hidden -ArgumentList " + inner + '; exit $p.ExitCode } catch { exit 1 }';
127
- return spawnSync(SYSTEM.powershell(), ['-NoProfile', '-NonInteractive', '-Command', outer], { stdio: 'inherit' }).status === 0;
128
- };
129
- export const setup = async (ctx) => {
130
- if (!isWin)
131
- fail('Windows Sandbox only runs on Windows.', 'Run sandbox setup on a Windows 10/11 Pro, Enterprise or Education PC.');
132
- console.log('Checking this PC for Windows Sandbox...\n');
133
- const facts = gatherFacts(ctx.storageRoot);
134
- let checks = buildChecks(facts);
135
- print(checks);
136
- if (canEnableFeature(facts)) {
137
- console.log('');
138
- if (await askYesNo('Turn on Windows Sandbox now? Windows will ask for admin permission.')) {
139
- console.log('Enabling Windows Sandbox (this can take a minute)...');
140
- if (!enableFeature())
141
- fail('Windows Sandbox could not be enabled (or the admin prompt was declined).', 'In an admin PowerShell run: ' + ENABLE_COMMAND + ', then restart Windows.');
142
- console.log('Done. Restart Windows to finish, then run sandbox setup again.\n');
143
- checks = buildChecks(gatherFacts(ctx.storageRoot));
144
- print(checks);
145
- }
146
- else
147
- console.log('To do it yourself, in an admin PowerShell run:\n ' + ENABLE_COMMAND + '\nthen restart Windows.');
148
- }
149
- const todo = checks.filter((c) => c.status === 'action').length;
150
- 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).'));
151
- return todo ? 1 : 0;
152
- };
@@ -1,22 +0,0 @@
1
- import { z } from 'zod';
2
- import type { Context } from './commands/context.js';
3
- import { type CommandSpecT } from './manifest.js';
4
- declare const History: z.ZodArray<z.ZodObject<{
5
- argv: z.ZodArray<z.ZodString>;
6
- at: z.ZodString;
7
- }, z.core.$strict>>;
8
- export declare const readHistory: () => z.output<typeof History>;
9
- /** Remembers a command (menu or typed) so the menu can offer it again. Never fails the command itself. */
10
- export declare const recordHistory: (argv: string[]) => void;
11
- /** argv as you would type it (quoted where needed). */
12
- export declare const commandLine: (argv: string[]) => string;
13
- /** Splits "a b 'c d'" like a shell would (enough for agent arguments and exec commands). */
14
- export declare const splitArgs: (text: string) => string[];
15
- /** Asks everything a command takes and returns its argv (or null when the user backs out). */
16
- export declare const askCommand: (ctx: Context, spec: CommandSpecT, preset?: string[]) => Promise<string[] | null>;
17
- /**
18
- * Runs the menu until Exit, or until a command that takes over the terminal (launch, attach) has run. `run` executes
19
- * an argv exactly like typing it.
20
- */
21
- export declare const menu: (ctx: Context, run: (argv: string[]) => Promise<number>) => Promise<number>;
22
- export {};
package/dist/cli/menu.js DELETED
@@ -1,187 +0,0 @@
1
- // The interactive menu (`sandbox` with no arguments). Built from manifest.ts: pick a command, answer one question per
2
- // argument and option (on/off options as checkboxes), see the exact command line, run it. The command line it shows is
3
- // what you would type yourself, and the last one is offered again at the top next time.
4
- import fs from 'node:fs';
5
- import path from 'node:path';
6
- import { checkbox, confirm, input, select, Separator } from '@inquirer/prompts';
7
- import { z } from 'zod';
8
- import { userDir } from '../core/config.js';
9
- import { tryReadJson, writeJson } from '../core/util.js';
10
- import { listAccounts } from '../host/accounts.js';
11
- import { runningSessions } from '../host/channel.js';
12
- import { isSandboxRunning } from '../host/platform.js';
13
- import { settingsMenu } from './settings.js';
14
- import { COMMANDS, Group } from './manifest.js';
15
- const c = { bold: (s) => '\x1b[1m' + s + '\x1b[0m', dim: (s) => '\x1b[2m' + s + '\x1b[0m', cyan: (s) => '\x1b[36m' + s + '\x1b[0m', green: (s) => '\x1b[32m' + s + '\x1b[0m', yellow: (s) => '\x1b[33m' + s + '\x1b[0m' };
16
- // ---- history ---------------------------------------------------------------------------------------------------------
17
- const History = z.array(z.strictObject({ argv: z.array(z.string()).max(200), at: z.string() })).max(20);
18
- const historyFile = () => path.join(userDir(), 'history.json');
19
- const HIDDEN = new Set(['menu', '_watch', 'self-test']);
20
- export const readHistory = () => History.safeParse(tryReadJson(historyFile())).data ?? [];
21
- /** Remembers a command (menu or typed) so the menu can offer it again. Never fails the command itself. */
22
- export const recordHistory = (argv) => {
23
- if (!argv.length || HIDDEN.has(argv[0]) || argv.includes('--help'))
24
- return;
25
- try {
26
- writeJson(historyFile(), [{ argv, at: new Date().toISOString() }, ...readHistory().filter((h) => h.argv.join('\0') !== argv.join('\0'))].slice(0, 10));
27
- }
28
- catch { /* history is a convenience */ }
29
- };
30
- /** argv as you would type it (quoted where needed). */
31
- export const commandLine = (argv) => ['sandbox', ...argv].map((a) => (/[\s"]/.test(a) || a === '' ? '"' + a.replace(/"/g, '\\"') + '"' : a)).join(' ');
32
- // ---- asking for one command ------------------------------------------------------------------------------------------
33
- /** Values for a choices list the manifest names (filled in from config and the running sandbox). */
34
- const choicesFor = (ctx, choices) => {
35
- if (Array.isArray(choices))
36
- return choices;
37
- if (choices === 'harnesses')
38
- return Object.keys(ctx.loaded.config.harnesses);
39
- if (choices === 'accounts')
40
- return listAccounts(ctx.storageRoot, ctx.dataPath).map((a) => a.name);
41
- if (choices === 'sessions')
42
- return isSandboxRunning() ? runningSessions(ctx.dataPath).map((s) => s.id) : [];
43
- return [];
44
- };
45
- /** Splits "a b 'c d'" like a shell would (enough for agent arguments and exec commands). */
46
- export const splitArgs = (text) => [...text.matchAll(/"([^"]*)"|'([^']*)'|(\S+)/g)].map((m) => m[1] ?? m[2] ?? m[3]);
47
- const askArg = async (ctx, arg) => {
48
- const list = choicesFor(ctx, arg.choices);
49
- if (list.length && !arg.variadic) {
50
- const picked = await select({ message: arg.description, choices: [...list.map((v) => ({ name: v, value: v })), ...(arg.required ? [] : [{ name: c.dim('(skip)'), value: '' }])] });
51
- return picked ? [picked] : [];
52
- }
53
- const answer = await input({ message: arg.description + (arg.required ? '' : c.dim(' (optional)')), validate: (v) => (!arg.required || v.trim() !== '' ? true : 'required') });
54
- return arg.variadic ? splitArgs(answer) : answer.trim() ? [answer.trim()] : [];
55
- };
56
- const askOptions = async (ctx, options) => {
57
- const argv = [];
58
- const flags = options.filter((o) => o.type === 'boolean');
59
- if (flags.length)
60
- argv.push(...await checkbox({ message: 'Options ' + c.dim('(space to toggle, enter to continue)'), choices: flags.map((o) => ({ name: o.flag + c.dim(' ' + o.description), value: o.flag })) }));
61
- for (const o of options.filter((x) => x.type !== 'boolean')) {
62
- const list = choicesFor(ctx, o.choices);
63
- const label = o.flag + c.dim(' ' + o.description);
64
- const value = list.length
65
- ? await select({ message: label, choices: [{ name: c.dim('(default)'), value: '' }, ...list.map((v) => ({ name: v, value: v }))] })
66
- : await input({ message: label + c.dim(o.default !== undefined ? ' (default ' + o.default + ')' : ' (empty = default)'), validate: (v) => (o.type !== 'number' || v === '' || Number(v) > 0 ? true : 'a positive number') });
67
- if (!value)
68
- continue;
69
- for (const v of o.type === 'list' ? splitArgs(value) : [value])
70
- argv.push(o.flag, v);
71
- }
72
- return argv;
73
- };
74
- /** Asks everything a command takes and returns its argv (or null when the user backs out). */
75
- export const askCommand = async (ctx, spec, preset = []) => {
76
- console.log('\n' + c.bold(spec.name) + c.dim(' ' + spec.summary));
77
- const argv = [spec.name, ...preset];
78
- for (const arg of spec.args)
79
- argv.push(...await askArg(ctx, arg));
80
- const basic = spec.options.filter((o) => !o.advanced && !preset.includes(o.flag));
81
- const advanced = spec.options.filter((o) => o.advanced);
82
- argv.push(...await askOptions(ctx, basic));
83
- if (advanced.length && await confirm({ message: 'Change advanced options (' + advanced.map((o) => o.flag).join(', ') + ')?', default: false }))
84
- argv.push(...await askOptions(ctx, advanced));
85
- if (spec.json && await confirm({ message: 'Machine-readable output (--json)?', default: false }))
86
- argv.push('--json');
87
- if (spec.passthrough) {
88
- const extra = splitArgs(await input({ message: spec.passthrough === 'agent' ? 'Extra arguments for the agent ' + c.dim('(optional, e.g. --continue)') : 'Arguments for the command' + c.dim(' (optional)') }));
89
- if (extra.length)
90
- argv.push(...(spec.passthrough === 'agent' ? ['--', ...extra] : extra));
91
- }
92
- console.log(c.dim(' ' + commandLine(argv)));
93
- return await confirm({ message: 'Run it?', default: true }) ? argv : null;
94
- };
95
- // ---- the menu --------------------------------------------------------------------------------------------------------
96
- /** Is this agent already in the sandbox's persistent storage (so starting it needs no install)? */
97
- const agentInstalled = (ctx, name) => {
98
- const h = ctx.loaded.config.harnesses[name];
99
- const dir = path.join(ctx.dataPath, 'tools', 'agents', name);
100
- return Boolean(h) && [h.hostExecutable, h.npmBin && h.npmBin + '.cmd', h.uvBin && h.uvBin + '.exe'].some((f) => f && fs.existsSync(path.join(dir, f)));
101
- };
102
- const GROUP_TITLES = { Agents: 'Start an agent', Sandbox: 'Sandbox', Sessions: 'Sessions (attach, restart)', Accounts: 'Claude accounts', Data: 'Persisted data, sync and cleanup', Health: 'Diagnostics and repair (doctor, setup, repair, dry run, logs)', Config: 'Configuration' };
103
- const pickAgent = async (ctx) => {
104
- const names = Object.keys(ctx.loaded.config.harnesses);
105
- const name = await select({
106
- message: 'Which agent?',
107
- pageSize: 16,
108
- choices: [
109
- ...names.map((n) => ({ name: n.padEnd(10) + (agentInstalled(ctx, n) ? c.green(' installed') : c.dim(' installs on first start')) + (n === ctx.loaded.config.defaultHarness ? c.dim(' (default)') : ''), value: n })),
110
- new Separator(),
111
- { name: 'Claude on this PC, NOT sandboxed (host-claude)', value: '@host' },
112
- { name: c.dim('< back'), value: '' },
113
- ],
114
- });
115
- if (name === '@host')
116
- return { kind: 'ask', spec: COMMANDS.find((s) => s.name === 'host-claude') };
117
- return name ? { kind: 'ask', spec: COMMANDS.find((s) => s.name === 'launch'), preset: ['--harness', name] } : { kind: 'group', group: '' };
118
- };
119
- const mainMenu = async (ctx) => {
120
- const last = readHistory()[0];
121
- const running = isSandboxRunning();
122
- console.log('\n' + c.bold(c.cyan('Windows Development Sandbox')) + c.dim(' ' + process.cwd()) + '\n' + (running ? c.green(' A sandbox is running') + c.dim(' (' + runningSessions(ctx.dataPath).length + ' session(s))') : c.dim(' No sandbox running')));
123
- return select({
124
- message: 'What would you like to do?',
125
- pageSize: 14,
126
- choices: [
127
- ...(last ? [{ name: 'Repeat last: ' + c.cyan(commandLine(last.argv)), value: { kind: 'run', argv: last.argv } }] : []),
128
- { name: 'Start ' + ctx.loaded.config.defaultHarness + ' in this folder', value: { kind: 'ask', spec: COMMANDS.find((s) => s.name === 'launch'), preset: ['--harness', ctx.loaded.config.defaultHarness] } },
129
- { name: 'Start another agent...', value: { kind: 'group', group: '@agents' } },
130
- { name: 'Start a sandbox without an agent', value: { kind: 'ask', spec: COMMANDS.find((s) => s.name === 'start') } },
131
- new Separator(),
132
- ...Group.options.filter((g) => g !== 'Agents').map((g) => ({ name: GROUP_TITLES[g] + '...', value: { kind: 'group', group: g } })),
133
- { name: 'Settings ' + c.dim('(every setting, saved to your config file)'), value: { kind: 'group', group: '@settings' } },
134
- new Separator(),
135
- { name: 'Exit', value: { kind: 'exit' } },
136
- ],
137
- });
138
- };
139
- const groupMenu = async (group) => select({
140
- message: GROUP_TITLES[group] ?? group,
141
- pageSize: 16,
142
- choices: [
143
- ...COMMANDS.filter((s) => s.group === group && !s.hidden).map((s) => ({ name: s.name.padEnd(10) + c.dim(' ' + s.summary), value: { kind: 'ask', spec: s } })),
144
- { name: c.dim('< back'), value: { kind: 'group', group: '' } },
145
- ],
146
- });
147
- /**
148
- * Runs the menu until Exit, or until a command that takes over the terminal (launch, attach) has run. `run` executes
149
- * an argv exactly like typing it.
150
- */
151
- export const menu = async (ctx, run) => {
152
- if (!process.stdin.isTTY) {
153
- console.log('The menu needs an interactive terminal. Run sandbox --help for the commands.');
154
- return 1;
155
- }
156
- try {
157
- for (let pick = await mainMenu(ctx);;) {
158
- if (pick.kind === 'exit')
159
- return 0;
160
- if (pick.kind === 'group' && pick.group === '@settings') {
161
- await settingsMenu(ctx.loaded.configPath);
162
- pick = await mainMenu(ctx);
163
- }
164
- else if (pick.kind === 'group')
165
- pick = pick.group === '' ? await mainMenu(ctx) : pick.group === '@agents' ? await pickAgent(ctx) : await groupMenu(pick.group);
166
- else {
167
- const argv = pick.kind === 'run' ? pick.argv : await askCommand(ctx, pick.spec, pick.preset);
168
- if (!argv) {
169
- pick = await mainMenu(ctx);
170
- continue;
171
- }
172
- const code = await run(argv);
173
- if (COMMANDS.find((s) => s.name === argv[0])?.interactive)
174
- return code;
175
- await input({ message: c.dim((code === 0 ? 'Done' : yellowCode(code)) + '. Press Enter for the menu') });
176
- pick = await mainMenu(ctx);
177
- }
178
- }
179
- }
180
- catch (e) {
181
- // Ctrl+C in a prompt: leave quietly.
182
- if (e instanceof Error && e.name === 'ExitPromptError')
183
- return 130;
184
- throw e;
185
- }
186
- };
187
- const yellowCode = (code) => c.yellow('Exit code ' + code);
@@ -1,11 +0,0 @@
1
- /**
2
- * Applies one change (undefined = back to the default) to the config text and returns the new text, or the problems
3
- * that make the result invalid. Pure: the caller writes the file.
4
- */
5
- export declare const editConfigText: (text: string, key: string[], value: unknown) => {
6
- text: string;
7
- } | {
8
- problems: string[];
9
- };
10
- /** The settings screen. Changes are saved to the user config file (created when missing). */
11
- export declare const settingsMenu: (configPath: string | null) => Promise<void>;
@@ -1,156 +0,0 @@
1
- // The settings screen in the menu: every setting, generated from the zod schema (so nothing needs to be listed by
2
- // hand), shown with its current value and description, edited with a fitting prompt and saved to the user config
3
- // file. Edits go through jsonc-parser, so comments and formatting in the file are kept, and nothing is written unless
4
- // the whole config is still valid.
5
- import fs from 'node:fs';
6
- import path from 'node:path';
7
- import { input, select, Separator } from '@inquirer/prompts';
8
- import { applyEdits, modify, parse } from 'jsonc-parser';
9
- import _ from 'lodash';
10
- import { DEFAULTS, loadConfig, mergeConfig, userConfigPath } from '../core/config.js';
11
- import { Config as ConfigSchema, describeIssues } from '../core/schema.js';
12
- const c = { dim: (s) => '\x1b[2m' + s + '\x1b[0m', cyan: (s) => '\x1b[36m' + s + '\x1b[0m', green: (s) => '\x1b[32m' + s + '\x1b[0m', yellow: (s) => '\x1b[33m' + s + '\x1b[0m', red: (s) => '\x1b[31m' + s + '\x1b[0m' };
13
- /** optional()/default() wrappers hide the real type. */
14
- const unwrap = (s) => (['optional', 'default', 'nullable'].includes(s._zod.def.type) && s._zod.def.innerType ? unwrap(s._zod.def.innerType) : s);
15
- const kind = (s) => unwrap(s)._zod.def.type;
16
- const isSection = (s) => kind(s) === 'object' && Boolean(unwrap(s).shape);
17
- /** Settings not worth editing here (the schema link) or better edited in the file (agent definitions). */
18
- const HIDDEN = new Set(['$schema']);
19
- /** A value in one short line: maps as their keys, long lists shortened. */
20
- const show = (value) => {
21
- if (value === undefined)
22
- return c.dim('(not set)');
23
- if (typeof value === 'string')
24
- return value;
25
- if (value && typeof value === 'object' && !Array.isArray(value)) {
26
- const keys = Object.keys(value);
27
- return keys.length ? '{ ' + _.truncate(keys.join(', '), { length: 60 }) + ' }' : '{}';
28
- }
29
- return _.truncate(JSON.stringify(value), { length: 70 });
30
- };
31
- /** What the value is in the file the user owns (undefined when it comes from the built-in defaults). */
32
- const fileValue = (text, key) => _.get(parse(text) ?? {}, key);
33
- /**
34
- * Applies one change (undefined = back to the default) to the config text and returns the new text, or the problems
35
- * that make the result invalid. Pure: the caller writes the file.
36
- */
37
- export const editConfigText = (text, key, value) => {
38
- const edits = modify(text || '{}', key, value, { formattingOptions: { insertSpaces: true, tabSize: 2, eol: '\n' } });
39
- const next = applyEdits(text || '{}', edits);
40
- const parsed = ConfigSchema.safeParse(mergeConfig(DEFAULTS, parse(next) ?? {}));
41
- return parsed.success ? { text: next.endsWith('\n') ? next : next + '\n' } : { problems: describeIssues(parsed.error) };
42
- };
43
- /** A prompt that fits the setting's type; returns the new value, or undefined to change nothing. */
44
- const askValue = async (schema, label, current, config) => {
45
- const s = unwrap(schema);
46
- // The default agent is one of the configured ones: a list, not free text.
47
- if (label === 'defaultHarness' && config) {
48
- const v = await select({ message: label, choices: [...Object.keys(config.harnesses).map((h) => ({ name: h, value: h })), { name: c.dim('< cancel'), value: '@cancel' }], default: current });
49
- return v === '@cancel' ? undefined : { value: v };
50
- }
51
- const t = s._zod.def.type;
52
- const back = { name: c.dim('< cancel'), value: '@cancel' };
53
- const pick = async (choices) => {
54
- const v = await select({ message: label, choices: [...choices, back], default: current });
55
- return v === '@cancel' ? undefined : { value: v };
56
- };
57
- if (t === 'boolean')
58
- return pick([{ name: 'true', value: true }, { name: 'false', value: false }]);
59
- if (t === 'enum')
60
- return pick(Object.values(s._zod.def.entries ?? {}).map((v) => ({ name: String(v), value: v })));
61
- if (t === 'union') {
62
- const options = s._zod.def.options ?? [];
63
- const literals = options.filter((o) => kind(o) === 'literal').flatMap((o) => unwrap(o)._zod.def.values ?? []);
64
- const hasBool = options.some((o) => kind(o) === 'boolean');
65
- const other = options.find((o) => !['literal', 'boolean'].includes(kind(o)));
66
- const choices = [...(hasBool ? [{ name: 'true', value: true }, { name: 'false', value: false }] : []), ...literals.map((v) => ({ name: String(v), value: v }))];
67
- if (!other)
68
- return pick(choices);
69
- const chosen = await pick([...choices, { name: 'a ' + kind(other) + ' ...', value: '@other' }]);
70
- return chosen?.value === '@other' ? askValue(other, label, current) : chosen;
71
- }
72
- const typed = async (validate, convert) => {
73
- const answer = await input({ message: label + c.dim(' (empty = cancel)'), default: current === undefined ? '' : typeof current === 'string' ? current : JSON.stringify(current), validate: (v) => (v === '' ? true : validate(v)) });
74
- return answer === '' ? undefined : { value: convert(answer) };
75
- };
76
- if (t === 'number')
77
- return typed((v) => (Number.isFinite(Number(v)) ? true : 'a number'), Number);
78
- if (t === 'string')
79
- return typed(() => true, (v) => v);
80
- if (t === 'array' && kind(s._zod.def.element) === 'string') {
81
- return typed(() => true, (v) => (v.trim() === '[]' ? [] : v.split(',').map((x) => x.trim()).filter(Boolean)));
82
- }
83
- // Lists of objects and maps (mappings, harnesses, environment): edited as JSON.
84
- return typed((v) => { try {
85
- JSON.parse(v);
86
- return true;
87
- }
88
- catch {
89
- return 'valid JSON';
90
- } }, (v) => JSON.parse(v));
91
- };
92
- /** Edits one setting: shows it, asks, saves (or resets it to the default). */
93
- const editSetting = async (schema, key, config, file) => {
94
- const text = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
95
- const inFile = fileValue(text, key);
96
- console.log('\n' + c.cyan(key.join('.')) + (schema.description ? '\n' + c.dim(schema.description) : ''));
97
- console.log('Now: ' + show(_.get(config, key)) + (inFile === undefined ? c.dim(' (built-in default)') : c.dim(' (set in ' + file + ')')));
98
- const action = inFile === undefined ? 'change' : await select({ message: 'What now?', choices: [{ name: 'Change it', value: 'change' }, { name: 'Reset to the default (' + show(_.get(DEFAULTS, key)) + ')', value: 'reset' }, { name: c.dim('< back'), value: 'back' }] });
99
- if (action === 'back')
100
- return;
101
- const answer = action === 'reset' ? { value: undefined } : await askValue(schema, key.join('.'), _.get(config, key), config);
102
- if (!answer)
103
- return;
104
- // The default needs no line in the file; an unchanged value needs no save.
105
- const value = _.isEqual(answer.value, _.get(DEFAULTS, key)) ? undefined : answer.value;
106
- if (value === inFile && _.isEqual(answer.value, _.get(config, key))) {
107
- console.log(c.dim('Unchanged.'));
108
- return;
109
- }
110
- const result = editConfigText(text, key, value);
111
- if ('problems' in result) {
112
- console.log(c.red('Not saved: ') + result.problems.join('; '));
113
- return;
114
- }
115
- fs.mkdirSync(path.dirname(file), { recursive: true });
116
- fs.writeFileSync(file, result.text, 'utf8');
117
- console.log(c.green('Saved') + c.dim(' to ' + file + '. New sandboxes use it.'));
118
- };
119
- const SECTION_TITLES = {
120
- toolchains: 'Languages and tools', databases: 'Databases', sandbox: 'The sandbox machine (memory, window, GPU, network)', attach: 'Attaching (auto attach, auto hide)',
121
- provisioning: 'Provisioning and git', secrets: 'Secrets', claudeProfile: 'Your Claude settings and history', storage: 'Storage', network: 'Network', hooks: 'Hooks', personalization: 'Desktop look',
122
- };
123
- /** Browses one level of settings; sections open, leaves are edited. */
124
- const browse = async (schema, key, file) => {
125
- for (;;) {
126
- const config = loadConfig().config;
127
- const entries = Object.entries(unwrap(schema).shape ?? {}).filter(([name]) => !HIDDEN.has(name));
128
- const text = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
129
- const pickName = await select({
130
- message: key.length ? key.join('.') : 'Settings ' + c.dim('(saved to ' + file + ')'),
131
- pageSize: 18,
132
- choices: [
133
- ...entries.map(([name, s]) => {
134
- const full = [...key, name];
135
- const changed = fileValue(text, full) !== undefined;
136
- const label = isSection(s) ? (key.length ? name : SECTION_TITLES[name] ?? name) + c.dim(' ...') : name.padEnd(18) + ' ' + (changed ? c.yellow(show(_.get(config, full))) : show(_.get(config, full)));
137
- return { name: label, value: name, description: s.description };
138
- }),
139
- new Separator(),
140
- { name: c.dim('< back'), value: '@back' },
141
- ],
142
- });
143
- if (pickName === '@back')
144
- return;
145
- const child = unwrap(schema).shape[pickName];
146
- if (isSection(child))
147
- await browse(child, [...key, pickName], file);
148
- else
149
- await editSetting(child, [...key, pickName], config, file);
150
- }
151
- };
152
- /** The settings screen. Changes are saved to the user config file (created when missing). */
153
- export const settingsMenu = async (configPath) => {
154
- const file = configPath ?? userConfigPath();
155
- await browse(ConfigSchema, [], file);
156
- };