windows-development-sandbox 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (368) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +22 -6
  3. package/config.example.json +17 -0
  4. package/dist/api.js +2 -1
  5. package/dist/cli/args.d.ts +20 -0
  6. package/dist/cli/args.js +35 -3
  7. package/dist/cli/colors.d.ts +24 -0
  8. package/dist/cli/colors.js +42 -0
  9. package/dist/cli/commands/agent-commands.d.ts +6 -0
  10. package/dist/cli/commands/agent-commands.js +61 -0
  11. package/dist/cli/commands/context.d.ts +2 -0
  12. package/dist/cli/commands/context.js +3 -0
  13. package/dist/cli/commands/data/clean.d.ts +15 -0
  14. package/dist/cli/commands/data/clean.js +101 -0
  15. package/dist/cli/commands/data/collect.d.ts +11 -0
  16. package/dist/cli/commands/data/collect.js +54 -0
  17. package/dist/cli/commands/data/index.d.ts +4 -0
  18. package/dist/cli/commands/data/index.js +45 -0
  19. package/dist/cli/commands/data/remove.d.ts +2 -0
  20. package/dist/cli/commands/data/remove.js +30 -0
  21. package/dist/cli/commands/index.d.ts +5 -3
  22. package/dist/cli/commands/index.js +5 -3
  23. package/dist/cli/commands/launch.d.ts +26 -2
  24. package/dist/cli/commands/launch.js +220 -39
  25. package/dist/cli/commands/maintenance.js +33 -4
  26. package/dist/cli/commands/project.d.ts +29 -0
  27. package/dist/cli/commands/project.js +63 -0
  28. package/dist/cli/commands/projects.d.ts +14 -0
  29. package/dist/cli/commands/projects.js +146 -0
  30. package/dist/cli/commands/repair/checks.d.ts +2 -0
  31. package/dist/cli/commands/{repair.js → repair/checks.js} +19 -181
  32. package/dist/cli/commands/{repair.d.ts → repair/finders.d.ts} +0 -29
  33. package/dist/cli/commands/repair/finders.js +108 -0
  34. package/dist/cli/commands/repair/index.d.ts +12 -0
  35. package/dist/cli/commands/repair/index.js +61 -0
  36. package/dist/cli/commands/repair/types.d.ts +26 -0
  37. package/dist/cli/commands/repair/types.js +3 -0
  38. package/dist/cli/commands/restart.d.ts +6 -0
  39. package/dist/cli/commands/restart.js +85 -0
  40. package/dist/cli/commands/sessions.d.ts +2 -1
  41. package/dist/cli/commands/sessions.js +189 -45
  42. package/dist/cli/commands/setup/checks.d.ts +13 -0
  43. package/dist/cli/commands/setup/checks.js +71 -0
  44. package/dist/cli/commands/{setup.d.ts → setup/facts.d.ts} +1 -15
  45. package/dist/cli/commands/setup/facts.js +60 -0
  46. package/dist/cli/commands/setup/index.d.ts +2 -0
  47. package/dist/cli/commands/setup/index.js +43 -0
  48. package/dist/cli/commands/user-config.d.ts +8 -0
  49. package/dist/cli/commands/user-config.js +41 -0
  50. package/dist/cli/history.d.ts +11 -0
  51. package/dist/cli/history.js +21 -0
  52. package/dist/cli/index.d.ts +7 -1
  53. package/dist/cli/index.js +38 -13
  54. package/dist/cli/manifest.d.ts +8 -0
  55. package/dist/cli/manifest.js +35 -15
  56. package/dist/cli/menu-multi.d.ts +12 -0
  57. package/dist/cli/menu-multi.js +82 -0
  58. package/dist/cli/menu.d.ts +5 -13
  59. package/dist/cli/menu.js +149 -46
  60. package/dist/cli/progress.d.ts +9 -0
  61. package/dist/cli/progress.js +49 -0
  62. package/dist/cli/settings/edit.d.ts +13 -0
  63. package/dist/cli/settings/edit.js +19 -0
  64. package/dist/cli/settings/index.d.ts +8 -0
  65. package/dist/cli/settings/index.js +131 -0
  66. package/dist/cli/settings/prompts.d.ts +7 -0
  67. package/dist/cli/settings/prompts.js +48 -0
  68. package/dist/cli/settings/schema.d.ts +33 -0
  69. package/dist/cli/settings/schema.js +29 -0
  70. package/dist/cli/validate.d.ts +17 -0
  71. package/dist/cli/validate.js +89 -0
  72. package/dist/core/config.js +4 -1
  73. package/dist/core/disk.d.ts +16 -0
  74. package/dist/core/disk.js +55 -0
  75. package/dist/core/local-config.d.ts +8 -0
  76. package/dist/core/local-config.js +18 -5
  77. package/dist/core/project-policy.d.ts +15 -0
  78. package/dist/core/project-policy.js +35 -0
  79. package/dist/core/prompt.d.ts +4 -0
  80. package/dist/core/prompt.js +15 -0
  81. package/dist/core/safe-paths.d.ts +4 -0
  82. package/dist/core/safe-paths.js +37 -0
  83. package/dist/core/schema/common.d.ts +17 -0
  84. package/dist/core/schema/common.js +15 -0
  85. package/dist/core/schema/config.d.ts +594 -0
  86. package/dist/core/schema/config.js +63 -0
  87. package/dist/core/schema/databases.d.ts +33 -0
  88. package/dist/core/schema/databases.js +35 -0
  89. package/dist/core/schema/harness.d.ts +12 -0
  90. package/dist/core/schema/harness.js +17 -0
  91. package/dist/core/schema/json-schema.d.ts +5 -0
  92. package/dist/core/schema/json-schema.js +25 -0
  93. package/dist/core/schema/profile.d.ts +27 -0
  94. package/dist/core/schema/profile.js +29 -0
  95. package/dist/core/schema/projects.d.ts +9 -0
  96. package/dist/core/schema/projects.js +11 -0
  97. package/dist/core/schema/sandbox.d.ts +50 -0
  98. package/dist/core/schema/sandbox.js +36 -0
  99. package/dist/core/schema/toolchains.d.ts +93 -0
  100. package/dist/core/schema/toolchains.js +83 -0
  101. package/dist/core/schema.d.ts +6 -604
  102. package/dist/core/schema.js +8 -264
  103. package/dist/core/types.d.ts +29 -1
  104. package/dist/core/util.d.ts +7 -0
  105. package/dist/core/util.js +12 -3
  106. package/dist/core/workspace.d.ts +0 -5
  107. package/dist/core/workspace.js +12 -46
  108. package/dist/guest/boot-env.d.ts +4 -0
  109. package/dist/guest/boot-env.js +25 -0
  110. package/dist/guest/cmdline.d.ts +12 -0
  111. package/dist/guest/cmdline.js +23 -0
  112. package/dist/guest/daemon/exec.d.ts +5 -0
  113. package/dist/guest/daemon/exec.js +32 -0
  114. package/dist/guest/daemon/project.d.ts +14 -0
  115. package/dist/guest/daemon/project.js +132 -0
  116. package/dist/guest/daemon/session.d.ts +13 -0
  117. package/dist/guest/daemon/session.js +139 -0
  118. package/dist/guest/daemon/shutdown.d.ts +1 -0
  119. package/dist/guest/daemon/shutdown.js +21 -0
  120. package/dist/guest/daemon/state.d.ts +17 -0
  121. package/dist/guest/daemon/state.js +31 -0
  122. package/dist/guest/daemon.js +8 -141
  123. package/dist/guest/db.js +18 -3
  124. package/dist/guest/env.d.ts +2 -0
  125. package/dist/guest/env.js +4 -2
  126. package/dist/guest/guide.d.ts +6 -2
  127. package/dist/guest/guide.js +5 -4
  128. package/dist/guest/helpers.d.ts +3 -0
  129. package/dist/guest/helpers.js +26 -0
  130. package/dist/guest/main.js +17 -7
  131. package/dist/guest/ports.d.ts +6 -0
  132. package/dist/guest/ports.js +22 -0
  133. package/dist/guest/project-context.d.ts +7 -0
  134. package/dist/guest/project-context.js +10 -0
  135. package/dist/guest/project-env.d.ts +20 -0
  136. package/dist/guest/project-env.js +20 -0
  137. package/dist/guest/project-status.d.ts +5 -0
  138. package/dist/guest/project-status.js +17 -0
  139. package/dist/guest/ptyhost.d.ts +0 -18
  140. package/dist/guest/ptyhost.js +5 -63
  141. package/dist/guest/setup/agents.d.ts +5 -2
  142. package/dist/guest/setup/agents.js +22 -8
  143. package/dist/guest/setup/claude.js +11 -3
  144. package/dist/guest/setup/environment.d.ts +13 -3
  145. package/dist/guest/setup/environment.js +14 -10
  146. package/dist/guest/setup/hooks.d.ts +9 -1
  147. package/dist/guest/setup/hooks.js +28 -14
  148. package/dist/guest/toolchains/catalogue.d.ts +4 -0
  149. package/dist/guest/toolchains/catalogue.js +2 -2
  150. package/dist/guest/toolchains/cli.js +25 -17
  151. package/dist/guest/toolchains/common.d.ts +18 -1
  152. package/dist/guest/toolchains/common.js +34 -11
  153. package/dist/guest/toolchains/databases/dynamodb.d.ts +8 -0
  154. package/dist/guest/toolchains/databases/dynamodb.js +52 -0
  155. package/dist/guest/toolchains/{databases.d.ts → databases/index.d.ts} +2 -3
  156. package/dist/guest/toolchains/databases/index.js +62 -0
  157. package/dist/guest/toolchains/databases/mongodb.d.ts +3 -0
  158. package/dist/guest/toolchains/databases/mongodb.js +31 -0
  159. package/dist/guest/toolchains/databases/mysql.d.ts +3 -0
  160. package/dist/guest/toolchains/databases/mysql.js +38 -0
  161. package/dist/guest/toolchains/databases/postgres.d.ts +5 -0
  162. package/dist/guest/toolchains/databases/postgres.js +38 -0
  163. package/dist/guest/toolchains/databases/redis.d.ts +3 -0
  164. package/dist/guest/toolchains/databases/redis.js +16 -0
  165. package/dist/guest/toolchains/databases/server.d.ts +14 -0
  166. package/dist/guest/toolchains/databases/server.js +52 -0
  167. package/dist/guest/toolchains/git.js +25 -16
  168. package/dist/guest/toolchains/go.js +1 -1
  169. package/dist/guest/toolchains/index.d.ts +13 -9
  170. package/dist/guest/toolchains/index.js +40 -82
  171. package/dist/guest/toolchains/java.js +5 -5
  172. package/dist/guest/toolchains/leftovers.d.ts +2 -0
  173. package/dist/guest/toolchains/leftovers.js +26 -0
  174. package/dist/guest/toolchains/managers.js +12 -10
  175. package/dist/guest/toolchains/{binstall.js → native/binstall.js} +6 -9
  176. package/dist/guest/toolchains/native/cpp.d.ts +4 -0
  177. package/dist/guest/toolchains/native/cpp.js +38 -0
  178. package/dist/guest/toolchains/native/dotnet.d.ts +4 -0
  179. package/dist/guest/toolchains/native/dotnet.js +103 -0
  180. package/dist/guest/toolchains/native/mesa.d.ts +7 -0
  181. package/dist/guest/toolchains/native/mesa.js +46 -0
  182. package/dist/guest/toolchains/native/msvc/environment.d.ts +16 -0
  183. package/dist/guest/toolchains/native/msvc/environment.js +57 -0
  184. package/dist/guest/toolchains/native/msvc/index.d.ts +6 -0
  185. package/dist/guest/toolchains/native/msvc/index.js +48 -0
  186. package/dist/guest/toolchains/native/msvc/packages.d.ts +6 -0
  187. package/dist/guest/toolchains/native/msvc/packages.js +156 -0
  188. package/dist/guest/toolchains/native/msvc/tree.d.ts +15 -0
  189. package/dist/guest/toolchains/native/msvc/tree.js +21 -0
  190. package/dist/guest/toolchains/native/rust.d.ts +3 -0
  191. package/dist/guest/toolchains/native/rust.js +45 -0
  192. package/dist/guest/toolchains/node.js +26 -6
  193. package/dist/guest/toolchains/plan.d.ts +22 -0
  194. package/dist/guest/toolchains/plan.js +96 -0
  195. package/dist/guest/toolchains/pwsh.js +1 -1
  196. package/dist/guest/toolchains/python.js +17 -10
  197. package/dist/guest/toolchains/runtimes.d.ts +2 -0
  198. package/dist/guest/toolchains/runtimes.js +6 -8
  199. package/dist/guest/toolchains/scripting/php.d.ts +3 -0
  200. package/dist/guest/toolchains/scripting/php.js +71 -0
  201. package/dist/guest/toolchains/scripting/ruby.d.ts +3 -0
  202. package/dist/guest/toolchains/scripting/ruby.js +47 -0
  203. package/dist/guest/toolchains/scripting/vcruntime.d.ts +7 -0
  204. package/dist/guest/toolchains/scripting/vcruntime.js +45 -0
  205. package/dist/guest/view.js +21 -1
  206. package/dist/host/accounts.js +14 -5
  207. package/dist/host/agent-commands.d.ts +25 -0
  208. package/dist/host/agent-commands.js +145 -0
  209. package/dist/host/attach.d.ts +17 -0
  210. package/dist/host/attach.js +69 -0
  211. package/dist/host/channel.d.ts +23 -22
  212. package/dist/host/channel.js +66 -84
  213. package/dist/host/claude-profile.js +7 -3
  214. package/dist/host/cleanup.js +2 -11
  215. package/dist/host/database-plan.d.ts +12 -0
  216. package/dist/host/database-plan.js +52 -0
  217. package/dist/host/generate.d.ts +4 -12
  218. package/dist/host/generate.js +48 -135
  219. package/dist/host/join.d.ts +28 -0
  220. package/dist/host/join.js +76 -0
  221. package/dist/host/launch/mappings.d.ts +3 -0
  222. package/dist/host/launch/mappings.js +50 -0
  223. package/dist/host/launch/projects.d.ts +15 -0
  224. package/dist/host/launch/projects.js +56 -0
  225. package/dist/host/launch/stage.d.ts +11 -0
  226. package/dist/host/launch/stage.js +66 -0
  227. package/dist/host/launch/wsb.d.ts +10 -0
  228. package/dist/host/launch/wsb.js +34 -0
  229. package/dist/host/pc-scan.d.ts +6 -0
  230. package/dist/host/pc-scan.js +37 -0
  231. package/dist/host/platform.d.ts +2 -0
  232. package/dist/host/platform.js +13 -1
  233. package/dist/host/project-scan.d.ts +1 -17
  234. package/dist/host/project-scan.js +2 -86
  235. package/dist/host/projects.d.ts +100 -0
  236. package/dist/host/projects.js +147 -0
  237. package/dist/host/repo-guard.d.ts +15 -2
  238. package/dist/host/repo-guard.js +103 -12
  239. package/dist/host/terminal.js +3 -0
  240. package/dist/host/test-guard.d.ts +6 -0
  241. package/dist/host/test-guard.js +15 -0
  242. package/dist/host/window.js +7 -3
  243. package/dist/host/wsb.d.ts +37 -0
  244. package/dist/host/wsb.js +101 -0
  245. package/dist/runtime/daemon.js +10997 -2794
  246. package/dist/runtime/db.js +6428 -464
  247. package/dist/runtime/main.js +7359 -1135
  248. package/dist/runtime/view.js +5922 -47
  249. package/dist/selftest/accounts.test.d.ts +2 -0
  250. package/dist/selftest/accounts.test.js +44 -0
  251. package/dist/selftest/agent-commands.test.d.ts +2 -0
  252. package/dist/selftest/agent-commands.test.js +47 -0
  253. package/dist/selftest/channel.test.d.ts +2 -0
  254. package/dist/selftest/channel.test.js +37 -0
  255. package/dist/selftest/cli.test.d.ts +2 -2
  256. package/dist/selftest/cli.test.js +115 -27
  257. package/dist/selftest/cmdline.test.d.ts +2 -0
  258. package/dist/selftest/cmdline.test.js +45 -0
  259. package/dist/selftest/config.test.d.ts +2 -3
  260. package/dist/selftest/config.test.js +36 -72
  261. package/dist/selftest/data.test.d.ts +2 -2
  262. package/dist/selftest/data.test.js +77 -70
  263. package/dist/selftest/detect.test.d.ts +2 -2
  264. package/dist/selftest/detect.test.js +54 -38
  265. package/dist/selftest/fixtures.d.ts +44 -4
  266. package/dist/selftest/fixtures.js +40 -23
  267. package/dist/selftest/generate.test.d.ts +2 -0
  268. package/dist/selftest/generate.test.js +63 -0
  269. package/dist/selftest/git-access.test.d.ts +2 -0
  270. package/dist/selftest/git-access.test.js +26 -0
  271. package/dist/selftest/guest-boot.test.d.ts +2 -0
  272. package/dist/selftest/guest-boot.test.js +76 -0
  273. package/dist/selftest/harness.d.ts +44 -7
  274. package/dist/selftest/harness.js +62 -21
  275. package/dist/selftest/host-sim.test.d.ts +2 -0
  276. package/dist/selftest/host-sim.test.js +93 -0
  277. package/dist/selftest/hostile-input.test.d.ts +2 -0
  278. package/dist/selftest/hostile-input.test.js +54 -0
  279. package/dist/selftest/index.d.ts +8 -1
  280. package/dist/selftest/index.js +134 -35
  281. package/dist/selftest/links.test.d.ts +2 -0
  282. package/dist/selftest/links.test.js +50 -0
  283. package/dist/selftest/live-daemon.test.d.ts +2 -0
  284. package/dist/selftest/live-daemon.test.js +91 -0
  285. package/dist/selftest/live-git.test.d.ts +2 -0
  286. package/dist/selftest/live-git.test.js +60 -0
  287. package/dist/selftest/live-projects.test.d.ts +2 -0
  288. package/dist/selftest/live-projects.test.js +127 -0
  289. package/dist/selftest/live.d.ts +65 -0
  290. package/dist/selftest/live.js +109 -0
  291. package/dist/selftest/local-config.test.d.ts +2 -0
  292. package/dist/selftest/local-config.test.js +107 -0
  293. package/dist/selftest/mirror.test.d.ts +2 -3
  294. package/dist/selftest/mirror.test.js +92 -65
  295. package/dist/selftest/network.test.d.ts +2 -0
  296. package/dist/selftest/network.test.js +28 -0
  297. package/dist/selftest/personalization.test.d.ts +2 -0
  298. package/dist/selftest/personalization.test.js +53 -0
  299. package/dist/selftest/projects.test.d.ts +2 -0
  300. package/dist/selftest/projects.test.js +254 -0
  301. package/dist/selftest/repair.test.d.ts +2 -0
  302. package/dist/selftest/repair.test.js +42 -0
  303. package/dist/selftest/repo-guard.test.d.ts +2 -0
  304. package/dist/selftest/repo-guard.test.js +63 -0
  305. package/dist/selftest/secrets.test.d.ts +2 -0
  306. package/dist/selftest/secrets.test.js +71 -0
  307. package/dist/selftest/setup.test.d.ts +2 -2
  308. package/dist/selftest/setup.test.js +45 -22
  309. package/dist/selftest/suite.d.ts +13 -0
  310. package/dist/selftest/suite.js +1 -0
  311. package/dist/selftest/sync.test.d.ts +2 -3
  312. package/dist/selftest/sync.test.js +90 -78
  313. package/dist/selftest/test-guard.test.d.ts +2 -0
  314. package/dist/selftest/test-guard.test.js +49 -0
  315. package/dist/selftest/toolchains.test.d.ts +2 -0
  316. package/dist/selftest/toolchains.test.js +103 -0
  317. package/dist/selftest/workspace.test.d.ts +2 -0
  318. package/dist/selftest/workspace.test.js +53 -0
  319. package/dist/shared/download.d.ts +3 -1
  320. package/dist/shared/download.js +19 -4
  321. package/dist/shared/paths.d.ts +5 -0
  322. package/dist/shared/paths.js +6 -0
  323. package/dist/shared/projects.d.ts +53 -0
  324. package/dist/shared/projects.js +67 -0
  325. package/dist/shared/rundir.d.ts +10 -1
  326. package/dist/shared/rundir.js +12 -4
  327. package/dist/shared/safe-remove.js +11 -0
  328. package/dist/shared/schema.d.ts +443 -0
  329. package/dist/shared/schema.js +41 -2
  330. package/dist/shared/test-guard.d.ts +4 -0
  331. package/dist/shared/test-guard.js +37 -0
  332. package/dist/sync/claude-mem.js +3 -1
  333. package/dist/sync/history.d.ts +23 -12
  334. package/dist/sync/history.js +52 -20
  335. package/dist/sync/watcher.d.ts +8 -0
  336. package/dist/sync/watcher.js +23 -4
  337. package/package.json +6 -6
  338. package/sandbox/bootstrap.ps1 +258 -258
  339. package/sandbox/cpucap.ps1 +51 -0
  340. package/sandbox/logon.cmd +18 -16
  341. package/sandbox/openssl.cs +42 -0
  342. package/sandbox/ptyhost.cs +15 -4
  343. package/sandbox/trust.cmd +35 -35
  344. package/schemas/config.schema.json +100 -1
  345. package/schemas/sandbox-config.schema.json +17 -1
  346. package/dist/cli/commands/data.d.ts +0 -46
  347. package/dist/cli/commands/data.js +0 -249
  348. package/dist/cli/commands/setup.js +0 -152
  349. package/dist/cli/settings.d.ts +0 -11
  350. package/dist/cli/settings.js +0 -156
  351. package/dist/guest/toolchains/databases.js +0 -226
  352. package/dist/guest/toolchains/msvc.d.ts +0 -10
  353. package/dist/guest/toolchains/msvc.js +0 -216
  354. package/dist/guest/toolchains/native.d.ts +0 -12
  355. package/dist/guest/toolchains/native.js +0 -220
  356. package/dist/guest/toolchains/scripting.d.ts +0 -10
  357. package/dist/guest/toolchains/scripting.js +0 -137
  358. package/dist/selftest/guest.test.d.ts +0 -3
  359. package/dist/selftest/guest.test.js +0 -61
  360. package/dist/selftest/helpers.test.d.ts +0 -2
  361. package/dist/selftest/helpers.test.js +0 -125
  362. package/dist/selftest/host.test.d.ts +0 -2
  363. package/dist/selftest/host.test.js +0 -71
  364. package/dist/selftest/live.test.d.ts +0 -7
  365. package/dist/selftest/live.test.js +0 -127
  366. package/dist/selftest/security.test.d.ts +0 -3
  367. package/dist/selftest/security.test.js +0 -143
  368. /package/dist/guest/toolchains/{binstall.d.ts → native/binstall.d.ts} +0 -0
@@ -1,216 +0,0 @@
1
- // MSVC (cl, link) and the Windows SDK without an installer, the way mmozeiko's portable-msvc.py does it: packages
2
- // from Microsoft's Visual Studio channel manifest (each payload checked against the manifest's sha256), the compiler
3
- // unpacked from its .vsix, the SDK from its .msi files with `msiexec /a`. Laid out like a Build Tools install, with
4
- // vcvars64.bat and a vswhere that points at it, so CMake, cargo (msvc host), node-gyp and vcvars-based scripts work.
5
- // Use is governed by the Visual Studio licence terms: it only installs with toolchains.msvc.acceptLicense.
6
- import fs from 'node:fs';
7
- import path from 'node:path';
8
- import { download, extractArchive, fetchJson } from '../../shared/download.js';
9
- import { safeRemove } from '../../shared/safe-remove.js';
10
- import { system32 } from '../../shared/system.js';
11
- import { dryRun, execAsync, exists, log, mkdirp } from '../env.js';
12
- import { ensureCompiled } from '../ptyhost.js';
13
- import { tool } from './common.js';
14
- import { msiCabs, pickMsvcVersion, pickWindowsSdk } from './versions.js';
15
- const CHANNEL = 'https://aka.ms/vs/17/release/channel';
16
- const ROOT = tool('msvc');
17
- const MANIFEST_DAY_MS = 24 * 60 * 60 * 1000;
18
- /** Where build tools look for vswhere (local to the sandbox, so it is written each boot). */
19
- const VSWHERE_DIR = path.join(process.env['ProgramFiles(x86)'] ?? 'C:\\Program Files (x86)', 'Microsoft Visual Studio', 'Installer');
20
- /** The VS manifest (~18 MB), cached for a day. */
21
- const manifest = async () => {
22
- const cached = path.join(ROOT, '.manifest.json');
23
- if (exists(cached) && Date.now() - fs.statSync(cached).mtimeMs < MANIFEST_DAY_MS)
24
- return JSON.parse(fs.readFileSync(cached, 'utf8'));
25
- const channel = await fetchJson(CHANNEL);
26
- const item = channel.channelItems.find((i) => i.id === 'Microsoft.VisualStudio.Manifests.VisualStudio')?.payloads?.[0];
27
- if (!item)
28
- throw new Error('the Visual Studio channel has no package manifest');
29
- mkdirp(ROOT);
30
- // The channel's sha256 for the manifest is not the file's hash (portable-msvc.py does not check it either); the
31
- // manifest comes over HTTPS from Microsoft, and every package payload it lists IS checked against its own sha256.
32
- await download(item.url, cached);
33
- return JSON.parse(fs.readFileSync(cached, 'utf8'));
34
- };
35
- /** The compiler's packages (host x64, target x64): tools, resources, CRT headers/libs, ASAN/PGO headers, DIA SDK. */
36
- const MSVC_PARTS = ['tools.hostx64.targetx64.base', 'tools.hostx64.targetx64.res.base', 'crt.headers.base', 'crt.x64.desktop.base', 'crt.x64.store.base', 'crt.source.base', 'asan.headers.base', 'pgo.headers.base'];
37
- /** The SDK's .msi files (Desktop + UCRT + Store headers/libs/tools for x64), as in portable-msvc.py. */
38
- const SDK_MSIS = [
39
- 'Windows SDK for Windows Store Apps Tools-x86_en-us.msi', 'Windows SDK for Windows Store Apps Headers-x86_en-us.msi',
40
- 'Windows SDK for Windows Store Apps Headers OnecoreUap-x86_en-us.msi', 'Windows SDK for Windows Store Apps Libs-x86_en-us.msi',
41
- 'Universal CRT Headers Libraries and Sources-x86_en-us.msi', 'Windows SDK Desktop Headers x86-x86_en-us.msi',
42
- 'Windows SDK Desktop Headers x64-x86_en-us.msi', 'Windows SDK Desktop Libs x64-x86_en-us.msi', 'Windows SDK Desktop Tools x64-x86_en-us.msi',
43
- ];
44
- /** .vsix contents are percent-encoded file names under Contents/; unpack them into the root, names decoded. */
45
- const unpackVsix = async (vsix, work) => {
46
- const out = mkdirp(path.join(work, 'vsix-' + path.basename(vsix)));
47
- await extractArchive(vsix, out, ['Contents']);
48
- const decode = (dir) => {
49
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
50
- const from = path.join(dir, entry.name);
51
- let name = entry.name;
52
- try {
53
- if (name.includes('%'))
54
- name = decodeURIComponent(name);
55
- }
56
- catch { /* not percent-encoded after all */ }
57
- const to = path.join(dir, name);
58
- if (to !== from)
59
- fs.renameSync(from, to);
60
- if (entry.isDirectory())
61
- decode(to);
62
- }
63
- };
64
- decode(out);
65
- fs.cpSync(path.join(out, 'Contents'), ROOT, { recursive: true, force: true });
66
- safeRemove(out, work);
67
- };
68
- const install = async (config) => {
69
- const m = await manifest();
70
- const byId = new Map();
71
- for (const p of m.packages)
72
- byId.set(p.id.toLowerCase(), [...(byId.get(p.id.toLowerCase()) ?? []), p]);
73
- const pick = (id) => byId.get(id.toLowerCase())?.find((p) => !p.language || p.language === 'en-US');
74
- const ids = [...byId.keys()];
75
- const version = pickMsvcVersion(ids, config.msvcVersion);
76
- if (!version)
77
- throw new Error('no MSVC ' + config.msvcVersion + ' in the Visual Studio manifest');
78
- const sdk = pickWindowsSdk(ids, config.sdkVersion);
79
- if (!sdk)
80
- throw new Error('no Windows SDK ' + config.sdkVersion + ' in the Visual Studio manifest');
81
- const work = mkdirp(path.join(ROOT, '.download'));
82
- const fetchPayload = async (p) => {
83
- const file = path.join(work, path.basename(p.fileName.replace(/\\/g, '/')));
84
- if (!exists(file))
85
- await download(p.url, file, p.sha256);
86
- return file;
87
- };
88
- log('Downloading MSVC ' + version + ' (compiler, CRT) ...');
89
- for (const part of MSVC_PARTS) {
90
- const p = pick('microsoft.vc.' + version + '.' + part);
91
- if (!p) {
92
- if (part.startsWith('tools') || part.startsWith('crt.headers') || part.startsWith('crt.x64.desktop'))
93
- throw new Error('MSVC ' + version + ' has no ' + part);
94
- continue;
95
- }
96
- for (const payload of p.payloads ?? [])
97
- await unpackVsix(await fetchPayload(payload), work);
98
- }
99
- const dia = pick('microsoft.visualcpp.dia.sdk');
100
- for (const payload of dia?.payloads ?? [])
101
- await unpackVsix(await fetchPayload(payload), work);
102
- // The SDK component depends on the package that holds the .msi/.cab payloads (e.g. Win11SDK_10.0.26100).
103
- const sdkPackage = pick(Object.keys(pick(sdk.componentId)?.dependencies ?? {})[0] ?? '');
104
- if (!sdkPackage?.payloads)
105
- throw new Error('the Windows SDK ' + sdk.build + ' package has no payloads');
106
- log('Downloading the Windows SDK ' + sdk.build + ' (headers, libraries) ...');
107
- const payload = (name) => sdkPackage.payloads.find((p) => p.fileName.toLowerCase() === ('installers\\' + name).toLowerCase());
108
- const msis = [];
109
- for (const name of SDK_MSIS) {
110
- const p = payload(name);
111
- if (!p)
112
- throw new Error('the Windows SDK package has no ' + name);
113
- const file = await fetchPayload(p);
114
- msis.push(file);
115
- for (const cab of msiCabs(fs.readFileSync(file))) {
116
- const c = payload(cab);
117
- if (c)
118
- await fetchPayload(c);
119
- }
120
- }
121
- for (const msi of msis) {
122
- // An administrative install only unpacks files; its verbose log explains a failure (e.g. a path over 260 characters).
123
- const logFile = msi + '.log';
124
- try {
125
- await execAsync(system32('msiexec.exe'), ['/a', msi, '/quiet', '/qn', 'TARGETDIR=' + ROOT, '/l*v', logFile], { stdio: 'ignore' });
126
- }
127
- catch (e) {
128
- const lines = exists(logFile) ? fs.readFileSync(logFile, 'utf16le').split(/\r?\n/).filter((l) => /error|return value 3|failed/i.test(l)).slice(-4).join(' | ') : '';
129
- throw new Error(path.basename(msi) + ' did not unpack: ' + (e instanceof Error ? e.message : String(e)) + (lines ? ' (' + lines.slice(0, 600) + ')' : ''));
130
- }
131
- }
132
- // msiexec /a also copies every .msi into the target; they are not needed.
133
- for (const name of fs.readdirSync(ROOT).filter((n) => n.toLowerCase().endsWith('.msi')))
134
- fs.rmSync(path.join(ROOT, name), { force: true });
135
- safeRemove(work, ROOT);
136
- const sdkVersion = fs.readdirSync(path.join(ROOT, 'Windows Kits', '10', 'Include')).filter((d) => d.startsWith('10.0.' + sdk.build)).sort().pop();
137
- if (!sdkVersion)
138
- throw new Error('the Windows SDK did not unpack (no Include\\10.0.' + sdk.build + '.*)');
139
- // The tools folder is named after the toolset (14.44.35207), not the package version (14.44.17.14).
140
- const toolsRoot = path.join(ROOT, 'VC', 'Tools', 'MSVC');
141
- const tools = fs.readdirSync(toolsRoot).filter((d) => exists(path.join(toolsRoot, d, 'bin', 'Hostx64', 'x64', 'cl.exe'))).sort((a, b) => a.localeCompare(b, undefined, { numeric: true })).pop();
142
- if (!tools)
143
- throw new Error('MSVC ' + version + ' did not unpack (no cl.exe under VC\\Tools\\MSVC)');
144
- return { msvc: tools, sdk: sdkVersion };
145
- };
146
- /** Variables vcvars64.bat would set for this tree (host x64, target x64). */
147
- export const vcvars = (root, msvc, sdk) => {
148
- const vc = path.join(root, 'VC', 'Tools', 'MSVC', msvc);
149
- const kits = path.join(root, 'Windows Kits', '10');
150
- const inc = (...p) => path.join(kits, 'Include', sdk, ...p);
151
- const lib = (...p) => path.join(kits, 'Lib', sdk, ...p);
152
- return {
153
- vars: {
154
- VSINSTALLDIR: root + '\\', VCINSTALLDIR: path.join(root, 'VC') + '\\', VCToolsInstallDir: vc + '\\', VCToolsVersion: msvc, VisualStudioVersion: '17.0',
155
- WindowsSdkDir: kits + '\\', WindowsSDKVersion: sdk + '\\', WindowsSDKLibVersion: sdk + '\\', UniversalCRTSdkDir: kits + '\\', UCRTVersion: sdk,
156
- VSCMD_ARG_HOST_ARCH: 'x64', VSCMD_ARG_TGT_ARCH: 'x64', Platform: 'x64',
157
- INCLUDE: [path.join(vc, 'include'), inc('ucrt'), inc('shared'), inc('um'), inc('winrt'), inc('cppwinrt')].join(';'),
158
- LIB: [path.join(vc, 'lib', 'x64'), lib('ucrt', 'x64'), lib('um', 'x64')].join(';'),
159
- LIBPATH: [path.join(vc, 'lib', 'x64'), path.join(vc, 'lib', 'x86', 'store', 'references')].join(';'),
160
- },
161
- path: [path.join(vc, 'bin', 'Hostx64', 'x64'), path.join(kits, 'bin', sdk, 'x64')],
162
- };
163
- };
164
- /** vcvars64.bat / vcvarsall.bat in the usual place, for scripts that `call` them. */
165
- const writeVcvars = (env) => {
166
- const dir = mkdirp(path.join(ROOT, 'VC', 'Auxiliary', 'Build'));
167
- const lines = ['@echo off', 'rem Written by Windows Development Sandbox for its portable MSVC (host x64, target x64).',
168
- ...Object.entries(env.vars).map(([k, v]) => 'set "' + k + '=' + v + '"'), 'set "PATH=' + env.path.join(';') + ';%PATH%"', 'exit /b 0'];
169
- fs.writeFileSync(path.join(dir, 'vcvars64.bat'), lines.join('\r\n') + '\r\n');
170
- fs.writeFileSync(path.join(dir, 'vcvarsall.bat'), ['@echo off', 'rem Only x64 (amd64) is installed.', 'call "%~dp0vcvars64.bat"', 'exit /b %errorlevel%'].join('\r\n') + '\r\n');
171
- };
172
- /** vswhere.exe where Visual Studio's installer would put it, answering with this tree. */
173
- const installVswhere = (msvc) => {
174
- mkdirp(VSWHERE_DIR);
175
- fs.copyFileSync(ensureCompiled('vswhere'), path.join(VSWHERE_DIR, 'vswhere.exe'));
176
- const [major, minor] = msvc.split('.').slice(2);
177
- fs.writeFileSync(path.join(VSWHERE_DIR, 'vswhere.json'), JSON.stringify({ installationPath: ROOT, installationVersion: '17.' + (minor ?? '0') + '.0.' + (major ?? '0') }));
178
- };
179
- /** The vcvars environment of an installed tree (for steps that run before the agent's environment exists). */
180
- export const installedMsvcEnv = () => {
181
- try {
182
- const done = JSON.parse(fs.readFileSync(path.join(ROOT, '.installed.json'), 'utf8'));
183
- return exists(path.join(ROOT, 'VC', 'Tools', 'MSVC', done.msvc, 'bin', 'Hostx64', 'x64', 'cl.exe')) ? vcvars(ROOT, done.msvc, done.sdk) : null;
184
- }
185
- catch {
186
- return null;
187
- }
188
- };
189
- export const setupMsvc = async (env, config) => {
190
- if (!config.acceptLicense) {
191
- throw new Error('MSVC is installed only with toolchains.msvc.acceptLicense = true: its use is governed by the Visual Studio licence terms (https://visualstudio.microsoft.com/license-terms/), which need a Visual Studio licence');
192
- }
193
- if (dryRun) {
194
- log('(dry run) would provision MSVC ' + config.msvcVersion + ' and Windows SDK ' + config.sdkVersion);
195
- return;
196
- }
197
- const marker = path.join(ROOT, '.installed.json');
198
- const want = config.msvcVersion + '/' + config.sdkVersion;
199
- let done = exists(marker) ? JSON.parse(fs.readFileSync(marker, 'utf8')) : null;
200
- if (!done || done.want !== want || !exists(path.join(ROOT, 'VC', 'Tools', 'MSVC', done.msvc, 'bin', 'Hostx64', 'x64', 'cl.exe'))) {
201
- log('Installing MSVC and the Windows SDK (~1-2 GB, once; licence accepted in toolchains.msvc.acceptLicense) ...');
202
- // A previous attempt's verified downloads (.download) are kept; everything it unpacked is redone.
203
- if (exists(ROOT))
204
- for (const name of fs.readdirSync(ROOT).filter((n) => n !== '.manifest.json' && n !== '.download'))
205
- safeRemove(path.join(ROOT, name), ROOT);
206
- const installed = await install(config);
207
- done = { ...installed, want };
208
- fs.writeFileSync(marker, JSON.stringify(done, null, 2));
209
- }
210
- const vc = vcvars(ROOT, done.msvc, done.sdk);
211
- writeVcvars(vc);
212
- installVswhere(done.msvc);
213
- Object.assign(env.vars, vc.vars);
214
- env.prepend.push(...vc.path);
215
- env.summary.push('msvc: ' + done.msvc + ', Windows SDK ' + done.sdk + ' (cl, link; vcvars64.bat and vswhere work)');
216
- };
@@ -1,12 +0,0 @@
1
- import type { Toolchains } from '../../core/types.js';
2
- import { type ToolEnv } from './common.js';
3
- /** MinGW-w64 GCC from WinLibs: gcc/g++/mingw32-make for the agent, and the linker for Rust's GNU host. */
4
- export declare const setupCpp: (env: ToolEnv, config: Toolchains["cpp"]) => Promise<void>;
5
- /**
6
- * Mesa 3D for Windows (llvmpipe): OpenGL 4.5 on the CPU when the sandbox has no GPU. Windows only offers OpenGL 1.1 in
7
- * software, and opengl32.dll cannot be replaced system-wide, so programs pick Mesa up from their own folder.
8
- */
9
- export declare const setupMesa: (env: ToolEnv, config: Toolchains["mesa"]) => Promise<void>;
10
- export declare const setupRust: (env: ToolEnv, config: Toolchains["rust"], refresh: boolean) => Promise<void>;
11
- /** .NET SDK channels from config plus whatever the workspace's global.json pins. Installs are remembered in a marker. */
12
- export declare const setupDotnet: (env: ToolEnv, config: Toolchains["dotnet"], refresh: boolean) => Promise<void>;
@@ -1,220 +0,0 @@
1
- // Compiled languages: C/C++ (MinGW-w64 GCC, flex/bison), Rust (rustup + cargo), C# (.NET SDK and .NET Framework
2
- // targeting), and Mesa for running OpenGL programs without a GPU.
3
- import fs from 'node:fs';
4
- import path from 'node:path';
5
- import { z } from 'zod';
6
- import { download, fetchJson, githubAsset, installArchive } from '../../shared/download.js';
7
- import { readJsonFile, writeJsonAtomic } from '../../shared/rundir.js';
8
- import { SYSTEM } from '../../shared/system.js';
9
- import { dryRun, errMsg, execAsync, execOutput, exists, log, mkdirp, warn, workspace } from '../env.js';
10
- import { arch, installOnce, tool, versionOf } from './common.js';
11
- import { CMAKE_FLOOR, cmakeTooOld } from './versions.js';
12
- import { crateInstaller } from './binstall.js';
13
- import { installedMsvcEnv } from './msvc.js';
14
- /** MinGW-w64 GCC from WinLibs: gcc/g++/mingw32-make for the agent, and the linker for Rust's GNU host. */
15
- export const setupCpp = async (env, config) => {
16
- const bin = tool('mingw', 'mingw64', 'bin');
17
- const gcc = path.join(bin, 'gcc.exe');
18
- if (dryRun) {
19
- log('(dry run) would provision MinGW-w64 GCC');
20
- return;
21
- }
22
- if (!exists(gcc)) {
23
- const asset = await githubAsset('brechtsanders/winlibs_mingw', arch === 'arm64' ? /^winlibs-aarch64-.*\.zip$/ : /^winlibs-x86_64-posix-seh-gcc-.*\.zip$/, true);
24
- log('Downloading ' + asset.name + ' (C/C++ compiler, ~270 MB once) ...');
25
- await installArchive(asset.url, tool('mingw'), { sha256: asset.sha256, fileName: asset.name });
26
- }
27
- if (!exists(gcc))
28
- throw new Error('gcc.exe missing after installing MinGW');
29
- // Appended (not prepended) so MinGW's tools never shadow Windows or toolchain commands of the same name.
30
- env.append.unshift(bin);
31
- env.vars.CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER = gcc;
32
- const cmake = versionOf(path.join(bin, 'cmake.exe'));
33
- if (cmakeTooOld(cmake))
34
- warn('The bundled CMake ' + cmake + ' is older than ' + CMAKE_FLOOR + ', which many projects need; set toolchains.cpp.enabled to refresh it, or install a newer CMake into C:\\AgentData\\tools\\bin.');
35
- env.summary.push('gcc: ' + versionOf(gcc) + ' (MinGW-w64, with cmake ' + cmake + ', ninja, premake5)');
36
- if (config.flexBison)
37
- await setupFlexBison(env);
38
- };
39
- /** win_flex / win_bison (CMake's FindFLEX/FindBISON look for these names). Kept with the C/C++ tools on PATH. */
40
- const setupFlexBison = async (env) => {
41
- const dir = tool('winflexbison');
42
- if (!exists(path.join(dir, 'win_bison.exe'))) {
43
- const asset = await githubAsset('lexxmark/winflexbison', /^win_flex_bison-[\d.]+\.zip$/);
44
- log('Downloading ' + asset.name + ' ...');
45
- await installArchive(asset.url, dir, { sha256: asset.sha256, locate: 'win_bison.exe', fileName: asset.name });
46
- }
47
- env.append.push(dir);
48
- env.summary.push('win_flex/win_bison: ' + versionOf(path.join(dir, 'win_bison.exe')));
49
- };
50
- /**
51
- * Mesa 3D for Windows (llvmpipe): OpenGL 4.5 on the CPU when the sandbox has no GPU. Windows only offers OpenGL 1.1 in
52
- * software, and opengl32.dll cannot be replaced system-wide, so programs pick Mesa up from their own folder.
53
- */
54
- export const setupMesa = async (env, config) => {
55
- if (dryRun) {
56
- log('(dry run) would provision Mesa 3D');
57
- return;
58
- }
59
- const root = tool('mesa');
60
- const dir = path.join(root, arch === 'arm64' ? 'arm64' : 'x64');
61
- if (!exists(path.join(dir, 'opengl32.dll'))) {
62
- const asset = await githubAsset('pal1000/mesa-dist-win', /^mesa3d-[\d.]+-release-msvc\.7z$/);
63
- log('Downloading ' + asset.name + ' (software OpenGL) ...');
64
- await installArchive(asset.url, root, { sha256: asset.sha256, locate: 'opengl32.dll', up: 1, fileName: asset.name });
65
- }
66
- Object.assign(env.vars, { MESA_DIR: dir, GALLIUM_DRIVER: 'llvmpipe' });
67
- env.summary.push('mesa (software OpenGL 4.5): copy opengl32.dll + libgallium_wgl.dll from %MESA_DIR% next to a game\'s .exe');
68
- if (config.vulkan)
69
- await setupLavapipe(env, dir);
70
- };
71
- const VULKAN_RUNTIME = 'https://sdk.lunarg.com/sdk/download/latest/windows/vulkan-runtime-components.zip';
72
- /**
73
- * Software Vulkan: Mesa's lavapipe plus the Khronos loader (vulkan-1.dll, from LunarG's runtime components; Windows
74
- * Sandbox has no Vulkan loader). The loader ignores VK_DRIVER_FILES in elevated processes, and everything in the
75
- * sandbox runs elevated, so lavapipe is registered where the loader looks (HKLM; the registry is new every boot).
76
- */
77
- const setupLavapipe = async (env, mesaDir) => {
78
- const icd = path.join(mesaDir, arch === 'arm64' ? 'lvp_icd.aarch64.json' : 'lvp_icd.x86_64.json');
79
- if (!exists(icd))
80
- throw new Error('this Mesa build has no lavapipe (' + path.basename(icd) + ')');
81
- const dir = tool('vulkan');
82
- if (!exists(path.join(dir, 'vulkan-1.dll'))) {
83
- log('Downloading the Vulkan loader (LunarG runtime components) ...');
84
- // LunarG publishes no checksum for this zip; it comes over HTTPS from LunarG, the Khronos SDK's maintainer.
85
- // The zip has x86 and x64 folders: only the 64-bit loader (and vulkaninfo) is extracted.
86
- await installArchive(VULKAN_RUNTIME, dir, { locate: 'vulkan-1.dll', fileName: 'vulkan-runtime.zip', members: ['*/x64/*'] });
87
- }
88
- await execAsync(SYSTEM.reg(), ['add', 'HKLM\\SOFTWARE\\Khronos\\Vulkan\\Drivers', '/v', icd, '/t', 'REG_DWORD', '/d', '0', '/f'], { stdio: 'ignore' });
89
- env.prepend.push(dir);
90
- env.summary.push('vulkan: lavapipe (software Vulkan 1.4 on the CPU), vulkaninfo on PATH; WGPU_BACKEND=vulkan for wgpu');
91
- };
92
- export const setupRust = async (env, config, refresh) => {
93
- const RUSTUP_HOME = tool('rust', 'rustup');
94
- const CARGO_HOME = tool('rust', 'cargo');
95
- const rustEnv = { env: { ...process.env, RUSTUP_HOME, CARGO_HOME } };
96
- const rustup = path.join(CARGO_HOME, 'bin', 'rustup.exe');
97
- if (dryRun) {
98
- log('(dry run) would provision Rust ' + config.toolchain + ' (' + config.host + ')');
99
- return;
100
- }
101
- if (!exists(rustup)) {
102
- const init = tool('rust', 'rustup-init.exe');
103
- log('Downloading rustup ...');
104
- await download('https://static.rust-lang.org/rustup/dist/' + (arch === 'arm64' ? 'aarch64' : 'x86_64') + '-pc-windows-msvc/rustup-init.exe', init);
105
- await execAsync(init, ['-y', '--no-modify-path', '--profile', 'default', '--default-host', config.host, '--default-toolchain', config.toolchain], rustEnv);
106
- }
107
- else {
108
- // The host can change (GNU <-> msvc with toolchains.msvc): install the toolchain for it; a no-op when it is there.
109
- await execAsync(rustup, ['set', 'default-host', config.host], { ...rustEnv, stdio: 'ignore' });
110
- await execAsync(rustup, ['toolchain', 'install', config.toolchain, '--profile', 'default', '--no-self-update'], { ...rustEnv, stdio: 'ignore' });
111
- await execAsync(rustup, ['default', config.toolchain], { ...rustEnv, stdio: 'ignore' });
112
- if (refresh)
113
- await execAsync(rustup, ['update'], rustEnv);
114
- }
115
- Object.assign(env.vars, { RUSTUP_HOME, CARGO_HOME });
116
- env.prepend.push(path.join(CARGO_HOME, 'bin'));
117
- env.summary.push('rust: ' + versionOf(path.join(CARGO_HOME, 'bin', 'rustc.exe'), ['--version'], rustEnv) + ' [' + config.host + ']');
118
- const toolchainKey = 'rustup@' + config.toolchain;
119
- await installOnce(env, toolchainKey + ' components', config.components, (c) => execAsync(rustup, ['component', 'add', c], rustEnv));
120
- await installOnce(env, toolchainKey + ' targets', config.targets, (t) => execAsync(rustup, ['target', 'add', t], rustEnv));
121
- // cargo install links with MSVC's link.exe (msvc host) or MinGW's gcc (GNU host). Those steps ran first, but their
122
- // PATH only reaches the agent at the end, so it is put together here.
123
- const msvc = config.host.endsWith('-msvc') ? installedMsvcEnv() : null;
124
- const mingw = tool('mingw', 'mingw64', 'bin');
125
- const cargoEnv = msvc
126
- ? { env: { ...rustEnv.env, ...msvc.vars, PATH: [path.join(CARGO_HOME, 'bin'), ...msvc.path, process.env.PATH ?? ''].join(path.delimiter) } }
127
- : { env: { ...rustEnv.env, CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER: path.join(mingw, 'gcc.exe'), PATH: [path.join(CARGO_HOME, 'bin'), mingw, process.env.PATH ?? ''].join(path.delimiter) } };
128
- // Prebuilt through cargo-binstall where the crate publishes a Windows build, else compiled (same "cargo" keys as before).
129
- await installOnce(env, 'cargo', config.crates, crateInstaller(CARGO_HOME, cargoEnv));
130
- };
131
- /** tools\dotnet\.agent-sandbox-installed.json: which install keys (channel:8.0, global.json:...) already ran. */
132
- const DotnetMarker = z.record(z.string(), z.boolean());
133
- /** .NET SDK channels from config plus whatever the workspace's global.json pins. Installs are remembered in a marker. */
134
- export const setupDotnet = async (env, config, refresh) => {
135
- const root = tool('dotnet');
136
- const marker = path.join(root, '.agent-sandbox-installed.json');
137
- const script = tool('dotnet-install.ps1');
138
- if (dryRun) {
139
- log('(dry run) would provision .NET SDK channels ' + config.channels.join(', '));
140
- return;
141
- }
142
- // A corrupt or hand-edited marker just means "nothing recorded yet" (the installs are idempotent).
143
- const recorded = DotnetMarker.safeParse(exists(marker) && !refresh ? readJsonFile(marker) : {});
144
- const done = recorded.success ? recorded.data : {};
145
- const globalJson = path.join(workspace, 'global.json');
146
- const installs = [
147
- ...config.channels.map((c) => ['channel:' + c, ['-Channel', c]]),
148
- ...(exists(globalJson) ? [['global.json:' + fs.readFileSync(globalJson, 'utf8').replace(/\s+/g, ''), ['-JSonFile', globalJson]]] : []),
149
- ];
150
- for (const [key, args] of installs.filter(([k]) => !done[k])) {
151
- if (!exists(script) || refresh)
152
- await download('https://dot.net/v1/dotnet-install.ps1', script);
153
- log('Installing .NET SDK (' + key + ') ...');
154
- await execAsync(SYSTEM.powershell(), ['-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', script, ...args, '-InstallDir', root, '-NoPath']);
155
- done[key] = true;
156
- writeJsonAtomic(marker, done);
157
- }
158
- const home = mkdirp(tool('dotnet-home'));
159
- Object.assign(env.vars, { DOTNET_ROOT: root, DOTNET_CLI_HOME: home, DOTNET_CLI_TELEMETRY_OPTOUT: '1', DOTNET_NOLOGO: '1', DOTNET_SKIP_FIRST_TIME_EXPERIENCE: '1', NUGET_PACKAGES: tool('nuget', 'packages') });
160
- env.prepend.push(root, path.join(home, '.dotnet', 'tools'));
161
- env.summary.push('dotnet: ' + execOutput(path.join(root, 'dotnet.exe'), ['--list-sdks']).split(/\r?\n/).map((l) => l.split(' ')[0]).filter(Boolean).join(', '));
162
- const dotnet = path.join(root, 'dotnet.exe');
163
- const dotnetEnv = { env: { ...process.env, ...env.vars, PATH: root + path.delimiter + (process.env.PATH ?? '') } };
164
- await installOnce(env, 'dotnet workload', config.workloads, (w) => execAsync(dotnet, ['workload', 'install', w, '--skip-manifest-update'], dotnetEnv));
165
- await installOnce(env, 'dotnet tool', config.tools, (t) => execAsync(dotnet, ['tool', 'install', '--global', t], dotnetEnv));
166
- await setupFrameworkReferences(env, config.frameworkTargets);
167
- if (config.netfx35)
168
- await setupNetFx35(env);
169
- };
170
- /**
171
- * .NET Framework reference assemblies (the "targeting packs" Visual Studio would install) from NuGet, so net4x projects,
172
- * old-style .csproj included, build with the SDK's MSBuild. MSBuild reads TargetFrameworkRootPath from the environment.
173
- * Running .NET Framework 4.8 apps needs nothing: Windows ships it.
174
- */
175
- const setupFrameworkReferences = async (env, targets) => {
176
- if (!targets.length)
177
- return;
178
- const root = tool('netfx-references');
179
- for (const target of targets) {
180
- const id = 'microsoft.netframework.referenceassemblies.' + target.toLowerCase();
181
- const marker = path.join(root, '.installed-' + target.toLowerCase());
182
- if (exists(marker))
183
- continue;
184
- try {
185
- const { versions } = await fetchJson('https://api.nuget.org/v3-flatcontainer/' + id + '/index.json');
186
- const version = versions.filter((v) => /^\d+(\.\d+)*$/.test(v)).pop();
187
- if (!version)
188
- throw new Error('no stable version of ' + id);
189
- log('Downloading .NET Framework reference assemblies (' + target + ') ...');
190
- const staging = tool('netfx-references-' + target.toLowerCase());
191
- await installArchive('https://api.nuget.org/v3-flatcontainer/' + id + '/' + version + '/' + id + '.' + version + '.nupkg', staging, { fileName: id + '.zip' });
192
- fs.cpSync(path.join(staging, 'build', '.NETFramework'), path.join(root, '.NETFramework'), { recursive: true });
193
- fs.rmSync(staging, { recursive: true, force: true });
194
- fs.writeFileSync(marker, version);
195
- }
196
- catch (e) {
197
- warn('.NET Framework reference assemblies ' + target + ' failed: ' + errMsg(e));
198
- }
199
- }
200
- if (!exists(path.join(root, '.NETFramework')))
201
- return;
202
- env.vars.TargetFrameworkRootPath = root + path.sep;
203
- env.summary.push('.NET Framework targeting: ' + fs.readdirSync(path.join(root, '.NETFramework')).join(', '));
204
- };
205
- /** .NET Framework 3.5 is a Windows feature the sandbox image lacks; DISM fetches it from Windows Update on every boot. */
206
- const setupNetFx35 = async (env) => {
207
- if (execOutput(SYSTEM.reg(), ['query', 'HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.5', '/v', 'Install']).includes('0x1')) {
208
- env.summary.push('.NET Framework 3.5: on');
209
- return;
210
- }
211
- log('Enabling .NET Framework 3.5 (downloads from Windows Update, a few minutes) ...');
212
- try {
213
- await execAsync(SYSTEM.dism(), ['/Online', '/Enable-Feature', '/FeatureName:NetFx3', '/All', '/NoRestart', '/Quiet'], { stdio: 'ignore' });
214
- env.summary.push('.NET Framework 3.5: on');
215
- }
216
- catch (e) {
217
- warn('.NET Framework 3.5 could not be enabled: ' + errMsg(e));
218
- env.summary.push('.NET Framework 3.5: FAILED');
219
- }
220
- };
@@ -1,10 +0,0 @@
1
- import type { Toolchains } from '../../core/types.js';
2
- import { type ToolEnv } from './common.js';
3
- /**
4
- * vcruntime140.dll & co. A real installer (it writes to System32), so it runs on every boot; the installer itself is
5
- * kept in persistent storage and only run when Microsoft's signature on it checks out. Resolves true when this call
6
- * ran the installer. Only the "Visual C++ runtime" step reports it (`summary`); PHP calls it again just to be sure.
7
- */
8
- export declare const setupVcRuntime: (env: ToolEnv, summary?: boolean) => Promise<boolean>;
9
- export declare const setupRuby: (env: ToolEnv, config: Toolchains["ruby"]) => Promise<void>;
10
- export declare const setupPhp: (env: ToolEnv, config: Toolchains["php"]) => Promise<void>;
@@ -1,137 +0,0 @@
1
- // Ruby (RubyInstaller) and PHP (windows.php.net, with Composer), plus the Visual C++ runtime PHP and other native
2
- // tools need. Gems and Composer's global packages live in persistent storage.
3
- import fs from 'node:fs';
4
- import path from 'node:path';
5
- import { download, fetchJson, fetchText, installArchive, spawnAsync } from '../../shared/download.js';
6
- import { SYSTEM } from '../../shared/system.js';
7
- import { dryRun, execAsync, execOutput, exists, log, mkdirp } from '../env.js';
8
- import { installOnce, resolveCached, tool, versionFromFiles, versionOf } from './common.js';
9
- import { pickPackageVersion } from './versions.js';
10
- /** Writes `<name>.cmd` into tools\shims (on PATH). */
11
- const shim = (env, name, command) => {
12
- fs.writeFileSync(path.join(mkdirp(tool('shims')), name + '.cmd'), '@echo off\r\n' + command + ' %*\r\n');
13
- env.prepend.push(tool('shims'));
14
- };
15
- // ---- Visual C++ runtime ----------------------------------------------------------------------------------------------
16
- const VC_REDIST = 'https://aka.ms/vs/17/release/vc_redist.x64.exe';
17
- /**
18
- * vcruntime140.dll & co. A real installer (it writes to System32), so it runs on every boot; the installer itself is
19
- * kept in persistent storage and only run when Microsoft's signature on it checks out. Resolves true when this call
20
- * ran the installer. Only the "Visual C++ runtime" step reports it (`summary`); PHP calls it again just to be sure.
21
- */
22
- export const setupVcRuntime = async (env, summary = true) => {
23
- if (dryRun) {
24
- log('(dry run) would install the Visual C++ runtime');
25
- return false;
26
- }
27
- const system32 = path.join(process.env.WINDIR ?? 'C:\\Windows', 'System32');
28
- const missing = !exists(path.join(system32, 'vcruntime140.dll')) || !exists(path.join(system32, 'msvcp140.dll'));
29
- if (missing) {
30
- const installer = tool('vcredist', 'vc_redist.x64.exe');
31
- if (!exists(installer)) {
32
- log('Downloading the Visual C++ runtime ...');
33
- await download(VC_REDIST, installer);
34
- }
35
- const signer = execOutput(SYSTEM.powershell(), ['-NoLogo', '-NoProfile', '-Command', "$s = Get-AuthenticodeSignature -LiteralPath '" + installer.replace(/'/g, "''") + "'; if ($s.Status -eq 'Valid') { $s.SignerCertificate.Subject }"]);
36
- if (signer.trim() !== 'CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US') {
37
- fs.rmSync(installer, { force: true });
38
- throw new Error('vc_redist.x64.exe is not signed by Microsoft; deleted it (it is downloaded again next boot)');
39
- }
40
- log('Installing the Visual C++ runtime ...');
41
- // 1638: a newer version is already installed; 3010: installed, a restart would finish it (not needed for the DLLs).
42
- const code = await spawnAsync(installer, ['/install', '/quiet', '/norestart']);
43
- if (![0, 1638, 3010].includes(code ?? -1) || !exists(path.join(system32, 'vcruntime140.dll')))
44
- throw new Error('the Visual C++ runtime installer failed (exit ' + code + ')');
45
- }
46
- if (summary)
47
- env.summary.push('vc++ runtime: installed');
48
- return missing;
49
- };
50
- const RUBY_ASSET = /^rubyinstaller-(\d+\.\d+\.\d+)-(\d+)-x64\.7z$/;
51
- /** RubyInstaller builds per Ruby version (newest build of each), from its GitHub releases. */
52
- const rubyBuilds = async () => {
53
- const releases = await fetchJson('https://api.github.com/repos/oneclick/rubyinstaller2/releases?per_page=100');
54
- const builds = new Map();
55
- for (const asset of releases.filter((r) => !r.draft && !r.prerelease).flatMap((r) => r.assets)) {
56
- const match = RUBY_ASSET.exec(asset.name);
57
- const known = match && builds.get(match[1]);
58
- if (match && (!known || Number(RUBY_ASSET.exec(known.name)[2]) < Number(match[2])))
59
- builds.set(match[1], asset);
60
- }
61
- return builds;
62
- };
63
- export const setupRuby = async (env, config) => {
64
- const wantedSpec = versionFromFiles(['.ruby-version']) ?? { spec: config.version, source: 'config' };
65
- if (dryRun) {
66
- log('(dry run) would provision Ruby ' + wantedSpec.spec);
67
- return;
68
- }
69
- const version = await resolveCached('ruby:' + wantedSpec.spec, async () => pickPackageVersion([...(await rubyBuilds()).keys()], wantedSpec.spec) ?? Promise.reject(new Error('No RubyInstaller build matches "' + wantedSpec.spec + '"')));
70
- const home = tool('ruby', version);
71
- if (!exists(path.join(home, 'bin', 'ruby.exe'))) {
72
- const asset = (await rubyBuilds()).get(version);
73
- if (!asset)
74
- throw new Error('RubyInstaller ' + version + ' is no longer published');
75
- log('Downloading ' + asset.name + ' ...');
76
- await installArchive(asset.browser_download_url, home, { sha256: asset.digest?.startsWith('sha256:') ? asset.digest.slice(7) : null, locate: 'ruby.exe', up: 1, fileName: asset.name });
77
- }
78
- const gems = tool('ruby-gems', version.split('.').slice(0, 2).join('.'));
79
- Object.assign(env.vars, { GEM_HOME: gems, BUNDLE_USER_HOME: tool('bundle-home') });
80
- env.prepend.push(path.join(home, 'bin'), path.join(gems, 'bin'));
81
- env.summary.push('ruby: ' + versionOf(path.join(home, 'bin', 'ruby.exe')) + ' (from ' + wantedSpec.source + '; gems with C extensions need MSYS2, not included)');
82
- const gem = path.join(home, 'bin', 'gem.cmd');
83
- await installOnce(env, 'gem@' + version, config.gems, (pkg) => execAsync(SYSTEM.cmd(), ['/d', '/c', gem, 'install', pkg, '--no-document'], { env: { ...process.env, GEM_HOME: gems } }));
84
- };
85
- const PHP_EXTENSIONS = ['curl', 'fileinfo', 'gd', 'intl', 'mbstring', 'openssl', 'pdo_mysql', 'pdo_pgsql', 'pdo_sqlite', 'sodium', 'sqlite3', 'zip'];
86
- /** The non-thread-safe x64 zip of the newest release matching `spec` ("8" -> newest 8.x, "8.3" -> newest 8.3.x). */
87
- const phpBuild = async (spec) => {
88
- const releases = await fetchJson('https://windows.php.net/downloads/releases/releases.json');
89
- const pick = pickPackageVersion(Object.values(releases).map((r) => r.version), spec);
90
- const release = Object.values(releases).find((r) => r.version === pick);
91
- const build = release && Object.entries(release).find(([key, value]) => /^nts-vs\d+-x64$/.test(key) && typeof value === 'object' && value.zip)?.[1];
92
- if (!release || typeof build !== 'object' || !build.zip)
93
- throw new Error('No PHP build for Windows matches "' + spec + '"');
94
- return { version: release.version, ...build.zip };
95
- };
96
- /** php.ini from the development template with the common extensions switched on. */
97
- const writePhpIni = (home) => {
98
- const ini = path.join(home, 'php.ini');
99
- if (exists(ini) || !exists(path.join(home, 'php.ini-development')))
100
- return;
101
- let text = fs.readFileSync(path.join(home, 'php.ini-development'), 'utf8')
102
- .replace(/^;\s*extension_dir\s*=\s*"ext"/m, 'extension_dir = "' + path.join(home, 'ext') + '"');
103
- for (const ext of PHP_EXTENSIONS.filter((e) => exists(path.join(home, 'ext', 'php_' + e + '.dll'))))
104
- text = text.replace(new RegExp('^;(extension=' + ext + ')\\s*$', 'm'), '$1');
105
- fs.writeFileSync(ini, text);
106
- };
107
- export const setupPhp = async (env, config) => {
108
- const wantedSpec = versionFromFiles(['.php-version']) ?? { spec: config.version, source: 'config' };
109
- if (dryRun) {
110
- log('(dry run) would provision PHP ' + wantedSpec.spec + (config.composer ? ' + Composer' : ''));
111
- return;
112
- }
113
- // No summary line of its own: the Visual C++ runtime step (which runs first) already reports it.
114
- await setupVcRuntime(env, false);
115
- const version = await resolveCached('php:' + wantedSpec.spec, async () => (await phpBuild(wantedSpec.spec)).version);
116
- const home = tool('php', version);
117
- if (!exists(path.join(home, 'php.exe'))) {
118
- const build = await phpBuild(version);
119
- log('Downloading ' + build.path + ' ...');
120
- await installArchive('https://windows.php.net/downloads/releases/' + build.path, home, { sha256: build.sha256, locate: 'php.exe', fileName: build.path });
121
- }
122
- writePhpIni(home);
123
- env.prepend.push(home);
124
- env.summary.push('php: ' + version + ' (from ' + wantedSpec.source + ')');
125
- if (!config.composer)
126
- return;
127
- const phar = tool('composer', 'composer.phar');
128
- if (!exists(phar)) {
129
- log('Downloading Composer ...');
130
- await download('https://getcomposer.org/download/latest-stable/composer.phar', phar, (await fetchText('https://getcomposer.org/download/latest-stable/composer.phar.sha256sum')).trim().split(/\s+/)[0]);
131
- }
132
- const composerHome = tool('composer-home');
133
- env.vars.COMPOSER_HOME = composerHome;
134
- env.prepend.push(path.join(composerHome, 'vendor', 'bin'));
135
- shim(env, 'composer', '"' + path.join(home, 'php.exe') + '" "' + phar + '"');
136
- env.summary.push('composer: ' + (/\d+\.\d+\.\d+/.exec(execOutput(path.join(home, 'php.exe'), [phar, '--version', '--no-ansi']))?.[0] ?? 'installed'));
137
- };
@@ -1,3 +0,0 @@
1
- import type { Fixtures } from './fixtures.js';
2
- import type { Harness } from './harness.js';
3
- export declare const guestTests: (t: Harness, f: Fixtures) => Promise<string>;