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,3 +1,4 @@
1
+ import { type ReleaseAsset } from '../../shared/download.js';
1
2
  /** What the toolchains contribute to the agent's environment. */
2
3
  export interface ToolEnv {
3
4
  vars: Record<string, string>;
@@ -16,9 +17,24 @@ export declare const versionOf: (exe: string, args?: string[], options?: {}) =>
16
17
  * straight to the remembered versions instead of each waiting for the network; other errors (no such version) do not.
17
18
  */
18
19
  export declare const resolveCached: (key: string, resolve: () => Promise<string>) => Promise<string>;
20
+ /** A single file download that lands as `file` only when complete (and checksum-verified): a cut-off one never looks finished. */
21
+ export declare const downloadFile: (url: string, file: string, sha256?: string | null) => Promise<void>;
22
+ interface GithubInstall {
23
+ /** The folder the archive is unpacked into (replaced atomically). */
24
+ dest: string;
25
+ /** What the progress line says is being downloaded, with its size; a function gets the release asset. */
26
+ what: string | ((asset: ReleaseAsset) => string);
27
+ /** Look past the latest release for a matching asset. */
28
+ searchOlder?: boolean;
29
+ /** installArchive's `locate` / `up`: keep only the folder holding this file. */
30
+ locate?: string;
31
+ up?: number;
32
+ }
33
+ /** The newest GitHub release asset matching `pattern`, checksum-verified, unpacked into `dest`. */
34
+ export declare const installFromGithub: (repo: string, pattern: RegExp, o: GithubInstall) => Promise<void>;
19
35
  /** Runs one installer; a failure is reported and skipped so it never blocks the other toolchains. */
20
36
  export declare const step: (name: string, env: ToolEnv, work: () => Promise<void>) => Promise<void>;
21
- /** File names in the workspace and its direct sub-folders ("api/go.mod"); read once per boot. */
37
+ /** File names in the project's folder and its direct sub-folders ("api/go.mod"); read once per project. */
22
38
  export declare const workspaceEntries: () => string[];
23
39
  /**
24
40
  * true / false as configured. The host already turns "auto" into a decision at launch (core/detect-toolchains.ts);
@@ -35,3 +51,4 @@ export declare const versionFromFiles: (files: string[], parse?: (text: string,
35
51
  * "npm@22.11.0"). One failing package is reported and skipped; it is tried again next boot.
36
52
  */
37
53
  export declare const installOnce: (env: ToolEnv, group: string, packages: string[], install: (pkg: string) => void | Promise<void>) => Promise<void>;
54
+ export {};
@@ -3,8 +3,10 @@
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import { z } from 'zod';
6
+ import { download, githubAsset, installArchive } from '../../shared/download.js';
6
7
  import { writeJsonAtomic } from '../../shared/rundir.js';
7
- import { errMsg, execOutput, exists, log, session, toolsDir, warn, workspace } from '../env.js';
8
+ import { errMsg, execOutput, exists, log, session, toolsDir, warn } from '../env.js';
9
+ import { projectWorkspace } from '../project-context.js';
8
10
  import { MARKERS } from '../../core/detect-toolchains.js';
9
11
  import { projectUses, specFromVersionFile } from './versions.js';
10
12
  export const emptyEnv = () => ({ vars: {}, prepend: [], append: [], summary: [] });
@@ -49,27 +51,47 @@ export const resolveCached = async (key, resolve) => {
49
51
  offline = true;
50
52
  if (!cached)
51
53
  throw e;
52
- warn('Could not look up ' + key + ' online (' + errMsg(e) + '); using ' + cached.value + ' from an earlier boot.');
54
+ warn('Could not look up the latest ' + key + ' online (' + errMsg(e) + '); using ' + cached.value + ' from an earlier start.');
53
55
  return cached.value;
54
56
  }
55
57
  };
58
+ /** A single file download that lands as `file` only when complete (and checksum-verified): a cut-off one never looks finished. */
59
+ export const downloadFile = async (url, file, sha256 = null) => {
60
+ const part = file + '.part';
61
+ try {
62
+ await download(url, part, sha256);
63
+ fs.rmSync(file, { force: true });
64
+ fs.renameSync(part, file);
65
+ }
66
+ finally {
67
+ fs.rmSync(part, { force: true });
68
+ }
69
+ };
70
+ /** The newest GitHub release asset matching `pattern`, checksum-verified, unpacked into `dest`. */
71
+ export const installFromGithub = async (repo, pattern, o) => {
72
+ const asset = await githubAsset(repo, pattern, o.searchOlder);
73
+ log('Downloading ' + (typeof o.what === 'string' ? o.what : o.what(asset)) + ' ...');
74
+ await installArchive(asset.url, o.dest, { sha256: asset.sha256, fileName: asset.name, ...(o.locate ? { locate: o.locate } : {}), ...(o.up ? { up: o.up } : {}) });
75
+ };
56
76
  /** Runs one installer; a failure is reported and skipped so it never blocks the other toolchains. */
57
77
  export const step = async (name, env, work) => {
58
78
  try {
59
79
  await work();
60
80
  }
61
81
  catch (e) {
62
- warn(name + ' setup failed (continuing without it): ' + errMsg(e));
82
+ warn(name + ' could not be set up and is skipped; everything else still works, and the next start tries again. Reason: ' + errMsg(e));
63
83
  env.summary.push(name + ': FAILED');
64
84
  }
65
85
  };
66
86
  // ---- which toolchains a project needs ------------------------------------------------------------------------------
67
87
  const SKIP_DIRS = new Set(['node_modules', '.git', 'target', 'bin', 'obj', 'dist', 'build', 'vendor', '.venv', 'venv']);
68
- let entries = null;
69
- /** File names in the workspace and its direct sub-folders ("api/go.mod"); read once per boot. */
88
+ const entries = new Map();
89
+ /** File names in the project's folder and its direct sub-folders ("api/go.mod"); read once per project. */
70
90
  export const workspaceEntries = () => {
71
- if (entries)
72
- return entries;
91
+ const workspace = projectWorkspace();
92
+ const known = entries.get(workspace.toLowerCase());
93
+ if (known)
94
+ return known;
73
95
  const list = (dir) => { try {
74
96
  return fs.readdirSync(dir, { withFileTypes: true });
75
97
  }
@@ -79,8 +101,9 @@ export const workspaceEntries = () => {
79
101
  const top = list(workspace);
80
102
  const below = top.filter((d) => d.isDirectory() && !d.name.startsWith('.') && !SKIP_DIRS.has(d.name.toLowerCase()))
81
103
  .flatMap((d) => list(path.join(workspace, d.name)).filter((f) => f.isFile()).map((f) => d.name + '/' + f.name));
82
- entries = [...top.filter((d) => d.isFile()).map((d) => d.name), ...below];
83
- return entries;
104
+ const found = [...top.filter((d) => d.isFile()).map((d) => d.name), ...below];
105
+ entries.set(workspace.toLowerCase(), found);
106
+ return found;
84
107
  };
85
108
  /**
86
109
  * true / false as configured. The host already turns "auto" into a decision at launch (core/detect-toolchains.ts);
@@ -90,7 +113,7 @@ export const wanted = (setting, toolchain) => setting === 'auto' ? projectUses(w
90
113
  /** The first of these version files in the workspace root, parsed (e.g. .ruby-version "3.3.1"). */
91
114
  export const versionFromFiles = (files, parse = (t) => specFromVersionFile(t)) => {
92
115
  for (const name of files) {
93
- const file = path.join(workspace, name);
116
+ const file = path.join(projectWorkspace(), name);
94
117
  if (!exists(file))
95
118
  continue;
96
119
  const spec = parse(fs.readFileSync(file, 'utf8'), name);
@@ -115,7 +138,7 @@ export const installOnce = async (env, group, packages, install) => {
115
138
  writeJsonAtomic(installedFile(), { ...installed(), [group + ':' + pkg]: new Date().toISOString() });
116
139
  }
117
140
  catch (e) {
118
- warn(group + ' package ' + pkg + ' failed: ' + errMsg(e));
141
+ warn(pkg + ' (' + group + ') could not be installed and is skipped; the next start tries again. Reason: ' + errMsg(e));
119
142
  }
120
143
  }
121
144
  const ok = packages.filter((p) => installed()[group + ':' + p]);
@@ -0,0 +1,8 @@
1
+ import type { DatabasePlan } from '../../../core/types.js';
2
+ import { type ToolEnv } from '../common.js';
3
+ /**
4
+ * Amazon's DynamoDB Local (a Java program; the Java toolchain step runs first, else this installs the LTS JDK) and
5
+ * dynamodb-admin, the web UI to browse and edit tables, installed with the runner's own npm. Every AWS SDK picks the
6
+ * local endpoint up from AWS_ENDPOINT_URL_DYNAMODB, with dummy credentials (real ones never reach the sandbox).
7
+ */
8
+ export declare const setupDynamo: (env: ToolEnv, plan: DatabasePlan) => Promise<void>;
@@ -0,0 +1,52 @@
1
+ // DynamoDB Local + dynamodb-admin.
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { spawn } from 'node:child_process';
5
+ import { fetchText, installArchive } from '../../../shared/download.js';
6
+ import { execAsync, exists, log, logsDir, mkdirp } from '../../env.js';
7
+ import { ensureJdk } from '../java.js';
8
+ import { tool } from '../common.js';
9
+ import { portOpen, waitForPort } from '../../ports.js';
10
+ import { dataFor, startServer } from './server.js';
11
+ const DYNAMO_ZIP = 'https://d1ni2b6xgvw0s0.cloudfront.net/v2.x/dynamodb_local_latest.zip';
12
+ /**
13
+ * Amazon's DynamoDB Local (a Java program; the Java toolchain step runs first, else this installs the LTS JDK) and
14
+ * dynamodb-admin, the web UI to browse and edit tables, installed with the runner's own npm. Every AWS SDK picks the
15
+ * local endpoint up from AWS_ENDPOINT_URL_DYNAMODB, with dummy credentials (real ones never reach the sandbox).
16
+ */
17
+ export const setupDynamo = async (env, plan) => {
18
+ const java = path.join(await ensureJdk(), 'bin', 'java.exe');
19
+ const home = tool('dynamodb-local');
20
+ if (!exists(path.join(home, 'DynamoDBLocal.jar'))) {
21
+ log('Downloading DynamoDB Local (~60 MB once) ...');
22
+ const sha256 = (await fetchText(DYNAMO_ZIP + '.sha256')).trim().split(/\s+/)[0];
23
+ await installArchive(DYNAMO_ZIP, home, { sha256, locate: 'DynamoDBLocal.jar', fileName: 'dynamodb_local.zip' });
24
+ }
25
+ const data = mkdirp(dataFor('dynamodb'));
26
+ // -sharedDb: one database whatever region/credentials a client uses, so the admin UI and the app see the same tables.
27
+ await startServer('dynamodb', java, ['-Djava.library.path=' + path.join(home, 'DynamoDBLocal_lib'), '-jar', path.join(home, 'DynamoDBLocal.jar'), '-sharedDb', '-dbPath', data, '-port', String(plan.port)], plan.port);
28
+ const endpoint = 'http://localhost:' + plan.port;
29
+ const aws = { AWS_ENDPOINT_URL_DYNAMODB: endpoint, DYNAMO_ENDPOINT: endpoint, AWS_ACCESS_KEY_ID: 'local', AWS_SECRET_ACCESS_KEY: 'local', AWS_REGION: 'us-east-1', AWS_DEFAULT_REGION: 'us-east-1' };
30
+ Object.assign(env.vars, aws);
31
+ env.summary.push('dynamodb (DynamoDB Local): ' + endpoint + ' (AWS SDKs use it automatically via AWS_ENDPOINT_URL_DYNAMODB; dummy credentials)');
32
+ if (!plan.adminPort)
33
+ return;
34
+ const prefix = tool('dynamodb-admin');
35
+ const script = path.join(prefix, 'node_modules', 'dynamodb-admin', 'dist', 'dynamodb-admin.js');
36
+ if (!exists(script)) {
37
+ log('Installing dynamodb-admin, the DynamoDB web UI (~20 MB once) ...');
38
+ const npmCli = path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
39
+ await execAsync(process.execPath, [npmCli, 'install', '--global', '--prefix', prefix, 'dynamodb-admin', '--no-fund', '--no-audit'], { stdio: 'ignore' });
40
+ }
41
+ // The database matters more than its UI: a taken port is reported, not fatal.
42
+ if (await portOpen(plan.adminPort)) {
43
+ env.summary.push('dynamodb-admin: NOT started, port ' + plan.adminPort + ' is in use (change databases.dynamodb.adminPort)');
44
+ return;
45
+ }
46
+ const out = fs.openSync(path.join(logsDir, 'dynamodb-admin.log'), 'a');
47
+ spawn(process.execPath, [script, '--port', String(plan.adminPort), '--dynamo-endpoint', endpoint, '--skip-default-credentials'], { detached: true, stdio: ['ignore', out, out], windowsHide: true, env: { ...process.env, ...aws } }).unref();
48
+ fs.closeSync(out);
49
+ if (!(await waitForPort(plan.adminPort, 30)))
50
+ throw new Error('dynamodb-admin did not start; see ' + path.join(logsDir, 'dynamodb-admin.log'));
51
+ env.summary.push('dynamodb-admin: http://localhost:' + plan.adminPort + ' (browse and edit tables in the sandbox\'s browser)');
52
+ };
@@ -1,6 +1,5 @@
1
- import type { DatabasePlan } from '../../core/types.js';
2
- import { type ToolEnv } from './common.js';
3
- export declare const portOpen: (port: number) => Promise<boolean>;
1
+ import type { DatabasePlan } from '../../../core/types.js';
2
+ import { type ToolEnv } from '../common.js';
4
3
  export declare const DATABASE_NAMES: Record<DatabasePlan['database'], string>;
5
4
  /** Stops a database (and DynamoDB's admin UI). PostgreSQL shuts down cleanly through pg_ctl; the others are ended. */
6
5
  export declare const stopDatabase: (plan: DatabasePlan) => Promise<boolean>;
@@ -0,0 +1,62 @@
1
+ // Databases inside the sandbox: PostgreSQL, MariaDB (MySQL-compatible), MongoDB, Redis and DynamoDB Local. Binaries
2
+ // live in C:\AgentData\tools, data in C:\AgentData\databases (both persist), and every server listens on localhost only.
3
+ // Which ones run was decided on the host (session.databases): only what the project needs.
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { spawnSync } from 'node:child_process';
7
+ import { SYSTEM } from '../../../shared/system.js';
8
+ import { dryRun, execAsync, exists, log } from '../../env.js';
9
+ import { tool } from '../common.js';
10
+ import { setupDynamo } from './dynamodb.js';
11
+ import { setupMongo } from './mongodb.js';
12
+ import { setupMysql } from './mysql.js';
13
+ import { postgresData, setupPostgres } from './postgres.js';
14
+ import { setupRedis } from './redis.js';
15
+ import { portOpen } from '../../ports.js';
16
+ import { duringSession } from '../../project-context.js';
17
+ const SETUP = { postgres: setupPostgres, mysql: setupMysql, mongodb: setupMongo, redis: setupRedis, dynamodb: setupDynamo };
18
+ export const DATABASE_NAMES = { postgres: 'PostgreSQL', mysql: 'MySQL (MariaDB)', mongodb: 'MongoDB', redis: 'Redis', dynamodb: 'DynamoDB Local', sqlserver: 'SQL Server' };
19
+ /** The process listening on a local TCP port (netstat: no PowerShell start-up cost), or null. */
20
+ const listenerPid = (port) => {
21
+ const out = spawnSync(SYSTEM.netstat(), ['-ano', '-p', 'TCP'], { encoding: 'utf8', windowsHide: true }).stdout ?? '';
22
+ const hit = out.split(/\r?\n/).map((l) => /^\s*TCP\s+\S+:(\d+)\s+\S+\s+LISTENING\s+(\d+)/.exec(l)).find((m) => m && Number(m[1]) === port);
23
+ return hit ? Number(hit[2]) : null;
24
+ };
25
+ /** Stops a database (and DynamoDB's admin UI). PostgreSQL shuts down cleanly through pg_ctl; the others are ended. */
26
+ export const stopDatabase = async (plan) => {
27
+ if (!(await portOpen(plan.port)))
28
+ return false;
29
+ if (plan.database === 'postgres' && exists(tool('postgres'))) {
30
+ for (const version of fs.readdirSync(tool('postgres'))) {
31
+ const data = postgresData(version);
32
+ const pgCtl = path.join(tool('postgres', version), 'bin', 'pg_ctl.exe');
33
+ if (exists(path.join(data, 'postmaster.pid')) && exists(pgCtl))
34
+ await execAsync(pgCtl, ['stop', '-D', data, '-m', 'fast', '-w'], { stdio: 'ignore' });
35
+ }
36
+ }
37
+ for (const port of [plan.port, plan.adminPort ?? 0]) {
38
+ const pid = port && listenerPid(port);
39
+ if (pid)
40
+ try {
41
+ process.kill(pid);
42
+ }
43
+ catch { /* already gone */ }
44
+ }
45
+ return true;
46
+ };
47
+ export const setupDatabase = async (env, plan) => {
48
+ if (plan.database === 'sqlserver') {
49
+ env.summary.push('sqlserver: NOT available (SQL Server cannot start inside Windows Sandbox). Switch the project to Postgres or SQLite for local work');
50
+ return;
51
+ }
52
+ if (dryRun) {
53
+ log('(dry run) would run ' + DATABASE_NAMES[plan.database] + ' on port ' + plan.port + ' (' + plan.reasons.join('; ') + ')');
54
+ return;
55
+ }
56
+ // A project that joined later, while another project's server already listens there: they share it.
57
+ if (duringSession() && (await portOpen(plan.port))) {
58
+ env.summary.push(plan.database + ': shared, already running on port ' + plan.port);
59
+ return;
60
+ }
61
+ await SETUP[plan.database](env, plan);
62
+ };
@@ -0,0 +1,3 @@
1
+ import type { DatabasePlan } from '../../../core/types.js';
2
+ import { type ToolEnv } from '../common.js';
3
+ export declare const setupMongo: (env: ToolEnv, plan: DatabasePlan) => Promise<void>;
@@ -0,0 +1,31 @@
1
+ // MongoDB Community, with mongosh.
2
+ import path from 'node:path';
3
+ import { fetchJson, installArchive } from '../../../shared/download.js';
4
+ import { exists, log, mkdirp } from '../../env.js';
5
+ import { installFromGithub, resolveCached, tool } from '../common.js';
6
+ import { clearStaleLocks, dataFor, startServer } from './server.js';
7
+ export const setupMongo = async (env, plan) => {
8
+ const find = async () => {
9
+ const { versions } = await fetchJson('https://downloads.mongodb.org/full.json');
10
+ const prod = versions.filter((v) => v.production_release);
11
+ const hit = plan.version === 'latest' ? prod[0] : prod.find((v) => v.version === plan.version || v.version.startsWith(plan.version + '.'));
12
+ const archive = hit?.downloads.find((d) => d.target === 'windows' && d.arch === 'x86_64' && d.edition === 'base')?.archive;
13
+ return hit && archive ? { version: hit.version, archive } : Promise.reject(new Error('No MongoDB ' + plan.version + ' for Windows'));
14
+ };
15
+ const version = await resolveCached('mongodb:' + plan.version, async () => (await find()).version);
16
+ const home = tool('mongodb', version);
17
+ if (!exists(path.join(home, 'bin', 'mongod.exe'))) {
18
+ const { archive } = await find();
19
+ log('Downloading MongoDB ' + version + ' (~400 MB once) ...');
20
+ await installArchive(archive.url, home, { sha256: archive.sha256, locate: 'mongod.exe', up: 1 });
21
+ }
22
+ const shell = tool('mongosh');
23
+ if (!exists(path.join(shell, 'mongosh.exe'))) {
24
+ await installFromGithub('mongodb-js/mongosh', /^mongosh-[\d.]+-win32-x64\.zip$/, { dest: shell, what: 'mongosh, the MongoDB shell (~50 MB once)', locate: 'mongosh.exe' });
25
+ }
26
+ const data = mkdirp(dataFor('mongodb'));
27
+ await clearStaleLocks(data, ['mongod.lock'], plan.port);
28
+ await startServer('mongodb', path.join(home, 'bin', 'mongod.exe'), ['--dbpath', data, '--bind_ip', '127.0.0.1', '--port', String(plan.port)], plan.port);
29
+ env.prepend.push(path.join(home, 'bin'), shell);
30
+ env.summary.push('mongodb ' + version + ': mongodb://127.0.0.1:' + plan.port + ' (mongosh on PATH)');
31
+ };
@@ -0,0 +1,3 @@
1
+ import type { DatabasePlan } from '../../../core/types.js';
2
+ import { type ToolEnv } from '../common.js';
3
+ export declare const setupMysql: (env: ToolEnv, plan: DatabasePlan) => Promise<void>;
@@ -0,0 +1,38 @@
1
+ // MariaDB, the MySQL-compatible server.
2
+ import path from 'node:path';
3
+ import { fetchJson, installArchive } from '../../../shared/download.js';
4
+ import { execAsync, exists, log } from '../../env.js';
5
+ import { resolveCached, tool } from '../common.js';
6
+ import { createOnce, dataFor, startServer } from './server.js';
7
+ const MARIA = 'https://downloads.mariadb.org/rest-api/mariadb/';
8
+ export const setupMysql = async (env, plan) => {
9
+ const series = await resolveCached('mariadb:' + plan.version, async () => {
10
+ if (plan.version !== 'lts')
11
+ return plan.version;
12
+ const { major_releases: all } = await fetchJson(MARIA);
13
+ return all.find((r) => r.release_status === 'Stable' && r.release_support_type === 'Long Term Support')?.release_id ?? Promise.reject(new Error('No MariaDB LTS release found'));
14
+ });
15
+ const latest = async () => {
16
+ const { releases } = await fetchJson(MARIA + series + '/latest/');
17
+ const [version, release] = Object.entries(releases)[0] ?? [];
18
+ const zip = release?.files.find((f) => /-winx64\.zip$/.test(f.file_name));
19
+ return zip ? { version, zip } : Promise.reject(new Error('No MariaDB ' + series + ' zip for Windows'));
20
+ };
21
+ const version = await resolveCached('mariadb-version:' + series, async () => (await latest()).version);
22
+ const home = tool('mariadb', version);
23
+ const bin = path.join(home, 'bin');
24
+ if (!exists(path.join(bin, 'mariadbd.exe'))) {
25
+ const { zip } = await latest();
26
+ log('Downloading MariaDB ' + version + ' (~90 MB once) ...');
27
+ await installArchive(zip.file_download_url.replace(/^http:/, 'https:'), home, { sha256: zip.checksum?.sha256sum ?? null, locate: 'mariadbd.exe', up: 1, fileName: zip.file_name });
28
+ }
29
+ const data = dataFor('mariadb');
30
+ await createOnce(data, 'mysql', async () => {
31
+ log('Creating the MariaDB database (first start) ...');
32
+ await execAsync(path.join(bin, 'mariadb-install-db.exe'), ['--datadir=' + data, '--port=' + plan.port], { stdio: 'ignore' });
33
+ });
34
+ await startServer('mysql', path.join(bin, 'mariadbd.exe'), ['--datadir=' + data, '--port=' + plan.port, '--bind-address=127.0.0.1', '--console'], plan.port);
35
+ Object.assign(env.vars, { MYSQL_HOST: '127.0.0.1', MYSQL_TCP_PORT: String(plan.port) });
36
+ env.prepend.push(bin);
37
+ env.summary.push('mysql (MariaDB ' + version + '): 127.0.0.1:' + plan.port + ', user root, no password (mysql://root@127.0.0.1:' + plan.port + ')');
38
+ };
@@ -0,0 +1,5 @@
1
+ import type { DatabasePlan } from '../../../core/types.js';
2
+ import { type ToolEnv } from '../common.js';
3
+ /** The data folder of a PostgreSQL major version (one cluster per major). */
4
+ export declare const postgresData: (version: string) => string;
5
+ export declare const setupPostgres: (env: ToolEnv, plan: DatabasePlan) => Promise<void>;
@@ -0,0 +1,38 @@
1
+ // PostgreSQL from EnterpriseDB's Windows binaries (without pgAdmin).
2
+ import path from 'node:path';
3
+ import { fetchJson, installArchive } from '../../../shared/download.js';
4
+ import { execAsync, exists, log, logsDir } from '../../env.js';
5
+ import { resolveCached, tool } from '../common.js';
6
+ import { portOpen } from '../../ports.js';
7
+ import { clearStaleLocks, createOnce, dataFor } from './server.js';
8
+ /** The data folder of a PostgreSQL major version (one cluster per major). */
9
+ export const postgresData = (version) => dataFor('postgres-' + version.split('.')[0]);
10
+ export const setupPostgres = async (env, plan) => {
11
+ const exact = await resolveCached('postgres:' + plan.version, async () => {
12
+ const versions = (await fetchJson('https://www.postgresql.org/versions.json')).filter((v) => v.supported);
13
+ const hit = plan.version === 'latest' ? versions.find((v) => v.current) ?? versions[versions.length - 1] : versions.find((v) => v.major === plan.version.split('.')[0]);
14
+ return hit ? hit.major + '.' + hit.latestMinor : Promise.reject(new Error('No supported PostgreSQL ' + plan.version));
15
+ });
16
+ const home = tool('postgres', exact);
17
+ const bin = path.join(home, 'bin');
18
+ if (!exists(path.join(bin, 'pg_ctl.exe'))) {
19
+ log('Downloading PostgreSQL ' + exact + ' (~330 MB once) ...');
20
+ // EDB publishes no checksums; the download is HTTPS from EnterpriseDB, PostgreSQL's official Windows packager.
21
+ await installArchive('https://get.enterprisedb.com/postgresql/postgresql-' + exact + '-1-windows-x64-binaries.zip', home, {
22
+ locate: 'pg_ctl.exe', up: 1, fileName: 'postgresql-' + exact + '.zip', members: ['pgsql/bin', 'pgsql/lib', 'pgsql/share', 'pgsql/include'],
23
+ });
24
+ }
25
+ const data = postgresData(exact);
26
+ await createOnce(data, 'PG_VERSION', async () => {
27
+ log('Creating the PostgreSQL database (first start) ...');
28
+ await execAsync(path.join(bin, 'initdb.exe'), ['-D', data, '-U', 'postgres', '-A', 'trust', '-E', 'UTF8', '--locale=C'], { stdio: 'ignore' });
29
+ });
30
+ if (await portOpen(plan.port))
31
+ throw new Error('port ' + plan.port + ' is already in use (change databases.postgres.port)');
32
+ await clearStaleLocks(data, ['postmaster.pid'], plan.port);
33
+ // postgres.exe refuses to run as an administrator (the sandbox user is one); pg_ctl drops the rights first.
34
+ await execAsync(path.join(bin, 'pg_ctl.exe'), ['start', '-D', data, '-w', '-t', '60', '-l', path.join(logsDir, 'postgres.log'), '-o', '-p ' + plan.port + ' -c listen_addresses=localhost'], { stdio: 'ignore' });
35
+ Object.assign(env.vars, { PGHOST: 'localhost', PGPORT: String(plan.port), PGUSER: 'postgres' });
36
+ env.prepend.push(bin);
37
+ env.summary.push('postgres ' + exact + ': localhost:' + plan.port + ', user postgres, no password (postgres://postgres@localhost:' + plan.port + '/postgres)');
38
+ };
@@ -0,0 +1,3 @@
1
+ import type { DatabasePlan } from '../../../core/types.js';
2
+ import { type ToolEnv } from '../common.js';
3
+ export declare const setupRedis: (env: ToolEnv, plan: DatabasePlan) => Promise<void>;
@@ -0,0 +1,16 @@
1
+ // Redis (the maintained community Windows build of upstream Redis).
2
+ import path from 'node:path';
3
+ import { exists, mkdirp } from '../../env.js';
4
+ import { installFromGithub, tool } from '../common.js';
5
+ import { dataFor, startServer } from './server.js';
6
+ export const setupRedis = async (env, plan) => {
7
+ const home = tool('redis');
8
+ if (!exists(path.join(home, 'redis-server.exe'))) {
9
+ // Redis has no official Windows build; this one is built from upstream Redis with MSYS2.
10
+ await installFromGithub('redis-windows/redis-windows', /^Redis-[\d.]+-Windows-x64-msys2\.zip$/, { dest: home, what: (a) => 'Redis (' + a.name + ', ~10 MB once)', locate: 'redis-server.exe' });
11
+ }
12
+ const data = mkdirp(dataFor('redis'));
13
+ await startServer('redis', path.join(home, 'redis-server.exe'), ['--port', String(plan.port), '--bind', '127.0.0.1', '--dir', data.replace(/\\/g, '/'), '--appendonly', 'yes'], plan.port);
14
+ env.prepend.push(home);
15
+ env.summary.push('redis: redis://127.0.0.1:' + plan.port + ' (redis-cli on PATH)');
16
+ };
@@ -0,0 +1,14 @@
1
+ /** C:\AgentData\databases\<name>: the data survives the sandbox. */
2
+ export declare const dataFor: (name: string) => string;
3
+ /** Starts a server detached (it outlives the boot script) with its output in logs\<name>.log, and waits for its port. */
4
+ export declare const startServer: (name: string, exe: string, args: string[], port: number) => Promise<void>;
5
+ /**
6
+ * Lock files a sandbox that was closed without shutting the server down leaves behind. Only when nothing listens on
7
+ * the port: a server already running in this VM keeps its lock.
8
+ */
9
+ export declare const clearStaleLocks: (dir: string, files: string[], port: number) => Promise<void>;
10
+ /**
11
+ * Creates a database folder with `create` (initdb, install-db) unless `marker` is there. A failed or interrupted run
12
+ * removes the folder again when this call made it, so the half-made one is not taken for a finished database next boot.
13
+ */
14
+ export declare const createOnce: (data: string, marker: string, create: () => Promise<void>) => Promise<void>;
@@ -0,0 +1,52 @@
1
+ // What every database module shares: where data lives, starting a server detached, and safe first-time creation.
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { spawn } from 'node:child_process';
5
+ import { safeRemove } from '../../../shared/safe-remove.js';
6
+ import { dataDir, exists, logsDir, mkdirp } from '../../env.js';
7
+ import { portOpen, waitForPort } from '../../ports.js';
8
+ /** C:\AgentData\databases\<name>: the data survives the sandbox. */
9
+ export const dataFor = (name) => path.join(dataDir, 'databases', name);
10
+ /** Starts a server detached (it outlives the boot script) with its output in logs\<name>.log, and waits for its port. */
11
+ export const startServer = async (name, exe, args, port) => {
12
+ if (await portOpen(port))
13
+ throw new Error('port ' + port + ' is already in use (change databases.' + name + '.port)');
14
+ const logFile = path.join(logsDir, name + '.log');
15
+ const out = fs.openSync(logFile, 'a');
16
+ spawn(exe, args, { detached: true, stdio: ['ignore', out, out], windowsHide: true }).unref();
17
+ fs.closeSync(out);
18
+ if (!(await waitForPort(port, 60)))
19
+ throw new Error(name + ' did not start listening on port ' + port + '; see ' + logFile);
20
+ };
21
+ /**
22
+ * Lock files a sandbox that was closed without shutting the server down leaves behind. Only when nothing listens on
23
+ * the port: a server already running in this VM keeps its lock.
24
+ */
25
+ export const clearStaleLocks = async (dir, files, port) => {
26
+ if (await portOpen(port))
27
+ return;
28
+ for (const f of files)
29
+ fs.rmSync(path.join(dir, f), { force: true });
30
+ };
31
+ /**
32
+ * Creates a database folder with `create` (initdb, install-db) unless `marker` is there. A failed or interrupted run
33
+ * removes the folder again when this call made it, so the half-made one is not taken for a finished database next boot.
34
+ */
35
+ export const createOnce = async (data, marker, create) => {
36
+ if (exists(path.join(data, marker)))
37
+ return;
38
+ const existed = exists(data);
39
+ mkdirp(path.dirname(data));
40
+ try {
41
+ await create();
42
+ }
43
+ catch (e) {
44
+ if (!existed) {
45
+ try {
46
+ safeRemove(data, dataDir);
47
+ }
48
+ catch { /* the original error is the one to report */ }
49
+ }
50
+ throw e;
51
+ }
52
+ };
@@ -3,18 +3,24 @@
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import { download, githubAsset, moveIntoPlace, stagingFolder } from '../../shared/download.js';
6
- import { dataDir, dryRun, errMsg, execAsync, execOutput, exists, hostToolsDir, launchDir, log, mkdirp, robocopy, warn } from '../env.js';
7
- import { ensureCompiled } from '../ptyhost.js';
6
+ import { safeRemove } from '../../shared/safe-remove.js';
7
+ import { dataDir, dryRun, errMsg, execAsync, execOutput, exists, hostToolsDir, launchDir, log, mkdirp, robocopy, toolsDir, warn } from '../env.js';
8
+ import { ensureCompiled } from '../helpers.js';
8
9
  import { arch, tool } from './common.js';
10
+ import { duringSession } from '../project-context.js';
9
11
  const BLOCKED_HOOKS = ['pre-commit', 'commit-msg', 'pre-merge-commit', 'pre-push', 'pre-rebase', 'pre-applypatch', 'applypatch-msg'];
10
12
  const posix = (p) => p.replace(/\\/g, '/');
11
13
  const installPortableGit = async (root) => {
12
14
  const asset = await githubAsset('git-for-windows/git', arch === 'arm64' ? /^PortableGit-[\d.]+-arm64\.7z\.exe$/ : /^PortableGit-[\d.]+-64-bit\.7z\.exe$/);
13
15
  const sfx = tool(asset.name);
14
- log('Downloading ' + asset.name + ' ...');
15
- await download(asset.url, sfx, asset.sha256);
16
- await execAsync(sfx, ['-o' + root, '-y'], { stdio: 'ignore' });
17
- fs.rmSync(sfx, { force: true });
16
+ log('Downloading Git for Windows (' + asset.name + ', ~60 MB once) ...');
17
+ try {
18
+ await download(asset.url, sfx, asset.sha256);
19
+ await execAsync(sfx, ['-o' + root, '-y'], { stdio: 'ignore' });
20
+ }
21
+ finally {
22
+ fs.rmSync(sfx, { force: true });
23
+ }
18
24
  };
19
25
  /**
20
26
  * readonly / none: a `git` wrapper first on PATH and as a bash function (Git Bash would otherwise find the real
@@ -24,14 +30,17 @@ const installGuard = (env, access, realGit) => {
24
30
  const dir = mkdirp(tool('gitguard'));
25
31
  const guard = path.join(launchDir, 'runtime', 'gitguard.js');
26
32
  // A real git.exe (programs like Claude Code only look for .exe); a .cmd only if the C# compiler is unavailable.
27
- try {
28
- fs.copyFileSync(ensureCompiled('gitshim'), path.join(dir, 'git.exe'));
29
- fs.rmSync(path.join(dir, 'git.cmd'), { force: true });
30
- }
31
- catch (e) {
32
- warn('git guard: falling back to git.cmd (' + errMsg(e) + ')');
33
- fs.writeFileSync(path.join(dir, 'git.cmd'), '@"' + process.execPath + '" "' + guard + '" %*\r\n');
34
- }
33
+ // A project added later reuses the boot's: sessions may be running it, and Windows cannot replace a running exe.
34
+ if (duringSession() && (exists(path.join(dir, 'git.exe')) || exists(path.join(dir, 'git.cmd')))) { /* keep it */ }
35
+ else
36
+ try {
37
+ fs.copyFileSync(ensureCompiled('gitshim'), path.join(dir, 'git.exe'));
38
+ fs.rmSync(path.join(dir, 'git.cmd'), { force: true });
39
+ }
40
+ catch (e) {
41
+ warn('git guard: falling back to git.cmd (' + errMsg(e) + ')');
42
+ fs.writeFileSync(path.join(dir, 'git.cmd'), '@"' + process.execPath + '" "' + guard + '" %*\r\n');
43
+ }
35
44
  fs.writeFileSync(path.join(dir, 'bashenv.sh'), 'git() { "' + posix(process.execPath) + '" "' + posix(guard) + '" "$@"; }\nexport -f git\n');
36
45
  env.prepend.unshift(dir);
37
46
  Object.assign(env.vars, {
@@ -71,7 +80,7 @@ export const setupGit = async (env, hostCopy, refresh, access) => {
71
80
  const hostGit = path.join(hostToolsDir, 'git');
72
81
  if (exists(root) && !exists(bash)) {
73
82
  log('Replacing the old MinGit (no bash) with full Git for Windows.');
74
- fs.rmSync(root, { recursive: true, force: true });
83
+ safeRemove(root, toolsDir);
75
84
  }
76
85
  if (!exists(bash) || refresh) {
77
86
  if (dryRun) {
@@ -93,7 +102,7 @@ export const setupGit = async (env, hostCopy, refresh, access) => {
93
102
  moveIntoPlace(work, root);
94
103
  }
95
104
  finally {
96
- fs.rmSync(work, { recursive: true, force: true });
105
+ safeRemove(work, toolsDir);
97
106
  }
98
107
  }
99
108
  if (!exists(bash))
@@ -19,7 +19,7 @@ export const setupGo = async (env, config) => {
19
19
  const file = (await findRelease(version)).files.find((f) => f.os === 'windows' && f.arch === goArch && f.kind === 'archive');
20
20
  if (!file)
21
21
  throw new Error(version + ' has no Windows ' + goArch + ' zip');
22
- log('Downloading ' + file.filename + ' ...');
22
+ log('Downloading Go ' + version.replace(/^go/, '') + ' (~80 MB once) ...');
23
23
  await installArchive('https://go.dev/dl/' + file.filename, root, { sha256: file.sha256, locate: 'VERSION', fileName: file.filename });
24
24
  }
25
25
  const GOPATH = tool('go-path');
@@ -1,13 +1,17 @@
1
- import type { DatabasePlan, GitAccess, Toolchains, ToolVersions } from '../../core/types.js';
1
+ import type { Toolchains, ToolVersions } from '../../core/types.js';
2
2
  import { type ToolEnv } from './common.js';
3
+ import { type ToolchainOptions } from './plan.js';
3
4
  export type { ToolEnv } from './common.js';
4
- export { pickNodeVersion, pickPackageVersion } from './versions.js';
5
- export interface ToolchainOptions {
6
- gitEnabled: boolean;
7
- gitAccess: GitAccess;
8
- hostGit: boolean;
9
- refresh: boolean;
10
- databases: DatabasePlan[];
5
+ export type { ToolchainOptions } from './plan.js';
6
+ export interface RunOptions {
7
+ /** Progress lines ("Installing Go, Rust (3 of 9 done)"); default: the sandbox's boot status. */
8
+ report?: (message: string) => void;
9
+ /** What machine-wide (shared) steps contributed at boot: reused instead of run again. */
10
+ reuse?: Record<string, ToolEnv>;
11
+ }
12
+ /** The combined environment, plus what each step contributed (the boot keeps it so later projects can reuse shared steps). */
13
+ export interface ToolchainResult extends ToolEnv {
14
+ steps: Record<string, ToolEnv>;
11
15
  }
12
16
  /** Provisions every enabled toolchain and database, and returns the combined environment for the agent. */
13
- export declare const setupToolchains: (config: Toolchains, versions: ToolVersions, options: ToolchainOptions) => Promise<ToolEnv>;
17
+ export declare const setupToolchains: (config: Toolchains, versions: ToolVersions, options: ToolchainOptions, run?: RunOptions) => Promise<ToolchainResult>;