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
@@ -7,9 +7,11 @@ declare const LeaseSchema: z.ZodObject<{
7
7
  play: "play";
8
8
  expo: "expo";
9
9
  appstore: "appstore";
10
- cfx: "cfx";
11
10
  steam: "steam";
12
11
  epic: "epic";
12
+ cfx: "cfx";
13
+ perplexity: "perplexity";
14
+ comet: "comet";
13
15
  }>;
14
16
  dataSet: z.ZodString;
15
17
  launchId: z.ZodString;
@@ -29,6 +31,10 @@ export declare class AccountLeases {
29
31
  private all;
30
32
  private keep;
31
33
  holder(kind: ServiceKind): Lease | null;
34
+ /** The kind's lease that this data set holds, if any. */
35
+ leaseOf(kind: ServiceKind, dataSet: string): Lease | null;
36
+ /** A lease for this data set and launch beside the other data sets' (a sign-in several sandboxes may use). */
37
+ claimShared(kind: ServiceKind, dataSet: string, launchId: string, delivered: string | null): void;
32
38
  /** The lease for this data set and launch, unless another data set's sandbox still runs with it. */
33
39
  claim(kind: ServiceKind, dataSet: string, launchId: string, delivered: string | null, isRunning: (dataSet: string) => boolean): ClaimResult;
34
40
  release(kind: ServiceKind, dataSet: string): void;
@@ -1,6 +1,7 @@
1
1
  // Which data set holds a one-holder sign-in (Epic, Steam, Unity work in one place at a time), in host-only storage
2
2
  // (<storage>\accounts\leases.json). A launch claims the lease even when nothing is saved yet, so a first sign-in made
3
- // in that sandbox can come back; a lease whose sandbox no longer runs is stale and is taken over.
3
+ // in that sandbox can come back; a lease whose sandbox no longer runs is stale and is taken over. A sign-in several
4
+ // sandboxes may use at once (Comet) has one lease per data set (claimShared): each may hand it back, the last wins.
4
5
  import path from 'node:path';
5
6
  import { z } from 'zod';
6
7
  import { JsonFile } from '../../shared/io/index.js';
@@ -14,7 +15,8 @@ const LeaseSchema = z.object({
14
15
  /** The fingerprint of what was handed over (null: nothing saved yet). */
15
16
  delivered: z.string().max(128).nullable(),
16
17
  });
17
- const LeasesSchema = z.object({ leases: z.array(LeaseSchema).max(SERVICE_KINDS.length) });
18
+ /** Room for one lease per kind, plus one per data set for the kinds several sandboxes share. */
19
+ const LeasesSchema = z.object({ leases: z.array(LeaseSchema).max(SERVICE_KINDS.length * 64) });
18
20
  export class AccountLeases {
19
21
  file;
20
22
  constructor(storageRoot) {
@@ -29,6 +31,15 @@ export class AccountLeases {
29
31
  holder(kind) {
30
32
  return this.all().find((l) => l.kind === kind) ?? null;
31
33
  }
34
+ /** The kind's lease that this data set holds, if any. */
35
+ leaseOf(kind, dataSet) {
36
+ return this.all().find((l) => l.kind === kind && l.dataSet === dataSet) ?? null;
37
+ }
38
+ /** A lease for this data set and launch beside the other data sets' (a sign-in several sandboxes may use). */
39
+ claimShared(kind, dataSet, launchId, delivered) {
40
+ const lease = { kind, dataSet, launchId, issuedAt: new Date().toISOString(), delivered };
41
+ this.keep([...this.all().filter((l) => !(l.kind === kind && l.dataSet === dataSet)), lease]);
42
+ }
32
43
  /** The lease for this data set and launch, unless another data set's sandbox still runs with it. */
33
44
  claim(kind, dataSet, launchId, delivered, isRunning) {
34
45
  const held = this.holder(kind);
@@ -1,10 +1,16 @@
1
1
  import type { Payload, ServiceKind } from '../../shared/accounts/index.js';
2
- /** This PC's profile folders (passed in, so the self-test uses its own). */
2
+ /** This PC's profile folders and environment (passed in, so the self-test uses its own). */
3
3
  export interface PcEnv {
4
4
  readonly localAppData: string;
5
5
  readonly appData: string;
6
6
  readonly programData: string;
7
+ /** The environment the command runs with (a variable set in this terminal). */
8
+ readonly env?: Readonly<Record<string, string | undefined>>;
9
+ /** A variable of this Windows user's saved environment (HKCU\Environment), or null when it is not set. */
10
+ readonly userVariable?: (name: string) => string | null;
7
11
  }
12
+ /** The value of `name` in `reg query <key> /v <name>` output, or null. */
13
+ export declare function parseRegValue(text: string, name: string): string | null;
8
14
  export interface HostSource<K extends ServiceKind> {
9
15
  /** This PC's program's sign-in (`from`: another folder to read it from), or null when it has none. */
10
16
  fromPc?(pc: PcEnv, from?: string): Payload<K> | null;
@@ -1,5 +1,30 @@
1
+ // How a sign-in reaches the store on this PC: taken from this PC's own program (fromPc), read from a file you name
2
+ // (fromFile, `sandbox account import <kind> --from <file>`), or typed (fromAnswers, `sandbox account login <kind>`).
3
+ // One module per kind under kinds/; sources.ts lists them.
4
+ import { spawnSync } from 'node:child_process';
1
5
  import { fail } from '../../core/util/index.js';
6
+ import { SYSTEM } from '../../shared/system.js';
7
+ import { refuseInSelfTest } from '../test-guard.js';
2
8
  import { SERVICE_SPECS } from '../../shared/accounts/index.js';
9
+ /** The value of `name` in `reg query <key> /v <name>` output, or null. */
10
+ export function parseRegValue(text, name) {
11
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
12
+ const m = new RegExp('^\\s+' + escaped + '\\s+REG_(?:EXPAND_)?SZ\\s+(.*)$', 'im').exec(text);
13
+ return m ? m[1].trim() : null;
14
+ }
15
+ /** A variable of this Windows user's saved environment, read with reg.exe (by its full path); null when unset. */
16
+ function savedUserVariable(name) {
17
+ if (!/^[A-Za-z_][A-Za-z0-9_]{0,100}$/.test(name))
18
+ return null;
19
+ refuseInSelfTest("read this PC's saved environment");
20
+ const r = spawnSync(SYSTEM.reg(), ['query', 'HKCU\\Environment', '/v', name], {
21
+ encoding: 'utf8',
22
+ windowsHide: true,
23
+ timeout: 15000,
24
+ maxBuffer: 64 * 1024,
25
+ });
26
+ return r.status === 0 ? parseRegValue(String(r.stdout ?? ''), name) : null;
27
+ }
3
28
  /** This PC's folders from the environment (never HOME or USERPROFILE). */
4
29
  export function pcEnv() {
5
30
  const env = process.env;
@@ -7,6 +32,8 @@ export function pcEnv() {
7
32
  localAppData: env.LOCALAPPDATA ?? '',
8
33
  appData: env.APPDATA ?? '',
9
34
  programData: env.ProgramData ?? env.PROGRAMDATA ?? 'C:\\ProgramData',
35
+ env,
36
+ userVariable: savedUserVariable,
10
37
  };
11
38
  }
12
39
  /** What a kind whose host side is not written yet says. */
@@ -1,8 +1,10 @@
1
1
  import { appstoreSource } from './kinds/appstore.js';
2
2
  import { cfxSource } from './kinds/cfx.js';
3
+ import { cometSource } from './kinds/comet.js';
3
4
  import { epicSource } from './kinds/epic.js';
4
5
  import { expoSource } from './kinds/expo.js';
5
6
  import { keystoreSource } from './kinds/keystore.js';
7
+ import { perplexitySource } from './kinds/perplexity.js';
6
8
  import { playSource } from './kinds/play.js';
7
9
  import { steamSource } from './kinds/steam.js';
8
10
  import { unitySource } from './kinds/unity.js';
@@ -15,4 +17,6 @@ export const HOST_SOURCES = {
15
17
  play: playSource,
16
18
  expo: expoSource,
17
19
  appstore: appstoreSource,
20
+ perplexity: perplexitySource,
21
+ comet: cometSource,
18
22
  };
@@ -10,4 +10,4 @@ export interface GithubDelivery {
10
10
  readonly notes: readonly string[];
11
11
  }
12
12
  /** What this launch hands over. Never throws: a token that cannot be read is a note, and the launch goes on. */
13
- export declare function prepareGithubToken(config: Config, storageRoot: string, store?: GithubToken, others?: readonly BootWant[]): GithubDelivery;
13
+ export declare function prepareGithubToken(config: Config, storageRoot: string, store?: GithubToken, others?: readonly BootWant[], now?: number): GithubDelivery;
@@ -10,8 +10,26 @@ function stricterProject(config, others) {
10
10
  export function githubTokenAllowed(config, others = []) {
11
11
  return config.github.shareToken && stricterProject(config, others) === null;
12
12
  }
13
+ /** A token that ends within this many days is mentioned at launch. */
14
+ const EXPIRY_NOTE_DAYS = 7;
15
+ /** "Your GitHub token expires in 3 days" (or has expired), from what GitHub said when it was saved. */
16
+ function expiryNotes(tokens, now = Date.now()) {
17
+ const at = Date.parse(tokens.check()?.expiresAt ?? '');
18
+ if (Number.isNaN(at))
19
+ return [];
20
+ const days = Math.ceil((at - now) / 86_400_000);
21
+ if (at <= now)
22
+ return ['Your GitHub token has expired, so GitHub will refuse it. Make a new one in Accounts.'];
23
+ if (days > EXPIRY_NOTE_DAYS)
24
+ return [];
25
+ return [
26
+ 'Your GitHub token expires ' +
27
+ (days <= 1 ? 'within a day' : 'in ' + days + ' days') +
28
+ '. Make a new one in Accounts.',
29
+ ];
30
+ }
13
31
  /** What this launch hands over. Never throws: a token that cannot be read is a note, and the launch goes on. */
14
- export function prepareGithubToken(config, storageRoot, store, others = []) {
32
+ export function prepareGithubToken(config, storageRoot, store, others = [], now = Date.now()) {
15
33
  if (!config.github.shareToken)
16
34
  return { notes: [] };
17
35
  const stricter = stricterProject(config, others);
@@ -46,6 +64,7 @@ export function prepareGithubToken(config, storageRoot, store, others = []) {
46
64
  token,
47
65
  notes: [
48
66
  'Your GitHub token is handed to this sandbox (github.shareToken): the agent can read it and use it for whatever it allows.',
67
+ ...expiryNotes(tokens, now),
49
68
  ],
50
69
  };
51
70
  }
@@ -1,2 +1,3 @@
1
- export { GithubToken, checkGithubToken, type TokenState } from './token.js';
1
+ export { GithubToken, checkGithubToken, type SavedCheck, type TokenState } from './token.js';
2
2
  export { githubTokenAllowed, prepareGithubToken, type GithubDelivery } from './deliver.js';
3
+ export { expiryFromHeader, verifyGithubToken, type GithubCheck, type GithubVerdict } from './verify.js';
@@ -1,3 +1,4 @@
1
1
  // The GitHub token for sandboxes: its encrypted store and whether a launch hands it over.
2
2
  export { GithubToken, checkGithubToken } from './token.js';
3
3
  export { githubTokenAllowed, prepareGithubToken } from './deliver.js';
4
+ export { expiryFromHeader, verifyGithubToken } from './verify.js';
@@ -1,5 +1,14 @@
1
+ import { z } from 'zod';
1
2
  import { type GithubTokenKind } from '../../shared/github/index.js';
2
3
  import { SecretStore } from '../secrets/index.js';
4
+ /** What GitHub said about the token when it was saved (never the token): shown in the status. */
5
+ declare const SavedCheckSchema: z.ZodObject<{
6
+ login: z.ZodString;
7
+ expiresAt: z.ZodNullable<z.ZodString>;
8
+ repoCount: z.ZodNumber;
9
+ checkedAt: z.ZodString;
10
+ }, z.core.$strip>;
11
+ export type SavedCheck = z.output<typeof SavedCheckSchema>;
3
12
  /** What the status may say about the token: never its value. */
4
13
  export type TokenState = 'none' | 'saved' | 'unreadable';
5
14
  /** The token's kind, or a refusal that says what to do (the message never holds what was typed). */
@@ -10,7 +19,9 @@ export declare class GithubToken {
10
19
  constructor(storageRoot: string, secrets?: SecretStore);
11
20
  private file;
12
21
  /** Checks, encrypts and saves the token (replacing an earlier one). Returns its kind. */
13
- save(token: string): GithubTokenKind;
22
+ save(token: string, check?: SavedCheck): GithubTokenKind;
23
+ /** What GitHub said when the token was saved: who it is, when it expires, how many repositories (or null). */
24
+ check(): SavedCheck | null;
14
25
  /** The token, decrypted. Only the launch calls this; throws when this Windows user cannot open it. */
15
26
  read(): string | null;
16
27
  /** Whether a token is saved and can be decrypted by this Windows user (the value is never returned). */
@@ -19,3 +30,4 @@ export declare class GithubToken {
19
30
  kind(): GithubTokenKind | null;
20
31
  remove(): boolean;
21
32
  }
33
+ export {};
@@ -8,8 +8,16 @@ import { JsonFile } from '../../shared/io/index.js';
8
8
  import { GITHUB_TOKEN_KINDS, GithubTokenSchema, githubTokenKind, githubTokenProblem, } from '../../shared/github/index.js';
9
9
  import { fail } from '../../core/util/index.js';
10
10
  import { SecretStore } from '../secrets/index.js';
11
+ /** What GitHub said about the token when it was saved (never the token): shown in the status. */
12
+ const SavedCheckSchema = z.object({
13
+ login: z.string().max(100),
14
+ expiresAt: z.string().max(64).nullable(),
15
+ repoCount: z.number().int().min(0).max(100000),
16
+ checkedAt: z.string().max(64),
17
+ });
11
18
  const SavedSchema = z.object({
12
19
  savedAt: z.string().max(64),
20
+ check: SavedCheckSchema.optional(),
13
21
  kind: z.enum(GITHUB_TOKEN_KINDS),
14
22
  sealed: z.string().max(10000),
15
23
  });
@@ -31,11 +39,20 @@ export class GithubToken {
31
39
  return new JsonFile(SavedSchema, path.join(this.storageRoot, 'github', 'token.json'));
32
40
  }
33
41
  /** Checks, encrypts and saves the token (replacing an earlier one). Returns its kind. */
34
- save(token) {
42
+ save(token, check) {
35
43
  const kind = checkGithubToken(token);
36
- this.file().write({ savedAt: new Date().toISOString(), kind, sealed: this.secrets.sealJson({ token }) });
44
+ this.file().write({
45
+ savedAt: new Date().toISOString(),
46
+ kind,
47
+ sealed: this.secrets.sealJson({ token }),
48
+ ...(check ? { check } : {}),
49
+ });
37
50
  return kind;
38
51
  }
52
+ /** What GitHub said when the token was saved: who it is, when it expires, how many repositories (or null). */
53
+ check() {
54
+ return this.file().read()?.check ?? null;
55
+ }
39
56
  /** The token, decrypted. Only the launch calls this; throws when this Windows user cannot open it. */
40
57
  read() {
41
58
  const saved = this.file().read();
@@ -0,0 +1,27 @@
1
+ import { type RequestFetcher } from '../../shared/download/index.js';
2
+ /** What GitHub told about a token (never the token). */
3
+ export interface GithubCheck {
4
+ readonly login: string;
5
+ /** ISO time, or null: it never expires (or GitHub did not say). */
6
+ readonly expiresAt: string | null;
7
+ readonly repos: {
8
+ readonly count: number;
9
+ readonly sample: readonly string[];
10
+ };
11
+ /** A classic token's scopes, as GitHub lists them. */
12
+ readonly scopes?: string;
13
+ }
14
+ /** The answer: a check, GitHub refusing the token, or GitHub not reachable (the token may still be fine). */
15
+ export type GithubVerdict = {
16
+ readonly kind: 'ok';
17
+ readonly check: GithubCheck;
18
+ } | {
19
+ readonly kind: 'refused';
20
+ readonly status: number;
21
+ } | {
22
+ readonly kind: 'unreachable';
23
+ readonly reason: string;
24
+ };
25
+ /** "2027-02-03 10:00:00 UTC" as an ISO time, or null. */
26
+ export declare function expiryFromHeader(value: string | null): string | null;
27
+ export declare function verifyGithubToken(token: string, fetcher?: RequestFetcher): Promise<GithubVerdict>;
@@ -0,0 +1,61 @@
1
+ // Asks GitHub who a token belongs to, when it expires and how many repositories it reaches, right after it was typed,
2
+ // so a wrong or expired token is caught at once. The token goes only in the Authorization header of two requests to
3
+ // api.github.com; the answers are size-limited and read through strict shapes. Nothing here prints the token.
4
+ import { z } from 'zod';
5
+ import { fetchBoundedResponse } from '../../shared/download/index.js';
6
+ const API = 'https://api.github.com';
7
+ const MAX_BYTES = 1024 * 1024;
8
+ /** GitHub's header on a token's answers: "2027-02-03 10:00:00 UTC". */
9
+ const EXPIRY_HEADER = 'github-authentication-token-expiration';
10
+ const UserSchema = z.object({ login: z.string().min(1).max(100) });
11
+ const ReposSchema = z.array(z.object({ full_name: z.string().min(1).max(200) })).max(100);
12
+ /** "2027-02-03 10:00:00 UTC" as an ISO time, or null. */
13
+ export function expiryFromHeader(value) {
14
+ const match = /^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) UTC$/.exec(value?.trim() ?? '');
15
+ if (!match)
16
+ return null;
17
+ const time = Date.parse(match[1] + 'T' + match[2] + 'Z');
18
+ return Number.isNaN(time) ? null : new Date(time).toISOString();
19
+ }
20
+ export async function verifyGithubToken(token, fetcher = fetchBoundedResponse) {
21
+ const init = {
22
+ headers: {
23
+ Authorization: 'Bearer ' + token,
24
+ Accept: 'application/vnd.github+json',
25
+ 'X-GitHub-Api-Version': '2022-11-28',
26
+ },
27
+ };
28
+ try {
29
+ const user = await fetcher(API + '/user', MAX_BYTES, init);
30
+ if (user.status === 401 || user.status === 403)
31
+ return { kind: 'refused', status: user.status };
32
+ if (user.status !== 200)
33
+ return { kind: 'unreachable', reason: 'GitHub answered ' + user.status + '.' };
34
+ const who = UserSchema.safeParse(JSON.parse(user.text));
35
+ if (!who.success)
36
+ return { kind: 'unreachable', reason: 'GitHub gave an answer this program does not know.' };
37
+ const expiresAt = expiryFromHeader(user.header(EXPIRY_HEADER));
38
+ const scopes = user.header('x-oauth-scopes')?.trim();
39
+ const repos = await fetcher(API + '/user/repos?per_page=100&affiliation=owner,collaborator,organization_member', MAX_BYTES, init);
40
+ const list = repos.status === 200 ? ReposSchema.safeParse(JSON.parse(repos.text)) : undefined;
41
+ const names = list?.success ? list.data.map((r) => r.full_name) : [];
42
+ return {
43
+ kind: 'ok',
44
+ check: {
45
+ login: who.data.login,
46
+ expiresAt,
47
+ repos: { count: names.length, sample: names.slice(0, 5) },
48
+ ...(scopes ? { scopes } : {}),
49
+ },
50
+ };
51
+ }
52
+ catch (e) {
53
+ // A timeout, no network, a body that is too big or not JSON: the token itself is not at fault.
54
+ const reason = e instanceof Error && e.name === 'TimeoutError'
55
+ ? 'GitHub did not answer in time.'
56
+ : e instanceof Error && e.message.includes('too big')
57
+ ? e.message
58
+ : 'GitHub could not be reached.';
59
+ return { kind: 'unreachable', reason };
60
+ }
61
+ }
@@ -1,3 +1,5 @@
1
1
  import type { Toolchains } from '../../../core/types.js';
2
2
  import type { ToolchainHints } from './hint.js';
3
- export declare function fivemHints(_root: string, _settings: Toolchains): ToolchainHints;
3
+ /** The resource manifests in the project (relative paths), at most 3 folders down. */
4
+ export declare function findManifests(root: string): string[];
5
+ export declare function fivemHints(root: string, settings: Toolchains): ToolchainHints;
@@ -1,3 +1,69 @@
1
- export function fivemHints(_root, _settings) {
2
- return {};
1
+ // FiveM / RedM: toolchain hints from file contents: fxmanifest.lua or __resource.lua up to 3 folders down turns fivem
2
+ // on (resources\[local]\my-resource\fxmanifest.lua), a C# resource dotnet (a manifest that loads *.net.dll, or a
3
+ // .csproj in the resource's folder or one below). Files are found by name and size-checked before they are read, links
4
+ // are never followed and the walk is bounded (the project may be hostile).
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ const SKIP = /^(node_modules|\.git|\.hg|\.svn|vendor|target|bin|obj|dist|build|out|cache|\.vs|\.idea|packages)$/i;
8
+ const MANIFEST = /^(fxmanifest|__resource)\.lua$/i;
9
+ const MAX_DEPTH = 3;
10
+ const MAX_FOLDERS = 2000;
11
+ const MAX_MANIFESTS = 200;
12
+ const MAX_BYTES = 64 * 1024;
13
+ const list = (dir) => {
14
+ try {
15
+ return fs.readdirSync(dir, { withFileTypes: true }).slice(0, 2000);
16
+ }
17
+ catch {
18
+ return [];
19
+ }
20
+ };
21
+ /** The resource manifests in the project (relative paths), at most 3 folders down. */
22
+ export function findManifests(root) {
23
+ const found = [];
24
+ let folders = 0;
25
+ const walk = (dir, level) => {
26
+ if (++folders > MAX_FOLDERS || found.length >= MAX_MANIFESTS)
27
+ return;
28
+ for (const e of list(dir)) {
29
+ if (e.isFile() && MANIFEST.test(e.name))
30
+ found.push(path.relative(root, path.join(dir, e.name)));
31
+ else if (e.isDirectory() && level < MAX_DEPTH && !SKIP.test(e.name))
32
+ walk(path.join(dir, e.name), level + 1);
33
+ }
34
+ };
35
+ walk(root, 0);
36
+ return found;
37
+ }
38
+ const readSmall = (file) => {
39
+ try {
40
+ const st = fs.lstatSync(file);
41
+ return st.isFile() && st.size <= MAX_BYTES ? fs.readFileSync(file, 'utf8') : '';
42
+ }
43
+ catch {
44
+ return '';
45
+ }
46
+ };
47
+ const hasCsproj = (dir) => list(dir).some((e) => (e.isFile() && /\.csproj$/i.test(e.name)) ||
48
+ (e.isDirectory() &&
49
+ !SKIP.test(e.name) &&
50
+ list(path.join(dir, e.name)).some((f) => f.isFile() && /\.csproj$/i.test(f.name))));
51
+ /** Whether the resource whose manifest this is is written in C# (FiveM loads C# from *.net.dll). */
52
+ function isCsharpResource(root, manifest) {
53
+ if (/\.net\.dll/i.test(readSmall(path.join(root, manifest))))
54
+ return true;
55
+ return hasCsproj(path.dirname(path.join(root, manifest)));
56
+ }
57
+ export function fivemHints(root, settings) {
58
+ if (settings.fivem.enabled === false)
59
+ return {};
60
+ const manifests = findManifests(root);
61
+ if (!manifests.length)
62
+ return {};
63
+ const shown = (rel) => rel.split(path.sep).join('/');
64
+ const hints = { fivem: shown(manifests[0]) + ' (a FiveM / RedM resource)' };
65
+ const csharp = manifests.slice(0, 50).find((m) => isCsharpResource(root, m));
66
+ if (csharp)
67
+ hints.dotnet = 'a C# FiveM resource (' + shown(csharp) + ')';
68
+ return hints;
3
69
  }
@@ -32,6 +32,10 @@ export function guestSession(o, parts) {
32
32
  projectsDir: PROJECTS_GUEST,
33
33
  // Every folder mapped for other projects (roots and projects mapped at boot): the guest accepts projects in them.
34
34
  roots: [...parts.boot.roots, ...parts.boot.projects].map((r) => r.guest),
35
+ mappedProjects: [
36
+ ...parts.boot.roots.map((r) => ({ host: r.host, guest: r.guest, root: true })),
37
+ ...parts.boot.projects.map((r) => ({ host: r.host, guest: r.guest })),
38
+ ],
35
39
  ...(o.autoSession === false ? { autoSession: false } : {}),
36
40
  harnessName,
37
41
  // The environment (it may hold API keys) travels in a one-shot file in <data>\secrets, never in this file.
@@ -6,6 +6,7 @@ import { decideToolchains } from '../../core/detect/index.js';
6
6
  export declare function decideProjectToolchains(o: Pick<GenerateOptions, 'run' | 'pcLanguages'>): ReturnType<typeof decideToolchains>;
7
7
  /**
8
8
  * The toolchains a service sign-in may serve that are on for this boot: the launch's own project (its files decide
9
- * "auto") and the other boot projects that turn one on in their settings.
9
+ * "auto") and the other boot projects that turn one on in their settings. Plus the apps that use one (Perplexity's
10
+ * MCP server, Comet), from your apps.install: apps are installed for the launch's settings only.
10
11
  */
11
12
  export declare function toolchainsOnForBoot(o: Pick<GenerateOptions, 'run' | 'pcLanguages'>, others?: readonly BootWant[]): Set<UsedBy>;
@@ -1,4 +1,4 @@
1
- import { USED_BY } from '../../shared/accounts/index.js';
1
+ import { appUsersOn, USED_BY } from '../../shared/accounts/index.js';
2
2
  import { decideToolchains, readPwshScripts, usesPwsh } from '../../core/detect/index.js';
3
3
  import { pcLanguages } from './pc-scan.js';
4
4
  import { msvcHint, projectEntries, uvHint } from './project-scan.js';
@@ -24,9 +24,11 @@ export function decideProjectToolchains(o) {
24
24
  }
25
25
  /**
26
26
  * The toolchains a service sign-in may serve that are on for this boot: the launch's own project (its files decide
27
- * "auto") and the other boot projects that turn one on in their settings.
27
+ * "auto") and the other boot projects that turn one on in their settings. Plus the apps that use one (Perplexity's
28
+ * MCP server, Comet), from your apps.install: apps are installed for the launch's settings only.
28
29
  */
29
30
  export function toolchainsOnForBoot(o, others = []) {
30
31
  const decisions = decideProjectToolchains(o);
31
- return new Set(USED_BY.filter((u) => decisions[u].on || others.some((p) => p.config.toolchains[u].enabled === true)));
32
+ const toolchains = USED_BY.filter((u) => decisions[u].on || others.some((p) => p.config.toolchains[u].enabled === true));
33
+ return new Set([...toolchains, ...appUsersOn(o.run.config.apps.install)]);
32
34
  }
@@ -17,9 +17,9 @@ export interface CaptureOptions {
17
17
  /** ANTHROPIC_API_KEY of this PC: the last place looked at (--from-host only). */
18
18
  readonly envKey?: string | null;
19
19
  }
20
- /** What is wrong with a typed or found API key (sk-ant-api or sk-ant-usr), or null. Never quotes the key. */
20
+ /** What is wrong with a typed or found API key (any Claude Console key), or null. Never quotes the key. */
21
21
  export declare const apiKeyProblem: typeof anthropicKeyProblem;
22
- /** The most of a key that is ever shown: sk-ant-...abcd. */
22
+ /** The most of a key that is ever shown: its prefix (sk-ant-...abcd, skt-ant-...abcd), then its last 4 characters. */
23
23
  export declare function maskApiKey(key: string): string;
24
24
  export interface LoginFiles {
25
25
  readonly claudeDir: string;
@@ -14,11 +14,12 @@ import { assertTestWritable } from '../test-guard.js';
14
14
  import { hostDataFolder, hostDataRoot } from '../storage/index.js';
15
15
  import { assertNoLinks, fail, fileExists, isLink, removeLink, tryReadJson, writeJson } from '../../core/util/index.js';
16
16
  export const LOGIN_KINDS = ['subscription', 'console', 'api-key'];
17
- /** What is wrong with a typed or found API key (sk-ant-api or sk-ant-usr), or null. Never quotes the key. */
17
+ /** What is wrong with a typed or found API key (any Claude Console key), or null. Never quotes the key. */
18
18
  export const apiKeyProblem = anthropicKeyProblem;
19
- /** The most of a key that is ever shown: sk-ant-...abcd. */
19
+ /** The most of a key that is ever shown: its prefix (sk-ant-...abcd, skt-ant-...abcd), then its last 4 characters. */
20
20
  export function maskApiKey(key) {
21
- return 'sk-ant-...' + key.slice(-4);
21
+ const prefix = /^[A-Za-z0-9]{1,8}-[A-Za-z]{1,8}-/.exec(key)?.[0] ?? '';
22
+ return prefix + '...' + key.slice(-4);
22
23
  }
23
24
  /** What Claude Code records in customApiKeyResponses for a key it may use (its last 20 characters). */
24
25
  function approvalOf(key) {
@@ -1,7 +1,6 @@
1
1
  import { z } from 'zod';
2
- /** Reads a URL as text, refusing more than `max` bytes (a server cannot make this PC download without end). */
3
- export type Fetcher = (url: string, max: number) => Promise<string>;
4
- export declare function fetchBounded(url: string, max: number): Promise<string>;
2
+ import { fetchBounded, type Fetcher } from '../../shared/download/index.js';
3
+ export { fetchBounded, type Fetcher };
5
4
  declare const CountriesSchema: z.ZodArray<z.ZodObject<{
6
5
  id: z.ZodNumber;
7
6
  name: z.ZodString;
@@ -18,4 +17,3 @@ export declare function findCountry(countries: z.output<typeof CountriesSchema>,
18
17
  export declare function recommendedServer(protocol: 'udp' | 'tcp', country: string, fetchText?: Fetcher): Promise<NordServer>;
19
18
  /** The server's OpenVPN profile (not yet checked: shared/vpn/ovpn.ts does that). */
20
19
  export declare function nordProfile(server: NordServer, protocol: 'udp' | 'tcp', fetchText?: Fetcher): Promise<string>;
21
- export {};
@@ -3,35 +3,13 @@
3
3
  // is checked here (bounded size, strict shapes, shared/vpn/ovpn.ts) before the sandbox gets it. Nothing here needs
4
4
  // the login: the profiles are public.
5
5
  import { z } from 'zod';
6
- import { fetchRetry } from '../../shared/download/index.js';
6
+ import { fetchBounded } from '../../shared/download/index.js';
7
7
  import { fail } from '../../core/util/index.js';
8
8
  import { MAX_OVPN_BYTES } from '../../shared/vpn/ovpn.js';
9
9
  const API = 'https://api.nordvpn.com/v1';
10
10
  const CONFIGS = 'https://downloads.nordcdn.com/configs/files';
11
11
  const MAX_JSON_BYTES = 2 * 1024 * 1024;
12
- export async function fetchBounded(url, max) {
13
- const response = await fetchRetry(url, {}, 2, 30000);
14
- const length = Number(response.headers.get('content-length')) || 0;
15
- if (length > max)
16
- throw new Error('The answer from ' + new URL(url).host + ' is too big.');
17
- const reader = response.body?.getReader();
18
- if (!reader)
19
- throw new Error('Empty answer from ' + new URL(url).host + '.');
20
- const chunks = [];
21
- let size = 0;
22
- for (;;) {
23
- const { done, value } = await reader.read();
24
- if (done)
25
- break;
26
- size += value.length;
27
- if (size > max) {
28
- await reader.cancel();
29
- throw new Error('The answer from ' + new URL(url).host + ' is too big.');
30
- }
31
- chunks.push(value);
32
- }
33
- return Buffer.concat(chunks).toString('utf8');
34
- }
12
+ export { fetchBounded };
35
13
  const CountriesSchema = z
36
14
  .array(z.object({ id: z.number().int(), name: z.string().max(100), code: z.string().max(4) }))
37
15
  .max(400);