windows-development-sandbox 1.25.0 → 1.27.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 (287) hide show
  1. package/config.example.json +17 -5
  2. package/dist/app/features/accounts/services.def.d.ts +1 -1
  3. package/dist/app/features/accounts/services.def.js +1 -1
  4. package/dist/app/features/data/data.def.d.ts +4 -4
  5. package/dist/app/features/data/data.run.d.ts +1 -1
  6. package/dist/app/features/defs.d.ts +356 -6
  7. package/dist/app/features/defs.js +15 -0
  8. package/dist/app/features/projects/projects.def.d.ts +1 -1
  9. package/dist/app/features/projects/projects.run.d.ts +1 -1
  10. package/dist/app/features/settings/categories.def.js +1 -0
  11. package/dist/app/features/unity/index.d.ts +2 -0
  12. package/dist/app/features/unity/index.js +3 -0
  13. package/dist/app/features/unity/unity.def.d.ts +294 -0
  14. package/dist/app/features/unity/unity.def.js +471 -0
  15. package/dist/app/features/unity/unity.run.d.ts +142 -0
  16. package/dist/app/features/unity/unity.run.js +363 -0
  17. package/dist/app/features/unreal/index.d.ts +2 -0
  18. package/dist/app/features/unreal/index.js +3 -0
  19. package/dist/app/features/unreal/unreal.def.d.ts +236 -0
  20. package/dist/app/features/unreal/unreal.def.js +379 -0
  21. package/dist/app/features/unreal/unreal.run.d.ts +96 -0
  22. package/dist/app/features/unreal/unreal.run.js +217 -0
  23. package/dist/app/registry.js +11 -0
  24. package/dist/bin/agent-sandbox.js +2 -2
  25. package/dist/bin/{chunk-IZRGTSQ5.js → chunk-6WWMBCT2.js} +4632 -1941
  26. package/dist/bin/{chunk-SUYYVIDW.js → chunk-JXL6ZOO3.js} +126 -6
  27. package/dist/bin/{chunk-KH4MI2U5.js → chunk-ZXCAVVS4.js} +1 -1
  28. package/dist/bin/claude-host.js +2 -2
  29. package/dist/bin/claude-sbx.js +2 -2
  30. package/dist/bin/codex-sbx.js +2 -2
  31. package/dist/bin/gemini-sbx.js +2 -2
  32. package/dist/bin/sandbox.js +2 -2
  33. package/dist/bin/sbx-hide.js +2 -2
  34. package/dist/bin/sbx-show.js +2 -2
  35. package/dist/cli/commands/launch/launch.js +17 -2
  36. package/dist/cli/commands/registry.js +6 -0
  37. package/dist/cli/manifest.d.ts +51 -1
  38. package/dist/cli/manifest.js +58 -1
  39. package/dist/cli/menu/coverage-exempt.js +2 -0
  40. package/dist/cli/menu/pages/sandbox-page.d.ts +6 -0
  41. package/dist/cli/menu/pages/sandbox-page.js +71 -4
  42. package/dist/cli/menu/views.js +4 -0
  43. package/dist/core/config/defaults.js +33 -6
  44. package/dist/core/schema/accounts.d.ts +3 -0
  45. package/dist/core/schema/accounts.js +1 -0
  46. package/dist/core/schema/config.d.ts +32 -14
  47. package/dist/core/schema/github.d.ts +1 -0
  48. package/dist/core/schema/github.js +3 -0
  49. package/dist/core/schema/pc-installs.d.ts +5 -2
  50. package/dist/core/schema/pc-installs.js +16 -2
  51. package/dist/core/schema/toolchains.d.ts +8 -0
  52. package/dist/core/schema/toolchains.js +20 -5
  53. package/dist/guest/accounts/adapters.js +2 -0
  54. package/dist/guest/accounts/apply.d.ts +4 -2
  55. package/dist/guest/accounts/kinds/epic.js +18 -13
  56. package/dist/guest/accounts/kinds/perplexityapp.d.ts +7 -0
  57. package/dist/guest/accounts/kinds/perplexityapp.js +53 -0
  58. package/dist/guest/accounts/kinds/unity.d.ts +27 -0
  59. package/dist/guest/accounts/kinds/unity.js +164 -7
  60. package/dist/guest/apps/after-install.d.ts +1 -1
  61. package/dist/guest/apps/after-install.js +6 -1
  62. package/dist/guest/apps/app-installer.d.ts +1 -0
  63. package/dist/guest/apps/app-installer.js +31 -3
  64. package/dist/guest/apps/comet-unpack.js +3 -2
  65. package/dist/guest/apps/resolvers-gamedev.js +55 -1
  66. package/dist/guest/apps/store-app.d.ts +4 -0
  67. package/dist/guest/apps/store-app.js +19 -0
  68. package/dist/guest/apps/store-check.d.ts +8 -0
  69. package/dist/guest/apps/store-check.js +33 -0
  70. package/dist/guest/boot/boot.js +15 -2
  71. package/dist/guest/boot/guide.js +42 -0
  72. package/dist/guest/browser/comet.d.ts +3 -1
  73. package/dist/guest/browser/comet.js +1 -1
  74. package/dist/guest/browser/perplexity-app.d.ts +12 -0
  75. package/dist/guest/browser/perplexity-app.js +60 -0
  76. package/dist/guest/daemon/daemon.js +5 -0
  77. package/dist/guest/devtools/devtool.d.ts +1 -1
  78. package/dist/guest/devtools/registry.js +1 -2
  79. package/dist/guest/entries/tool.js +1 -1
  80. package/dist/guest/setup/addon-mcp.d.ts +12 -0
  81. package/dist/guest/setup/addon-mcp.js +29 -4
  82. package/dist/guest/setup/browser-gpu.js +14 -11
  83. package/dist/guest/setup/github-token.js +4 -4
  84. package/dist/guest/setup/headroom.js +4 -0
  85. package/dist/guest/setup/unity-agent.d.ts +11 -0
  86. package/dist/guest/setup/unity-agent.js +36 -0
  87. package/dist/guest/toolchains/engines/epic-launcher.d.ts +26 -0
  88. package/dist/guest/toolchains/engines/epic-launcher.js +127 -0
  89. package/dist/guest/toolchains/engines/unity-cli.d.ts +24 -0
  90. package/dist/guest/toolchains/engines/unity-cli.js +74 -0
  91. package/dist/guest/toolchains/engines/unity-hub.d.ts +2 -0
  92. package/dist/guest/toolchains/engines/unity-hub.js +27 -0
  93. package/dist/guest/toolchains/engines/unity-release.d.ts +4 -15
  94. package/dist/guest/toolchains/engines/unity-release.js +17 -5
  95. package/dist/guest/toolchains/engines/unity-status.d.ts +11 -0
  96. package/dist/guest/toolchains/engines/unity-status.js +39 -0
  97. package/dist/guest/toolchains/engines/unity.d.ts +20 -1
  98. package/dist/guest/toolchains/engines/unity.js +250 -3
  99. package/dist/guest/toolchains/engines/unreal-engines.d.ts +21 -1
  100. package/dist/guest/toolchains/engines/unreal-engines.js +163 -3
  101. package/dist/guest/toolchains/engines/unreal-status.d.ts +13 -0
  102. package/dist/guest/toolchains/engines/unreal-status.js +41 -0
  103. package/dist/guest/toolchains/native/debuggers.d.ts +6 -0
  104. package/dist/guest/toolchains/native/debuggers.js +57 -0
  105. package/dist/guest/toolchains/native/mesa.js +15 -8
  106. package/dist/guest/toolchains/native/rust-extras.d.ts +7 -0
  107. package/dist/guest/toolchains/native/rust-extras.js +23 -1
  108. package/dist/guest/toolchains/native/unreal.d.ts +2 -2
  109. package/dist/guest/toolchains/native/unreal.js +96 -18
  110. package/dist/guest/toolchains/plan.js +17 -2
  111. package/dist/guest/toolchains/tools/cli.js +2 -2
  112. package/dist/guest/toolchains/tools/vscode-profile.js +2 -0
  113. package/dist/host/accounts/deliver.js +4 -0
  114. package/dist/host/accounts/kinds/epic.js +11 -5
  115. package/dist/host/accounts/kinds/perplexityapp.d.ts +2 -0
  116. package/dist/host/accounts/kinds/perplexityapp.js +8 -0
  117. package/dist/host/accounts/kinds/unity.d.ts +1 -1
  118. package/dist/host/accounts/kinds/unity.js +60 -8
  119. package/dist/host/accounts/leases.d.ts +2 -1
  120. package/dist/host/accounts/source.d.ts +2 -0
  121. package/dist/host/accounts/source.js +1 -0
  122. package/dist/host/accounts/sources.js +2 -0
  123. package/dist/host/apps/index.d.ts +1 -0
  124. package/dist/host/apps/index.js +2 -0
  125. package/dist/host/apps/store-package.d.ts +51 -0
  126. package/dist/host/apps/store-package.js +209 -0
  127. package/dist/host/github/deliver.d.ts +8 -3
  128. package/dist/host/github/deliver.js +42 -29
  129. package/dist/host/github/index.d.ts +1 -0
  130. package/dist/host/github/index.js +1 -0
  131. package/dist/host/github/pc-login.d.ts +27 -0
  132. package/dist/host/github/pc-login.js +129 -0
  133. package/dist/host/pc-installs/discover.d.ts +6 -0
  134. package/dist/host/pc-installs/discover.js +37 -5
  135. package/dist/host/pc-installs/epic.d.ts +3 -0
  136. package/dist/host/pc-installs/epic.js +33 -2
  137. package/dist/host/pc-installs/source.d.ts +1 -1
  138. package/dist/host/pc-installs/unity-assets.d.ts +5 -0
  139. package/dist/host/pc-installs/unity-assets.js +18 -0
  140. package/dist/host/pc-installs/unity.d.ts +20 -0
  141. package/dist/host/pc-installs/unity.js +135 -2
  142. package/dist/host/pc-installs/unreal-vault.d.ts +11 -0
  143. package/dist/host/pc-installs/unreal-vault.js +78 -0
  144. package/dist/host/pc-installs/unreal.d.ts +36 -0
  145. package/dist/host/pc-installs/unreal.js +145 -2
  146. package/dist/host/profile/claude-profile.d.ts +8 -0
  147. package/dist/host/profile/claude-profile.js +43 -4
  148. package/dist/host/profile/index.d.ts +1 -1
  149. package/dist/host/profile/index.js +1 -1
  150. package/dist/runtime/agent.js +197 -18
  151. package/dist/runtime/daemon.js +3938 -1386
  152. package/dist/runtime/db.js +197 -18
  153. package/dist/runtime/main.js +4336 -1978
  154. package/dist/runtime/netproxy.js +197 -18
  155. package/dist/runtime/tool.js +214 -38
  156. package/dist/runtime/view.js +197 -18
  157. package/dist/selftest/agent-paths.test.js +1 -0
  158. package/dist/selftest/agents-web.test.js +14 -1
  159. package/dist/selftest/engines.test.js +602 -3
  160. package/dist/selftest/github.test.js +64 -1
  161. package/dist/selftest/heavy-load.test.js +3 -1
  162. package/dist/selftest/hostile-input.test.js +11 -1
  163. package/dist/selftest/index.js +12 -0
  164. package/dist/selftest/menu-screens.js +5 -1
  165. package/dist/selftest/mixamo.test.d.ts +2 -0
  166. package/dist/selftest/mixamo.test.js +60 -0
  167. package/dist/selftest/persisted-installs.test.js +1 -1
  168. package/dist/selftest/secrets.test.js +36 -1
  169. package/dist/selftest/service-accounts.test.js +1 -1
  170. package/dist/selftest/store-app.test.d.ts +2 -0
  171. package/dist/selftest/store-app.test.js +162 -0
  172. package/dist/selftest/support/engine-golden.js +10 -0
  173. package/dist/selftest/support/fixtures.d.ts +13 -2
  174. package/dist/selftest/support/unity-sample.d.ts +2 -0
  175. package/dist/selftest/support/unity-sample.js +80 -0
  176. package/dist/selftest/support/unreal-sample.d.ts +2 -0
  177. package/dist/selftest/support/unreal-sample.js +83 -0
  178. package/dist/selftest/toolchains.test.js +32 -2
  179. package/dist/selftest/unity-accounts.test.d.ts +2 -0
  180. package/dist/selftest/unity-accounts.test.js +106 -0
  181. package/dist/selftest/unity-agent.test.d.ts +2 -0
  182. package/dist/selftest/unity-agent.test.js +47 -0
  183. package/dist/selftest/unity-page.test.d.ts +2 -0
  184. package/dist/selftest/unity-page.test.js +177 -0
  185. package/dist/selftest/unity-setup.test.d.ts +2 -0
  186. package/dist/selftest/unity-setup.test.js +49 -0
  187. package/dist/selftest/unity.test.js +238 -3
  188. package/dist/shared/accounts/index.d.ts +2 -1
  189. package/dist/shared/accounts/index.js +2 -1
  190. package/dist/shared/accounts/kinds/epic.d.ts +6 -1
  191. package/dist/shared/accounts/kinds/epic.js +11 -1
  192. package/dist/shared/accounts/kinds/unity.d.ts +28 -0
  193. package/dist/shared/accounts/kinds/unity.js +88 -0
  194. package/dist/shared/accounts/kinds.d.ts +2 -2
  195. package/dist/shared/accounts/kinds.js +23 -1
  196. package/dist/shared/accounts/payloads.d.ts +52 -0
  197. package/dist/shared/accounts/payloads.js +14 -2
  198. package/dist/shared/apps/associations.d.ts +7 -0
  199. package/dist/shared/apps/associations.js +26 -0
  200. package/dist/shared/apps/catalogue-agents-web.d.ts +11 -1
  201. package/dist/shared/apps/catalogue-agents-web.js +28 -2
  202. package/dist/shared/apps/catalogue-gamedev.d.ts +3 -0
  203. package/dist/shared/apps/catalogue-gamedev.js +102 -1
  204. package/dist/shared/apps/catalogue.d.ts +8 -0
  205. package/dist/shared/apps/index.d.ts +5 -2
  206. package/dist/shared/apps/index.js +5 -2
  207. package/dist/shared/apps/preview.d.ts +2 -0
  208. package/dist/shared/apps/preview.js +10 -0
  209. package/dist/shared/apps/selection.d.ts +6 -0
  210. package/dist/shared/apps/selection.js +9 -0
  211. package/dist/shared/apps/store-package.d.ts +31 -0
  212. package/dist/shared/apps/store-package.js +34 -0
  213. package/dist/shared/browser-policy.d.ts +14 -0
  214. package/dist/shared/browser-policy.js +22 -0
  215. package/dist/shared/channel/schema.d.ts +24 -0
  216. package/dist/shared/channel/secrets.d.ts +17 -0
  217. package/dist/shared/io/index.d.ts +1 -0
  218. package/dist/shared/io/index.js +1 -0
  219. package/dist/shared/io/rotate-log.d.ts +2 -0
  220. package/dist/shared/io/rotate-log.js +16 -0
  221. package/dist/shared/memory/policy.d.ts +5 -0
  222. package/dist/shared/memory/policy.js +7 -0
  223. package/dist/shared/paths/pc-installs.d.ts +13 -2
  224. package/dist/shared/paths/pc-installs.js +2 -0
  225. package/dist/shared/readiness.d.ts +2 -3
  226. package/dist/shared/readiness.js +2 -3
  227. package/dist/shared/signer.d.ts +4 -0
  228. package/dist/shared/signer.js +2 -0
  229. package/dist/shared/unity/asset-cache.d.ts +20 -0
  230. package/dist/shared/unity/asset-cache.js +44 -0
  231. package/dist/shared/unity/cli.d.ts +40 -0
  232. package/dist/shared/unity/cli.js +77 -0
  233. package/dist/shared/unity/editors.d.ts +46 -0
  234. package/dist/shared/unity/editors.js +89 -0
  235. package/dist/shared/unity/guide.d.ts +9 -0
  236. package/dist/shared/unity/guide.js +84 -0
  237. package/dist/shared/unity/index.d.ts +9 -0
  238. package/dist/shared/unity/index.js +11 -0
  239. package/dist/shared/unity/manifest.d.ts +10 -0
  240. package/dist/shared/unity/manifest.js +53 -0
  241. package/dist/shared/unity/mcp.d.ts +8 -0
  242. package/dist/shared/unity/mcp.js +12 -0
  243. package/dist/shared/unity/release.d.ts +28 -0
  244. package/dist/shared/unity/release.js +83 -0
  245. package/dist/shared/unity/status.d.ts +59 -0
  246. package/dist/shared/unity/status.js +45 -0
  247. package/dist/shared/unity/version.d.ts +27 -0
  248. package/dist/shared/unity/version.js +75 -0
  249. package/dist/shared/unreal/engines.d.ts +161 -0
  250. package/dist/shared/unreal/engines.js +308 -0
  251. package/dist/shared/unreal/guide.d.ts +19 -0
  252. package/dist/shared/unreal/guide.js +105 -0
  253. package/dist/shared/unreal/index.d.ts +7 -0
  254. package/dist/shared/unreal/index.js +8 -0
  255. package/dist/shared/unreal/launcher-files.d.ts +88 -0
  256. package/dist/shared/unreal/launcher-files.js +292 -0
  257. package/dist/shared/unreal/mixamo.d.ts +5 -0
  258. package/dist/shared/unreal/mixamo.js +16 -0
  259. package/dist/shared/unreal/room.d.ts +17 -0
  260. package/dist/shared/unreal/room.js +34 -0
  261. package/dist/shared/unreal/status.d.ts +54 -0
  262. package/dist/shared/unreal/status.js +57 -0
  263. package/dist/shared/unreal/vault.d.ts +21 -0
  264. package/dist/shared/unreal/vault.js +67 -0
  265. package/dist/shared/unreal/vs-check.d.ts +39 -0
  266. package/dist/shared/unreal/vs-check.js +79 -0
  267. package/dist/web/index.html +1 -1
  268. package/dist/web/{main-CCILLX2M.js → main-VPMYTSUL.js} +87 -87
  269. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  270. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  271. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  272. package/native/scripts/get-store-package.ps1 +58 -0
  273. package/package.json +1 -1
  274. package/sandbox/addons/openupm-cli/package-lock.json +2921 -0
  275. package/sandbox/addons/openupm-cli/package.json +7 -0
  276. package/sandbox/addons/unity-mcp/requirements.in +1 -0
  277. package/sandbox/addons/unity-mcp/requirements.txt +1165 -0
  278. package/sandbox/csharp/AppActivation.cs +38 -0
  279. package/sandbox/csharp/VsSetup.cs +210 -0
  280. package/sandbox/scripts/get-vs-setup.ps1 +93 -0
  281. package/sandbox/scripts/install-store-package.ps1 +28 -0
  282. package/sandbox/scripts/set-registry-values.ps1 +35 -0
  283. package/sandbox/scripts/start-store-app.ps1 +29 -0
  284. package/schemas/config.schema.json +96 -8
  285. package/schemas/sandbox-config.schema.json +46 -6
  286. package/dist/guest/devtools/unity.d.ts +0 -2
  287. package/dist/guest/devtools/unity.js +0 -7
@@ -5,7 +5,8 @@ import { SYSTEM } from '../../../shared/system.js';
5
5
  import { dryRun } from '../../runtime/paths.js';
6
6
  import { execAsync, scriptOutput } from '../../runtime/process.js';
7
7
  import { exists } from '../../runtime/fs.js';
8
- import { log } from '../../runtime/log.js';
8
+ import { log, warn } from '../../runtime/log.js';
9
+ import { errMsg } from '../../../core/util/errors.js';
9
10
  import { arch, tool } from '../tool-env.js';
10
11
  import { installFromGithub } from '../install.js';
11
12
  /**
@@ -33,6 +34,8 @@ export async function setupMesa(env, config) {
33
34
  await setupLavapipe(env, dir);
34
35
  }
35
36
  const VULKAN_RUNTIME = 'https://sdk.lunarg.com/sdk/download/latest/windows/vulkan-runtime-components.zip';
37
+ /** Used when "latest" is not published yet (LunarG announces a version before its runtime zip is up: a 404). */
38
+ const VULKAN_RUNTIME_PINNED = 'https://sdk.lunarg.com/sdk/download/1.4.350.0/windows/vulkan-runtime-components.zip';
36
39
  /**
37
40
  * Software Vulkan: Mesa's lavapipe plus the Khronos loader (vulkan-1.dll, from LunarG's runtime components; Windows
38
41
  * Sandbox has no Vulkan loader). The loader ignores VK_DRIVER_FILES in elevated processes, and everything in the
@@ -43,15 +46,19 @@ const setupLavapipe = async (env, mesaDir) => {
43
46
  if (!exists(icd))
44
47
  throw new Error('this Mesa build has no lavapipe (' + path.basename(icd) + ')');
45
48
  const dir = tool('vulkan');
46
- if (!exists(path.join(dir, 'vulkan-1.dll'))) {
47
- log('Downloading the Vulkan loader (~10 MB once) ...');
49
+ // vulkaninfo.exe too: a copy kept from an older runtime zip may lack it.
50
+ if (!exists(path.join(dir, 'vulkan-1.dll')) || !exists(path.join(dir, 'vulkaninfo.exe'))) {
51
+ log('Downloading the Vulkan loader (~20 MB once) ...');
48
52
  // LunarG publishes no checksum for this zip; it comes over HTTPS from LunarG, the Khronos SDK's maintainer.
49
53
  // The zip has x86 and x64 folders: only the 64-bit loader (and vulkaninfo) is extracted.
50
- await downloader.archive(VULKAN_RUNTIME, dir, {
51
- locate: 'vulkan-1.dll',
52
- fileName: 'vulkan-runtime.zip',
53
- members: ['*/x64/*'],
54
- });
54
+ const fetchRuntime = (url) => downloader.archive(url, dir, { locate: 'vulkan-1.dll', fileName: 'vulkan-runtime.zip', members: ['*/x64/*'] });
55
+ try {
56
+ await fetchRuntime(VULKAN_RUNTIME);
57
+ }
58
+ catch (e) {
59
+ warn('The latest Vulkan runtime could not be downloaded (' + errMsg(e) + '); using version 1.4.350.0.');
60
+ await fetchRuntime(VULKAN_RUNTIME_PINNED);
61
+ }
55
62
  }
56
63
  const register = (file) => execAsync(SYSTEM.reg(), ['add', 'HKLM\\SOFTWARE\\Khronos\\Vulkan\\Drivers', '/v', file, '/t', 'REG_DWORD', '/d', '0', '/f'], { stdio: 'ignore' });
57
64
  await register(icd);
@@ -14,6 +14,13 @@ export declare function cargoShim(env: ToolEnv, rust: Toolchains['rust']): void;
14
14
  * run at once in the whole sandbox and, with linker "lld", has MinGW's gcc use Rust's own lld instead of GNU ld.
15
15
  */
16
16
  export declare function linkGate(env: ToolEnv, rust: Toolchains['rust'], host: string): void;
17
+ /**
18
+ * MSVC host: link.exe writes an executable into a mapped folder (the workspace, C:\AgentData) as all zeros, so a
19
+ * fresh build script or test program fails with "The file or directory is corrupted and unreadable" (os error 1392).
20
+ * Rust's own lld (a copy of rust-lld.exe named lld-link.exe, which is how lld picks its mode) writes them whole.
21
+ * `rust.linker` "gnu" keeps link.exe; a linker the user set for the target wins.
22
+ */
23
+ export declare function lldLink(env: ToolEnv, rust: Toolchains['rust'], host: string): void;
17
24
  /**
18
25
  * Cargo defaults that fit Windows, written only when there is no $CARGO_HOME\config.toml yet: fewer parallel jobs
19
26
  * (parallel rustc ran out of paging file) and, for the GNU host, only line tables as debug info (full debug info made
@@ -7,7 +7,7 @@ import { execOutput } from '../../runtime/process.js';
7
7
  import { ensureCompiled } from '../../runtime/csc.js';
8
8
  import { arch, tool } from '../tool-env.js';
9
9
  import { wanted } from '../detect.js';
10
- import { cargoJobs, chooseTargetDir } from '../../../shared/memory/index.js';
10
+ import { cargoJobs, cargoLinkerVar, chooseTargetDir } from '../../../shared/memory/index.js';
11
11
  import { linkerSlots } from '../../governor/decide.js';
12
12
  import { warn } from '../../runtime/log.js';
13
13
  import { freeBytes } from '../persisted.js';
@@ -81,6 +81,28 @@ export function linkGate(env, rust, host) {
81
81
  env.vars.AGENT_LINK_SLOTS +
82
82
  ' at once');
83
83
  }
84
+ /**
85
+ * MSVC host: link.exe writes an executable into a mapped folder (the workspace, C:\AgentData) as all zeros, so a
86
+ * fresh build script or test program fails with "The file or directory is corrupted and unreadable" (os error 1392).
87
+ * Rust's own lld (a copy of rust-lld.exe named lld-link.exe, which is how lld picks its mode) writes them whole.
88
+ * `rust.linker` "gnu" keeps link.exe; a linker the user set for the target wins.
89
+ */
90
+ export function lldLink(env, rust, host) {
91
+ if (!host.endsWith('-pc-windows-msvc') || rust.linker !== 'lld')
92
+ return;
93
+ const variable = cargoLinkerVar(host);
94
+ if (process.env[variable])
95
+ return;
96
+ const rustEnv = { ...process.env, RUSTUP_HOME: tool('rust', 'rustup'), CARGO_HOME: tool('rust', 'cargo') };
97
+ const sysroot = execOutput(tool('rust', 'cargo', 'bin', 'rustc.exe'), ['--print', 'sysroot'], { env: rustEnv });
98
+ const lld = sysroot ? path.join(sysroot, 'lib', 'rustlib', host, 'bin', 'rust-lld.exe') : '';
99
+ if (!lld || !exists(lld))
100
+ return;
101
+ const shim = path.join(mkdirp(tool('shims')), 'lld-link.exe');
102
+ fs.copyFileSync(lld, shim);
103
+ env.vars[variable] = shim;
104
+ env.summary.push('rust linking: lld (link.exe writes empty files into mapped folders)');
105
+ }
84
106
  /**
85
107
  * Cargo defaults that fit Windows, written only when there is no $CARGO_HOME\config.toml yet: fewer parallel jobs
86
108
  * (parallel rustc ran out of paging file) and, for the GNU host, only line tables as debug info (full debug info made
@@ -2,6 +2,6 @@ import type { Toolchains } from '../../../core/types.js';
2
2
  import type { ToolCtx } from '../step.js';
3
3
  import { type ToolEnv } from '../tool-env.js';
4
4
  export { UNREAL_COMPONENTS } from './vs-request.js';
5
- /** Downloads the DirectX redistributable ahead of the install (only once the licence is accepted). */
5
+ /** Downloads the DirectX redistributable and the launcher ahead of the install (only once the licence is accepted). */
6
6
  export declare function fetchDirectX(config: Toolchains['unreal']): Promise<void>;
7
- export declare function setupUnreal(env: ToolEnv, config: Toolchains['unreal'], ctx: ToolCtx): Promise<void>;
7
+ export declare function setupUnreal(env: ToolEnv, config: Toolchains['unreal'], _ctx: ToolCtx): Promise<void>;
@@ -1,18 +1,25 @@
1
- // Unreal Engine development (toolchains.unreal): the DirectX End-User Runtime the engine lists (a real installer: it
2
- // runs every boot), then the engines (engines/unreal-engines.ts: the launcher's install folder on the data disk, PC
3
- // engines from pcInstalls, the derived data cache). Visual Studio (Community with the C++ game workload, or the Build
4
- // Tools) is its own step (visual-studio.ts), which the plan runs first. The engine itself comes from the Epic Games
5
- // Launcher or its source.
1
+ // Unreal Engine development, machine-wide (toolchains.unreal, once per boot): the DirectX End-User Runtime the engine
2
+ // lists and the Epic Games Launcher (real installers: they run every boot), the launcher pointed at the data disk for
3
+ // engines and given back the engines it installed there, the Windows SDK debuggers for crash dumps, and a check that
4
+ // UnrealBuildTool finds Visual Studio the way it looks (the Setup Configuration API). Visual Studio itself is its own
5
+ // step (visual-studio.ts), which the plan runs first; each project's engine is set up by engines/unreal-engines.ts.
6
+ import fs from 'node:fs';
6
7
  import os from 'node:os';
7
8
  import path from 'node:path';
8
- import { runInstaller } from '../../runtime/pools.js';
9
+ import { errMsg } from '../../../core/util/errors.js';
10
+ import { readValid } from '../../../shared/io/json.js';
9
11
  import { safeRemove } from '../../../shared/io/safe-remove.js';
12
+ import { VsSetupReportSchema, vsVerdict } from '../../../shared/unreal/index.js';
13
+ import { runInstaller } from '../../runtime/pools.js';
10
14
  import { dryRun } from '../../runtime/paths.js';
11
15
  import { exists, mkdirp } from '../../runtime/fs.js';
12
- import { log } from '../../runtime/log.js';
13
- import { setupUnrealEngines } from '../engines/unreal-engines.js';
16
+ import { log, warn } from '../../runtime/log.js';
17
+ import { runScriptAsync } from '../../runtime/process.js';
18
+ import { ensureLauncher, fetchLauncher, placeEngines, restoreLauncherItems } from '../engines/epic-launcher.js';
19
+ import { resetUnrealStatus, updateUnrealStatus } from '../engines/unreal-status.js';
14
20
  import { SIGNERS, signedInstaller } from '../installers.js';
15
21
  import { tool } from '../tool-env.js';
22
+ import { ensureDebuggers } from './debuggers.js';
16
23
  import { installedVs } from './visual-studio.js';
17
24
  export { UNREAL_COMPONENTS } from './vs-request.js';
18
25
  const DIRECTX_URL = 'https://download.microsoft.com/download/8/4/A/84A35BF1-DAFE-4AE8-82AF-AD2AE20B6B14/directx_Jun2010_redist.exe';
@@ -23,10 +30,13 @@ const directXInstalled = () => {
23
30
  };
24
31
  /** The redistributable (downloaded once, signature checked). */
25
32
  const directXRedist = () => signedInstaller(DIRECTX_URL, path.join(tool('visualstudio'), 'directx_Jun2010_redist.exe'), 'the DirectX End-User Runtime (~95 MB once)', SIGNERS.microsoft);
26
- /** Downloads the DirectX redistributable ahead of the install (only once the licence is accepted). */
33
+ /** Downloads the DirectX redistributable and the launcher ahead of the install (only once the licence is accepted). */
27
34
  export async function fetchDirectX(config) {
28
- if (config.acceptLicense && !directXInstalled())
35
+ if (!config.acceptLicense)
36
+ return;
37
+ if (!directXInstalled())
29
38
  await directXRedist();
39
+ await fetchLauncher();
30
40
  }
31
41
  async function installDirectX() {
32
42
  if (directXInstalled())
@@ -42,17 +52,85 @@ async function installDirectX() {
42
52
  if (code !== 0)
43
53
  throw new Error('the DirectX End-User Runtime installer failed (exit ' + code + ')');
44
54
  }
45
- export async function setupUnreal(env, config, ctx) {
55
+ /** What UnrealBuildTool's way of finding Visual Studio finds (scripts\get-vs-setup.ps1 calls the Setup API). */
56
+ async function checkVisualStudio() {
57
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'sbx-vs-check-'));
58
+ const file = path.join(dir, 'result.json');
59
+ try {
60
+ await runScriptAsync('get-vs-setup.ps1', ['-ResultFile', file]);
61
+ const report = readValid(VsSetupReportSchema, file);
62
+ return report ? vsVerdict(report) : { ok: false, text: 'the check left no result' };
63
+ }
64
+ catch (e) {
65
+ return { ok: false, text: 'the check failed: ' + errMsg(e) };
66
+ }
67
+ finally {
68
+ fs.rmSync(dir, { recursive: true, force: true });
69
+ }
70
+ }
71
+ /** Runs one part, warning (not failing the step) when it does not work. */
72
+ async function part(what, work, fallback) {
73
+ try {
74
+ return await work();
75
+ }
76
+ catch (e) {
77
+ warn('Unreal: ' + what + ' failed (' + errMsg(e) + ')');
78
+ return fallback;
79
+ }
80
+ }
81
+ export async function setupUnreal(env, config, _ctx) {
46
82
  if (!config.acceptLicense)
47
- throw new Error('Unreal tools are installed only with toolchains.unreal.acceptLicense = true: Visual Studio Community and the DirectX End-User Runtime come under Microsoft licence terms (https://visualstudio.microsoft.com/license-terms/)');
83
+ throw new Error("Unreal tools are installed only with toolchains.unreal.acceptLicense = true: Visual Studio Community and the DirectX End-User Runtime come under Microsoft licence terms (https://visualstudio.microsoft.com/license-terms/), the Epic Games Launcher under Epic's (https://www.epicgames.com/site/eula)");
48
84
  if (dryRun) {
49
- log('(dry run) would install the DirectX End-User Runtime and set up the Unreal engines');
85
+ log('(dry run) would install the DirectX End-User Runtime and the Epic Games Launcher');
50
86
  return;
51
87
  }
52
- await installDirectX();
53
- await setupUnrealEngines(env, config, ctx);
88
+ resetUnrealStatus();
89
+ await part('the DirectX End-User Runtime', installDirectX, undefined);
90
+ const launcher = await part('the Epic Games Launcher', ensureLauncher, null);
91
+ // Before anyone opens the launcher: engines go to the data disk, and it knows the ones it installed there before.
92
+ const place = placeEngines(config.persistEngines);
93
+ if (config.persistEngines)
94
+ await part("restoring the launcher's engines", async () => restoreLauncherItems(), []);
95
+ if (place.problem) {
96
+ warn('Unreal: ' + place.problem);
97
+ env.summary.push('UNREAL: ' + place.problem);
98
+ }
99
+ const debuggers = await part('the Windows SDK debuggers', ensureDebuggers, null);
100
+ if (debuggers)
101
+ env.append.push(debuggers);
54
102
  const vs = installedVs();
55
- env.summary.push('unreal: ' +
56
- (vs ? 'Visual Studio ' + vs.version + ' at ' + vs.path + '; ' : '') +
57
- 'DirectX End-User Runtime; get the engine from the Epic Games Launcher (Start menu)');
103
+ const check = vs ? await checkVisualStudio() : { ok: false, text: 'no Visual Studio with the C++ tools' };
104
+ if (!check.ok)
105
+ warn('Unreal: UnrealBuildTool would not find a compiler: ' + check.text);
106
+ else
107
+ log('Unreal: UnrealBuildTool finds ' + check.text);
108
+ const kept = Boolean(vs && /\\visualstudio\\persist\\/i.test(vs.path + '\\'));
109
+ updateUnrealStatus((s) => ({
110
+ ...s,
111
+ launcher: {
112
+ installed: launcher !== null,
113
+ installLocation: place.installLocation,
114
+ problem: place.problem,
115
+ freeBytes: place.freeBytes,
116
+ needBytes: place.needBytes,
117
+ },
118
+ visualStudio: {
119
+ version: vs?.version ?? '',
120
+ path: vs?.path ?? '',
121
+ how: vs ? (kept ? 'kept on the data disk' : 'installed in this sandbox') : '',
122
+ check: check.text,
123
+ ok: check.ok,
124
+ },
125
+ debugger: debuggers ? path.join(debuggers, 'cdb.exe') : null,
126
+ }));
127
+ env.summary.push('unreal tools: ' +
128
+ (vs
129
+ ? 'Visual Studio ' +
130
+ vs.version +
131
+ (check.ok ? ' (UnrealBuildTool finds it)' : ' (UnrealBuildTool would NOT find it)')
132
+ : 'no Visual Studio') +
133
+ ', DirectX End-User Runtime' +
134
+ (launcher ? ', Epic Games Launcher' : '') +
135
+ (debuggers ? ', cdb/windbg' : ''));
58
136
  }
@@ -12,6 +12,7 @@ import { setupDotnet } from './native/dotnet.js';
12
12
  import { setupMesa } from './native/mesa.js';
13
13
  import { setupMsvc } from './native/msvc/index.js';
14
14
  import { fetchDirectX, setupUnreal } from './native/unreal.js';
15
+ import { setupUnrealEngines } from './engines/unreal-engines.js';
15
16
  import { fetchRustupInit, setupRust } from './native/rust.js';
16
17
  import { setupNode } from './languages/node.js';
17
18
  import { setupPwsh } from './tools/pwsh.js';
@@ -25,14 +26,15 @@ import { ensureVisualStudio, prefetchVisualStudio, visualStudioSaved, visualStud
25
26
  import { duringSession } from '../runtime/project-context.js';
26
27
  import { setupSteam } from './steam/steam.js';
27
28
  import { setupGodot } from './engines/godot.js';
28
- import { setupUnity } from './engines/unity.js';
29
+ import { setupUnity, unityEditorSaved } from './engines/unity.js';
30
+ import { prefetchUnityCli } from './engines/unity-cli.js';
29
31
  import { setupFivem } from './fivem/fivem.js';
30
32
  import { setupAndroid } from './mobile/android.js';
31
33
  import { setupFlutter } from './mobile/flutter.js';
32
34
  import { setupDart } from './mobile/dart.js';
33
35
  import { setupReactNative } from './mobile/react-native.js';
34
36
  import { setupKotlin } from './languages/kotlin.js';
35
- import { rustHost, rustCaches, cargoDefaults, cargoShim, linkGate, resolveTargetDir, targetSummary, } from './native/rust-extras.js';
37
+ import { rustHost, rustCaches, cargoDefaults, cargoShim, linkGate, lldLink, resolveTargetDir, targetSummary, } from './native/rust-extras.js';
36
38
  import { setupPythonTools } from './languages/python-tools.js';
37
39
  export function plan(config, versions, o) {
38
40
  const apps = Boolean(o.apps && (o.apps.install.length || Object.keys(o.apps.custom).length));
@@ -87,6 +89,14 @@ export function plan(config, versions, o) {
87
89
  ...ahead(() => fetchDirectX(config.unreal)),
88
90
  install: (env) => setupUnreal(env, config.unreal, ctx),
89
91
  },
92
+ // Each project's engine (its .uproject's EngineAssociation) on its PATH: not shared, a project that joins later
93
+ // gets its own. It registers every engine too, so this PC's engines work without the licence step above.
94
+ {
95
+ name: 'Unreal engines',
96
+ enabled: unreal,
97
+ after: ['Unreal'],
98
+ install: (env) => setupUnrealEngines(env, config.unreal, ctx),
99
+ },
90
100
  {
91
101
  name: 'MSVC',
92
102
  enabled: wanted(config.msvc.enabled, 'msvc') && vs === null,
@@ -108,6 +118,8 @@ export function plan(config, versions, o) {
108
118
  cargoDefaults(rustHost(config));
109
119
  cargoShim(env, rust);
110
120
  linkGate(env, rust, rustHost(config));
121
+ lldLink(env, rust, rustHost(config));
122
+ lldLink(env, rust, rustHost(config));
111
123
  }
112
124
  },
113
125
  },
@@ -135,6 +147,9 @@ export function plan(config, versions, o) {
135
147
  name: 'Unity',
136
148
  enabled: wanted(config.unity.enabled, 'unity'),
137
149
  after: ['Visual Studio', 'Android SDK'],
150
+ ...ahead(() => prefetchUnityCli(config.unity.cliVersion)),
151
+ // Only the first install of an editor (several GB) goes to the background; a saved one is back in seconds.
152
+ background: deferred && !unityEditorSaved(config.unity, ctx),
138
153
  install: (env) => setupUnity(env, config.unity, ctx),
139
154
  },
140
155
  {
@@ -34,13 +34,13 @@ export function ghExePath() {
34
34
  * gh keeps its token in Windows Credential Manager unless told otherwise, and that is wiped with the sandbox: only a
35
35
  * login made with --insecure-storage (the token in GH_CONFIG_DIR, which persists) is still there next time.
36
36
  */
37
- const GH_NOTE = ' (log in once with: gh auth login --web --insecure-storage; it is kept between sandboxes)';
37
+ const GH_NOTE = ' (signed in on your PC? turn on github.usePcLogin and the login comes from there; otherwise log in once with: gh auth login --web --insecure-storage, which is kept between sandboxes)';
38
38
  /** A saved gh account whose token was in Credential Manager (gone now): say how to log in so that it lasts. */
39
39
  function warnKeyringLogin() {
40
40
  try {
41
41
  const hosts = fs.readFileSync(path.join(ghConfigDir(), 'hosts.yml'), 'utf8');
42
42
  if (/^\s*user:/m.test(hosts) && !/oauth_token:/.test(hosts))
43
- warn('gh: the GitHub login from an earlier sandbox was kept in Windows Credential Manager, which is wiped with each sandbox. Log in again with: gh auth login --web --insecure-storage (then it lasts).');
43
+ warn('gh: the GitHub login from an earlier sandbox was kept in Windows Credential Manager, which is wiped with each sandbox. Log in again with: gh auth login --web --insecure-storage (then it lasts), or sign gh in on your PC and turn on github.usePcLogin.');
44
44
  }
45
45
  catch {
46
46
  // No saved login: nothing to say.
@@ -54,6 +54,8 @@ const BY_TOOLCHAIN = {
54
54
  rust: ['rust-lang.rust-analyzer', 'vadimcn.vscode-lldb'],
55
55
  cpp: ['ms-vscode.cpptools', 'ms-vscode.cmake-tools'],
56
56
  msvc: ['ms-vscode.cpptools', 'ms-vscode.cmake-tools'],
57
+ // Unreal C++ (IntelliSense from the generated .code-workspace) and the C# of the *.Build.cs / *.Target.cs rules.
58
+ unreal: ['ms-vscode.cpptools', 'ms-dotnettools.csharp'],
57
59
  dotnet: ['ms-dotnettools.csharp'],
58
60
  ruby: ['shopify.ruby-lsp'],
59
61
  php: ['bmewburn.vscode-intelephense-client', 'xdebug.php-debug'],
@@ -9,8 +9,12 @@ function howToSave(spec) {
9
9
  return "sign in to the Epic Games Launcher in this sandbox once, or take this PC's: sandbox account import epic";
10
10
  if (spec.kind === 'steam')
11
11
  return 'sign in once in this sandbox with sbx-steam login';
12
+ if (spec.kind === 'unity')
13
+ return 'sign in to Unity once in this sandbox (Unity page: Sign in to Unity), or: sandbox account login unity';
12
14
  if (spec.kind === 'comet')
13
15
  return 'sign in to Comet once in this sandbox, and it is kept for the next ones';
16
+ if (spec.kind === 'perplexityapp')
17
+ return 'sign in to the Perplexity app once in this sandbox, and it is kept for the next ones';
14
18
  if (spec.returns)
15
19
  return 'sign in once in this sandbox, or: sandbox account import ' + spec.kind;
16
20
  return spec.sources.includes('typed')
@@ -1,15 +1,21 @@
1
- // Epic Games: this PC's launcher sign-in ("Remember me" in its GameUserSettings.ini, often UTF-16). Taking it signs
2
- // the launcher on this PC out the first time a sandbox uses it (a sign-in works in one place at a time).
1
+ // Epic Games: this PC's launcher sign-in ("Remember me" in its GameUserSettings.ini, often UTF-16; launcher 17+ keeps
2
+ // it in Config\WindowsEditor, older ones in Config\Windows). Taking it signs the launcher on this PC out the first time
3
+ // a sandbox uses it (a sign-in works in one place at a time).
3
4
  import path from 'node:path';
4
- import { epicSettingsFile, MAX_EPIC_INI_BYTES, readEpicLogin, } from '../../../shared/accounts/index.js';
5
+ import { epicSettingsFiles, MAX_EPIC_INI_BYTES, readEpicLogin, } from '../../../shared/accounts/index.js';
5
6
  import { decodeText, readBoundedFile } from '../../../shared/io/index.js';
6
7
  export const epicSource = {
7
8
  fromPc(pc, from) {
8
9
  const localAppData = from ? path.resolve(from) : pc.localAppData;
9
10
  if (!localAppData)
10
11
  return null;
11
- const bytes = readBoundedFile(epicSettingsFile(localAppData), MAX_EPIC_INI_BYTES);
12
- return bytes ? readEpicLogin(decodeText(bytes).text) : null;
12
+ for (const file of epicSettingsFiles(localAppData)) {
13
+ const bytes = readBoundedFile(file, MAX_EPIC_INI_BYTES);
14
+ const login = bytes ? readEpicLogin(decodeText(bytes).text) : null;
15
+ if (login)
16
+ return login;
17
+ }
18
+ return null;
13
19
  },
14
20
  summary: () => null,
15
21
  };
@@ -0,0 +1,2 @@
1
+ import type { HostSource } from '../source.js';
2
+ export declare const perplexityAppSource: HostSource<'perplexityapp'>;
@@ -0,0 +1,8 @@
1
+ // Perplexity desktop app: nothing to take from this PC. The app is an Electron program in a Microsoft Store package;
2
+ // its cookies are sealed to your Windows account (Chromium's cookie key is protected by DPAPI), so they cannot open in
3
+ // a sandbox, and reading them would be what password stealers do. The sign-in only ever comes from a sandbox you
4
+ // signed in in (the same way as Comet's).
5
+ import { cometSummary } from '../../../shared/accounts/index.js';
6
+ export const perplexityAppSource = {
7
+ summary: (p) => cometSummary(p),
8
+ };
@@ -1,2 +1,2 @@
1
- import { type HostSource } from '../source.js';
1
+ import type { HostSource } from '../source.js';
2
2
  export declare const unitySource: HostSource<'unity'>;
@@ -1,13 +1,65 @@
1
- import { notAvailableYet } from '../source.js';
1
+ // Unity: this PC's licence file (Unity_lic.ulf, or the named-user UnityEntitlementLicense.xml), a licence file you
2
+ // name, or the Unity ID (and serial) typed for a sandbox to sign in with. Unity ties a licence file to the computer, so
3
+ // a sandbox may refuse one taken from this PC; the typed Unity ID is the way that always works.
4
+ import path from 'node:path';
5
+ import { MAX_ULF_BYTES, unityEntitlementFile, unitySummary, unityUlfFile, UnityPayloadSchema, } from '../../../shared/accounts/index.js';
6
+ import { fail } from '../../../core/util/index.js';
7
+ import { decodeText, readBoundedFile } from '../../../shared/io/index.js';
8
+ const NOT_A_LICENCE = 'That is not a Unity licence file (Unity_lic.ulf) or entitlement file.';
9
+ const WHERE = 'Pick the file from C:\\ProgramData\\Unity or %LOCALAPPDATA%\\Unity\\licenses.';
10
+ function readText(file) {
11
+ const bytes = readBoundedFile(file, MAX_ULF_BYTES);
12
+ return bytes ? decodeText(bytes).text : null;
13
+ }
14
+ /** The email, password and serial answers that were typed (trimmed, empty ones left out). */
15
+ function typed(answers) {
16
+ const out = {};
17
+ const email = (answers.email ?? '').trim();
18
+ const password = answers.password ?? '';
19
+ const serial = (answers.serial ?? '').trim();
20
+ if (email)
21
+ out.email = email;
22
+ if (password)
23
+ out.password = password;
24
+ if (serial)
25
+ out.serial = serial;
26
+ return out;
27
+ }
28
+ function checked(candidate) {
29
+ const parsed = UnityPayloadSchema.safeParse(candidate);
30
+ if (!parsed.success)
31
+ fail('That Unity sign-in is not valid (check the email, and that the licence file is not too big).', WHERE);
32
+ return parsed.data;
33
+ }
2
34
  export const unitySource = {
3
- fromPc(_pc, _from) {
4
- return notAvailableYet('unity');
35
+ fromPc(pc, from) {
36
+ const ulf = (pc.programData || from ? readText(unityUlfFile(from ? path.resolve(from) : pc.programData)) : null) ??
37
+ (pc.localAppData
38
+ ? readText(path.win32.join(pc.localAppData, 'VirtualStore', 'ProgramData', 'Unity', 'Unity_lic.ulf'))
39
+ : null);
40
+ const entitlement = pc.localAppData ? readText(unityEntitlementFile(pc.localAppData)) : null;
41
+ if (ulf === null && entitlement === null)
42
+ return null;
43
+ return { ...(ulf !== null ? { ulf } : {}), ...(entitlement !== null ? { entitlement } : {}) };
5
44
  },
6
- fromFile(_bytes, _answers) {
7
- return notAvailableYet('unity');
45
+ fromFile(bytes, answers) {
46
+ const text = decodeText(bytes).text;
47
+ const candidate = { ...typed(answers) };
48
+ if (text.includes('Entitlement'))
49
+ candidate.entitlement = text;
50
+ else if (text.includes('<License'))
51
+ candidate.ulf = text;
52
+ else
53
+ fail(NOT_A_LICENCE, WHERE);
54
+ return checked(candidate);
8
55
  },
9
- fromAnswers(_answers) {
10
- return notAvailableYet('unity');
56
+ fromAnswers(answers) {
57
+ const t = typed(answers);
58
+ if (t.serial && !(t.email && t.password))
59
+ fail('A serial needs your Unity ID email and password.', 'Type them too, or leave the serial empty.');
60
+ if (!t.email || !t.password)
61
+ fail('A Unity sign-in needs your Unity ID email and password.', 'Type both: sandbox account login unity');
62
+ return checked(t);
11
63
  },
12
- summary: () => null,
64
+ summary: unitySummary,
13
65
  };
@@ -2,16 +2,17 @@ import { z } from 'zod';
2
2
  import { type ServiceKind } from '../../shared/accounts/index.js';
3
3
  declare const LeaseSchema: z.ZodObject<{
4
4
  kind: z.ZodEnum<{
5
- unity: "unity";
6
5
  keystore: "keystore";
7
6
  play: "play";
8
7
  expo: "expo";
9
8
  appstore: "appstore";
9
+ unity: "unity";
10
10
  steam: "steam";
11
11
  epic: "epic";
12
12
  cfx: "cfx";
13
13
  perplexity: "perplexity";
14
14
  comet: "comet";
15
+ perplexityapp: "perplexityapp";
15
16
  }>;
16
17
  dataSet: z.ZodString;
17
18
  launchId: z.ZodString;
@@ -4,6 +4,8 @@ export interface PcEnv {
4
4
  readonly localAppData: string;
5
5
  readonly appData: string;
6
6
  readonly programData: string;
7
+ /** C:\Program Files (where the Epic Games Launcher installs engines by default). */
8
+ readonly programFiles?: string;
7
9
  /** The environment the command runs with (a variable set in this terminal). */
8
10
  readonly env?: Readonly<Record<string, string | undefined>>;
9
11
  /** A variable of this Windows user's saved environment (HKCU\Environment), or null when it is not set. */
@@ -32,6 +32,7 @@ export function pcEnv() {
32
32
  localAppData: env.LOCALAPPDATA ?? '',
33
33
  appData: env.APPDATA ?? '',
34
34
  programData: env.ProgramData ?? env.PROGRAMDATA ?? 'C:\\ProgramData',
35
+ programFiles: env.ProgramFiles ?? 'C:\\Program Files',
35
36
  env,
36
37
  userVariable: savedUserVariable,
37
38
  };
@@ -4,6 +4,7 @@ import { cometSource } from './kinds/comet.js';
4
4
  import { epicSource } from './kinds/epic.js';
5
5
  import { expoSource } from './kinds/expo.js';
6
6
  import { keystoreSource } from './kinds/keystore.js';
7
+ import { perplexityAppSource } from './kinds/perplexityapp.js';
7
8
  import { perplexitySource } from './kinds/perplexity.js';
8
9
  import { playSource } from './kinds/play.js';
9
10
  import { steamSource } from './kinds/steam.js';
@@ -19,4 +20,5 @@ export const HOST_SOURCES = {
19
20
  appstore: appstoreSource,
20
21
  perplexity: perplexitySource,
21
22
  comet: cometSource,
23
+ perplexityapp: perplexityAppSource,
22
24
  };
@@ -0,0 +1 @@
1
+ export { prepareStorePackages, spawnStoreRunner, stagedPackageDir, StorePackages, STORE_PACKAGE_MAX_AGE_MS, type ScriptRun, type StoreApp, type StoreRunner, type StoreStaging, } from './store-package.js';
@@ -0,0 +1,2 @@
1
+ // Store apps for the sandbox (host side): the package fetched on this PC and staged in the data folder.
2
+ export { prepareStorePackages, spawnStoreRunner, stagedPackageDir, StorePackages, STORE_PACKAGE_MAX_AGE_MS, } from './store-package.js';
@@ -0,0 +1,51 @@
1
+ import { type StorePackageManifest } from '../../shared/apps/index.js';
2
+ /** How long a fetched package is used before a launch looks for a newer one (a failed look keeps the old one). */
3
+ export declare const STORE_PACKAGE_MAX_AGE_MS: number;
4
+ export interface ScriptRun {
5
+ readonly status: number | null;
6
+ readonly stdout: string;
7
+ readonly stderr: string;
8
+ }
9
+ /** Runs a program and collects its output; the self-test passes a fake. */
10
+ export type StoreRunner = (exe: string, args: readonly string[], timeoutMs: number) => Promise<ScriptRun>;
11
+ export declare const spawnStoreRunner: StoreRunner;
12
+ /** What an app needs fetched (catalogue source.store). */
13
+ export interface StoreApp {
14
+ readonly id: string;
15
+ readonly productId: string;
16
+ readonly packageName: string;
17
+ }
18
+ export interface StoreStaging {
19
+ readonly storageRoot: string;
20
+ readonly dataPath: string;
21
+ readonly run?: StoreRunner;
22
+ readonly now?: () => number;
23
+ }
24
+ /** Where the data folder keeps an app's package for the sandbox. */
25
+ export declare function stagedPackageDir(dataPath: string, appId: string): string;
26
+ export declare class StorePackages {
27
+ private readonly o;
28
+ constructor(o: StoreStaging);
29
+ cacheDir(productId: string): string;
30
+ /** The cached package when its files are all there at the size hashed; null otherwise. */
31
+ cached(productId: string): StorePackageManifest | null;
32
+ private fresh;
33
+ /** Fetches the package into the cache with this PC's Store tooling and returns what was kept. */
34
+ fetch(app: StoreApp): Promise<StorePackageManifest>;
35
+ /** Checks what the script fetched into `work`, hashes it and moves it into the cache. */
36
+ private keep;
37
+ /** The package to use: the cache when fresh, else a new fetch (the old one stands when that fails). */
38
+ ensure(app: StoreApp): Promise<{
39
+ manifest: StorePackageManifest;
40
+ note: string;
41
+ }>;
42
+ /** Copies the cached package into the data folder for the sandbox (files already there at the same size stay). */
43
+ stage(app: StoreApp, manifest: StorePackageManifest): string;
44
+ }
45
+ /**
46
+ * For a launch: every Store app chosen gets its package fetched (or kept) and staged. A failure never stops the launch:
47
+ * it is a note, and the sandbox skips that app.
48
+ */
49
+ export declare function prepareStorePackages(apps: readonly StoreApp[], o: StoreStaging): Promise<{
50
+ readonly notes: readonly string[];
51
+ }>;