windows-development-sandbox 1.27.1 → 1.27.3

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 (78) hide show
  1. package/dist/app/context.d.ts +5 -0
  2. package/dist/app/context.js +13 -0
  3. package/dist/app/features/accounts/accounts.def.d.ts +16 -0
  4. package/dist/app/features/accounts/accounts.run.d.ts +4 -0
  5. package/dist/app/features/accounts/services.def.d.ts +15 -1
  6. package/dist/app/features/accounts/services.def.js +67 -31
  7. package/dist/app/features/accounts/services.run.d.ts +7 -2
  8. package/dist/app/features/accounts/services.run.js +25 -6
  9. package/dist/app/features/defs.d.ts +17 -0
  10. package/dist/app/registry.d.ts +1 -1
  11. package/dist/app/registry.js +15 -2
  12. package/dist/bin/agent-sandbox.js +2 -2
  13. package/dist/bin/{chunk-QUHNXHVG.js → chunk-4M5SZZ5P.js} +46 -41
  14. package/dist/bin/{chunk-4TLSWPG7.js → chunk-FA5VWLP4.js} +1 -1
  15. package/dist/bin/{chunk-RSIX7C6D.js → chunk-RU7XRN62.js} +249 -101
  16. package/dist/bin/claude-host.js +2 -2
  17. package/dist/bin/claude-sbx.js +2 -2
  18. package/dist/bin/codex-sbx.js +2 -2
  19. package/dist/bin/gemini-sbx.js +2 -2
  20. package/dist/bin/sandbox.js +2 -2
  21. package/dist/bin/sbx-hide.js +2 -2
  22. package/dist/bin/sbx-show.js +2 -2
  23. package/dist/cli/menu/menu.js +6 -1
  24. package/dist/cli/menu/pages/accounts-page.js +9 -5
  25. package/dist/guest/apps/app-installer.js +7 -5
  26. package/dist/guest/apps/comet-unpack.js +2 -2
  27. package/dist/guest/runtime/pools.d.ts +4 -4
  28. package/dist/guest/runtime/pools.js +4 -15
  29. package/dist/guest/runtime/process.d.ts +5 -2
  30. package/dist/guest/runtime/process.js +23 -3
  31. package/dist/guest/toolchains/engines/epic-launcher.d.ts +6 -0
  32. package/dist/guest/toolchains/engines/epic-launcher.js +19 -1
  33. package/dist/guest/toolchains/engines/unreal-engines.js +2 -1
  34. package/dist/guest/toolchains/installers.d.ts +3 -3
  35. package/dist/guest/toolchains/installers.js +5 -5
  36. package/dist/guest/toolchains/native/vcruntime.js +2 -2
  37. package/dist/guest/toolchains/native/vs-persist.d.ts +2 -2
  38. package/dist/guest/toolchains/signature.d.ts +2 -0
  39. package/dist/guest/toolchains/signature.js +9 -2
  40. package/dist/guest/toolchains/step-scheduler.js +1 -1
  41. package/dist/host/accounts/deliver.js +15 -7
  42. package/dist/host/pc-installs/discover.js +5 -2
  43. package/dist/host/pc-installs/unreal.d.ts +3 -1
  44. package/dist/host/pc-installs/unreal.js +27 -1
  45. package/dist/runtime/agent.js +34 -22
  46. package/dist/runtime/daemon.js +96 -51
  47. package/dist/runtime/db.js +32 -16
  48. package/dist/runtime/main.js +98 -53
  49. package/dist/runtime/netproxy.js +26 -12
  50. package/dist/runtime/tool.js +32 -15
  51. package/dist/runtime/view.js +26 -12
  52. package/dist/selftest/app.test.js +20 -2
  53. package/dist/selftest/boot-runner.test.js +15 -1
  54. package/dist/selftest/engines.test.js +22 -1
  55. package/dist/selftest/menu-screens.js +52 -1
  56. package/dist/selftest/menu.test.js +29 -3
  57. package/dist/selftest/service-accounts.test.js +77 -2
  58. package/dist/selftest/ui-text.test.js +4 -0
  59. package/dist/shared/accounts/kinds.js +9 -9
  60. package/dist/shared/apps/catalogue-gamedev.js +1 -0
  61. package/dist/shared/apps/catalogue.d.ts +2 -0
  62. package/dist/shared/concurrency/pools.js +1 -1
  63. package/dist/shared/download/archive.js +4 -2
  64. package/dist/shared/download/index.d.ts +1 -1
  65. package/dist/shared/download/index.js +1 -1
  66. package/dist/shared/download/spawn.d.ts +7 -1
  67. package/dist/shared/download/spawn.js +16 -1
  68. package/dist/shared/paths/pc-installs.d.ts +8 -0
  69. package/dist/shared/unreal/launcher-files.d.ts +7 -0
  70. package/dist/shared/unreal/launcher-files.js +32 -1
  71. package/dist/web/index.html +1 -1
  72. package/dist/web/{main-VPMYTSUL.js → main-NKM7ECSB.js} +59 -59
  73. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  74. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  75. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  76. package/package.json +1 -1
  77. package/schemas/config.schema.json +9 -9
  78. package/schemas/sandbox-config.schema.json +9 -9
@@ -1,6 +1,7 @@
1
1
  import { assertSafeMapping } from '../../core/safe-paths.js';
2
2
  import { dirExists, errMsg } from '../../core/util/index.js';
3
3
  import { pcInstallGuest } from '../../shared/paths/index.js';
4
+ import { relocateItem } from '../../shared/unreal/index.js';
4
5
  import { pcEnv } from '../accounts/index.js';
5
6
  import { epicInstalls } from './epic.js';
6
7
  import { steamInstalls } from './steam.js';
@@ -74,8 +75,10 @@ export function discoverPcInstalls(s, o, pc = pcEnv()) {
74
75
  readOnly: !writable,
75
76
  label: writable ? 'this PC, writable' : 'this PC, read-only',
76
77
  });
77
- const { host: _host, ...shown } = install;
78
- items.push({ ...shown, guest });
78
+ const { host: _host, launcherItem, ...shown } = install;
79
+ // The manifest names the sandbox's folder, or it is left out: no path of this PC goes to the sandbox.
80
+ const moved = launcherItem ? relocateItem(launcherItem.text, install.host, guest) : null;
81
+ items.push({ ...shown, guest, ...(moved ? { launcherItem: { name: launcherItem.name, text: moved } } : {}) });
79
82
  }
80
83
  catch (e) {
81
84
  notes.push('pcInstalls: ' + install.name + ' is not mapped: ' + errMsg(e));
@@ -13,6 +13,8 @@ export interface UnrealPcFiles {
13
13
  readonly defaultRoot: string;
14
14
  /** More folders that may hold UE_x.y folders: the same places on every drive (D:\Program Files\Epic Games ...). */
15
15
  readonly otherRoots?: readonly string[];
16
+ /** The launcher's Data\Manifests folder (its .item files), or none. */
17
+ readonly manifests?: string;
16
18
  readonly list: (dir: string) => string[];
17
19
  readonly read: (file: string, max: number) => Buffer | null;
18
20
  }
@@ -33,7 +35,7 @@ export interface PcSourceBuild {
33
35
  export declare function pcSourceBuilds(files: Pick<UnrealPcFiles, 'read' | 'builds'>): PcSourceBuild[];
34
36
  /** The engine folders you registered (pcInstalls.unrealEngineFolders), mapped writable. A folder that is a source
35
37
  * build of this PC keeps the PC's build id. */
36
- export declare function findEngineFolders(files: Pick<UnrealPcFiles, 'read' | 'builds'>, folders: readonly (string | number)[]): PcInstall[];
38
+ export declare function findEngineFolders(files: Pick<UnrealPcFiles, 'read' | 'builds'> & Partial<Pick<UnrealPcFiles, 'launcherInstalled' | 'manifests' | 'list'>>, folders: readonly (string | number)[]): PcInstall[];
37
39
  /** This PC's files (reg.exe through system32, bounded reads). */
38
40
  export declare function unrealPcFiles(pc: PcEnv): UnrealPcFiles;
39
41
  export declare const unrealInstalls: PcInstallSource;
@@ -10,7 +10,7 @@ import fs from 'node:fs';
10
10
  import path from 'node:path';
11
11
  import { readBoundedFile } from '../../shared/io/index.js';
12
12
  import { SYSTEM } from '../../shared/system.js';
13
- import { BUILDS_KEY, launcherInstallations, launcherInstalledFile, MAX_LAUNCHER_FILE_BYTES, parseBuildVersion, versionText, } from '../../shared/unreal/index.js';
13
+ import { BUILDS_KEY, itemInstallLocation, ITEM_NAME, launcherInstallations, launcherInstalledFile, launcherManifestsDir, MAX_LAUNCHER_FILE_BYTES, parseBuildVersion, versionText, } from '../../shared/unreal/index.js';
14
14
  const win = path.win32;
15
15
  /** At most this many engines of this PC are mapped. */
16
16
  export const MAX_PC_ENGINES = 8;
@@ -29,6 +29,27 @@ const versionOf = (dir, files) => {
29
29
  const bytes = files.read(win.join(dir, 'Engine', 'Build', 'Build.version'), 64 * 1024);
30
30
  return bytes ? parseBuildVersion(bytes.toString('utf8')) : null;
31
31
  };
32
+ /** At most this much of a manifest goes into session.json (an engine's is a few KB). */
33
+ const MAX_ITEM_BYTES = 64 * 1024;
34
+ /** The launcher's manifest of the app in `dir` (with this PC's paths), or nothing. */
35
+ function launcherItemOf(dir, files) {
36
+ if (!files.manifests)
37
+ return {};
38
+ const want = win.resolve(dir).toLowerCase();
39
+ for (const name of files.list(files.manifests).filter((n) => ITEM_NAME.test(n))) {
40
+ const bytes = files.read(win.join(files.manifests, name), MAX_ITEM_BYTES);
41
+ const at = bytes ? itemInstallLocation(bytes.toString('utf8')) : null;
42
+ if (bytes && at && win.resolve(at).toLowerCase() === want)
43
+ return { launcherItem: { name, text: bytes.toString('utf8') } };
44
+ }
45
+ return {};
46
+ }
47
+ /** The launcher's ids for the app in `dir` (from LauncherInstalled.dat), or nothing. */
48
+ function launcherIdsOf(dir, files) {
49
+ const want = win.resolve(dir).toLowerCase();
50
+ const entry = (files.launcherInstalled ? launcherInstallations(files.launcherInstalled) : []).find((i) => APP_NAME.test(i.appName) && i.ids && win.resolve(i.installLocation).toLowerCase() === want);
51
+ return entry?.ids ? { launcher: entry.ids } : {};
52
+ }
32
53
  const isWanted = (wanted, v) => wanted === true || wanted.map((w) => String(w).replace(/^UE_/i, '')).includes(v.major + '.' + v.minor);
33
54
  /** The engines of this PC that `wanted` maps, each folder once, at most MAX_PC_ENGINES. */
34
55
  export function findUnrealInstalls(files, wanted) {
@@ -53,6 +74,7 @@ export function findUnrealInstalls(files, wanted) {
53
74
  host: win.normalize(dir),
54
75
  guest: '',
55
76
  ...(ids ? { launcher: ids } : {}),
77
+ ...launcherItemOf(dir, files),
56
78
  });
57
79
  };
58
80
  const listed = files.launcherInstalled ? launcherInstallations(files.launcherInstalled) : [];
@@ -117,6 +139,9 @@ export function findEngineFolders(files, folders) {
117
139
  guest: '',
118
140
  writable: true,
119
141
  ...(source ? { buildId: source.buildId } : {}),
142
+ // An engine the launcher installed keeps its ids and manifest, so the sandbox's launcher lists it too.
143
+ ...launcherIdsOf(dir, { launcherInstalled: files.launcherInstalled ?? null }),
144
+ ...(files.list ? launcherItemOf(dir, { manifests: files.manifests, list: files.list, read: files.read }) : {}),
120
145
  });
121
146
  }
122
147
  return found;
@@ -146,6 +171,7 @@ export function unrealPcFiles(pc) {
146
171
  win.join(d, 'Program Files', 'Epic Games'),
147
172
  win.join(d, 'Epic Games'),
148
173
  ]),
174
+ manifests: launcherManifestsDir(pc.programData),
149
175
  list: listDir,
150
176
  read: readBoundedFile,
151
177
  };
@@ -26093,6 +26093,7 @@ var GAMEDEV_APPS = [
26093
26093
  size: "100 MB",
26094
26094
  source: { github: "f3d-app/f3d", asset: "^F3D-[\\d.]+-Windows-x86_64\\.zip$" },
26095
26095
  exe: "f3d.exe",
26096
+ onPath: true,
26096
26097
  needs: ["mesa"]
26097
26098
  },
26098
26099
  {
@@ -27192,7 +27193,7 @@ var SERVICE_SPECS = {
27192
27193
  },
27193
27194
  steam: {
27194
27195
  kind: "steam",
27195
- label: "Steam (SteamCMD)",
27196
+ label: "Steam",
27196
27197
  usedBy: ["steam"],
27197
27198
  sources: ["file", "sandbox"],
27198
27199
  singleHolder: true,
@@ -27228,7 +27229,7 @@ var SERVICE_SPECS = {
27228
27229
  },
27229
27230
  cfx: {
27230
27231
  kind: "cfx",
27231
- label: "Cfx.re (FiveM / RedM server key)",
27232
+ label: "FiveM key",
27232
27233
  usedBy: ["fivem"],
27233
27234
  sources: ["typed", "file"],
27234
27235
  singleHolder: false,
@@ -27257,7 +27258,7 @@ var SERVICE_SPECS = {
27257
27258
  },
27258
27259
  keystore: {
27259
27260
  kind: "keystore",
27260
- label: "Android upload keystore",
27261
+ label: "Android keystore",
27261
27262
  usedBy: ["android"],
27262
27263
  sources: ["file"],
27263
27264
  singleHolder: false,
@@ -27272,7 +27273,7 @@ var SERVICE_SPECS = {
27272
27273
  },
27273
27274
  play: {
27274
27275
  kind: "play",
27275
- label: "Google Play (service account)",
27276
+ label: "Google Play",
27276
27277
  usedBy: ["android"],
27277
27278
  sources: ["file"],
27278
27279
  singleHolder: false,
@@ -27283,7 +27284,7 @@ var SERVICE_SPECS = {
27283
27284
  },
27284
27285
  expo: {
27285
27286
  kind: "expo",
27286
- label: "Expo (EAS)",
27287
+ label: "Expo",
27287
27288
  usedBy: ["reactNative"],
27288
27289
  sources: ["typed"],
27289
27290
  singleHolder: false,
@@ -27294,7 +27295,7 @@ var SERVICE_SPECS = {
27294
27295
  },
27295
27296
  appstore: {
27296
27297
  kind: "appstore",
27297
- label: "App Store Connect API key",
27298
+ label: "App Store Connect",
27298
27299
  usedBy: ["reactNative", "flutter"],
27299
27300
  sources: ["file", "typed"],
27300
27301
  singleHolder: false,
@@ -27308,7 +27309,7 @@ var SERVICE_SPECS = {
27308
27309
  },
27309
27310
  perplexity: {
27310
27311
  kind: "perplexity",
27311
- label: "Perplexity API key",
27312
+ label: "Perplexity API",
27312
27313
  usedBy: ["perplexity"],
27313
27314
  sources: ["pc", "file", "typed"],
27314
27315
  singleHolder: false,
@@ -27332,7 +27333,7 @@ var SERVICE_SPECS = {
27332
27333
  },
27333
27334
  comet: {
27334
27335
  kind: "comet",
27335
- label: "Comet (Perplexity browser)",
27336
+ label: "Comet",
27336
27337
  usedBy: ["comet"],
27337
27338
  sources: ["sandbox"],
27338
27339
  singleHolder: false,
@@ -27348,7 +27349,7 @@ var SERVICE_SPECS = {
27348
27349
  },
27349
27350
  perplexityapp: {
27350
27351
  kind: "perplexityapp",
27351
- label: "Perplexity desktop app",
27352
+ label: "Perplexity app",
27352
27353
  usedBy: ["perplexityapp"],
27353
27354
  sources: ["sandbox"],
27354
27355
  singleHolder: false,
@@ -28074,7 +28075,7 @@ import path12 from "node:path";
28074
28075
 
28075
28076
  // src/guest/runtime/process.ts
28076
28077
  import fs10 from "node:fs";
28077
- import os3 from "node:os";
28078
+ import os4 from "node:os";
28078
28079
  import path11 from "node:path";
28079
28080
  import { spawn as spawn2, spawnSync } from "node:child_process";
28080
28081
 
@@ -28134,10 +28135,20 @@ function scriptArgs(name, args = []) {
28134
28135
  }
28135
28136
 
28136
28137
  // src/shared/download/spawn.ts
28138
+ import os3 from "node:os";
28137
28139
  import { spawn } from "node:child_process";
28140
+ function lowerPriority(pid) {
28141
+ if (pid === void 0) return;
28142
+ try {
28143
+ os3.setPriority(pid, os3.constants.priority.PRIORITY_BELOW_NORMAL);
28144
+ } catch {
28145
+ }
28146
+ }
28138
28147
  function spawnAsync(file2, args, options = {}) {
28148
+ const { lowPriority, ...spawnOptions } = options;
28139
28149
  return new Promise((resolve, reject) => {
28140
- const child = spawn(file2, args, { stdio: "ignore", windowsHide: true, ...options });
28150
+ const child = spawn(file2, args, { stdio: "ignore", windowsHide: true, ...spawnOptions });
28151
+ if (lowPriority) lowerPriority(child.pid);
28141
28152
  child.once("error", reject);
28142
28153
  child.once("close", (code) => resolve(code ?? -1));
28143
28154
  });
@@ -28158,13 +28169,16 @@ function extractArchive(archive, destination, members2 = []) {
28158
28169
  }
28159
28170
  async function extract(archive, destination, members2) {
28160
28171
  fs8.mkdirSync(destination, { recursive: true });
28161
- let status = await spawnAsync(SYSTEM.tar(), ["-xf", archive, "-C", destination, ...members2]);
28172
+ let status = await spawnAsync(SYSTEM.tar(), ["-xf", archive, "-C", destination, ...members2], {
28173
+ lowPriority: true
28174
+ });
28162
28175
  if (status !== 0 && /\.(zip|nupkg)$/i.test(archive)) {
28163
28176
  const zip = archive.endsWith(".zip") ? archive : archive + ".zip";
28164
28177
  if (zip !== archive) fs8.copyFileSync(archive, zip);
28165
28178
  status = await spawnAsync(
28166
28179
  SYSTEM.powershell(),
28167
- scriptArgs("expand-archive.ps1", ["-Archive", zip, "-Destination", destination])
28180
+ scriptArgs("expand-archive.ps1", ["-Archive", zip, "-Destination", destination]),
28181
+ { lowPriority: true }
28168
28182
  );
28169
28183
  }
28170
28184
  if (status !== 0) throw new Error("Could not extract " + archive);
@@ -28381,13 +28395,15 @@ function logLines(stream) {
28381
28395
  stream.on("end", () => logOutput(partial2));
28382
28396
  }
28383
28397
  async function execAsync(file2, args, options = {}) {
28398
+ const { lowPriority, ...spawnOptions } = options;
28384
28399
  const code = captureOutput() ? await new Promise((resolve, reject) => {
28385
- const child = spawn2(file2, args, { stdio: ["inherit", "pipe", "pipe"], windowsHide: true, ...options });
28400
+ const child = spawn2(file2, args, { stdio: ["inherit", "pipe", "pipe"], windowsHide: true, ...spawnOptions });
28401
+ if (lowPriority) lowerPriority(child.pid);
28386
28402
  logLines(child.stdout);
28387
28403
  logLines(child.stderr);
28388
28404
  child.once("error", reject);
28389
28405
  child.once("close", (c) => resolve(c ?? -1));
28390
- }) : await spawnAsync(file2, args, { stdio: "inherit", ...options });
28406
+ }) : await spawnAsync(file2, args, { stdio: "inherit", ...spawnOptions, ...lowPriority ? { lowPriority } : {} });
28391
28407
  if (code !== 0)
28392
28408
  throw new Error(path11.basename(file2) + " " + args.slice(0, 3).join(" ") + " failed with exit code " + code);
28393
28409
  }
@@ -28416,7 +28432,7 @@ function runScript(name, args = []) {
28416
28432
  throw new Error(name + " failed with exit code " + result.status + (errors ? ": " + errors.slice(0, 400) : ""));
28417
28433
  }
28418
28434
  function runScriptWithJson(name, param, data, args = []) {
28419
- const dir = fs10.mkdtempSync(path11.join(os3.tmpdir(), "sbx-"));
28435
+ const dir = fs10.mkdtempSync(path11.join(os4.tmpdir(), "sbx-"));
28420
28436
  const file2 = path11.join(dir, "input.json");
28421
28437
  try {
28422
28438
  fs10.writeFileSync(file2, JSON.stringify(data), "utf8");
@@ -28492,9 +28508,6 @@ async function setupUv(env) {
28492
28508
  env.prepend.push(mkdirp(tool("uv-bin")));
28493
28509
  }
28494
28510
 
28495
- // src/guest/runtime/pools.ts
28496
- import os4 from "node:os";
28497
-
28498
28511
  // src/shared/concurrency/limiter.ts
28499
28512
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
28500
28513
  var held = new AsyncLocalStorage2();
@@ -28554,14 +28567,13 @@ var Limiter = class {
28554
28567
  };
28555
28568
 
28556
28569
  // src/shared/concurrency/pools.ts
28557
- var net = new Limiter(4, "net");
28570
+ var net = new Limiter(3, "net");
28558
28571
  var heavy = new Limiter(2, "heavy");
28559
28572
  var installer = new Limiter(1, "installer");
28560
28573
 
28561
28574
  // src/guest/runtime/pools.ts
28562
- var BELOW_NORMAL = os4.constants.priority.PRIORITY_BELOW_NORMAL;
28563
28575
  function execHeavy(file2, args, options = {}) {
28564
- return heavy.run(() => execAsync(file2, args, options));
28576
+ return heavy.run(() => execAsync(file2, args, { ...options, lowPriority: true }));
28565
28577
  }
28566
28578
 
28567
28579
  // src/guest/runtime/session.ts
@@ -26113,6 +26113,7 @@ var GAMEDEV_APPS = [
26113
26113
  size: "100 MB",
26114
26114
  source: { github: "f3d-app/f3d", asset: "^F3D-[\\d.]+-Windows-x86_64\\.zip$" },
26115
26115
  exe: "f3d.exe",
26116
+ onPath: true,
26116
26117
  needs: ["mesa"]
26117
26118
  },
26118
26119
  {
@@ -27413,7 +27414,7 @@ var SERVICE_SPECS = {
27413
27414
  },
27414
27415
  steam: {
27415
27416
  kind: "steam",
27416
- label: "Steam (SteamCMD)",
27417
+ label: "Steam",
27417
27418
  usedBy: ["steam"],
27418
27419
  sources: ["file", "sandbox"],
27419
27420
  singleHolder: true,
@@ -27449,7 +27450,7 @@ var SERVICE_SPECS = {
27449
27450
  },
27450
27451
  cfx: {
27451
27452
  kind: "cfx",
27452
- label: "Cfx.re (FiveM / RedM server key)",
27453
+ label: "FiveM key",
27453
27454
  usedBy: ["fivem"],
27454
27455
  sources: ["typed", "file"],
27455
27456
  singleHolder: false,
@@ -27478,7 +27479,7 @@ var SERVICE_SPECS = {
27478
27479
  },
27479
27480
  keystore: {
27480
27481
  kind: "keystore",
27481
- label: "Android upload keystore",
27482
+ label: "Android keystore",
27482
27483
  usedBy: ["android"],
27483
27484
  sources: ["file"],
27484
27485
  singleHolder: false,
@@ -27493,7 +27494,7 @@ var SERVICE_SPECS = {
27493
27494
  },
27494
27495
  play: {
27495
27496
  kind: "play",
27496
- label: "Google Play (service account)",
27497
+ label: "Google Play",
27497
27498
  usedBy: ["android"],
27498
27499
  sources: ["file"],
27499
27500
  singleHolder: false,
@@ -27504,7 +27505,7 @@ var SERVICE_SPECS = {
27504
27505
  },
27505
27506
  expo: {
27506
27507
  kind: "expo",
27507
- label: "Expo (EAS)",
27508
+ label: "Expo",
27508
27509
  usedBy: ["reactNative"],
27509
27510
  sources: ["typed"],
27510
27511
  singleHolder: false,
@@ -27515,7 +27516,7 @@ var SERVICE_SPECS = {
27515
27516
  },
27516
27517
  appstore: {
27517
27518
  kind: "appstore",
27518
- label: "App Store Connect API key",
27519
+ label: "App Store Connect",
27519
27520
  usedBy: ["reactNative", "flutter"],
27520
27521
  sources: ["file", "typed"],
27521
27522
  singleHolder: false,
@@ -27529,7 +27530,7 @@ var SERVICE_SPECS = {
27529
27530
  },
27530
27531
  perplexity: {
27531
27532
  kind: "perplexity",
27532
- label: "Perplexity API key",
27533
+ label: "Perplexity API",
27533
27534
  usedBy: ["perplexity"],
27534
27535
  sources: ["pc", "file", "typed"],
27535
27536
  singleHolder: false,
@@ -27553,7 +27554,7 @@ var SERVICE_SPECS = {
27553
27554
  },
27554
27555
  comet: {
27555
27556
  kind: "comet",
27556
- label: "Comet (Perplexity browser)",
27557
+ label: "Comet",
27557
27558
  usedBy: ["comet"],
27558
27559
  sources: ["sandbox"],
27559
27560
  singleHolder: false,
@@ -27569,7 +27570,7 @@ var SERVICE_SPECS = {
27569
27570
  },
27570
27571
  perplexityapp: {
27571
27572
  kind: "perplexityapp",
27572
- label: "Perplexity desktop app",
27573
+ label: "Perplexity app",
27573
27574
  usedBy: ["perplexityapp"],
27574
27575
  sources: ["sandbox"],
27575
27576
  singleHolder: false,
@@ -28531,10 +28532,20 @@ function scriptArgs(name, args = []) {
28531
28532
  }
28532
28533
 
28533
28534
  // src/shared/download/spawn.ts
28535
+ import os3 from "node:os";
28534
28536
  import { spawn } from "node:child_process";
28537
+ function lowerPriority(pid) {
28538
+ if (pid === void 0) return;
28539
+ try {
28540
+ os3.setPriority(pid, os3.constants.priority.PRIORITY_BELOW_NORMAL);
28541
+ } catch {
28542
+ }
28543
+ }
28535
28544
  function spawnAsync(file3, args, options = {}) {
28545
+ const { lowPriority, ...spawnOptions } = options;
28536
28546
  return new Promise((resolve2, reject) => {
28537
- const child = spawn(file3, args, { stdio: "ignore", windowsHide: true, ...options });
28547
+ const child = spawn(file3, args, { stdio: "ignore", windowsHide: true, ...spawnOptions });
28548
+ if (lowPriority) lowerPriority(child.pid);
28538
28549
  child.once("error", reject);
28539
28550
  child.once("close", (code) => resolve2(code ?? -1));
28540
28551
  });
@@ -28558,13 +28569,16 @@ function extractArchive(archive, destination, members2 = []) {
28558
28569
  }
28559
28570
  async function extract(archive, destination, members2) {
28560
28571
  fs10.mkdirSync(destination, { recursive: true });
28561
- let status = await spawnAsync(SYSTEM.tar(), ["-xf", archive, "-C", destination, ...members2]);
28572
+ let status = await spawnAsync(SYSTEM.tar(), ["-xf", archive, "-C", destination, ...members2], {
28573
+ lowPriority: true
28574
+ });
28562
28575
  if (status !== 0 && /\.(zip|nupkg)$/i.test(archive)) {
28563
28576
  const zip = archive.endsWith(".zip") ? archive : archive + ".zip";
28564
28577
  if (zip !== archive) fs10.copyFileSync(archive, zip);
28565
28578
  status = await spawnAsync(
28566
28579
  SYSTEM.powershell(),
28567
- scriptArgs("expand-archive.ps1", ["-Archive", zip, "-Destination", destination])
28580
+ scriptArgs("expand-archive.ps1", ["-Archive", zip, "-Destination", destination]),
28581
+ { lowPriority: true }
28568
28582
  );
28569
28583
  }
28570
28584
  if (status !== 0) throw new Error("Could not extract " + archive);
@@ -29160,7 +29174,6 @@ function withRequestSecrets(req) {
29160
29174
  }
29161
29175
 
29162
29176
  // src/guest/runtime/pools.ts
29163
- import os4 from "node:os";
29164
29177
  import { spawn as spawn3 } from "node:child_process";
29165
29178
  import path21 from "node:path";
29166
29179
 
@@ -29234,13 +29247,13 @@ function installerRetry(code, attempt) {
29234
29247
  }
29235
29248
 
29236
29249
  // src/shared/concurrency/pools.ts
29237
- var net2 = new Limiter(4, "net");
29250
+ var net2 = new Limiter(3, "net");
29238
29251
  var heavy = new Limiter(2, "heavy");
29239
29252
  var installer = new Limiter(1, "installer");
29240
29253
 
29241
29254
  // src/guest/runtime/process.ts
29242
29255
  import fs16 from "node:fs";
29243
- import os3 from "node:os";
29256
+ import os4 from "node:os";
29244
29257
  import path20 from "node:path";
29245
29258
  import { spawn as spawn2, spawnSync as spawnSync2 } from "node:child_process";
29246
29259
  var outputSink = null;
@@ -29262,13 +29275,15 @@ function logLines(stream) {
29262
29275
  stream.on("end", () => logOutput(partial2));
29263
29276
  }
29264
29277
  async function execAsync(file3, args, options = {}) {
29278
+ const { lowPriority, ...spawnOptions } = options;
29265
29279
  const code = captureOutput() ? await new Promise((resolve2, reject) => {
29266
- const child = spawn2(file3, args, { stdio: ["inherit", "pipe", "pipe"], windowsHide: true, ...options });
29280
+ const child = spawn2(file3, args, { stdio: ["inherit", "pipe", "pipe"], windowsHide: true, ...spawnOptions });
29281
+ if (lowPriority) lowerPriority(child.pid);
29267
29282
  logLines(child.stdout);
29268
29283
  logLines(child.stderr);
29269
29284
  child.once("error", reject);
29270
29285
  child.once("close", (c) => resolve2(c ?? -1));
29271
- }) : await spawnAsync(file3, args, { stdio: "inherit", ...options });
29286
+ }) : await spawnAsync(file3, args, { stdio: "inherit", ...spawnOptions, ...lowPriority ? { lowPriority } : {} });
29272
29287
  if (code !== 0)
29273
29288
  throw new Error(path20.basename(file3) + " " + args.slice(0, 3).join(" ") + " failed with exit code " + code);
29274
29289
  }
@@ -29322,8 +29337,23 @@ function runScriptAsync(name, args = []) {
29322
29337
  function scriptOutput(name, args = [], options = {}) {
29323
29338
  return execOutput(SYSTEM.powershell(), scriptArgs(name, args), options);
29324
29339
  }
29340
+ function scriptOutputAsync(name, args = []) {
29341
+ return new Promise((resolve2) => {
29342
+ const child = spawn2(SYSTEM.powershell(), scriptArgs(name, args), {
29343
+ stdio: ["ignore", "pipe", "ignore"],
29344
+ windowsHide: true
29345
+ });
29346
+ let out = "";
29347
+ child.stdout?.setEncoding("utf8");
29348
+ child.stdout?.on("data", (chunk) => {
29349
+ if (out.length < CAPTURE_BYTES) out += chunk;
29350
+ });
29351
+ child.once("error", () => resolve2(""));
29352
+ child.once("close", (code) => resolve2(code === 0 ? out.trim() : ""));
29353
+ });
29354
+ }
29325
29355
  function runScriptWithJson(name, param, data, args = []) {
29326
- const dir = fs16.mkdtempSync(path20.join(os3.tmpdir(), "sbx-"));
29356
+ const dir = fs16.mkdtempSync(path20.join(os4.tmpdir(), "sbx-"));
29327
29357
  const file3 = path20.join(dir, "input.json");
29328
29358
  try {
29329
29359
  fs16.writeFileSync(file3, JSON.stringify(data), "utf8");
@@ -29333,7 +29363,7 @@ function runScriptWithJson(name, param, data, args = []) {
29333
29363
  }
29334
29364
  }
29335
29365
  async function runScriptWithJsonAsync(name, param, data, args = []) {
29336
- const dir = fs16.mkdtempSync(path20.join(os3.tmpdir(), "sbx-"));
29366
+ const dir = fs16.mkdtempSync(path20.join(os4.tmpdir(), "sbx-"));
29337
29367
  const file3 = path20.join(dir, "input.json");
29338
29368
  try {
29339
29369
  fs16.writeFileSync(file3, JSON.stringify(data), "utf8");
@@ -29404,14 +29434,7 @@ function installPools() {
29404
29434
  downloader.limit = net2;
29405
29435
  limitExtraction(heavy);
29406
29436
  }
29407
- var BELOW_NORMAL = os4.constants.priority.PRIORITY_BELOW_NORMAL;
29408
- function belowNormal(pid) {
29409
- if (pid === void 0) return;
29410
- try {
29411
- os4.setPriority(pid, BELOW_NORMAL);
29412
- } catch {
29413
- }
29414
- }
29437
+ var belowNormal = lowerPriority;
29415
29438
  async function runInstaller(file3, args, o = {}) {
29416
29439
  for (let attempt = 1; ; attempt++) {
29417
29440
  const code = await installer.run(
@@ -29437,7 +29460,7 @@ async function runInstallerChecked(file3, args, o = {}) {
29437
29460
  throw new Error(path21.basename(file3) + " " + args.slice(0, 3).join(" ") + " failed with exit code " + code);
29438
29461
  }
29439
29462
  function execHeavy(file3, args, options = {}) {
29440
- return heavy.run(() => execAsync(file3, args, options));
29463
+ return heavy.run(() => execAsync(file3, args, { ...options, lowPriority: true }));
29441
29464
  }
29442
29465
 
29443
29466
  // src/guest/daemon/daemon.ts
@@ -30361,13 +30384,14 @@ function itemSizes(saved2) {
30361
30384
  }
30362
30385
  return sizes;
30363
30386
  }
30387
+ var ITEM_NAME = /^[A-Za-z0-9_-]{1,80}\.item$/;
30364
30388
  function isItemUnder(text2, root) {
30365
30389
  const at = itemInstallLocation(text2);
30366
30390
  return at !== null && isWithin2(root, at) && win2.resolve(at) !== win2.resolve(root);
30367
30391
  }
30368
30392
  function itemFiles(dir) {
30369
30393
  try {
30370
- return fs22.readdirSync(dir).filter((f) => /^[A-Za-z0-9_-]{1,80}\.item$/.test(f));
30394
+ return fs22.readdirSync(dir).filter((f) => ITEM_NAME.test(f));
30371
30395
  } catch {
30372
30396
  return [];
30373
30397
  }
@@ -31525,7 +31549,10 @@ import path42 from "node:path";
31525
31549
  // src/guest/toolchains/signature.ts
31526
31550
  var SignatureSchema = external_exports.object({ subject: external_exports.string().max(4096), issuer: external_exports.string().max(4096) });
31527
31551
  function signatureOf(file3) {
31528
- const text2 = scriptOutput("get-signature.ps1", ["-Path", file3]).trim();
31552
+ return parseSignature(scriptOutput("get-signature.ps1", ["-Path", file3]));
31553
+ }
31554
+ function parseSignature(output2) {
31555
+ const text2 = output2.trim();
31529
31556
  if (!text2) return null;
31530
31557
  try {
31531
31558
  const parsed = SignatureSchema.safeParse(JSON.parse(text2));
@@ -31537,11 +31564,14 @@ function signatureOf(file3) {
31537
31564
  function signedBy(file3, signer) {
31538
31565
  return signerMatches(signatureOf(file3), signer);
31539
31566
  }
31567
+ async function signedByAsync(file3, signer) {
31568
+ return signerMatches(parseSignature(await scriptOutputAsync("get-signature.ps1", ["-Path", file3])), signer);
31569
+ }
31540
31570
 
31541
31571
  // src/guest/toolchains/installers.ts
31542
31572
  function signatureCheck(signer, what) {
31543
- return (file3) => {
31544
- if (!signedBy(file3, signer)) throw new Error(what + " is not signed by its vendor");
31573
+ return async (file3) => {
31574
+ if (!await signedByAsync(file3, signer)) throw new Error(what + " is not signed by its vendor");
31545
31575
  };
31546
31576
  }
31547
31577
  async function signedInstaller(url2, file3, what, signer, sha256 = null) {
@@ -31550,7 +31580,7 @@ async function signedInstaller(url2, file3, what, signer, sha256 = null) {
31550
31580
  mkdirp(path42.dirname(file3));
31551
31581
  await downloader.file(url2, file3, { sha256, verify: signatureCheck(signer, path42.basename(file3)) });
31552
31582
  }
31553
- if (!signedBy(file3, signer)) {
31583
+ if (!await signedByAsync(file3, signer)) {
31554
31584
  fs25.rmSync(file3, { force: true });
31555
31585
  throw new Error(
31556
31586
  path42.basename(file3) + " is not signed by its vendor; deleted it (it is downloaded again next boot)"
@@ -31658,6 +31688,18 @@ function savedEntries() {
31658
31688
  function restoreLauncherItems() {
31659
31689
  return restoreItems(SAVED_MANIFESTS, launcherManifestsDir(programData()), ENGINES_DIR, exists);
31660
31690
  }
31691
+ function writePcLauncherItems(items) {
31692
+ const dir = launcherManifestsDir(programData());
31693
+ let written = 0;
31694
+ for (const i of items) {
31695
+ if (i.kind !== "unreal" || !i.launcherItem || !ITEM_NAME.test(i.launcherItem.name)) continue;
31696
+ const at = itemInstallLocation(i.launcherItem.text);
31697
+ if (!at || path44.win32.resolve(at).toLowerCase() !== path44.win32.resolve(i.guest).toLowerCase()) continue;
31698
+ fs26.writeFileSync(path44.join(mkdirp(dir), i.launcherItem.name), i.launcherItem.text, "utf8");
31699
+ written++;
31700
+ }
31701
+ return written;
31702
+ }
31661
31703
  function writeLauncherEntries(entries2) {
31662
31704
  if (!entries2.length) return;
31663
31705
  const file3 = launcherInstalledFile(programData());
@@ -31741,6 +31783,7 @@ async function registerEngines(engines, selectorFrom, ctx, aliases = []) {
31741
31783
  ];
31742
31784
  if (values.length) await runScriptWithJsonAsync("set-registry-values.ps1", "PlanFile", { values });
31743
31785
  try {
31786
+ writePcLauncherItems(ctx.pcInstalls.items);
31744
31787
  writeLauncherEntries(launcherEntries(engines, ctx));
31745
31788
  } catch (e) {
31746
31789
  warn("Unreal: the Epic Games Launcher's LauncherInstalled.dat was left as it is (" + errMsg(e) + ")");
@@ -32621,7 +32664,7 @@ async function installFromKept(dest, signer, what) {
32621
32664
  if (!installer2) return null;
32622
32665
  const installed = installedCometExe();
32623
32666
  if (fs35.existsSync(installed)) return installed;
32624
- if (!signedBy(installer2, signer)) {
32667
+ if (!await signedByAsync(installer2, signer)) {
32625
32668
  fs35.rmSync(path54.join(dest, ".agent-sandbox-app"), { force: true });
32626
32669
  fs35.rmSync(installer2, { force: true });
32627
32670
  throw new Error(what + "'s installer is not signed by its vendor; deleted it (it is downloaded again next boot)");
@@ -32923,7 +32966,7 @@ var MARKER = ".agent-sandbox-app";
32923
32966
  function prefetchedFile(entry, fileName) {
32924
32967
  return path58.join(tool("apps", ...entry.id.split("/")) + ".download", fileName.replace(/[\\/:*?"<>|]/g, "_"));
32925
32968
  }
32926
- var PARALLEL = 3;
32969
+ var PARALLEL = 2;
32927
32970
  var AppInstaller = class {
32928
32971
  constructor(env, options, parallel = PARALLEL) {
32929
32972
  this.env = env;
@@ -32944,19 +32987,21 @@ var AppInstaller = class {
32944
32987
  const todo = entries2.filter(
32945
32988
  (e) => !("remote" in e.source) && !("locked" in e.source) && !("store" in e.source) && !("npm" in e.source) && e.id !== "7zip" && (this.options.refresh || !exists(path58.join(tool("apps", ...e.id.split("/")), MARKER)))
32946
32989
  );
32947
- await Promise.all(
32948
- todo.map(async (entry) => {
32949
- try {
32950
- const d = await resolve(entry);
32951
- const file3 = prefetchedFile(entry, d.fileName);
32952
- if (exists(file3)) return;
32953
- await downloader.file(d.url, file3, {
32954
- sha256: d.sha256 ?? null,
32955
- ...entry.signer ? { verify: signatureCheck(entry.signer, d.fileName) } : {}
32956
- });
32957
- } catch {
32958
- }
32959
- })
32990
+ await asBackground(
32991
+ () => Promise.all(
32992
+ todo.map(async (entry) => {
32993
+ try {
32994
+ const d = await resolve(entry);
32995
+ const file3 = prefetchedFile(entry, d.fileName);
32996
+ if (exists(file3)) return;
32997
+ await downloader.file(d.url, file3, {
32998
+ sha256: d.sha256 ?? null,
32999
+ ...entry.signer ? { verify: signatureCheck(entry.signer, d.fileName) } : {}
33000
+ });
33001
+ } catch {
33002
+ }
33003
+ })
33004
+ )
32960
33005
  );
32961
33006
  }
32962
33007
  /** Installs the entries (7-Zip first: the NSIS apps need it to unpack), then their shortcuts. */
@@ -33013,7 +33058,7 @@ var AppInstaller = class {
33013
33058
  const exe = findExe(dest, entry.exe) ?? (entry.unpack === "chromium" && entry.signer ? await installFromKept(dest, entry.signer, entry.name) : null);
33014
33059
  if (!exe) throw new Error(entry.exe + " not found in the download");
33015
33060
  await afterInstall(entry, exe);
33016
- if (!entry.service && (entry.cli || entry.id === "sysinternals")) {
33061
+ if (!entry.service && (entry.cli || entry.onPath || entry.id === "sysinternals")) {
33017
33062
  if (entry.id === "rust-analyzer") this.env.prepend.unshift(path58.dirname(exe));
33018
33063
  else this.env.prepend.push(path58.dirname(exe));
33019
33064
  }
@@ -36168,7 +36213,7 @@ async function setupVcRuntime(env, summary = true) {
36168
36213
  log("Downloading the Visual C++ runtime (~25 MB once) ...");
36169
36214
  await downloadFile(VC_REDIST, installer2);
36170
36215
  }
36171
- if (!signedBy(installer2, SIGNERS.microsoft)) {
36216
+ if (!await signedByAsync(installer2, SIGNERS.microsoft)) {
36172
36217
  fs62.rmSync(installer2, { force: true });
36173
36218
  throw new Error("vc_redist.x64.exe is not signed by Microsoft; deleted it (it is downloaded again next boot)");
36174
36219
  }
@@ -38204,7 +38249,7 @@ function plan(config2, versions, o) {
38204
38249
 
38205
38250
  // src/guest/toolchains/step-scheduler.ts
38206
38251
  import { AsyncLocalStorage as AsyncLocalStorage4 } from "node:async_hooks";
38207
- var PARALLEL2 = 4;
38252
+ var PARALLEL2 = 3;
38208
38253
  async function runReported(sink2, name, env, work) {
38209
38254
  try {
38210
38255
  await work();