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
@@ -6,7 +6,7 @@ import path from 'node:path';
6
6
  import { downloader, extractArchive, fetchJson } from '../../../../shared/download/index.js';
7
7
  import { safeRemove } from '../../../../shared/io/safe-remove.js';
8
8
  import { system32 } from '../../../../shared/system.js';
9
- import { execAsync } from '../../../runtime/process.js';
9
+ import { runInstallerChecked } from '../../../runtime/pools.js';
10
10
  import { exists, mkdirp } from '../../../runtime/fs.js';
11
11
  import { log } from '../../../runtime/log.js';
12
12
  import { msiCabs, pickMsvcVersion, pickWindowsSdk } from '../../../../core/detect/versions.js';
@@ -93,9 +93,15 @@ const unpackVsix = async (vsix, work) => {
93
93
  const unpackMsi = async (msi) => {
94
94
  const logFile = msi + '.log';
95
95
  try {
96
- await execAsync(system32('msiexec.exe'), ['/a', msi, '/quiet', '/qn', 'TARGETDIR=' + ROOT, '/l*v', logFile], {
97
- stdio: 'ignore',
98
- });
96
+ await runInstallerChecked(system32('msiexec.exe'), [
97
+ '/a',
98
+ msi,
99
+ '/quiet',
100
+ '/qn',
101
+ 'TARGETDIR=' + ROOT,
102
+ '/l*v',
103
+ logFile,
104
+ ]);
99
105
  }
100
106
  catch (e) {
101
107
  const lines = exists(logFile)
@@ -1,3 +1,5 @@
1
1
  import type { Toolchains } from '../../../core/types.js';
2
2
  import { type ToolEnv } from '../tool-env.js';
3
+ /** rustup-init.exe (the small installer; the toolchain itself downloads when it runs), kept with Rust's tools. */
4
+ export declare function fetchRustupInit(): Promise<void>;
3
5
  export declare function setupRust(env: ToolEnv, config: Toolchains['rust'], refresh: boolean): Promise<void>;
@@ -1,7 +1,7 @@
1
1
  // Rust: rustup + cargo, with crates installed prebuilt where possible (binstall.ts).
2
2
  import path from 'node:path';
3
3
  import { dryRun } from '../../runtime/paths.js';
4
- import { execAsync } from '../../runtime/process.js';
4
+ import { execHeavy as execAsync } from '../../runtime/pools.js';
5
5
  import { exists } from '../../runtime/fs.js';
6
6
  import { log } from '../../runtime/log.js';
7
7
  import { arch, tool, versionOf } from '../tool-env.js';
@@ -9,6 +9,15 @@ import { downloadFile } from '../install.js';
9
9
  import { installOnce } from '../package-ledger.js';
10
10
  import { crateInstaller } from './binstall.js';
11
11
  import { installedMsvcEnv } from './msvc/index.js';
12
+ /** rustup-init.exe (the small installer; the toolchain itself downloads when it runs), kept with Rust's tools. */
13
+ export async function fetchRustupInit() {
14
+ const init = tool('rust', 'rustup-init.exe');
15
+ if (exists(init) || exists(path.join(tool('rust', 'cargo'), 'bin', 'rustup.exe')))
16
+ return;
17
+ await downloadFile('https://static.rust-lang.org/rustup/dist/' +
18
+ (arch === 'arm64' ? 'aarch64' : 'x86_64') +
19
+ '-pc-windows-msvc/rustup-init.exe', init);
20
+ }
12
21
  export async function setupRust(env, config, refresh) {
13
22
  const RUSTUP_HOME = tool('rust', 'rustup');
14
23
  const CARGO_HOME = tool('rust', 'cargo');
@@ -21,9 +30,7 @@ export async function setupRust(env, config, refresh) {
21
30
  if (!exists(rustup)) {
22
31
  const init = tool('rust', 'rustup-init.exe');
23
32
  log('Installing Rust with rustup (~300 MB once; the toolchain downloads after rustup itself) ...');
24
- await downloadFile('https://static.rust-lang.org/rustup/dist/' +
25
- (arch === 'arm64' ? 'aarch64' : 'x86_64') +
26
- '-pc-windows-msvc/rustup-init.exe', init);
33
+ await fetchRustupInit();
27
34
  await execAsync(init, [
28
35
  '-y',
29
36
  '--no-modify-path',
@@ -2,4 +2,6 @@ import type { Toolchains } from '../../../core/types.js';
2
2
  import type { ToolCtx } from '../step.js';
3
3
  import { type ToolEnv } from '../tool-env.js';
4
4
  export { UNREAL_COMPONENTS } from './vs-request.js';
5
+ /** Downloads the DirectX redistributable ahead of the install (only once the licence is accepted). */
6
+ export declare function fetchDirectX(config: Toolchains['unreal']): Promise<void>;
5
7
  export declare function setupUnreal(env: ToolEnv, config: Toolchains['unreal'], ctx: ToolCtx): Promise<void>;
@@ -4,7 +4,7 @@
4
4
  // (visual-studio.ts), which the plan runs first. The engine itself comes from the Epic Games Launcher or its source.
5
5
  import os from 'node:os';
6
6
  import path from 'node:path';
7
- import { spawnAsync } from '../../../shared/download/index.js';
7
+ import { runInstaller } from '../../runtime/pools.js';
8
8
  import { safeRemove } from '../../../shared/io/safe-remove.js';
9
9
  import { dryRun } from '../../runtime/paths.js';
10
10
  import { exists, mkdirp } from '../../runtime/fs.js';
@@ -17,17 +17,27 @@ import { installedVs } from './visual-studio.js';
17
17
  export { UNREAL_COMPONENTS } from './vs-request.js';
18
18
  const DIRECTX_URL = 'https://download.microsoft.com/download/8/4/A/84A35BF1-DAFE-4AE8-82AF-AD2AE20B6B14/directx_Jun2010_redist.exe';
19
19
  /** The June 2010 DirectX End-User Runtime (D3DX, XAudio 2.7, XInput 1.3), which Epic lists for the engine. */
20
- async function installDirectX() {
20
+ const directXInstalled = () => {
21
21
  const system32 = path.join(process.env.WINDIR ?? 'C:\\Windows', 'System32');
22
- if (exists(path.join(system32, 'd3dx9_43.dll')) && exists(path.join(system32, 'XAudio2_7.dll')))
22
+ return exists(path.join(system32, 'd3dx9_43.dll')) && exists(path.join(system32, 'XAudio2_7.dll'));
23
+ };
24
+ /** The redistributable (downloaded once, signature checked). */
25
+ const directXRedist = () => signedInstaller(DIRECTX_URL, path.join(tool('visualstudio'), 'directx_Jun2010_redist.exe'), 'the DirectX End-User Runtime (~95 MB once)', SIGNERS.microsoft);
26
+ /** Downloads the DirectX redistributable ahead of the install (only once the licence is accepted). */
27
+ export async function fetchDirectX(config) {
28
+ if (config.acceptLicense && !directXInstalled())
29
+ await directXRedist();
30
+ }
31
+ async function installDirectX() {
32
+ if (directXInstalled())
23
33
  return;
24
- const redist = await signedInstaller(DIRECTX_URL, path.join(tool('visualstudio'), 'directx_Jun2010_redist.exe'), 'the DirectX End-User Runtime (~95 MB once)', SIGNERS.microsoft);
34
+ const redist = await directXRedist();
25
35
  const unpacked = path.join(os.tmpdir(), 'directx-redist');
26
36
  safeRemove(unpacked, os.tmpdir());
27
37
  mkdirp(unpacked);
28
- if ((await spawnAsync(redist, ['/Q', '/T:' + unpacked])) !== 0 || !exists(path.join(unpacked, 'DXSETUP.exe')))
38
+ if ((await runInstaller(redist, ['/Q', '/T:' + unpacked])) !== 0 || !exists(path.join(unpacked, 'DXSETUP.exe')))
29
39
  throw new Error('could not unpack the DirectX End-User Runtime');
30
- const code = await spawnAsync(path.join(unpacked, 'DXSETUP.exe'), ['/silent']);
40
+ const code = await runInstaller(path.join(unpacked, 'DXSETUP.exe'), ['/silent']);
31
41
  safeRemove(unpacked, os.tmpdir());
32
42
  if (code !== 0)
33
43
  throw new Error('the DirectX End-User Runtime installer failed (exit ' + code + ')');
@@ -1,4 +1,6 @@
1
1
  import { type ToolEnv } from '../tool-env.js';
2
+ /** Downloads the installer ahead of `setupVcRuntime` (nothing when the runtime or the installer is there). */
3
+ export declare function fetchVcRuntime(): Promise<void>;
2
4
  /**
3
5
  * A real installer (it writes to System32), so it runs on every boot; the installer itself is kept in persistent
4
6
  * storage and only run when Microsoft's signature on it checks out. Resolves true when this call ran the installer.
@@ -1,7 +1,7 @@
1
1
  // The Visual C++ runtime (vcruntime140.dll & co.) that PHP and other native tools need.
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
- import { spawnAsync } from '../../../shared/download/index.js';
4
+ import { runInstaller } from '../../runtime/pools.js';
5
5
  import { dryRun } from '../../runtime/paths.js';
6
6
  import { exists } from '../../runtime/fs.js';
7
7
  import { log } from '../../runtime/log.js';
@@ -11,6 +11,14 @@ import { tool } from '../tool-env.js';
11
11
  const VC_REDIST = 'https://aka.ms/vs/17/release/vc_redist.x64.exe';
12
12
  /** 1638: a newer version is already installed; 3010: installed, a restart would finish it (not needed for the DLLs). */
13
13
  const OK_EXIT_CODES = [0, 1638, 3010];
14
+ /** Downloads the installer ahead of `setupVcRuntime` (nothing when the runtime or the installer is there). */
15
+ export async function fetchVcRuntime() {
16
+ const system32 = path.join(process.env.WINDIR ?? 'C:\\Windows', 'System32');
17
+ const installer = tool('vcredist', 'vc_redist.x64.exe');
18
+ if (exists(installer) || exists(path.join(system32, 'vcruntime140.dll')))
19
+ return;
20
+ await downloadFile(VC_REDIST, installer);
21
+ }
14
22
  /**
15
23
  * A real installer (it writes to System32), so it runs on every boot; the installer itself is kept in persistent
16
24
  * storage and only run when Microsoft's signature on it checks out. Resolves true when this call ran the installer.
@@ -34,7 +42,7 @@ export async function setupVcRuntime(env, summary = true) {
34
42
  throw new Error('vc_redist.x64.exe is not signed by Microsoft; deleted it (it is downloaded again next boot)');
35
43
  }
36
44
  log('Installing the Visual C++ runtime ...');
37
- const code = await spawnAsync(installer, ['/install', '/quiet', '/norestart']);
45
+ const code = await runInstaller(installer, ['/install', '/quiet', '/norestart']);
38
46
  if (!OK_EXIT_CODES.includes(code ?? -1) || !exists(path.join(system32, 'vcruntime140.dll')))
39
47
  throw new Error('the Visual C++ runtime installer failed (exit ' + code + ')');
40
48
  }
@@ -6,9 +6,12 @@ export declare function installedVs(): {
6
6
  path: string;
7
7
  version: string;
8
8
  } | null;
9
+ /** Downloads the bootstrapper now, so the install later starts without waiting for it. */
10
+ export declare function prefetchVisualStudio(req: VsRequest): Promise<void>;
9
11
  /** Visual Studio as asked for: installed at boot when it is not there yet; its folder and version. */
10
12
  export declare function ensureVisualStudio(env: ToolEnv, req: VsRequest, o: {
11
13
  keepInstallFiles: boolean;
14
+ lowPriority?: boolean;
12
15
  }): Promise<{
13
16
  path: string;
14
17
  version: string;
@@ -9,6 +9,7 @@ import os from 'node:os';
9
9
  import path from 'node:path';
10
10
  import { z } from 'zod';
11
11
  import { spawnAsync } from '../../../shared/download/index.js';
12
+ import { heavy, runInstaller } from '../../runtime/pools.js';
12
13
  import { writeJsonAtomic } from '../../../shared/io/json.js';
13
14
  import { robocopyFailed } from '../../../shared/io/robocopy-result.js';
14
15
  import { safeRemove } from '../../../shared/io/safe-remove.js';
@@ -18,11 +19,12 @@ import { exists, mkdirp } from '../../runtime/fs.js';
18
19
  import { log, warn } from '../../runtime/log.js';
19
20
  import { logsDir } from '../../runtime/paths.js';
20
21
  import { duringSession } from '../../runtime/project-context.js';
22
+ import { backgroundRunning } from '../../runtime/status.js';
21
23
  import { errMsg } from '../../../core/util/errors.js';
22
24
  import { SIGNERS, signedInstaller } from '../installers.js';
23
- import { assertLocalRoom, freeBytes, LOCAL_RESERVE_BYTES } from '../persisted.js';
25
+ import { assertLocalRoom, freeBytes } from '../persisted.js';
24
26
  import { tool } from '../tool-env.js';
25
- import { layoutNextStep, vsExitText } from './vs-request.js';
27
+ import { LAYOUT_STAGING_BYTES, layoutNextStep, STAGING_RESERVE_BYTES, vsExitText, } from './vs-request.js';
26
28
  export { BUILD_TOOLS_COMPONENTS, UNREAL_COMPONENTS, unrealUsesAutosdk, visualStudioWanted, } from './vs-request.js';
27
29
  const BOOTSTRAPPERS = {
28
30
  community: {
@@ -120,11 +122,11 @@ function reportLayoutFailure(code, temps) {
120
122
  /** Copies a finished layout onto the data disk (never /MOV: mapped folders refuse rename-over). */
121
123
  async function copyLayout(from, to) {
122
124
  mkdirp(to);
123
- const code = await spawnAsync(SYSTEM.robocopy(), [from, to, '/E', '/COPY:DAT', '/R:1', '/W:1', '/MT:8', '/NP']);
125
+ const code = await heavy.run(() => spawnAsync(SYSTEM.robocopy(), [from, to, '/E', '/COPY:DAT', '/R:1', '/W:1', '/MT:8', '/NP']));
124
126
  return !robocopyFailed(code);
125
127
  }
126
128
  /** The offline copy's installer when it holds every component; made or completed when there is room. */
127
- async function layoutInstaller(req, bootstrapper) {
129
+ async function layoutInstaller(req, bootstrapper, lowPriority) {
128
130
  const layout = layoutDir(req.product);
129
131
  const marker = path.join(layout, '.complete.json');
130
132
  const installer = path.join(layout, BOOTSTRAPPERS[req.product].file);
@@ -151,24 +153,21 @@ async function layoutInstaller(req, bootstrapper) {
151
153
  }
152
154
  log('Saving the Visual Studio install files for later boots (~10 GB, once) ...');
153
155
  fs.rmSync(marker, { force: true });
154
- // (a) The installer writes its scratch files to TEMP and moves them into the layout: a TEMP on the same (mapped)
155
- // volume, inside the layout's folder tree, keeps those moves on one volume.
156
- const scratch = mkdirp(path.join(VS_DIR, 'tmp'));
157
- const code = await spawnAsync(bootstrapper, layoutArgs(req, layout), {
158
- env: { ...process.env, TEMP: scratch, TMP: scratch },
159
- });
160
- if (OK_EXIT_CODES.includes(code) && exists(installer))
161
- return finish();
162
- reportLayoutFailure(code, [scratch, os.tmpdir()]);
163
- // (b) Build it on the sandbox's own disk instead, then copy it over.
156
+ // The installer crashes (0xE0434352) when it builds the layout in a mapped folder, so it is built on the sandbox's
157
+ // own disk and copied over. That runs before Visual Studio is installed and the copy on C: is deleted first, so it
158
+ // needs room for the layout only (STAGING_RESERVE_BYTES spare), not for the layout and the install together.
164
159
  const drive = (process.env.SystemDrive ?? 'C:') + '\\';
165
- if (layoutNextStep(code, freeBytes(drive), LOCAL_RESERVE_BYTES) !== 'stage') {
166
- warn('Visual Studio: not enough room on ' + drive + ' to build the install files there; downloading this boot');
160
+ if (layoutNextStep(null, freeBytes(drive), STAGING_RESERVE_BYTES) !== 'stage') {
161
+ warn('Visual Studio: less than ' +
162
+ Math.round((LAYOUT_STAGING_BYTES + STAGING_RESERVE_BYTES) / 1024 ** 3) +
163
+ ' GB free on ' +
164
+ drive +
165
+ ' to build the install files; downloading this boot');
167
166
  return null;
168
167
  }
169
168
  const staging = path.join(drive, 'vs-layout-staging');
170
169
  log('Building the install files on ' + drive + ' and copying them to ' + layout + ' ...');
171
- const stagedCode = await spawnAsync(bootstrapper, layoutArgs(req, staging));
170
+ const stagedCode = await runInstaller(bootstrapper, layoutArgs(req, staging), { lowPriority });
172
171
  const staged = path.join(staging, BOOTSTRAPPERS[req.product].file);
173
172
  let kept = false;
174
173
  if (OK_EXIT_CODES.includes(stagedCode) && exists(staged)) {
@@ -182,27 +181,44 @@ async function layoutInstaller(req, bootstrapper) {
182
181
  safeRemove(staging, drive);
183
182
  return kept && exists(installer) ? finish() : null;
184
183
  }
185
- async function install(req, keepInstallFiles) {
184
+ /** The bootstrapper, downloaded (net pool) and checked; kept in tools\ for later boots. */
185
+ function bootstrapperFor(req) {
186
+ const boot = BOOTSTRAPPERS[req.product];
187
+ return signedInstaller(boot.url, path.join(VS_DIR, boot.file), 'the ' + boot.name + ' installer', SIGNERS.microsoft);
188
+ }
189
+ /** Downloads the bootstrapper now, so the install later starts without waiting for it. */
190
+ export async function prefetchVisualStudio(req) {
191
+ if (!req.accepted || installedVs())
192
+ return;
193
+ await bootstrapperFor(req);
194
+ }
195
+ async function install(req, keepInstallFiles, lowPriority) {
186
196
  const boot = BOOTSTRAPPERS[req.product];
187
- const bootstrapper = await signedInstaller(boot.url, path.join(VS_DIR, boot.file), 'the ' + boot.name + ' installer', SIGNERS.microsoft);
197
+ const bootstrapper = await bootstrapperFor(req);
188
198
  let installer = null;
189
199
  if (keepInstallFiles) {
190
200
  try {
191
- installer = await layoutInstaller(req, bootstrapper);
201
+ installer = await layoutInstaller(req, bootstrapper, lowPriority);
192
202
  }
193
203
  catch (e) {
194
204
  warn('Visual Studio: the offline copy failed (' + errMsg(e) + '); downloading this boot');
195
205
  }
196
206
  }
197
- log('Installing ' + boot.name + (installer ? ' from the saved install files' : ' (downloads ~5-8 GB)') + ' ...');
198
- const code = await spawnAsync(installer ?? bootstrapper, [
207
+ const run = (from, offline) => runInstaller(from, [
199
208
  '--quiet',
200
209
  '--wait',
201
210
  '--norestart',
202
211
  '--nocache',
203
- ...(installer ? ['--noWeb'] : []),
212
+ ...(offline ? ['--noWeb'] : []),
204
213
  ...req.components.flatMap((id) => ['--add', id]),
205
- ]);
214
+ ], { lowPriority });
215
+ log('Installing ' + boot.name + (installer ? ' from the saved install files' : ' (downloads ~5-8 GB)') + ' ...');
216
+ let code = await run(installer ?? bootstrapper, installer !== null);
217
+ if (installer && (!OK_EXIT_CODES.includes(code) || !installedVs())) {
218
+ // The saved copy is on the shared data disk: if installing from it fails, this boot downloads instead.
219
+ warn('Visual Studio: installing from the saved install files failed (' + vsExitText(code) + '); downloading');
220
+ code = await run(bootstrapper, false);
221
+ }
206
222
  if (!OK_EXIT_CODES.includes(code) || !installedVs())
207
223
  throw new Error('the Visual Studio installer failed (exit ' +
208
224
  code +
@@ -226,9 +242,11 @@ export async function ensureVisualStudio(env, req, o) {
226
242
  if (have)
227
243
  return have;
228
244
  if (duringSession())
229
- throw new Error('Visual Studio is installed when the sandbox starts: restart it (sandbox restart) to get it');
245
+ throw new Error(backgroundRunning('Visual Studio')
246
+ ? 'Visual Studio is still installing in the background; it is ready in a few minutes'
247
+ : 'Visual Studio is installed when the sandbox starts: restart it (sandbox restart) to get it');
230
248
  assertLocalRoom(INSTALL_BYTES[req.product], BOOTSTRAPPERS[req.product].name);
231
- await install(req, o.keepInstallFiles);
249
+ await install(req, o.keepInstallFiles, o.lowPriority === true);
232
250
  const vs = installedVs();
233
251
  env.summary.push('visual studio: ' +
234
252
  BOOTSTRAPPERS[req.product].name +
@@ -23,6 +23,8 @@ export declare function unrealUsesAutosdk(t: Toolchains, on?: (setting: boolean
23
23
  export declare function vsExitText(code: number): string;
24
24
  /** Room the staging copy of the layout needs on the sandbox's own disk, on top of the reserve. */
25
25
  export declare const LAYOUT_STAGING_BYTES: number;
26
+ /** Spare room on the sandbox's disk while the layout is built there (before Visual Studio is installed). */
27
+ export declare const STAGING_RESERVE_BYTES: number;
26
28
  /**
27
29
  * What to do after the layout attempt in the data disk: 'done' when it worked, 'stage' (build it on the sandbox's
28
30
  * own disk and copy it over) when it failed and that disk has room, 'skip' (download this boot) otherwise.
@@ -76,6 +76,8 @@ export function vsExitText(code) {
76
76
  }
77
77
  /** Room the staging copy of the layout needs on the sandbox's own disk, on top of the reserve. */
78
78
  export const LAYOUT_STAGING_BYTES = 12 * 1024 ** 3;
79
+ /** Spare room on the sandbox's disk while the layout is built there (before Visual Studio is installed). */
80
+ export const STAGING_RESERVE_BYTES = 3 * 1024 ** 3;
79
81
  /**
80
82
  * What to do after the layout attempt in the data disk: 'done' when it worked, 'stage' (build it on the sandbox's
81
83
  * own disk and copy it over) when it failed and that disk has room, 'skip' (download this boot) otherwise.
@@ -1,4 +1,4 @@
1
- import { appsNeedMesa, appsNeedUv, setupApps } from '../apps/index.js';
1
+ import { appsNeedMesa, appsNeedUv, prefetchApps, setupApps } from '../apps/index.js';
2
2
  import { dryRun } from '../runtime/paths.js';
3
3
  import { wanted } from './detect.js';
4
4
  import { DATABASE_NAMES, setupDatabase } from '../databases/index.js';
@@ -11,17 +11,17 @@ import { setupCpp } from './native/cpp.js';
11
11
  import { setupDotnet } from './native/dotnet.js';
12
12
  import { setupMesa } from './native/mesa.js';
13
13
  import { setupMsvc } from './native/msvc/index.js';
14
- import { setupUnreal } from './native/unreal.js';
15
- import { setupRust } from './native/rust.js';
14
+ import { fetchDirectX, setupUnreal } from './native/unreal.js';
15
+ import { fetchRustupInit, setupRust } from './native/rust.js';
16
16
  import { setupNode } from './languages/node.js';
17
17
  import { setupPwsh } from './tools/pwsh.js';
18
18
  import { setupPython } from './languages/python.js';
19
19
  import { setupBun, setupDeno, setupUv } from './runtimes.js';
20
- import { setupPhp } from './languages/php.js';
20
+ import { fetchPhp, setupPhp } from './languages/php.js';
21
21
  import { setupRuby } from './languages/ruby.js';
22
- import { setupVcRuntime } from './native/vcruntime.js';
22
+ import { fetchVcRuntime, setupVcRuntime } from './native/vcruntime.js';
23
23
  import { setupVscode } from './tools/vscode.js';
24
- import { ensureVisualStudio, unrealUsesAutosdk, visualStudioWanted } from './native/visual-studio.js';
24
+ import { ensureVisualStudio, prefetchVisualStudio, unrealUsesAutosdk, visualStudioWanted, } from './native/visual-studio.js';
25
25
  import { duringSession } from '../runtime/project-context.js';
26
26
  import { setupSteam } from './steam/steam.js';
27
27
  import { setupGodot } from './engines/godot.js';
@@ -49,6 +49,9 @@ export function plan(config, versions, o) {
49
49
  pcInstalls: o.pcInstalls ?? { items: [] },
50
50
  duringSession: duringSession(),
51
51
  };
52
+ // Downloads that run ahead of their steps (the scheduler starts them at once); a dry run downloads nothing.
53
+ const ahead = (work) => (dryRun ? {} : { fetch: work });
54
+ const deferred = o.deferHeavy === true && !dryRun;
52
55
  return [
53
56
  {
54
57
  name: 'Git',
@@ -71,8 +74,10 @@ export function plan(config, versions, o) {
71
74
  name: 'Visual Studio',
72
75
  enabled: vs !== null,
73
76
  shared: true,
77
+ background: deferred,
78
+ ...ahead(() => prefetchVisualStudio(vs)),
74
79
  install: async (env) => {
75
- await ensureVisualStudio(env, vs, { keepInstallFiles: config.unreal.keepInstallFiles });
80
+ await ensureVisualStudio(env, vs, { keepInstallFiles: config.unreal.keepInstallFiles, lowPriority: deferred });
76
81
  },
77
82
  },
78
83
  {
@@ -80,6 +85,7 @@ export function plan(config, versions, o) {
80
85
  enabled: unreal,
81
86
  shared: true,
82
87
  after: ['Visual Studio', 'MSVC'],
88
+ ...ahead(() => fetchDirectX(config.unreal)),
83
89
  install: (env) => setupUnreal(env, config.unreal, ctx),
84
90
  },
85
91
  {
@@ -94,7 +100,9 @@ export function plan(config, versions, o) {
94
100
  {
95
101
  name: 'Rust',
96
102
  enabled: wanted(config.rust.enabled, 'rust'),
97
- after: ['C/C++', 'MSVC', 'Visual Studio', 'Unreal'],
103
+ // Only the msvc host links with Visual Studio (and Unreal's tools): the GNU host does not wait for them.
104
+ after: rustHost(config).endsWith('-msvc') ? ['C/C++', 'MSVC', 'Visual Studio', 'Unreal'] : ['C/C++'],
105
+ ...ahead(fetchRustupInit),
98
106
  install: async (env) => {
99
107
  await setupRust(env, { ...config.rust, host: rustHost(config) }, o.refresh);
100
108
  const rust = resolveTargetDir(config.rust);
@@ -164,6 +172,7 @@ export function plan(config, versions, o) {
164
172
  name: 'Visual C++ runtime',
165
173
  enabled: config.vcRuntime.enabled || apps,
166
174
  shared: true,
175
+ ...ahead(fetchVcRuntime),
167
176
  install: async (env) => {
168
177
  await setupVcRuntime(env);
169
178
  },
@@ -172,6 +181,7 @@ export function plan(config, versions, o) {
172
181
  name: 'PHP',
173
182
  enabled: wanted(config.php.enabled, 'php'),
174
183
  after: ['Visual C++ runtime'],
184
+ ...ahead(() => fetchPhp(config.php)),
175
185
  install: (env) => setupPhp(env, config.php),
176
186
  },
177
187
  { name: 'Deno', enabled: wanted(config.deno.enabled, 'deno'), install: (env) => setupDeno(env) },
@@ -208,6 +218,7 @@ export function plan(config, versions, o) {
208
218
  enabled: apps,
209
219
  shared: true,
210
220
  after: ['Mesa 3D', 'Visual C++ runtime', 'uv'],
221
+ ...ahead(() => prefetchApps(o.apps, { gitAccess: o.gitAccess, refresh: o.refresh })),
211
222
  install: (env) => setupApps(env, o.apps, { gitAccess: o.gitAccess, refresh: o.refresh }),
212
223
  },
213
224
  {
@@ -1,22 +1,3 @@
1
- import { type ToolEnv } from './tool-env.js';
2
- import type { ToolchainStep } from './step.js';
3
- export interface RunOptions {
4
- /** Progress lines ("Installing Go, Rust (3 of 9 done)"); default: the sandbox's boot status. */
5
- report?: (message: string) => void;
6
- /** What machine-wide (shared) steps contributed at boot: reused instead of run again. */
7
- reuse?: Record<string, ToolEnv>;
8
- }
9
- export declare class StepScheduler {
10
- private readonly parallel;
11
- /** One lock per kind of step ("Node 20" and "Node 22" share nvm's folders): held while that step runs, in any run. */
12
- private readonly locks;
13
- /** The step a download belongs to (several run at once), for its progress. */
14
- private readonly currentStep;
15
- constructor(parallel?: number);
16
- /** Runs the steps (each with its own environment) and returns each step's environment, in plan order. */
17
- run(steps: readonly ToolchainStep[], o: RunOptions): Promise<Map<string, ToolEnv>>;
18
- /** Runs `work` once no other step of the same kind (first word of the name) is running. */
19
- private exclusive;
20
- }
21
- /** The one scheduler: its locks span the boot's run and every project's run. */
1
+ import { StepScheduler } from './step-scheduler.js';
2
+ export { StepScheduler, type RunOptions, type SchedulerSink } from './step-scheduler.js';
22
3
  export declare const scheduler: StepScheduler;
@@ -1,123 +1,9 @@
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';
1
+ // The one scheduler: its locks span the boot's run and every project's run.
5
2
  import { warn } from '../runtime/log.js';
6
3
  import { setSandboxStatus } from '../runtime/status.js';
7
- import { errMsg } from '../../core/util/errors.js';
8
- import { emptyEnv } from './tool-env.js';
9
- /** How many installers run at once: enough to overlap downloads with installs, few enough not to thrash the disk. */
10
- const PARALLEL = 4;
11
- /** Runs one installer; a failure is reported and skipped so it never blocks the other toolchains. */
12
- async function runReported(name, env, work) {
13
- try {
14
- await work();
15
- }
16
- catch (e) {
17
- warn(name +
18
- ' could not be set up and is skipped; everything else still works, and the next start tries again. Reason: ' +
19
- errMsg(e));
20
- env.summary.push(name + ': FAILED');
21
- }
22
- }
23
- export class StepScheduler {
24
- parallel;
25
- /** One lock per kind of step ("Node 20" and "Node 22" share nvm's folders): held while that step runs, in any run. */
26
- locks = new Map();
27
- /** The step a download belongs to (several run at once), for its progress. */
28
- currentStep = new AsyncLocalStorage();
29
- constructor(parallel = PARALLEL) {
30
- this.parallel = parallel;
31
- }
32
- /** Runs the steps (each with its own environment) and returns each step's environment, in plan order. */
33
- async run(steps, o) {
34
- const envs = new Map(steps.map((s) => [s.name, emptyEnv()]));
35
- const finished = new Map();
36
- const running = new Set();
37
- const waiting = [];
38
- let done = 0;
39
- // The boot (no `report`) also publishes structured progress: what runs now, how far its download is, how many are
40
- // done.
41
- const percent = new Map();
42
- const active = () => [...running].map((name) => ({ name, ...(percent.has(name) ? { percent: percent.get(name) } : {}) }));
43
- const say = o.report ??
44
- ((message) => setSandboxStatus('provisioning', message, { active: active(), done, total: steps.length }));
45
- const report = () => {
46
- if (running.size)
47
- say('Installing ' + [...running].join(', ') + ' (' + done + ' of ' + steps.length + ' done)');
48
- };
49
- if (!o.report)
50
- downloader.watch(() => {
51
- const name = this.currentStep.getStore();
52
- return name
53
- ? (fraction) => {
54
- percent.set(name, Math.round(fraction * 100));
55
- report();
56
- }
57
- : null;
58
- });
59
- for (const s of steps) {
60
- // Dependencies first (they are earlier in the plan, so their promises exist), then a free slot.
61
- finished.set(s.name, (async () => {
62
- await Promise.all((s.after ?? []).map((name) => finished.get(name)));
63
- const env = envs.get(s.name);
64
- const reused = s.shared ? o.reuse?.[s.name] : undefined;
65
- if (reused) {
66
- envs.set(s.name, structuredClone(reused));
67
- done++;
68
- return;
69
- }
70
- while (running.size >= this.parallel)
71
- await new Promise((r) => waiting.push(r));
72
- running.add(s.name);
73
- report();
74
- try {
75
- await this.currentStep.run(s.name, () => this.exclusive(s.name, () => runReported(s.name, env, async () => s.install(env))));
76
- }
77
- finally {
78
- running.delete(s.name);
79
- percent.delete(s.name);
80
- done++;
81
- report();
82
- waiting.shift()?.();
83
- }
84
- })());
85
- }
86
- try {
87
- await Promise.all(finished.values());
88
- }
89
- finally {
90
- if (!o.report)
91
- downloader.watch(null);
92
- }
93
- if (!o.report)
94
- setSandboxStatus('provisioning', 'Set up ' + steps.length + ' toolchains and databases', {
95
- active: [],
96
- done,
97
- total: steps.length,
98
- });
99
- return envs;
100
- }
101
- /** Runs `work` once no other step of the same kind (first word of the name) is running. */
102
- async exclusive(name, work) {
103
- const key = name.split(' ')[0].toLowerCase();
104
- const previous = this.locks.get(key) ?? Promise.resolve();
105
- let release = () => undefined;
106
- const mine = new Promise((r) => {
107
- release = r;
108
- });
109
- const chained = previous.then(() => mine);
110
- this.locks.set(key, chained);
111
- await previous;
112
- try {
113
- return await work();
114
- }
115
- finally {
116
- release();
117
- if (this.locks.get(key) === chained)
118
- this.locks.delete(key);
119
- }
120
- }
121
- }
122
- /** The one scheduler: its locks span the boot's run and every project's run. */
123
- export const scheduler = new StepScheduler();
4
+ import { StepScheduler } from './step-scheduler.js';
5
+ export { StepScheduler } from './step-scheduler.js';
6
+ export const scheduler = new StepScheduler({
7
+ warn,
8
+ status: (message, progress) => setSandboxStatus('provisioning', message, progress),
9
+ });
@@ -5,6 +5,19 @@ import { type RunOptions } from './scheduler.js';
5
5
  /** The combined environment, plus what each step contributed (kept so later projects reuse the shared steps). */
6
6
  export interface ToolchainResult extends ToolEnv {
7
7
  steps: Record<string, ToolEnv>;
8
+ /** Every enabled step of the plan (foreground and background), in plan order: the order the parts are merged in. */
9
+ names: string[];
8
10
  }
9
- /** Provisions every enabled toolchain and database, and returns the combined environment for the agent. */
10
- export declare function setupToolchains(config: Toolchains, versions: ToolVersions, options: ToolchainOptions, run?: RunOptions): Promise<ToolchainResult>;
11
+ export interface SetupExtras {
12
+ /** The background steps start once this resolves (the boot: the agent's first session is running). */
13
+ gate?: Promise<void>;
14
+ }
15
+ /** The parts of these steps merged in the order of `names` (and the config's own variables on top). */
16
+ export declare function mergeToolchains(config: Toolchains, names: readonly string[], parts: ReadonlyMap<string, ToolEnv> | Readonly<Record<string, ToolEnv>>, planNames?: readonly string[]): ToolchainResult;
17
+ /**
18
+ * Provisions every enabled toolchain and database, and returns the combined environment for the agent. `later` is
19
+ * set when steps were left for the background: it resolves with the result of those steps alone.
20
+ */
21
+ export declare function setupToolchains(config: Toolchains, versions: ToolVersions, options: ToolchainOptions, run?: RunOptions, extras?: SetupExtras): Promise<ToolchainResult & {
22
+ later: Promise<ToolchainResult> | null;
23
+ }>;