windows-development-sandbox 0.0.0-stage → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (426) hide show
  1. package/.sandbox-config.example.json +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +94 -2
  4. package/config.example.json +349 -0
  5. package/dist/api.d.ts +31 -0
  6. package/dist/api.js +31 -0
  7. package/dist/bin/agent-sandbox.d.ts +2 -0
  8. package/dist/bin/agent-sandbox.js +3 -0
  9. package/dist/bin/claude-host.d.ts +2 -0
  10. package/dist/bin/claude-host.js +5 -0
  11. package/dist/bin/claude-sbx.d.ts +2 -0
  12. package/dist/bin/claude-sbx.js +3 -0
  13. package/dist/bin/codex-sbx.d.ts +2 -0
  14. package/dist/bin/codex-sbx.js +3 -0
  15. package/dist/bin/gemini-sbx.d.ts +2 -0
  16. package/dist/bin/gemini-sbx.js +3 -0
  17. package/dist/bin/sandbox.d.ts +2 -0
  18. package/dist/bin/sandbox.js +4 -0
  19. package/dist/bin/sbx-hide.d.ts +2 -0
  20. package/dist/bin/sbx-hide.js +3 -0
  21. package/dist/bin/sbx-show.d.ts +2 -0
  22. package/dist/bin/sbx-show.js +3 -0
  23. package/dist/cli/args.d.ts +52 -0
  24. package/dist/cli/args.js +143 -0
  25. package/dist/cli/colors.d.ts +24 -0
  26. package/dist/cli/colors.js +42 -0
  27. package/dist/cli/commands/accounts.d.ts +2 -0
  28. package/dist/cli/commands/accounts.js +52 -0
  29. package/dist/cli/commands/agent-commands.d.ts +6 -0
  30. package/dist/cli/commands/agent-commands.js +61 -0
  31. package/dist/cli/commands/context.d.ts +23 -0
  32. package/dist/cli/commands/context.js +57 -0
  33. package/dist/cli/commands/data/clean.d.ts +15 -0
  34. package/dist/cli/commands/data/clean.js +101 -0
  35. package/dist/cli/commands/data/collect.d.ts +11 -0
  36. package/dist/cli/commands/data/collect.js +54 -0
  37. package/dist/cli/commands/data/index.d.ts +4 -0
  38. package/dist/cli/commands/data/index.js +45 -0
  39. package/dist/cli/commands/data/remove.d.ts +2 -0
  40. package/dist/cli/commands/data/remove.js +30 -0
  41. package/dist/cli/commands/index.d.ts +10 -0
  42. package/dist/cli/commands/index.js +10 -0
  43. package/dist/cli/commands/launch.d.ts +29 -0
  44. package/dist/cli/commands/launch.js +333 -0
  45. package/dist/cli/commands/maintenance.d.ts +8 -0
  46. package/dist/cli/commands/maintenance.js +154 -0
  47. package/dist/cli/commands/project.d.ts +29 -0
  48. package/dist/cli/commands/project.js +63 -0
  49. package/dist/cli/commands/projects.d.ts +14 -0
  50. package/dist/cli/commands/projects.js +146 -0
  51. package/dist/cli/commands/repair/checks.d.ts +2 -0
  52. package/dist/cli/commands/repair/checks.js +170 -0
  53. package/dist/cli/commands/repair/finders.d.ts +30 -0
  54. package/dist/cli/commands/repair/finders.js +108 -0
  55. package/dist/cli/commands/repair/index.d.ts +12 -0
  56. package/dist/cli/commands/repair/index.js +61 -0
  57. package/dist/cli/commands/repair/types.d.ts +26 -0
  58. package/dist/cli/commands/repair/types.js +3 -0
  59. package/dist/cli/commands/restart.d.ts +6 -0
  60. package/dist/cli/commands/restart.js +85 -0
  61. package/dist/cli/commands/sessions.d.ts +12 -0
  62. package/dist/cli/commands/sessions.js +257 -0
  63. package/dist/cli/commands/setup/checks.d.ts +13 -0
  64. package/dist/cli/commands/setup/checks.js +71 -0
  65. package/dist/cli/commands/setup/facts.d.ts +31 -0
  66. package/dist/cli/commands/setup/facts.js +60 -0
  67. package/dist/cli/commands/setup/index.d.ts +2 -0
  68. package/dist/cli/commands/setup/index.js +43 -0
  69. package/dist/cli/commands/user-config.d.ts +8 -0
  70. package/dist/cli/commands/user-config.js +41 -0
  71. package/dist/cli/history.d.ts +11 -0
  72. package/dist/cli/history.js +21 -0
  73. package/dist/cli/index.d.ts +20 -0
  74. package/dist/cli/index.js +121 -0
  75. package/dist/cli/manifest.d.ts +103 -0
  76. package/dist/cli/manifest.js +107 -0
  77. package/dist/cli/menu-multi.d.ts +12 -0
  78. package/dist/cli/menu-multi.js +82 -0
  79. package/dist/cli/menu.d.ts +14 -0
  80. package/dist/cli/menu.js +290 -0
  81. package/dist/cli/progress.d.ts +9 -0
  82. package/dist/cli/progress.js +49 -0
  83. package/dist/cli/settings/edit.d.ts +13 -0
  84. package/dist/cli/settings/edit.js +19 -0
  85. package/dist/cli/settings/index.d.ts +8 -0
  86. package/dist/cli/settings/index.js +131 -0
  87. package/dist/cli/settings/prompts.d.ts +7 -0
  88. package/dist/cli/settings/prompts.js +48 -0
  89. package/dist/cli/settings/schema.d.ts +33 -0
  90. package/dist/cli/settings/schema.js +29 -0
  91. package/dist/cli/validate.d.ts +17 -0
  92. package/dist/cli/validate.js +89 -0
  93. package/dist/core/config.d.ts +17 -0
  94. package/dist/core/config.js +106 -0
  95. package/dist/core/detect-databases.d.ts +15 -0
  96. package/dist/core/detect-databases.js +147 -0
  97. package/dist/core/detect-pwsh.d.ts +18 -0
  98. package/dist/core/detect-pwsh.js +111 -0
  99. package/dist/core/detect-toolchains.d.ts +30 -0
  100. package/dist/core/detect-toolchains.js +58 -0
  101. package/dist/core/disk.d.ts +16 -0
  102. package/dist/core/disk.js +55 -0
  103. package/dist/core/local-config.d.ts +20 -0
  104. package/dist/core/local-config.js +77 -0
  105. package/dist/core/project-policy.d.ts +15 -0
  106. package/dist/core/project-policy.js +35 -0
  107. package/dist/core/prompt.d.ts +4 -0
  108. package/dist/core/prompt.js +15 -0
  109. package/dist/core/safe-paths.d.ts +4 -0
  110. package/dist/core/safe-paths.js +37 -0
  111. package/dist/core/schema/common.d.ts +17 -0
  112. package/dist/core/schema/common.js +15 -0
  113. package/dist/core/schema/config.d.ts +594 -0
  114. package/dist/core/schema/config.js +63 -0
  115. package/dist/core/schema/databases.d.ts +33 -0
  116. package/dist/core/schema/databases.js +35 -0
  117. package/dist/core/schema/harness.d.ts +12 -0
  118. package/dist/core/schema/harness.js +17 -0
  119. package/dist/core/schema/json-schema.d.ts +5 -0
  120. package/dist/core/schema/json-schema.js +25 -0
  121. package/dist/core/schema/profile.d.ts +27 -0
  122. package/dist/core/schema/profile.js +29 -0
  123. package/dist/core/schema/projects.d.ts +9 -0
  124. package/dist/core/schema/projects.js +11 -0
  125. package/dist/core/schema/sandbox.d.ts +50 -0
  126. package/dist/core/schema/sandbox.js +36 -0
  127. package/dist/core/schema/toolchains.d.ts +93 -0
  128. package/dist/core/schema/toolchains.js +83 -0
  129. package/dist/core/schema.d.ts +6 -0
  130. package/dist/core/schema.js +8 -0
  131. package/dist/core/types.d.ts +190 -0
  132. package/dist/core/types.js +1 -0
  133. package/dist/core/util.d.ts +60 -0
  134. package/dist/core/util.js +154 -0
  135. package/dist/core/workspace.d.ts +18 -0
  136. package/dist/core/workspace.js +128 -0
  137. package/dist/guest/boot-env.d.ts +4 -0
  138. package/dist/guest/boot-env.js +25 -0
  139. package/dist/guest/cmdline.d.ts +12 -0
  140. package/dist/guest/cmdline.js +23 -0
  141. package/dist/guest/daemon/exec.d.ts +5 -0
  142. package/dist/guest/daemon/exec.js +32 -0
  143. package/dist/guest/daemon/project.d.ts +14 -0
  144. package/dist/guest/daemon/project.js +132 -0
  145. package/dist/guest/daemon/session.d.ts +13 -0
  146. package/dist/guest/daemon/session.js +139 -0
  147. package/dist/guest/daemon/shutdown.d.ts +1 -0
  148. package/dist/guest/daemon/shutdown.js +21 -0
  149. package/dist/guest/daemon/state.d.ts +17 -0
  150. package/dist/guest/daemon/state.js +31 -0
  151. package/dist/guest/daemon.d.ts +1 -0
  152. package/dist/guest/daemon.js +53 -0
  153. package/dist/guest/db.d.ts +1 -0
  154. package/dist/guest/db.js +75 -0
  155. package/dist/guest/env.d.ts +51 -0
  156. package/dist/guest/env.js +101 -0
  157. package/dist/guest/gitguard.d.ts +7 -0
  158. package/dist/guest/gitguard.js +60 -0
  159. package/dist/guest/gitrules.d.ts +2 -0
  160. package/dist/guest/gitrules.js +11 -0
  161. package/dist/guest/guide.d.ts +16 -0
  162. package/dist/guest/guide.js +69 -0
  163. package/dist/guest/helpers.d.ts +3 -0
  164. package/dist/guest/helpers.js +26 -0
  165. package/dist/guest/main.d.ts +1 -0
  166. package/dist/guest/main.js +130 -0
  167. package/dist/guest/ports.d.ts +6 -0
  168. package/dist/guest/ports.js +22 -0
  169. package/dist/guest/project-context.d.ts +7 -0
  170. package/dist/guest/project-context.js +10 -0
  171. package/dist/guest/project-env.d.ts +20 -0
  172. package/dist/guest/project-env.js +20 -0
  173. package/dist/guest/project-status.d.ts +5 -0
  174. package/dist/guest/project-status.js +17 -0
  175. package/dist/guest/ptyhost.d.ts +22 -0
  176. package/dist/guest/ptyhost.js +36 -0
  177. package/dist/guest/setup/agents.d.ts +11 -0
  178. package/dist/guest/setup/agents.js +87 -0
  179. package/dist/guest/setup/claude.d.ts +21 -0
  180. package/dist/guest/setup/claude.js +146 -0
  181. package/dist/guest/setup/environment.d.ts +16 -0
  182. package/dist/guest/setup/environment.js +69 -0
  183. package/dist/guest/setup/hooks.d.ts +9 -0
  184. package/dist/guest/setup/hooks.js +50 -0
  185. package/dist/guest/setup/network.d.ts +10 -0
  186. package/dist/guest/setup/network.js +99 -0
  187. package/dist/guest/setup/policy.d.ts +3 -0
  188. package/dist/guest/setup/policy.js +18 -0
  189. package/dist/guest/setup/user.d.ts +7 -0
  190. package/dist/guest/setup/user.js +47 -0
  191. package/dist/guest/toolchains/catalogue.d.ts +55 -0
  192. package/dist/guest/toolchains/catalogue.js +31 -0
  193. package/dist/guest/toolchains/cli.d.ts +10 -0
  194. package/dist/guest/toolchains/cli.js +72 -0
  195. package/dist/guest/toolchains/common.d.ts +54 -0
  196. package/dist/guest/toolchains/common.js +147 -0
  197. package/dist/guest/toolchains/databases/dynamodb.d.ts +8 -0
  198. package/dist/guest/toolchains/databases/dynamodb.js +52 -0
  199. package/dist/guest/toolchains/databases/index.d.ts +6 -0
  200. package/dist/guest/toolchains/databases/index.js +62 -0
  201. package/dist/guest/toolchains/databases/mongodb.d.ts +3 -0
  202. package/dist/guest/toolchains/databases/mongodb.js +31 -0
  203. package/dist/guest/toolchains/databases/mysql.d.ts +3 -0
  204. package/dist/guest/toolchains/databases/mysql.js +38 -0
  205. package/dist/guest/toolchains/databases/postgres.d.ts +5 -0
  206. package/dist/guest/toolchains/databases/postgres.js +38 -0
  207. package/dist/guest/toolchains/databases/redis.d.ts +3 -0
  208. package/dist/guest/toolchains/databases/redis.js +16 -0
  209. package/dist/guest/toolchains/databases/server.d.ts +14 -0
  210. package/dist/guest/toolchains/databases/server.js +52 -0
  211. package/dist/guest/toolchains/git.d.ts +3 -0
  212. package/dist/guest/toolchains/git.js +121 -0
  213. package/dist/guest/toolchains/go.d.ts +3 -0
  214. package/dist/guest/toolchains/go.js +33 -0
  215. package/dist/guest/toolchains/index.d.ts +17 -0
  216. package/dist/guest/toolchains/index.js +80 -0
  217. package/dist/guest/toolchains/java.d.ts +8 -0
  218. package/dist/guest/toolchains/java.js +96 -0
  219. package/dist/guest/toolchains/leftovers.d.ts +2 -0
  220. package/dist/guest/toolchains/leftovers.js +26 -0
  221. package/dist/guest/toolchains/managers.d.ts +16 -0
  222. package/dist/guest/toolchains/managers.js +84 -0
  223. package/dist/guest/toolchains/native/binstall.d.ts +7 -0
  224. package/dist/guest/toolchains/native/binstall.js +52 -0
  225. package/dist/guest/toolchains/native/cpp.d.ts +4 -0
  226. package/dist/guest/toolchains/native/cpp.js +38 -0
  227. package/dist/guest/toolchains/native/dotnet.d.ts +4 -0
  228. package/dist/guest/toolchains/native/dotnet.js +103 -0
  229. package/dist/guest/toolchains/native/mesa.d.ts +7 -0
  230. package/dist/guest/toolchains/native/mesa.js +46 -0
  231. package/dist/guest/toolchains/native/msvc/environment.d.ts +16 -0
  232. package/dist/guest/toolchains/native/msvc/environment.js +57 -0
  233. package/dist/guest/toolchains/native/msvc/index.d.ts +6 -0
  234. package/dist/guest/toolchains/native/msvc/index.js +48 -0
  235. package/dist/guest/toolchains/native/msvc/packages.d.ts +6 -0
  236. package/dist/guest/toolchains/native/msvc/packages.js +156 -0
  237. package/dist/guest/toolchains/native/msvc/tree.d.ts +15 -0
  238. package/dist/guest/toolchains/native/msvc/tree.js +21 -0
  239. package/dist/guest/toolchains/native/rust.d.ts +3 -0
  240. package/dist/guest/toolchains/native/rust.js +45 -0
  241. package/dist/guest/toolchains/node.d.ts +3 -0
  242. package/dist/guest/toolchains/node.js +152 -0
  243. package/dist/guest/toolchains/plan.d.ts +22 -0
  244. package/dist/guest/toolchains/plan.js +96 -0
  245. package/dist/guest/toolchains/pwsh.d.ts +4 -0
  246. package/dist/guest/toolchains/pwsh.js +14 -0
  247. package/dist/guest/toolchains/python.d.ts +3 -0
  248. package/dist/guest/toolchains/python.js +54 -0
  249. package/dist/guest/toolchains/runtimes.d.ts +19 -0
  250. package/dist/guest/toolchains/runtimes.js +38 -0
  251. package/dist/guest/toolchains/scripting/php.d.ts +3 -0
  252. package/dist/guest/toolchains/scripting/php.js +71 -0
  253. package/dist/guest/toolchains/scripting/ruby.d.ts +3 -0
  254. package/dist/guest/toolchains/scripting/ruby.js +47 -0
  255. package/dist/guest/toolchains/scripting/vcruntime.d.ts +7 -0
  256. package/dist/guest/toolchains/scripting/vcruntime.js +45 -0
  257. package/dist/guest/toolchains/versions.d.ts +47 -0
  258. package/dist/guest/toolchains/versions.js +77 -0
  259. package/dist/guest/toolchains/vscode.d.ts +2 -0
  260. package/dist/guest/toolchains/vscode.js +53 -0
  261. package/dist/guest/view.d.ts +1 -0
  262. package/dist/guest/view.js +42 -0
  263. package/dist/host/accounts.d.ts +32 -0
  264. package/dist/host/accounts.js +128 -0
  265. package/dist/host/agent-commands.d.ts +25 -0
  266. package/dist/host/agent-commands.js +145 -0
  267. package/dist/host/attach.d.ts +17 -0
  268. package/dist/host/attach.js +69 -0
  269. package/dist/host/channel.d.ts +38 -0
  270. package/dist/host/channel.js +162 -0
  271. package/dist/host/claude-profile.d.ts +15 -0
  272. package/dist/host/claude-profile.js +107 -0
  273. package/dist/host/cleanup.d.ts +23 -0
  274. package/dist/host/cleanup.js +57 -0
  275. package/dist/host/database-plan.d.ts +12 -0
  276. package/dist/host/database-plan.js +52 -0
  277. package/dist/host/generate.d.ts +7 -0
  278. package/dist/host/generate.js +98 -0
  279. package/dist/host/join.d.ts +28 -0
  280. package/dist/host/join.js +76 -0
  281. package/dist/host/launch/mappings.d.ts +3 -0
  282. package/dist/host/launch/mappings.js +50 -0
  283. package/dist/host/launch/projects.d.ts +15 -0
  284. package/dist/host/launch/projects.js +56 -0
  285. package/dist/host/launch/stage.d.ts +11 -0
  286. package/dist/host/launch/stage.js +66 -0
  287. package/dist/host/launch/wsb.d.ts +10 -0
  288. package/dist/host/launch/wsb.js +34 -0
  289. package/dist/host/pc-scan.d.ts +6 -0
  290. package/dist/host/pc-scan.js +37 -0
  291. package/dist/host/personalize.d.ts +10 -0
  292. package/dist/host/personalize.js +97 -0
  293. package/dist/host/platform.d.ts +19 -0
  294. package/dist/host/platform.js +126 -0
  295. package/dist/host/project-scan.d.ts +7 -0
  296. package/dist/host/project-scan.js +89 -0
  297. package/dist/host/projects.d.ts +100 -0
  298. package/dist/host/projects.js +147 -0
  299. package/dist/host/repo-guard.d.ts +36 -0
  300. package/dist/host/repo-guard.js +219 -0
  301. package/dist/host/secrets.d.ts +14 -0
  302. package/dist/host/secrets.js +133 -0
  303. package/dist/host/terminal.d.ts +32 -0
  304. package/dist/host/terminal.js +130 -0
  305. package/dist/host/test-guard.d.ts +6 -0
  306. package/dist/host/test-guard.js +15 -0
  307. package/dist/host/window.d.ts +4 -0
  308. package/dist/host/window.js +72 -0
  309. package/dist/host/wsb.d.ts +37 -0
  310. package/dist/host/wsb.js +101 -0
  311. package/dist/runtime/daemon.js +28494 -0
  312. package/dist/runtime/db.js +26387 -0
  313. package/dist/runtime/gitguard.js +84 -0
  314. package/dist/runtime/main.js +28622 -0
  315. package/dist/runtime/view.js +25841 -0
  316. package/dist/selftest/accounts.test.d.ts +2 -0
  317. package/dist/selftest/accounts.test.js +44 -0
  318. package/dist/selftest/agent-commands.test.d.ts +2 -0
  319. package/dist/selftest/agent-commands.test.js +47 -0
  320. package/dist/selftest/channel.test.d.ts +2 -0
  321. package/dist/selftest/channel.test.js +37 -0
  322. package/dist/selftest/cli.test.d.ts +2 -0
  323. package/dist/selftest/cli.test.js +118 -0
  324. package/dist/selftest/cmdline.test.d.ts +2 -0
  325. package/dist/selftest/cmdline.test.js +45 -0
  326. package/dist/selftest/config.test.d.ts +2 -0
  327. package/dist/selftest/config.test.js +38 -0
  328. package/dist/selftest/data.test.d.ts +2 -0
  329. package/dist/selftest/data.test.js +80 -0
  330. package/dist/selftest/detect.test.d.ts +2 -0
  331. package/dist/selftest/detect.test.js +58 -0
  332. package/dist/selftest/fixtures.d.ts +272 -0
  333. package/dist/selftest/fixtures.js +80 -0
  334. package/dist/selftest/generate.test.d.ts +2 -0
  335. package/dist/selftest/generate.test.js +63 -0
  336. package/dist/selftest/git-access.test.d.ts +2 -0
  337. package/dist/selftest/git-access.test.js +26 -0
  338. package/dist/selftest/guest-boot.test.d.ts +2 -0
  339. package/dist/selftest/guest-boot.test.js +76 -0
  340. package/dist/selftest/harness.d.ts +51 -0
  341. package/dist/selftest/harness.js +77 -0
  342. package/dist/selftest/host-sim.test.d.ts +2 -0
  343. package/dist/selftest/host-sim.test.js +93 -0
  344. package/dist/selftest/hostile-input.test.d.ts +2 -0
  345. package/dist/selftest/hostile-input.test.js +54 -0
  346. package/dist/selftest/index.d.ts +8 -0
  347. package/dist/selftest/index.js +155 -0
  348. package/dist/selftest/links.test.d.ts +2 -0
  349. package/dist/selftest/links.test.js +50 -0
  350. package/dist/selftest/live-daemon.test.d.ts +2 -0
  351. package/dist/selftest/live-daemon.test.js +91 -0
  352. package/dist/selftest/live-git.test.d.ts +2 -0
  353. package/dist/selftest/live-git.test.js +60 -0
  354. package/dist/selftest/live-projects.test.d.ts +2 -0
  355. package/dist/selftest/live-projects.test.js +127 -0
  356. package/dist/selftest/live.d.ts +65 -0
  357. package/dist/selftest/live.js +109 -0
  358. package/dist/selftest/local-config.test.d.ts +2 -0
  359. package/dist/selftest/local-config.test.js +107 -0
  360. package/dist/selftest/mirror.test.d.ts +2 -0
  361. package/dist/selftest/mirror.test.js +101 -0
  362. package/dist/selftest/network.test.d.ts +2 -0
  363. package/dist/selftest/network.test.js +28 -0
  364. package/dist/selftest/personalization.test.d.ts +2 -0
  365. package/dist/selftest/personalization.test.js +53 -0
  366. package/dist/selftest/projects.test.d.ts +2 -0
  367. package/dist/selftest/projects.test.js +254 -0
  368. package/dist/selftest/repair.test.d.ts +2 -0
  369. package/dist/selftest/repair.test.js +42 -0
  370. package/dist/selftest/repo-guard.test.d.ts +2 -0
  371. package/dist/selftest/repo-guard.test.js +63 -0
  372. package/dist/selftest/secrets.test.d.ts +2 -0
  373. package/dist/selftest/secrets.test.js +71 -0
  374. package/dist/selftest/setup.test.d.ts +2 -0
  375. package/dist/selftest/setup.test.js +46 -0
  376. package/dist/selftest/suite.d.ts +13 -0
  377. package/dist/selftest/suite.js +1 -0
  378. package/dist/selftest/sync.test.d.ts +2 -0
  379. package/dist/selftest/sync.test.js +94 -0
  380. package/dist/selftest/test-guard.test.d.ts +2 -0
  381. package/dist/selftest/test-guard.test.js +49 -0
  382. package/dist/selftest/toolchains.test.d.ts +2 -0
  383. package/dist/selftest/toolchains.test.js +103 -0
  384. package/dist/selftest/workspace.test.d.ts +2 -0
  385. package/dist/selftest/workspace.test.js +53 -0
  386. package/dist/shared/download.d.ts +60 -0
  387. package/dist/shared/download.js +215 -0
  388. package/dist/shared/guest-paths.d.ts +30 -0
  389. package/dist/shared/guest-paths.js +61 -0
  390. package/dist/shared/netrules.d.ts +18 -0
  391. package/dist/shared/netrules.js +98 -0
  392. package/dist/shared/paths.d.ts +14 -0
  393. package/dist/shared/paths.js +28 -0
  394. package/dist/shared/projects.d.ts +53 -0
  395. package/dist/shared/projects.js +67 -0
  396. package/dist/shared/pty-client.d.ts +86 -0
  397. package/dist/shared/pty-client.js +227 -0
  398. package/dist/shared/rundir.d.ts +39 -0
  399. package/dist/shared/rundir.js +94 -0
  400. package/dist/shared/safe-remove.d.ts +10 -0
  401. package/dist/shared/safe-remove.js +66 -0
  402. package/dist/shared/schema.d.ts +527 -0
  403. package/dist/shared/schema.js +90 -0
  404. package/dist/shared/system.d.ts +15 -0
  405. package/dist/shared/system.js +19 -0
  406. package/dist/shared/test-guard.d.ts +4 -0
  407. package/dist/shared/test-guard.js +37 -0
  408. package/dist/sync/claude-mem.d.ts +8 -0
  409. package/dist/sync/claude-mem.js +79 -0
  410. package/dist/sync/history.d.ts +41 -0
  411. package/dist/sync/history.js +161 -0
  412. package/dist/sync/watcher.d.ts +21 -0
  413. package/dist/sync/watcher.js +135 -0
  414. package/hooks/post-agent/README.txt +1 -0
  415. package/hooks/pre-provision/README.txt +1 -0
  416. package/package.json +91 -4
  417. package/sandbox/bootstrap.ps1 +258 -0
  418. package/sandbox/cpucap.ps1 +51 -0
  419. package/sandbox/gitshim.cs +35 -0
  420. package/sandbox/logon.cmd +18 -0
  421. package/sandbox/openssl.cs +42 -0
  422. package/sandbox/ptyhost.cs +614 -0
  423. package/sandbox/trust.cmd +35 -0
  424. package/sandbox/vswhere.cs +83 -0
  425. package/schemas/config.schema.json +1161 -0
  426. package/schemas/sandbox-config.schema.json +968 -0
@@ -0,0 +1,32 @@
1
+ // `exec` requests: a command run for the host in the workspace.
2
+ import { spawn } from 'node:child_process';
3
+ import { SYSTEM } from '../../shared/system.js';
4
+ import { errMsg, exists } from '../env.js';
5
+ import { provisionedFor } from './project.js';
6
+ import { insideWorkspace, respond } from './state.js';
7
+ const MAX_OUTPUT = 1024 * 1024;
8
+ const quoteForCmd = (a) => (/[\s"&|<>^]/.test(a) ? '"' + a.replace(/"/g, '""') + '"' : a);
9
+ const cap = (s) => (s.length > MAX_OUTPUT ? s.slice(0, MAX_OUTPUT) + '\n[output truncated]' : s);
10
+ /** Runs a command for the host without blocking the daemon (other requests and pings keep being answered). */
11
+ export const execCommand = (req) => {
12
+ if (!insideWorkspace(req.cwd) || !exists(req.cwd)) {
13
+ respond(req.id, { ok: false, message: 'Folder is not inside the sandbox workspace: ' + req.cwd });
14
+ return;
15
+ }
16
+ // chcp 65001 first: console programs then write UTF-8 (not the OEM code page) into the pipes.
17
+ // In a project registered after boot, its own environment (its Node, Python, database variables).
18
+ const env = provisionedFor(req.cwd)?.env ?? process.env;
19
+ const child = spawn(SYSTEM.cmd(), ['/d', '/s', '/c', '"chcp 65001>nul & ' + req.command.map(quoteForCmd).join(' ') + '"'], { cwd: req.cwd, env, windowsHide: true, windowsVerbatimArguments: true });
20
+ const output = { stdout: '', stderr: '' };
21
+ // setEncoding decodes across chunks: a multi-byte character split between two reads is not mangled.
22
+ const collect = (key) => (d) => { if (output[key].length < MAX_OUTPUT)
23
+ output[key] += d; };
24
+ child.stdout.setEncoding('utf8').on('data', collect('stdout'));
25
+ child.stderr.setEncoding('utf8').on('data', collect('stderr'));
26
+ const timer = setTimeout(() => child.kill(), Math.min(req.timeoutSeconds, 3600) * 1000);
27
+ child.on('error', (e) => { clearTimeout(timer); respond(req.id, { ok: false, message: 'Could not run the command: ' + errMsg(e) }); });
28
+ child.on('close', (code) => {
29
+ clearTimeout(timer);
30
+ respond(req.id, { ok: code === 0, message: code === null ? 'stopped after ' + req.timeoutSeconds + ' s' : 'exit ' + code, exitCode: code ?? -1, stdout: cap(output.stdout), stderr: cap(output.stderr) });
31
+ });
32
+ };
@@ -0,0 +1,14 @@
1
+ import type { ProjectSpecT } from '../../shared/schema.js';
2
+ export interface Provisioned {
3
+ spec: ProjectSpecT;
4
+ env: NodeJS.ProcessEnv;
5
+ summary: string[];
6
+ }
7
+ /** The provisioned project containing this folder, if any (exec in its folders uses its environment). */
8
+ export declare const provisionedFor: (cwd: string) => Provisioned | null;
9
+ export declare const provisionedProjects: () => Provisioned[];
10
+ /**
11
+ * Provisions a project (or joins the run already doing it: two sessions requested at once wait for the same one).
12
+ * `say` gets the progress lines.
13
+ */
14
+ export declare const provisionProject: (spec: ProjectSpecT, say: (message: string) => void) => Promise<Provisioned>;
@@ -0,0 +1,132 @@
1
+ // Projects registered after boot (sandbox claude from another folder on the PC, which the host links into the hub).
2
+ // Each is provisioned once, on demand, while every other session keeps running: the toolchain steps it needs that are
3
+ // not installed yet (installOnce markers and installed folders make the rest free), its Node/Python versions, its
4
+ // databases and hooks. Its sessions get ITS environment, built from the boot's base environment rather than the boot
5
+ // project's: nothing machine-wide is switched under the sessions already running (no nvm symlink switch, machine
6
+ // variables only added, never replaced). A failure is that project's alone.
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+ import { isWithin } from '../../shared/projects.js';
10
+ import { insideWorkspace } from './state.js';
11
+ import { baseEnvFor, readBootEnv } from '../boot-env.js';
12
+ import { dryRun, errMsg, exists, localDir, log, mkdirp, session, warn } from '../env.js';
13
+ import { writeGuides } from '../guide.js';
14
+ import { withProject } from '../project-context.js';
15
+ import { saveProjectStatus } from '../project-status.js';
16
+ import { addMachineEnvironment, agentEnv } from '../setup/environment.js';
17
+ import { projectHooks, runHooksAsync } from '../setup/hooks.js';
18
+ import { setupToolchains } from '../toolchains/index.js';
19
+ /** By project name (lower case). The same spec again is free; a changed one (its config was edited) provisions again. */
20
+ const projects = new Map();
21
+ /** The last finished provisioning of each project (kept while a changed spec provisions again). */
22
+ const ready = new Map();
23
+ /** The provisioned project containing this folder, if any (exec in its folders uses its environment). */
24
+ export const provisionedFor = (cwd) => [...ready.values()].filter((p) => isWithin(p.spec.workspace, cwd)).sort((a, b) => b.spec.workspace.length - a.spec.workspace.length)[0] ?? null;
25
+ export const provisionedProjects = () => [...ready.values()];
26
+ /** `set` lines for a .cmd file. Values are quoted; a value with a line break cannot be set this way and is left out. */
27
+ const cmdSet = (env, names) => names.filter((n) => env[n] !== undefined && !/[\r\n]/.test(String(env[n]))).map((n) => '@set "' + n + '=' + String(env[n]).replace(/%/g, '%%') + '"');
28
+ /**
29
+ * A terminal for the project inside the sandbox: `sbx-shell <name>` (from any terminal tab) opens cmd in its folder with
30
+ * its environment. Written to the sandbox's own disk.
31
+ */
32
+ const writeShell = (spec, env, names) => {
33
+ const dir = mkdirp(path.join(localDir, 'projects', spec.name));
34
+ fs.writeFileSync(path.join(dir, 'env.cmd'), [...cmdSet(env, [...names, 'PATH']), ''].join('\r\n'));
35
+ fs.writeFileSync(path.join(dir, 'shell.cmd'), ['@call "' + path.join(dir, 'env.cmd') + '"', '@cd /d "' + spec.workspace + '"', '@title ' + spec.name, ''].join('\r\n'));
36
+ const bin = mkdirp(path.join(localDir, 'bin'));
37
+ fs.writeFileSync(path.join(bin, 'sbx-shell.cmd'), [
38
+ '@if "%~1"=="" (echo Usage: sbx-shell ^<project^> ^(a terminal in that project with its tools^) & dir /b "' + path.join(localDir, 'projects') + '" & exit /b 2)',
39
+ '@if not exist "' + path.join(localDir, 'projects') + '\\%~1\\shell.cmd" (echo No project %~1 in this sandbox. & exit /b 2)',
40
+ '@cmd /k ""' + path.join(localDir, 'projects') + '\\%~1\\shell.cmd""',
41
+ '',
42
+ ].join('\r\n'));
43
+ };
44
+ /** The guide every agent reads lists every project in the sandbox (they can all see each other's folders). */
45
+ const refreshGuide = async () => {
46
+ // The self-test must never rewrite this machine's real managed CLAUDE.md.
47
+ if (dryRun || (process.env.AGENT_SANDBOX_SELFTEST === '1' && !process.env.AGENT_MANAGED_DIR))
48
+ return;
49
+ const boot = readBootEnv();
50
+ if (!boot)
51
+ return;
52
+ const list = provisionedProjects().map((p) => ({ guest: p.spec.workspace, host: p.spec.workspaceHost }));
53
+ try {
54
+ writeGuides(boot.summary, boot.lan, list);
55
+ }
56
+ catch (e) {
57
+ warn('Could not update the sandbox guide: ' + errMsg(e));
58
+ }
59
+ };
60
+ const provision = async (spec, say) => {
61
+ if (!insideWorkspace(spec.workspace) || !exists(spec.workspace)) {
62
+ throw new Error('Project folder ' + spec.workspace + ' (' + spec.workspaceHost + ') is not visible inside the sandbox. Stop the sandbox (sandbox stop) and start it from that folder, or add its parent folder to projects.roots.');
63
+ }
64
+ saveProjectStatus(spec.name, spec.workspace, 'provisioning', 'Setting up ' + spec.name, spec.databases, []);
65
+ log('Project ' + spec.name + ': setting up ' + spec.workspace + ' (' + spec.workspaceHost + ')');
66
+ const boot = readBootEnv();
67
+ const base = baseEnvFor(boot);
68
+ const hooks = projectHooks(spec.name, spec.workspace);
69
+ if (spec.hooks) {
70
+ say('Pre-provision hooks');
71
+ await runHooksAsync('pre-provision', base, hooks);
72
+ }
73
+ const tools = await withProject({ workspace: spec.workspace, duringSession: true }, () => setupToolchains(spec.toolchains, spec.versions, {
74
+ gitEnabled: session.provisioning.git.enabled,
75
+ // Git access is machine-wide: the host refuses a project that needs a stricter one, so the sandbox's applies.
76
+ gitAccess: session.provisioning.git.access ?? 'full',
77
+ hostGit: session.hostTools.git,
78
+ refresh: false,
79
+ databases: spec.databases,
80
+ }, { report: (m) => { saveProjectStatus(spec.name, spec.workspace, 'provisioning', m, spec.databases, []); say(m); }, reuse: boot?.steps }));
81
+ tools.prepend.push(path.join(localDir, 'bin'));
82
+ const env = agentEnv(tools, { workspace: spec.workspace, project: spec.name, base });
83
+ const specFile = path.join(mkdirp(path.join(localDir, 'projects', spec.name)), 'project.json');
84
+ fs.writeFileSync(specFile, JSON.stringify(spec));
85
+ env.AGENT_PROJECT_FILE = specFile;
86
+ // New terminal tabs see the new tools too, after everything the boot project has (never in place of it).
87
+ addMachineEnvironment(tools.vars, [...tools.prepend, ...tools.append], true);
88
+ try {
89
+ writeShell(spec, env, [...Object.keys(tools.vars), 'AGENT_WORKSPACE', 'AGENT_PROJECT', 'AGENT_SANDBOX_WORKSPACE', 'AGENT_PROJECT_FILE']);
90
+ }
91
+ catch (e) {
92
+ warn('sbx-shell for ' + spec.name + ': ' + errMsg(e));
93
+ }
94
+ if (spec.hooks) {
95
+ say('Pre-agent hooks');
96
+ await runHooksAsync('pre-agent', env, hooks);
97
+ }
98
+ for (const line of tools.summary)
99
+ log(' [' + spec.name + '] ' + line);
100
+ saveProjectStatus(spec.name, spec.workspace, 'ready', 'Ready', spec.databases, tools.summary);
101
+ return { spec, env, summary: tools.summary };
102
+ };
103
+ /**
104
+ * Provisions a project (or joins the run already doing it: two sessions requested at once wait for the same one).
105
+ * `say` gets the progress lines.
106
+ */
107
+ export const provisionProject = (spec, say) => {
108
+ const id = spec.name.toLowerCase();
109
+ const key = JSON.stringify(spec);
110
+ const known = projects.get(id);
111
+ if (known && known.key === key) {
112
+ known.listeners.add(say);
113
+ return known.work.finally(() => known.listeners.delete(say));
114
+ }
115
+ const listeners = new Set([say]);
116
+ const broadcast = (message) => { for (const l of listeners)
117
+ try {
118
+ l(message);
119
+ }
120
+ catch { /* a listener's write failing never stops provisioning */ } };
121
+ const work = provision(spec, broadcast);
122
+ const entry = { key, work, listeners };
123
+ projects.set(id, entry);
124
+ work.then((done) => { entry.done = done; ready.set(id, done); return refreshGuide(); }, (e) => {
125
+ warn('Project ' + spec.name + ' could not be set up: ' + errMsg(e));
126
+ saveProjectStatus(spec.name, spec.workspace, 'failed', errMsg(e), spec.databases, []);
127
+ // The next session for it tries again.
128
+ if (projects.get(id) === entry)
129
+ projects.delete(id);
130
+ });
131
+ return work;
132
+ };
@@ -0,0 +1,13 @@
1
+ import type { GuestRequest } from '../../core/types.js';
2
+ type SessionRequest = Extract<GuestRequest, {
3
+ kind: 'session';
4
+ }>;
5
+ export declare const startSession: (req: SessionRequest) => Promise<void>;
6
+ /** `project` requests: provision a registered project without an agent (sandbox start / exec / db from its folder). */
7
+ export declare const prepareProject: (req: Extract<GuestRequest, {
8
+ kind: "project";
9
+ }>) => Promise<void>;
10
+ export declare const restartSessions: (req: Extract<GuestRequest, {
11
+ kind: "restart";
12
+ }>) => Promise<void>;
13
+ export {};
@@ -0,0 +1,139 @@
1
+ // `session` and `restart` requests: agent sessions, each in its own ptyhost process. A session runs in the launch
2
+ // folder's project (the boot environment) or in a project registered later, which is provisioned first (daemon/project).
3
+ import path from 'node:path';
4
+ import crypto from 'node:crypto';
5
+ import { spawn } from 'node:child_process';
6
+ import { FrameType, sendControl } from '../../shared/pty-client.js';
7
+ import { isWithin } from '../../shared/projects.js';
8
+ import { now } from '../../shared/rundir.js';
9
+ import { SYSTEM } from '../../shared/system.js';
10
+ import { commandLine } from '../cmdline.js';
11
+ import { freePort } from '../ports.js';
12
+ import { primaryProject } from '../project-status.js';
13
+ import { ensurePtyHost, ptyHostLogTail, startPtyHost, waitForPtyHost } from '../ptyhost.js';
14
+ import { provisionHarness, resolveLaunch } from '../setup/agents.js';
15
+ import { saveClaudeMem } from '../setup/claude.js';
16
+ import { projectHooks, runHooks, runHooksAsync } from '../setup/hooks.js';
17
+ import { guestAddresses } from '../setup/network.js';
18
+ import { dataDir, errMsg, exists, log, session, toolsDir, warn, workspace } from '../env.js';
19
+ import { provisionProject } from './project.js';
20
+ import { maybeShutdown } from './shutdown.js';
21
+ import { insidePrimary, live, pending, respond, safely, saveStatus } from './state.js';
22
+ const terminalTab = (id, title, cwd) => {
23
+ const view = [process.execPath, path.join(path.dirname(process.argv[1]), 'view.js'), id];
24
+ const wt = path.join(toolsDir, 'terminal', 'wt.exe');
25
+ if (exists(wt))
26
+ spawn(wt, ['-w', '0', 'nt', '--title', title, '-d', cwd, ...view], { detached: true, stdio: 'ignore' }).unref();
27
+ else
28
+ spawn(SYSTEM.cmd(), ['/d', '/c', 'start', title, ...view], { detached: true, stdio: 'ignore', cwd }).unref();
29
+ };
30
+ const onExit = (running, place) => (code) => {
31
+ live.delete(running.id);
32
+ const tail = code === 0 ? '' : ptyHostLogTail(running.id, 6);
33
+ saveStatus({ ...running, phase: 'exited', message: running.harness + ' exited with code ' + code + (tail ? '. Last output:\n' + tail : ''), exitCode: code ?? 1, attach: undefined });
34
+ log('Session ' + running.id + ' ended (exit ' + code + ')');
35
+ if (place.hooks)
36
+ void runHooksAsync('post-agent', place.env, place.hooks).catch((e) => warn('post-agent hooks failed: ' + errMsg(e)));
37
+ else
38
+ safely('post-agent hooks', () => runHooks('post-agent', process.env));
39
+ safely('claude-mem save', saveClaudeMem);
40
+ maybeShutdown();
41
+ };
42
+ /**
43
+ * The agent's definition: the one the sandbox booted with, the one the host sent along (another agent, e.g.
44
+ * sandbox codex in a sandbox started for claude), or one the project's config names.
45
+ */
46
+ const harnessFor = (req) => {
47
+ if (req.harness === session.harnessName && session.harness)
48
+ return session.harness;
49
+ const sent = req.harnessSpec ?? req.project?.harnesses[req.harness];
50
+ if (sent)
51
+ return sent;
52
+ throw new Error('Agent "' + req.harness + '" is not configured for this sandbox (it runs ' + session.harnessName + ').');
53
+ };
54
+ /** Starts ptyhost for the agent and waits until it listens; a failure carries the tail of ptyhost's own log. */
55
+ const launchPtyHost = async (req, remote, env, say) => {
56
+ const harness = harnessFor(req);
57
+ // Never refresh here: main already did, and replacing a running agent's executable would fail.
58
+ say('Preparing ' + req.harness);
59
+ await provisionHarness(req.harness, harness, env, false);
60
+ const launch = resolveLaunch(req.harness, harness);
61
+ const base = [...launch.args, ...harness.arguments];
62
+ const port = await freePort();
63
+ const token = crypto.randomBytes(24).toString('base64url');
64
+ const child = startPtyHost({
65
+ exe: ensurePtyHost(), port, token, cols: 120, rows: 32, id: req.id, cwd: req.cwd, env: { ...env, ...harness.environment }, remote,
66
+ command: commandLine(launch.file, [...base, ...req.args]), restartCommand: commandLine(launch.file, [...base, ...(harness.resumeArguments ?? [])]),
67
+ });
68
+ try {
69
+ await waitForPtyHost(child, port, 15000);
70
+ }
71
+ catch (e) {
72
+ const tail = ptyHostLogTail(req.id);
73
+ throw new Error(errMsg(e) + (tail ? '. From its log:\n' + tail : '') + '\nLog: ' + path.join(dataDir, 'logs', 'ptyhost-' + req.id + '.log'));
74
+ }
75
+ return { child, port, token };
76
+ };
77
+ /** The launch folder's project, or the registered project this session is for (provisioned first if needed). */
78
+ const placeFor = async (req, say) => {
79
+ if (!req.project) {
80
+ if (!insidePrimary(req.cwd) || !exists(req.cwd))
81
+ throw new Error('Folder is not inside the sandbox workspace ' + workspace + ': ' + req.cwd);
82
+ return { env: process.env, hooks: null, project: primaryProject() };
83
+ }
84
+ if (!isWithin(req.project.workspace, req.cwd))
85
+ throw new Error('Folder ' + req.cwd + ' is not inside project ' + req.project.name + ' (' + req.project.workspace + ').');
86
+ const ready = await provisionProject(req.project, say);
87
+ if (!exists(req.cwd))
88
+ throw new Error('Folder does not exist in the sandbox: ' + req.cwd);
89
+ return { env: ready.env, hooks: req.project.hooks ? projectHooks(req.project.name, req.project.workspace) : null, project: req.project.name };
90
+ };
91
+ export const startSession = async (req) => {
92
+ let status = { id: req.id, launchId: session.launchId, harness: req.harness, cwd: req.cwd, ...(req.project ? { project: req.project.name } : {}), phase: 'starting', message: 'Starting ' + req.harness, updatedAt: now(), startedAt: now() };
93
+ saveStatus(status);
94
+ // Progress goes into the session's status: the host prints it while it waits, and the tab inside shows it.
95
+ const say = (message) => { status = { ...status, message }; saveStatus(status); };
96
+ pending.starting++;
97
+ // Sessions after the first get their own tab right away (it shows the setup progress until the agent starts).
98
+ if (req.id !== session.launchId && process.env.AGENT_NO_TABS !== '1')
99
+ safely('terminal tab', () => terminalTab(req.id, req.harness + ' ' + path.basename(req.cwd), req.cwd));
100
+ try {
101
+ const place = await placeFor(req, say);
102
+ const remote = session.networking && session.attach.enabled;
103
+ const { child, port, token } = await launchPtyHost(req, remote, place.env, say);
104
+ const running = { ...status, project: place.project, phase: 'running', message: req.harness + ' is running', attach: { addresses: remote ? guestAddresses() : [], port, token } };
105
+ live.set(req.id, { status: running, child });
106
+ child.once('exit', onExit(running, place));
107
+ saveStatus(running);
108
+ log('Session ' + req.id + ' started (' + req.harness + ' in ' + req.cwd + ', project ' + place.project + ', port ' + port + ')');
109
+ }
110
+ catch (e) {
111
+ saveStatus({ ...status, phase: 'failed', message: errMsg(e) });
112
+ warn('Session ' + req.id + ' failed: ' + errMsg(e));
113
+ maybeShutdown();
114
+ }
115
+ finally {
116
+ pending.starting--;
117
+ }
118
+ };
119
+ /** `project` requests: provision a registered project without an agent (sandbox start / exec / db from its folder). */
120
+ export const prepareProject = async (req) => {
121
+ pending.starting++;
122
+ try {
123
+ const ready = await provisionProject(req.project, () => undefined);
124
+ respond(req.id, { ok: true, message: 'Project ' + req.project.name + ' is ready in ' + req.project.workspace + '\n' + ready.summary.join('\n') });
125
+ }
126
+ catch (e) {
127
+ respond(req.id, { ok: false, message: 'Project ' + req.project.name + ' could not be set up: ' + errMsg(e) });
128
+ }
129
+ finally {
130
+ pending.starting--;
131
+ maybeShutdown();
132
+ }
133
+ };
134
+ export const restartSessions = async (req) => {
135
+ const targets = [...live.values()].filter((s) => s.status.attach && (req.harness === null || s.status.harness === req.harness) && (!req.cwd || isWithin(req.cwd, s.status.cwd)));
136
+ for (const s of targets)
137
+ await sendControl({ addresses: ['127.0.0.1'], port: s.status.attach.port, token: s.status.attach.token }, FrameType.Restart);
138
+ respond(req.id, { ok: true, message: 'restarted ' + targets.length + ' session(s)' });
139
+ };
@@ -0,0 +1 @@
1
+ export declare const maybeShutdown: () => void;
@@ -0,0 +1,21 @@
1
+ // A headless sandbox with nothing running (or starting) only wastes RAM, so it shuts itself down.
2
+ import { spawnSync } from 'node:child_process';
3
+ import { SYSTEM } from '../../shared/system.js';
4
+ import { scanRequests } from '../../shared/rundir.js';
5
+ import { saveClaudeMem } from '../setup/claude.js';
6
+ import { dataDir, isWin, log, session } from '../env.js';
7
+ import { live, pending, safely } from './state.js';
8
+ export const maybeShutdown = () => {
9
+ if (!session.headless || !isWin)
10
+ return;
11
+ const idle = () => live.size === 0 && pending.starting === 0 && scanRequests(dataDir).valid.length === 0;
12
+ if (!idle())
13
+ return;
14
+ setTimeout(() => {
15
+ if (!idle())
16
+ return;
17
+ log('Headless sandbox has no sessions left; shutting down.');
18
+ safely('claude-mem save', saveClaudeMem);
19
+ spawnSync(SYSTEM.shutdown(), ['/s', '/t', '5'], { windowsHide: true });
20
+ }, 15000);
21
+ };
@@ -0,0 +1,17 @@
1
+ import type { ChildProcess } from 'node:child_process';
2
+ import type { GuestResponse, SessionStatus } from '../../core/types.js';
3
+ export declare const live: Map<string, {
4
+ status: SessionStatus;
5
+ child: ChildProcess;
6
+ }>;
7
+ /** Sessions being started right now (a headless sandbox must not shut down under them). */
8
+ export declare const pending: {
9
+ starting: number;
10
+ };
11
+ /** Inside the launch folder's workspace. */
12
+ export declare const insidePrimary: (cwd: string) => boolean;
13
+ /** Inside any project of this sandbox: the launch folder, a project root, or C:\Projects\<name> (older launch folders: only the first). */
14
+ export declare const insideWorkspace: (cwd: string) => boolean;
15
+ export declare const respond: (id: string, result: Omit<GuestResponse, "id" | "finishedAt">) => void;
16
+ export declare const saveStatus: (status: SessionStatus) => void;
17
+ export declare const safely: (what: string, work: () => void) => void;
@@ -0,0 +1,31 @@
1
+ // What the daemon's request handlers share: the running sessions and best-effort status/response writes.
2
+ import path from 'node:path';
3
+ import { now, writeJsonAtomic } from '../../shared/rundir.js';
4
+ import { insideProjects } from '../../shared/projects.js';
5
+ import { errMsg, run, session, warn, workspace } from '../env.js';
6
+ const MAX_MESSAGE = 3500;
7
+ export const live = new Map();
8
+ /** Sessions being started right now (a headless sandbox must not shut down under them). */
9
+ export const pending = { starting: 0 };
10
+ /** Inside the launch folder's workspace. */
11
+ export const insidePrimary = (cwd) => {
12
+ const rel = path.relative(workspace.toLowerCase(), path.resolve(cwd).toLowerCase());
13
+ return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
14
+ };
15
+ /** Inside any project of this sandbox: the launch folder, a project root, or C:\Projects\<name> (older launch folders: only the first). */
16
+ export const insideWorkspace = (cwd) => insidePrimary(cwd) || (session.projectsDir !== undefined && insideProjects(path.resolve(cwd), session.roots ?? [], session.projectsDir));
17
+ /** Status/response writes are best-effort: a briefly locked file (antivirus, the host's sync) must not kill the daemon. */
18
+ const tryWrite = (file, value) => { try {
19
+ writeJsonAtomic(file, value);
20
+ }
21
+ catch (e) {
22
+ warn('Could not write ' + file + ': ' + errMsg(e));
23
+ } };
24
+ export const respond = (id, result) => tryWrite(run.response(id), { id, ...result, message: result.message.slice(0, MAX_MESSAGE), finishedAt: now() });
25
+ export const saveStatus = (status) => tryWrite(run.session(status.id), { ...status, message: status.message.slice(0, MAX_MESSAGE), updatedAt: now() });
26
+ export const safely = (what, work) => { try {
27
+ work();
28
+ }
29
+ catch (e) {
30
+ warn(what + ' failed: ' + errMsg(e));
31
+ } };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,53 @@
1
+ // Session daemon (runs detached inside the sandbox for its whole life). It is the only thing that acts on host
2
+ // requests, and it only knows four request kinds: start a session, run a command, restart sessions, ping.
3
+ // It must never die quietly: every iteration is guarded, and anything unexpected is written to its log.
4
+ // The handlers live in daemon/ (session, exec); this file is the polling loop.
5
+ import fs from 'node:fs';
6
+ import { readSession, scanRequests } from '../shared/rundir.js';
7
+ import { saveClaudeMem } from './setup/claude.js';
8
+ import { execCommand } from './daemon/exec.js';
9
+ import { prepareProject, restartSessions, startSession } from './daemon/session.js';
10
+ import { live, respond, safely } from './daemon/state.js';
11
+ import { dataDir, errMsg, log, run, warn } from './env.js';
12
+ let lastMemSave = Date.now();
13
+ process.on('uncaughtException', (e) => warn('daemon: unexpected error (continuing): ' + (e.stack ?? errMsg(e))));
14
+ process.on('unhandledRejection', (e) => warn('daemon: unexpected error (continuing): ' + errMsg(e)));
15
+ const handle = (req) => {
16
+ switch (req.kind) {
17
+ case 'session': return startSession(req);
18
+ case 'project': return prepareProject(req);
19
+ case 'exec': return execCommand(req);
20
+ case 'restart': return restartSessions(req);
21
+ case 'ping': return respond(req.id, { ok: true, message: 'pong (' + live.size + ' session(s) running)' });
22
+ }
23
+ };
24
+ const tick = () => {
25
+ const { valid, invalid } = scanRequests(dataDir);
26
+ // Anything in requests/ that is not a valid request (wrong kind, bad id, junk) is deleted unread.
27
+ for (const file of invalid)
28
+ safely('removing an invalid request', () => fs.rmSync(file, { recursive: true, force: true }));
29
+ for (const req of valid) {
30
+ safely('claiming a request', () => fs.rmSync(run.request(req.id), { force: true }));
31
+ if (readSession(dataDir, req.id))
32
+ continue;
33
+ // Not awaited: starting a session or running a command must not hold up other requests.
34
+ void Promise.resolve(handle(req)).catch((e) => respond(req.id, { ok: false, message: errMsg(e) }));
35
+ }
36
+ if (Date.now() - lastMemSave > 120000) {
37
+ lastMemSave = Date.now();
38
+ safely('claude-mem save', saveClaudeMem);
39
+ }
40
+ };
41
+ const loop = () => {
42
+ try {
43
+ tick();
44
+ }
45
+ catch (e) {
46
+ warn('daemon loop: ' + errMsg(e));
47
+ }
48
+ finally {
49
+ setTimeout(loop, 400);
50
+ }
51
+ };
52
+ log('Session daemon started (pid ' + process.pid + ').');
53
+ loop();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,75 @@
1
+ // `sbx-db`: list, start and stop the sandbox's databases from a terminal inside it (`sandbox db` on the PC runs it too).
2
+ // Usage: sbx-db [list] | sbx-db start <name> | sbx-db stop <name>
3
+ import fs from 'node:fs';
4
+ import { ProjectSpec } from '../shared/schema.js';
5
+ import { portOpen } from './ports.js';
6
+ import { DATABASE_NAMES, setupDatabase, stopDatabase } from './toolchains/databases/index.js';
7
+ import { emptyEnv } from './toolchains/common.js';
8
+ import { addMachineEnvironment } from './setup/environment.js';
9
+ import { errMsg, session } from './env.js';
10
+ const USAGE = 'Usage: sbx-db [list] | sbx-db start <name> | sbx-db stop <name>';
11
+ /** In a project registered after boot (its terminals and exec have AGENT_PROJECT_FILE): that project's database plan. */
12
+ const projectPlans = () => {
13
+ const file = process.env.AGENT_PROJECT_FILE;
14
+ if (!file)
15
+ return null;
16
+ try {
17
+ return ProjectSpec.safeParse(JSON.parse(fs.readFileSync(file, 'utf8'))).data?.databases ?? null;
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ };
23
+ /** The project's own plans first (their versions and ports were chosen for it), then everything else. */
24
+ const plans = () => {
25
+ const boot = projectPlans() ?? session.databases ?? [];
26
+ return [...boot, ...(session.databaseCatalog ?? []).filter((c) => !boot.some((b) => b.database === c.database))];
27
+ };
28
+ const find = (name) => {
29
+ const all = plans();
30
+ const plan = all.find((p) => p.database === name?.toLowerCase());
31
+ if (!plan)
32
+ throw new Error((name ? 'Unknown database "' + name + '". ' : '') + 'Choose one of: ' + all.map((p) => p.database).join(', '));
33
+ if (plan.database === 'sqlserver')
34
+ throw new Error('SQL Server cannot run inside Windows Sandbox.');
35
+ return plan;
36
+ };
37
+ const list = async () => {
38
+ for (const p of plans().filter((x) => x.database !== 'sqlserver')) {
39
+ const state = (await portOpen(p.port)) ? 'running' : 'stopped';
40
+ console.log(p.database.padEnd(10) + state.padEnd(9) + 'port ' + p.port + ' ' + DATABASE_NAMES[p.database]);
41
+ }
42
+ return 0;
43
+ };
44
+ const start = async (name) => {
45
+ const plan = find(name);
46
+ if (await portOpen(plan.port)) {
47
+ console.log(plan.database + ' is already running on port ' + plan.port + '.');
48
+ return 0;
49
+ }
50
+ console.log('Starting ' + DATABASE_NAMES[plan.database] + ' (the first time downloads it) ...');
51
+ const env = emptyEnv();
52
+ await setupDatabase(env, plan);
53
+ addMachineEnvironment(env.vars, env.prepend);
54
+ for (const line of env.summary)
55
+ console.log(' ' + line);
56
+ console.log('New terminal tabs have its variables and tools; this one does not.');
57
+ return 0;
58
+ };
59
+ const stop = async (name) => {
60
+ const plan = find(name);
61
+ console.log((await stopDatabase(plan)) ? 'Stopped ' + plan.database + '.' : plan.database + ' was not running.');
62
+ return 0;
63
+ };
64
+ const main = (args) => {
65
+ const [action = 'list', name] = args;
66
+ if (action === 'list')
67
+ return list();
68
+ if (action === 'start')
69
+ return start(name);
70
+ if (action === 'stop')
71
+ return stop(name);
72
+ console.error(USAGE);
73
+ return Promise.resolve(2);
74
+ };
75
+ main(process.argv.slice(2)).then((code) => { process.exitCode = code; }, (e) => { console.error(errMsg(e)); process.exitCode = 1; });
@@ -0,0 +1,51 @@
1
+ import { type SpawnOptions, type SpawnSyncOptions } from 'node:child_process';
2
+ import type { GuestSession, SandboxStatus } from '../core/types.js';
3
+ export declare const isWin: boolean;
4
+ export declare const dryRun: boolean;
5
+ /** Real provisioning, but nothing machine-wide (environment variables): for end-to-end tests outside a sandbox. */
6
+ export declare const testMode: boolean;
7
+ export declare const launchDir: string;
8
+ export declare const dataDir: string;
9
+ export declare const hostToolsDir: string;
10
+ /** Folders that must stay on the sandbox's own disk (symlinks, SQLite) rather than in a mapped folder. */
11
+ export declare const localDir: string;
12
+ export declare const toolsDir: string;
13
+ export declare const logsDir: string;
14
+ export declare const run: {
15
+ run: string;
16
+ sandbox: string;
17
+ requests: string;
18
+ responses: string;
19
+ sessions: string;
20
+ projects: string;
21
+ request: (id: string) => string;
22
+ response: (id: string) => string;
23
+ session: (id: string) => string;
24
+ project: (id: string) => string;
25
+ };
26
+ export declare const session: GuestSession;
27
+ export declare const workspace: string;
28
+ export declare const stateDir: string;
29
+ export declare const claudeDir: string;
30
+ export declare const log: (message: string) => void;
31
+ export declare const warn: (message: string) => void;
32
+ export declare const errMsg: (e: unknown) => string;
33
+ export declare const exists: (p: string) => boolean;
34
+ export declare const mkdirp: (p: string) => string;
35
+ export declare const logPath: string;
36
+ /** Best-effort: progress reporting must never be what breaks the boot. */
37
+ export declare const setSandboxStatus: (phase: SandboxStatus["phase"], message: string) => void;
38
+ /** Runs a program, streaming output, and throws with a readable message when it fails. */
39
+ export declare const exec: (file: string, args: string[], options?: SpawnSyncOptions) => void;
40
+ /** exec without blocking: other toolchains keep downloading while this one runs an installer. */
41
+ export declare const execAsync: (file: string, args: string[], options?: SpawnOptions) => Promise<void>;
42
+ export declare const execOutput: (file: string, args: string[], options?: SpawnSyncOptions) => string;
43
+ export declare const powershell: (script: string) => void;
44
+ /** Copies a folder with robocopy (fast, restartable), or just the named files in it; exit codes below 8 mean success. */
45
+ export declare const robocopy: (from: string, to: string, files?: string[]) => void;
46
+ /**
47
+ * A Node install without its global packages (nvm keeps those per version: often tens of thousands of small files,
48
+ * which is what made copying from this PC slow). Same file list as Copy-NodeFromHost in sandbox/bootstrap.ps1.
49
+ * Copied into a staging folder and renamed into place: node.exe alone (an interrupted copy) must not look like a full install.
50
+ */
51
+ export declare const copyNode: (from: string, to: string) => void;