windows-development-sandbox 1.20.0 → 1.23.1

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 (337) hide show
  1. package/config.example.json +8 -1
  2. package/dist/app/features/access/access.def.js +5 -2
  3. package/dist/app/features/access/access.run.js +7 -3
  4. package/dist/app/features/accounts/accounts.def.d.ts +29 -1
  5. package/dist/app/features/accounts/accounts.def.js +10 -4
  6. package/dist/app/features/accounts/accounts.run.d.ts +1 -0
  7. package/dist/app/features/accounts/accounts.run.js +7 -2
  8. package/dist/app/features/accounts/services.def.d.ts +4 -1
  9. package/dist/app/features/accounts/services.def.js +15 -14
  10. package/dist/app/features/accounts/services.run.d.ts +1 -0
  11. package/dist/app/features/accounts/services.run.js +41 -15
  12. package/dist/app/features/defs.d.ts +39 -2
  13. package/dist/app/features/engine/engine.run.js +1 -1
  14. package/dist/app/features/github/github.def.d.ts +44 -3
  15. package/dist/app/features/github/github.def.js +145 -63
  16. package/dist/app/features/github/github.run.d.ts +9 -2
  17. package/dist/app/features/github/github.run.js +43 -5
  18. package/dist/app/features/health/health.def.js +8 -2
  19. package/dist/app/features/logs/logs.def.js +0 -8
  20. package/dist/app/features/projects/projects.def.js +2 -2
  21. package/dist/app/features/projects/projects.run.js +5 -5
  22. package/dist/app/features/sandbox/sandbox.def.js +7 -3
  23. package/dist/app/features/sessions/sessions.def.d.ts +4 -4
  24. package/dist/app/features/sessions/sessions.def.js +0 -5
  25. package/dist/app/features/sessions/sessions.run.d.ts +1 -1
  26. package/dist/app/features/sessions/sessions.run.js +2 -2
  27. package/dist/app/features/settings/categories.def.js +2 -0
  28. package/dist/app/features/settings/settings-rows.js +1 -1
  29. package/dist/app/features/settings/settings.def.js +1 -1
  30. package/dist/app/features/settings/settings.run.js +3 -3
  31. package/dist/app/features/storage/storage.run.js +18 -9
  32. package/dist/app/features/updates/update-lines.js +2 -2
  33. package/dist/app/features/updates/updates.def.js +1 -1
  34. package/dist/app/features/updates/updates.run.js +1 -1
  35. package/dist/app/features/web/web.def.js +2 -2
  36. package/dist/app/features/web/web.run.js +12 -4
  37. package/dist/app/kernel/errors.d.ts +7 -0
  38. package/dist/app/kernel/errors.js +18 -3
  39. package/dist/app/kernel/rpc.d.ts +5 -0
  40. package/dist/app/kernel/view.d.ts +11 -1
  41. package/dist/bin/agent-sandbox.js +2 -2
  42. package/dist/bin/{chunk-DK4NTBUF.js → chunk-7YPEDAJD.js} +172 -48
  43. package/dist/bin/{chunk-2IMT7ZCT.js → chunk-QFSJX2KW.js} +1 -1
  44. package/dist/bin/{chunk-Z2P5OGKL.js → chunk-USTRSHIE.js} +2436 -1693
  45. package/dist/bin/claude-host.js +2 -2
  46. package/dist/bin/claude-sbx.js +2 -2
  47. package/dist/bin/codex-sbx.js +2 -2
  48. package/dist/bin/gemini-sbx.js +2 -2
  49. package/dist/bin/sandbox.js +2 -2
  50. package/dist/bin/sbx-hide.js +2 -2
  51. package/dist/bin/sbx-show.js +2 -2
  52. package/dist/cli/manifest.d.ts +2 -2
  53. package/dist/cli/manifest.js +2 -1
  54. package/dist/cli/menu/coverage-exempt.d.ts +17 -0
  55. package/dist/cli/menu/coverage-exempt.js +61 -0
  56. package/dist/cli/menu/io-menu.d.ts +2 -0
  57. package/dist/cli/menu/io-menu.js +50 -5
  58. package/dist/cli/menu/pages/accounts-page.js +1 -1
  59. package/dist/cli/menu/pages/agents-page.js +8 -2
  60. package/dist/cli/menu/pages/maintenance-page.d.ts +6 -0
  61. package/dist/cli/menu/pages/maintenance-page.js +9 -2
  62. package/dist/cli/menu/pages/sessions-page.d.ts +2 -0
  63. package/dist/cli/menu/pages/sessions-page.js +12 -4
  64. package/dist/cli/menu/pages/settings-page.js +12 -1
  65. package/dist/cli/menu/remote-menu.js +1 -1
  66. package/dist/cli/menu/shell.d.ts +5 -0
  67. package/dist/cli/menu/shell.js +13 -0
  68. package/dist/cli/report.js +2 -0
  69. package/dist/cli/tui/app.d.ts +5 -0
  70. package/dist/cli/tui/app.js +2 -0
  71. package/dist/core/config/defaults.js +3 -1
  72. package/dist/core/schema/accounts.d.ts +6 -0
  73. package/dist/core/schema/accounts.js +13 -11
  74. package/dist/core/schema/config.d.ts +20 -8
  75. package/dist/core/schema/config.js +4 -4
  76. package/dist/core/schema/github.js +1 -1
  77. package/dist/core/schema/profile.js +8 -4
  78. package/dist/core/schema/resume.js +1 -1
  79. package/dist/core/schema/sandbox.js +3 -3
  80. package/dist/core/schema/vpn.js +1 -1
  81. package/dist/core/schema/web.js +3 -3
  82. package/dist/core/types.d.ts +6 -0
  83. package/dist/core/util/errors.d.ts +11 -1
  84. package/dist/core/util/errors.js +7 -1
  85. package/dist/core/util/index.d.ts +1 -1
  86. package/dist/core/util/index.js +1 -1
  87. package/dist/guest/accounts/adapter.d.ts +2 -0
  88. package/dist/guest/accounts/adapters.js +4 -0
  89. package/dist/guest/accounts/apply.d.ts +6 -2
  90. package/dist/guest/accounts/kinds/cfx.d.ts +7 -0
  91. package/dist/guest/accounts/kinds/cfx.js +33 -3
  92. package/dist/guest/accounts/kinds/comet.d.ts +7 -0
  93. package/dist/guest/accounts/kinds/comet.js +50 -0
  94. package/dist/guest/accounts/kinds/perplexity.d.ts +2 -0
  95. package/dist/guest/accounts/kinds/perplexity.js +12 -0
  96. package/dist/guest/accounts/watch.d.ts +2 -0
  97. package/dist/guest/accounts/watch.js +12 -2
  98. package/dist/guest/agents/agent-installer.js +1 -1
  99. package/dist/guest/agents/profile-links.d.ts +2 -0
  100. package/dist/guest/agents/profile-links.js +18 -4
  101. package/dist/guest/apps/after-install.js +19 -0
  102. package/dist/guest/apps/app-installer.d.ts +11 -0
  103. package/dist/guest/apps/app-installer.js +69 -6
  104. package/dist/guest/apps/comet-unpack.d.ts +20 -0
  105. package/dist/guest/apps/comet-unpack.js +127 -0
  106. package/dist/guest/apps/index.d.ts +1 -1
  107. package/dist/guest/apps/index.js +1 -1
  108. package/dist/guest/apps/resolvers-agents-web.d.ts +2 -0
  109. package/dist/guest/apps/resolvers-agents-web.js +7 -0
  110. package/dist/guest/apps/resolvers.js +2 -0
  111. package/dist/guest/apps/shortcuts.d.ts +2 -0
  112. package/dist/guest/apps/shortcuts.js +1 -0
  113. package/dist/guest/apps/unpack.js +14 -4
  114. package/dist/guest/boot/boot.d.ts +45 -16
  115. package/dist/guest/boot/boot.js +316 -145
  116. package/dist/guest/boot/graph.d.ts +252 -0
  117. package/dist/guest/boot/graph.js +257 -0
  118. package/dist/guest/boot/guide-lines.d.ts +25 -0
  119. package/dist/guest/boot/guide-lines.js +41 -0
  120. package/dist/guest/boot/guide.d.ts +1 -4
  121. package/dist/guest/boot/guide.js +6 -8
  122. package/dist/guest/boot/runner.d.ts +77 -0
  123. package/dist/guest/boot/runner.js +215 -0
  124. package/dist/guest/browser/cdp.d.ts +71 -0
  125. package/dist/guest/browser/cdp.js +306 -0
  126. package/dist/guest/browser/comet.d.ts +28 -0
  127. package/dist/guest/browser/comet.js +155 -0
  128. package/dist/guest/daemon/project-provisioner.js +6 -1
  129. package/dist/guest/devtools/fivem.d.ts +1 -1
  130. package/dist/guest/devtools/fivem.js +243 -3
  131. package/dist/guest/entries/daemon.js +2 -0
  132. package/dist/guest/runtime/log.d.ts +12 -0
  133. package/dist/guest/runtime/log.js +29 -5
  134. package/dist/guest/runtime/pools.d.ts +24 -0
  135. package/dist/guest/runtime/pools.js +60 -0
  136. package/dist/guest/runtime/process.d.ts +4 -0
  137. package/dist/guest/runtime/process.js +38 -1
  138. package/dist/guest/runtime/status.d.ts +5 -1
  139. package/dist/guest/runtime/status.js +19 -0
  140. package/dist/guest/setup/addon-mcp.d.ts +1 -1
  141. package/dist/guest/setup/addon-mcp.js +52 -12
  142. package/dist/guest/setup/agent-addons.d.ts +3 -2
  143. package/dist/guest/setup/agent-addons.js +12 -3
  144. package/dist/guest/setup/environment.d.ts +1 -1
  145. package/dist/guest/setup/environment.js +25 -12
  146. package/dist/guest/setup/headroom.d.ts +8 -3
  147. package/dist/guest/setup/headroom.js +44 -15
  148. package/dist/guest/setup/path-plan.d.ts +28 -0
  149. package/dist/guest/setup/path-plan.js +69 -2
  150. package/dist/guest/setup/shell-path.d.ts +1 -1
  151. package/dist/guest/setup/shell-path.js +3 -3
  152. package/dist/guest/toolchains/fivem/artifacts.d.ts +48 -1
  153. package/dist/guest/toolchains/fivem/artifacts.js +97 -4
  154. package/dist/guest/toolchains/fivem/fivem.d.ts +32 -1
  155. package/dist/guest/toolchains/fivem/fivem.js +168 -3
  156. package/dist/guest/toolchains/fivem/scaffold.d.ts +35 -0
  157. package/dist/guest/toolchains/fivem/scaffold.js +105 -0
  158. package/dist/guest/toolchains/fivem/serve-plan.d.ts +34 -0
  159. package/dist/guest/toolchains/fivem/serve-plan.js +61 -0
  160. package/dist/guest/toolchains/languages/go.js +1 -1
  161. package/dist/guest/toolchains/languages/node.js +2 -1
  162. package/dist/guest/toolchains/languages/php.d.ts +2 -0
  163. package/dist/guest/toolchains/languages/php.js +13 -1
  164. package/dist/guest/toolchains/languages/python-tools.js +1 -1
  165. package/dist/guest/toolchains/languages/ruby.js +1 -1
  166. package/dist/guest/toolchains/native/binstall.js +1 -1
  167. package/dist/guest/toolchains/native/msvc/packages.js +10 -4
  168. package/dist/guest/toolchains/native/rust.d.ts +2 -0
  169. package/dist/guest/toolchains/native/rust.js +11 -4
  170. package/dist/guest/toolchains/native/unreal.d.ts +2 -0
  171. package/dist/guest/toolchains/native/unreal.js +16 -6
  172. package/dist/guest/toolchains/native/vcruntime.d.ts +2 -0
  173. package/dist/guest/toolchains/native/vcruntime.js +10 -2
  174. package/dist/guest/toolchains/native/visual-studio.d.ts +3 -0
  175. package/dist/guest/toolchains/native/visual-studio.js +44 -26
  176. package/dist/guest/toolchains/native/vs-request.d.ts +2 -0
  177. package/dist/guest/toolchains/native/vs-request.js +2 -0
  178. package/dist/guest/toolchains/plan.js +19 -8
  179. package/dist/guest/toolchains/scheduler.d.ts +2 -21
  180. package/dist/guest/toolchains/scheduler.js +7 -121
  181. package/dist/guest/toolchains/setup.d.ts +15 -2
  182. package/dist/guest/toolchains/setup.js +39 -20
  183. package/dist/guest/toolchains/split.d.ts +28 -0
  184. package/dist/guest/toolchains/split.js +60 -0
  185. package/dist/guest/toolchains/steam/client.js +2 -1
  186. package/dist/guest/toolchains/step-scheduler.d.ts +51 -0
  187. package/dist/guest/toolchains/step-scheduler.js +147 -0
  188. package/dist/guest/toolchains/step.d.ts +13 -0
  189. package/dist/guest/toolchains/tools/git.js +18 -0
  190. package/dist/guest/vpn/openvpn.js +3 -4
  191. package/dist/guest/vpn/wireguard.js +11 -4
  192. package/dist/host/accounts/deliver.js +7 -0
  193. package/dist/host/accounts/handoff.js +2 -6
  194. package/dist/host/accounts/index.d.ts +1 -1
  195. package/dist/host/accounts/index.js +1 -1
  196. package/dist/host/accounts/kinds/cfx.d.ts +1 -1
  197. package/dist/host/accounts/kinds/cfx.js +47 -4
  198. package/dist/host/accounts/kinds/comet.d.ts +2 -0
  199. package/dist/host/accounts/kinds/comet.js +6 -0
  200. package/dist/host/accounts/kinds/perplexity.d.ts +2 -0
  201. package/dist/host/accounts/kinds/perplexity.js +30 -0
  202. package/dist/host/accounts/leases.d.ts +7 -1
  203. package/dist/host/accounts/leases.js +13 -2
  204. package/dist/host/accounts/source.d.ts +7 -1
  205. package/dist/host/accounts/source.js +27 -0
  206. package/dist/host/accounts/sources.js +4 -0
  207. package/dist/host/github/deliver.d.ts +1 -1
  208. package/dist/host/github/deliver.js +20 -1
  209. package/dist/host/github/index.d.ts +2 -1
  210. package/dist/host/github/index.js +1 -0
  211. package/dist/host/github/token.d.ts +13 -1
  212. package/dist/host/github/token.js +19 -2
  213. package/dist/host/github/verify.d.ts +27 -0
  214. package/dist/host/github/verify.js +61 -0
  215. package/dist/host/launch/hints/fivem.d.ts +3 -1
  216. package/dist/host/launch/hints/fivem.js +68 -2
  217. package/dist/host/launch/session-file.js +4 -0
  218. package/dist/host/launch/toolchains.d.ts +2 -1
  219. package/dist/host/launch/toolchains.js +5 -3
  220. package/dist/host/profile/claude-login.d.ts +2 -2
  221. package/dist/host/profile/claude-login.js +4 -3
  222. package/dist/host/vpn/nordvpn.d.ts +2 -4
  223. package/dist/host/vpn/nordvpn.js +2 -24
  224. package/dist/runtime/agent.js +405 -90
  225. package/dist/runtime/daemon.js +3010 -1383
  226. package/dist/runtime/db.js +333 -90
  227. package/dist/runtime/main.js +4720 -1954
  228. package/dist/runtime/netproxy.js +286 -69
  229. package/dist/runtime/tool.js +2852 -1780
  230. package/dist/runtime/view.js +286 -69
  231. package/dist/selftest/accounts.test.js +1 -1
  232. package/dist/selftest/agent-paths.test.js +39 -1
  233. package/dist/selftest/agent-settings.test.js +6 -0
  234. package/dist/selftest/agents-web.test.d.ts +2 -0
  235. package/dist/selftest/agents-web.test.js +647 -0
  236. package/dist/selftest/app.test.js +5 -2
  237. package/dist/selftest/boot-runner.test.d.ts +2 -0
  238. package/dist/selftest/boot-runner.test.js +456 -0
  239. package/dist/selftest/channel.test.js +15 -0
  240. package/dist/selftest/fivem.test.js +249 -3
  241. package/dist/selftest/github.test.js +94 -9
  242. package/dist/selftest/guest-boot.test.js +3 -0
  243. package/dist/selftest/health.test.js +1 -1
  244. package/dist/selftest/index.js +8 -0
  245. package/dist/selftest/layout.test.js +5 -0
  246. package/dist/selftest/menu-coverage.test.d.ts +2 -0
  247. package/dist/selftest/menu-coverage.test.js +46 -0
  248. package/dist/selftest/menu-screens.js +37 -1
  249. package/dist/selftest/persisted-installs.test.js +6 -3
  250. package/dist/selftest/service-accounts.test.js +3 -2
  251. package/dist/selftest/support/engine-golden.js +27 -0
  252. package/dist/selftest/support/fixtures.d.ts +6 -0
  253. package/dist/selftest/support/web-preview.d.ts +34 -1
  254. package/dist/selftest/support/web-preview.js +21 -18
  255. package/dist/selftest/toolchains.test.js +23 -1
  256. package/dist/selftest/ui-text.test.d.ts +7 -0
  257. package/dist/selftest/ui-text.test.js +300 -0
  258. package/dist/shared/accounts/anthropic.d.ts +1 -1
  259. package/dist/shared/accounts/anthropic.js +9 -8
  260. package/dist/shared/accounts/index.d.ts +2 -0
  261. package/dist/shared/accounts/index.js +2 -0
  262. package/dist/shared/accounts/kinds/comet.d.ts +12 -0
  263. package/dist/shared/accounts/kinds/comet.js +64 -0
  264. package/dist/shared/accounts/kinds/perplexity.d.ts +8 -0
  265. package/dist/shared/accounts/kinds/perplexity.js +37 -0
  266. package/dist/shared/accounts/kinds.d.ts +24 -5
  267. package/dist/shared/accounts/kinds.js +86 -4
  268. package/dist/shared/accounts/payloads.d.ts +81 -0
  269. package/dist/shared/accounts/payloads.js +39 -1
  270. package/dist/shared/apps/agent-addons.d.ts +33 -5
  271. package/dist/shared/apps/agent-addons.js +95 -3
  272. package/dist/shared/apps/catalogue-agents-web.d.ts +14 -0
  273. package/dist/shared/apps/catalogue-agents-web.js +112 -0
  274. package/dist/shared/apps/catalogue-modding.js +32 -1
  275. package/dist/shared/apps/catalogue.d.ts +27 -3
  276. package/dist/shared/apps/catalogue.js +2 -1
  277. package/dist/shared/apps/index.d.ts +3 -1
  278. package/dist/shared/apps/index.js +3 -1
  279. package/dist/shared/channel/schema.d.ts +21 -3
  280. package/dist/shared/channel/schema.js +7 -0
  281. package/dist/shared/channel/secrets.d.ts +19 -0
  282. package/dist/shared/concurrency/index.d.ts +3 -0
  283. package/dist/shared/concurrency/index.js +3 -0
  284. package/dist/shared/concurrency/installer-retry.d.ts +6 -0
  285. package/dist/shared/concurrency/installer-retry.js +10 -0
  286. package/dist/shared/concurrency/limiter.d.ts +19 -0
  287. package/dist/shared/concurrency/limiter.js +67 -0
  288. package/dist/shared/concurrency/pools.d.ts +7 -0
  289. package/dist/shared/concurrency/pools.js +9 -0
  290. package/dist/shared/download/archive.d.ts +2 -0
  291. package/dist/shared/download/archive.js +11 -1
  292. package/dist/shared/download/bounded-fetch.d.ts +13 -0
  293. package/dist/shared/download/bounded-fetch.js +40 -0
  294. package/dist/shared/download/downloader.d.ts +5 -0
  295. package/dist/shared/download/downloader.js +50 -38
  296. package/dist/shared/download/index.d.ts +2 -1
  297. package/dist/shared/download/index.js +2 -1
  298. package/dist/shared/readiness.d.ts +2 -2
  299. package/dist/shared/readiness.js +2 -2
  300. package/dist/web/index.html +1 -1
  301. package/dist/web/{main-OUUQYNK4.js → main-CCILLX2M.js} +67 -67
  302. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  303. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  304. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  305. package/package.json +1 -1
  306. package/sandbox/addons/browser-use-mcp/requirements.in +1 -0
  307. package/sandbox/addons/browser-use-mcp/requirements.txt +2350 -0
  308. package/sandbox/addons/chrome-devtools-mcp/package-lock.json +38 -0
  309. package/sandbox/addons/chrome-devtools-mcp/package.json +7 -0
  310. package/sandbox/addons/comet-mcp/package-lock.json +1277 -0
  311. package/sandbox/addons/comet-mcp/package.json +7 -0
  312. package/sandbox/addons/perplexity-mcp/package-lock.json +1655 -0
  313. package/sandbox/addons/perplexity-mcp/package.json +7 -0
  314. package/sandbox/scripts/add-firewall-port.ps1 +18 -4
  315. package/sandbox/scripts/set-machine-environment.ps1 +30 -15
  316. package/sandbox/templates/fivem/README.txt +6 -2
  317. package/sandbox/templates/fivem/cs/Client/Client.csproj +15 -0
  318. package/sandbox/templates/fivem/cs/Client/ClientMain.cs +20 -0
  319. package/sandbox/templates/fivem/cs/Server/Server.csproj +14 -0
  320. package/sandbox/templates/fivem/cs/Server/ServerMain.cs +20 -0
  321. package/sandbox/templates/fivem/cs/fxmanifest.lua +11 -0
  322. package/sandbox/templates/fivem/js/client.js +12 -0
  323. package/sandbox/templates/fivem/js/fxmanifest.lua +9 -0
  324. package/sandbox/templates/fivem/js/server.js +6 -0
  325. package/sandbox/templates/fivem/lua/client.lua +8 -0
  326. package/sandbox/templates/fivem/lua/fxmanifest.lua +10 -0
  327. package/sandbox/templates/fivem/lua/server.lua +6 -0
  328. package/sandbox/templates/fivem/server.cfg +37 -0
  329. package/sandbox/templates/fivem/ts/build.js +17 -0
  330. package/sandbox/templates/fivem/ts/fxmanifest.lua +9 -0
  331. package/sandbox/templates/fivem/ts/package.json +16 -0
  332. package/sandbox/templates/fivem/ts/src/client/client.ts +12 -0
  333. package/sandbox/templates/fivem/ts/src/client/tsconfig.json +13 -0
  334. package/sandbox/templates/fivem/ts/src/server/server.ts +6 -0
  335. package/sandbox/templates/fivem/ts/src/server/tsconfig.json +13 -0
  336. package/schemas/config.schema.json +56 -27
  337. package/schemas/sandbox-config.schema.json +37 -14
@@ -1,3 +1,4 @@
1
+ import type { AppSigner } from '../signer.js';
1
2
  export type AppCategory = 'agents' | 'editors' | 'databases' | 'api' | 'git' | 'utilities' | 'browsers' | 'gamedev' | 'cloud' | 'diagrams' | 'network' | 'reversing' | 'cli';
2
3
  export declare const APP_CATEGORIES: {
3
4
  id: AppCategory;
@@ -23,13 +24,31 @@ export type AppSource = {
23
24
  locked: 'npm' | 'uv';
24
25
  package: string;
25
26
  version: string;
27
+ }
28
+ /** Nothing to download: a remote MCP server (AddonMcp.url). */
29
+ | {
30
+ remote: true;
26
31
  };
27
- /** An MCP server an add-on adds to the sandbox's Claude Code: `node <its folder>\<script> <args>`. */
32
+ /**
33
+ * An MCP server an add-on adds to the sandbox's Claude Code (and Codex, with `codex`): `node <its folder>\<script>
34
+ * <args>`, the add-on's own program (`program`), or a remote server (`url`).
35
+ */
28
36
  export interface AddonMcp {
29
37
  /** The server's name in Claude Code (sbx-...: the sandbox removes its own names again when the add-on is off). */
30
38
  readonly name: string;
31
- readonly script: string;
39
+ /** The script node runs, below the add-on's folder. */
40
+ readonly script?: string;
41
+ /** Instead: the add-on's own program (its exe) runs the server. */
42
+ readonly program?: boolean;
43
+ /** Instead: a remote server over HTTP; nothing runs in the sandbox, and it signs in with OAuth on first use. */
44
+ readonly url?: string;
32
45
  readonly args?: readonly string[];
46
+ /** Variables of the agent's environment the server reads (Codex passes on only those it is told about). */
47
+ readonly envVars?: readonly string[];
48
+ /** Fixed variables for the server (switches such as telemetry off; never a secret). */
49
+ readonly env?: Readonly<Record<string, string>>;
50
+ /** Also in the sandbox's Codex (its config.toml), not only Claude Code. */
51
+ readonly codex?: boolean;
33
52
  }
34
53
  export interface AppEntry {
35
54
  id: string;
@@ -41,7 +60,7 @@ export interface AppEntry {
41
60
  /**
42
61
  * How the download is unpacked: an archive (zip/7z/tar, the default), the file itself, an NSIS installer or an MSI.
43
62
  */
44
- unpack?: 'archive' | 'file' | 'nsis' | 'msi';
63
+ unpack?: 'archive' | 'file' | 'nsis' | 'msi' | 'chromium';
45
64
  /** The program to start (or the tool on PATH): a file name, or a regex (/.../) for names with a version in them. */
46
65
  exe: string;
47
66
  /** A command-line tool: its folder goes on PATH (no desktop shortcut). */
@@ -63,6 +82,11 @@ export interface AppEntry {
63
82
  service?: boolean;
64
83
  /** The MCP server it adds to the sandbox's Claude Code while it is on. */
65
84
  mcp?: AddonMcp;
85
+ /**
86
+ * Who must sign the download (Authenticode), for a vendor that publishes no checksum: a download signed by anyone
87
+ * else is refused, and it counts as integrity for downloads.requireChecksum.
88
+ */
89
+ signer?: AppSigner;
66
90
  }
67
91
  /** rtk (rtk-ai/rtk), pinned: the Apache-2.0 edition's Windows build. The checksum is the release's checksums.txt. */
68
92
  export declare const RTK_VERSION = "1.0.1";
@@ -3,6 +3,7 @@
3
3
  // shared by the menu on the PC (to list them) and the sandbox (to install them, guest/apps). VS Code and Windows
4
4
  // Terminal have settings of their own (toolchains.vscode, provisioning.terminal) and are listed here too, like
5
5
  // claude-mem (claudeProfile.syncClaudeMem) among the agent add-ons.
6
+ import { AGENTS_WEB_APPS } from './catalogue-agents-web.js';
6
7
  import { GAMEDEV_APPS } from './catalogue-gamedev.js';
7
8
  import { MOBILE_APPS } from './catalogue-mobile.js';
8
9
  import { MODDING_APPS } from './catalogue-modding.js';
@@ -891,7 +892,7 @@ const CORE_APPS = [
891
892
  },
892
893
  ];
893
894
  /** Every app: the ones above, then the game-development, modding and mobile ones (each list in its own file). */
894
- export const APPS = [...CORE_APPS, ...GAMEDEV_APPS, ...MODDING_APPS, ...MOBILE_APPS];
895
+ export const APPS = [...CORE_APPS, ...GAMEDEV_APPS, ...MODDING_APPS, ...MOBILE_APPS, ...AGENTS_WEB_APPS];
895
896
  export function appById(id) {
896
897
  return APPS.find((a) => a.id === id);
897
898
  }
@@ -1,3 +1,5 @@
1
1
  export { type AddonMcp, type AppCategory, APP_CATEGORIES, type AppSource, type AppEntry, APPS, appById, CCUSAGE_VERSION, CONTEXT7_MCP_VERSION, HEADROOM_VERSION, PLAYWRIGHT_MCP_VERSION, RTK_VERSION, } from './catalogue.js';
2
2
  export { exeMatcher, appsToInstall, appsNeedMesa, appsNeedUv } from './selection.js';
3
- export { ADDON_MCP_PREFIX, addonGuideLine, addonMcpServer, claudeManagedSettings, HEADROOM_ENV, HEADROOM_PORT, HEADROOM_UPSTREAM, headroomArgs, headroomEnv, headroomGuideLine, headroomUrl, type ManagedSettings, type McpServerConfig, rtkHookCommand, RTK_GUIDE_LINE, withAddonMcpServers, } from './agent-addons.js';
3
+ export { signerMatches, type AppSigner, type Signature } from '../signer.js';
4
+ export { AGENTS_WEB_APPS, BROWSER_USE_VERSION, CHROME_DEVTOOLS_MCP_VERSION, COMET_DOWNLOAD_URL, COMET_MCP_VERSION, COMET_SIGNER, PERPLEXITY_COMPUTER_MCP_URL, PERPLEXITY_MCP_VERSION, } from './catalogue-agents-web.js';
5
+ export { ADDON_MCP_PREFIX, addonGuideLine, addonMcpServer, type AddonPlace, claudeManagedSettings, codexWithAddonMcpServers, type CodexMcpServer, HEADROOM_ENV, HEADROOM_PORT, HEADROOM_UPSTREAM, headroomArgs, headroomEnv, headroomGuideLine, headroomUrl, type ManagedSettings, type McpServerConfig, rtkHookCommand, RTK_GUIDE_LINE, withAddonMcpServers, } from './agent-addons.js';
@@ -1,3 +1,5 @@
1
1
  export { APP_CATEGORIES, APPS, appById, CCUSAGE_VERSION, CONTEXT7_MCP_VERSION, HEADROOM_VERSION, PLAYWRIGHT_MCP_VERSION, RTK_VERSION, } from './catalogue.js';
2
2
  export { exeMatcher, appsToInstall, appsNeedMesa, appsNeedUv } from './selection.js';
3
- export { ADDON_MCP_PREFIX, addonGuideLine, addonMcpServer, claudeManagedSettings, HEADROOM_ENV, HEADROOM_PORT, HEADROOM_UPSTREAM, headroomArgs, headroomEnv, headroomGuideLine, headroomUrl, rtkHookCommand, RTK_GUIDE_LINE, withAddonMcpServers, } from './agent-addons.js';
3
+ export { signerMatches } from '../signer.js';
4
+ export { AGENTS_WEB_APPS, BROWSER_USE_VERSION, CHROME_DEVTOOLS_MCP_VERSION, COMET_DOWNLOAD_URL, COMET_MCP_VERSION, COMET_SIGNER, PERPLEXITY_COMPUTER_MCP_URL, PERPLEXITY_MCP_VERSION, } from './catalogue-agents-web.js';
5
+ export { ADDON_MCP_PREFIX, addonGuideLine, addonMcpServer, claudeManagedSettings, codexWithAddonMcpServers, HEADROOM_ENV, HEADROOM_PORT, HEADROOM_UPSTREAM, headroomArgs, headroomEnv, headroomGuideLine, headroomUrl, rtkHookCommand, RTK_GUIDE_LINE, withAddonMcpServers, } from './agent-addons.js';
@@ -17,14 +17,23 @@ export declare const BootProgressSchema: z.ZodObject<{
17
17
  done: z.ZodNumber;
18
18
  total: z.ZodNumber;
19
19
  }, z.core.$strict>;
20
+ /** Work that goes on after the agent has started (Visual Studio installing): its name and how it is going. */
21
+ export declare const BackgroundWorkSchema: z.ZodObject<{
22
+ name: z.ZodString;
23
+ state: z.ZodEnum<{
24
+ done: "done";
25
+ running: "running";
26
+ failed: "failed";
27
+ }>;
28
+ }, z.core.$strict>;
20
29
  /** C:\AgentData\run\sandbox.json: boot progress of the sandbox as a whole. */
21
30
  export declare const SandboxStatusSchema: z.ZodObject<{
22
31
  launchId: z.ZodString;
23
32
  phase: z.ZodEnum<{
24
33
  provisioning: "provisioning";
34
+ failed: "failed";
25
35
  booting: "booting";
26
36
  ready: "ready";
27
- failed: "failed";
28
37
  }>;
29
38
  message: z.ZodString;
30
39
  updatedAt: z.ZodString;
@@ -43,6 +52,14 @@ export declare const SandboxStatusSchema: z.ZodObject<{
43
52
  done: z.ZodNumber;
44
53
  total: z.ZodNumber;
45
54
  }, z.core.$strict>>>;
55
+ background: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodObject<{
56
+ name: z.ZodString;
57
+ state: z.ZodEnum<{
58
+ done: "done";
59
+ running: "running";
60
+ failed: "failed";
61
+ }>;
62
+ }, z.core.$strict>>>>;
46
63
  }, z.core.$strip>;
47
64
  /**
48
65
  * A project the host registered after boot (sandbox claude from another folder): everything the guest needs to
@@ -866,9 +883,9 @@ export declare const SessionStatusSchema: z.ZodObject<{
866
883
  cwd: z.ZodString;
867
884
  project: z.ZodOptional<z.ZodString>;
868
885
  phase: z.ZodEnum<{
886
+ running: "running";
869
887
  failed: "failed";
870
888
  starting: "starting";
871
- running: "running";
872
889
  exited: "exited";
873
890
  }>;
874
891
  message: z.ZodString;
@@ -890,8 +907,8 @@ export declare const ProjectStatusSchema: z.ZodObject<{
890
907
  workspace: z.ZodString;
891
908
  phase: z.ZodEnum<{
892
909
  provisioning: "provisioning";
893
- ready: "ready";
894
910
  failed: "failed";
911
+ ready: "ready";
895
912
  }>;
896
913
  message: z.ZodString;
897
914
  updatedAt: z.ZodString;
@@ -929,6 +946,7 @@ export type ProjectStatus = z.output<typeof ProjectStatusSchema>;
929
946
  export type AttachEndpoint = z.output<typeof AttachEndpointSchema>;
930
947
  export type SandboxStatus = z.output<typeof SandboxStatusSchema>;
931
948
  export type BootProgress = z.output<typeof BootProgressSchema>;
949
+ export type BackgroundWork = z.output<typeof BackgroundWorkSchema>;
932
950
  export type GuestRequest = z.output<typeof GuestRequestSchema>;
933
951
  export type GuestResponse = z.output<typeof GuestResponseSchema>;
934
952
  export type SessionStatus = z.output<typeof SessionStatusSchema>;
@@ -20,6 +20,11 @@ export const BootProgressSchema = z.strictObject({
20
20
  done: z.number().int().min(0).max(1000),
21
21
  total: z.number().int().min(0).max(1000),
22
22
  });
23
+ /** Work that goes on after the agent has started (Visual Studio installing): its name and how it is going. */
24
+ export const BackgroundWorkSchema = z.strictObject({
25
+ name: Text(64),
26
+ state: z.enum(['running', 'done', 'failed']),
27
+ });
23
28
  /** C:\AgentData\run\sandbox.json: boot progress of the sandbox as a whole. */
24
29
  export const SandboxStatusSchema = z.object({
25
30
  launchId: Text(64),
@@ -33,6 +38,8 @@ export const SandboxStatusSchema = z.object({
33
38
  addresses: z.array(z.string().max(45)).max(8).optional(),
34
39
  /** A malformed progress is dropped, never the status (the phase still counts). */
35
40
  progress: BootProgressSchema.optional().catch(undefined),
41
+ /** Installs that continue after the boot is ready; a malformed list is dropped, never the status. */
42
+ background: z.array(BackgroundWorkSchema).max(16).optional().catch(undefined),
36
43
  });
37
44
  const VersionSchema = z.strictObject({ spec: Text(128), source: Text(256) });
38
45
  const DatabasePlanSchema = z.strictObject({
@@ -51,6 +51,25 @@ export declare const SecretsFileSchema: z.ZodObject<{
51
51
  keyId: z.ZodString;
52
52
  p8: z.ZodString;
53
53
  }, z.core.$strict>>;
54
+ perplexity: z.ZodOptional<z.ZodObject<{
55
+ apiKey: z.ZodString;
56
+ }, z.core.$strict>>;
57
+ comet: z.ZodOptional<z.ZodObject<{
58
+ cookies: z.ZodArray<z.ZodObject<{
59
+ name: z.ZodString;
60
+ value: z.ZodString;
61
+ domain: z.ZodString;
62
+ path: z.ZodString;
63
+ expires: z.ZodOptional<z.ZodNumber>;
64
+ secure: z.ZodBoolean;
65
+ httpOnly: z.ZodBoolean;
66
+ sameSite: z.ZodOptional<z.ZodEnum<{
67
+ Strict: "Strict";
68
+ Lax: "Lax";
69
+ None: "None";
70
+ }>>;
71
+ }, z.core.$strict>>;
72
+ }, z.core.$strict>>;
54
73
  }, z.core.$strict>>;
55
74
  liveFoldersKey: z.ZodOptional<z.ZodString>;
56
75
  }, z.core.$strict>;
@@ -0,0 +1,3 @@
1
+ export { Limiter, asBackground, type Priority } from './limiter.js';
2
+ export { ERROR_INSTALL_ALREADY_RUNNING, INSTALLER_RETRIES, INSTALLER_WAIT_MS, installerRetry, } from './installer-retry.js';
3
+ export { net, heavy, installer } from './pools.js';
@@ -0,0 +1,3 @@
1
+ export { Limiter, asBackground } from './limiter.js';
2
+ export { ERROR_INSTALL_ALREADY_RUNNING, INSTALLER_RETRIES, INSTALLER_WAIT_MS, installerRetry, } from './installer-retry.js';
3
+ export { net, heavy, installer } from './pools.js';
@@ -0,0 +1,6 @@
1
+ /** msiexec: "another installation is already in progress". */
2
+ export declare const ERROR_INSTALL_ALREADY_RUNNING = 1618;
3
+ export declare const INSTALLER_RETRIES = 20;
4
+ export declare const INSTALLER_WAIT_MS = 15000;
5
+ /** Whether an installer that exited with `code` after `attempt` tries (1 = the first) should be started again. */
6
+ export declare function installerRetry(code: number | null, attempt: number): boolean;
@@ -0,0 +1,10 @@
1
+ // Windows Installer lets one install run at a time machine-wide: another process holding Global\_MSIExecute makes
2
+ // msiexec exit with 1618. Pure.
3
+ /** msiexec: "another installation is already in progress". */
4
+ export const ERROR_INSTALL_ALREADY_RUNNING = 1618;
5
+ export const INSTALLER_RETRIES = 20;
6
+ export const INSTALLER_WAIT_MS = 15_000;
7
+ /** Whether an installer that exited with `code` after `attempt` tries (1 = the first) should be started again. */
8
+ export function installerRetry(code, attempt) {
9
+ return code === ERROR_INSTALL_ALREADY_RUNNING && attempt <= INSTALLER_RETRIES;
10
+ }
@@ -0,0 +1,19 @@
1
+ export type Priority = 'foreground' | 'background';
2
+ /** Runs `work` so that every pool slot it asks for queues behind foreground work. */
3
+ export declare function asBackground<T>(work: () => T): T;
4
+ export declare class Limiter {
5
+ private readonly size;
6
+ readonly name: string;
7
+ private active;
8
+ private peakActive;
9
+ private readonly queues;
10
+ constructor(size: number, name?: string);
11
+ /** The most slots that were ever busy at once (tests). */
12
+ get peak(): number;
13
+ get running(): number;
14
+ /** Runs `work` in a free slot (waiting its turn), and frees the slot afterwards. */
15
+ run<T>(work: () => Promise<T> | T, priority?: Priority): Promise<T>;
16
+ private acquire;
17
+ private take;
18
+ private release;
19
+ }
@@ -0,0 +1,67 @@
1
+ // A counting limiter for heavy work (downloads, unpacking, installers). FIFO, foreground before background. A task
2
+ // that holds a slot may not take a slot of another limiter: nested pools throw, so a deadlock cannot happen. Pure.
3
+ import { AsyncLocalStorage } from 'node:async_hooks';
4
+ /** The pool whose slot the current async context holds (at most one). */
5
+ const held = new AsyncLocalStorage();
6
+ /** The priority of work started in this async context (set by `asBackground`). */
7
+ const priorityOf = new AsyncLocalStorage();
8
+ /** Runs `work` so that every pool slot it asks for queues behind foreground work. */
9
+ export function asBackground(work) {
10
+ return priorityOf.run('background', work);
11
+ }
12
+ export class Limiter {
13
+ size;
14
+ name;
15
+ active = 0;
16
+ peakActive = 0;
17
+ queues = { foreground: [], background: [] };
18
+ constructor(size, name = 'pool') {
19
+ this.size = size;
20
+ this.name = name;
21
+ if (!Number.isInteger(size) || size < 1)
22
+ throw new Error('A pool needs a size of at least 1');
23
+ }
24
+ /** The most slots that were ever busy at once (tests). */
25
+ get peak() {
26
+ return this.peakActive;
27
+ }
28
+ get running() {
29
+ return this.active;
30
+ }
31
+ /** Runs `work` in a free slot (waiting its turn), and frees the slot afterwards. */
32
+ async run(work, priority = priorityOf.getStore() ?? 'foreground') {
33
+ const holding = held.getStore();
34
+ if (holding)
35
+ throw new Error('nested pool: ' + this.name + ' requested while holding ' + holding.name);
36
+ await this.acquire(priority);
37
+ try {
38
+ return await held.run(this, async () => work());
39
+ }
40
+ finally {
41
+ this.release();
42
+ }
43
+ }
44
+ acquire(priority) {
45
+ if (this.active < this.size) {
46
+ this.take();
47
+ return Promise.resolve();
48
+ }
49
+ return new Promise((resolve) => {
50
+ this.queues[priority].push({
51
+ start: () => {
52
+ this.take();
53
+ resolve();
54
+ },
55
+ });
56
+ });
57
+ }
58
+ take() {
59
+ this.active++;
60
+ this.peakActive = Math.max(this.peakActive, this.active);
61
+ }
62
+ release() {
63
+ this.active--;
64
+ const next = this.queues.foreground.shift() ?? this.queues.background.shift();
65
+ next?.start();
66
+ }
67
+ }
@@ -0,0 +1,7 @@
1
+ import { Limiter } from './limiter.js';
2
+ /** HTTP bodies. */
3
+ export declare const net: Limiter;
4
+ /** extractArchive, 7-Zip/NSIS unpack, npm ci/install, uv/pip, Steam, robocopy of big layouts, rustup/cargo. */
5
+ export declare const heavy: Limiter;
6
+ /** Every msiexec, vcredist, DXSETUP and the Visual Studio bootstrapper: Windows runs one install at a time. */
7
+ export declare const installer: Limiter;
@@ -0,0 +1,9 @@
1
+ // The three pools (one set per process): the limiter instances only, so pure code can take a slot. The guest wires
2
+ // them to the downloader and extractor (guest/runtime/pools.ts installPools).
3
+ import { Limiter } from './limiter.js';
4
+ /** HTTP bodies. */
5
+ export const net = new Limiter(4, 'net');
6
+ /** extractArchive, 7-Zip/NSIS unpack, npm ci/install, uv/pip, Steam, robocopy of big layouts, rustup/cargo. */
7
+ export const heavy = new Limiter(2, 'heavy');
8
+ /** Every msiexec, vcredist, DXSETUP and the Visual Studio bootstrapper: Windows runs one install at a time. */
9
+ export const installer = new Limiter(1, 'installer');
@@ -1,3 +1,4 @@
1
+ import type { Limiter } from '../concurrency/index.js';
1
2
  /**
2
3
  * A fresh `<destination>.staging-<hex>` folder next to `destination`: fill it, then moveIntoPlace. An interrupted copy
3
4
  * or extraction leaves only the staging folder (cleanup/repair remove those), never a half-filled destination.
@@ -5,6 +6,7 @@
5
6
  export declare function stagingFolder(destination: string): string;
6
7
  /** Replaces `destination` with the finished `work` folder (a folder rename: the destination is never half there). */
7
8
  export declare function moveIntoPlace(work: string, destination: string): void;
9
+ export declare function limitExtraction(limiter: Limiter | null): void;
8
10
  /**
9
11
  * Extracts .zip / .nupkg / .7z / .tar.gz with the tar.exe that ships with Windows, falling back to Expand-Archive.
10
12
  * `members` limits it to those paths inside the archive (e.g. skip pgAdmin in the PostgreSQL zip).
@@ -18,11 +18,21 @@ export function moveIntoPlace(work, destination) {
18
18
  fs.mkdirSync(path.dirname(destination), { recursive: true });
19
19
  fs.renameSync(work, destination);
20
20
  }
21
+ /** The guest's heavy pool (set by the guest at start-up; host code leaves it unset). */
22
+ let extractLimit = null;
23
+ export function limitExtraction(limiter) {
24
+ extractLimit = limiter;
25
+ }
21
26
  /**
22
27
  * Extracts .zip / .nupkg / .7z / .tar.gz with the tar.exe that ships with Windows, falling back to Expand-Archive.
23
28
  * `members` limits it to those paths inside the archive (e.g. skip pgAdmin in the PostgreSQL zip).
24
29
  */
25
- export async function extractArchive(archive, destination, members = []) {
30
+ export function extractArchive(archive, destination, members = []) {
31
+ return extractLimit
32
+ ? extractLimit.run(() => extract(archive, destination, members))
33
+ : extract(archive, destination, members);
34
+ }
35
+ async function extract(archive, destination, members) {
26
36
  fs.mkdirSync(destination, { recursive: true });
27
37
  let status = await spawnAsync(SYSTEM.tar(), ['-xf', archive, '-C', destination, ...members]);
28
38
  if (status !== 0 && /\.(zip|nupkg)$/i.test(archive)) {
@@ -0,0 +1,13 @@
1
+ /** Reads a URL as text, refusing more than `max` bytes. */
2
+ export type Fetcher = (url: string, max: number) => Promise<string>;
3
+ /** An answer whatever its status: the status, the header values asked for, and the body (at most `max` bytes). */
4
+ export interface BoundedResponse {
5
+ readonly status: number;
6
+ readonly header: (name: string) => string | null;
7
+ readonly text: string;
8
+ }
9
+ /** Reads a request with headers (or any other init) once; a status that is not 200 is an answer, not an error. */
10
+ export type RequestFetcher = (url: string, max: number, init?: RequestInit) => Promise<BoundedResponse>;
11
+ export declare function fetchBounded(url: string, max: number): Promise<string>;
12
+ /** One request with a timeout (10 s by default), no retries: for a check that should answer quickly. */
13
+ export declare function fetchBoundedResponse(url: string, max: number, init?: RequestInit, timeoutMs?: number): Promise<BoundedResponse>;
@@ -0,0 +1,40 @@
1
+ // Reading an answer from the internet with a size limit: a server cannot make this PC download without end. Node
2
+ // built-ins only (it is part of the shared download helpers).
3
+ import { fetchRetry } from './http.js';
4
+ /** The body of a response, refusing more than `max` bytes. */
5
+ async function readBounded(response, url, max) {
6
+ const host = new URL(url).host;
7
+ const length = Number(response.headers.get('content-length')) || 0;
8
+ if (length > max)
9
+ throw new Error('The answer from ' + host + ' is too big.');
10
+ const reader = response.body?.getReader();
11
+ if (!reader)
12
+ throw new Error('Empty answer from ' + host + '.');
13
+ const chunks = [];
14
+ let size = 0;
15
+ for (;;) {
16
+ const { done, value } = await reader.read();
17
+ if (done)
18
+ break;
19
+ size += value.length;
20
+ if (size > max) {
21
+ await reader.cancel();
22
+ throw new Error('The answer from ' + host + ' is too big.');
23
+ }
24
+ chunks.push(value);
25
+ }
26
+ return Buffer.concat(chunks).toString('utf8');
27
+ }
28
+ export async function fetchBounded(url, max) {
29
+ return readBounded(await fetchRetry(url, {}, 2, 30000), url, max);
30
+ }
31
+ /** One request with a timeout (10 s by default), no retries: for a check that should answer quickly. */
32
+ export async function fetchBoundedResponse(url, max, init = {}, timeoutMs = 10000) {
33
+ const response = await fetch(url, {
34
+ ...init,
35
+ signal: AbortSignal.timeout(timeoutMs),
36
+ headers: { 'User-Agent': 'windows-development-sandbox', ...(init.headers ?? {}) },
37
+ });
38
+ const text = await readBounded(response, url, max);
39
+ return { status: response.status, header: (name) => response.headers.get(name), text };
40
+ }
@@ -1,3 +1,4 @@
1
+ import type { Limiter } from '../concurrency/index.js';
1
2
  /** Expected checksums of a download; any that are given must match. */
2
3
  export interface Checksums {
3
4
  sha256?: string | null;
@@ -16,6 +17,8 @@ export interface ArchiveOptions extends Checksums {
16
17
  /** Folders above `locate`'s to keep. */
17
18
  up?: number;
18
19
  fileName?: string;
20
+ /** A copy of the archive that `file()` already downloaded (checked then): used and removed instead of downloading. */
21
+ cached?: string;
19
22
  members?: string[];
20
23
  }
21
24
  /**
@@ -26,6 +29,8 @@ export type DownloadWatcher = () => ((fraction: number) => void) | null;
26
29
  export declare class Downloader {
27
30
  /** downloads.requireChecksum: refuse anything without an expected hash. The guest sets it from session.json. */
28
31
  requireChecksum: boolean;
32
+ /** The guest's net pool: how many HTTP bodies stream at once. Host code leaves it null. */
33
+ limit: Limiter | null;
29
34
  private watcher;
30
35
  /** Reports coarse progress of the downloads that start from now on to `watcher` (null: stop). */
31
36
  watch(watcher: DownloadWatcher | null): void;
@@ -9,6 +9,8 @@ import { extractArchive, findFile, moveIntoPlace, stagingFolder } from './archiv
9
9
  export class Downloader {
10
10
  /** downloads.requireChecksum: refuse anything without an expected hash. The guest sets it from session.json. */
11
11
  requireChecksum = false;
12
+ /** The guest's net pool: how many HTTP bodies stream at once. Host code leaves it null. */
13
+ limit = null;
12
14
  watcher = null;
13
15
  /** Reports coarse progress of the downloads that start from now on to `watcher` (null: stop). */
14
16
  watch(watcher) {
@@ -28,48 +30,52 @@ export class Downloader {
28
30
  url +
29
31
  '): no checksum is published for it, and downloads.requireChecksum is on. Turn that setting off to allow downloads that cannot be verified.');
30
32
  }
31
- const response = await fetchRetry(url, {}, 3, 20 * 60 * 1000);
32
- if (!response.body)
33
- throw new Error('Empty response for ' + url);
34
33
  fs.mkdirSync(path.dirname(destination), { recursive: true });
35
34
  // Only the hashes that will be checked (sha512 of a 330 MB archive is not free).
36
35
  const checks = ['sha256', 'sha512', 'sha1'].flatMap((kind) => {
37
36
  const want = sums[kind]?.trim().toLowerCase();
38
37
  return want ? [{ want, hash: crypto.createHash(kind) }] : [];
39
38
  });
40
- // Progress for the boot screen: every 10% or 5 s at most, never per chunk; called here so it knows who is
41
- // downloading.
42
- const size = Number(response.headers.get('content-length')) || 0;
43
- const report = size > 0 ? (this.watcher?.() ?? null) : null;
44
- let received = 0;
45
- let tenth = 0;
46
- let reportedAt = Date.now();
47
- const tap = new Transform({
48
- transform(chunk, _encoding, done) {
49
- for (const c of checks)
50
- c.hash.update(chunk);
51
- if (report) {
52
- received += chunk.length;
53
- const now = Math.floor((received * 10) / size);
54
- if (now !== tenth || Date.now() - reportedAt > 5000) {
55
- tenth = now;
56
- reportedAt = Date.now();
57
- try {
58
- report(Math.min(1, received / size));
59
- }
60
- catch {
61
- /* progress never breaks a download */
62
- }
63
- }
64
- }
65
- done(null, chunk);
66
- },
67
- });
68
39
  // Written to <destination>.part and moved into place only once complete and verified, so an interrupted download
69
40
  // never leaves a partial file under the real name.
70
41
  const partial = destination + '.part';
71
- try {
42
+ // Only the transfer holds a net slot; the signature check and the rename below stay outside it.
43
+ const transfer = async () => {
44
+ const response = await fetchRetry(url, {}, 3, 20 * 60 * 1000);
45
+ if (!response.body)
46
+ throw new Error('Empty response for ' + url);
47
+ // Progress for the boot screen: every 10% or 5 s at most, never per chunk; called here so it knows who is
48
+ // downloading.
49
+ const size = Number(response.headers.get('content-length')) || 0;
50
+ const report = size > 0 ? (this.watcher?.() ?? null) : null;
51
+ let received = 0;
52
+ let tenth = 0;
53
+ let reportedAt = Date.now();
54
+ const tap = new Transform({
55
+ transform(chunk, _encoding, done) {
56
+ for (const c of checks)
57
+ c.hash.update(chunk);
58
+ if (report) {
59
+ received += chunk.length;
60
+ const now = Math.floor((received * 10) / size);
61
+ if (now !== tenth || Date.now() - reportedAt > 5000) {
62
+ tenth = now;
63
+ reportedAt = Date.now();
64
+ try {
65
+ report(Math.min(1, received / size));
66
+ }
67
+ catch {
68
+ /* progress never breaks a download */
69
+ }
70
+ }
71
+ }
72
+ done(null, chunk);
73
+ },
74
+ });
72
75
  await pipeline(Readable.fromWeb(response.body), tap, fs.createWriteStream(partial));
76
+ };
77
+ try {
78
+ await (this.limit ? this.limit.run(transfer) : transfer());
73
79
  if (checks.some((c) => c.hash.digest('hex') !== c.want))
74
80
  throw new Error('Checksum mismatch for ' + url);
75
81
  if (sums.verify)
@@ -90,12 +96,18 @@ export class Downloader {
90
96
  const work = stagingFolder(destination);
91
97
  try {
92
98
  const archive = path.join(work, options.fileName ?? path.basename(new URL(url).pathname));
93
- await this.file(url, archive, {
94
- sha256: options.sha256,
95
- sha512: options.sha512,
96
- sha1: options.sha1,
97
- ...(options.verify ? { verify: options.verify } : {}),
98
- });
99
+ if (options.cached && fs.existsSync(options.cached)) {
100
+ fs.mkdirSync(work, { recursive: true });
101
+ fs.copyFileSync(options.cached, archive);
102
+ fs.rmSync(options.cached, { force: true });
103
+ }
104
+ else
105
+ await this.file(url, archive, {
106
+ sha256: options.sha256,
107
+ sha512: options.sha512,
108
+ sha1: options.sha1,
109
+ ...(options.verify ? { verify: options.verify } : {}),
110
+ });
99
111
  const extracted = path.join(work, 'x');
100
112
  await extractArchive(archive, extracted, options.members);
101
113
  let root = extracted;
@@ -1,5 +1,6 @@
1
1
  export { fetchRetry, fetchJson, fetchText } from './http.js';
2
- export { stagingFolder, moveIntoPlace, extractArchive, findFile } from './archive.js';
2
+ export { fetchBounded, fetchBoundedResponse, type BoundedResponse, type Fetcher, type RequestFetcher, } from './bounded-fetch.js';
3
+ export { stagingFolder, moveIntoPlace, extractArchive, findFile, limitExtraction } from './archive.js';
3
4
  export { type ArchiveOptions, type Checksums, type DownloadWatcher, Downloader, downloader } from './downloader.js';
4
5
  export { spawnAsync } from './spawn.js';
5
6
  export { type ReleaseAsset, githubAsset, parseReleasePage, latestGithubTag, parseReleaseTags } from './github.js';
@@ -1,5 +1,6 @@
1
1
  export { fetchRetry, fetchJson, fetchText } from './http.js';
2
- export { stagingFolder, moveIntoPlace, extractArchive, findFile } from './archive.js';
2
+ export { fetchBounded, fetchBoundedResponse, } from './bounded-fetch.js';
3
+ export { stagingFolder, moveIntoPlace, extractArchive, findFile, limitExtraction } from './archive.js';
3
4
  export { Downloader, downloader } from './downloader.js';
4
5
  export { spawnAsync } from './spawn.js';
5
6
  export { githubAsset, parseReleasePage, latestGithubTag, parseReleaseTags } from './github.js';
@@ -1,8 +1,8 @@
1
1
  export declare const NOT_READY: {
2
2
  /** Toolchains whose step is a stub: their settings rows are hidden and "auto" never turns them on. */
3
- readonly toolchains: readonly ["unity", "godot", "android", "flutter", "dart", "reactNative", "fivem"];
3
+ readonly toolchains: readonly ["unity", "godot", "android", "flutter", "dart", "reactNative"];
4
4
  /** Service sign-ins whose host or guest side is a stub: the menu and the web do not list them. */
5
- readonly kinds: readonly ["unity", "keystore", "play", "expo", "appstore", "cfx"];
5
+ readonly kinds: readonly ["unity", "keystore", "play", "expo", "appstore"];
6
6
  readonly unreal: readonly ["engines"];
7
7
  };
8
8
  export declare const isToolchainReady: (name: string) => boolean;
@@ -2,9 +2,9 @@
2
2
  // entries here (the end of round 2 leaves it empty). Pure.
3
3
  export const NOT_READY = {
4
4
  /** Toolchains whose step is a stub: their settings rows are hidden and "auto" never turns them on. */
5
- toolchains: ['unity', 'godot', 'android', 'flutter', 'dart', 'reactNative', 'fivem'],
5
+ toolchains: ['unity', 'godot', 'android', 'flutter', 'dart', 'reactNative'],
6
6
  /** Service sign-ins whose host or guest side is a stub: the menu and the web do not list them. */
7
- kinds: ['unity', 'keystore', 'play', 'expo', 'appstore', 'cfx'],
7
+ kinds: ['unity', 'keystore', 'play', 'expo', 'appstore'],
8
8
  unreal: ['engines'],
9
9
  };
10
10
  export const isToolchainReady = (name) => !NOT_READY.toolchains.includes(name);
@@ -6,7 +6,7 @@
6
6
  <title>Sandbox</title>
7
7
  <link rel="icon" href="data:," />
8
8
  <link rel="stylesheet" href="/main-XT7T6ODM.css" />
9
- <script type="module" src="/main-OUUQYNK4.js"></script>
9
+ <script type="module" src="/main-CCILLX2M.js"></script>
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>