windows-development-sandbox 1.17.0 → 1.18.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 (319) hide show
  1. package/README.md +92 -92
  2. package/config.example.json +97 -4
  3. package/dist/app/features/accounts/accounts.def.d.ts +116 -2
  4. package/dist/app/features/accounts/accounts.def.js +80 -12
  5. package/dist/app/features/accounts/accounts.run.d.ts +29 -0
  6. package/dist/app/features/accounts/accounts.run.js +36 -6
  7. package/dist/app/features/accounts/index.d.ts +2 -0
  8. package/dist/app/features/accounts/index.js +4 -1
  9. package/dist/app/features/accounts/services.def.d.ts +111 -0
  10. package/dist/app/features/accounts/services.def.js +187 -0
  11. package/dist/app/features/accounts/services.run.d.ts +65 -0
  12. package/dist/app/features/accounts/services.run.js +212 -0
  13. package/dist/app/features/defs.d.ts +112 -1
  14. package/dist/app/features/defs.js +5 -0
  15. package/dist/app/features/settings/categories.def.js +21 -4
  16. package/dist/app/registry.js +5 -1
  17. package/dist/bin/agent-sandbox.js +2 -2
  18. package/dist/bin/{chunk-QLGGAG3D.js → chunk-BKF43GJ3.js} +51 -16
  19. package/dist/bin/{chunk-REE3EMHD.js → chunk-YD3A67RS.js} +3812 -1702
  20. package/dist/bin/{chunk-XYUVIVJT.js → chunk-Z5D32FN3.js} +1 -1
  21. package/dist/bin/claude-host.js +2 -2
  22. package/dist/bin/claude-sbx.js +2 -2
  23. package/dist/bin/codex-sbx.js +2 -2
  24. package/dist/bin/gemini-sbx.js +2 -2
  25. package/dist/bin/sandbox.js +2 -2
  26. package/dist/bin/sbx-hide.js +2 -2
  27. package/dist/bin/sbx-show.js +2 -2
  28. package/dist/cli/commands/launch/launch.js +30 -1
  29. package/dist/cli/manifest.d.ts +93 -65
  30. package/dist/cli/manifest.js +23 -5
  31. package/dist/cli/menu/pages/accounts-page.js +31 -16
  32. package/dist/cli/services/accounts-service.d.ts +14 -4
  33. package/dist/cli/services/accounts-service.js +19 -6
  34. package/dist/cli/services/data-collect.js +26 -1
  35. package/dist/core/config/defaults.js +47 -4
  36. package/dist/core/detect/toolchains.d.ts +15 -1
  37. package/dist/core/detect/toolchains.js +45 -12
  38. package/dist/core/local-config/overrides.js +6 -0
  39. package/dist/core/schema/accounts.d.ts +27 -0
  40. package/dist/core/schema/accounts.js +32 -0
  41. package/dist/core/schema/config.d.ts +239 -0
  42. package/dist/core/schema/config.js +6 -0
  43. package/dist/core/schema/pc-installs.d.ts +7 -0
  44. package/dist/core/schema/pc-installs.js +17 -0
  45. package/dist/core/schema/toolchains.d.ts +61 -0
  46. package/dist/core/schema/toolchains.js +106 -0
  47. package/dist/core/types.d.ts +18 -0
  48. package/dist/guest/accounts/adapter.d.ts +25 -0
  49. package/dist/guest/accounts/adapter.js +21 -0
  50. package/dist/guest/accounts/adapters.d.ts +5 -0
  51. package/dist/guest/accounts/adapters.js +18 -0
  52. package/dist/guest/accounts/apply.d.ts +41 -0
  53. package/dist/guest/accounts/apply.js +47 -0
  54. package/dist/guest/accounts/kinds/appstore.d.ts +2 -0
  55. package/dist/guest/accounts/kinds/appstore.js +7 -0
  56. package/dist/guest/accounts/kinds/cfx.d.ts +2 -0
  57. package/dist/guest/accounts/kinds/cfx.js +7 -0
  58. package/dist/guest/accounts/kinds/epic.d.ts +2 -0
  59. package/dist/guest/accounts/kinds/epic.js +26 -0
  60. package/dist/guest/accounts/kinds/expo.d.ts +2 -0
  61. package/dist/guest/accounts/kinds/expo.js +7 -0
  62. package/dist/guest/accounts/kinds/keystore.d.ts +2 -0
  63. package/dist/guest/accounts/kinds/keystore.js +7 -0
  64. package/dist/guest/accounts/kinds/play.d.ts +2 -0
  65. package/dist/guest/accounts/kinds/play.js +7 -0
  66. package/dist/guest/accounts/kinds/steam.d.ts +4 -0
  67. package/dist/guest/accounts/kinds/steam.js +17 -0
  68. package/dist/guest/accounts/kinds/unity.d.ts +2 -0
  69. package/dist/guest/accounts/kinds/unity.js +7 -0
  70. package/dist/guest/accounts/watch.d.ts +28 -0
  71. package/dist/guest/accounts/watch.js +78 -0
  72. package/dist/guest/agents/agent-files.d.ts +6 -0
  73. package/dist/guest/agents/agent-files.js +14 -0
  74. package/dist/guest/agents/local-state.d.ts +2 -0
  75. package/dist/guest/agents/local-state.js +13 -1
  76. package/dist/guest/apps/resolvers-gamedev.d.ts +2 -0
  77. package/dist/guest/apps/resolvers-gamedev.js +1 -0
  78. package/dist/guest/apps/resolvers-mobile.d.ts +2 -0
  79. package/dist/guest/apps/resolvers-mobile.js +1 -0
  80. package/dist/guest/apps/resolvers.js +4 -0
  81. package/dist/guest/boot/boot.d.ts +5 -0
  82. package/dist/guest/boot/boot.js +36 -1
  83. package/dist/guest/daemon/accounts.d.ts +3 -0
  84. package/dist/guest/daemon/accounts.js +25 -0
  85. package/dist/guest/daemon/daemon.js +2 -0
  86. package/dist/guest/daemon/idle-shutdown.js +3 -1
  87. package/dist/guest/daemon/sessions.js +2 -0
  88. package/dist/guest/devtools/android.d.ts +2 -0
  89. package/dist/guest/devtools/android.js +7 -0
  90. package/dist/guest/devtools/devtool.d.ts +9 -0
  91. package/dist/guest/devtools/devtool.js +5 -0
  92. package/dist/guest/devtools/fivem.d.ts +2 -0
  93. package/dist/guest/devtools/fivem.js +7 -0
  94. package/dist/guest/devtools/registry.d.ts +6 -0
  95. package/dist/guest/devtools/registry.js +12 -0
  96. package/dist/guest/devtools/steam.d.ts +2 -0
  97. package/dist/guest/devtools/steam.js +228 -0
  98. package/dist/guest/devtools/unity.d.ts +2 -0
  99. package/dist/guest/devtools/unity.js +7 -0
  100. package/dist/guest/entries/daemon.js +2 -0
  101. package/dist/guest/entries/tool.d.ts +1 -0
  102. package/dist/guest/entries/tool.js +17 -0
  103. package/dist/guest/toolchains/engines/godot.d.ts +4 -0
  104. package/dist/guest/toolchains/engines/godot.js +4 -0
  105. package/dist/guest/toolchains/engines/unity-release.d.ts +15 -0
  106. package/dist/guest/toolchains/engines/unity-release.js +6 -0
  107. package/dist/guest/toolchains/engines/unity.d.ts +4 -0
  108. package/dist/guest/toolchains/engines/unity.js +4 -0
  109. package/dist/guest/toolchains/engines/unreal-engines.d.ts +4 -0
  110. package/dist/guest/toolchains/engines/unreal-engines.js +4 -0
  111. package/dist/guest/toolchains/fivem/artifacts.d.ts +6 -0
  112. package/dist/guest/toolchains/fivem/artifacts.js +7 -0
  113. package/dist/guest/toolchains/fivem/fivem.d.ts +4 -0
  114. package/dist/guest/toolchains/fivem/fivem.js +4 -0
  115. package/dist/guest/toolchains/installers.d.ts +12 -0
  116. package/dist/guest/toolchains/installers.js +39 -0
  117. package/dist/guest/toolchains/languages/java.js +1 -1
  118. package/dist/guest/toolchains/languages/kotlin.d.ts +3 -0
  119. package/dist/guest/toolchains/languages/kotlin.js +6 -0
  120. package/dist/guest/toolchains/mobile/android-repo.d.ts +11 -0
  121. package/dist/guest/toolchains/mobile/android-repo.js +10 -0
  122. package/dist/guest/toolchains/mobile/android.d.ts +17 -0
  123. package/dist/guest/toolchains/mobile/android.js +16 -0
  124. package/dist/guest/toolchains/mobile/dart.d.ts +4 -0
  125. package/dist/guest/toolchains/mobile/dart.js +4 -0
  126. package/dist/guest/toolchains/mobile/flutter.d.ts +4 -0
  127. package/dist/guest/toolchains/mobile/flutter.js +4 -0
  128. package/dist/guest/toolchains/mobile/react-native.d.ts +4 -0
  129. package/dist/guest/toolchains/mobile/react-native.js +4 -0
  130. package/dist/guest/toolchains/native/unreal.d.ts +3 -7
  131. package/dist/guest/toolchains/native/unreal.js +16 -155
  132. package/dist/guest/toolchains/native/visual-studio.d.ts +15 -0
  133. package/dist/guest/toolchains/native/visual-studio.js +159 -0
  134. package/dist/guest/toolchains/native/vs-request.d.ts +19 -0
  135. package/dist/guest/toolchains/native/vs-request.js +46 -0
  136. package/dist/guest/toolchains/not-yet.d.ts +2 -0
  137. package/dist/guest/toolchains/not-yet.js +6 -0
  138. package/dist/guest/toolchains/persisted.d.ts +48 -0
  139. package/dist/guest/toolchains/persisted.js +159 -0
  140. package/dist/guest/toolchains/plan.js +80 -4
  141. package/dist/guest/toolchains/steam/apps.d.ts +27 -0
  142. package/dist/guest/toolchains/steam/apps.js +112 -0
  143. package/dist/guest/toolchains/steam/client.d.ts +5 -0
  144. package/dist/guest/toolchains/steam/client.js +67 -0
  145. package/dist/guest/toolchains/steam/commands.d.ts +58 -0
  146. package/dist/guest/toolchains/steam/commands.js +155 -0
  147. package/dist/guest/toolchains/steam/steam.d.ts +4 -0
  148. package/dist/guest/toolchains/steam/steam.js +61 -0
  149. package/dist/guest/toolchains/steam/steamcmd-install.d.ts +29 -0
  150. package/dist/guest/toolchains/steam/steamcmd-install.js +177 -0
  151. package/dist/guest/toolchains/steam/steamcmd.d.ts +6 -0
  152. package/dist/guest/toolchains/steam/steamcmd.js +23 -0
  153. package/dist/guest/toolchains/step.d.ts +10 -0
  154. package/dist/host/accounts/deliver.d.ts +26 -0
  155. package/dist/host/accounts/deliver.js +80 -0
  156. package/dist/host/accounts/handoff.d.ts +17 -0
  157. package/dist/host/accounts/handoff.js +110 -0
  158. package/dist/host/accounts/index.d.ts +6 -0
  159. package/dist/host/accounts/index.js +8 -0
  160. package/dist/host/accounts/kinds/appstore.d.ts +2 -0
  161. package/dist/host/accounts/kinds/appstore.js +10 -0
  162. package/dist/host/accounts/kinds/cfx.d.ts +2 -0
  163. package/dist/host/accounts/kinds/cfx.js +7 -0
  164. package/dist/host/accounts/kinds/epic.d.ts +2 -0
  165. package/dist/host/accounts/kinds/epic.js +15 -0
  166. package/dist/host/accounts/kinds/expo.d.ts +2 -0
  167. package/dist/host/accounts/kinds/expo.js +7 -0
  168. package/dist/host/accounts/kinds/keystore.d.ts +2 -0
  169. package/dist/host/accounts/kinds/keystore.js +7 -0
  170. package/dist/host/accounts/kinds/play.d.ts +2 -0
  171. package/dist/host/accounts/kinds/play.js +7 -0
  172. package/dist/host/accounts/kinds/steam.d.ts +6 -0
  173. package/dist/host/accounts/kinds/steam.js +31 -0
  174. package/dist/host/accounts/kinds/unity.d.ts +2 -0
  175. package/dist/host/accounts/kinds/unity.js +13 -0
  176. package/dist/host/accounts/leases.d.ts +40 -0
  177. package/dist/host/accounts/leases.js +62 -0
  178. package/dist/host/accounts/source.d.ts +21 -0
  179. package/dist/host/accounts/source.js +15 -0
  180. package/dist/host/accounts/sources.d.ts +5 -0
  181. package/dist/host/accounts/sources.js +18 -0
  182. package/dist/host/accounts/store.d.ts +23 -0
  183. package/dist/host/accounts/store.js +74 -0
  184. package/dist/host/audit/log.d.ts +1 -1
  185. package/dist/host/launch/generate.d.ts +2 -0
  186. package/dist/host/launch/generate.js +16 -1
  187. package/dist/host/launch/hints/android.d.ts +3 -0
  188. package/dist/host/launch/hints/android.js +3 -0
  189. package/dist/host/launch/hints/engines.d.ts +3 -0
  190. package/dist/host/launch/hints/engines.js +3 -0
  191. package/dist/host/launch/hints/fivem.d.ts +3 -0
  192. package/dist/host/launch/hints/fivem.js +3 -0
  193. package/dist/host/launch/hints/hint.d.ts +2 -0
  194. package/dist/host/launch/hints/hint.js +1 -0
  195. package/dist/host/launch/hints/index.d.ts +5 -0
  196. package/dist/host/launch/hints/index.js +29 -0
  197. package/dist/host/launch/hints/mobile.d.ts +3 -0
  198. package/dist/host/launch/hints/mobile.js +3 -0
  199. package/dist/host/launch/hints/steam.d.ts +7 -0
  200. package/dist/host/launch/hints/steam.js +59 -0
  201. package/dist/host/launch/index.d.ts +2 -2
  202. package/dist/host/launch/index.js +2 -2
  203. package/dist/host/launch/mappings.js +2 -0
  204. package/dist/host/launch/session-file.js +2 -0
  205. package/dist/host/launch/stage.js +10 -1
  206. package/dist/host/launch/toolchains.d.ts +7 -0
  207. package/dist/host/launch/toolchains.js +11 -0
  208. package/dist/host/pc-installs/discover.d.ts +19 -0
  209. package/dist/host/pc-installs/discover.js +59 -0
  210. package/dist/host/pc-installs/epic.d.ts +2 -0
  211. package/dist/host/pc-installs/epic.js +7 -0
  212. package/dist/host/pc-installs/index.d.ts +3 -0
  213. package/dist/host/pc-installs/index.js +2 -0
  214. package/dist/host/pc-installs/source.d.ts +9 -0
  215. package/dist/host/pc-installs/source.js +1 -0
  216. package/dist/host/pc-installs/steam.d.ts +16 -0
  217. package/dist/host/pc-installs/steam.js +143 -0
  218. package/dist/host/pc-installs/unity.d.ts +2 -0
  219. package/dist/host/pc-installs/unity.js +7 -0
  220. package/dist/host/pc-installs/unreal.d.ts +2 -0
  221. package/dist/host/pc-installs/unreal.js +7 -0
  222. package/dist/host/profile/accounts.d.ts +21 -6
  223. package/dist/host/profile/accounts.js +108 -28
  224. package/dist/host/profile/claude-login.d.ts +32 -2
  225. package/dist/host/profile/claude-login.js +100 -8
  226. package/dist/host/profile/index.d.ts +1 -1
  227. package/dist/host/profile/index.js +1 -1
  228. package/dist/host/secrets/launch-secrets.js +1 -0
  229. package/dist/runtime/agent.js +321 -9
  230. package/dist/runtime/daemon.js +1987 -595
  231. package/dist/runtime/db.js +334 -11
  232. package/dist/runtime/main.js +2530 -709
  233. package/dist/runtime/netproxy.js +321 -9
  234. package/dist/runtime/tool.js +28549 -0
  235. package/dist/runtime/view.js +321 -9
  236. package/dist/selftest/accounts.test.js +172 -1
  237. package/dist/selftest/agent-paths.test.js +13 -1
  238. package/dist/selftest/android.test.d.ts +2 -0
  239. package/dist/selftest/android.test.js +7 -0
  240. package/dist/selftest/app.test.js +1 -1
  241. package/dist/selftest/engines.test.d.ts +2 -0
  242. package/dist/selftest/engines.test.js +7 -0
  243. package/dist/selftest/fivem.test.d.ts +2 -0
  244. package/dist/selftest/fivem.test.js +7 -0
  245. package/dist/selftest/generate.test.js +1 -1
  246. package/dist/selftest/index.js +18 -0
  247. package/dist/selftest/menu-screens.js +1 -0
  248. package/dist/selftest/menu.test.js +2 -1
  249. package/dist/selftest/mobile.test.d.ts +2 -0
  250. package/dist/selftest/mobile.test.js +7 -0
  251. package/dist/selftest/pc-installs.test.d.ts +2 -0
  252. package/dist/selftest/pc-installs.test.js +111 -0
  253. package/dist/selftest/persisted-installs.test.d.ts +2 -0
  254. package/dist/selftest/persisted-installs.test.js +107 -0
  255. package/dist/selftest/projects.test.js +6 -4
  256. package/dist/selftest/service-accounts.test.d.ts +2 -0
  257. package/dist/selftest/service-accounts.test.js +320 -0
  258. package/dist/selftest/steam.test.d.ts +2 -0
  259. package/dist/selftest/steam.test.js +316 -0
  260. package/dist/selftest/storage-helper.test.js +31 -8
  261. package/dist/selftest/support/engine-golden.js +3 -0
  262. package/dist/selftest/support/fixtures.d.ts +87 -0
  263. package/dist/selftest/support/fixtures.js +4 -3
  264. package/dist/selftest/support/pc-memory.d.ts +2 -0
  265. package/dist/selftest/support/pc-memory.js +17 -0
  266. package/dist/selftest/support/web-preview.js +20 -1
  267. package/dist/selftest/unity.test.d.ts +2 -0
  268. package/dist/selftest/unity.test.js +7 -0
  269. package/dist/shared/accounts/fingerprint.d.ts +1 -0
  270. package/dist/shared/accounts/fingerprint.js +20 -0
  271. package/dist/shared/accounts/index.d.ts +5 -0
  272. package/dist/shared/accounts/index.js +7 -0
  273. package/dist/shared/accounts/kinds/epic.d.ts +10 -0
  274. package/dist/shared/accounts/kinds/epic.js +56 -0
  275. package/dist/shared/accounts/kinds/steam.d.ts +28 -0
  276. package/dist/shared/accounts/kinds/steam.js +99 -0
  277. package/dist/shared/accounts/kinds.d.ts +41 -0
  278. package/dist/shared/accounts/kinds.js +154 -0
  279. package/dist/shared/accounts/payloads.d.ts +151 -0
  280. package/dist/shared/accounts/payloads.js +128 -0
  281. package/dist/shared/apps/catalogue-gamedev.d.ts +2 -0
  282. package/dist/shared/apps/catalogue-gamedev.js +1 -0
  283. package/dist/shared/apps/catalogue-mobile.d.ts +2 -0
  284. package/dist/shared/apps/catalogue-mobile.js +1 -0
  285. package/dist/shared/apps/catalogue-modding.d.ts +2 -0
  286. package/dist/shared/apps/catalogue-modding.js +1 -0
  287. package/dist/shared/apps/catalogue.d.ts +1 -0
  288. package/dist/shared/apps/catalogue.js +6 -1
  289. package/dist/shared/channel/schema.d.ts +183 -0
  290. package/dist/shared/channel/secrets.d.ts +42 -0
  291. package/dist/shared/channel/secrets.js +3 -0
  292. package/dist/shared/download/downloader.d.ts +7 -0
  293. package/dist/shared/download/downloader.js +10 -3
  294. package/dist/shared/installs.d.ts +19 -0
  295. package/dist/shared/installs.js +13 -0
  296. package/dist/shared/io/bounded.d.ts +6 -0
  297. package/dist/shared/io/bounded.js +38 -0
  298. package/dist/shared/io/index.d.ts +2 -0
  299. package/dist/shared/io/index.js +2 -0
  300. package/dist/shared/io/text.d.ts +9 -0
  301. package/dist/shared/io/text.js +18 -0
  302. package/dist/shared/paths/guest-paths.js +1 -1
  303. package/dist/shared/paths/index.d.ts +1 -0
  304. package/dist/shared/paths/index.js +1 -0
  305. package/dist/shared/paths/pc-installs.d.ts +20 -0
  306. package/dist/shared/paths/pc-installs.js +19 -0
  307. package/dist/shared/vdf.d.ts +25 -0
  308. package/dist/shared/vdf.js +151 -0
  309. package/dist/sync/sync-cycle.js +9 -0
  310. package/dist/web/index.html +1 -1
  311. package/dist/web/{main-JTH3FT4O.js → main-CWIF4VQI.js} +63 -61
  312. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  313. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  314. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  315. package/package.json +1 -1
  316. package/sandbox/scripts/add-firewall-port.ps1 +13 -0
  317. package/sandbox/templates/fivem/README.txt +2 -0
  318. package/schemas/config.schema.json +485 -0
  319. package/schemas/sandbox-config.schema.json +416 -0
@@ -0,0 +1,128 @@
1
+ // The shape of each saved sign-in (what the host seals, what travels in the one-shot secrets file, what a sandbox may
2
+ // hand back). Pure zod, web-safe. Every text is size-bounded: the sandbox may have written it.
3
+ import { z } from 'zod';
4
+ import { SERVICE_KINDS } from './kinds.js';
5
+ const KB = 1024;
6
+ const base64 = (maxBytes) => z
7
+ .string()
8
+ .min(4)
9
+ .max(maxBytes)
10
+ .regex(/^[A-Za-z0-9+/]+={0,2}$/);
11
+ const secretText = (max) => z
12
+ .string()
13
+ .min(1)
14
+ .max(max)
15
+ .regex(/^[^\r\n]+$/);
16
+ /** The Epic Games Launcher's "Remember me" (the Data value of [RememberMe] in its GameUserSettings.ini). */
17
+ export const EpicPayloadSchema = z.strictObject({
18
+ data: z
19
+ .string()
20
+ .min(16)
21
+ .max(16384)
22
+ .regex(/^[A-Za-z0-9+/=_-]+$/),
23
+ });
24
+ /** SteamCMD's cached login: config\config.vdf (ConnectCache) and the ssfn sentry files. No password. */
25
+ export const SteamPayloadSchema = z.strictObject({
26
+ account: z.string().regex(/^[A-Za-z0-9_]{1,64}$/),
27
+ configVdf: z
28
+ .string()
29
+ .min(1)
30
+ .max(256 * KB),
31
+ sentries: z.array(z.strictObject({ name: z.string().regex(/^ssfn\d{1,40}$/), base64: base64(64 * KB) })).max(4),
32
+ /** Reserved for the Steam client's own login (very likely not portable between Windows users). */
33
+ client: base64(256 * KB).optional(),
34
+ });
35
+ /** A Unity licence file (Unity_lic.ulf), and the Unity ID to sign in with when it has to be made again. */
36
+ export const UnityPayloadSchema = z.strictObject({
37
+ ulf: z
38
+ .string()
39
+ .min(1)
40
+ .max(128 * KB)
41
+ .optional(),
42
+ email: z.string().email().max(256).optional(),
43
+ password: secretText(1024).optional(),
44
+ serial: secretText(64).optional(),
45
+ });
46
+ /** A FiveM / RedM server licence key from the Cfx.re keymaster, and an optional Steam Web API key. */
47
+ export const CfxPayloadSchema = z.strictObject({
48
+ licenseKey: z
49
+ .string()
50
+ .max(256)
51
+ .regex(/^cfxk_[A-Za-z0-9_]+$/),
52
+ steamWebApiKey: z
53
+ .string()
54
+ .regex(/^[A-F0-9]{32}$/)
55
+ .optional(),
56
+ });
57
+ /** An Android upload keystore and what opens it. */
58
+ export const KeystorePayloadSchema = z.strictObject({
59
+ fileName: z.string().regex(/^[A-Za-z0-9._-]{1,100}\.(jks|keystore|p12)$/i),
60
+ jksBase64: base64(256 * KB),
61
+ storePassword: secretText(1024),
62
+ keyAlias: secretText(256),
63
+ keyPassword: secretText(1024),
64
+ });
65
+ /** A Google Play service account key (its JSON file). */
66
+ export const PlayPayloadSchema = z.strictObject({
67
+ serviceAccountJson: z
68
+ .string()
69
+ .min(2)
70
+ .max(64 * KB)
71
+ .refine((text) => {
72
+ try {
73
+ const parsed = JSON.parse(text);
74
+ return (typeof parsed === 'object' && parsed !== null && parsed.type === 'service_account');
75
+ }
76
+ catch {
77
+ return false;
78
+ }
79
+ }, 'not a Google service account key (its "type" is not "service_account")'),
80
+ });
81
+ /** An Expo access token (EXPO_TOKEN). */
82
+ export const ExpoPayloadSchema = z.strictObject({
83
+ token: z.string().regex(/^[A-Za-z0-9_-]{8,512}$/),
84
+ });
85
+ /** An App Store Connect API key: its issuer, its ID and the .p8 private key. */
86
+ export const AppstorePayloadSchema = z.strictObject({
87
+ issuerId: z.string().uuid(),
88
+ keyId: z.string().regex(/^[A-Z0-9]{10}$/),
89
+ p8: z
90
+ .string()
91
+ .max(8 * KB)
92
+ .regex(/^-----BEGIN PRIVATE KEY-----\r?\n[A-Za-z0-9+/=\r\n]+-----END PRIVATE KEY-----\s*$/),
93
+ });
94
+ export const PAYLOADS = {
95
+ epic: EpicPayloadSchema,
96
+ steam: SteamPayloadSchema,
97
+ unity: UnityPayloadSchema,
98
+ cfx: CfxPayloadSchema,
99
+ keystore: KeystorePayloadSchema,
100
+ play: PlayPayloadSchema,
101
+ expo: ExpoPayloadSchema,
102
+ appstore: AppstorePayloadSchema,
103
+ };
104
+ /** The secrets-file field: the sign-ins this launch hands over, each optional. */
105
+ export const ServiceAccountsSchema = z.strictObject({
106
+ epic: EpicPayloadSchema.optional(),
107
+ steam: SteamPayloadSchema.optional(),
108
+ unity: UnityPayloadSchema.optional(),
109
+ cfx: CfxPayloadSchema.optional(),
110
+ keystore: KeystorePayloadSchema.optional(),
111
+ play: PlayPayloadSchema.optional(),
112
+ expo: ExpoPayloadSchema.optional(),
113
+ appstore: AppstorePayloadSchema.optional(),
114
+ });
115
+ /** A launch's id (generate.ts: "<ms>-<6 hex>"). */
116
+ export const LaunchIdSchema = z.string().regex(/^[A-Za-z0-9-]{1,64}$/);
117
+ /** What a sandbox leaves in state\accounts\<kind>.json: the renewed sign-in and the launch it belongs to. */
118
+ export function handoffSchema(kind) {
119
+ return z.strictObject({
120
+ kind: z.literal(kind),
121
+ launchId: LaunchIdSchema,
122
+ payload: PAYLOADS[kind],
123
+ });
124
+ }
125
+ /** The kinds that are in a secrets-file field, in order. */
126
+ export function deliveredKinds(given) {
127
+ return given ? SERVICE_KINDS.filter((k) => given[k] !== undefined) : [];
128
+ }
@@ -0,0 +1,2 @@
1
+ import type { AppEntry } from './catalogue.js';
2
+ export declare const GAMEDEV_APPS: readonly AppEntry[];
@@ -0,0 +1 @@
1
+ export const GAMEDEV_APPS = [];
@@ -0,0 +1,2 @@
1
+ import type { AppEntry } from './catalogue.js';
2
+ export declare const MOBILE_APPS: readonly AppEntry[];
@@ -0,0 +1 @@
1
+ export const MOBILE_APPS = [];
@@ -0,0 +1,2 @@
1
+ import type { AppEntry } from './catalogue.js';
2
+ export declare const MODDING_APPS: readonly AppEntry[];
@@ -0,0 +1 @@
1
+ export const MODDING_APPS = [];
@@ -71,5 +71,6 @@ export declare const HEADROOM_VERSION = "0.41.0";
71
71
  export declare const CCUSAGE_VERSION = "20.0.36";
72
72
  export declare const PLAYWRIGHT_MCP_VERSION = "0.0.83";
73
73
  export declare const CONTEXT7_MCP_VERSION = "4.4.2";
74
+ /** Every app: the ones above, then the game-development, modding and mobile ones (each list in its own file). */
74
75
  export declare const APPS: AppEntry[];
75
76
  export declare function appById(id: string): AppEntry | undefined;
@@ -3,6 +3,9 @@
3
3
  // shared by the menu on the PC (to list them) and the sandbox (to install them, guest/apps). VS Code and Windows
4
4
  // Terminal have settings of their own (toolchains.vscode, provisioning.terminal) and are listed here too, like
5
5
  // claude-mem (claudeProfile.syncClaudeMem) among the agent add-ons.
6
+ import { GAMEDEV_APPS } from './catalogue-gamedev.js';
7
+ import { MOBILE_APPS } from './catalogue-mobile.js';
8
+ import { MODDING_APPS } from './catalogue-modding.js';
6
9
  export const APP_CATEGORIES = [
7
10
  {
8
11
  id: 'agents',
@@ -64,7 +67,7 @@ export const HEADROOM_VERSION = '0.41.0';
64
67
  export const CCUSAGE_VERSION = '20.0.36';
65
68
  export const PLAYWRIGHT_MCP_VERSION = '0.0.83';
66
69
  export const CONTEXT7_MCP_VERSION = '4.4.2';
67
- export const APPS = [
70
+ const CORE_APPS = [
68
71
  // ---- agent add-ons
69
72
  {
70
73
  id: 'rtk',
@@ -887,6 +890,8 @@ export const APPS = [
887
890
  cli: true,
888
891
  },
889
892
  ];
893
+ /** Every app: the ones above, then the game-development, modding and mobile ones (each list in its own file). */
894
+ export const APPS = [...CORE_APPS, ...GAMEDEV_APPS, ...MODDING_APPS, ...MOBILE_APPS];
890
895
  export function appById(id) {
891
896
  return APPS.find((a) => a.id === id);
892
897
  }
@@ -93,6 +93,65 @@ export declare const ProjectSpecSchema: z.ZodObject<{
93
93
  acceptLicense: z.ZodBoolean;
94
94
  components: z.ZodArray<z.ZodString>;
95
95
  keepInstallFiles: z.ZodBoolean;
96
+ persistEngines: z.ZodBoolean;
97
+ derivedDataCache: z.ZodEnum<{
98
+ persisted: "persisted";
99
+ local: "local";
100
+ }>;
101
+ }, z.core.$strict>;
102
+ unity: z.ZodObject<{
103
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
104
+ acceptLicense: z.ZodBoolean;
105
+ version: z.ZodString;
106
+ modules: z.ZodArray<z.ZodString>;
107
+ hub: z.ZodBoolean;
108
+ keepVersions: z.ZodNumber;
109
+ }, z.core.$strict>;
110
+ godot: z.ZodObject<{
111
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
112
+ version: z.ZodString;
113
+ dotnet: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
114
+ exportTemplates: z.ZodBoolean;
115
+ keepVersions: z.ZodNumber;
116
+ }, z.core.$strict>;
117
+ steam: z.ZodObject<{
118
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
119
+ client: z.ZodBoolean;
120
+ sdkFolder: z.ZodString;
121
+ }, z.core.$strict>;
122
+ fivem: z.ZodObject<{
123
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
124
+ acceptLicense: z.ZodBoolean;
125
+ game: z.ZodEnum<{
126
+ gta5: "gta5";
127
+ rdr3: "rdr3";
128
+ }>;
129
+ artifact: z.ZodString;
130
+ txAdmin: z.ZodBoolean;
131
+ keepVersions: z.ZodNumber;
132
+ }, z.core.$strict>;
133
+ android: z.ZodObject<{
134
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
135
+ acceptLicense: z.ZodBoolean;
136
+ platforms: z.ZodArray<z.ZodString>;
137
+ buildTools: z.ZodString;
138
+ ndk: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodString]>;
139
+ cmake: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodString]>;
140
+ packages: z.ZodArray<z.ZodString>;
141
+ }, z.core.$strict>;
142
+ flutter: z.ZodObject<{
143
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
144
+ version: z.ZodString;
145
+ windowsDesktop: z.ZodBoolean;
146
+ precache: z.ZodArray<z.ZodString>;
147
+ }, z.core.$strict>;
148
+ dart: z.ZodObject<{
149
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
150
+ version: z.ZodString;
151
+ }, z.core.$strict>;
152
+ reactNative: z.ZodObject<{
153
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
154
+ easCli: z.ZodBoolean;
96
155
  }, z.core.$strict>;
97
156
  cpp: z.ZodObject<{
98
157
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
@@ -105,6 +164,7 @@ export declare const ProjectSpecSchema: z.ZodObject<{
105
164
  workloads: z.ZodArray<z.ZodString>;
106
165
  frameworkTargets: z.ZodArray<z.ZodString>;
107
166
  netfx35: z.ZodBoolean;
167
+ templates: z.ZodArray<z.ZodString>;
108
168
  }, z.core.$strict>;
109
169
  go: z.ZodObject<{
110
170
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
@@ -116,6 +176,7 @@ export declare const ProjectSpecSchema: z.ZodObject<{
116
176
  version: z.ZodString;
117
177
  maven: z.ZodBoolean;
118
178
  gradle: z.ZodBoolean;
179
+ kotlin: z.ZodBoolean;
119
180
  }, z.core.$strict>;
120
181
  ruby: z.ZodObject<{
121
182
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
@@ -265,6 +326,65 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
265
326
  acceptLicense: z.ZodBoolean;
266
327
  components: z.ZodArray<z.ZodString>;
267
328
  keepInstallFiles: z.ZodBoolean;
329
+ persistEngines: z.ZodBoolean;
330
+ derivedDataCache: z.ZodEnum<{
331
+ persisted: "persisted";
332
+ local: "local";
333
+ }>;
334
+ }, z.core.$strict>;
335
+ unity: z.ZodObject<{
336
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
337
+ acceptLicense: z.ZodBoolean;
338
+ version: z.ZodString;
339
+ modules: z.ZodArray<z.ZodString>;
340
+ hub: z.ZodBoolean;
341
+ keepVersions: z.ZodNumber;
342
+ }, z.core.$strict>;
343
+ godot: z.ZodObject<{
344
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
345
+ version: z.ZodString;
346
+ dotnet: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
347
+ exportTemplates: z.ZodBoolean;
348
+ keepVersions: z.ZodNumber;
349
+ }, z.core.$strict>;
350
+ steam: z.ZodObject<{
351
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
352
+ client: z.ZodBoolean;
353
+ sdkFolder: z.ZodString;
354
+ }, z.core.$strict>;
355
+ fivem: z.ZodObject<{
356
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
357
+ acceptLicense: z.ZodBoolean;
358
+ game: z.ZodEnum<{
359
+ gta5: "gta5";
360
+ rdr3: "rdr3";
361
+ }>;
362
+ artifact: z.ZodString;
363
+ txAdmin: z.ZodBoolean;
364
+ keepVersions: z.ZodNumber;
365
+ }, z.core.$strict>;
366
+ android: z.ZodObject<{
367
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
368
+ acceptLicense: z.ZodBoolean;
369
+ platforms: z.ZodArray<z.ZodString>;
370
+ buildTools: z.ZodString;
371
+ ndk: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodString]>;
372
+ cmake: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodString]>;
373
+ packages: z.ZodArray<z.ZodString>;
374
+ }, z.core.$strict>;
375
+ flutter: z.ZodObject<{
376
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
377
+ version: z.ZodString;
378
+ windowsDesktop: z.ZodBoolean;
379
+ precache: z.ZodArray<z.ZodString>;
380
+ }, z.core.$strict>;
381
+ dart: z.ZodObject<{
382
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
383
+ version: z.ZodString;
384
+ }, z.core.$strict>;
385
+ reactNative: z.ZodObject<{
386
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
387
+ easCli: z.ZodBoolean;
268
388
  }, z.core.$strict>;
269
389
  cpp: z.ZodObject<{
270
390
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
@@ -277,6 +397,7 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
277
397
  workloads: z.ZodArray<z.ZodString>;
278
398
  frameworkTargets: z.ZodArray<z.ZodString>;
279
399
  netfx35: z.ZodBoolean;
400
+ templates: z.ZodArray<z.ZodString>;
280
401
  }, z.core.$strict>;
281
402
  go: z.ZodObject<{
282
403
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
@@ -288,6 +409,7 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
288
409
  version: z.ZodString;
289
410
  maven: z.ZodBoolean;
290
411
  gradle: z.ZodBoolean;
412
+ kotlin: z.ZodBoolean;
291
413
  }, z.core.$strict>;
292
414
  ruby: z.ZodObject<{
293
415
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
@@ -457,6 +579,65 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
457
579
  acceptLicense: z.ZodBoolean;
458
580
  components: z.ZodArray<z.ZodString>;
459
581
  keepInstallFiles: z.ZodBoolean;
582
+ persistEngines: z.ZodBoolean;
583
+ derivedDataCache: z.ZodEnum<{
584
+ persisted: "persisted";
585
+ local: "local";
586
+ }>;
587
+ }, z.core.$strict>;
588
+ unity: z.ZodObject<{
589
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
590
+ acceptLicense: z.ZodBoolean;
591
+ version: z.ZodString;
592
+ modules: z.ZodArray<z.ZodString>;
593
+ hub: z.ZodBoolean;
594
+ keepVersions: z.ZodNumber;
595
+ }, z.core.$strict>;
596
+ godot: z.ZodObject<{
597
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
598
+ version: z.ZodString;
599
+ dotnet: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
600
+ exportTemplates: z.ZodBoolean;
601
+ keepVersions: z.ZodNumber;
602
+ }, z.core.$strict>;
603
+ steam: z.ZodObject<{
604
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
605
+ client: z.ZodBoolean;
606
+ sdkFolder: z.ZodString;
607
+ }, z.core.$strict>;
608
+ fivem: z.ZodObject<{
609
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
610
+ acceptLicense: z.ZodBoolean;
611
+ game: z.ZodEnum<{
612
+ gta5: "gta5";
613
+ rdr3: "rdr3";
614
+ }>;
615
+ artifact: z.ZodString;
616
+ txAdmin: z.ZodBoolean;
617
+ keepVersions: z.ZodNumber;
618
+ }, z.core.$strict>;
619
+ android: z.ZodObject<{
620
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
621
+ acceptLicense: z.ZodBoolean;
622
+ platforms: z.ZodArray<z.ZodString>;
623
+ buildTools: z.ZodString;
624
+ ndk: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodString]>;
625
+ cmake: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodString]>;
626
+ packages: z.ZodArray<z.ZodString>;
627
+ }, z.core.$strict>;
628
+ flutter: z.ZodObject<{
629
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
630
+ version: z.ZodString;
631
+ windowsDesktop: z.ZodBoolean;
632
+ precache: z.ZodArray<z.ZodString>;
633
+ }, z.core.$strict>;
634
+ dart: z.ZodObject<{
635
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
636
+ version: z.ZodString;
637
+ }, z.core.$strict>;
638
+ reactNative: z.ZodObject<{
639
+ enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
640
+ easCli: z.ZodBoolean;
460
641
  }, z.core.$strict>;
461
642
  cpp: z.ZodObject<{
462
643
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
@@ -469,6 +650,7 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
469
650
  workloads: z.ZodArray<z.ZodString>;
470
651
  frameworkTargets: z.ZodArray<z.ZodString>;
471
652
  netfx35: z.ZodBoolean;
653
+ templates: z.ZodArray<z.ZodString>;
472
654
  }, z.core.$strict>;
473
655
  go: z.ZodObject<{
474
656
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
@@ -480,6 +662,7 @@ export declare const GuestRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
480
662
  version: z.ZodString;
481
663
  maven: z.ZodBoolean;
482
664
  gradle: z.ZodBoolean;
665
+ kotlin: z.ZodBoolean;
483
666
  }, z.core.$strict>;
484
667
  ruby: z.ZodObject<{
485
668
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
@@ -10,6 +10,48 @@ export declare const SecretsFileSchema: z.ZodObject<{
10
10
  }, z.core.$strict>>;
11
11
  wireguardConfig: z.ZodOptional<z.ZodString>;
12
12
  githubToken: z.ZodOptional<z.ZodString>;
13
+ serviceAccounts: z.ZodOptional<z.ZodObject<{
14
+ epic: z.ZodOptional<z.ZodObject<{
15
+ data: z.ZodString;
16
+ }, z.core.$strict>>;
17
+ steam: z.ZodOptional<z.ZodObject<{
18
+ account: z.ZodString;
19
+ configVdf: z.ZodString;
20
+ sentries: z.ZodArray<z.ZodObject<{
21
+ name: z.ZodString;
22
+ base64: z.ZodString;
23
+ }, z.core.$strict>>;
24
+ client: z.ZodOptional<z.ZodString>;
25
+ }, z.core.$strict>>;
26
+ unity: z.ZodOptional<z.ZodObject<{
27
+ ulf: z.ZodOptional<z.ZodString>;
28
+ email: z.ZodOptional<z.ZodString>;
29
+ password: z.ZodOptional<z.ZodString>;
30
+ serial: z.ZodOptional<z.ZodString>;
31
+ }, z.core.$strict>>;
32
+ cfx: z.ZodOptional<z.ZodObject<{
33
+ licenseKey: z.ZodString;
34
+ steamWebApiKey: z.ZodOptional<z.ZodString>;
35
+ }, z.core.$strict>>;
36
+ keystore: z.ZodOptional<z.ZodObject<{
37
+ fileName: z.ZodString;
38
+ jksBase64: z.ZodString;
39
+ storePassword: z.ZodString;
40
+ keyAlias: z.ZodString;
41
+ keyPassword: z.ZodString;
42
+ }, z.core.$strict>>;
43
+ play: z.ZodOptional<z.ZodObject<{
44
+ serviceAccountJson: z.ZodString;
45
+ }, z.core.$strict>>;
46
+ expo: z.ZodOptional<z.ZodObject<{
47
+ token: z.ZodString;
48
+ }, z.core.$strict>>;
49
+ appstore: z.ZodOptional<z.ZodObject<{
50
+ issuerId: z.ZodString;
51
+ keyId: z.ZodString;
52
+ p8: z.ZodString;
53
+ }, z.core.$strict>>;
54
+ }, z.core.$strict>>;
13
55
  liveFoldersKey: z.ZodOptional<z.ZodString>;
14
56
  }, z.core.$strict>;
15
57
  export type SecretsFile = z.output<typeof SecretsFileSchema>;
@@ -4,6 +4,7 @@
4
4
  // sandbox may change run\, never this folder. The agent in the sandbox can still read what it was given (its own
5
5
  // environment, its login): that is inherent. Every part is optional.
6
6
  import { z } from 'zod';
7
+ import { ServiceAccountsSchema } from '../accounts/payloads.js';
7
8
  import { GithubTokenSchema } from '../github/index.js';
8
9
  const Environment = z.record(z.string().max(256), z.string().max(65536));
9
10
  export const SecretsFileSchema = z.strictObject({
@@ -28,6 +29,8 @@ export const SecretsFileSchema = z.strictObject({
28
29
  .optional(),
29
30
  /** The GitHub token you saved for sandboxes (github.shareToken), only with git access "full". */
30
31
  githubToken: GithubTokenSchema.optional(),
32
+ /** Your saved service sign-ins (accounts.<kind>.share): each goes to its tool's own files at boot. */
33
+ serviceAccounts: ServiceAccountsSchema.optional(),
31
34
  /** projects.liveFolders "hcs": this launch's key for signed live-folder requests (live.ts). */
32
35
  liveFoldersKey: z
33
36
  .string()
@@ -2,6 +2,13 @@
2
2
  export interface Checksums {
3
3
  sha256?: string | null;
4
4
  sha512?: string | null;
5
+ /** Only where a vendor publishes nothing better (the Android repository). */
6
+ sha1?: string | null;
7
+ /**
8
+ * Checks the finished file before it is moved into place (an Authenticode signer); throwing refuses it. It counts as
9
+ * integrity for downloads.requireChecksum.
10
+ */
11
+ verify?: (file: string) => void | Promise<void>;
5
12
  }
6
13
  export interface ArchiveOptions extends Checksums {
7
14
  /** Keep only the folder holding this file. */
@@ -21,7 +21,7 @@ export class Downloader {
21
21
  */
22
22
  async file(url, destination, expected = null) {
23
23
  const sums = typeof expected === 'string' || expected === null ? { sha256: expected } : expected;
24
- if (this.requireChecksum && !sums.sha256?.trim() && !sums.sha512?.trim()) {
24
+ if (this.requireChecksum && !sums.sha256?.trim() && !sums.sha512?.trim() && !sums.sha1?.trim() && !sums.verify) {
25
25
  throw new Error('Refusing to download ' +
26
26
  path.basename(destination) +
27
27
  ' (' +
@@ -33,7 +33,7 @@ export class Downloader {
33
33
  throw new Error('Empty response for ' + url);
34
34
  fs.mkdirSync(path.dirname(destination), { recursive: true });
35
35
  // Only the hashes that will be checked (sha512 of a 330 MB archive is not free).
36
- const checks = ['sha256', 'sha512'].flatMap((kind) => {
36
+ const checks = ['sha256', 'sha512', 'sha1'].flatMap((kind) => {
37
37
  const want = sums[kind]?.trim().toLowerCase();
38
38
  return want ? [{ want, hash: crypto.createHash(kind) }] : [];
39
39
  });
@@ -72,6 +72,8 @@ export class Downloader {
72
72
  await pipeline(Readable.fromWeb(response.body), tap, fs.createWriteStream(partial));
73
73
  if (checks.some((c) => c.hash.digest('hex') !== c.want))
74
74
  throw new Error('Checksum mismatch for ' + url);
75
+ if (sums.verify)
76
+ await sums.verify(partial);
75
77
  // Mapped folders refuse rename-over, so the old file goes first.
76
78
  fs.rmSync(destination, { force: true });
77
79
  fs.renameSync(partial, destination);
@@ -88,7 +90,12 @@ export class Downloader {
88
90
  const work = stagingFolder(destination);
89
91
  try {
90
92
  const archive = path.join(work, options.fileName ?? path.basename(new URL(url).pathname));
91
- await this.file(url, archive, { sha256: options.sha256, sha512: options.sha512 });
93
+ await this.file(url, archive, {
94
+ sha256: options.sha256,
95
+ sha512: options.sha512,
96
+ sha1: options.sha1,
97
+ ...(options.verify ? { verify: options.verify } : {}),
98
+ });
92
99
  const extracted = path.join(work, 'x');
93
100
  await extractArchive(archive, extracted, options.members);
94
101
  let root = extracted;
@@ -0,0 +1,19 @@
1
+ import { z } from 'zod';
2
+ export declare const INSTALL_RECORDS_FILE = "installs.json";
3
+ export declare const InstallRecordSchema: z.ZodObject<{
4
+ name: z.ZodString;
5
+ version: z.ZodString;
6
+ sizeBytes: z.ZodNullable<z.ZodNumber>;
7
+ installedAt: z.ZodString;
8
+ usedAt: z.ZodString;
9
+ }, z.core.$strip>;
10
+ export type InstallRecord = z.output<typeof InstallRecordSchema>;
11
+ export declare const InstallRecordsSchema: z.ZodObject<{
12
+ installs: z.ZodArray<z.ZodObject<{
13
+ name: z.ZodString;
14
+ version: z.ZodString;
15
+ sizeBytes: z.ZodNullable<z.ZodNumber>;
16
+ installedAt: z.ZodString;
17
+ usedAt: z.ZodString;
18
+ }, z.core.$strip>>;
19
+ }, z.core.$strip>;
@@ -0,0 +1,13 @@
1
+ // tools\installs.json: the large installs a sandbox keeps (engines, editors, SDKs), each with its size and last use.
2
+ // The sandbox writes it (guest/toolchains/persisted.ts); this PC reads it zod-checked, so `sandbox data` can show 60 GB
3
+ // of engines without walking them, and prints what it says through safeText.
4
+ import { z } from 'zod';
5
+ export const INSTALL_RECORDS_FILE = 'installs.json';
6
+ export const InstallRecordSchema = z.object({
7
+ name: z.string().max(100),
8
+ version: z.string().max(100),
9
+ sizeBytes: z.number().nonnegative().nullable(),
10
+ installedAt: z.string().max(64),
11
+ usedAt: z.string().max(64),
12
+ });
13
+ export const InstallRecordsSchema = z.object({ installs: z.array(InstallRecordSchema).max(1000) });
@@ -0,0 +1,6 @@
1
+ /** Test seam: runs between the size check and the read (a file that grows there must still be refused). */
2
+ export interface BoundedReadHooks {
3
+ readonly afterStat?: () => void;
4
+ }
5
+ /** The file's bytes, or null: missing, a link, not a plain file, or bigger than `max` bytes. */
6
+ export declare function readBoundedFile(file: string, max: number, hooks?: BoundedReadHooks): Buffer | null;
@@ -0,0 +1,38 @@
1
+ // Reading a file another party may still be writing (the sandbox, through the data folder) with a hard size bound: no
2
+ // link is followed, and the bound holds even when the file grows between the size check and the read (at most max + 1
3
+ // bytes are ever read from the open descriptor).
4
+ import fs from 'node:fs';
5
+ /** The file's bytes, or null: missing, a link, not a plain file, or bigger than `max` bytes. */
6
+ export function readBoundedFile(file, max, hooks = {}) {
7
+ try {
8
+ if (!fs.lstatSync(file).isFile())
9
+ return null;
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ let fd = null;
15
+ try {
16
+ fd = fs.openSync(file, 'r');
17
+ const stat = fs.fstatSync(fd);
18
+ if (!stat.isFile() || stat.size > max)
19
+ return null;
20
+ hooks.afterStat?.();
21
+ const buffer = Buffer.alloc(max + 1);
22
+ let total = 0;
23
+ while (total < buffer.length) {
24
+ const read = fs.readSync(fd, buffer, total, buffer.length - total, total);
25
+ if (read === 0)
26
+ break;
27
+ total += read;
28
+ }
29
+ return total > max ? null : buffer.subarray(0, total);
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ finally {
35
+ if (fd !== null)
36
+ fs.closeSync(fd);
37
+ }
38
+ }
@@ -3,3 +3,5 @@ export * from './json-file.js';
3
3
  export * from './safe-remove.js';
4
4
  export * from './robocopy-result.js';
5
5
  export * from './copy-tree.js';
6
+ export * from './bounded.js';
7
+ export * from './text.js';
@@ -3,3 +3,5 @@ export * from './json-file.js';
3
3
  export * from './safe-remove.js';
4
4
  export * from './robocopy-result.js';
5
5
  export * from './copy-tree.js';
6
+ export * from './bounded.js';
7
+ export * from './text.js';
@@ -0,0 +1,9 @@
1
+ export type TextEncoding = 'utf8' | 'utf8bom' | 'utf16le';
2
+ export interface DecodedText {
3
+ readonly text: string;
4
+ readonly encoding: TextEncoding;
5
+ }
6
+ /** The text of a file's bytes: UTF-16LE with its mark, UTF-8 with or without one (the mark is not part of the text). */
7
+ export declare function decodeText(bytes: Buffer): DecodedText;
8
+ /** The bytes of a text in an encoding (with its mark for UTF-16LE and UTF-8 with a mark). */
9
+ export declare function encodeText(text: string, encoding: TextEncoding): Buffer;