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,160 +1,77 @@
1
+ // Writes a launch folder for one sandbox: session.json (what the guest needs to know), the bundled runtime, hooks, the
2
+ // staged Claude settings and the .wsb file that starts Windows Sandbox. The pieces live in launch/.
1
3
  import fs from 'node:fs';
2
- import os from 'node:os';
3
4
  import path from 'node:path';
4
5
  import crypto from 'node:crypto';
5
- import { assertNoGuestOverlap, userDir } from '../core/config.js';
6
- import { capturePersonalization } from './personalize.js';
7
- import { assertSafeMapping } from '../core/workspace.js';
8
- import { isValidGuestPath, MIRROR_ROOT, sandboxFolder, substDrives } from '../shared/guest-paths.js';
9
- import { hostClaudeDir, stageClaudeProfile } from './claude-profile.js';
10
6
  import { decideToolchains, resolveToolchains } from '../core/detect-toolchains.js';
11
- import { databaseCatalog, msvcHint, pcLanguages, planDatabases, projectEntries } from './project-scan.js';
12
7
  import { readPwshScripts, usesPwsh } from '../core/detect-pwsh.js';
13
- import { dirExists, ensureDir, fail, fileExists, homeDir, writeJson, xmlEscape } from '../core/util.js';
14
- export const GUEST = { launch: 'C:\\AgentLaunch', data: 'C:\\AgentData', hostTools: 'C:\\HostTools', mirror: MIRROR_ROOT };
15
- const HOOK_STAGES = ['pre-provision', 'pre-agent', 'post-agent'];
16
- const toggle = (value) => (value ? 'Enable' : 'Disable');
17
- export const resolveMemoryMb = (sandbox, freeMemoryBytes = os.freemem()) => {
18
- if (sandbox.memoryMb !== 'auto')
19
- return sandbox.memoryMb;
20
- const freeMb = Math.floor(freeMemoryBytes / 1048576);
21
- const rounded = Math.floor(Math.min(sandbox.maxMemoryMb, freeMb - sandbox.reserveHostMb) / 1024) * 1024;
22
- if (rounded < 4096)
23
- fail('Not enough free RAM for the sandbox (' + freeMb + ' MB free, ' + sandbox.reserveHostMb + ' MB kept for this PC).', 'Close some apps, lower sandbox.reserveHostMb, or set sandbox.memoryMb to a fixed value (>= 4096).');
24
- return rounded;
25
- };
26
- export const buildWsb = (config, memoryMb, mappings) => {
27
- const s = config.sandbox;
28
- // Guest paths on other drive letters (F:\x) really live under C:\Mirror\F; bootstrap.ps1 substs F: onto it at boot.
29
- const folders = mappings.map((m) => '<MappedFolder><HostFolder>' + xmlEscape(m.host) + '</HostFolder><SandboxFolder>' + xmlEscape(sandboxFolder(m.guest)) + '</SandboxFolder><ReadOnly>' + String(m.readOnly) + '</ReadOnly></MappedFolder>').join('');
30
- return '<Configuration>'
31
- + '<MemoryInMB>' + memoryMb + '</MemoryInMB>'
32
- + '<vGPU>' + toggle(s.vGpu) + '</vGPU>'
33
- + '<Networking>' + toggle(s.networking) + '</Networking>'
34
- + '<ClipboardRedirection>' + toggle(s.clipboard) + '</ClipboardRedirection>'
35
- + '<AudioInput>' + toggle(s.audioInput) + '</AudioInput>'
36
- + '<VideoInput>' + toggle(s.videoInput) + '</VideoInput>'
37
- + '<PrinterRedirection>' + toggle(s.printerRedirection) + '</PrinterRedirection>'
38
- + '<ProtectedClient>' + toggle(s.protectedClient) + '</ProtectedClient>'
39
- + '<MappedFolders>' + folders + '</MappedFolders>'
40
- + '<LogonCommand><Command>' + xmlEscape(GUEST.launch + '\\sandbox\\logon.cmd') + '</Command></LogonCommand>'
41
- + '</Configuration>';
42
- };
43
- const collectMappings = (o, launchPath) => {
44
- const { config, dataPath, workspaceHost, harnessName } = o.run;
45
- assertGuestPath(config.guestWorkspace, 'guestWorkspace');
46
- const mappings = [
47
- { host: launchPath, guest: GUEST.launch, readOnly: true },
48
- { host: dataPath, guest: GUEST.data, readOnly: false },
49
- { host: workspaceHost, guest: config.guestWorkspace, readOnly: config.readOnly },
50
- ];
51
- for (const m of config.mappings) {
52
- const host = m.host;
53
- assertGuestPath(m.guest, 'mappings[].guest for ' + host);
54
- if (!dirExists(host)) {
55
- if (m.optional) {
56
- console.warn('Skipping optional mapping (folder not found): ' + host);
57
- continue;
58
- }
59
- fail('A mapped folder does not exist: ' + host, 'Create it, fix the path in your config, or mark the mapping "optional": true.');
60
- }
61
- assertSafeMapping(host, o.packageRoot, o.run.storageRoot);
62
- mappings.push({ host, guest: m.guest, readOnly: m.readOnly });
63
- }
64
- const { probe } = o;
65
- if (probe.nodeDir)
66
- mappings.push({ host: probe.nodeDir, guest: GUEST.hostTools + '\\node', readOnly: true });
67
- if (probe.harnessExe && harnessName !== 'none')
68
- mappings.push({ host: probe.harnessExe.dir, guest: GUEST.hostTools + '\\agents\\' + harnessName, readOnly: true });
69
- if (probe.gitDir)
70
- mappings.push({ host: probe.gitDir, guest: GUEST.hostTools + '\\git', readOnly: true });
71
- if (probe.nvmDir)
72
- mappings.push({ host: probe.nvmDir, guest: GUEST.hostTools + '\\nvm', readOnly: true });
73
- assertNoGuestOverlap(mappings);
74
- // Same check on where the folders really are (F:\x and C:\Mirror\F\x are one place).
75
- assertNoGuestOverlap(mappings.map((m) => ({ guest: sandboxFolder(m.guest) })));
76
- return mappings;
77
- };
78
- /** The schema checks this already; the host re-checks what goes into the .wsb and the subst list anyway. */
79
- const assertGuestPath = (guest, what) => {
80
- if (!isValidGuestPath(guest))
81
- fail(what + ' is not a usable sandbox folder: ' + guest, 'Use a folder under C:\\ (not Windows, Program Files, Users, AgentData, Mirror, ...) or on drive D: to Z:, e.g. C:\\Assets or F:\\SteamLibrary\\steamapps\\common\\Game.');
82
- };
83
- /** Package hooks first, then ~/.agent-sandbox/hooks (same file name = user's wins), then --script / "scripts". */
84
- const stageHooks = (packageRoot, launchPath, scripts) => {
85
- for (const stage of HOOK_STAGES) {
86
- const target = path.join(launchPath, 'hooks', stage);
87
- ensureDir(target);
88
- for (const source of [path.join(packageRoot, 'hooks', stage), path.join(userDir(), 'hooks', stage)]) {
89
- if (dirExists(source))
90
- fs.cpSync(source, target, { recursive: true, force: true });
91
- }
92
- }
93
- scripts.forEach((script, index) => {
94
- if (!fileExists(script))
95
- fail('Script file not found: ' + script, 'Check the --script path.');
96
- fs.copyFileSync(script, path.join(launchPath, 'hooks', 'pre-agent', '90-extra-' + index + '-' + path.basename(script)));
97
- });
98
- };
99
- export const RUNTIME_FILES = ['main.js', 'daemon.js', 'view.js', 'gitguard.js', 'db.js'];
100
- /** The bundled sandbox runtime (dist/runtime: one self-contained file per entry point) and the sandbox/ scripts. */
101
- const copyRuntime = (packageRoot, launchPath) => {
102
- const runtime = path.join(packageRoot, 'dist', 'runtime');
103
- const missing = RUNTIME_FILES.filter((f) => !fileExists(path.join(runtime, f)));
104
- if (missing.length)
105
- fail('The sandbox runtime is missing from ' + runtime + ' (' + missing.join(', ') + ').', 'Rebuild it: npm run build (or reinstall the package).');
106
- ensureDir(path.join(launchPath, 'runtime'));
107
- for (const file of RUNTIME_FILES)
108
- fs.copyFileSync(path.join(runtime, file), path.join(launchPath, 'runtime', file));
109
- fs.writeFileSync(path.join(launchPath, 'package.json'), '{ "type": "module" }\n', 'utf8');
110
- fs.cpSync(path.join(packageRoot, 'sandbox'), path.join(launchPath, 'sandbox'), { recursive: true });
111
- };
112
- /** Only the identity from ~/.gitconfig: the rest can hold credentials (helpers, tokens in insteadOf URLs). */
113
- export const gitIdentity = (gitconfig) => {
114
- const values = {};
115
- let section = '';
116
- for (const line of gitconfig.split(/\r?\n/)) {
117
- const header = /^\s*\[([^\]]+)\]/.exec(line);
118
- if (header) {
119
- section = header[1].trim().toLowerCase();
120
- continue;
121
- }
122
- const pair = /^\s*(name|email)\s*=\s*(.*?)\s*$/i.exec(line);
123
- if (section === 'user' && pair && !/[\r\n]/.test(pair[2]))
124
- values[pair[1].toLowerCase()] = pair[2].replace(/^"(.*)"$/, '$1');
125
- }
126
- const lines = Object.entries(values).map(([k, v]) => '\t' + k + ' = ' + v);
127
- return lines.length ? '[user]\n' + lines.join('\n') + '\n' : '';
8
+ import { dirExists, ensureDir, fail, writeJson } from '../core/util.js';
9
+ import { substDrives } from '../shared/guest-paths.js';
10
+ import { PROJECTS_GUEST, hubName } from '../shared/projects.js';
11
+ import { planBootProjects } from './launch/projects.js';
12
+ import { hostClaudeDir, stageClaudeProfile } from './claude-profile.js';
13
+ import { databaseCatalog, planDatabases } from './database-plan.js';
14
+ import { collectMappings } from './launch/mappings.js';
15
+ import { copyRuntime, stageGitIdentity, stageHooks } from './launch/stage.js';
16
+ import { buildWsb, resolveMemoryMb } from './launch/wsb.js';
17
+ import { capturePersonalization } from './personalize.js';
18
+ import { pcLanguages } from './pc-scan.js';
19
+ import { msvcHint, projectEntries } from './project-scan.js';
20
+ import { assertTestWritable } from './test-guard.js';
21
+ const NO_PROFILE = { hostDir: null, staged: [], skipped: [], credentials: false, secretsRemoved: [] };
22
+ /** Which toolchains the project needs ("auto" settings follow the project's files and what this PC has). */
23
+ export const decideProjectToolchains = (o) => {
24
+ const { config, workspaceHost } = o.run;
25
+ const msvcScript = config.toolchains.msvc.enabled === 'auto' ? msvcHint(workspaceHost) : undefined;
26
+ const pwshScript = config.toolchains.pwsh.enabled === 'auto' ? usesPwsh(readPwshScripts(workspaceHost)) : null;
27
+ const hints = { ...(msvcScript ? { msvc: msvcScript } : {}), ...(pwshScript ? { pwsh: pwshScript } : {}) };
28
+ return decideToolchains(config.toolchains, projectEntries(workspaceHost), o.pcLanguages ?? pcLanguages(), hints);
128
29
  };
30
+ /** What was found on this PC to copy instead of download. */
31
+ const hostToolsOf = (probe) => ({
32
+ node: Boolean(probe.nodeDir),
33
+ nodeVersion: probe.nodeDir || probe.nvmNode ? probe.hostNodeVersion.replace(/^v/, '') : null,
34
+ harnessExecutable: probe.harnessExe?.file ?? null,
35
+ git: Boolean(probe.gitDir),
36
+ nvm: Boolean(probe.nvmDir),
37
+ volta: Boolean(probe.volta),
38
+ pyenv: Boolean(probe.pyenv),
39
+ });
40
+ /**
41
+ * env.cmd, which logon.cmd calls before it opens Windows Terminal: Terminal expands "%AGENT_WORKSPACE%" (the starting
42
+ * folder of new tabs) from its own environment, and the runtime only sets the variable machine-wide after Terminal
43
+ * started. `%` is doubled for cmd; a Windows path holds no quotes.
44
+ */
45
+ export const logonEnv = (workspace) => '@set "AGENT_WORKSPACE=' + workspace.replace(/[\r\n"]/g, '').replace(/%/g, '%%') + '"\r\n';
129
46
  export const generateSandbox = (o) => {
130
47
  const { config, dataPath, workspaceHost, harnessName, storageRoot } = o.run;
131
48
  const harness = harnessName === 'none' ? null : config.harnesses[harnessName] ?? fail('Unknown agent: ' + harnessName, 'Configured agents: ' + Object.keys(config.harnesses).join(', '));
132
49
  if (!dirExists(workspaceHost))
133
50
  fail('The workspace folder does not exist: ' + workspaceHost, 'cd into the project first.');
51
+ assertTestWritable(storageRoot);
52
+ assertTestWritable(dataPath);
134
53
  // Checked before anything is written, so a refusal leaves nothing behind.
135
54
  const memoryMb = resolveMemoryMb(config.sandbox, o.freeMemoryBytes);
136
55
  const launchId = Date.now() + '-' + crypto.randomBytes(3).toString('hex');
137
56
  const launchPath = path.join(storageRoot, 'launches', launchId);
138
57
  ensureDir(launchPath);
139
58
  ensureDir(dataPath);
140
- const mappings = collectMappings(o, launchPath);
141
- const profile = harnessName === 'claude' ? stageClaudeProfile(config.claudeProfile, launchPath, workspaceHost, config.secrets) : { hostDir: null, staged: [], skipped: [], credentials: false, secretsRemoved: [] };
142
- const gitconfig = path.join(homeDir(), '.gitconfig');
143
- const identity = fileExists(gitconfig) ? gitIdentity(fs.readFileSync(gitconfig, 'utf8')) : '';
144
- if (identity) {
145
- ensureDir(path.join(launchPath, 'home'));
146
- fs.writeFileSync(path.join(launchPath, 'home', '.gitconfig'), identity, 'utf8');
147
- }
59
+ const boot = planBootProjects({ run: o.run, packageRoot: o.packageRoot, extraProjects: o.extraProjects, knownProjects: o.knownProjects });
60
+ const mappings = collectMappings(o, launchPath, boot);
61
+ const profile = harnessName === 'claude' ? stageClaudeProfile(config.claudeProfile, launchPath, workspaceHost, config.secrets) : NO_PROFILE;
62
+ stageGitIdentity(launchPath);
148
63
  const personalization = config.personalization.enabled ? capturePersonalization(launchPath) : 'disabled';
149
64
  const databases = o.databases ?? planDatabases(config.databases, workspaceHost);
150
- const msvcScript = config.toolchains.msvc.enabled === 'auto' ? msvcHint(workspaceHost) : undefined;
151
- const pwshScript = config.toolchains.pwsh.enabled === 'auto' ? usesPwsh(readPwshScripts(workspaceHost)) : null;
152
- const toolchainDecisions = decideToolchains(config.toolchains, projectEntries(workspaceHost), o.pcLanguages ?? pcLanguages(), { ...(msvcScript ? { msvc: msvcScript } : {}), ...(pwshScript ? { pwsh: pwshScript } : {}) });
65
+ const toolchainDecisions = decideProjectToolchains(o);
153
66
  const session = {
154
67
  launchId,
155
68
  project: path.basename(workspaceHost),
69
+ projectId: hubName(workspaceHost, []),
156
70
  workspace: config.guestWorkspace,
157
71
  workspaceHost,
72
+ projectsDir: PROJECTS_GUEST,
73
+ roots: boot.roots.map((r) => r.guest),
74
+ ...(o.autoSession === false ? { autoSession: false } : {}),
158
75
  harnessName,
159
76
  harness,
160
77
  harnessArguments: o.harnessArgs,
@@ -170,16 +87,22 @@ export const generateSandbox = (o) => {
170
87
  versions: o.run.versions,
171
88
  attach: config.attach,
172
89
  hooks: config.hooks,
173
- hostTools: { node: Boolean(o.probe.nodeDir), nodeVersion: o.probe.nodeDir || o.probe.nvmNode ? o.probe.hostNodeVersion.replace(/^v/, '') : null, harnessExecutable: o.probe.harnessExe?.file ?? null, git: Boolean(o.probe.gitDir), nvm: Boolean(o.probe.nvmDir), volta: Boolean(o.probe.volta), pyenv: Boolean(o.probe.pyenv) },
90
+ downloads: config.downloads,
91
+ hostTools: hostToolsOf(o.probe),
174
92
  claudeProfile: { overwrite: config.claudeProfile.overwrite, items: profile.staged, credentials: profile.credentials, hostConfigDir: profile.hostDir ?? hostClaudeDir(config.claudeProfile), syncClaudeMem: config.claudeProfile.syncClaudeMem },
175
93
  personalization: fs.existsSync(path.join(launchPath, 'personalize', 'personalize.reg')),
176
94
  substDrives: substDrives(mappings.map((m) => m.guest)),
95
+ cpuLimitPercent: config.sandbox.cpuLimit === 'none' ? null : config.sandbox.cpuLimit,
96
+ pageFileMb: config.sandbox.pageFileMb,
97
+ apps: config.apps,
98
+ auditProcesses: config.audit.enabled && config.audit.processes,
177
99
  };
178
100
  const sessionPath = path.join(launchPath, 'session.json');
179
101
  writeJson(sessionPath, session);
102
+ fs.writeFileSync(path.join(launchPath, 'env.cmd'), logonEnv(session.workspace), 'utf8');
180
103
  copyRuntime(o.packageRoot, launchPath);
181
104
  stageHooks(o.packageRoot, launchPath, o.run.scripts);
182
105
  const wsbPath = path.join(launchPath, 'agent.wsb');
183
106
  fs.writeFileSync(wsbPath, buildWsb(config, memoryMb, mappings), 'utf8');
184
- return { wsbPath, launchPath, launchId, sessionPath, memoryMb, mappings, profile, personalization, databases, toolchains: toolchainDecisions };
107
+ return { wsbPath, launchPath, launchId, sessionPath, memoryMb, mappings, profile, personalization, databases, toolchains: toolchainDecisions, projects: boot };
185
108
  };
@@ -0,0 +1,28 @@
1
+ import type { ResolvedRun } from '../core/types.js';
2
+ import { type ProjectSpecT } from '../shared/schema.js';
3
+ import { type ProjectMapper, type RegistryEntry } from './projects.js';
4
+ /** The launch folder of the running sandbox (host-only storage; its id comes from the guest, so it is checked). */
5
+ export declare const runningLaunchPath: (storageRoot: string, dataPath: string) => string | null;
6
+ /** What a session or command from this folder needs: where it is inside, and (for another project) its spec. */
7
+ export interface Joined {
8
+ project: RegistryEntry;
9
+ guestDir: string;
10
+ spec: ProjectSpecT | null;
11
+ run: ResolvedRun | null;
12
+ notes: string[];
13
+ }
14
+ /** The project spec the guest provisions from (scans the project folder like a launch does). */
15
+ export declare const projectSpec: (run: ResolvedRun, entry: RegistryEntry, hooks: boolean) => ProjectSpecT;
16
+ /**
17
+ * Makes `hostDir` usable in the running sandbox. The launch folder (or a folder below it) needs nothing; any other
18
+ * folder is resolved with `resolve` (the caller's resolveRun, with the approval prompt), checked, and registered.
19
+ */
20
+ export declare const joinRunning: (o: {
21
+ storageRoot: string;
22
+ dataPath: string;
23
+ packageRoot: string;
24
+ hostDir: string;
25
+ wsbShare: boolean;
26
+ resolve: (projectRoot: string) => Promise<ResolvedRun>;
27
+ mapper?: ProjectMapper;
28
+ }) => Promise<Joined>;
@@ -0,0 +1,76 @@
1
+ // Joining the running sandbox from another folder. The folder's run is resolved exactly as a fresh launch would
2
+ // resolve it (its approved .sandbox-config.json, versions, toolchain detection, database plan), checked against the
3
+ // running sandbox's machine-wide protections, made visible inside (host/projects.ts: a project root, a project
4
+ // mapped at boot, or wsb share) and described to the guest, which provisions what is missing before its session.
5
+ // When none of those can make it visible, NeedsRestart reaches the caller, which offers a restart.
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import { checkJoin, protectionsOf } from '../core/project-policy.js';
9
+ import { resolveToolchains } from '../core/detect-toolchains.js';
10
+ import { assertNoLinks, fail } from '../core/util.js';
11
+ import { Id } from '../shared/schema.js';
12
+ import { readSandboxStatus } from '../shared/rundir.js';
13
+ import { syncProjectIn } from '../sync/watcher.js';
14
+ import { planDatabases } from './database-plan.js';
15
+ import { decideProjectToolchains } from './generate.js';
16
+ import { projectHere, projectMapper, readRegistry, rememberProject, rootHere } from './projects.js';
17
+ import { stageScripts } from './launch/stage.js';
18
+ import { addRepoSnapshot } from './repo-guard.js';
19
+ /** The launch folder of the running sandbox (host-only storage; its id comes from the guest, so it is checked). */
20
+ export const runningLaunchPath = (storageRoot, dataPath) => {
21
+ const launchId = readSandboxStatus(dataPath)?.launchId;
22
+ if (!launchId || !/^\d+-[0-9a-f]{6}$/.test(launchId) || readRegistry(storageRoot)?.launchId !== launchId)
23
+ return null;
24
+ const launchPath = path.join(storageRoot, 'launches', launchId);
25
+ assertNoLinks(storageRoot, launchPath);
26
+ return fs.existsSync(path.join(launchPath, 'session.json')) ? launchPath : null;
27
+ };
28
+ /** The project spec the guest provisions from (scans the project folder like a launch does). */
29
+ export const projectSpec = (run, entry, hooks) => {
30
+ const { config } = run;
31
+ const toolchains = resolveToolchains(config.toolchains, decideProjectToolchains({ run }));
32
+ const harness = run.harnessName === 'none' ? null : config.harnesses[run.harnessName];
33
+ return {
34
+ name: Id.parse(entry.name), workspace: entry.guest, workspaceHost: entry.host, toolchains, versions: run.versions,
35
+ databases: planDatabases(config.databases, run.workspaceHost).slice(0, 8),
36
+ harnesses: harness ? { [run.harnessName]: harness } : {},
37
+ hooks,
38
+ };
39
+ };
40
+ /**
41
+ * Makes `hostDir` usable in the running sandbox. The launch folder (or a folder below it) needs nothing; any other
42
+ * folder is resolved with `resolve` (the caller's resolveRun, with the approval prompt), checked, and registered.
43
+ */
44
+ export const joinRunning = async (o) => {
45
+ const here = projectHere(o.storageRoot, o.hostDir);
46
+ if (here?.project.primary)
47
+ return { ...here, spec: null, run: null, notes: [] };
48
+ const registry = readRegistry(o.storageRoot) ?? fail('The running sandbox was started by an older version, so it cannot take another folder.', 'Stop it (sandbox stop) and start it again.');
49
+ // A folder below a project already in the sandbox uses that project; anything else becomes a project of its own.
50
+ const root = here?.project.host ?? path.resolve(o.hostDir);
51
+ const run = await o.resolve(root);
52
+ const check = checkJoin(registry.protections, protectionsOf(run.config));
53
+ if (check.stricter.length) {
54
+ fail('This project needs stricter protection than the running sandbox has, and those settings are machine-wide:\n ' + check.stricter.join('\n '), 'Its sessions cannot get them without changing them for the sessions already running. Stop the sandbox (sandbox stop) and start it from ' + root + '.');
55
+ }
56
+ const mapper = o.mapper ?? projectMapper({ storageRoot: o.storageRoot, packageRoot: o.packageRoot, wsbShare: o.wsbShare });
57
+ // Guarded from now on. A folder the sandbox could already write (inside a root, or mapped at boot) and that has no
58
+ // snapshot from the launch gets an empty one: what is there now may have been planted, so it counts as new.
59
+ // Anything else is snapshotted before it becomes visible.
60
+ addRepoSnapshot(o.dataPath, root, { empty: Boolean(here) || Boolean(rootHere(o.storageRoot, root)) });
61
+ const entry = mapper.register(root);
62
+ rememberProject(o.storageRoot, entry.host);
63
+ const launchPath = runningLaunchPath(o.storageRoot, o.dataPath);
64
+ // Your hooks in ~/.agent-sandbox/hooks ran at boot (once per sandbox); the project's own scripts ("scripts" in its
65
+ // .sandbox-config.json, --script) run before its first agent. The launch folder is read-only for the sandbox, but
66
+ // this side can still add them to it.
67
+ const hooks = Boolean(launchPath) && run.scripts.length > 0;
68
+ if (launchPath && hooks)
69
+ stageScripts(path.join(launchPath, 'projects', entry.name, 'hooks', 'pre-agent'), run.scripts);
70
+ try {
71
+ syncProjectIn(o.dataPath, { hostWorkspace: entry.host, guestWorkspace: entry.guest });
72
+ }
73
+ catch { /* history is a convenience; never block the session */ }
74
+ const notes = [...run.notes, ...check.looser.map((l) => 'The running sandbox is stricter: ' + l + '.')];
75
+ return { project: entry, guestDir: here ? here.guestDir : entry.guest, spec: projectSpec(run, entry, hooks), run, notes };
76
+ };
@@ -0,0 +1,3 @@
1
+ import type { GenerateOptions, ResolvedMapping } from '../../core/types.js';
2
+ import type { BootProjects } from './projects.js';
3
+ export declare const collectMappings: (o: GenerateOptions, launchPath: string, boot?: BootProjects) => ResolvedMapping[];
@@ -0,0 +1,54 @@
1
+ import { assertNoGuestOverlap } from '../../core/config.js';
2
+ import { assertSafeMapping } from '../../core/safe-paths.js';
3
+ import { dirExists, fail } from '../../core/util.js';
4
+ import { isValidGuestPath, sandboxFolder } from '../../shared/guest-paths.js';
5
+ import { GUEST } from './wsb.js';
6
+ import { guestGitDir, planView } from '../workspace-view/index.js';
7
+ export const collectMappings = (o, launchPath, boot = { roots: [], projects: [], notes: [] }) => {
8
+ const { config, dataPath, workspaceHost, harnessName } = o.run;
9
+ assertGuestPath(config.guestWorkspace, 'guestWorkspace');
10
+ // workspaceFilter: the sandbox gets the project's filtered view (no git-ignored files) and its real .git beside it.
11
+ const view = planView(config, o.run.storageRoot, workspaceHost);
12
+ const mappings = [
13
+ { host: launchPath, guest: GUEST.launch, readOnly: true },
14
+ { host: dataPath, guest: GUEST.data, readOnly: false },
15
+ { host: view ? view.options.view : workspaceHost, guest: config.guestWorkspace, readOnly: config.readOnly },
16
+ ...(view?.hostGit ? [{ host: view.hostGit, guest: guestGitDir(config.guestWorkspace), readOnly: config.readOnly }] : []),
17
+ ];
18
+ for (const m of config.mappings) {
19
+ const host = m.host;
20
+ assertGuestPath(m.guest, 'mappings[].guest for ' + host);
21
+ if (!dirExists(host)) {
22
+ if (m.optional) {
23
+ console.warn('Skipping optional mapping (folder not found): ' + host);
24
+ continue;
25
+ }
26
+ fail('A mapped folder does not exist: ' + host, 'Create it, fix the path in your config, or mark the mapping "optional": true.');
27
+ }
28
+ assertSafeMapping(host, o.packageRoot, o.run.storageRoot);
29
+ mappings.push({ host, guest: m.guest, readOnly: m.readOnly });
30
+ }
31
+ // Project roots and other projects, so they can join the running sandbox without a restart (launch/projects.ts).
32
+ for (const m of [...boot.roots, ...boot.projects]) {
33
+ assertGuestPath(m.guest, 'project folder for ' + m.host);
34
+ mappings.push({ host: m.host, guest: m.guest, readOnly: false });
35
+ }
36
+ const { probe } = o;
37
+ if (probe.nodeDir)
38
+ mappings.push({ host: probe.nodeDir, guest: GUEST.hostTools + '\\node', readOnly: true });
39
+ if (probe.harnessExe && harnessName !== 'none')
40
+ mappings.push({ host: probe.harnessExe.dir, guest: GUEST.hostTools + '\\agents\\' + harnessName, readOnly: true });
41
+ if (probe.gitDir)
42
+ mappings.push({ host: probe.gitDir, guest: GUEST.hostTools + '\\git', readOnly: true });
43
+ if (probe.nvmDir)
44
+ mappings.push({ host: probe.nvmDir, guest: GUEST.hostTools + '\\nvm', readOnly: true });
45
+ assertNoGuestOverlap(mappings);
46
+ // Same check on where the folders really are (F:\x and C:\Mirror\F\x are one place).
47
+ assertNoGuestOverlap(mappings.map((m) => ({ guest: sandboxFolder(m.guest) })));
48
+ return mappings;
49
+ };
50
+ /** The schema checks this already; the host re-checks what goes into the .wsb and the subst list anyway. */
51
+ const assertGuestPath = (guest, what) => {
52
+ if (!isValidGuestPath(guest))
53
+ fail(what + ' is not a usable sandbox folder: ' + guest, 'Use a folder under C:\\ (not Windows, Program Files, Users, AgentData, Mirror, ...) or on drive D: to Z:, e.g. C:\\Assets or F:\\SteamLibrary\\steamapps\\common\\Game.');
54
+ };
@@ -0,0 +1,15 @@
1
+ import type { ResolvedRun } from '../../core/types.js';
2
+ import { type RootMapping } from '../../shared/projects.js';
3
+ /** More folders than this would make the .wsb (and every boot) unwieldy; the rest join with a restart later. */
4
+ export declare const MAX_BOOT_PROJECTS = 48;
5
+ export interface BootProjects {
6
+ roots: RootMapping[];
7
+ projects: RootMapping[];
8
+ notes: string[];
9
+ }
10
+ export declare const planBootProjects: (o: {
11
+ run: ResolvedRun;
12
+ packageRoot: string;
13
+ extraProjects?: string[];
14
+ knownProjects?: string[];
15
+ }) => BootProjects;
@@ -0,0 +1,56 @@
1
+ // Which folders besides the launch folder are mapped so other projects can join without a restart: the project
2
+ // roots (projects.roots), projects carried over by a restart that added one, and with projects.all every project
3
+ // started before (except projects.exclude). Every one passes the same checks as a launch folder.
4
+ import path from 'node:path';
5
+ import { assertSafeMapping, assertSafeWorkspace } from '../../core/safe-paths.js';
6
+ import { dirExists, errMsg, fail } from '../../core/util.js';
7
+ import { isWithin, planProjectMappings } from '../../shared/projects.js';
8
+ import { allProjectsToMap, readKnown } from '../projects.js';
9
+ /** More folders than this would make the .wsb (and every boot) unwieldy; the rest join with a restart later. */
10
+ export const MAX_BOOT_PROJECTS = 48;
11
+ export const planBootProjects = (o) => {
12
+ const { config, workspaceHost, storageRoot } = o.run;
13
+ const notes = [];
14
+ const safe = (folder) => {
15
+ try {
16
+ assertSafeWorkspace(folder, o.packageRoot, storageRoot);
17
+ assertSafeMapping(folder, o.packageRoot, storageRoot);
18
+ return null;
19
+ }
20
+ catch (e) {
21
+ return errMsg(e);
22
+ }
23
+ };
24
+ const roots = [];
25
+ for (const root of config.projects.roots.map((r) => path.resolve(r))) {
26
+ if (!dirExists(root)) {
27
+ notes.push('Project root not found, skipped: ' + root);
28
+ continue;
29
+ }
30
+ const refusal = safe(root);
31
+ if (refusal)
32
+ fail('projects.roots: ' + refusal, 'Use a folder that holds your projects, e.g. F:\\Code (not a drive, your profile or a system folder).');
33
+ if (!roots.some((r) => isWithin(r, root)))
34
+ roots.push(root);
35
+ }
36
+ const known = o.knownProjects ?? readKnown(storageRoot).map((k) => k.host);
37
+ const wanted = [...(o.extraProjects ?? []), ...(config.projects.all ? allProjectsToMap(known, { primary: workspaceHost, roots, exclude: config.projects.exclude }) : [])];
38
+ const projects = [];
39
+ for (const folder of wanted.map((p) => path.resolve(p))) {
40
+ if (isWithin(workspaceHost, folder) || roots.some((r) => isWithin(r, folder)) || projects.some((p) => isWithin(p, folder) || isWithin(folder, p)))
41
+ continue;
42
+ if (!dirExists(folder))
43
+ continue;
44
+ const refusal = safe(folder);
45
+ if (refusal) {
46
+ notes.push('Not mapped: ' + refusal);
47
+ continue;
48
+ }
49
+ if (projects.length >= MAX_BOOT_PROJECTS) {
50
+ notes.push('More than ' + MAX_BOOT_PROJECTS + ' projects: the rest are not mapped (a project root holds any number).');
51
+ break;
52
+ }
53
+ projects.push(folder);
54
+ }
55
+ return { ...planProjectMappings(roots, projects, Boolean(o.run.mirror)), notes };
56
+ };
@@ -0,0 +1,11 @@
1
+ /** Package hooks first, then ~/.agent-sandbox/hooks (same file name = user's wins), then --script / "scripts". */
2
+ export declare const stageHooks: (packageRoot: string, launchPath: string, scripts: string[]) => void;
3
+ /** --script / "scripts" files, run before the agent in this order. */
4
+ export declare const stageScripts: (target: string, scripts: string[]) => void;
5
+ export declare const RUNTIME_FILES: string[];
6
+ /** The bundled sandbox runtime (dist/runtime: one self-contained file per entry point) and the sandbox/ scripts. */
7
+ export declare const copyRuntime: (packageRoot: string, launchPath: string) => void;
8
+ /** Only the identity from ~/.gitconfig: the rest can hold credentials (helpers, tokens in insteadOf URLs). */
9
+ export declare const gitIdentity: (gitconfig: string) => string;
10
+ /** Writes this PC's git name and email to home\.gitconfig in the launch folder (nothing when there are none). */
11
+ export declare const stageGitIdentity: (launchPath: string) => void;
@@ -0,0 +1,66 @@
1
+ // What is copied into the launch folder (C:\AgentLaunch inside the sandbox): hooks, the bundled runtime, the git identity.
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { userDir } from '../../core/config.js';
5
+ import { dirExists, ensureDir, fail, fileExists, homeDir } from '../../core/util.js';
6
+ const HOOK_STAGES = ['pre-provision', 'pre-agent', 'post-agent'];
7
+ /** Package hooks first, then ~/.agent-sandbox/hooks (same file name = user's wins), then --script / "scripts". */
8
+ export const stageHooks = (packageRoot, launchPath, scripts) => {
9
+ for (const stage of HOOK_STAGES) {
10
+ const target = path.join(launchPath, 'hooks', stage);
11
+ ensureDir(target);
12
+ for (const source of [path.join(packageRoot, 'hooks', stage), path.join(userDir(), 'hooks', stage)]) {
13
+ if (dirExists(source))
14
+ fs.cpSync(source, target, { recursive: true, force: true });
15
+ }
16
+ }
17
+ stageScripts(path.join(launchPath, 'hooks', 'pre-agent'), scripts);
18
+ };
19
+ /** --script / "scripts" files, run before the agent in this order. */
20
+ export const stageScripts = (target, scripts) => {
21
+ ensureDir(target);
22
+ scripts.forEach((script, index) => {
23
+ if (!fileExists(script))
24
+ fail('Script file not found: ' + script, 'Check the --script path.');
25
+ fs.copyFileSync(script, path.join(target, '90-extra-' + index + '-' + path.basename(script)));
26
+ });
27
+ };
28
+ export const RUNTIME_FILES = ['main.js', 'daemon.js', 'view.js', 'gitguard.js', 'db.js', 'netproxy.js'];
29
+ /** The bundled sandbox runtime (dist/runtime: one self-contained file per entry point) and the sandbox/ scripts. */
30
+ export const copyRuntime = (packageRoot, launchPath) => {
31
+ const runtime = path.join(packageRoot, 'dist', 'runtime');
32
+ const missing = RUNTIME_FILES.filter((f) => !fileExists(path.join(runtime, f)));
33
+ if (missing.length)
34
+ fail('The sandbox runtime is missing from ' + runtime + ' (' + missing.join(', ') + ').', 'Rebuild it: npm run build (or reinstall the package).');
35
+ ensureDir(path.join(launchPath, 'runtime'));
36
+ for (const file of RUNTIME_FILES)
37
+ fs.copyFileSync(path.join(runtime, file), path.join(launchPath, 'runtime', file));
38
+ fs.writeFileSync(path.join(launchPath, 'package.json'), '{ "type": "module" }\n', 'utf8');
39
+ fs.cpSync(path.join(packageRoot, 'sandbox'), path.join(launchPath, 'sandbox'), { recursive: true });
40
+ };
41
+ /** Only the identity from ~/.gitconfig: the rest can hold credentials (helpers, tokens in insteadOf URLs). */
42
+ export const gitIdentity = (gitconfig) => {
43
+ const values = {};
44
+ let section = '';
45
+ for (const line of gitconfig.split(/\r?\n/)) {
46
+ const header = /^\s*\[([^\]]+)\]/.exec(line);
47
+ if (header) {
48
+ section = header[1].trim().toLowerCase();
49
+ continue;
50
+ }
51
+ const pair = /^\s*(name|email)\s*=\s*(.*?)\s*$/i.exec(line);
52
+ if (section === 'user' && pair && !/[\r\n]/.test(pair[2]))
53
+ values[pair[1].toLowerCase()] = pair[2].replace(/^"(.*)"$/, '$1');
54
+ }
55
+ const lines = Object.entries(values).map(([k, v]) => '\t' + k + ' = ' + v);
56
+ return lines.length ? '[user]\n' + lines.join('\n') + '\n' : '';
57
+ };
58
+ /** Writes this PC's git name and email to home\.gitconfig in the launch folder (nothing when there are none). */
59
+ export const stageGitIdentity = (launchPath) => {
60
+ const gitconfig = path.join(homeDir(), '.gitconfig');
61
+ const identity = fileExists(gitconfig) ? gitIdentity(fs.readFileSync(gitconfig, 'utf8')) : '';
62
+ if (!identity)
63
+ return;
64
+ ensureDir(path.join(launchPath, 'home'));
65
+ fs.writeFileSync(path.join(launchPath, 'home', '.gitconfig'), identity, 'utf8');
66
+ };
@@ -0,0 +1,10 @@
1
+ import type { Config, ResolvedMapping } from '../../core/types.js';
2
+ export declare const GUEST: {
3
+ readonly launch: "C:\\AgentLaunch";
4
+ readonly data: "C:\\AgentData";
5
+ readonly hostTools: "C:\\HostTools";
6
+ readonly mirror: "C:\\Mirror";
7
+ };
8
+ /** "auto": free RAM minus the reserve, but never more than maxMemoryMb or maxMemoryPercent of the PC's total RAM. */
9
+ export declare const resolveMemoryMb: (sandbox: Config["sandbox"], freeMemoryBytes?: number, totalMemoryBytes?: number) => number;
10
+ export declare const buildWsb: (config: Config, memoryMb: number, mappings: ResolvedMapping[]) => string;
@@ -0,0 +1,34 @@
1
+ // The .wsb file Windows Sandbox starts from: memory, devices and the folders mapped in.
2
+ import os from 'node:os';
3
+ import { fail, xmlEscape } from '../../core/util.js';
4
+ import { MIRROR_ROOT, sandboxFolder } from '../../shared/guest-paths.js';
5
+ export const GUEST = { launch: 'C:\\AgentLaunch', data: 'C:\\AgentData', hostTools: 'C:\\HostTools', mirror: MIRROR_ROOT };
6
+ const toggle = (value) => (value ? 'Enable' : 'Disable');
7
+ /** "auto": free RAM minus the reserve, but never more than maxMemoryMb or maxMemoryPercent of the PC's total RAM. */
8
+ export const resolveMemoryMb = (sandbox, freeMemoryBytes = os.freemem(), totalMemoryBytes = os.totalmem()) => {
9
+ if (sandbox.memoryMb !== 'auto')
10
+ return sandbox.memoryMb;
11
+ const freeMb = Math.floor(freeMemoryBytes / 1048576);
12
+ const shareMb = Math.floor((totalMemoryBytes / 1048576) * sandbox.maxMemoryPercent / 100);
13
+ const rounded = Math.floor(Math.min(sandbox.maxMemoryMb, shareMb, freeMb - sandbox.reserveHostMb) / 1024) * 1024;
14
+ if (rounded < 4096)
15
+ fail('Not enough free RAM for the sandbox (' + freeMb + ' MB free, ' + sandbox.reserveHostMb + ' MB kept for this PC).', 'Close some apps, lower sandbox.reserveHostMb, or set sandbox.memoryMb to a fixed value (>= 4096).');
16
+ return rounded;
17
+ };
18
+ export const buildWsb = (config, memoryMb, mappings) => {
19
+ const s = config.sandbox;
20
+ // Guest paths on other drive letters (F:\x) really live under C:\Mirror\F; bootstrap.ps1 substs F: onto it at boot.
21
+ const folders = mappings.map((m) => '<MappedFolder><HostFolder>' + xmlEscape(m.host) + '</HostFolder><SandboxFolder>' + xmlEscape(sandboxFolder(m.guest)) + '</SandboxFolder><ReadOnly>' + String(m.readOnly) + '</ReadOnly></MappedFolder>').join('');
22
+ return '<Configuration>'
23
+ + '<MemoryInMB>' + memoryMb + '</MemoryInMB>'
24
+ + '<vGPU>' + toggle(s.vGpu) + '</vGPU>'
25
+ + '<Networking>' + toggle(s.networking) + '</Networking>'
26
+ + '<ClipboardRedirection>' + toggle(s.clipboard) + '</ClipboardRedirection>'
27
+ + '<AudioInput>' + toggle(s.audioInput) + '</AudioInput>'
28
+ + '<VideoInput>' + toggle(s.videoInput) + '</VideoInput>'
29
+ + '<PrinterRedirection>' + toggle(s.printerRedirection) + '</PrinterRedirection>'
30
+ + '<ProtectedClient>' + toggle(s.protectedClient) + '</ProtectedClient>'
31
+ + '<MappedFolders>' + folders + '</MappedFolders>'
32
+ + '<LogonCommand><Command>' + xmlEscape(GUEST.launch + '\\sandbox\\logon.cmd') + '</Command></LogonCommand>'
33
+ + '</Configuration>';
34
+ };
@@ -0,0 +1,17 @@
1
+ /** What Windows runs when you open it (not source files such as .js or .py: those need a program you choose). */
2
+ export declare const RISKY: RegExp;
3
+ /** Marks one file if it is a program or script (and a plain file, not a link). Returns true when it marked it. */
4
+ export declare const markFromSandbox: (file: string) => boolean;
5
+ /** Has this file the mark? */
6
+ export declare const isMarked: (file: string) => boolean;
7
+ /** Watches the projects while the sandbox runs and marks what appears or changes; returns a function that stops it. */
8
+ export declare const watchAndMark: (projects: string[], note?: (message: string) => void) => (() => void);
9
+ /** The projects to watch and mark (empty: storage.markSandboxFiles is off). */
10
+ export declare const saveMarkedProjects: (storageRoot: string, dataPath: string, projects: string[]) => void;
11
+ export declare const readMarkedProjects: (storageRoot: string, dataPath: string) => string[];
12
+ /** The data folder's size, measured by the watcher when a sandbox closes (measuring at launch would be slow). */
13
+ export declare const saveDataSize: (storageRoot: string, dataPath: string, bytes: number) => void;
14
+ export declare const readDataSize: (storageRoot: string, dataPath: string) => {
15
+ bytes: number;
16
+ at: string;
17
+ } | null;