windows-development-sandbox 1.18.4 → 1.23.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 (438) hide show
  1. package/README.md +1 -1
  2. package/config.example.json +17 -1
  3. package/dist/app/features/access/access.def.js +5 -2
  4. package/dist/app/features/access/access.run.js +7 -3
  5. package/dist/app/features/accounts/accounts.def.d.ts +33 -1
  6. package/dist/app/features/accounts/accounts.def.js +10 -4
  7. package/dist/app/features/accounts/accounts.run.d.ts +2 -0
  8. package/dist/app/features/accounts/accounts.run.js +7 -2
  9. package/dist/app/features/accounts/services.def.d.ts +7 -1
  10. package/dist/app/features/accounts/services.def.js +19 -16
  11. package/dist/app/features/accounts/services.run.d.ts +2 -0
  12. package/dist/app/features/accounts/services.run.js +44 -16
  13. package/dist/app/features/agent-commands/agent-commands.def.d.ts +37 -0
  14. package/dist/app/features/agent-commands/agent-commands.def.js +91 -0
  15. package/dist/app/features/agent-commands/agent-commands.run.d.ts +28 -0
  16. package/dist/app/features/agent-commands/agent-commands.run.js +35 -0
  17. package/dist/app/features/agent-commands/index.d.ts +2 -0
  18. package/dist/app/features/agent-commands/index.js +3 -0
  19. package/dist/app/features/agents/agents.def.d.ts +14 -1
  20. package/dist/app/features/agents/agents.def.js +61 -6
  21. package/dist/app/features/agents/agents.run.d.ts +1 -0
  22. package/dist/app/features/agents/agents.run.js +8 -1
  23. package/dist/app/features/agents/claude-env.def.d.ts +36 -0
  24. package/dist/app/features/agents/claude-env.def.js +305 -0
  25. package/dist/app/features/agents/env.def.d.ts +163 -0
  26. package/dist/app/features/agents/env.def.js +184 -0
  27. package/dist/app/features/agents/env.run.d.ts +43 -0
  28. package/dist/app/features/agents/env.run.js +86 -0
  29. package/dist/app/features/agents/index.d.ts +2 -0
  30. package/dist/app/features/agents/index.js +2 -0
  31. package/dist/app/features/defs.d.ts +171 -3
  32. package/dist/app/features/defs.js +9 -0
  33. package/dist/app/features/engine/engine.run.js +1 -1
  34. package/dist/app/features/github/github.def.d.ts +44 -3
  35. package/dist/app/features/github/github.def.js +145 -63
  36. package/dist/app/features/github/github.run.d.ts +9 -2
  37. package/dist/app/features/github/github.run.js +43 -5
  38. package/dist/app/features/health/health.def.js +8 -2
  39. package/dist/app/features/logs/logs.def.js +0 -8
  40. package/dist/app/features/projects/projects.def.js +2 -2
  41. package/dist/app/features/projects/projects.run.js +5 -5
  42. package/dist/app/features/sandbox/sandbox.def.js +7 -3
  43. package/dist/app/features/sessions/sessions.def.d.ts +4 -4
  44. package/dist/app/features/sessions/sessions.def.js +0 -5
  45. package/dist/app/features/sessions/sessions.run.d.ts +1 -1
  46. package/dist/app/features/sessions/sessions.run.js +2 -2
  47. package/dist/app/features/settings/categories.def.js +7 -2
  48. package/dist/app/features/settings/settings-rows.js +6 -2
  49. package/dist/app/features/settings/settings.def.js +1 -1
  50. package/dist/app/features/settings/settings.run.d.ts +8 -0
  51. package/dist/app/features/settings/settings.run.js +12 -3
  52. package/dist/app/features/storage/storage.run.js +18 -9
  53. package/dist/app/features/updates/update-lines.js +2 -2
  54. package/dist/app/features/updates/updates.def.js +1 -1
  55. package/dist/app/features/updates/updates.run.js +13 -3
  56. package/dist/app/features/vpn/vpn.def.d.ts +33 -0
  57. package/dist/app/features/vpn/vpn.def.js +30 -6
  58. package/dist/app/features/vpn/vpn.run.d.ts +11 -0
  59. package/dist/app/features/vpn/vpn.run.js +4 -1
  60. package/dist/app/features/web/web.def.js +2 -2
  61. package/dist/app/features/web/web.run.js +12 -4
  62. package/dist/app/kernel/errors.d.ts +7 -0
  63. package/dist/app/kernel/errors.js +18 -3
  64. package/dist/app/kernel/rpc.d.ts +6 -1
  65. package/dist/app/kernel/view.d.ts +11 -1
  66. package/dist/app/registry.js +7 -1
  67. package/dist/bin/agent-sandbox.js +2 -2
  68. package/dist/bin/{chunk-N4GXYTLV.js → chunk-JETGP2C3.js} +239 -72
  69. package/dist/bin/{chunk-MY7NLQQY.js → chunk-XZKOPGNH.js} +1 -1
  70. package/dist/bin/{chunk-FRPLQLSY.js → chunk-ZTTOF5V3.js} +6566 -4964
  71. package/dist/bin/claude-host.js +2 -2
  72. package/dist/bin/claude-sbx.js +2 -2
  73. package/dist/bin/codex-sbx.js +2 -2
  74. package/dist/bin/gemini-sbx.js +2 -2
  75. package/dist/bin/sandbox.js +2 -2
  76. package/dist/bin/sbx-hide.js +2 -2
  77. package/dist/bin/sbx-show.js +2 -2
  78. package/dist/cli/commands/agent-commands.d.ts +0 -1
  79. package/dist/cli/commands/agent-commands.js +2 -32
  80. package/dist/cli/commands/index.d.ts +1 -1
  81. package/dist/cli/commands/index.js +1 -1
  82. package/dist/cli/commands/launch/launch.js +2 -2
  83. package/dist/cli/commands/registry.js +3 -2
  84. package/dist/cli/manifest.d.ts +3 -3
  85. package/dist/cli/manifest.js +3 -2
  86. package/dist/cli/menu/coverage-exempt.d.ts +17 -0
  87. package/dist/cli/menu/coverage-exempt.js +61 -0
  88. package/dist/cli/menu/io-menu.d.ts +2 -0
  89. package/dist/cli/menu/io-menu.js +50 -5
  90. package/dist/cli/menu/menu.d.ts +1 -1
  91. package/dist/cli/menu/menu.js +3 -3
  92. package/dist/cli/menu/pages/accounts-page.js +1 -1
  93. package/dist/cli/menu/pages/agents-page.d.ts +7 -2
  94. package/dist/cli/menu/pages/agents-page.js +75 -13
  95. package/dist/cli/menu/pages/maintenance-page.d.ts +6 -0
  96. package/dist/cli/menu/pages/maintenance-page.js +10 -4
  97. package/dist/cli/menu/pages/sessions-page.d.ts +2 -0
  98. package/dist/cli/menu/pages/sessions-page.js +12 -4
  99. package/dist/cli/menu/pages/settings-page.js +13 -1
  100. package/dist/cli/menu/remote-menu.js +1 -1
  101. package/dist/cli/menu/shell.d.ts +5 -0
  102. package/dist/cli/menu/shell.js +13 -0
  103. package/dist/cli/menu/views.js +2 -1
  104. package/dist/cli/report.js +2 -0
  105. package/dist/cli/services/agent-commands-service.d.ts +2 -0
  106. package/dist/cli/services/agent-commands-service.js +13 -7
  107. package/dist/cli/services/vpn-service.d.ts +5 -1
  108. package/dist/cli/services/vpn-service.js +6 -1
  109. package/dist/cli/tui/app.d.ts +5 -0
  110. package/dist/cli/tui/app.js +2 -0
  111. package/dist/cli/tui/dialogs/text-screen.js +2 -1
  112. package/dist/cli/tui/terminal.js +3 -1
  113. package/dist/core/config/defaults.js +12 -1
  114. package/dist/core/detect/toolchains.js +9 -0
  115. package/dist/core/disk.d.ts +1 -5
  116. package/dist/core/disk.js +1 -16
  117. package/dist/core/schema/accounts.d.ts +6 -0
  118. package/dist/core/schema/accounts.js +13 -11
  119. package/dist/core/schema/codex.d.ts +29 -0
  120. package/dist/core/schema/codex.js +24 -0
  121. package/dist/core/schema/config.d.ts +58 -14
  122. package/dist/core/schema/config.js +6 -4
  123. package/dist/core/schema/github.js +1 -1
  124. package/dist/core/schema/index.d.ts +1 -0
  125. package/dist/core/schema/index.js +1 -0
  126. package/dist/core/schema/profile.js +8 -4
  127. package/dist/core/schema/resume.js +1 -1
  128. package/dist/core/schema/sandbox.js +3 -3
  129. package/dist/core/schema/toolchains.d.ts +4 -0
  130. package/dist/core/schema/toolchains.js +3 -0
  131. package/dist/core/schema/vpn.js +1 -1
  132. package/dist/core/schema/web.js +3 -3
  133. package/dist/core/types.d.ts +8 -0
  134. package/dist/core/util/errors.d.ts +11 -1
  135. package/dist/core/util/errors.js +7 -1
  136. package/dist/core/util/index.d.ts +1 -1
  137. package/dist/core/util/index.js +1 -1
  138. package/dist/guest/accounts/adapter.d.ts +2 -0
  139. package/dist/guest/accounts/adapters.js +4 -0
  140. package/dist/guest/accounts/apply.d.ts +10 -6
  141. package/dist/guest/accounts/kinds/cfx.d.ts +7 -0
  142. package/dist/guest/accounts/kinds/cfx.js +33 -3
  143. package/dist/guest/accounts/kinds/comet.d.ts +7 -0
  144. package/dist/guest/accounts/kinds/comet.js +50 -0
  145. package/dist/guest/accounts/kinds/perplexity.d.ts +2 -0
  146. package/dist/guest/accounts/kinds/perplexity.js +12 -0
  147. package/dist/guest/accounts/watch.d.ts +2 -0
  148. package/dist/guest/accounts/watch.js +12 -2
  149. package/dist/guest/agents/agent-files.d.ts +26 -6
  150. package/dist/guest/agents/agent-files.js +84 -14
  151. package/dist/guest/agents/agent-installer.js +1 -1
  152. package/dist/guest/agents/local-state.d.ts +5 -2
  153. package/dist/guest/agents/local-state.js +24 -10
  154. package/dist/guest/agents/profile-links.d.ts +2 -0
  155. package/dist/guest/agents/profile-links.js +18 -4
  156. package/dist/guest/apps/after-install.js +19 -0
  157. package/dist/guest/apps/app-installer.d.ts +11 -0
  158. package/dist/guest/apps/app-installer.js +69 -6
  159. package/dist/guest/apps/comet-unpack.d.ts +20 -0
  160. package/dist/guest/apps/comet-unpack.js +127 -0
  161. package/dist/guest/apps/index.d.ts +1 -1
  162. package/dist/guest/apps/index.js +1 -1
  163. package/dist/guest/apps/resolvers-agents-web.d.ts +2 -0
  164. package/dist/guest/apps/resolvers-agents-web.js +7 -0
  165. package/dist/guest/apps/resolvers.js +2 -0
  166. package/dist/guest/apps/shortcuts.d.ts +2 -0
  167. package/dist/guest/apps/shortcuts.js +1 -0
  168. package/dist/guest/apps/unpack.js +14 -4
  169. package/dist/guest/boot/boot.d.ts +45 -16
  170. package/dist/guest/boot/boot.js +325 -149
  171. package/dist/guest/boot/graph.d.ts +252 -0
  172. package/dist/guest/boot/graph.js +257 -0
  173. package/dist/guest/boot/guide-lines.d.ts +25 -0
  174. package/dist/guest/boot/guide-lines.js +41 -0
  175. package/dist/guest/boot/guide.d.ts +1 -4
  176. package/dist/guest/boot/guide.js +6 -8
  177. package/dist/guest/boot/runner.d.ts +77 -0
  178. package/dist/guest/boot/runner.js +215 -0
  179. package/dist/guest/browser/cdp.d.ts +71 -0
  180. package/dist/guest/browser/cdp.js +306 -0
  181. package/dist/guest/browser/comet.d.ts +28 -0
  182. package/dist/guest/browser/comet.js +155 -0
  183. package/dist/guest/daemon/project-provisioner.js +6 -1
  184. package/dist/guest/daemon/sessions.js +2 -1
  185. package/dist/guest/devtools/fivem.d.ts +1 -1
  186. package/dist/guest/devtools/fivem.js +243 -3
  187. package/dist/guest/entries/daemon.js +2 -0
  188. package/dist/guest/runtime/log.d.ts +12 -0
  189. package/dist/guest/runtime/log.js +29 -5
  190. package/dist/guest/runtime/pools.d.ts +24 -0
  191. package/dist/guest/runtime/pools.js +60 -0
  192. package/dist/guest/runtime/process.d.ts +4 -0
  193. package/dist/guest/runtime/process.js +38 -1
  194. package/dist/guest/runtime/status.d.ts +5 -1
  195. package/dist/guest/runtime/status.js +19 -0
  196. package/dist/guest/setup/addon-mcp.d.ts +1 -1
  197. package/dist/guest/setup/addon-mcp.js +52 -12
  198. package/dist/guest/setup/agent-addons.d.ts +3 -2
  199. package/dist/guest/setup/agent-addons.js +12 -3
  200. package/dist/guest/setup/environment.d.ts +1 -1
  201. package/dist/guest/setup/environment.js +25 -12
  202. package/dist/guest/setup/headroom.d.ts +8 -3
  203. package/dist/guest/setup/headroom.js +44 -15
  204. package/dist/guest/setup/path-plan.d.ts +28 -0
  205. package/dist/guest/setup/path-plan.js +69 -2
  206. package/dist/guest/setup/shell-path.d.ts +1 -1
  207. package/dist/guest/setup/shell-path.js +3 -3
  208. package/dist/guest/toolchains/fivem/artifacts.d.ts +48 -1
  209. package/dist/guest/toolchains/fivem/artifacts.js +97 -4
  210. package/dist/guest/toolchains/fivem/fivem.d.ts +32 -1
  211. package/dist/guest/toolchains/fivem/fivem.js +168 -3
  212. package/dist/guest/toolchains/fivem/scaffold.d.ts +35 -0
  213. package/dist/guest/toolchains/fivem/scaffold.js +105 -0
  214. package/dist/guest/toolchains/fivem/serve-plan.d.ts +34 -0
  215. package/dist/guest/toolchains/fivem/serve-plan.js +61 -0
  216. package/dist/guest/toolchains/installers.d.ts +6 -10
  217. package/dist/guest/toolchains/installers.js +4 -12
  218. package/dist/guest/toolchains/languages/go.js +1 -1
  219. package/dist/guest/toolchains/languages/node.js +2 -1
  220. package/dist/guest/toolchains/languages/php.d.ts +2 -0
  221. package/dist/guest/toolchains/languages/php.js +13 -1
  222. package/dist/guest/toolchains/languages/python-tools.js +1 -1
  223. package/dist/guest/toolchains/languages/ruby.js +1 -1
  224. package/dist/guest/toolchains/native/autosdk.d.ts +3 -0
  225. package/dist/guest/toolchains/native/autosdk.js +50 -0
  226. package/dist/guest/toolchains/native/binstall.js +1 -1
  227. package/dist/guest/toolchains/native/msvc/packages.js +10 -4
  228. package/dist/guest/toolchains/native/rust.d.ts +2 -0
  229. package/dist/guest/toolchains/native/rust.js +11 -4
  230. package/dist/guest/toolchains/native/unreal.d.ts +2 -0
  231. package/dist/guest/toolchains/native/unreal.js +19 -6
  232. package/dist/guest/toolchains/native/vcruntime.d.ts +2 -3
  233. package/dist/guest/toolchains/native/vcruntime.js +12 -7
  234. package/dist/guest/toolchains/native/visual-studio.d.ts +5 -2
  235. package/dist/guest/toolchains/native/visual-studio.js +127 -25
  236. package/dist/guest/toolchains/native/vs-request.d.ts +23 -0
  237. package/dist/guest/toolchains/native/vs-request.js +62 -1
  238. package/dist/guest/toolchains/not-yet.d.ts +1 -1
  239. package/dist/guest/toolchains/not-yet.js +3 -3
  240. package/dist/guest/toolchains/persisted.js +2 -7
  241. package/dist/guest/toolchains/plan.js +27 -11
  242. package/dist/guest/toolchains/scheduler.d.ts +2 -21
  243. package/dist/guest/toolchains/scheduler.js +7 -121
  244. package/dist/guest/toolchains/setup.d.ts +15 -2
  245. package/dist/guest/toolchains/setup.js +39 -20
  246. package/dist/guest/toolchains/signature.d.ts +5 -0
  247. package/dist/guest/toolchains/signature.js +22 -0
  248. package/dist/guest/toolchains/split.d.ts +28 -0
  249. package/dist/guest/toolchains/split.js +60 -0
  250. package/dist/guest/toolchains/steam/client.js +2 -1
  251. package/dist/guest/toolchains/step-scheduler.d.ts +51 -0
  252. package/dist/guest/toolchains/step-scheduler.js +147 -0
  253. package/dist/guest/toolchains/step.d.ts +13 -0
  254. package/dist/guest/toolchains/tools/git.js +18 -0
  255. package/dist/guest/vpn/openvpn.js +3 -4
  256. package/dist/guest/vpn/tunnel.d.ts +3 -0
  257. package/dist/guest/vpn/tunnel.js +10 -12
  258. package/dist/guest/vpn/vpn.js +6 -1
  259. package/dist/guest/vpn/wireguard.js +11 -4
  260. package/dist/host/accounts/deliver.js +7 -0
  261. package/dist/host/accounts/handoff.js +2 -6
  262. package/dist/host/accounts/index.d.ts +1 -1
  263. package/dist/host/accounts/index.js +1 -1
  264. package/dist/host/accounts/kinds/cfx.d.ts +1 -1
  265. package/dist/host/accounts/kinds/cfx.js +47 -4
  266. package/dist/host/accounts/kinds/comet.d.ts +2 -0
  267. package/dist/host/accounts/kinds/comet.js +6 -0
  268. package/dist/host/accounts/kinds/perplexity.d.ts +2 -0
  269. package/dist/host/accounts/kinds/perplexity.js +30 -0
  270. package/dist/host/accounts/leases.d.ts +9 -3
  271. package/dist/host/accounts/leases.js +13 -2
  272. package/dist/host/accounts/source.d.ts +7 -1
  273. package/dist/host/accounts/source.js +28 -1
  274. package/dist/host/accounts/sources.js +4 -0
  275. package/dist/host/agent-commands/commands.js +1 -1
  276. package/dist/host/agent-commands/forwarders.d.ts +14 -2
  277. package/dist/host/agent-commands/forwarders.js +31 -13
  278. package/dist/host/agent-commands/reconcile.d.ts +2 -1
  279. package/dist/host/agent-commands/reconcile.js +7 -8
  280. package/dist/host/agent-commands/user-path.d.ts +5 -0
  281. package/dist/host/agent-commands/user-path.js +13 -0
  282. package/dist/host/github/deliver.d.ts +1 -1
  283. package/dist/host/github/deliver.js +20 -1
  284. package/dist/host/github/index.d.ts +2 -1
  285. package/dist/host/github/index.js +1 -0
  286. package/dist/host/github/token.d.ts +13 -1
  287. package/dist/host/github/token.js +19 -2
  288. package/dist/host/github/verify.d.ts +27 -0
  289. package/dist/host/github/verify.js +61 -0
  290. package/dist/host/launch/hints/fivem.d.ts +3 -1
  291. package/dist/host/launch/hints/fivem.js +68 -2
  292. package/dist/host/launch/session-file.js +5 -0
  293. package/dist/host/launch/toolchains.d.ts +2 -1
  294. package/dist/host/launch/toolchains.js +5 -3
  295. package/dist/host/profile/accounts.js +2 -4
  296. package/dist/host/profile/claude-login.d.ts +3 -2
  297. package/dist/host/profile/claude-login.js +3 -13
  298. package/dist/host/vpn/index.d.ts +2 -1
  299. package/dist/host/vpn/index.js +2 -1
  300. package/dist/host/vpn/last.d.ts +28 -0
  301. package/dist/host/vpn/last.js +43 -0
  302. package/dist/host/vpn/login.d.ts +3 -1
  303. package/dist/host/vpn/login.js +17 -3
  304. package/dist/host/vpn/nordvpn.d.ts +2 -4
  305. package/dist/host/vpn/nordvpn.js +2 -24
  306. package/dist/runtime/agent.js +433 -90
  307. package/dist/runtime/daemon.js +3378 -1546
  308. package/dist/runtime/db.js +363 -90
  309. package/dist/runtime/main.js +5134 -2096
  310. package/dist/runtime/netproxy.js +308 -69
  311. package/dist/runtime/tool.js +2867 -1765
  312. package/dist/runtime/view.js +308 -69
  313. package/dist/selftest/agent-commands.test.js +45 -7
  314. package/dist/selftest/agent-paths.test.js +40 -13
  315. package/dist/selftest/agent-settings.test.d.ts +2 -0
  316. package/dist/selftest/agent-settings.test.js +94 -0
  317. package/dist/selftest/agents-web.test.d.ts +2 -0
  318. package/dist/selftest/agents-web.test.js +647 -0
  319. package/dist/selftest/app.test.js +7 -3
  320. package/dist/selftest/boot-runner.test.d.ts +2 -0
  321. package/dist/selftest/boot-runner.test.js +456 -0
  322. package/dist/selftest/channel.test.js +15 -0
  323. package/dist/selftest/cli.test.js +4 -0
  324. package/dist/selftest/fivem.test.js +249 -3
  325. package/dist/selftest/github.test.js +94 -9
  326. package/dist/selftest/guest-boot.test.js +3 -0
  327. package/dist/selftest/health.test.js +1 -1
  328. package/dist/selftest/index.js +10 -0
  329. package/dist/selftest/layout.test.js +5 -0
  330. package/dist/selftest/menu-coverage.test.d.ts +2 -0
  331. package/dist/selftest/menu-coverage.test.js +46 -0
  332. package/dist/selftest/menu-screens.js +37 -1
  333. package/dist/selftest/menu.test.js +5 -3
  334. package/dist/selftest/pc-installs.test.js +5 -0
  335. package/dist/selftest/persisted-installs.test.js +72 -0
  336. package/dist/selftest/service-accounts.test.js +3 -2
  337. package/dist/selftest/support/engine-golden.js +28 -1
  338. package/dist/selftest/support/fixtures.d.ts +15 -0
  339. package/dist/selftest/support/web-preview.d.ts +34 -1
  340. package/dist/selftest/support/web-preview.js +21 -18
  341. package/dist/selftest/toolchains.test.js +23 -1
  342. package/dist/selftest/ui-text.test.d.ts +7 -0
  343. package/dist/selftest/ui-text.test.js +300 -0
  344. package/dist/selftest/vpn.test.js +51 -3
  345. package/dist/shared/accounts/anthropic.d.ts +5 -0
  346. package/dist/shared/accounts/anthropic.js +15 -0
  347. package/dist/shared/accounts/index.d.ts +3 -0
  348. package/dist/shared/accounts/index.js +3 -0
  349. package/dist/shared/accounts/kinds/comet.d.ts +12 -0
  350. package/dist/shared/accounts/kinds/comet.js +64 -0
  351. package/dist/shared/accounts/kinds/perplexity.d.ts +8 -0
  352. package/dist/shared/accounts/kinds/perplexity.js +37 -0
  353. package/dist/shared/accounts/kinds.d.ts +24 -5
  354. package/dist/shared/accounts/kinds.js +86 -4
  355. package/dist/shared/accounts/payloads.d.ts +81 -0
  356. package/dist/shared/accounts/payloads.js +39 -1
  357. package/dist/shared/apps/agent-addons.d.ts +33 -5
  358. package/dist/shared/apps/agent-addons.js +95 -3
  359. package/dist/shared/apps/catalogue-agents-web.d.ts +14 -0
  360. package/dist/shared/apps/catalogue-agents-web.js +112 -0
  361. package/dist/shared/apps/catalogue-modding.js +32 -1
  362. package/dist/shared/apps/catalogue.d.ts +27 -3
  363. package/dist/shared/apps/catalogue.js +2 -1
  364. package/dist/shared/apps/index.d.ts +3 -1
  365. package/dist/shared/apps/index.js +3 -1
  366. package/dist/shared/channel/schema.d.ts +40 -3
  367. package/dist/shared/channel/schema.js +9 -0
  368. package/dist/shared/channel/secrets.d.ts +19 -0
  369. package/dist/shared/concurrency/index.d.ts +3 -0
  370. package/dist/shared/concurrency/index.js +3 -0
  371. package/dist/shared/concurrency/installer-retry.d.ts +6 -0
  372. package/dist/shared/concurrency/installer-retry.js +10 -0
  373. package/dist/shared/concurrency/limiter.d.ts +19 -0
  374. package/dist/shared/concurrency/limiter.js +67 -0
  375. package/dist/shared/concurrency/pools.d.ts +7 -0
  376. package/dist/shared/concurrency/pools.js +9 -0
  377. package/dist/shared/download/archive.d.ts +2 -0
  378. package/dist/shared/download/archive.js +11 -1
  379. package/dist/shared/download/bounded-fetch.d.ts +13 -0
  380. package/dist/shared/download/bounded-fetch.js +40 -0
  381. package/dist/shared/download/downloader.d.ts +5 -0
  382. package/dist/shared/download/downloader.js +50 -38
  383. package/dist/shared/download/index.d.ts +2 -1
  384. package/dist/shared/download/index.js +2 -1
  385. package/dist/shared/io/disk.d.ts +9 -0
  386. package/dist/shared/io/disk.js +19 -0
  387. package/dist/shared/io/index.d.ts +1 -0
  388. package/dist/shared/io/index.js +1 -0
  389. package/dist/shared/readiness.d.ts +9 -0
  390. package/dist/shared/readiness.js +11 -0
  391. package/dist/shared/signer.d.ts +24 -0
  392. package/dist/shared/signer.js +15 -0
  393. package/dist/shared/vpn/failure.d.ts +10 -0
  394. package/dist/shared/vpn/failure.js +28 -0
  395. package/dist/sync/watcher.js +2 -0
  396. package/dist/web/index.html +1 -1
  397. package/dist/web/{main-GVZWWP4I.js → main-CCILLX2M.js} +75 -74
  398. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  399. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  400. package/native/csharp/HostTools/Program.cs +14 -1
  401. package/native/csharp/HostTools/UserPath.cs +17 -0
  402. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  403. package/native/scripts/edit-user-path.ps1 +6 -2
  404. package/package.json +1 -1
  405. package/sandbox/addons/browser-use-mcp/requirements.in +1 -0
  406. package/sandbox/addons/browser-use-mcp/requirements.txt +2350 -0
  407. package/sandbox/addons/chrome-devtools-mcp/package-lock.json +38 -0
  408. package/sandbox/addons/chrome-devtools-mcp/package.json +7 -0
  409. package/sandbox/addons/comet-mcp/package-lock.json +1277 -0
  410. package/sandbox/addons/comet-mcp/package.json +7 -0
  411. package/sandbox/addons/perplexity-mcp/package-lock.json +1655 -0
  412. package/sandbox/addons/perplexity-mcp/package.json +7 -0
  413. package/sandbox/scripts/add-firewall-port.ps1 +18 -4
  414. package/sandbox/scripts/get-signature.ps1 +13 -0
  415. package/sandbox/scripts/set-machine-environment.ps1 +30 -15
  416. package/sandbox/templates/fivem/README.txt +6 -2
  417. package/sandbox/templates/fivem/cs/Client/Client.csproj +15 -0
  418. package/sandbox/templates/fivem/cs/Client/ClientMain.cs +20 -0
  419. package/sandbox/templates/fivem/cs/Server/Server.csproj +14 -0
  420. package/sandbox/templates/fivem/cs/Server/ServerMain.cs +20 -0
  421. package/sandbox/templates/fivem/cs/fxmanifest.lua +11 -0
  422. package/sandbox/templates/fivem/js/client.js +12 -0
  423. package/sandbox/templates/fivem/js/fxmanifest.lua +9 -0
  424. package/sandbox/templates/fivem/js/server.js +6 -0
  425. package/sandbox/templates/fivem/lua/client.lua +8 -0
  426. package/sandbox/templates/fivem/lua/fxmanifest.lua +10 -0
  427. package/sandbox/templates/fivem/lua/server.lua +6 -0
  428. package/sandbox/templates/fivem/server.cfg +37 -0
  429. package/sandbox/templates/fivem/ts/build.js +17 -0
  430. package/sandbox/templates/fivem/ts/fxmanifest.lua +9 -0
  431. package/sandbox/templates/fivem/ts/package.json +16 -0
  432. package/sandbox/templates/fivem/ts/src/client/client.ts +12 -0
  433. package/sandbox/templates/fivem/ts/src/client/tsconfig.json +13 -0
  434. package/sandbox/templates/fivem/ts/src/server/server.ts +6 -0
  435. package/sandbox/templates/fivem/ts/src/server/tsconfig.json +13 -0
  436. package/schemas/config.schema.json +120 -27
  437. package/schemas/sandbox-config.schema.json +45 -14
  438. package/sandbox/scripts/get-signer.ps1 +0 -8
@@ -0,0 +1,51 @@
1
+ import { type ToolEnv } from './tool-env.js';
2
+ import type { ToolchainStep } from './step.js';
3
+ /** Where the scheduler's warnings and the boot status go (injected, so tests need neither the log nor session.json). */
4
+ export interface SchedulerSink {
5
+ warn(message: string): void;
6
+ status(message: string, progress: {
7
+ active: {
8
+ name: string;
9
+ percent?: number;
10
+ }[];
11
+ done: number;
12
+ total: number;
13
+ }): void;
14
+ }
15
+ export interface RunOptions {
16
+ /** Progress lines ("Installing Go, Rust (3 of 9 done)"); default: the sandbox's boot status. */
17
+ report?: (message: string) => void;
18
+ /** What machine-wide (shared) steps contributed at boot: reused instead of run again. */
19
+ reuse?: Record<string, ToolEnv>;
20
+ /** The boot: gets what runs now instead of the scheduler writing the sandbox status itself. */
21
+ progress?: (p: {
22
+ active: {
23
+ name: string;
24
+ percent?: number;
25
+ }[];
26
+ done: number;
27
+ total: number;
28
+ }) => void;
29
+ /** A step finished (installed, failed or reused). */
30
+ onDone?: (name: string) => void;
31
+ }
32
+ export declare class StepScheduler {
33
+ private readonly sink;
34
+ private readonly parallel;
35
+ /** One lock per kind of step ("Node 20" and "Node 22" share nvm's folders): held while that step runs, in any run. */
36
+ private readonly locks;
37
+ /** The step a download belongs to (several run at once), for its progress. */
38
+ private readonly currentStep;
39
+ /** Downloads started ahead of their steps (by name), until the step has used them. */
40
+ private readonly fetches;
41
+ constructor(sink: SchedulerSink, parallel?: number);
42
+ /**
43
+ * Starts the downloads of these steps now (once per step); `run` waits for a step's own download before it installs.
44
+ * Failures are not reported here: the install downloads again and reports the real reason.
45
+ */
46
+ startFetches(steps: readonly ToolchainStep[]): void;
47
+ /** Runs the steps (each with its own environment) and returns each step's environment, in plan order. */
48
+ run(steps: readonly ToolchainStep[], o: RunOptions): Promise<Map<string, ToolEnv>>;
49
+ /** Runs `work` once no other step of the same kind (first word of the name) is running. */
50
+ private exclusive;
51
+ }
@@ -0,0 +1,147 @@
1
+ // Runs the planned steps in parallel, a few at a time, after the steps they depend on. One lock per kind of step is
2
+ // held across runs: the boot and projects that join later never install two Nodes (or two Gos) at once.
3
+ import { AsyncLocalStorage } from 'node:async_hooks';
4
+ import { downloader } from '../../shared/download/index.js';
5
+ import { errMsg } from '../../core/util/errors.js';
6
+ import { emptyEnv } from './tool-env.js';
7
+ /** How many installers run at once: enough to overlap downloads with installs, few enough not to thrash the disk. */
8
+ const PARALLEL = 4;
9
+ /** Runs one installer; a failure is reported and skipped so it never blocks the other toolchains. */
10
+ async function runReported(sink, name, env, work) {
11
+ try {
12
+ await work();
13
+ }
14
+ catch (e) {
15
+ sink.warn(name +
16
+ ' could not be set up and is skipped; everything else still works, and the next start tries again. Reason: ' +
17
+ errMsg(e));
18
+ env.summary.push(name + ': FAILED');
19
+ }
20
+ }
21
+ export class StepScheduler {
22
+ sink;
23
+ parallel;
24
+ /** One lock per kind of step ("Node 20" and "Node 22" share nvm's folders): held while that step runs, in any run. */
25
+ locks = new Map();
26
+ /** The step a download belongs to (several run at once), for its progress. */
27
+ currentStep = new AsyncLocalStorage();
28
+ /** Downloads started ahead of their steps (by name), until the step has used them. */
29
+ fetches = new Map();
30
+ constructor(sink, parallel = PARALLEL) {
31
+ this.sink = sink;
32
+ this.parallel = parallel;
33
+ }
34
+ /**
35
+ * Starts the downloads of these steps now (once per step); `run` waits for a step's own download before it installs.
36
+ * Failures are not reported here: the install downloads again and reports the real reason.
37
+ */
38
+ startFetches(steps) {
39
+ for (const s of steps) {
40
+ if (!s.fetch || this.fetches.has(s.name))
41
+ continue;
42
+ this.fetches.set(s.name, this.currentStep
43
+ .run(s.name, async () => s.fetch())
44
+ .catch(() => {
45
+ /* install downloads again and says why it failed */
46
+ }));
47
+ }
48
+ }
49
+ /** Runs the steps (each with its own environment) and returns each step's environment, in plan order. */
50
+ async run(steps, o) {
51
+ const envs = new Map(steps.map((s) => [s.name, emptyEnv()]));
52
+ const finished = new Map();
53
+ const running = new Set();
54
+ const waiting = [];
55
+ let done = 0;
56
+ // The boot (no `report`) also publishes structured progress: what runs now, how far its download is, how many are
57
+ // done.
58
+ const percent = new Map();
59
+ const active = () => [...running].map((name) => ({ name, ...(percent.has(name) ? { percent: percent.get(name) } : {}) }));
60
+ const say = o.report ??
61
+ ((message) => o.progress
62
+ ? o.progress({ active: active(), done, total: steps.length })
63
+ : this.sink.status(message, { active: active(), done, total: steps.length }));
64
+ const report = () => {
65
+ if (running.size)
66
+ say('Installing ' + [...running].join(', ') + ' (' + done + ' of ' + steps.length + ' done)');
67
+ };
68
+ this.startFetches(steps.filter((s) => !(s.shared && o.reuse?.[s.name])));
69
+ if (!o.report)
70
+ downloader.watch(() => {
71
+ const name = this.currentStep.getStore();
72
+ return name
73
+ ? (fraction) => {
74
+ percent.set(name, Math.round(fraction * 100));
75
+ report();
76
+ }
77
+ : null;
78
+ });
79
+ for (const s of steps) {
80
+ // Dependencies first (they are earlier in the plan, so their promises exist), then a free slot.
81
+ finished.set(s.name, (async () => {
82
+ await Promise.all((s.after ?? []).map((name) => finished.get(name)));
83
+ const env = envs.get(s.name);
84
+ const reused = s.shared ? o.reuse?.[s.name] : undefined;
85
+ if (reused) {
86
+ envs.set(s.name, structuredClone(reused));
87
+ done++;
88
+ o.onDone?.(s.name);
89
+ return;
90
+ }
91
+ // Its download (started above, before the dependencies finished) is in by now, or still coming.
92
+ await this.fetches.get(s.name);
93
+ this.fetches.delete(s.name);
94
+ while (running.size >= this.parallel)
95
+ await new Promise((r) => waiting.push(r));
96
+ running.add(s.name);
97
+ report();
98
+ try {
99
+ await this.currentStep.run(s.name, () => this.exclusive(s.name, () => runReported(this.sink, s.name, env, async () => s.install(env))));
100
+ }
101
+ finally {
102
+ running.delete(s.name);
103
+ percent.delete(s.name);
104
+ done++;
105
+ report();
106
+ waiting.shift()?.();
107
+ o.onDone?.(s.name);
108
+ }
109
+ })());
110
+ }
111
+ try {
112
+ await Promise.all(finished.values());
113
+ }
114
+ finally {
115
+ if (!o.report)
116
+ downloader.watch(null);
117
+ }
118
+ o.progress?.({ active: [], done, total: steps.length });
119
+ if (!o.report && !o.progress)
120
+ this.sink.status('Set up ' + steps.length + ' toolchains and databases', {
121
+ active: [],
122
+ done,
123
+ total: steps.length,
124
+ });
125
+ return envs;
126
+ }
127
+ /** Runs `work` once no other step of the same kind (first word of the name) is running. */
128
+ async exclusive(name, work) {
129
+ const key = name.split(' ')[0].toLowerCase();
130
+ const previous = this.locks.get(key) ?? Promise.resolve();
131
+ let release = () => undefined;
132
+ const mine = new Promise((r) => {
133
+ release = r;
134
+ });
135
+ const chained = previous.then(() => mine);
136
+ this.locks.set(key, chained);
137
+ await previous;
138
+ try {
139
+ return await work();
140
+ }
141
+ finally {
142
+ release();
143
+ if (this.locks.get(key) === chained)
144
+ this.locks.delete(key);
145
+ }
146
+ }
147
+ }
@@ -11,6 +11,12 @@ export interface ToolchainOptions {
11
11
  apps?: Config['apps'];
12
12
  /** pcInstalls: this PC's games, engines and editors mapped read-only (session.json). */
13
13
  pcInstalls?: PcInstallManifest;
14
+ /**
15
+ * Boot only: the heavy installs (Visual Studio, and what depends on it) run in the background after the agent has
16
+ * started. Off with a VPN or network.allowOnly (their firewall would cut the downloads), in a dry run and when a
17
+ * project joins a running sandbox.
18
+ */
19
+ deferHeavy?: boolean;
14
20
  }
15
21
  /** What a toolchain module's step gets besides its settings. */
16
22
  export interface ToolCtx {
@@ -30,5 +36,12 @@ export interface ToolchainStep {
30
36
  * git guard): a project added after boot reuses what the boot's run contributed instead of running it again.
31
37
  */
32
38
  readonly shared?: boolean;
39
+ /**
40
+ * Download only what `install` will need (no-op when it is there). The scheduler starts every step's fetch at once,
41
+ * outside the step slots and `after`; only the net pool limits them. A failure is ignored: install downloads again.
42
+ */
43
+ fetch?(): Promise<void>;
44
+ /** At boot, runs after the agent has started (the steps that depend on it follow it). */
45
+ readonly background?: boolean;
33
46
  install(env: ToolEnv): Promise<void> | void;
34
47
  }
@@ -94,6 +94,22 @@ const pointAtOwnCertificates = (root) => {
94
94
  if (fixed !== text)
95
95
  fs.writeFileSync(config, fixed);
96
96
  };
97
+ /**
98
+ * Coreutils' link.exe (hard links) next to MSVC's linker: Git Bash puts its own /usr/bin first on PATH, whatever the
99
+ * Windows PATH says, so a msvc Rust build ran the wrong `link` (os error 1392). Renamed; nobody needs it here.
100
+ */
101
+ const hideCoreutilsLink = (usrBin) => {
102
+ const link = path.join(usrBin, 'link.exe');
103
+ if (!exists(link))
104
+ return;
105
+ try {
106
+ fs.rmSync(path.join(usrBin, 'link-coreutils.exe'), { force: true });
107
+ fs.renameSync(link, path.join(usrBin, 'link-coreutils.exe'));
108
+ }
109
+ catch (e) {
110
+ warn("Could not rename Git's coreutils link.exe (MSVC link may be shadowed in Git Bash): " + errMsg(e));
111
+ }
112
+ };
97
113
  export async function setupGit(env, hostCopy, refresh, access) {
98
114
  const root = tool('git');
99
115
  const bash = path.join(root, 'bin', 'bash.exe');
@@ -130,6 +146,8 @@ export async function setupGit(env, hostCopy, refresh, access) {
130
146
  throw new Error('bash.exe missing after installing Git');
131
147
  pointAtOwnCertificates(root);
132
148
  env.vars.CLAUDE_CODE_GIT_BASH_PATH = bash;
149
+ // usr\bin is on PATH for Git's tools in cmd and PowerShell, last (path-plan.ts keeps MSVC before it).
150
+ hideCoreutilsLink(path.join(root, 'usr', 'bin'));
133
151
  env.append.push(path.join(root, 'usr', 'bin'));
134
152
  const version = execOutput(gitExe, ['--version']).replace(/^git version /, '');
135
153
  if (access === 'full') {
@@ -4,7 +4,8 @@
4
4
  import crypto from 'node:crypto';
5
5
  import fs from 'node:fs';
6
6
  import path from 'node:path';
7
- import { downloader, spawnAsync } from '../../shared/download/index.js';
7
+ import { runInstaller } from '../runtime/pools.js';
8
+ import { downloader } from '../../shared/download/index.js';
8
9
  import { SYSTEM } from '../../shared/system.js';
9
10
  import { dryRun, isWin, toolsDir } from '../runtime/paths.js';
10
11
  import { exists, mkdirp } from '../runtime/fs.js';
@@ -48,9 +49,7 @@ export async function ensureOpenVpn() {
48
49
  repairServiceQueries();
49
50
  log('VPN: installing OpenVPN');
50
51
  const logFile = path.join(toolsDir, 'downloads', 'openvpn-install.log');
51
- const code = await spawnAsync(SYSTEM.msiexec(), ['/i', msi, '/qn', '/norestart', '/l*v', logFile], {
52
- stdio: 'ignore',
53
- });
52
+ const code = await runInstaller(SYSTEM.msiexec(), ['/i', msi, '/qn', '/norestart', '/l*v', logFile]);
54
53
  // 3010: installed, a restart is wanted (nothing here needs one).
55
54
  if (code !== 0 && code !== 3010)
56
55
  throw new Error('The OpenVPN installer failed (exit ' + code + '). Its log: ' + logFile);
@@ -1,6 +1,9 @@
1
+ import { type VpnFailureCode } from '../../shared/vpn/failure.js';
1
2
  export interface TunnelOutcome {
2
3
  readonly ok: boolean;
3
4
  readonly message: string;
5
+ /** Why it failed (when it did), for the host to act on. */
6
+ readonly code?: VpnFailureCode;
4
7
  /** The tunnel's own address (what the kill switch lets through). */
5
8
  readonly tunnelIp?: string;
6
9
  }
@@ -6,21 +6,14 @@ import { spawn } from 'node:child_process';
6
6
  import fs from 'node:fs';
7
7
  import os from 'node:os';
8
8
  import path from 'node:path';
9
+ import { classifyFailure } from '../../shared/vpn/failure.js';
9
10
  import { tunnelAddressFromLog } from '../../shared/vpn/kill-switch.js';
10
11
  import { logsDir } from '../runtime/paths.js';
11
12
  import { log } from '../runtime/log.js';
12
13
  const DONE = 'Initialization Sequence Completed';
13
14
  /** Why the log says OpenVPN stopped, in a short plain sentence (or null when it does not say). */
14
15
  export function failureReason(text) {
15
- if (/AUTH_FAILED/i.test(text))
16
- return 'the VPN refused the login (for NordVPN use the service credentials, not your account password)';
17
- if (/TLS Error: TLS key negotiation failed|TLS handshake failed/i.test(text))
18
- return 'it could not reach the VPN server (the network blocks it, or the server is down)';
19
- if (/All wintun adapters on this system are in use|Cannot open TUN\/TAP|Could not create Wintun|ovpn-dco.*(error|failed)/i.test(text))
20
- return 'the network driver for the tunnel could not be used';
21
- if (/Exiting due to fatal error/i.test(text))
22
- return 'OpenVPN stopped with an error';
23
- return null;
16
+ return classifyFailure(text)?.reason ?? null;
24
17
  }
25
18
  const tail = (text, lines = 12) => text.trim().split(/\r?\n/).slice(-lines).join('\n');
26
19
  /** Connects. Never leaves the login on disk; kills OpenVPN when the tunnel does not come up in time. */
@@ -57,17 +50,22 @@ export async function startTunnel(o) {
57
50
  const tunnelIp = tunnelAddressFromLog(text) ?? undefined;
58
51
  return { ok: true, message: 'the tunnel is up', ...(tunnelIp ? { tunnelIp } : {}) };
59
52
  }
60
- const reason = failureReason(text);
53
+ const failure = classifyFailure(text);
54
+ const reason = failure?.reason ?? null;
61
55
  if (exited !== null || reason) {
62
56
  if (exited === null)
63
57
  child.kill();
64
58
  keepLog();
65
- return { ok: false, message: (reason ?? 'OpenVPN stopped') + '. ' + tail(text, 4) };
59
+ return {
60
+ ok: false,
61
+ code: failure?.code ?? 'other',
62
+ message: (reason ?? 'OpenVPN stopped') + '. ' + tail(text, 4),
63
+ };
66
64
  }
67
65
  if (Date.now() > deadline) {
68
66
  child.kill();
69
67
  keepLog();
70
- return { ok: false, message: 'the tunnel did not come up in time. ' + tail(text, 4) };
68
+ return { ok: false, code: 'timeout', message: 'the tunnel did not come up in time. ' + tail(text, 4) };
71
69
  }
72
70
  await new Promise((r) => setTimeout(r, 500));
73
71
  }
@@ -123,6 +123,7 @@ export async function setupVpn(secrets, ptyHostExe) {
123
123
  warn('VPN: the kill switch did not hold: a direct connection still got out. If the tunnel drops, traffic leaves through your normal connection.');
124
124
  };
125
125
  setVpnStatus('starting', 'Starting the VPN');
126
+ let failureCode;
126
127
  try {
127
128
  if (!session.networking)
128
129
  throw new Error('networking is off for this sandbox');
@@ -178,6 +179,7 @@ export async function setupVpn(secrets, ptyHostExe) {
178
179
  if (!result.ok) {
179
180
  if (wireguardExe)
180
181
  await removeWireguardTunnel(wireguardExe);
182
+ failureCode = result.code;
181
183
  throw new Error(result.message);
182
184
  }
183
185
  if (!result.tunnelIp || !hasAdapter(result.tunnelIp))
@@ -207,7 +209,10 @@ export async function setupVpn(secrets, ptyHostExe) {
207
209
  ? ' The kill switch is on, so this sandbox has NO internet access.'
208
210
  : ' The kill switch could NOT close the network, so this sandbox is ONLINE through your normal connection, without the VPN.';
209
211
  warn('VPN failed: ' + reason + after);
210
- setVpnStatus('failed', reason, killSwitch ? { killSwitchHeld: held } : {});
212
+ setVpnStatus('failed', reason, {
213
+ code: failureCode ?? 'other',
214
+ ...(killSwitch ? { killSwitchHeld: held } : {}),
215
+ });
211
216
  return false;
212
217
  }
213
218
  }
@@ -7,7 +7,8 @@ import crypto from 'node:crypto';
7
7
  import fs from 'node:fs';
8
8
  import os from 'node:os';
9
9
  import path from 'node:path';
10
- import { downloader, spawnAsync } from '../../shared/download/index.js';
10
+ import { runInstaller } from '../runtime/pools.js';
11
+ import { downloader } from '../../shared/download/index.js';
11
12
  import { SYSTEM } from '../../shared/system.js';
12
13
  import { WIREGUARD_MSI } from '../../shared/vpn/download.js';
13
14
  import { handshakeDone, serviceRunning } from '../../shared/vpn/wireguard.js';
@@ -46,9 +47,15 @@ export async function ensureWireguard() {
46
47
  log('VPN: installing WireGuard');
47
48
  const logFile = path.join(toolsDir, 'downloads', 'wireguard-install.log');
48
49
  // DO_NOT_LAUNCH: the installer would otherwise start the manager window.
49
- const code = await spawnAsync(SYSTEM.msiexec(), ['/i', msi, '/qn', '/norestart', 'DO_NOT_LAUNCH=1', '/l*v', logFile], {
50
- stdio: 'ignore',
51
- });
50
+ const code = await runInstaller(SYSTEM.msiexec(), [
51
+ '/i',
52
+ msi,
53
+ '/qn',
54
+ '/norestart',
55
+ 'DO_NOT_LAUNCH=1',
56
+ '/l*v',
57
+ logFile,
58
+ ]);
52
59
  if (code !== 0 && code !== 3010)
53
60
  throw new Error('The WireGuard installer failed (exit ' + code + '). Its log: ' + logFile);
54
61
  if (!exists(exe))
@@ -9,6 +9,8 @@ function howToSave(spec) {
9
9
  return "sign in to the Epic Games Launcher in this sandbox once, or take this PC's: sandbox account import epic";
10
10
  if (spec.kind === 'steam')
11
11
  return 'sign in once in this sandbox with sbx-steam login';
12
+ if (spec.kind === 'comet')
13
+ return 'sign in to Comet once in this sandbox, and it is kept for the next ones';
12
14
  if (spec.returns)
13
15
  return 'sign in once in this sandbox, or: sandbox account import ' + spec.kind;
14
16
  return spec.sources.includes('typed')
@@ -56,6 +58,11 @@ export function prepareServiceAccounts(i) {
56
58
  if (spec.returns)
57
59
  leased.push(kind);
58
60
  }
61
+ else if (spec.returns) {
62
+ // Several sandboxes may use it at once: each gets its own lease, so each may hand a renewed one back.
63
+ leases.claimShared(kind, dataSet, i.launchId, payload ? fingerprint(payload) : null);
64
+ leased.push(kind);
65
+ }
59
66
  if (unreadable) {
60
67
  notes.push('Your saved ' +
61
68
  spec.label +
@@ -61,12 +61,8 @@ export function takeHandoffs(dataPath, final, store = new ServiceAccountStore(st
61
61
  const spec = SERVICE_SPECS[kind];
62
62
  const bytes = readBoundedFile(file, spec.maxBytes + ENVELOPE_BYTES);
63
63
  const parsed = bytes ? handoffSchema(kind).safeParse(JSON.parse(bytes.toString('utf8'))) : null;
64
- const lease = leases.holder(kind);
65
- if (parsed?.success &&
66
- spec.returns &&
67
- lease &&
68
- lease.dataSet === dataSet &&
69
- lease.launchId === parsed.data.launchId) {
64
+ const lease = leases.leaseOf(kind, dataSet);
65
+ if (parsed?.success && spec.returns && lease && lease.launchId === parsed.data.launchId) {
70
66
  const payload = parsed.data.payload;
71
67
  store.save(kind, payload, 'sandbox', HOST_SOURCES[kind].summary(payload));
72
68
  ok = true;
@@ -2,5 +2,5 @@ export { accountsDir, ServiceAccountStore, type AccountInfo, type AccountState }
2
2
  export { AccountLeases, type ClaimResult, type Lease } from './leases.js';
3
3
  export { prepareServiceAccounts, type AccountDelivery, type DeliveryInput } from './deliver.js';
4
4
  export { forgetEverywhere, handoffDir, takeHandoffs } from './handoff.js';
5
- export { notAvailableYet, pcEnv, type HostSource, type PcEnv } from './source.js';
5
+ export { notAvailableYet, parseRegValue, pcEnv, type HostSource, type PcEnv } from './source.js';
6
6
  export { HOST_SOURCES } from './sources.js';
@@ -4,5 +4,5 @@ export { accountsDir, ServiceAccountStore } from './store.js';
4
4
  export { AccountLeases } from './leases.js';
5
5
  export { prepareServiceAccounts } from './deliver.js';
6
6
  export { forgetEverywhere, handoffDir, takeHandoffs } from './handoff.js';
7
- export { notAvailableYet, pcEnv } from './source.js';
7
+ export { notAvailableYet, parseRegValue, pcEnv } from './source.js';
8
8
  export { HOST_SOURCES } from './sources.js';
@@ -1,2 +1,2 @@
1
- import { type HostSource } from '../source.js';
1
+ import type { HostSource } from '../source.js';
2
2
  export declare const cfxSource: HostSource<'cfx'>;
@@ -1,7 +1,50 @@
1
- import { notAvailableYet } from '../source.js';
1
+ // Cfx.re: a FiveM / RedM server licence key from the keymaster (https://portal.cfx.re), and an optional Steam Web API
2
+ // key. Typed with `sandbox account login cfx`, or read from a server.cfg with `sandbox account import cfx --from`.
3
+ import { CfxPayloadSchema } from '../../../shared/accounts/index.js';
4
+ import { fail } from '../../../core/util/index.js';
5
+ import { decodeText } from '../../../shared/io/index.js';
6
+ const MAX_LINES = 20_000;
7
+ const LICENSE_LINE = /^\s*(?:set[rs]?\s+)?sv_licenseKey\s+"?([^"\s]+)"?/i;
8
+ const STEAM_LINE = /^\s*set[rs]?\s+steam_webApiKey\s+"?([A-Fa-f0-9]{32})"?/i;
9
+ /** The line without a `#` or `//` comment (quotes are respected). */
10
+ function withoutComment(line) {
11
+ let quoted = false;
12
+ for (let i = 0; i < line.length; i++) {
13
+ const c = line[i];
14
+ if (c === '"')
15
+ quoted = !quoted;
16
+ else if (!quoted && (c === '#' || (c === '/' && line[i + 1] === '/')))
17
+ return line.slice(0, i);
18
+ }
19
+ return line;
20
+ }
2
21
  export const cfxSource = {
3
- fromAnswers(_answers) {
4
- return notAvailableYet('cfx');
22
+ fromFile(bytes) {
23
+ let licenseKey = '';
24
+ let steamWebApiKey = '';
25
+ for (const raw of decodeText(bytes).text.split(/\r?\n/, MAX_LINES)) {
26
+ const line = withoutComment(raw);
27
+ const key = LICENSE_LINE.exec(line);
28
+ if (key)
29
+ licenseKey = key[1];
30
+ const steam = STEAM_LINE.exec(line);
31
+ if (steam)
32
+ steamWebApiKey = steam[1].toUpperCase();
33
+ }
34
+ if (!licenseKey)
35
+ fail('That server.cfg has no sv_licenseKey.', 'Type the key instead: sandbox account login cfx');
36
+ const parsed = CfxPayloadSchema.safeParse({ licenseKey, ...(steamWebApiKey ? { steamWebApiKey } : {}) });
37
+ if (!parsed.success)
38
+ fail('The sv_licenseKey in that server.cfg is not a Cfx.re server key (cfxk_...).', 'Copy the key from https://portal.cfx.re (Server keys) and run: sandbox account login cfx');
39
+ return parsed.data;
5
40
  },
6
- summary: () => null,
41
+ fromAnswers(answers) {
42
+ const licenseKey = (answers.licenseKey ?? '').trim();
43
+ const steamWebApiKey = (answers.steamWebApiKey ?? '').trim().toUpperCase();
44
+ const parsed = CfxPayloadSchema.safeParse({ licenseKey, ...(steamWebApiKey ? { steamWebApiKey } : {}) });
45
+ if (!parsed.success)
46
+ fail('That is not a Cfx.re server key (cfxk_...) or a Steam Web API key (32 hex characters).', 'Copy the key from https://portal.cfx.re (Server keys) and run: sandbox account login cfx');
47
+ return parsed.data;
48
+ },
49
+ summary: (p) => 'server key' + (p.steamWebApiKey ? ' and Steam Web API key' : ''),
7
50
  };
@@ -0,0 +1,2 @@
1
+ import type { HostSource } from '../source.js';
2
+ export declare const cometSource: HostSource<'comet'>;
@@ -0,0 +1,6 @@
1
+ // Comet: nothing to take from this PC. Its sign-in there is sealed to your Windows account (and reading another
2
+ // browser's cookies is what password stealers do), so it only ever comes from a sandbox you signed in in.
3
+ import { cometSummary } from '../../../shared/accounts/index.js';
4
+ export const cometSource = {
5
+ summary: (p) => cometSummary(p),
6
+ };
@@ -0,0 +1,2 @@
1
+ import type { HostSource } from '../source.js';
2
+ export declare const perplexitySource: HostSource<'perplexity'>;
@@ -0,0 +1,30 @@
1
+ // Perplexity: an API key, taken from this PC (`sandbox account import perplexity`: PERPLEXITY_API_KEY of this
2
+ // terminal, else of your saved Windows user environment), from a file that holds it (`--from .env`: only its
3
+ // PERPLEXITY_API_KEY line is read), or typed (`sandbox account login perplexity`). The key is never printed.
4
+ import { fail } from '../../../core/util/index.js';
5
+ import { PERPLEXITY_ENV, PerplexityPayloadSchema, perplexityKeyFromText, perplexitySummary, } from '../../../shared/accounts/index.js';
6
+ import { decodeText } from '../../../shared/io/index.js';
7
+ /** A key value as it may sit in a variable (quotes and spaces around it taken off). */
8
+ const keyFrom = (value) => {
9
+ const parsed = PerplexityPayloadSchema.safeParse({ apiKey: (value ?? '').trim().replace(/^(["'])(.*)\1$/, '$2') });
10
+ return parsed.success ? parsed.data : null;
11
+ };
12
+ export const perplexitySource = {
13
+ /** PERPLEXITY_API_KEY of this PC: this terminal's environment first, then your saved user environment. */
14
+ fromPc(pc) {
15
+ return keyFrom(pc.env?.[PERPLEXITY_ENV]) ?? keyFrom(pc.userVariable?.(PERPLEXITY_ENV));
16
+ },
17
+ fromFile(bytes) {
18
+ const key = perplexityKeyFromText(decodeText(bytes).text);
19
+ if (!key)
20
+ fail('That file holds no Perplexity API key.', 'Name a .env file with a line PERPLEXITY_API_KEY=pplx-..., or type the key: sandbox account login perplexity');
21
+ return key;
22
+ },
23
+ fromAnswers(answers) {
24
+ const parsed = PerplexityPayloadSchema.safeParse({ apiKey: (answers.apiKey ?? '').trim() });
25
+ if (!parsed.success)
26
+ fail('That is not a Perplexity API key.', 'Copy it from perplexity.ai/account/api/keys: it starts with pplx-.');
27
+ return parsed.data;
28
+ },
29
+ summary: (p) => perplexitySummary(p),
30
+ };
@@ -3,13 +3,15 @@ import { type ServiceKind } from '../../shared/accounts/index.js';
3
3
  declare const LeaseSchema: z.ZodObject<{
4
4
  kind: z.ZodEnum<{
5
5
  unity: "unity";
6
- steam: "steam";
7
- epic: "epic";
8
- cfx: "cfx";
9
6
  keystore: "keystore";
10
7
  play: "play";
11
8
  expo: "expo";
12
9
  appstore: "appstore";
10
+ steam: "steam";
11
+ epic: "epic";
12
+ cfx: "cfx";
13
+ perplexity: "perplexity";
14
+ comet: "comet";
13
15
  }>;
14
16
  dataSet: z.ZodString;
15
17
  launchId: z.ZodString;
@@ -29,6 +31,10 @@ export declare class AccountLeases {
29
31
  private all;
30
32
  private keep;
31
33
  holder(kind: ServiceKind): Lease | null;
34
+ /** The kind's lease that this data set holds, if any. */
35
+ leaseOf(kind: ServiceKind, dataSet: string): Lease | null;
36
+ /** A lease for this data set and launch beside the other data sets' (a sign-in several sandboxes may use). */
37
+ claimShared(kind: ServiceKind, dataSet: string, launchId: string, delivered: string | null): void;
32
38
  /** The lease for this data set and launch, unless another data set's sandbox still runs with it. */
33
39
  claim(kind: ServiceKind, dataSet: string, launchId: string, delivered: string | null, isRunning: (dataSet: string) => boolean): ClaimResult;
34
40
  release(kind: ServiceKind, dataSet: string): void;
@@ -1,6 +1,7 @@
1
1
  // Which data set holds a one-holder sign-in (Epic, Steam, Unity work in one place at a time), in host-only storage
2
2
  // (<storage>\accounts\leases.json). A launch claims the lease even when nothing is saved yet, so a first sign-in made
3
- // in that sandbox can come back; a lease whose sandbox no longer runs is stale and is taken over.
3
+ // in that sandbox can come back; a lease whose sandbox no longer runs is stale and is taken over. A sign-in several
4
+ // sandboxes may use at once (Comet) has one lease per data set (claimShared): each may hand it back, the last wins.
4
5
  import path from 'node:path';
5
6
  import { z } from 'zod';
6
7
  import { JsonFile } from '../../shared/io/index.js';
@@ -14,7 +15,8 @@ const LeaseSchema = z.object({
14
15
  /** The fingerprint of what was handed over (null: nothing saved yet). */
15
16
  delivered: z.string().max(128).nullable(),
16
17
  });
17
- const LeasesSchema = z.object({ leases: z.array(LeaseSchema).max(SERVICE_KINDS.length) });
18
+ /** Room for one lease per kind, plus one per data set for the kinds several sandboxes share. */
19
+ const LeasesSchema = z.object({ leases: z.array(LeaseSchema).max(SERVICE_KINDS.length * 64) });
18
20
  export class AccountLeases {
19
21
  file;
20
22
  constructor(storageRoot) {
@@ -29,6 +31,15 @@ export class AccountLeases {
29
31
  holder(kind) {
30
32
  return this.all().find((l) => l.kind === kind) ?? null;
31
33
  }
34
+ /** The kind's lease that this data set holds, if any. */
35
+ leaseOf(kind, dataSet) {
36
+ return this.all().find((l) => l.kind === kind && l.dataSet === dataSet) ?? null;
37
+ }
38
+ /** A lease for this data set and launch beside the other data sets' (a sign-in several sandboxes may use). */
39
+ claimShared(kind, dataSet, launchId, delivered) {
40
+ const lease = { kind, dataSet, launchId, issuedAt: new Date().toISOString(), delivered };
41
+ this.keep([...this.all().filter((l) => !(l.kind === kind && l.dataSet === dataSet)), lease]);
42
+ }
32
43
  /** The lease for this data set and launch, unless another data set's sandbox still runs with it. */
33
44
  claim(kind, dataSet, launchId, delivered, isRunning) {
34
45
  const held = this.holder(kind);
@@ -1,10 +1,16 @@
1
1
  import type { Payload, ServiceKind } from '../../shared/accounts/index.js';
2
- /** This PC's profile folders (passed in, so the self-test uses its own). */
2
+ /** This PC's profile folders and environment (passed in, so the self-test uses its own). */
3
3
  export interface PcEnv {
4
4
  readonly localAppData: string;
5
5
  readonly appData: string;
6
6
  readonly programData: string;
7
+ /** The environment the command runs with (a variable set in this terminal). */
8
+ readonly env?: Readonly<Record<string, string | undefined>>;
9
+ /** A variable of this Windows user's saved environment (HKCU\Environment), or null when it is not set. */
10
+ readonly userVariable?: (name: string) => string | null;
7
11
  }
12
+ /** The value of `name` in `reg query <key> /v <name>` output, or null. */
13
+ export declare function parseRegValue(text: string, name: string): string | null;
8
14
  export interface HostSource<K extends ServiceKind> {
9
15
  /** This PC's program's sign-in (`from`: another folder to read it from), or null when it has none. */
10
16
  fromPc?(pc: PcEnv, from?: string): Payload<K> | null;