windows-development-sandbox 1.5.0 → 1.7.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 (448) hide show
  1. package/README.md +1 -0
  2. package/config.example.json +17 -1
  3. package/dist/cli/args.d.ts +13 -2
  4. package/dist/cli/args.js +95 -24
  5. package/dist/cli/commands/accounts.js +45 -17
  6. package/dist/cli/commands/context.d.ts +2 -0
  7. package/dist/cli/commands/context.js +9 -2
  8. package/dist/cli/commands/data/clean.js +32 -9
  9. package/dist/cli/commands/index.d.ts +1 -0
  10. package/dist/cli/commands/index.js +1 -0
  11. package/dist/cli/commands/launch/add-root.d.ts +3 -2
  12. package/dist/cli/commands/launch/add-root.js +11 -10
  13. package/dist/cli/commands/launch/launch.js +57 -4
  14. package/dist/cli/commands/launch/prepare.js +3 -1
  15. package/dist/cli/commands/maintenance/audit.js +1 -1
  16. package/dist/cli/commands/maintenance/clean.d.ts +1 -1
  17. package/dist/cli/commands/maintenance/clean.js +17 -3
  18. package/dist/cli/commands/maintenance/config.d.ts +1 -1
  19. package/dist/cli/commands/maintenance/config.js +112 -10
  20. package/dist/cli/commands/maintenance/doctor.d.ts +1 -1
  21. package/dist/cli/commands/maintenance/doctor.js +2 -4
  22. package/dist/cli/commands/maintenance/migrate.d.ts +2 -1
  23. package/dist/cli/commands/maintenance/migrate.js +19 -4
  24. package/dist/cli/commands/maintenance/sync.js +2 -0
  25. package/dist/cli/commands/maintenance/update.d.ts +2 -0
  26. package/dist/cli/commands/maintenance/update.js +27 -4
  27. package/dist/cli/commands/projects/here.js +12 -7
  28. package/dist/cli/commands/projects/projects.js +26 -17
  29. package/dist/cli/commands/registry.js +7 -0
  30. package/dist/cli/commands/repair/checks/agents.d.ts +2 -0
  31. package/dist/cli/commands/repair/checks/agents.js +70 -0
  32. package/dist/cli/commands/repair/checks/common.d.ts +8 -0
  33. package/dist/cli/commands/repair/checks/common.js +22 -0
  34. package/dist/cli/commands/repair/checks/data.d.ts +2 -0
  35. package/dist/cli/commands/repair/checks/data.js +241 -0
  36. package/dist/cli/commands/repair/checks/index.d.ts +2 -0
  37. package/dist/cli/commands/repair/checks/index.js +20 -0
  38. package/dist/cli/commands/repair/checks/install.d.ts +5 -0
  39. package/dist/cli/commands/repair/checks/install.js +318 -0
  40. package/dist/cli/commands/repair/checks/machine.d.ts +2 -0
  41. package/dist/cli/commands/repair/checks/machine.js +232 -0
  42. package/dist/cli/commands/repair/checks/network.d.ts +6 -0
  43. package/dist/cli/commands/repair/checks/network.js +60 -0
  44. package/dist/cli/commands/repair/checks/pc.d.ts +4 -0
  45. package/dist/cli/commands/repair/checks/pc.js +55 -0
  46. package/dist/cli/commands/repair/checks/sandbox.d.ts +2 -0
  47. package/dist/cli/commands/repair/checks/sandbox.js +267 -0
  48. package/dist/cli/commands/repair/checks/settings.d.ts +2 -0
  49. package/dist/cli/commands/repair/checks/settings.js +169 -0
  50. package/dist/cli/commands/repair/checks/vpn.d.ts +2 -0
  51. package/dist/cli/commands/repair/checks/vpn.js +53 -0
  52. package/dist/cli/commands/repair/engine.d.ts +32 -0
  53. package/dist/cli/commands/repair/engine.js +127 -0
  54. package/dist/cli/commands/repair/env.d.ts +7 -0
  55. package/dist/cli/commands/repair/env.js +49 -0
  56. package/dist/cli/commands/repair/index.d.ts +1 -9
  57. package/dist/cli/commands/repair/index.js +2 -54
  58. package/dist/cli/commands/repair/log.d.ts +17 -0
  59. package/dist/cli/commands/repair/log.js +82 -0
  60. package/dist/cli/commands/repair/printer.d.ts +32 -0
  61. package/dist/cli/commands/repair/printer.js +132 -0
  62. package/dist/cli/commands/repair/run.d.ts +33 -0
  63. package/dist/cli/commands/repair/run.js +133 -0
  64. package/dist/cli/commands/repair/types.d.ts +97 -9
  65. package/dist/cli/commands/repair/types.js +11 -7
  66. package/dist/cli/commands/resume.js +2 -2
  67. package/dist/cli/commands/sessions/list.js +5 -2
  68. package/dist/cli/commands/sessions/status.d.ts +2 -0
  69. package/dist/cli/commands/sessions/status.js +27 -6
  70. package/dist/cli/commands/setup/index.d.ts +5 -0
  71. package/dist/cli/commands/setup/index.js +25 -1
  72. package/dist/cli/commands/storage/index.d.ts +2 -0
  73. package/dist/cli/commands/storage/index.js +2 -0
  74. package/dist/cli/commands/storage/offer.d.ts +3 -0
  75. package/dist/cli/commands/storage/offer.js +85 -0
  76. package/dist/cli/commands/storage/storage.d.ts +8 -0
  77. package/dist/cli/commands/storage/storage.js +171 -0
  78. package/dist/cli/commands/user-config.js +1 -1
  79. package/dist/cli/commands/vpn.d.ts +4 -0
  80. package/dist/cli/commands/vpn.js +75 -0
  81. package/dist/cli/commands/web.d.ts +4 -0
  82. package/dist/cli/commands/web.js +95 -0
  83. package/dist/cli/history.js +1 -1
  84. package/dist/cli/index.js +46 -3
  85. package/dist/cli/manifest.d.ts +303 -29
  86. package/dist/cli/manifest.js +173 -43
  87. package/dist/cli/menu/add-agent.d.ts +47 -0
  88. package/dist/cli/menu/add-agent.js +168 -0
  89. package/dist/cli/menu/apps.d.ts +31 -2
  90. package/dist/cli/menu/apps.js +44 -5
  91. package/dist/cli/menu/command-fields.d.ts +18 -0
  92. package/dist/cli/menu/command-fields.js +61 -0
  93. package/dist/cli/menu/command-form.d.ts +22 -0
  94. package/dist/cli/menu/command-form.js +169 -46
  95. package/dist/cli/menu/data-folder.d.ts +18 -0
  96. package/dist/cli/menu/data-folder.js +99 -0
  97. package/dist/cli/menu/menu.d.ts +1 -1
  98. package/dist/cli/menu/menu.js +11 -8
  99. package/dist/cli/menu/pages/accounts-page.js +55 -24
  100. package/dist/cli/menu/pages/agents-page.d.ts +66 -0
  101. package/dist/cli/menu/pages/agents-page.js +338 -0
  102. package/dist/cli/menu/pages/apps-page.js +1 -2
  103. package/dist/cli/menu/pages/list-page.d.ts +16 -3
  104. package/dist/cli/menu/pages/list-page.js +40 -8
  105. package/dist/cli/menu/pages/maintenance-page.d.ts +9 -0
  106. package/dist/cli/menu/pages/maintenance-page.js +161 -72
  107. package/dist/cli/menu/pages/sandbox-page.d.ts +3 -0
  108. package/dist/cli/menu/pages/sandbox-page.js +132 -43
  109. package/dist/cli/menu/pages/sessions-page.d.ts +4 -0
  110. package/dist/cli/menu/pages/sessions-page.js +41 -25
  111. package/dist/cli/menu/pages/settings-page.d.ts +8 -4
  112. package/dist/cli/menu/pages/settings-page.js +36 -13
  113. package/dist/cli/menu/settings/categories.d.ts +2 -0
  114. package/dist/cli/menu/settings/categories.js +19 -44
  115. package/dist/cli/menu/settings/collections/collection-editor.d.ts +7 -1
  116. package/dist/cli/menu/settings/collections/collection-editor.js +12 -8
  117. package/dist/cli/menu/settings/collections/map-editor.js +5 -1
  118. package/dist/cli/menu/settings/collections/object-list-editor.js +2 -2
  119. package/dist/cli/menu/settings/collections/object-map-editor.js +2 -2
  120. package/dist/cli/menu/settings/collections/string-list-editor.d.ts +11 -1
  121. package/dist/cli/menu/settings/collections/string-list-editor.js +23 -0
  122. package/dist/cli/menu/settings/index.d.ts +1 -1
  123. package/dist/cli/menu/settings/index.js +1 -1
  124. package/dist/cli/menu/settings/object-editor.d.ts +5 -1
  125. package/dist/cli/menu/settings/object-editor.js +15 -4
  126. package/dist/cli/menu/settings/path-kind.d.ts +20 -0
  127. package/dist/cli/menu/settings/path-kind.js +44 -0
  128. package/dist/cli/menu/settings/schema-node.d.ts +9 -0
  129. package/dist/cli/menu/settings/schema-node.js +26 -0
  130. package/dist/cli/menu/settings/value-editor.d.ts +5 -0
  131. package/dist/cli/menu/settings/value-editor.js +51 -18
  132. package/dist/cli/menu/several.js +7 -5
  133. package/dist/cli/menu/shell.d.ts +37 -10
  134. package/dist/cli/menu/shell.js +90 -10
  135. package/dist/cli/menu/vpn-login.d.ts +16 -0
  136. package/dist/cli/menu/vpn-login.js +66 -0
  137. package/dist/cli/output/print.d.ts +2 -0
  138. package/dist/cli/output/print.js +8 -1
  139. package/dist/cli/services/accounts-service.d.ts +2 -0
  140. package/dist/cli/services/accounts-service.js +2 -2
  141. package/dist/cli/services/health-service.d.ts +0 -6
  142. package/dist/cli/services/health-service.js +5 -42
  143. package/dist/cli/services/index.d.ts +3 -1
  144. package/dist/cli/services/index.js +3 -1
  145. package/dist/cli/services/migrate-service.js +1 -1
  146. package/dist/cli/services/update-service.d.ts +2 -0
  147. package/dist/cli/services/update-service.js +13 -6
  148. package/dist/cli/services/vpn-service.d.ts +28 -0
  149. package/dist/cli/services/vpn-service.js +46 -0
  150. package/dist/cli/services/web-service.d.ts +22 -0
  151. package/dist/cli/services/web-service.js +111 -0
  152. package/dist/cli/tui/app.d.ts +6 -3
  153. package/dist/cli/tui/app.js +12 -5
  154. package/dist/cli/tui/dialogs/path-listing.d.ts +43 -0
  155. package/dist/cli/tui/dialogs/path-listing.js +127 -0
  156. package/dist/cli/tui/dialogs/path-picker.d.ts +67 -0
  157. package/dist/cli/tui/dialogs/path-picker.js +423 -0
  158. package/dist/cli/tui/dialogs/text-screen.d.ts +6 -0
  159. package/dist/cli/tui/dialogs/text-screen.js +10 -3
  160. package/dist/cli/tui/frame.d.ts +7 -10
  161. package/dist/cli/tui/frame.js +19 -10
  162. package/dist/cli/tui/index.d.ts +4 -3
  163. package/dist/cli/tui/index.js +2 -1
  164. package/dist/cli/tui/list/list-screen.d.ts +14 -2
  165. package/dist/cli/tui/list/list-screen.js +68 -32
  166. package/dist/cli/tui/list/list-types.d.ts +8 -4
  167. package/dist/cli/tui/nav.d.ts +29 -0
  168. package/dist/cli/tui/nav.js +67 -0
  169. package/dist/cli/validate.d.ts +1 -1
  170. package/dist/cli/validate.js +3 -3
  171. package/dist/cli/version.d.ts +2 -0
  172. package/dist/cli/version.js +11 -0
  173. package/dist/core/config/defaults.js +5 -1
  174. package/dist/core/config/loader.js +12 -2
  175. package/dist/core/local-config/overrides.js +1 -0
  176. package/dist/core/prompt.d.ts +19 -0
  177. package/dist/core/prompt.js +43 -3
  178. package/dist/core/schema/common.d.ts +6 -0
  179. package/dist/core/schema/common.js +8 -0
  180. package/dist/core/schema/config.d.ts +23 -0
  181. package/dist/core/schema/config.js +10 -1
  182. package/dist/core/schema/harness.d.ts +1 -0
  183. package/dist/core/schema/harness.js +8 -2
  184. package/dist/core/schema/index.d.ts +3 -1
  185. package/dist/core/schema/index.js +3 -1
  186. package/dist/core/schema/profile.d.ts +1 -0
  187. package/dist/core/schema/profile.js +3 -0
  188. package/dist/core/schema/redact.d.ts +13 -0
  189. package/dist/core/schema/redact.js +75 -0
  190. package/dist/core/schema/sandbox.js +1 -0
  191. package/dist/core/schema/vpn.d.ts +17 -0
  192. package/dist/core/schema/vpn.js +22 -0
  193. package/dist/core/schema/web.d.ts +6 -0
  194. package/dist/core/schema/web.js +14 -0
  195. package/dist/core/types.d.ts +26 -0
  196. package/dist/guest/agents/agent-files.d.ts +49 -0
  197. package/dist/guest/agents/agent-files.js +63 -0
  198. package/dist/guest/agents/agent-installer.js +2 -9
  199. package/dist/guest/agents/local-state.d.ts +12 -0
  200. package/dist/guest/agents/local-state.js +198 -0
  201. package/dist/guest/agents/sbx-agent.d.ts +2 -0
  202. package/dist/guest/agents/sbx-agent.js +35 -0
  203. package/dist/guest/boot/boot.d.ts +4 -0
  204. package/dist/guest/boot/boot.js +50 -11
  205. package/dist/guest/daemon/daemon.js +4 -1
  206. package/dist/guest/daemon/exec.js +2 -1
  207. package/dist/guest/daemon/idle-shutdown.js +2 -0
  208. package/dist/guest/daemon/sessions.js +4 -1
  209. package/dist/guest/entries/agent.d.ts +1 -0
  210. package/dist/guest/entries/agent.js +10 -0
  211. package/dist/guest/entries/daemon.js +2 -0
  212. package/dist/guest/runtime/csc.d.ts +1 -0
  213. package/dist/guest/runtime/csc.js +1 -0
  214. package/dist/guest/runtime/secrets.d.ts +12 -0
  215. package/dist/guest/runtime/secrets.js +63 -0
  216. package/dist/guest/setup/claude-profile.d.ts +2 -1
  217. package/dist/guest/setup/claude-profile.js +12 -1
  218. package/dist/guest/setup/environment.d.ts +6 -0
  219. package/dist/guest/setup/environment.js +56 -6
  220. package/dist/guest/setup/path-plan.d.ts +32 -0
  221. package/dist/guest/setup/path-plan.js +67 -0
  222. package/dist/guest/setup/shell-path.d.ts +1 -0
  223. package/dist/guest/setup/shell-path.js +22 -0
  224. package/dist/guest/toolchains/languages/python.js +1 -1
  225. package/dist/guest/toolchains/runtimes.js +2 -2
  226. package/dist/guest/vpn/index.d.ts +1 -0
  227. package/dist/guest/vpn/index.js +1 -0
  228. package/dist/guest/vpn/openvpn.d.ts +9 -0
  229. package/dist/guest/vpn/openvpn.js +47 -0
  230. package/dist/guest/vpn/tunnel.d.ts +17 -0
  231. package/dist/guest/vpn/tunnel.js +78 -0
  232. package/dist/guest/vpn/vpn.d.ts +6 -0
  233. package/dist/guest/vpn/vpn.js +126 -0
  234. package/dist/host/channel/attach.d.ts +3 -1
  235. package/dist/host/channel/attach.js +1 -1
  236. package/dist/host/channel/channel.d.ts +7 -0
  237. package/dist/host/channel/channel.js +26 -2
  238. package/dist/host/health/acl-read.d.ts +17 -0
  239. package/dist/host/health/acl-read.js +45 -0
  240. package/dist/host/health/config-backup.d.ts +11 -0
  241. package/dist/host/health/config-backup.js +73 -0
  242. package/dist/host/health/index.d.ts +6 -0
  243. package/dist/host/health/index.js +7 -0
  244. package/dist/host/health/install-state.d.ts +33 -0
  245. package/dist/host/health/install-state.js +110 -0
  246. package/dist/host/health/path-copies.d.ts +32 -0
  247. package/dist/host/health/path-copies.js +82 -0
  248. package/dist/host/health/stale.d.ts +45 -0
  249. package/dist/host/health/stale.js +120 -0
  250. package/dist/host/health/system.d.ts +37 -0
  251. package/dist/host/health/system.js +87 -0
  252. package/dist/host/install/copies.d.ts +13 -0
  253. package/dist/host/install/copies.js +63 -0
  254. package/dist/host/install/index.d.ts +1 -0
  255. package/dist/host/install/index.js +1 -0
  256. package/dist/host/launch/generate.js +25 -1
  257. package/dist/host/launch/session-file.js +4 -1
  258. package/dist/host/launch/stage.js +1 -1
  259. package/dist/host/migrate/backup.js +3 -0
  260. package/dist/host/migrate/migrations/config-file.d.ts +2 -0
  261. package/dist/host/migrate/migrations/config-file.js +7 -0
  262. package/dist/host/migrate/migrations/follow-machine-storage.js +9 -1
  263. package/dist/host/migrate/migrations/pin-storage-root.d.ts +4 -0
  264. package/dist/host/migrate/migrations/pin-storage-root.js +46 -0
  265. package/dist/host/migrate/migrations/unpin-config-defaults.js +4 -1
  266. package/dist/host/migrate/registry.js +2 -1
  267. package/dist/host/profile/accounts.d.ts +25 -1
  268. package/dist/host/profile/accounts.js +96 -13
  269. package/dist/host/profile/agent-accounts.d.ts +5 -1
  270. package/dist/host/profile/agent-accounts.js +35 -5
  271. package/dist/host/profile/index.d.ts +2 -1
  272. package/dist/host/profile/index.js +1 -0
  273. package/dist/host/profile/plugin-share.d.ts +26 -0
  274. package/dist/host/profile/plugin-share.js +146 -0
  275. package/dist/host/projects/join.d.ts +7 -1
  276. package/dist/host/projects/join.js +21 -4
  277. package/dist/host/projects/registry.d.ts +16 -10
  278. package/dist/host/projects/registry.js +74 -6
  279. package/dist/host/sandbox/platform.d.ts +2 -0
  280. package/dist/host/sandbox/platform.js +21 -2
  281. package/dist/host/secrets/index.d.ts +2 -0
  282. package/dist/host/secrets/index.js +2 -0
  283. package/dist/host/secrets/launch-secrets.d.ts +17 -0
  284. package/dist/host/secrets/launch-secrets.js +93 -0
  285. package/dist/host/secrets/secret-store.d.ts +25 -0
  286. package/dist/host/secrets/secret-store.js +89 -0
  287. package/dist/host/sharing/acl.d.ts +16 -2
  288. package/dist/host/sharing/acl.js +35 -10
  289. package/dist/host/sharing/current.js +11 -2
  290. package/dist/host/sharing/doctor.d.ts +2 -0
  291. package/dist/host/sharing/doctor.js +44 -0
  292. package/dist/host/sharing/group-access.d.ts +6 -0
  293. package/dist/host/sharing/group-access.js +24 -0
  294. package/dist/host/sharing/index.d.ts +2 -1
  295. package/dist/host/sharing/index.js +2 -1
  296. package/dist/host/sharing/lifecycle.d.ts +8 -2
  297. package/dist/host/sharing/lifecycle.js +16 -7
  298. package/dist/host/storage/cleanup.d.ts +2 -0
  299. package/dist/host/storage/cleanup.js +2 -1
  300. package/dist/host/storage/history.d.ts +21 -0
  301. package/dist/host/storage/history.js +49 -0
  302. package/dist/host/storage/index.d.ts +4 -0
  303. package/dist/host/storage/index.js +4 -0
  304. package/dist/host/storage/move.d.ts +95 -0
  305. package/dist/host/storage/move.js +384 -0
  306. package/dist/host/storage/offer.d.ts +11 -0
  307. package/dist/host/storage/offer.js +36 -0
  308. package/dist/host/storage/root.d.ts +18 -0
  309. package/dist/host/storage/root.js +77 -0
  310. package/dist/host/tools/windows-terminal.d.ts +2 -0
  311. package/dist/host/tools/windows-terminal.js +23 -5
  312. package/dist/host/update/index.d.ts +1 -1
  313. package/dist/host/update/index.js +1 -1
  314. package/dist/host/update/notice.d.ts +2 -0
  315. package/dist/host/update/notice.js +13 -1
  316. package/dist/host/update/update-state.d.ts +6 -0
  317. package/dist/host/update/update-state.js +2 -0
  318. package/dist/host/update/updater.d.ts +19 -0
  319. package/dist/host/update/updater.js +88 -12
  320. package/dist/host/vpn/index.d.ts +4 -0
  321. package/dist/host/vpn/index.js +4 -0
  322. package/dist/host/vpn/login.d.ts +24 -0
  323. package/dist/host/vpn/login.js +79 -0
  324. package/dist/host/vpn/nordvpn.d.ts +21 -0
  325. package/dist/host/vpn/nordvpn.js +77 -0
  326. package/dist/host/vpn/prepare.d.ts +17 -0
  327. package/dist/host/vpn/prepare.js +77 -0
  328. package/dist/host/vpn/status.d.ts +12 -0
  329. package/dist/host/vpn/status.js +53 -0
  330. package/dist/host/web/auth.d.ts +38 -0
  331. package/dist/host/web/auth.js +96 -0
  332. package/dist/host/web/backend.d.ts +14 -0
  333. package/dist/host/web/backend.js +1 -0
  334. package/dist/host/web/bridge.d.ts +27 -0
  335. package/dist/host/web/bridge.js +144 -0
  336. package/dist/host/web/browser.d.ts +1 -0
  337. package/dist/host/web/browser.js +10 -0
  338. package/dist/host/web/control.d.ts +17 -0
  339. package/dist/host/web/control.js +67 -0
  340. package/dist/host/web/index.d.ts +9 -0
  341. package/dist/host/web/index.js +8 -0
  342. package/dist/host/web/runner.d.ts +14 -0
  343. package/dist/host/web/runner.js +45 -0
  344. package/dist/host/web/server.d.ts +40 -0
  345. package/dist/host/web/server.js +218 -0
  346. package/dist/host/web/state.d.ts +20 -0
  347. package/dist/host/web/state.js +41 -0
  348. package/dist/host/web/static-files.d.ts +13 -0
  349. package/dist/host/web/static-files.js +57 -0
  350. package/dist/runtime/agent.js +27614 -0
  351. package/dist/runtime/daemon.js +1012 -618
  352. package/dist/runtime/db.js +236 -106
  353. package/dist/runtime/main.js +1093 -250
  354. package/dist/runtime/netproxy.js +102 -3
  355. package/dist/runtime/view.js +102 -3
  356. package/dist/selftest/accounts.test.js +40 -0
  357. package/dist/selftest/agent-paths.test.d.ts +2 -0
  358. package/dist/selftest/agent-paths.test.js +115 -0
  359. package/dist/selftest/channel.test.js +5 -0
  360. package/dist/selftest/cli.test.js +34 -4
  361. package/dist/selftest/credentials.test.d.ts +2 -0
  362. package/dist/selftest/credentials.test.js +198 -0
  363. package/dist/selftest/generate.test.js +1 -1
  364. package/dist/selftest/health.test.d.ts +2 -0
  365. package/dist/selftest/health.test.js +470 -0
  366. package/dist/selftest/host-sim.test.js +2 -0
  367. package/dist/selftest/index.js +19 -0
  368. package/dist/selftest/layout.test.js +2 -0
  369. package/dist/selftest/menu-screens.d.ts +7 -0
  370. package/dist/selftest/menu-screens.js +65 -16
  371. package/dist/selftest/menu.test.js +574 -21
  372. package/dist/selftest/migrate.test.js +1 -1
  373. package/dist/selftest/personalization.test.js +3 -1
  374. package/dist/selftest/projects.test.js +39 -1
  375. package/dist/selftest/shared-use.test.js +64 -2
  376. package/dist/selftest/sharing.test.js +50 -1
  377. package/dist/selftest/storage.test.d.ts +2 -0
  378. package/dist/selftest/storage.test.js +363 -0
  379. package/dist/selftest/support/fixtures.d.ts +14 -0
  380. package/dist/selftest/support/releases.js +1 -1
  381. package/dist/selftest/toolchains.test.js +21 -0
  382. package/dist/selftest/update-service.test.js +87 -3
  383. package/dist/selftest/vpn.test.d.ts +2 -0
  384. package/dist/selftest/vpn.test.js +279 -0
  385. package/dist/selftest/web.test.d.ts +2 -0
  386. package/dist/selftest/web.test.js +304 -0
  387. package/dist/shared/apps/catalogue.js +31 -0
  388. package/dist/shared/channel/index.d.ts +1 -0
  389. package/dist/shared/channel/index.js +1 -0
  390. package/dist/shared/channel/run-dir.d.ts +6 -1
  391. package/dist/shared/channel/run-dir.js +12 -1
  392. package/dist/shared/channel/schema.d.ts +19 -0
  393. package/dist/shared/channel/schema.js +11 -0
  394. package/dist/shared/channel/secrets.d.ts +28 -0
  395. package/dist/shared/channel/secrets.js +44 -0
  396. package/dist/shared/vpn/download.d.ts +13 -0
  397. package/dist/shared/vpn/download.js +14 -0
  398. package/dist/shared/vpn/kill-switch.d.ts +18 -0
  399. package/dist/shared/vpn/kill-switch.js +81 -0
  400. package/dist/shared/vpn/ovpn.d.ts +23 -0
  401. package/dist/shared/vpn/ovpn.js +244 -0
  402. package/dist/shared/web/index.d.ts +1 -0
  403. package/dist/shared/web/index.js +1 -0
  404. package/dist/shared/web/protocol.d.ts +61 -0
  405. package/dist/shared/web/protocol.js +35 -0
  406. package/dist/sync/sync-cycle.js +5 -1
  407. package/dist/sync/sync-state.d.ts +1 -0
  408. package/dist/sync/sync-state.js +1 -0
  409. package/dist/sync/watcher.js +10 -0
  410. package/dist/web/index.html +13 -0
  411. package/dist/web/main-CXESL347.js +45 -0
  412. package/dist/web/main-VLR7DVQ4.css +1 -0
  413. package/native/csharp/FolderSecurity.cs +29 -0
  414. package/native/scripts/protect-data.ps1 +29 -0
  415. package/native/scripts/read-folder-sddl.ps1 +10 -0
  416. package/native/scripts/set-folder-access.ps1 +49 -7
  417. package/node_modules/ws/LICENSE +20 -0
  418. package/node_modules/ws/README.md +548 -0
  419. package/node_modules/ws/browser.js +8 -0
  420. package/node_modules/ws/index.js +22 -0
  421. package/node_modules/ws/lib/buffer-util.js +131 -0
  422. package/node_modules/ws/lib/constants.js +19 -0
  423. package/node_modules/ws/lib/event-target.js +292 -0
  424. package/node_modules/ws/lib/extension.js +203 -0
  425. package/node_modules/ws/lib/limiter.js +55 -0
  426. package/node_modules/ws/lib/permessage-deflate.js +530 -0
  427. package/node_modules/ws/lib/receiver.js +743 -0
  428. package/node_modules/ws/lib/sender.js +607 -0
  429. package/node_modules/ws/lib/stream.js +161 -0
  430. package/node_modules/ws/lib/subprotocol.js +62 -0
  431. package/node_modules/ws/lib/validation.js +152 -0
  432. package/node_modules/ws/lib/websocket-server.js +562 -0
  433. package/node_modules/ws/lib/websocket.js +1421 -0
  434. package/node_modules/ws/package.json +74 -0
  435. package/node_modules/ws/wrapper.mjs +21 -0
  436. package/package.json +15 -5
  437. package/sandbox/csharp/EnvBroadcast.cs +33 -0
  438. package/sandbox/csharp/PathSync.cs +90 -0
  439. package/sandbox/scripts/install-shell-path.ps1 +73 -0
  440. package/sandbox/scripts/set-machine-environment.ps1 +37 -4
  441. package/schemas/config.schema.json +79 -4
  442. package/schemas/sandbox-config.schema.json +14 -1
  443. package/dist/cli/commands/repair/checks.d.ts +0 -2
  444. package/dist/cli/commands/repair/checks.js +0 -246
  445. package/dist/cli/menu/pages/start-page.d.ts +0 -43
  446. package/dist/cli/menu/pages/start-page.js +0 -192
  447. package/dist/cli/tui/tab-bar.d.ts +0 -9
  448. package/dist/cli/tui/tab-bar.js +0 -33
@@ -0,0 +1,198 @@
1
+ // Agents that keep SQLite databases (codex ...): SQLite cannot map its shared memory in a mapped folder (error 8714,
2
+ // "disk I/O error"), so they run from a copy on the sandbox's own disk. The copy comes from persistent storage at boot
3
+ // and goes back when no agent process holds the files, so the data lives on between sandboxes.
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { SYSTEM } from '../../shared/system.js';
7
+ import { dryRun, isWin, localDir } from '../runtime/paths.js';
8
+ import { exists, mkdirp } from '../runtime/fs.js';
9
+ import { execOutput } from '../runtime/process.js';
10
+ import { log, warn } from '../runtime/log.js';
11
+ import { session, stateDir } from '../runtime/session.js';
12
+ import { errMsg } from '../../core/util/errors.js';
13
+ import { isCopyableDatabaseFile, isDatabaseFile, localStateEntries } from './agent-files.js';
14
+ const SQLITE_HEADER = 'SQLite format 3\0';
15
+ /** The sandbox-disk folder that holds the working copies. */
16
+ const localRoot = () => path.join(localDir, 'agent-state');
17
+ /** Every SQLite folder of every configured agent (none in launch folders that predate the agents list). */
18
+ export function agentLocalStates() {
19
+ return localStateEntries(session.agents ?? {}, stateDir, localRoot());
20
+ }
21
+ /** Variable -> working folder, for the agent environment (and the machine environment of new terminal tabs). */
22
+ export function localStateVars() {
23
+ return Object.fromEntries(agentLocalStates().map((s) => [s.env, s.local]));
24
+ }
25
+ const databaseFiles = (dir, keep) => exists(dir)
26
+ ? fs.readdirSync(dir, { withFileTypes: true }).flatMap((e) => (e.isFile() && keep(e.name) ? [e.name] : []))
27
+ : [];
28
+ /** A database file that starts like SQLite (an empty or cut-off file must never replace a saved copy). */
29
+ const looksLikeSqlite = (file) => {
30
+ const fd = fs.openSync(file, 'r');
31
+ try {
32
+ const head = Buffer.alloc(16);
33
+ return fs.readSync(fd, head, 0, 16, 0) === 16 && head.toString('latin1') === SQLITE_HEADER;
34
+ }
35
+ finally {
36
+ fs.closeSync(fd);
37
+ }
38
+ };
39
+ const copyFiles = (from, to, names) => {
40
+ fs.rmSync(to, { recursive: true, force: true });
41
+ mkdirp(to);
42
+ for (const name of names)
43
+ fs.copyFileSync(path.join(from, name), path.join(to, name));
44
+ };
45
+ /** Replaces the folder `store` with the staged `incoming` folder; the old one is only removed once the new is in. */
46
+ const swapIn = (incoming, store) => {
47
+ const previous = store + '.previous';
48
+ fs.rmSync(previous, { recursive: true, force: true });
49
+ if (exists(store))
50
+ fs.renameSync(store, previous);
51
+ try {
52
+ fs.renameSync(incoming, store);
53
+ }
54
+ catch (e) {
55
+ if (exists(previous))
56
+ fs.renameSync(previous, store);
57
+ throw e;
58
+ }
59
+ fs.rmSync(previous, { recursive: true, force: true });
60
+ };
61
+ /**
62
+ * Databases an agent kept in its state folder before (codex: state_5.sqlite ...) move into the saved copy. The
63
+ * originals
64
+ * go only after the copy is in place and has the same sizes.
65
+ */
66
+ function adoptLegacy(s) {
67
+ if (exists(s.store))
68
+ return;
69
+ const names = databaseFiles(s.legacy, isDatabaseFile);
70
+ if (!names.length)
71
+ return;
72
+ const copyable = names.filter(isCopyableDatabaseFile);
73
+ const incoming = s.store + '.incoming';
74
+ copyFiles(s.legacy, incoming, copyable);
75
+ swapIn(incoming, s.store);
76
+ const same = copyable.every((n) => fs.statSync(path.join(s.store, n)).size === fs.statSync(path.join(s.legacy, n)).size);
77
+ if (!same) {
78
+ warn(s.agent + ': the copied databases differ in size from the originals; the originals were kept.');
79
+ return;
80
+ }
81
+ for (const n of names)
82
+ fs.rmSync(path.join(s.legacy, n), { force: true });
83
+ log(s.agent + ': moved ' + copyable.length + ' database file(s) to ' + s.store + '.');
84
+ }
85
+ /** Boot: the saved databases of every configured agent are copied to the sandbox's disk, where the agent runs them. */
86
+ export function restoreLocalState() {
87
+ if (dryRun)
88
+ return;
89
+ for (const s of agentLocalStates()) {
90
+ try {
91
+ adoptLegacy(s);
92
+ mkdirp(s.local);
93
+ const names = databaseFiles(s.store, isCopyableDatabaseFile);
94
+ for (const n of names)
95
+ fs.copyFileSync(path.join(s.store, n), path.join(s.local, n));
96
+ if (names.length)
97
+ log(s.agent + ': databases restored to ' + s.local + '.');
98
+ }
99
+ catch (e) {
100
+ warn(s.agent + ': could not restore its databases (' + errMsg(e) + '); it starts with empty ones.');
101
+ }
102
+ }
103
+ }
104
+ /** True while one of the programs that open the databases runs. */
105
+ function held(s) {
106
+ if (!isWin)
107
+ return false;
108
+ return s.processes.some((image) => execOutput(SYSTEM.tasklist(), ['/FI', 'IMAGENAME eq ' + image, '/NH', '/FO', 'CSV'])
109
+ .toLowerCase()
110
+ .includes(image.toLowerCase()));
111
+ }
112
+ const fingerprint = (dir) => databaseFiles(dir, isCopyableDatabaseFile)
113
+ .sort()
114
+ .map((n) => {
115
+ const st = fs.statSync(path.join(dir, n));
116
+ return n + ':' + st.size + ':' + st.mtimeMs;
117
+ })
118
+ .join('|');
119
+ /** The working copies as of the last save by this process. */
120
+ const saved = new Map();
121
+ /** Snapshots in progress (one per working folder) and whether the missing online backup was said already. */
122
+ const snapshotting = new Set();
123
+ let saidNoBackup = false;
124
+ /**
125
+ * While the agent runs its databases are never copied as files (the -wal and the database would not match): SQLite's
126
+ * online backup writes a consistent snapshot into the staging folder, which is then swapped in. Needs node:sqlite
127
+ * (newer Node); without it the save waits until the agent has exited.
128
+ */
129
+ function snapshotWhileRunning(s, print) {
130
+ if (snapshotting.has(s.local))
131
+ return;
132
+ snapshotting.add(s.local);
133
+ void (async () => {
134
+ try {
135
+ const sqlite = await import('node:sqlite').catch(() => null);
136
+ if (!sqlite || typeof sqlite.backup !== 'function') {
137
+ if (!saidNoBackup)
138
+ log(s.agent + ': this Node has no SQLite online backup; its databases are saved when it exits.');
139
+ saidNoBackup = true;
140
+ return;
141
+ }
142
+ const incoming = s.store + '.incoming';
143
+ fs.rmSync(incoming, { recursive: true, force: true });
144
+ mkdirp(incoming);
145
+ for (const name of databaseFiles(s.local, (n) => isCopyableDatabaseFile(n) && !/-wal$/i.test(n))) {
146
+ const db = new sqlite.DatabaseSync(path.join(s.local, name), { readOnly: true });
147
+ try {
148
+ await sqlite.backup(db, path.join(incoming, name));
149
+ }
150
+ finally {
151
+ db.close();
152
+ }
153
+ }
154
+ swapIn(incoming, s.store);
155
+ saved.set(s.local, print);
156
+ }
157
+ catch (e) {
158
+ warn(s.agent + ': could not snapshot its running databases: ' + errMsg(e));
159
+ }
160
+ finally {
161
+ snapshotting.delete(s.local);
162
+ }
163
+ })();
164
+ }
165
+ /**
166
+ * Copies the working databases back to persistent storage (staging folder + swap). While the agent runs they are
167
+ * snapshotted with SQLite's online backup instead (see snapshotWhileRunning); nothing is done when nothing changed.
168
+ */
169
+ export function saveLocalState() {
170
+ if (dryRun)
171
+ return;
172
+ for (const s of agentLocalStates()) {
173
+ try {
174
+ if (!exists(s.local))
175
+ continue;
176
+ const print = fingerprint(s.local);
177
+ if (!print || (saved.get(s.local) === print && exists(s.store)))
178
+ continue;
179
+ if (held(s)) {
180
+ snapshotWhileRunning(s, print);
181
+ continue;
182
+ }
183
+ const names = databaseFiles(s.local, isCopyableDatabaseFile);
184
+ const bad = names.filter((n) => !/-wal$/i.test(n) && !looksLikeSqlite(path.join(s.local, n)));
185
+ if (bad.length) {
186
+ warn(s.agent + ': ' + bad.join(', ') + ' do not look like SQLite databases, so they were not saved.');
187
+ continue;
188
+ }
189
+ const incoming = s.store + '.incoming';
190
+ copyFiles(s.local, incoming, names);
191
+ swapIn(incoming, s.store);
192
+ saved.set(s.local, print);
193
+ }
194
+ catch (e) {
195
+ warn(s.agent + ': could not save its databases: ' + errMsg(e));
196
+ }
197
+ }
198
+ }
@@ -0,0 +1,2 @@
1
+ /** Runs one sbx-agent command; returns the exit code. Prints, as the command line tool it is. */
2
+ export declare function runSbxAgent(argv: readonly string[]): Promise<number>;
@@ -0,0 +1,35 @@
1
+ // `sbx-agent`: install a configured agent from a terminal inside the sandbox (the agent commands call it the first
2
+ // time). Usage: sbx-agent list | sbx-agent install <agent>
3
+ import { agents } from './agent-installer.js';
4
+ import { agentCommand, agentPaths } from './agent-files.js';
5
+ import { toolsDir } from '../runtime/paths.js';
6
+ import { exists } from '../runtime/fs.js';
7
+ import { session } from '../runtime/session.js';
8
+ /** Runs one sbx-agent command; returns the exit code. Prints, as the command line tool it is. */
9
+ export async function runSbxAgent(argv) {
10
+ const known = session.agents ?? {};
11
+ const [verb, name] = argv;
12
+ if (verb === 'list' || verb === undefined) {
13
+ for (const [agent, harness] of Object.entries(known)) {
14
+ const p = agentPaths(toolsDir, agent, harness);
15
+ const installed = (p.exe !== null && exists(p.exe)) || (p.cmd !== null && exists(p.cmd));
16
+ console.log(agentCommand(agent, harness).padEnd(14) + (installed ? 'installed' : 'not installed yet'));
17
+ }
18
+ return 0;
19
+ }
20
+ const harness = verb === 'install' && name ? known[name] : undefined;
21
+ if (!harness) {
22
+ console.error(verb === 'install' && name
23
+ ? 'Unknown agent "' + name + '". Configured agents: ' + Object.keys(known).join(', ')
24
+ : 'Usage: sbx-agent list | sbx-agent install <agent>');
25
+ return 1;
26
+ }
27
+ try {
28
+ await agents.provision(name, harness, process.env, false);
29
+ }
30
+ catch (e) {
31
+ console.error('Could not install ' + name + ': ' + (e instanceof Error ? e.message : String(e)));
32
+ return 1;
33
+ }
34
+ return 0;
35
+ }
@@ -3,11 +3,15 @@ export declare class Boot {
3
3
  private ptyHost;
4
4
  private lan;
5
5
  private readonly gitAccess;
6
+ /** What the host handed over in its one-shot file (read and deleted at the start of run()). */
7
+ private secrets;
6
8
  run(): Promise<void>;
7
9
  private networkAndSessionHost;
8
10
  private hooksAndDesktop;
9
11
  /** Toolchains, databases, apps, the network proxy and VS Code; then the agent environment, saved machine-wide. */
10
12
  private developerTools;
13
+ /** vpn.enabled: the tunnel and its kill switch, before anything else uses the network (git, the agent). */
14
+ private vpn;
11
15
  private gitIdentity;
12
16
  private claude;
13
17
  private agent;
@@ -5,6 +5,9 @@ import { spawn } from 'node:child_process';
5
5
  import { writeGuides } from './guide.js';
6
6
  import { setupToolchains } from '../toolchains/index.js';
7
7
  import { agents } from '../agents/agent-installer.js';
8
+ import { agentShims, sbxAgentText, shimText } from '../agents/agent-files.js';
9
+ import { restoreLocalState } from '../agents/local-state.js';
10
+ import { installShellPath } from '../setup/shell-path.js';
8
11
  import { linkProfileDirs } from '../agents/profile-links.js';
9
12
  import { restoreClaudeMem } from '../setup/claude-mem.js';
10
13
  import { syncClaudeProfile } from '../setup/claude-profile.js';
@@ -20,6 +23,7 @@ import { pruneToolLeftovers } from '../toolchains/leftovers.js';
20
23
  import { wanted } from '../toolchains/detect.js';
21
24
  import { configureVscode } from '../toolchains/tools/vscode.js';
22
25
  import { enableProcessAudit } from '../audit/process-audit.js';
26
+ import { setupVpn } from '../vpn/index.js';
23
27
  import { now } from '../../shared/channel/run-dir.js';
24
28
  import { writeJsonAtomic } from '../../shared/io/json.js';
25
29
  import { viewSession } from '../sessions/viewer.js';
@@ -29,9 +33,10 @@ import { dryRun, isWin, localDir, logsDir, run, toolsDir } from '../runtime/path
29
33
  import { log, logPath, warn } from '../runtime/log.js';
30
34
  import { mkdirp } from '../runtime/fs.js';
31
35
  import { session, workspace } from '../runtime/session.js';
36
+ import { applyBootEnvironment, BOOT_SECRETS_VARIABLE, takeSecrets } from '../runtime/secrets.js';
32
37
  import { setSandboxStatus } from '../runtime/status.js';
33
38
  import { errMsg } from '../../core/util/errors.js';
34
- const STEPS = 8;
39
+ const STEPS = 9;
35
40
  const PRIMARY_POLLS = 600;
36
41
  const POLL_MS = 500;
37
42
  const VSCODE_LANGUAGES = ['python', 'java', 'go', 'rust', 'cpp', 'msvc', 'dotnet', 'ruby', 'php', 'deno', 'pwsh'];
@@ -56,12 +61,19 @@ async function soft(what, work) {
56
61
  warn(what + ' failed (continuing): ' + errMsg(e));
57
62
  }
58
63
  }
59
- function startDaemon(env) {
64
+ function startDaemon(env, secrets) {
60
65
  spawn(process.execPath, [path.join(path.dirname(process.argv[1]), 'daemon.js')], {
61
66
  detached: true,
62
67
  stdio: 'ignore',
63
68
  windowsHide: true,
64
- env: { ...env, AGENT_LOG_PREFIX: 'daemon' },
69
+ env: {
70
+ ...env,
71
+ AGENT_LOG_PREFIX: 'daemon',
72
+ // The agent's environment (it may hold API keys): the daemon moves it into memory and removes this at once.
73
+ ...(secrets.harnessEnvironment
74
+ ? { [BOOT_SECRETS_VARIABLE]: JSON.stringify({ harnessEnvironment: secrets.harnessEnvironment }) }
75
+ : {}),
76
+ },
65
77
  }).unref();
66
78
  }
67
79
  /**
@@ -72,8 +84,19 @@ function writeDbCommand() {
72
84
  const dir = mkdirp(path.join(localDir, 'bin'));
73
85
  const db = path.join(path.dirname(process.argv[1]), 'db.js');
74
86
  fs.writeFileSync(path.join(dir, 'sbx-db.cmd'), '@setlocal\r\n@set AGENT_LOG_PREFIX=db\r\n@"' + process.execPath + '" "' + db + '" %*\r\n');
87
+ writeAgentCommands(dir);
75
88
  return dir;
76
89
  }
90
+ /**
91
+ * `claude`, `codex` ... by name in every terminal tab, installed yet or not (the command installs the agent on first
92
+ * use), and `sbx-agent`. Written to the sandbox's disk each boot, next to `sbx-db`.
93
+ */
94
+ function writeAgentCommands(dir) {
95
+ const install = { node: process.execPath, script: path.join(path.dirname(process.argv[1]), 'agent.js') };
96
+ for (const shim of agentShims(toolsDir, session.agents ?? {}))
97
+ fs.writeFileSync(path.join(dir, shim.command + '.cmd'), shimText(shim, install));
98
+ fs.writeFileSync(path.join(dir, 'sbx-agent.cmd'), sbxAgentText(install));
99
+ }
77
100
  async function waitForPrimary() {
78
101
  for (let i = 0; i < PRIMARY_POLLS; i++) {
79
102
  const s = run.readSession(session.launchId);
@@ -97,6 +120,8 @@ export class Boot {
97
120
  ptyHost = null;
98
121
  lan = session.networking ? 'open' : 'offline';
99
122
  gitAccess = session.provisioning.git.access ?? 'full';
123
+ /** What the host handed over in its one-shot file (read and deleted at the start of run()). */
124
+ secrets = {};
100
125
  async run() {
101
126
  log('Agent sandbox: ' +
102
127
  session.project +
@@ -110,9 +135,12 @@ export class Boot {
110
135
  throw new Error('Workspace folder is missing in the sandbox: ' + workspace);
111
136
  mkdirp(toolsDir);
112
137
  mkdirp(run.root);
138
+ this.secrets = takeSecrets(session.launchId);
139
+ applyBootEnvironment(this.secrets.harnessEnvironment);
113
140
  await this.networkAndSessionHost();
114
141
  await this.hooksAndDesktop();
115
142
  const { tools, env } = await this.developerTools();
143
+ await this.vpn();
116
144
  await this.gitIdentity(env);
117
145
  await this.claude(tools);
118
146
  await this.agent(env);
@@ -165,6 +193,7 @@ export class Boot {
165
193
  }));
166
194
  const env = agentEnv(tools);
167
195
  persistEnvironment(env, Object.keys(tools.vars));
196
+ await soft('Shell start-up PATH', installShellPath);
168
197
  saveBootEnv({
169
198
  basePath,
170
199
  toolVars: Object.keys(tools.vars),
@@ -178,36 +207,46 @@ export class Boot {
178
207
  await soft('Cleaning old tool builds', () => pruneToolLeftovers());
179
208
  return { tools, env };
180
209
  }
210
+ /** vpn.enabled: the tunnel and its kill switch, before anything else uses the network (git, the agent). */
211
+ async vpn() {
212
+ step(4, session.vpn ? 'VPN (' + session.vpn.server + ')' : 'VPN (off)');
213
+ if (!session.vpn)
214
+ return;
215
+ await soft('VPN', async () => {
216
+ await setupVpn(this.secrets, this.ptyHost);
217
+ });
218
+ }
181
219
  async gitIdentity(env) {
182
- step(4, 'Git identity');
220
+ step(5, 'Git identity');
183
221
  await soft('Git config', () => setupGitConfig(env));
184
222
  }
185
223
  async claude(tools) {
186
- step(5, 'Claude settings, history and claude-mem');
224
+ step(6, 'Claude settings, history and claude-mem');
187
225
  if (session.harnessName === 'claude')
188
- await soft('Claude settings', syncClaudeProfile);
226
+ await soft('Claude settings', () => syncClaudeProfile(this.secrets.claudeCredentials));
189
227
  await soft('claude-mem restore', restoreClaudeMem);
228
+ await soft('Agent databases restore', restoreLocalState);
190
229
  await soft('Sandbox guide for the agent', () => writeGuides(tools.summary, this.lan));
191
230
  await soft('Claude managed settings', () => writeManagedSettings(this.gitAccess));
192
231
  }
193
232
  async agent(env) {
194
- step(6, 'Agent: ' + session.harnessName);
233
+ step(7, 'Agent: ' + session.harnessName);
195
234
  linkProfileDirs(session.agentProfileDirs ?? {});
196
235
  if (session.harness)
197
236
  await agents.provision(session.harnessName, session.harness, env);
198
237
  }
199
238
  preAgentHooks(env) {
200
- step(7, 'Pre-agent hooks');
239
+ step(8, 'Pre-agent hooks');
201
240
  runHooks('pre-agent', env);
202
241
  }
203
242
  /** Ready: starts the session daemon and, with an agent, its first session, shown in this window. */
204
243
  async start(env) {
205
- step(8, 'Starting ' + session.harnessName);
244
+ step(9, 'Starting ' + session.harnessName);
206
245
  setSandboxStatus('ready', 'Ready');
207
246
  // No agent, or a restart that adds a project (the host opens its sessions itself once the sandbox is ready).
208
247
  if (!session.harness || session.autoSession === false) {
209
248
  if (!dryRun)
210
- startDaemon(env);
249
+ startDaemon(env, this.secrets);
211
250
  log('Ready' + (session.harness ? '' : ' (no agent requested)') + '. Workspace: ' + workspace);
212
251
  return;
213
252
  }
@@ -220,7 +259,7 @@ export class Boot {
220
259
  runHooks('post-agent', env);
221
260
  return;
222
261
  }
223
- startDaemon(env);
262
+ startDaemon(env, this.secrets);
224
263
  writeJsonAtomic(run.requestFile(session.launchId), {
225
264
  kind: 'session',
226
265
  id: session.launchId,
@@ -1,10 +1,12 @@
1
1
  // The session daemon's polling loop: claims each valid request in run\requests and hands it to its handler.
2
2
  import fs from 'node:fs';
3
3
  import { saveClaudeMem } from '../setup/claude-mem.js';
4
+ import { saveLocalState } from '../agents/local-state.js';
4
5
  import { execCommand } from './exec.js';
5
6
  import { sessions } from './sessions.js';
6
7
  import { respond, safely } from './state.js';
7
8
  import { run } from '../runtime/paths.js';
9
+ import { withRequestSecrets } from '../runtime/secrets.js';
8
10
  import { warn } from '../runtime/log.js';
9
11
  import { errMsg } from '../../core/util/errors.js';
10
12
  const POLL_MS = 400;
@@ -33,11 +35,12 @@ export class Daemon {
33
35
  if (run.readSession(req.id))
34
36
  continue;
35
37
  // Not awaited: starting a session or running a command must not hold up other requests.
36
- void Promise.resolve(this.handle(req)).catch((e) => respond(req.id, { ok: false, message: errMsg(e) }));
38
+ void Promise.resolve(this.handle(withRequestSecrets(req))).catch((e) => respond(req.id, { ok: false, message: errMsg(e) }));
37
39
  }
38
40
  if (Date.now() - this.lastMemSave > MEM_SAVE_MS) {
39
41
  this.lastMemSave = Date.now();
40
42
  safely('claude-mem save', saveClaudeMem);
43
+ safely('agent databases save', saveLocalState);
41
44
  }
42
45
  }
43
46
  handle(req) {
@@ -2,6 +2,7 @@
2
2
  import { spawn } from 'node:child_process';
3
3
  import { SYSTEM } from '../../shared/system.js';
4
4
  import { exists } from '../runtime/fs.js';
5
+ import { withRegistryPath } from '../setup/environment.js';
5
6
  import { errMsg } from '../../core/util/errors.js';
6
7
  import { provisioner } from './project-provisioner.js';
7
8
  import { insideWorkspace, respond } from './state.js';
@@ -20,7 +21,7 @@ export function execCommand(req) {
20
21
  }
21
22
  // chcp 65001 first: console programs then write UTF-8 (not the OEM code page) into the pipes.
22
23
  // In a project registered after boot, its own environment (its Node, Python, database variables).
23
- const env = provisioner.provisionedFor(req.cwd)?.env ?? process.env;
24
+ const env = withRegistryPath(provisioner.provisionedFor(req.cwd)?.env ?? process.env);
24
25
  const child = spawn(SYSTEM.cmd(), ['/d', '/s', '/c', '"chcp 65001>nul & ' + req.command.map(quoteForCmd).join(' ') + '"'], { cwd: req.cwd, env, windowsHide: true, windowsVerbatimArguments: true });
25
26
  const output = { stdout: '', stderr: '' };
26
27
  // setEncoding decodes across chunks: a multi-byte character split between two reads is not mangled.
@@ -2,6 +2,7 @@
2
2
  import { spawnSync } from 'node:child_process';
3
3
  import { SYSTEM } from '../../shared/system.js';
4
4
  import { saveClaudeMem } from '../setup/claude-mem.js';
5
+ import { saveLocalState } from '../agents/local-state.js';
5
6
  import { isWin, run } from '../runtime/paths.js';
6
7
  import { log } from '../runtime/log.js';
7
8
  import { session } from '../runtime/session.js';
@@ -25,6 +26,7 @@ export class IdleShutdown {
25
26
  return;
26
27
  log('Headless sandbox has no sessions left; shutting down.');
27
28
  safely('claude-mem save', saveClaudeMem);
29
+ safely('agent databases save', saveLocalState);
28
30
  spawnSync(SYSTEM.shutdown(), ['/s', '/t', '5'], { windowsHide: true });
29
31
  }, GRACE_MS);
30
32
  }
@@ -14,8 +14,10 @@ import { primaryProject } from '../boot/project-status.js';
14
14
  import { ensurePtyHost, ptyHostLogTail, startPtyHost, waitForPtyHost } from '../sessions/pty-host.js';
15
15
  import { agents } from '../agents/agent-installer.js';
16
16
  import { saveClaudeMem } from '../setup/claude-mem.js';
17
+ import { saveLocalState } from '../agents/local-state.js';
17
18
  import { projectHooks, runHooks, runHooksAsync } from '../setup/hooks.js';
18
19
  import { guestAddresses } from '../network/addresses.js';
20
+ import { withRegistryPath } from '../setup/environment.js';
19
21
  import { dataDir, toolsDir } from '../runtime/paths.js';
20
22
  import { exists } from '../runtime/fs.js';
21
23
  import { log, warn } from '../runtime/log.js';
@@ -64,7 +66,7 @@ const launchPtyHost = async (req, remote, env, say) => {
64
66
  rows: 32,
65
67
  id: req.id,
66
68
  cwd: req.cwd,
67
- env: { ...env, ...harness.environment },
69
+ env: { ...withRegistryPath(env), ...harness.environment },
68
70
  remote,
69
71
  command: commandLine(launch.file, [...base, ...req.args]),
70
72
  restartCommand: commandLine(launch.file, [...base, ...(harness.resumeArguments ?? [])]),
@@ -257,6 +259,7 @@ export class SessionManager {
257
259
  else
258
260
  safely('post-agent hooks', () => runHooks('post-agent', process.env));
259
261
  safely('claude-mem save', saveClaudeMem);
262
+ safely('agent databases save', saveLocalState);
260
263
  this.idle.check();
261
264
  }
262
265
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ // `sbx-agent`: list agents and install a configured one inside the sandbox. Usage: sbx-agent list | install <agent>
2
+ import { dropInheritedModulePath } from '../../shared/system.js';
3
+ import { runSbxAgent } from '../agents/sbx-agent.js';
4
+ dropInheritedModulePath();
5
+ runSbxAgent(process.argv.slice(2)).then((code) => {
6
+ process.exitCode = code;
7
+ }, (e) => {
8
+ console.error(e instanceof Error ? e.message : String(e));
9
+ process.exitCode = 1;
10
+ });
@@ -6,10 +6,12 @@ import { startRelays } from '../daemon/relays.js';
6
6
  import { startProcessCollector } from '../audit/process-audit.js';
7
7
  import { safely } from '../daemon/state.js';
8
8
  import { log, warn } from '../runtime/log.js';
9
+ import { adoptBootSecrets } from '../runtime/secrets.js';
9
10
  import { errMsg } from '../../core/util/errors.js';
10
11
  import { Daemon } from '../daemon/daemon.js';
11
12
  process.on('uncaughtException', (e) => warn('daemon: unexpected error (continuing): ' + (e.stack ?? errMsg(e))));
12
13
  process.on('unhandledRejection', (e) => warn('daemon: unexpected error (continuing): ' + errMsg(e)));
14
+ adoptBootSecrets();
13
15
  log('Session daemon started (pid ' + process.pid + ').');
14
16
  safely('port forwards', startRelays);
15
17
  safely('audit', startProcessCollector);
@@ -6,6 +6,7 @@ declare const SOURCES: {
6
6
  readonly openssl: "OpenSsl.cs";
7
7
  readonly cargoshim: "CargoShim.cs";
8
8
  readonly linkgate: "LinkGate.cs";
9
+ readonly pathsync: "PathSync.cs";
9
10
  };
10
11
  export type Helper = keyof typeof SOURCES;
11
12
  /** %TEMP%\agent-sandbox\<name>-<hash>.exe, compiled from this launch's source the first time it is needed. */
@@ -19,6 +19,7 @@ const SOURCES = {
19
19
  openssl: 'OpenSsl.cs',
20
20
  cargoshim: 'CargoShim.cs',
21
21
  linkgate: 'LinkGate.cs',
22
+ pathsync: 'PathSync.cs',
22
23
  };
23
24
  function sourceOf(name) {
24
25
  return path.join(launchDir, 'sandbox', 'csharp', SOURCES[name]);
@@ -0,0 +1,12 @@
1
+ import type { GuestRequest } from '../../core/types.js';
2
+ import { type SecretsFile } from '../../shared/channel/secrets.js';
3
+ /** Where the boot's secrets travel from main.js to the daemon. */
4
+ export declare const BOOT_SECRETS_VARIABLE = "SBX_BOOT_SECRETS";
5
+ /** Reads and deletes <data>\secrets\<id>.json; nothing (or an unreadable file) gives {}. */
6
+ export declare function takeSecrets(id: string): SecretsFile;
7
+ /** The agent the sandbox booted with gets its environment (in memory only). */
8
+ export declare function applyBootEnvironment(environment: Record<string, string> | undefined): void;
9
+ /** The daemon takes what main.js passed it, and removes it from its own environment before any session starts. */
10
+ export declare function adoptBootSecrets(): void;
11
+ /** A request with the agent environment values that travelled in its own secrets file put back (in memory only). */
12
+ export declare function withRequestSecrets(req: GuestRequest): GuestRequest;
@@ -0,0 +1,63 @@
1
+ // The one-shot secrets the host handed over (shared/channel/secrets.ts): read once, the file deleted at once. What the
2
+ // boot read for the agent is passed to the daemon in its own environment (SBX_BOOT_SECRETS, removed there at start), so
3
+ // nothing waits on disk.
4
+ import fs from 'node:fs';
5
+ import { mergeRequestSecrets, SecretsFileSchema } from '../../shared/channel/secrets.js';
6
+ import { run } from './paths.js';
7
+ import { warn } from './log.js';
8
+ import { session } from './session.js';
9
+ /** Where the boot's secrets travel from main.js to the daemon. */
10
+ export const BOOT_SECRETS_VARIABLE = 'SBX_BOOT_SECRETS';
11
+ const MAX_BYTES = 4 * 1024 * 1024;
12
+ /** Reads and deletes <data>\secrets\<id>.json; nothing (or an unreadable file) gives {}. */
13
+ export function takeSecrets(id) {
14
+ const file = run.secretFile(id);
15
+ let text;
16
+ try {
17
+ if (fs.statSync(file).size > MAX_BYTES)
18
+ throw new Error('too big');
19
+ text = fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, '');
20
+ }
21
+ catch {
22
+ return {};
23
+ }
24
+ finally {
25
+ fs.rmSync(file, { force: true });
26
+ }
27
+ try {
28
+ const parsed = SecretsFileSchema.safeParse(JSON.parse(text));
29
+ if (parsed.success)
30
+ return parsed.data;
31
+ }
32
+ catch {
33
+ // Not JSON: treated like an invalid file below.
34
+ }
35
+ warn('The secrets file for ' + id + ' was not valid and was ignored.');
36
+ return {};
37
+ }
38
+ /** The agent the sandbox booted with gets its environment (in memory only). */
39
+ export function applyBootEnvironment(environment) {
40
+ if (session.harness && environment)
41
+ session.harness.environment = { ...environment };
42
+ }
43
+ /** The daemon takes what main.js passed it, and removes it from its own environment before any session starts. */
44
+ export function adoptBootSecrets() {
45
+ const text = process.env[BOOT_SECRETS_VARIABLE];
46
+ delete process.env[BOOT_SECRETS_VARIABLE];
47
+ if (!text)
48
+ return;
49
+ try {
50
+ const parsed = SecretsFileSchema.pick({ harnessEnvironment: true }).safeParse(JSON.parse(text));
51
+ if (parsed.success)
52
+ applyBootEnvironment(parsed.data.harnessEnvironment);
53
+ }
54
+ catch {
55
+ warn('The boot secrets given to the daemon were not valid.');
56
+ }
57
+ }
58
+ /** A request with the agent environment values that travelled in its own secrets file put back (in memory only). */
59
+ export function withRequestSecrets(req) {
60
+ if (req.kind !== 'session' && req.kind !== 'project')
61
+ return req;
62
+ return mergeRequestSecrets(req, takeSecrets(req.id));
63
+ }
@@ -4,4 +4,5 @@
4
4
  * Servers with the same name are replaced by this PC's definition; servers added inside the sandbox are kept.
5
5
  */
6
6
  export declare function mergeMcpServers(hostFile: string, guestFile: string, hostWorkspace: string, guestWorkspace: string): number;
7
- export declare function syncClaudeProfile(): void;
7
+ /** `credentials`: the text of this PC's .credentials.json, handed over in the one-shot secrets file. */
8
+ export declare function syncClaudeProfile(credentials?: string): void;