windows-development-sandbox 1.17.3 → 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 (315) hide show
  1. package/README.md +1 -1
  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/apps/resolvers-gamedev.d.ts +2 -0
  73. package/dist/guest/apps/resolvers-gamedev.js +1 -0
  74. package/dist/guest/apps/resolvers-mobile.d.ts +2 -0
  75. package/dist/guest/apps/resolvers-mobile.js +1 -0
  76. package/dist/guest/apps/resolvers.js +4 -0
  77. package/dist/guest/boot/boot.d.ts +5 -0
  78. package/dist/guest/boot/boot.js +35 -1
  79. package/dist/guest/daemon/accounts.d.ts +3 -0
  80. package/dist/guest/daemon/accounts.js +25 -0
  81. package/dist/guest/daemon/daemon.js +2 -0
  82. package/dist/guest/daemon/idle-shutdown.js +3 -1
  83. package/dist/guest/daemon/sessions.js +2 -0
  84. package/dist/guest/devtools/android.d.ts +2 -0
  85. package/dist/guest/devtools/android.js +7 -0
  86. package/dist/guest/devtools/devtool.d.ts +9 -0
  87. package/dist/guest/devtools/devtool.js +5 -0
  88. package/dist/guest/devtools/fivem.d.ts +2 -0
  89. package/dist/guest/devtools/fivem.js +7 -0
  90. package/dist/guest/devtools/registry.d.ts +6 -0
  91. package/dist/guest/devtools/registry.js +12 -0
  92. package/dist/guest/devtools/steam.d.ts +2 -0
  93. package/dist/guest/devtools/steam.js +228 -0
  94. package/dist/guest/devtools/unity.d.ts +2 -0
  95. package/dist/guest/devtools/unity.js +7 -0
  96. package/dist/guest/entries/daemon.js +2 -0
  97. package/dist/guest/entries/tool.d.ts +1 -0
  98. package/dist/guest/entries/tool.js +17 -0
  99. package/dist/guest/toolchains/engines/godot.d.ts +4 -0
  100. package/dist/guest/toolchains/engines/godot.js +4 -0
  101. package/dist/guest/toolchains/engines/unity-release.d.ts +15 -0
  102. package/dist/guest/toolchains/engines/unity-release.js +6 -0
  103. package/dist/guest/toolchains/engines/unity.d.ts +4 -0
  104. package/dist/guest/toolchains/engines/unity.js +4 -0
  105. package/dist/guest/toolchains/engines/unreal-engines.d.ts +4 -0
  106. package/dist/guest/toolchains/engines/unreal-engines.js +4 -0
  107. package/dist/guest/toolchains/fivem/artifacts.d.ts +6 -0
  108. package/dist/guest/toolchains/fivem/artifacts.js +7 -0
  109. package/dist/guest/toolchains/fivem/fivem.d.ts +4 -0
  110. package/dist/guest/toolchains/fivem/fivem.js +4 -0
  111. package/dist/guest/toolchains/installers.d.ts +12 -0
  112. package/dist/guest/toolchains/installers.js +39 -0
  113. package/dist/guest/toolchains/languages/java.js +1 -1
  114. package/dist/guest/toolchains/languages/kotlin.d.ts +3 -0
  115. package/dist/guest/toolchains/languages/kotlin.js +6 -0
  116. package/dist/guest/toolchains/mobile/android-repo.d.ts +11 -0
  117. package/dist/guest/toolchains/mobile/android-repo.js +10 -0
  118. package/dist/guest/toolchains/mobile/android.d.ts +17 -0
  119. package/dist/guest/toolchains/mobile/android.js +16 -0
  120. package/dist/guest/toolchains/mobile/dart.d.ts +4 -0
  121. package/dist/guest/toolchains/mobile/dart.js +4 -0
  122. package/dist/guest/toolchains/mobile/flutter.d.ts +4 -0
  123. package/dist/guest/toolchains/mobile/flutter.js +4 -0
  124. package/dist/guest/toolchains/mobile/react-native.d.ts +4 -0
  125. package/dist/guest/toolchains/mobile/react-native.js +4 -0
  126. package/dist/guest/toolchains/native/unreal.d.ts +3 -7
  127. package/dist/guest/toolchains/native/unreal.js +16 -167
  128. package/dist/guest/toolchains/native/visual-studio.d.ts +15 -0
  129. package/dist/guest/toolchains/native/visual-studio.js +159 -0
  130. package/dist/guest/toolchains/native/vs-request.d.ts +19 -0
  131. package/dist/guest/toolchains/native/vs-request.js +46 -0
  132. package/dist/guest/toolchains/not-yet.d.ts +2 -0
  133. package/dist/guest/toolchains/not-yet.js +6 -0
  134. package/dist/guest/toolchains/persisted.d.ts +48 -0
  135. package/dist/guest/toolchains/persisted.js +159 -0
  136. package/dist/guest/toolchains/plan.js +80 -4
  137. package/dist/guest/toolchains/steam/apps.d.ts +27 -0
  138. package/dist/guest/toolchains/steam/apps.js +112 -0
  139. package/dist/guest/toolchains/steam/client.d.ts +5 -0
  140. package/dist/guest/toolchains/steam/client.js +67 -0
  141. package/dist/guest/toolchains/steam/commands.d.ts +58 -0
  142. package/dist/guest/toolchains/steam/commands.js +155 -0
  143. package/dist/guest/toolchains/steam/steam.d.ts +4 -0
  144. package/dist/guest/toolchains/steam/steam.js +61 -0
  145. package/dist/guest/toolchains/steam/steamcmd-install.d.ts +29 -0
  146. package/dist/guest/toolchains/steam/steamcmd-install.js +177 -0
  147. package/dist/guest/toolchains/steam/steamcmd.d.ts +6 -0
  148. package/dist/guest/toolchains/steam/steamcmd.js +23 -0
  149. package/dist/guest/toolchains/step.d.ts +10 -0
  150. package/dist/host/accounts/deliver.d.ts +26 -0
  151. package/dist/host/accounts/deliver.js +80 -0
  152. package/dist/host/accounts/handoff.d.ts +17 -0
  153. package/dist/host/accounts/handoff.js +110 -0
  154. package/dist/host/accounts/index.d.ts +6 -0
  155. package/dist/host/accounts/index.js +8 -0
  156. package/dist/host/accounts/kinds/appstore.d.ts +2 -0
  157. package/dist/host/accounts/kinds/appstore.js +10 -0
  158. package/dist/host/accounts/kinds/cfx.d.ts +2 -0
  159. package/dist/host/accounts/kinds/cfx.js +7 -0
  160. package/dist/host/accounts/kinds/epic.d.ts +2 -0
  161. package/dist/host/accounts/kinds/epic.js +15 -0
  162. package/dist/host/accounts/kinds/expo.d.ts +2 -0
  163. package/dist/host/accounts/kinds/expo.js +7 -0
  164. package/dist/host/accounts/kinds/keystore.d.ts +2 -0
  165. package/dist/host/accounts/kinds/keystore.js +7 -0
  166. package/dist/host/accounts/kinds/play.d.ts +2 -0
  167. package/dist/host/accounts/kinds/play.js +7 -0
  168. package/dist/host/accounts/kinds/steam.d.ts +6 -0
  169. package/dist/host/accounts/kinds/steam.js +31 -0
  170. package/dist/host/accounts/kinds/unity.d.ts +2 -0
  171. package/dist/host/accounts/kinds/unity.js +13 -0
  172. package/dist/host/accounts/leases.d.ts +40 -0
  173. package/dist/host/accounts/leases.js +62 -0
  174. package/dist/host/accounts/source.d.ts +21 -0
  175. package/dist/host/accounts/source.js +15 -0
  176. package/dist/host/accounts/sources.d.ts +5 -0
  177. package/dist/host/accounts/sources.js +18 -0
  178. package/dist/host/accounts/store.d.ts +23 -0
  179. package/dist/host/accounts/store.js +74 -0
  180. package/dist/host/audit/log.d.ts +1 -1
  181. package/dist/host/launch/generate.d.ts +2 -0
  182. package/dist/host/launch/generate.js +16 -1
  183. package/dist/host/launch/hints/android.d.ts +3 -0
  184. package/dist/host/launch/hints/android.js +3 -0
  185. package/dist/host/launch/hints/engines.d.ts +3 -0
  186. package/dist/host/launch/hints/engines.js +3 -0
  187. package/dist/host/launch/hints/fivem.d.ts +3 -0
  188. package/dist/host/launch/hints/fivem.js +3 -0
  189. package/dist/host/launch/hints/hint.d.ts +2 -0
  190. package/dist/host/launch/hints/hint.js +1 -0
  191. package/dist/host/launch/hints/index.d.ts +5 -0
  192. package/dist/host/launch/hints/index.js +29 -0
  193. package/dist/host/launch/hints/mobile.d.ts +3 -0
  194. package/dist/host/launch/hints/mobile.js +3 -0
  195. package/dist/host/launch/hints/steam.d.ts +7 -0
  196. package/dist/host/launch/hints/steam.js +59 -0
  197. package/dist/host/launch/index.d.ts +2 -2
  198. package/dist/host/launch/index.js +2 -2
  199. package/dist/host/launch/mappings.js +2 -0
  200. package/dist/host/launch/session-file.js +2 -0
  201. package/dist/host/launch/stage.js +10 -1
  202. package/dist/host/launch/toolchains.d.ts +7 -0
  203. package/dist/host/launch/toolchains.js +11 -0
  204. package/dist/host/pc-installs/discover.d.ts +19 -0
  205. package/dist/host/pc-installs/discover.js +59 -0
  206. package/dist/host/pc-installs/epic.d.ts +2 -0
  207. package/dist/host/pc-installs/epic.js +7 -0
  208. package/dist/host/pc-installs/index.d.ts +3 -0
  209. package/dist/host/pc-installs/index.js +2 -0
  210. package/dist/host/pc-installs/source.d.ts +9 -0
  211. package/dist/host/pc-installs/source.js +1 -0
  212. package/dist/host/pc-installs/steam.d.ts +16 -0
  213. package/dist/host/pc-installs/steam.js +143 -0
  214. package/dist/host/pc-installs/unity.d.ts +2 -0
  215. package/dist/host/pc-installs/unity.js +7 -0
  216. package/dist/host/pc-installs/unreal.d.ts +2 -0
  217. package/dist/host/pc-installs/unreal.js +7 -0
  218. package/dist/host/profile/accounts.d.ts +21 -6
  219. package/dist/host/profile/accounts.js +108 -28
  220. package/dist/host/profile/claude-login.d.ts +32 -2
  221. package/dist/host/profile/claude-login.js +100 -8
  222. package/dist/host/profile/index.d.ts +1 -1
  223. package/dist/host/profile/index.js +1 -1
  224. package/dist/host/secrets/launch-secrets.js +1 -0
  225. package/dist/runtime/agent.js +321 -9
  226. package/dist/runtime/daemon.js +1987 -604
  227. package/dist/runtime/db.js +334 -11
  228. package/dist/runtime/main.js +2519 -723
  229. package/dist/runtime/netproxy.js +321 -9
  230. package/dist/runtime/tool.js +28549 -0
  231. package/dist/runtime/view.js +321 -9
  232. package/dist/selftest/accounts.test.js +172 -1
  233. package/dist/selftest/agent-paths.test.js +1 -0
  234. package/dist/selftest/android.test.d.ts +2 -0
  235. package/dist/selftest/android.test.js +7 -0
  236. package/dist/selftest/app.test.js +1 -1
  237. package/dist/selftest/engines.test.d.ts +2 -0
  238. package/dist/selftest/engines.test.js +7 -0
  239. package/dist/selftest/fivem.test.d.ts +2 -0
  240. package/dist/selftest/fivem.test.js +7 -0
  241. package/dist/selftest/generate.test.js +1 -1
  242. package/dist/selftest/index.js +18 -0
  243. package/dist/selftest/menu-screens.js +1 -0
  244. package/dist/selftest/menu.test.js +2 -1
  245. package/dist/selftest/mobile.test.d.ts +2 -0
  246. package/dist/selftest/mobile.test.js +7 -0
  247. package/dist/selftest/pc-installs.test.d.ts +2 -0
  248. package/dist/selftest/pc-installs.test.js +111 -0
  249. package/dist/selftest/persisted-installs.test.d.ts +2 -0
  250. package/dist/selftest/persisted-installs.test.js +107 -0
  251. package/dist/selftest/projects.test.js +6 -4
  252. package/dist/selftest/service-accounts.test.d.ts +2 -0
  253. package/dist/selftest/service-accounts.test.js +320 -0
  254. package/dist/selftest/steam.test.d.ts +2 -0
  255. package/dist/selftest/steam.test.js +316 -0
  256. package/dist/selftest/storage-helper.test.js +31 -8
  257. package/dist/selftest/support/engine-golden.js +3 -0
  258. package/dist/selftest/support/fixtures.d.ts +87 -0
  259. package/dist/selftest/support/fixtures.js +4 -3
  260. package/dist/selftest/support/pc-memory.d.ts +2 -0
  261. package/dist/selftest/support/pc-memory.js +17 -0
  262. package/dist/selftest/support/web-preview.js +20 -1
  263. package/dist/selftest/unity.test.d.ts +2 -0
  264. package/dist/selftest/unity.test.js +7 -0
  265. package/dist/shared/accounts/fingerprint.d.ts +1 -0
  266. package/dist/shared/accounts/fingerprint.js +20 -0
  267. package/dist/shared/accounts/index.d.ts +5 -0
  268. package/dist/shared/accounts/index.js +7 -0
  269. package/dist/shared/accounts/kinds/epic.d.ts +10 -0
  270. package/dist/shared/accounts/kinds/epic.js +56 -0
  271. package/dist/shared/accounts/kinds/steam.d.ts +28 -0
  272. package/dist/shared/accounts/kinds/steam.js +99 -0
  273. package/dist/shared/accounts/kinds.d.ts +41 -0
  274. package/dist/shared/accounts/kinds.js +154 -0
  275. package/dist/shared/accounts/payloads.d.ts +151 -0
  276. package/dist/shared/accounts/payloads.js +128 -0
  277. package/dist/shared/apps/catalogue-gamedev.d.ts +2 -0
  278. package/dist/shared/apps/catalogue-gamedev.js +1 -0
  279. package/dist/shared/apps/catalogue-mobile.d.ts +2 -0
  280. package/dist/shared/apps/catalogue-mobile.js +1 -0
  281. package/dist/shared/apps/catalogue-modding.d.ts +2 -0
  282. package/dist/shared/apps/catalogue-modding.js +1 -0
  283. package/dist/shared/apps/catalogue.d.ts +1 -0
  284. package/dist/shared/apps/catalogue.js +6 -1
  285. package/dist/shared/channel/schema.d.ts +183 -0
  286. package/dist/shared/channel/secrets.d.ts +42 -0
  287. package/dist/shared/channel/secrets.js +3 -0
  288. package/dist/shared/download/downloader.d.ts +7 -0
  289. package/dist/shared/download/downloader.js +10 -3
  290. package/dist/shared/installs.d.ts +19 -0
  291. package/dist/shared/installs.js +13 -0
  292. package/dist/shared/io/bounded.d.ts +6 -0
  293. package/dist/shared/io/bounded.js +38 -0
  294. package/dist/shared/io/index.d.ts +2 -0
  295. package/dist/shared/io/index.js +2 -0
  296. package/dist/shared/io/text.d.ts +9 -0
  297. package/dist/shared/io/text.js +18 -0
  298. package/dist/shared/paths/guest-paths.js +1 -1
  299. package/dist/shared/paths/index.d.ts +1 -0
  300. package/dist/shared/paths/index.js +1 -0
  301. package/dist/shared/paths/pc-installs.d.ts +20 -0
  302. package/dist/shared/paths/pc-installs.js +19 -0
  303. package/dist/shared/vdf.d.ts +25 -0
  304. package/dist/shared/vdf.js +151 -0
  305. package/dist/sync/sync-cycle.js +9 -0
  306. package/dist/web/index.html +1 -1
  307. package/dist/web/{main-JTH3FT4O.js → main-CWIF4VQI.js} +63 -61
  308. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  309. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  310. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  311. package/package.json +1 -1
  312. package/sandbox/scripts/add-firewall-port.ps1 +13 -0
  313. package/sandbox/templates/fivem/README.txt +2 -0
  314. package/schemas/config.schema.json +485 -0
  315. package/schemas/sandbox-config.schema.json +416 -0
@@ -0,0 +1,7 @@
1
+ export const androidSuite = {
2
+ name: 'android',
3
+ title: 'Android: the SDK, signing keys, Google Play, Kotlin (not available yet)',
4
+ run: (t) => {
5
+ t.check(true, 'android: not available yet');
6
+ },
7
+ };
@@ -551,7 +551,7 @@ export const appSuite = {
551
551
  const codexMenu = menuView(agentPage(codex), { category: 'codex', refresh: false });
552
552
  t.check(codexMenu.enterLabel('codex', 'codex:main') === 'use' &&
553
553
  ['s', 'p', 'd', 'r', 'x'].every((k) => codexMenu.actions.some((a) => a.key === k)) &&
554
- accountsPage({ agent: 'codex', agents: [codex] }).sections[0].cards === true, 'accounts: the page offers use on Enter and d/r/x on a login; the codex agent is a card');
554
+ accountsPage({ agent: 'codex', agents: [codex], services: [] }).sections[0].cards === true, 'accounts: the page offers use on Enter and d/r/x on a login; the codex agent is a card');
555
555
  const useDef = definitionFor('account', ['use']);
556
556
  t.check(JSON.stringify(cliInput(useDef, parseCli(['account', 'use', 'work', '--no-restart']))) ===
557
557
  JSON.stringify({ name: 'work', noRestart: true }) &&
@@ -0,0 +1,2 @@
1
+ import type { Suite } from './support/suite.js';
2
+ export declare const enginesSuite: Suite;
@@ -0,0 +1,7 @@
1
+ export const enginesSuite = {
2
+ name: 'engines',
3
+ title: 'Unreal engines, Godot and the game-development apps (not available yet)',
4
+ run: (t) => {
5
+ t.check(true, 'engines: not available yet');
6
+ },
7
+ };
@@ -0,0 +1,2 @@
1
+ import type { Suite } from './support/suite.js';
2
+ export declare const fivemSuite: Suite;
@@ -0,0 +1,7 @@
1
+ export const fivemSuite = {
2
+ name: 'fivem',
3
+ title: 'FiveM / RedM: server builds, the Cfx.re key, sbx-fivem (not available yet)',
4
+ run: (t) => {
5
+ t.check(true, 'fivem: not available yet');
6
+ },
7
+ };
@@ -38,7 +38,7 @@ export const generateSuite = {
38
38
  'sandbox/csharp/LinkGate.cs',
39
39
  ];
40
40
  const missing = expectedFiles.filter((file) => !fs.existsSync(path.join(gen.launchPath, file)));
41
- t.check(missing.length === 0 && fs.readdirSync(path.join(gen.launchPath, 'runtime')).length === 7, 'the launch folder has the runtime (7 self-contained files), the C# sources and the boot scripts', missing.join(', '));
41
+ t.check(missing.length === 0 && fs.readdirSync(path.join(gen.launchPath, 'runtime')).length === 8, 'the launch folder has the runtime (8 self-contained files), the C# sources and the boot scripts', missing.join(', '));
42
42
  const logon = fs.readFileSync(path.join(gen.launchPath, 'sandbox', 'boot', 'logon.cmd'), 'utf8');
43
43
  t.check(logon.indexOf('trust.cmd') < logon.indexOf('wt.exe') &&
44
44
  logon.includes('tools\\terminal\\wt.exe') &&
@@ -11,6 +11,15 @@ import { createFixtures } from './support/fixtures.js';
11
11
  import { createHarness } from './support/harness.js';
12
12
  import { liveTestsAvailable } from './support/live.js';
13
13
  import { accountsSuite } from './accounts.test.js';
14
+ import { serviceAccountsSuite } from './service-accounts.test.js';
15
+ import { persistedInstallsSuite } from './persisted-installs.test.js';
16
+ import { pcInstallsSuite } from './pc-installs.test.js';
17
+ import { steamSuite } from './steam.test.js';
18
+ import { enginesSuite } from './engines.test.js';
19
+ import { unitySuite } from './unity.test.js';
20
+ import { fivemSuite } from './fivem.test.js';
21
+ import { androidSuite } from './android.test.js';
22
+ import { mobileSuite } from './mobile.test.js';
14
23
  import { credentialsSuite } from './credentials.test.js';
15
24
  import { vpnSuite } from './vpn.test.js';
16
25
  import { githubSuite } from './github.test.js';
@@ -101,6 +110,8 @@ export const SUITES = [
101
110
  credentialsSuite,
102
111
  vpnSuite,
103
112
  githubSuite,
113
+ serviceAccountsSuite,
114
+ pcInstallsSuite,
104
115
  workspaceViewSuite,
105
116
  auditSuite,
106
117
  // an untrusted sandbox: what the host refuses to do with what it finds
@@ -117,6 +128,13 @@ export const SUITES = [
117
128
  // pure helpers and the command line
118
129
  layoutSuite,
119
130
  toolchainsSuite,
131
+ persistedInstallsSuite,
132
+ steamSuite,
133
+ enginesSuite,
134
+ unitySuite,
135
+ fivemSuite,
136
+ androidSuite,
137
+ mobileSuite,
120
138
  detectSuite,
121
139
  networkSuite,
122
140
  personalizationSuite,
@@ -34,6 +34,7 @@ const DATA_FOLDER_ROW = [
34
34
  ];
35
35
  const ACCOUNTS = {
36
36
  agent: 'claude',
37
+ services: [],
37
38
  agents: [
38
39
  {
39
40
  name: 'claude',
@@ -618,9 +618,10 @@ export const menuSuite = {
618
618
  !stoppedItems.some((i) => i.value === 'act:stop'), 'with no sandbox running, Sandbox offers to start one (Status and Databases) instead of dead rows');
619
619
  const noLogins = accountsOptions({
620
620
  agent: 'claude',
621
+ services: [],
621
622
  agents: [{ name: 'claude', kept: true, canSavePc: true, accounts: [] }],
622
623
  });
623
- t.equal(noLogins.items.map((i) => i.value), ['@save-sandbox', '@save-pc'], 'with no saved login, Accounts offers to save the sandbox login or this PC login (Enter)');
624
+ t.equal(noLogins.items.map((i) => i.value), ['@save-sandbox', '@save-pc', '@save-key'], 'with no saved login, Accounts offers to save the sandbox login, this PC login or an API key (Enter)');
624
625
  t.section('apps in a few groups');
625
626
  const groupIds = appsCategories(user2).map((c) => c.id);
626
627
  const appItems = appsItems(user2, t.dir('apps-groups'), new Draft()).filter((i) => i.value !== REQUEST_APP);
@@ -0,0 +1,2 @@
1
+ import type { Suite } from './support/suite.js';
2
+ export declare const mobileSuite: Suite;
@@ -0,0 +1,7 @@
1
+ export const mobileSuite = {
2
+ name: 'mobile',
3
+ title: 'Flutter, Dart, React Native / Expo and App Store Connect (not available yet)',
4
+ run: (t) => {
5
+ t.check(true, 'mobile: not available yet');
6
+ },
7
+ };
@@ -0,0 +1,2 @@
1
+ import type { Suite } from './support/suite.js';
2
+ export declare const pcInstallsSuite: Suite;
@@ -0,0 +1,111 @@
1
+ // pcInstalls (this PC's games, engines and editors, read-only in the sandbox): where each one appears, what is
2
+ // refused, that a project file can never ask for them, and the launch writing them into the .wsb and session.json
3
+ // (paths in the sandbox only). Also the new toolchains' detection: markers, what one brings along, licence gates, and
4
+ // that a project can never accept a licence for you. Temp folders and fake sources only.
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { DEFAULTS } from '../core/config/index.js';
8
+ import { decideToolchains, LICENSED_TOOLCHAINS } from '../core/detect/index.js';
9
+ import { applyOverrides, normalizeOverrides, OFF_IS_SAFER } from '../core/local-config/index.js';
10
+ import { LocalOverridesSchema } from '../core/schema/index.js';
11
+ import { discoverPcInstalls } from '../host/pc-installs/index.js';
12
+ import { generateSandbox } from '../host/launch/index.js';
13
+ import { isValidGuestPath, PC_INSTALLS_GUEST } from '../shared/paths/index.js';
14
+ const PC = { localAppData: '', appData: '', programData: '' };
15
+ export const pcInstallsSuite = {
16
+ name: 'pc-installs',
17
+ title: "this PC's installs mapped read-only; new toolchains' markers, implications and licence gates",
18
+ run: async (t, f) => {
19
+ t.section('placing them');
20
+ const game = t.dir('pc-games', 'Spacewar');
21
+ const engine = t.dir('pc-engines', 'UE_5.4');
22
+ const installs = (items) => ({
23
+ kind: 'steam-game',
24
+ setting: 'steamGames',
25
+ find: () => items,
26
+ });
27
+ const steam = installs([
28
+ { kind: 'steam-game', id: '480', name: 'Spacewar', host: game, guest: '' },
29
+ { kind: 'steam-game', id: '481', name: 'Gone', host: path.join(game, 'missing'), guest: '' },
30
+ { kind: 'steam-game', id: '..', name: 'Climber', host: game, guest: '' },
31
+ ]);
32
+ const unreal = {
33
+ kind: 'unreal',
34
+ setting: 'unrealEngines',
35
+ find: () => [
36
+ { kind: 'unreal', id: 'UE_5.4', name: 'Unreal Engine 5.4', version: '5.4', host: engine, guest: '' },
37
+ ],
38
+ };
39
+ const settings = { ...DEFAULTS.pcInstalls, steamGames: [480, 481], unrealEngines: true };
40
+ const found = discoverPcInstalls(settings, { packageRoot: t.packageRoot, storageRoot: f.baseConfig.storage.root, sources: [steam, unreal] }, PC);
41
+ t.equal(found.mappings.map((m) => [m.guest, m.readOnly]), [
42
+ [PC_INSTALLS_GUEST + '\\steam\\480', true],
43
+ [PC_INSTALLS_GUEST + '\\unreal\\UE_5.4', true],
44
+ ], 'each one appears read-only under C:\\PcInstalls by its kind and id');
45
+ t.check(found.notes.some((n) => n.includes('Gone')) && found.notes.some((n) => n.includes('Climber')), 'a folder that is not there, or an id that is not a folder name, is left out with a note');
46
+ t.check(!JSON.stringify(found.manifest).includes(game) &&
47
+ found.manifest.items.every((i) => i.guest.startsWith(PC_INSTALLS_GUEST)), 'the manifest for the sandbox holds no path of this PC');
48
+ t.equal(discoverPcInstalls(DEFAULTS.pcInstalls, { packageRoot: t.packageRoot, storageRoot: f.baseConfig.storage.root, sources: [steam] }, PC).mappings, [], 'off by default: nothing is looked for');
49
+ const unsafe = installs([
50
+ { kind: 'steam-game', id: '1', name: 'Storage', host: f.baseConfig.storage.root, guest: '' },
51
+ ]);
52
+ t.check(discoverPcInstalls({ ...settings, unrealEngines: false }, { packageRoot: t.packageRoot, storageRoot: f.baseConfig.storage.root, sources: [unsafe] }, PC).mappings.length === 0, "this tool's own folders are refused like any other mapping");
53
+ t.check(!isValidGuestPath('C:\\PcInstalls\\mine'), 'no other mapping may use C:\\PcInstalls');
54
+ t.section('yours only');
55
+ t.check(!('pcInstalls' in LocalOverridesSchema.shape), 'a project file has no pcInstalls');
56
+ await t.throws(() => normalizeOverrides({ pcInstalls: { steamGames: true } }, f.workA, 'project'), 'not valid', 'a project file asking for them is refused');
57
+ t.section('the launch');
58
+ const run = await f.run({}, f.makeConfig());
59
+ const launched = generateSandbox({
60
+ packageRoot: t.packageRoot,
61
+ run,
62
+ harnessArgs: [],
63
+ probe: f.probe,
64
+ pcLanguages: [],
65
+ pcInstalls: { mappings: found.mappings, manifest: found.manifest },
66
+ });
67
+ const wsb = fs.readFileSync(launched.wsbPath, 'utf8');
68
+ const session = f.readJson(launched.sessionPath);
69
+ t.check(wsb.includes(game) && wsb.includes(PC_INSTALLS_GUEST + '\\steam\\480'), 'the .wsb maps them');
70
+ t.check(session.pcInstalls?.items.map((i) => i.id).join() === '480,UE_5.4' &&
71
+ !JSON.stringify(session.pcInstalls).includes(game), 'session.json lists them by their place in the sandbox only');
72
+ t.section('detection of the new toolchains');
73
+ const tc = structuredClone(DEFAULTS.toolchains);
74
+ const decide = (entries, hints = {}, settingsOf = tc) => decideToolchains(settingsOf, entries, [], hints);
75
+ const godot = decide(['project.godot']);
76
+ const steamApp = decide(['steam_appid.txt']);
77
+ const unity = decide(['ProjectSettings/ProjectVersion.txt']);
78
+ t.check(godot.godot.on && steamApp.steam.on && decide(['src/Main.kt']).java.on, 'project.godot, steam_appid.txt and *.kt turn their toolchains on');
79
+ t.check(!unity.unity.on && unity.unity.reason.includes('toolchains.unity.acceptLicense'), 'Unity is licensed: "auto" waits for acceptLicense and says so');
80
+ const flutter = decide([], { flutter: 'pubspec.yaml uses Flutter' });
81
+ t.check(flutter.flutter.on &&
82
+ !flutter.android.on &&
83
+ flutter.android.reason.includes('Flutter') &&
84
+ flutter.android.reason.includes('acceptLicense') &&
85
+ !flutter.java.on, 'Flutter would bring the Android SDK, but not before its licence is accepted (and no JDK then)');
86
+ const accepted = structuredClone(tc);
87
+ accepted.android.acceptLicense = true;
88
+ const flutterOk = decide([], { reactNative: 'package.json uses react-native' }, accepted);
89
+ t.check(flutterOk.android.on && flutterOk.java.on && flutterOk.java.reason.includes('JDK'), 'React Native brings the Android SDK, and the Android SDK a JDK');
90
+ const godotNet = structuredClone(tc);
91
+ godotNet.godot.dotnet = true;
92
+ t.check(decide(['project.godot'], {}, godotNet).dotnet.on, "Godot's .NET build brings .NET");
93
+ const fivem = decide(['fxmanifest.lua']);
94
+ t.check(!fivem.fivem.on && fivem.fivem.reason.includes('fivem.acceptLicense'), 'the FiveM server is licensed too');
95
+ t.section('a project never accepts a licence or shares a sign-in');
96
+ t.check(LICENSED_TOOLCHAINS.every((n) => OFF_IS_SAFER.includes('toolchains.' + n + '.acceptLicense')) &&
97
+ OFF_IS_SAFER.includes('accounts.epic.share') &&
98
+ OFF_IS_SAFER.includes('accounts.appstore.share'), 'every acceptLicense and every share is a protection a project can only turn off');
99
+ const mine = structuredClone(DEFAULTS);
100
+ const merged = applyOverrides(mine, normalizeOverrides({
101
+ toolchains: {
102
+ unity: { acceptLicense: true },
103
+ msvc: { acceptLicense: true },
104
+ android: { acceptLicense: true },
105
+ },
106
+ }, f.workA, 'project'));
107
+ t.check(!merged.toolchains.unity.acceptLicense &&
108
+ !merged.toolchains.msvc.acceptLicense &&
109
+ !merged.toolchains.android.acceptLicense, 'a project asking to accept a licence leaves it unaccepted');
110
+ },
111
+ };
@@ -0,0 +1,2 @@
1
+ import type { Suite } from './support/suite.js';
2
+ export declare const persistedInstallsSuite: Suite;
@@ -0,0 +1,107 @@
1
+ // Large installs kept between sandboxes (guest/toolchains/persisted.ts): the staging folder and its TEMP, the marker,
2
+ // the refusal when the disk is short (an injected statfs), keeping only keepVersions, the records `sandbox data`
3
+ // reads, and which Visual Studio the toolchains ask for (vs-request.ts). Temp folders only.
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { DEFAULTS } from '../core/config/index.js';
7
+ import { assertLocalRoom, ensurePersisted, installRecords, InstallRecordsSchema, LOCAL_RESERVE_BYTES, recordUse, } from '../guest/toolchains/persisted.js';
8
+ import { BUILD_TOOLS_COMPONENTS, UNREAL_COMPONENTS, visualStudioWanted, } from '../guest/toolchains/native/vs-request.js';
9
+ import { collectDataEntries } from '../cli/services/data-collect.js';
10
+ const GB = 1024 ** 3;
11
+ export const persistedInstallsSuite = {
12
+ name: 'persisted-installs',
13
+ title: 'large persisted installs: staging, marker, room checks, pruning, records; the Visual Studio request',
14
+ run: async (t) => {
15
+ t.section('installing a version');
16
+ const tools = t.dir('persisted-tools');
17
+ const roomy = { toolsDir: tools, statfs: () => ({ bavail: 100 * GB, bsize: 1 }) };
18
+ const spec = {
19
+ name: 'engine',
20
+ version: '1.0',
21
+ needBytes: 2 * GB,
22
+ marker: 'bin/engine.exe',
23
+ what: 'Engine 1.0',
24
+ keepVersions: 2,
25
+ };
26
+ let seen = null;
27
+ const first = await ensurePersisted(spec, async (staging, temp) => {
28
+ seen = { staging, temp };
29
+ fs.writeFileSync(path.join(temp, 'download.part'), 'x'.repeat(10));
30
+ fs.mkdirSync(path.join(staging, 'bin'), { recursive: true });
31
+ fs.writeFileSync(path.join(staging, 'bin', 'engine.exe'), 'engine');
32
+ }, roomy);
33
+ const done = path.join(tools, 'engine', '1.0');
34
+ t.check(first.fresh &&
35
+ first.dir === done &&
36
+ fs.existsSync(path.join(done, '.complete.json')) &&
37
+ !fs.existsSync(path.join(done, '.temp')) &&
38
+ seen !== null &&
39
+ path.dirname(seen.staging) === path.join(tools, 'engine') &&
40
+ seen.temp.startsWith(seen.staging), 'it is built in a staging folder next to the target (TEMP inside it) and moved into place with its marker');
41
+ t.equal(first.sizeBytes, 'engine'.length, 'its size is counted once (the temporary files are gone)');
42
+ let ran = false;
43
+ const again = await ensurePersisted(spec, async () => {
44
+ ran = true;
45
+ }, roomy);
46
+ t.check(!again.fresh && !ran && again.sizeBytes === first.sizeBytes, 'a finished version is not installed again');
47
+ t.check(InstallRecordsSchema.safeParse(JSON.parse(fs.readFileSync(path.join(tools, 'installs.json'), 'utf8'))).success &&
48
+ installRecords(roomy).some((r) => r.name === 'engine' && r.version === '1.0' && r.sizeBytes === 6), 'tools\\installs.json records it with its size (what sandbox data shows)');
49
+ t.section('when something goes wrong');
50
+ await t.throws(() => ensurePersisted({ ...spec, version: '2.0' }, async () => undefined, roomy), 'without bin/engine.exe', 'an install that does not leave its marker fails');
51
+ t.check(!fs.existsSync(path.join(tools, 'engine', '2.0')) &&
52
+ !fs.readdirSync(path.join(tools, 'engine')).some((n) => n.startsWith('.staging')), 'and leaves nothing behind');
53
+ const short = { toolsDir: tools, statfs: () => ({ bavail: GB, bsize: 1 }) };
54
+ let installed = false;
55
+ await t.throws(() => ensurePersisted({ ...spec, version: '3.0' }, async () => {
56
+ installed = true;
57
+ }, short), 'it needs 2.0 GB', 'a disk with too little room refuses before anything is downloaded');
58
+ t.check(!installed, 'and the installer never ran');
59
+ await t.throws(() => ensurePersisted({ ...spec, version: '..' }, async () => undefined, roomy), 'not a usable', 'a version cannot climb out of tools\\');
60
+ await t.throws(() => Promise.resolve(assertLocalRoom(GB, 'Visual Studio', {
61
+ localDir: tools,
62
+ statfs: () => ({ bavail: LOCAL_RESERVE_BYTES, bsize: 1 }),
63
+ })), 'Visual Studio', "a real installer leaves 13 GB free on the sandbox's own disk");
64
+ t.section('keeping versions');
65
+ const make = (version) => ensurePersisted({ ...spec, version }, async (staging) => {
66
+ fs.mkdirSync(path.join(staging, 'bin'), { recursive: true });
67
+ fs.writeFileSync(path.join(staging, 'bin', 'engine.exe'), version);
68
+ }, roomy);
69
+ await make('1.1');
70
+ recordUse('engine', '1.0', roomy);
71
+ await make('1.2');
72
+ const left = fs.readdirSync(path.join(tools, 'engine')).sort();
73
+ t.equal(left, ['1.0', '1.2'], 'beyond keepVersions the least recently used version goes');
74
+ t.check(!installRecords(roomy).some((r) => r.version === '1.1'), 'and its record with it');
75
+ t.section('what sandbox data shows');
76
+ const data = t.dir('persisted-data');
77
+ const big = t.dir('persisted-data', 'tools', 'editor', '6000.0');
78
+ fs.writeFileSync(path.join(big, 'Unity.exe'), 'small here');
79
+ t.writeJson(path.join(data, 'tools', 'installs.json'), {
80
+ installs: [{ name: 'editor', version: '6000.0', sizeBytes: 7 * GB, installedAt: 'x', usedAt: 'x' }],
81
+ });
82
+ t.dir('persisted-data', 'tools', 'walked', '1.0');
83
+ const rows = collectDataEntries(data, t.dir('persisted-storage'));
84
+ t.check(rows.find((r) => r.name === 'editor')?.bytes === 7 * GB && rows.some((r) => r.name === 'walked'), 'a tool whose versions are all recorded shows the recorded size (not walked); the others are measured');
85
+ t.section('which Visual Studio');
86
+ const tc = structuredClone(DEFAULTS.toolchains);
87
+ t.equal(visualStudioWanted(tc), null, 'no toolchain that needs it: none');
88
+ tc.flutter.enabled = true;
89
+ tc.flutter.windowsDesktop = true;
90
+ const tools1 = visualStudioWanted(tc);
91
+ t.check(tools1?.product === 'buildtools' &&
92
+ BUILD_TOOLS_COMPONENTS.every((c) => tools1.components.includes(c)) &&
93
+ tools1.licence === 'toolchains.msvc.acceptLicense' &&
94
+ !tools1.accepted, "Flutter desktop: the Build Tools' C++ parts, under the MSVC licence setting");
95
+ tc.unity.enabled = true;
96
+ tc.unity.modules = ['windows-il2cpp'];
97
+ tc.unreal.enabled = true;
98
+ tc.unreal.acceptLicense = true;
99
+ tc.unreal.components = ['Microsoft.VisualStudio.Component.VC.Llvm.Clang'];
100
+ const all = visualStudioWanted(tc);
101
+ t.check(all?.product === 'community' &&
102
+ all.accepted &&
103
+ all.for.join() === 'unreal,flutter,unity' &&
104
+ UNREAL_COMPONENTS.every((c) => all.components.includes(c)) &&
105
+ all.components.includes('Microsoft.VisualStudio.Component.VC.Llvm.Clang'), 'with Unreal: one Community install serves all of them');
106
+ },
107
+ };
@@ -22,6 +22,7 @@ import { parseCli, parseSessionWant } from '../cli/args.js';
22
22
  import { severalAgentsArgv } from '../cli/menu/index.js';
23
23
  import { generateSandbox } from '../host/launch/index.js';
24
24
  import { describeSessions } from '../cli/commands/projects/index.js';
25
+ import { onFixedPc } from './support/pc-memory.js';
25
26
  /** The error `fn` throws, or null. */
26
27
  const caught = async (fn) => {
27
28
  try {
@@ -518,7 +519,8 @@ export const projectsSuite = {
518
519
  notRegistered &&
519
520
  (await joinGames('continue')).missing?.settings.join() === 'sandbox.vGpu', '"Restart with them" hands the restart offer what is missing, "Cancel" stops with a short line (neither registers the project), "Continue anyway" joins and says what is missing', cancelled);
520
521
  // The restart that adds it (and any launch with it mapped at boot) boots with the union.
521
- const unionGen = generateSandbox({
522
+ // On a pretend PC with plenty of RAM: the memory asked for is not lowered to what this machine can keep resident.
523
+ const unionGen = onFixedPc(() => generateSandbox({
522
524
  packageRoot: t.packageRoot,
523
525
  run: launchedRun,
524
526
  harnessArgs: [],
@@ -526,7 +528,7 @@ export const projectsSuite = {
526
528
  extraProjects: [games],
527
529
  autoSession: false,
528
530
  bootWants: { projects: [{ name: 'proj-games', config: gamesRun.config }], keepOff: [] },
529
- });
531
+ }));
530
532
  const unionWsb = fs.readFileSync(unionGen.wsbPath, 'utf8');
531
533
  t.check(unionWsb.includes('<vGPU>Enable</vGPU>') &&
532
534
  unionWsb.includes('<HostFolder>' +
@@ -536,7 +538,7 @@ export const projectsSuite = {
536
538
  const unionLearns = unionGen.widened['sandbox.vGpu'] === 'proj-games' &&
537
539
  unionGen.projects.notes.some((n) => n.startsWith('vGPU is on in this sandbox')) &&
538
540
  nothingMissing(missingAtBoot(protectionsOf(unionGen.bootConfig), { memoryMb: unionGen.memoryMb, mappings: unionGen.mappings }, gamesRun.config, fs.existsSync));
539
- const keptOff = generateSandbox({
541
+ const keptOff = onFixedPc(() => generateSandbox({
540
542
  packageRoot: t.packageRoot,
541
543
  run: launchedRun,
542
544
  harnessArgs: [],
@@ -544,7 +546,7 @@ export const projectsSuite = {
544
546
  extraProjects: [games],
545
547
  autoSession: false,
546
548
  bootWants: { projects: [{ name: 'proj-games', config: gamesRun.config }], keepOff: ['sandbox.vGpu'] },
547
- });
549
+ }));
548
550
  t.check(unionLearns && fs.readFileSync(keptOff.wsbPath, 'utf8').includes('<vGPU>Disable</vGPU>'), "the registry learns which setting was turned on for which project, after that restart nothing is missing; a device the launch folder's own file switched off stays off", JSON.stringify(unionGen.projects.notes));
549
551
  t.section('projects: wsb (Windows Sandbox command line) adapter');
550
552
  const id = '12345678-1234-1234-1234-1234567890ab';
@@ -0,0 +1,2 @@
1
+ import type { Suite } from './support/suite.js';
2
+ export declare const serviceAccountsSuite: Suite;