windows-development-sandbox 1.27.1 → 1.28.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 (121) 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-RSIX7C6D.js → chunk-CB44HTTN.js} +444 -175
  14. package/dist/bin/{chunk-4TLSWPG7.js → chunk-UZ5S3EMB.js} +1 -1
  15. package/dist/bin/{chunk-QUHNXHVG.js → chunk-ZMZI3625.js} +46 -41
  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/core/schema/sandbox.js +1 -1
  26. package/dist/guest/apps/app-installer.js +7 -5
  27. package/dist/guest/apps/comet-unpack.js +2 -2
  28. package/dist/guest/daemon/heartbeat.js +1 -0
  29. package/dist/guest/governor/core.d.ts +53 -1
  30. package/dist/guest/governor/core.js +95 -1
  31. package/dist/guest/governor/decide.d.ts +4 -0
  32. package/dist/guest/governor/decide.js +2 -0
  33. package/dist/guest/governor/governor.js +23 -9
  34. package/dist/guest/runtime/pools.d.ts +4 -4
  35. package/dist/guest/runtime/pools.js +4 -15
  36. package/dist/guest/runtime/process.d.ts +5 -2
  37. package/dist/guest/runtime/process.js +23 -3
  38. package/dist/guest/toolchains/engines/epic-launcher.d.ts +6 -0
  39. package/dist/guest/toolchains/engines/epic-launcher.js +19 -1
  40. package/dist/guest/toolchains/engines/unreal-engines.js +2 -1
  41. package/dist/guest/toolchains/installers.d.ts +3 -3
  42. package/dist/guest/toolchains/installers.js +5 -5
  43. package/dist/guest/toolchains/native/unreal.js +22 -3
  44. package/dist/guest/toolchains/native/vcruntime.js +2 -2
  45. package/dist/guest/toolchains/native/vs-persist.d.ts +2 -2
  46. package/dist/guest/toolchains/runtimes.js +7 -6
  47. package/dist/guest/toolchains/signature.d.ts +2 -0
  48. package/dist/guest/toolchains/signature.js +9 -2
  49. package/dist/guest/toolchains/step-scheduler.js +1 -1
  50. package/dist/host/accounts/deliver.js +15 -7
  51. package/dist/host/life/crash-report.d.ts +4 -0
  52. package/dist/host/life/crash-report.js +30 -1
  53. package/dist/host/life/last-end.js +1 -0
  54. package/dist/host/pc-installs/discover.js +5 -2
  55. package/dist/host/pc-installs/unreal.d.ts +3 -1
  56. package/dist/host/pc-installs/unreal.js +27 -1
  57. package/dist/host/tools/windows-terminal.d.ts +4 -1
  58. package/dist/host/tools/windows-terminal.js +25 -9
  59. package/dist/runtime/agent.js +39 -23
  60. package/dist/runtime/daemon.js +279 -121
  61. package/dist/runtime/db.js +37 -17
  62. package/dist/runtime/main.js +340 -109
  63. package/dist/runtime/netproxy.js +31 -13
  64. package/dist/runtime/tool.js +37 -16
  65. package/dist/runtime/view.js +150 -38
  66. package/dist/selftest/agent-paths.test.js +3 -1
  67. package/dist/selftest/app.test.js +20 -2
  68. package/dist/selftest/boot-runner.test.js +15 -1
  69. package/dist/selftest/channel.test.js +33 -0
  70. package/dist/selftest/engines.test.js +22 -1
  71. package/dist/selftest/heavy-load.test.js +116 -4
  72. package/dist/selftest/live-terminal.test.js +103 -0
  73. package/dist/selftest/menu-screens.js +52 -1
  74. package/dist/selftest/menu.test.js +29 -3
  75. package/dist/selftest/personalization.test.js +2 -1
  76. package/dist/selftest/service-accounts.test.js +77 -2
  77. package/dist/selftest/support/engine-golden.js +2 -0
  78. package/dist/selftest/ui-text.test.js +4 -0
  79. package/dist/shared/accounts/kinds.js +9 -9
  80. package/dist/shared/apps/catalogue-gamedev.js +1 -0
  81. package/dist/shared/apps/catalogue.d.ts +2 -0
  82. package/dist/shared/channel/schema.d.ts +2 -0
  83. package/dist/shared/channel/schema.js +4 -0
  84. package/dist/shared/concurrency/pools.js +1 -1
  85. package/dist/shared/download/archive.js +4 -2
  86. package/dist/shared/download/index.d.ts +1 -1
  87. package/dist/shared/download/index.js +1 -1
  88. package/dist/shared/download/spawn.d.ts +7 -1
  89. package/dist/shared/download/spawn.js +16 -1
  90. package/dist/shared/memory/index.d.ts +1 -0
  91. package/dist/shared/memory/index.js +1 -0
  92. package/dist/shared/memory/policy.d.ts +12 -0
  93. package/dist/shared/memory/policy.js +4 -0
  94. package/dist/shared/memory/unreal-build.d.ts +2 -0
  95. package/dist/shared/memory/unreal-build.js +21 -0
  96. package/dist/shared/paths/pc-installs.d.ts +8 -0
  97. package/dist/shared/pty/clipboard-watch.d.ts +4 -0
  98. package/dist/shared/pty/clipboard-watch.js +40 -0
  99. package/dist/shared/pty/ctrl-c.d.ts +13 -7
  100. package/dist/shared/pty/ctrl-c.js +35 -19
  101. package/dist/shared/pty/index.d.ts +1 -0
  102. package/dist/shared/pty/index.js +1 -0
  103. package/dist/shared/pty/terminal.d.ts +1 -1
  104. package/dist/shared/pty/terminal.js +16 -13
  105. package/dist/shared/unreal/launcher-files.d.ts +7 -0
  106. package/dist/shared/unreal/launcher-files.js +32 -1
  107. package/dist/shared/web/ctrl-c.d.ts +26 -0
  108. package/dist/shared/web/ctrl-c.js +64 -0
  109. package/dist/shared/web/index.d.ts +1 -0
  110. package/dist/shared/web/index.js +1 -0
  111. package/dist/web/index.html +1 -1
  112. package/dist/web/{main-VPMYTSUL.js → main-7NY4X6YU.js} +67 -67
  113. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  114. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  115. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  116. package/package.json +1 -1
  117. package/sandbox/csharp/ClipboardWatch.cs +59 -0
  118. package/sandbox/csharp/MemGov.cs +752 -7
  119. package/sandbox/scripts/watch-clipboard.ps1 +8 -0
  120. package/schemas/config.schema.json +10 -10
  121. package/schemas/sandbox-config.schema.json +9 -9
@@ -25494,7 +25494,7 @@ var SandboxSchema = external_exports.strictObject({
25494
25494
  'The last resort that keeps the sandbox up, in MB below its commit limit (RAM plus paging file): builds are slowed down long before (sandbox.pressureResponse); only if everything the agents start together would take the sandbox to the very edge of its memory, where Windows inside it would start failing, is a build refused memory instead of Windows. "auto" = the larger of 1536 MB and 3 % of the commit limit; "none" = no backstop. Yours only: a project file cannot change it.'
25495
25495
  ),
25496
25496
  pressureResponse: external_exports.boolean().describe(
25497
- "When the sandbox (or this PC) runs low on memory, slow builds down in steps, like a PC that gets slower instead of failing: build programs get lower CPU and memory priority, then less CPU, then compilers take turns (the waiting ones briefly suspended and trimmed to the paging file). Nothing is stopped or killed, and the agents themselves are never touched. Yours only: a project file cannot change it."
25497
+ "When the sandbox (or this PC) runs low on memory, slow builds down in steps, like a PC that gets slower instead of failing: build programs get lower CPU and memory priority, then less CPU, then compilers take turns (the waiting ones briefly suspended and trimmed to the paging file). Nothing is stopped or killed, and the agents themselves are never touched. A memory gate also holds new compilers and linkers for a moment when the memory of the build programs grows faster than 4 GB in 3 seconds (3 GB with vGPU), and lets them go one at a time. Yours only: a project file cannot change it."
25498
25498
  ),
25499
25499
  vGpu: external_exports.boolean().describe(
25500
25500
  "Virtual GPU. Off by default: Microsoft notes it widens the sandbox attack surface. The sandbox then uses this PC's graphics card, and with some graphics drivers heavy use can freeze the whole PC: turn it on only when a program in the sandbox needs the GPU (the mesa toolchain gives software OpenGL without it). Browsers in the sandbox never use it (they draw in software)."
@@ -26086,6 +26086,7 @@ var GAMEDEV_APPS = [
26086
26086
  size: "100 MB",
26087
26087
  source: { github: "f3d-app/f3d", asset: "^F3D-[\\d.]+-Windows-x86_64\\.zip$" },
26088
26088
  exe: "f3d.exe",
26089
+ onPath: true,
26089
26090
  needs: ["mesa"]
26090
26091
  },
26091
26092
  {
@@ -27426,7 +27427,7 @@ var SERVICE_SPECS = {
27426
27427
  },
27427
27428
  steam: {
27428
27429
  kind: "steam",
27429
- label: "Steam (SteamCMD)",
27430
+ label: "Steam",
27430
27431
  usedBy: ["steam"],
27431
27432
  sources: ["file", "sandbox"],
27432
27433
  singleHolder: true,
@@ -27462,7 +27463,7 @@ var SERVICE_SPECS = {
27462
27463
  },
27463
27464
  cfx: {
27464
27465
  kind: "cfx",
27465
- label: "Cfx.re (FiveM / RedM server key)",
27466
+ label: "FiveM key",
27466
27467
  usedBy: ["fivem"],
27467
27468
  sources: ["typed", "file"],
27468
27469
  singleHolder: false,
@@ -27491,7 +27492,7 @@ var SERVICE_SPECS = {
27491
27492
  },
27492
27493
  keystore: {
27493
27494
  kind: "keystore",
27494
- label: "Android upload keystore",
27495
+ label: "Android keystore",
27495
27496
  usedBy: ["android"],
27496
27497
  sources: ["file"],
27497
27498
  singleHolder: false,
@@ -27506,7 +27507,7 @@ var SERVICE_SPECS = {
27506
27507
  },
27507
27508
  play: {
27508
27509
  kind: "play",
27509
- label: "Google Play (service account)",
27510
+ label: "Google Play",
27510
27511
  usedBy: ["android"],
27511
27512
  sources: ["file"],
27512
27513
  singleHolder: false,
@@ -27517,7 +27518,7 @@ var SERVICE_SPECS = {
27517
27518
  },
27518
27519
  expo: {
27519
27520
  kind: "expo",
27520
- label: "Expo (EAS)",
27521
+ label: "Expo",
27521
27522
  usedBy: ["reactNative"],
27522
27523
  sources: ["typed"],
27523
27524
  singleHolder: false,
@@ -27528,7 +27529,7 @@ var SERVICE_SPECS = {
27528
27529
  },
27529
27530
  appstore: {
27530
27531
  kind: "appstore",
27531
- label: "App Store Connect API key",
27532
+ label: "App Store Connect",
27532
27533
  usedBy: ["reactNative", "flutter"],
27533
27534
  sources: ["file", "typed"],
27534
27535
  singleHolder: false,
@@ -27542,7 +27543,7 @@ var SERVICE_SPECS = {
27542
27543
  },
27543
27544
  perplexity: {
27544
27545
  kind: "perplexity",
27545
- label: "Perplexity API key",
27546
+ label: "Perplexity API",
27546
27547
  usedBy: ["perplexity"],
27547
27548
  sources: ["pc", "file", "typed"],
27548
27549
  singleHolder: false,
@@ -27566,7 +27567,7 @@ var SERVICE_SPECS = {
27566
27567
  },
27567
27568
  comet: {
27568
27569
  kind: "comet",
27569
- label: "Comet (Perplexity browser)",
27570
+ label: "Comet",
27570
27571
  usedBy: ["comet"],
27571
27572
  sources: ["sandbox"],
27572
27573
  singleHolder: false,
@@ -27582,7 +27583,7 @@ var SERVICE_SPECS = {
27582
27583
  },
27583
27584
  perplexityapp: {
27584
27585
  kind: "perplexityapp",
27585
- label: "Perplexity desktop app",
27586
+ label: "Perplexity app",
27586
27587
  usedBy: ["perplexityapp"],
27587
27588
  sources: ["sandbox"],
27588
27589
  singleHolder: false,
@@ -27933,6 +27934,8 @@ var PressureStatusSchema = external_exports.object({
27933
27934
  lowered: external_exports.number().int().min(0).max(1024),
27934
27935
  /** Compilers waiting their turn right now (name, process id, seconds until they may go on). */
27935
27936
  paused: external_exports.array(external_exports.object({ name: Text(64), pid: external_exports.number().int(), seconds: external_exports.number().int().min(0).max(60) })).max(8),
27937
+ /** Build programs the memory gate holds right now (let go one at a time). */
27938
+ held: external_exports.number().int().min(0).max(1024).optional(),
27936
27939
  /** The CPU share the compilers get together (percent; 100 = no cap). */
27937
27940
  cpuPercent: external_exports.number().int().min(0).max(100).optional(),
27938
27941
  /** The PC itself asked for slowing down (its memory or CPU is low). */
@@ -27953,6 +27956,8 @@ var HeartbeatSchema = external_exports.object({
27953
27956
  topProcesses: external_exports.array(external_exports.object({ name: Text(64), privateMb: external_exports.number().int().min(0).max(1e7) })).max(5),
27954
27957
  /** The disk queue this PC last reported while asking the sandbox to slow down. */
27955
27958
  diskQueueReported: external_exports.number().min(0).max(1e5).optional(),
27959
+ /** Build programs the memory gate holds right now (let go one at a time). */
27960
+ held: external_exports.number().int().min(0).max(1024).optional(),
27956
27961
  /** "ending": the sandbox is shutting down in order (idle shutdown, sandbox stop, Windows shutting down). */
27957
27962
  phase: external_exports.enum(["ready", "working", "ending"])
27958
27963
  });
@@ -28363,7 +28368,7 @@ var logsDir = path9.join(dataDir, "logs");
28363
28368
  var run = new RunDir(dataDir);
28364
28369
 
28365
28370
  // src/guest/runtime/status.ts
28366
- import os3 from "node:os";
28371
+ import os4 from "node:os";
28367
28372
 
28368
28373
  // src/core/util/errors.ts
28369
28374
  function errMsg(e) {
@@ -28548,10 +28553,20 @@ function scriptArgs(name, args = []) {
28548
28553
  }
28549
28554
 
28550
28555
  // src/shared/download/spawn.ts
28556
+ import os3 from "node:os";
28551
28557
  import { spawn } from "node:child_process";
28558
+ function lowerPriority(pid) {
28559
+ if (pid === void 0) return;
28560
+ try {
28561
+ os3.setPriority(pid, os3.constants.priority.PRIORITY_BELOW_NORMAL);
28562
+ } catch {
28563
+ }
28564
+ }
28552
28565
  function spawnAsync(file3, args, options = {}) {
28566
+ const { lowPriority, ...spawnOptions } = options;
28553
28567
  return new Promise((resolve2, reject) => {
28554
- const child = spawn(file3, args, { stdio: "ignore", windowsHide: true, ...options });
28568
+ const child = spawn(file3, args, { stdio: "ignore", windowsHide: true, ...spawnOptions });
28569
+ if (lowPriority) lowerPriority(child.pid);
28555
28570
  child.once("error", reject);
28556
28571
  child.once("close", (code) => resolve2(code ?? -1));
28557
28572
  });
@@ -28575,13 +28590,16 @@ function extractArchive(archive, destination, members2 = []) {
28575
28590
  }
28576
28591
  async function extract(archive, destination, members2) {
28577
28592
  fs10.mkdirSync(destination, { recursive: true });
28578
- let status = await spawnAsync(SYSTEM.tar(), ["-xf", archive, "-C", destination, ...members2]);
28593
+ let status = await spawnAsync(SYSTEM.tar(), ["-xf", archive, "-C", destination, ...members2], {
28594
+ lowPriority: true
28595
+ });
28579
28596
  if (status !== 0 && /\.(zip|nupkg)$/i.test(archive)) {
28580
28597
  const zip = archive.endsWith(".zip") ? archive : archive + ".zip";
28581
28598
  if (zip !== archive) fs10.copyFileSync(archive, zip);
28582
28599
  status = await spawnAsync(
28583
28600
  SYSTEM.powershell(),
28584
- scriptArgs("expand-archive.ps1", ["-Archive", zip, "-Destination", destination])
28601
+ scriptArgs("expand-archive.ps1", ["-Archive", zip, "-Destination", destination]),
28602
+ { lowPriority: true }
28585
28603
  );
28586
28604
  }
28587
28605
  if (status !== 0) throw new Error("Could not extract " + archive);
@@ -28825,7 +28843,7 @@ function setBackgroundWork(work) {
28825
28843
  }
28826
28844
  }
28827
28845
  function ownAddresses() {
28828
- return Object.values(os3.networkInterfaces()).flat().filter((i) => Boolean(i) && i.family === "IPv4" && !i.internal).map((i) => i.address).slice(0, 8);
28846
+ return Object.values(os4.networkInterfaces()).flat().filter((i) => Boolean(i) && i.family === "IPv4" && !i.internal).map((i) => i.address).slice(0, 8);
28829
28847
  }
28830
28848
  function writeStatus(phase, message) {
28831
28849
  writeJsonAtomic(run.sandboxFile, {
@@ -28845,7 +28863,7 @@ function writeStatus(phase, message) {
28845
28863
  // src/guest/boot/boot.ts
28846
28864
  import fs95 from "node:fs";
28847
28865
  import path126 from "node:path";
28848
- import { spawn as spawn16 } from "node:child_process";
28866
+ import { spawn as spawn17 } from "node:child_process";
28849
28867
 
28850
28868
  // src/guest/boot/guide.ts
28851
28869
  import fs23 from "node:fs";
@@ -28893,7 +28911,7 @@ import path18 from "node:path";
28893
28911
 
28894
28912
  // src/guest/runtime/process.ts
28895
28913
  import fs14 from "node:fs";
28896
- import os4 from "node:os";
28914
+ import os5 from "node:os";
28897
28915
  import path17 from "node:path";
28898
28916
  import { spawn as spawn2, spawnSync } from "node:child_process";
28899
28917
  var outputSink = null;
@@ -28931,13 +28949,15 @@ function exec(file3, args, options = {}) {
28931
28949
  throw new Error(path17.basename(file3) + " " + args.slice(0, 3).join(" ") + " failed with exit code " + result.status);
28932
28950
  }
28933
28951
  async function execAsync(file3, args, options = {}) {
28952
+ const { lowPriority, ...spawnOptions } = options;
28934
28953
  const code = captureOutput() ? await new Promise((resolve2, reject) => {
28935
- const child = spawn2(file3, args, { stdio: ["inherit", "pipe", "pipe"], windowsHide: true, ...options });
28954
+ const child = spawn2(file3, args, { stdio: ["inherit", "pipe", "pipe"], windowsHide: true, ...spawnOptions });
28955
+ if (lowPriority) lowerPriority(child.pid);
28936
28956
  logLines(child.stdout);
28937
28957
  logLines(child.stderr);
28938
28958
  child.once("error", reject);
28939
28959
  child.once("close", (c) => resolve2(c ?? -1));
28940
- }) : await spawnAsync(file3, args, { stdio: "inherit", ...options });
28960
+ }) : await spawnAsync(file3, args, { stdio: "inherit", ...spawnOptions, ...lowPriority ? { lowPriority } : {} });
28941
28961
  if (code !== 0)
28942
28962
  throw new Error(path17.basename(file3) + " " + args.slice(0, 3).join(" ") + " failed with exit code " + code);
28943
28963
  }
@@ -28991,8 +29011,23 @@ function runScriptAsync(name, args = []) {
28991
29011
  function scriptOutput(name, args = [], options = {}) {
28992
29012
  return execOutput(SYSTEM.powershell(), scriptArgs(name, args), options);
28993
29013
  }
29014
+ function scriptOutputAsync(name, args = []) {
29015
+ return new Promise((resolve2) => {
29016
+ const child = spawn2(SYSTEM.powershell(), scriptArgs(name, args), {
29017
+ stdio: ["ignore", "pipe", "ignore"],
29018
+ windowsHide: true
29019
+ });
29020
+ let out = "";
29021
+ child.stdout?.setEncoding("utf8");
29022
+ child.stdout?.on("data", (chunk) => {
29023
+ if (out.length < CAPTURE_BYTES) out += chunk;
29024
+ });
29025
+ child.once("error", () => resolve2(""));
29026
+ child.once("close", (code) => resolve2(code === 0 ? out.trim() : ""));
29027
+ });
29028
+ }
28994
29029
  function runScriptWithJson(name, param, data, args = []) {
28995
- const dir = fs14.mkdtempSync(path17.join(os4.tmpdir(), "sbx-"));
29030
+ const dir = fs14.mkdtempSync(path17.join(os5.tmpdir(), "sbx-"));
28996
29031
  const file3 = path17.join(dir, "input.json");
28997
29032
  try {
28998
29033
  fs14.writeFileSync(file3, JSON.stringify(data), "utf8");
@@ -29002,7 +29037,7 @@ function runScriptWithJson(name, param, data, args = []) {
29002
29037
  }
29003
29038
  }
29004
29039
  async function runScriptWithJsonAsync(name, param, data, args = []) {
29005
- const dir = fs14.mkdtempSync(path17.join(os4.tmpdir(), "sbx-"));
29040
+ const dir = fs14.mkdtempSync(path17.join(os5.tmpdir(), "sbx-"));
29006
29041
  const file3 = path17.join(dir, "input.json");
29007
29042
  try {
29008
29043
  fs14.writeFileSync(file3, JSON.stringify(data), "utf8");
@@ -29451,13 +29486,14 @@ function itemSizes(saved) {
29451
29486
  }
29452
29487
  return sizes;
29453
29488
  }
29489
+ var ITEM_NAME = /^[A-Za-z0-9_-]{1,80}\.item$/;
29454
29490
  function isItemUnder(text2, root) {
29455
29491
  const at = itemInstallLocation(text2);
29456
29492
  return at !== null && isWithin(root, at) && win2.resolve(at) !== win2.resolve(root);
29457
29493
  }
29458
29494
  function itemFiles(dir) {
29459
29495
  try {
29460
- return fs16.readdirSync(dir).filter((f) => /^[A-Za-z0-9_-]{1,80}\.item$/.test(f));
29496
+ return fs16.readdirSync(dir).filter((f) => ITEM_NAME.test(f));
29461
29497
  } catch {
29462
29498
  return [];
29463
29499
  }
@@ -30706,7 +30742,6 @@ import fs21 from "node:fs";
30706
30742
  import path34 from "node:path";
30707
30743
 
30708
30744
  // src/guest/runtime/pools.ts
30709
- import os5 from "node:os";
30710
30745
  import { spawn as spawn4 } from "node:child_process";
30711
30746
  import path31 from "node:path";
30712
30747
 
@@ -30780,7 +30815,7 @@ function installerRetry(code, attempt) {
30780
30815
  }
30781
30816
 
30782
30817
  // src/shared/concurrency/pools.ts
30783
- var net2 = new Limiter(4, "net");
30818
+ var net2 = new Limiter(3, "net");
30784
30819
  var heavy = new Limiter(2, "heavy");
30785
30820
  var installer = new Limiter(1, "installer");
30786
30821
 
@@ -30789,14 +30824,7 @@ function installPools() {
30789
30824
  downloader.limit = net2;
30790
30825
  limitExtraction(heavy);
30791
30826
  }
30792
- var BELOW_NORMAL = os5.constants.priority.PRIORITY_BELOW_NORMAL;
30793
- function belowNormal(pid) {
30794
- if (pid === void 0) return;
30795
- try {
30796
- os5.setPriority(pid, BELOW_NORMAL);
30797
- } catch {
30798
- }
30799
- }
30827
+ var belowNormal = lowerPriority;
30800
30828
  async function runInstaller(file3, args, o = {}) {
30801
30829
  for (let attempt = 1; ; attempt++) {
30802
30830
  const code = await installer.run(
@@ -30822,7 +30850,7 @@ async function runInstallerChecked(file3, args, o = {}) {
30822
30850
  throw new Error(path31.basename(file3) + " " + args.slice(0, 3).join(" ") + " failed with exit code " + code);
30823
30851
  }
30824
30852
  function execHeavy(file3, args, options = {}) {
30825
- return heavy.run(() => execAsync(file3, args, options));
30853
+ return heavy.run(() => execAsync(file3, args, { ...options, lowPriority: true }));
30826
30854
  }
30827
30855
 
30828
30856
  // src/guest/toolchains/installers.ts
@@ -30832,7 +30860,10 @@ import path32 from "node:path";
30832
30860
  // src/guest/toolchains/signature.ts
30833
30861
  var SignatureSchema = external_exports.object({ subject: external_exports.string().max(4096), issuer: external_exports.string().max(4096) });
30834
30862
  function signatureOf(file3) {
30835
- const text2 = scriptOutput("get-signature.ps1", ["-Path", file3]).trim();
30863
+ return parseSignature(scriptOutput("get-signature.ps1", ["-Path", file3]));
30864
+ }
30865
+ function parseSignature(output3) {
30866
+ const text2 = output3.trim();
30836
30867
  if (!text2) return null;
30837
30868
  try {
30838
30869
  const parsed = SignatureSchema.safeParse(JSON.parse(text2));
@@ -30844,11 +30875,14 @@ function signatureOf(file3) {
30844
30875
  function signedBy(file3, signer) {
30845
30876
  return signerMatches(signatureOf(file3), signer);
30846
30877
  }
30878
+ async function signedByAsync(file3, signer) {
30879
+ return signerMatches(parseSignature(await scriptOutputAsync("get-signature.ps1", ["-Path", file3])), signer);
30880
+ }
30847
30881
 
30848
30882
  // src/guest/toolchains/installers.ts
30849
30883
  function signatureCheck(signer, what) {
30850
- return (file3) => {
30851
- if (!signedBy(file3, signer)) throw new Error(what + " is not signed by its vendor");
30884
+ return async (file3) => {
30885
+ if (!await signedByAsync(file3, signer)) throw new Error(what + " is not signed by its vendor");
30852
30886
  };
30853
30887
  }
30854
30888
  async function signedInstaller(url2, file3, what, signer, sha256 = null) {
@@ -30857,7 +30891,7 @@ async function signedInstaller(url2, file3, what, signer, sha256 = null) {
30857
30891
  mkdirp(path32.dirname(file3));
30858
30892
  await downloader.file(url2, file3, { sha256, verify: signatureCheck(signer, path32.basename(file3)) });
30859
30893
  }
30860
- if (!signedBy(file3, signer)) {
30894
+ if (!await signedByAsync(file3, signer)) {
30861
30895
  fs20.rmSync(file3, { force: true });
30862
30896
  throw new Error(
30863
30897
  path32.basename(file3) + " is not signed by its vendor; deleted it (it is downloaded again next boot)"
@@ -30965,6 +30999,18 @@ function savedEntries() {
30965
30999
  function restoreLauncherItems() {
30966
31000
  return restoreItems(SAVED_MANIFESTS, launcherManifestsDir(programData()), ENGINES_DIR, exists);
30967
31001
  }
31002
+ function writePcLauncherItems(items) {
31003
+ const dir = launcherManifestsDir(programData());
31004
+ let written = 0;
31005
+ for (const i of items) {
31006
+ if (i.kind !== "unreal" || !i.launcherItem || !ITEM_NAME.test(i.launcherItem.name)) continue;
31007
+ const at = itemInstallLocation(i.launcherItem.text);
31008
+ if (!at || path34.win32.resolve(at).toLowerCase() !== path34.win32.resolve(i.guest).toLowerCase()) continue;
31009
+ fs21.writeFileSync(path34.join(mkdirp(dir), i.launcherItem.name), i.launcherItem.text, "utf8");
31010
+ written++;
31011
+ }
31012
+ return written;
31013
+ }
30968
31014
  function writeLauncherEntries(entries3) {
30969
31015
  if (!entries3.length) return;
30970
31016
  const file3 = launcherInstalledFile(programData());
@@ -31029,6 +31075,7 @@ async function registerEngines(engines, selectorFrom, ctx, aliases = []) {
31029
31075
  ];
31030
31076
  if (values.length) await runScriptWithJsonAsync("set-registry-values.ps1", "PlanFile", { values });
31031
31077
  try {
31078
+ writePcLauncherItems(ctx.pcInstalls.items);
31032
31079
  writeLauncherEntries(launcherEntries(engines, ctx));
31033
31080
  } catch (e) {
31034
31081
  warn("Unreal: the Epic Games Launcher's LauncherInstalled.dat was left as it is (" + errMsg(e) + ")");
@@ -31353,11 +31400,10 @@ async function setupBun(env) {
31353
31400
  tool("bun", "bin"),
31354
31401
  { size: "35 MB" }
31355
31402
  );
31356
- Object.assign(env.vars, {
31357
- BUN_INSTALL: tool("bun"),
31358
- BUN_INSTALL_CACHE_DIR: path38.join(localDir, "bun-cache"),
31359
- BUN_TMPDIR: path38.join(localDir, "bun-tmp")
31360
- });
31403
+ const cache = path38.join(localDir, "bun-cache");
31404
+ const temp = path38.join(localDir, "bun-tmp");
31405
+ if (!dryRun) [cache, temp].forEach((dir) => mkdirp(dir));
31406
+ Object.assign(env.vars, { BUN_INSTALL: tool("bun"), BUN_INSTALL_CACHE_DIR: cache, BUN_TMPDIR: temp });
31361
31407
  }
31362
31408
  async function setupDeno(env) {
31363
31409
  await setupSingleExe(
@@ -31912,7 +31958,7 @@ async function installFromKept(dest, signer, what) {
31912
31958
  if (!installer2) return null;
31913
31959
  const installed = installedCometExe();
31914
31960
  if (fs29.existsSync(installed)) return installed;
31915
- if (!signedBy(installer2, signer)) {
31961
+ if (!await signedByAsync(installer2, signer)) {
31916
31962
  fs29.rmSync(path44.join(dest, ".agent-sandbox-app"), { force: true });
31917
31963
  fs29.rmSync(installer2, { force: true });
31918
31964
  throw new Error(what + "'s installer is not signed by its vendor; deleted it (it is downloaded again next boot)");
@@ -32214,7 +32260,7 @@ var MARKER = ".agent-sandbox-app";
32214
32260
  function prefetchedFile(entry, fileName) {
32215
32261
  return path48.join(tool("apps", ...entry.id.split("/")) + ".download", fileName.replace(/[\\/:*?"<>|]/g, "_"));
32216
32262
  }
32217
- var PARALLEL = 3;
32263
+ var PARALLEL = 2;
32218
32264
  var AppInstaller = class {
32219
32265
  constructor(env, options, parallel = PARALLEL) {
32220
32266
  this.env = env;
@@ -32235,19 +32281,21 @@ var AppInstaller = class {
32235
32281
  const todo = entries3.filter(
32236
32282
  (e) => !("remote" in e.source) && !("locked" in e.source) && !("store" in e.source) && !("npm" in e.source) && e.id !== "7zip" && (this.options.refresh || !exists(path48.join(tool("apps", ...e.id.split("/")), MARKER)))
32237
32283
  );
32238
- await Promise.all(
32239
- todo.map(async (entry) => {
32240
- try {
32241
- const d = await resolve(entry);
32242
- const file3 = prefetchedFile(entry, d.fileName);
32243
- if (exists(file3)) return;
32244
- await downloader.file(d.url, file3, {
32245
- sha256: d.sha256 ?? null,
32246
- ...entry.signer ? { verify: signatureCheck(entry.signer, d.fileName) } : {}
32247
- });
32248
- } catch {
32249
- }
32250
- })
32284
+ await asBackground(
32285
+ () => Promise.all(
32286
+ todo.map(async (entry) => {
32287
+ try {
32288
+ const d = await resolve(entry);
32289
+ const file3 = prefetchedFile(entry, d.fileName);
32290
+ if (exists(file3)) return;
32291
+ await downloader.file(d.url, file3, {
32292
+ sha256: d.sha256 ?? null,
32293
+ ...entry.signer ? { verify: signatureCheck(entry.signer, d.fileName) } : {}
32294
+ });
32295
+ } catch {
32296
+ }
32297
+ })
32298
+ )
32251
32299
  );
32252
32300
  }
32253
32301
  /** Installs the entries (7-Zip first: the NSIS apps need it to unpack), then their shortcuts. */
@@ -32304,7 +32352,7 @@ var AppInstaller = class {
32304
32352
  const exe = findExe(dest, entry.exe) ?? (entry.unpack === "chromium" && entry.signer ? await installFromKept(dest, entry.signer, entry.name) : null);
32305
32353
  if (!exe) throw new Error(entry.exe + " not found in the download");
32306
32354
  await afterInstall(entry, exe);
32307
- if (!entry.service && (entry.cli || entry.id === "sysinternals")) {
32355
+ if (!entry.service && (entry.cli || entry.onPath || entry.id === "sysinternals")) {
32308
32356
  if (entry.id === "rust-analyzer") this.env.prepend.unshift(path48.dirname(exe));
32309
32357
  else this.env.prepend.push(path48.dirname(exe));
32310
32358
  }
@@ -34195,6 +34243,49 @@ import fs52 from "node:fs";
34195
34243
  import os9 from "node:os";
34196
34244
  import path73 from "node:path";
34197
34245
 
34246
+ // src/shared/memory/policy.ts
34247
+ function buildJobs(cpus, memoryGb) {
34248
+ return Math.max(2, Math.min(cpus, Math.floor((memoryGb - 6) / 3)));
34249
+ }
34250
+ function usableCpus(cpus, percent) {
34251
+ if (percent === null || percent >= 100) return cpus;
34252
+ return Math.max(2, Math.min(cpus, Math.floor(cpus * percent / 100)));
34253
+ }
34254
+ function cargoJobs(cpus, memoryGb, sessions) {
34255
+ const byCpu = Math.max(2, Math.min(8, Math.floor(cpus / 4)));
34256
+ const byMemory = Math.max(1, Math.floor(memoryGb / 4));
34257
+ const jobs = Math.min(byCpu, byMemory);
34258
+ return Math.max(1, Math.min(jobs, Math.floor(buildJobs(cpus, memoryGb) / Math.max(1, sessions))));
34259
+ }
34260
+ var LOCAL_TARGET_MIN_BYTES = 25 * 1024 ** 3;
34261
+ function chooseTargetDir(targetDir, freeBytes2) {
34262
+ if (targetDir !== "auto") return { targetDir };
34263
+ if (freeBytes2 >= LOCAL_TARGET_MIN_BYTES) return { targetDir: "local" };
34264
+ return {
34265
+ targetDir: "separate",
34266
+ warning: "Rust builds write through the mapped workspace; this can overload your PC's disk (C: has less than 25 GB free for a local build folder)"
34267
+ };
34268
+ }
34269
+ function cargoLinkerVar(host) {
34270
+ return "CARGO_TARGET_" + host.toUpperCase().replace(/[^A-Z0-9]/g, "_") + "_LINKER";
34271
+ }
34272
+
34273
+ // src/shared/memory/unreal-build.ts
34274
+ var NAMESPACE = "https://www.unrealengine.com/BuildConfiguration";
34275
+ function ubtBuildConfiguration(jobs) {
34276
+ const max = Math.max(1, Math.floor(jobs));
34277
+ return [
34278
+ '<?xml version="1.0" encoding="utf-8"?>',
34279
+ "<!-- Written by the sandbox at boot: compiles at once from the build budget (run\\budget.json, buildJobs). -->",
34280
+ '<Configuration xmlns="' + NAMESPACE + '">',
34281
+ " <BuildConfiguration>",
34282
+ " <MaxParallelActions>" + max + "</MaxParallelActions>",
34283
+ " </BuildConfiguration>",
34284
+ "</Configuration>",
34285
+ ""
34286
+ ].join("\r\n");
34287
+ }
34288
+
34198
34289
  // src/guest/toolchains/native/debuggers.ts
34199
34290
  import path69 from "node:path";
34200
34291
  var SDK_SETUP_URL = "https://go.microsoft.com/fwlink/?linkid=2311806";
@@ -34924,6 +35015,14 @@ async function checkVisualStudio() {
34924
35015
  fs52.rmSync(dir, { recursive: true, force: true });
34925
35016
  }
34926
35017
  }
35018
+ function capUnrealBuilds() {
35019
+ const cpus = usableCpus(os9.cpus().length, session.cpuLimitPercent ?? null);
35020
+ const jobs = buildJobs(cpus, os9.totalmem() / 1024 ** 3);
35021
+ const appData = process.env.APPDATA ?? "C:\\Users\\WDAGUtilityAccount\\AppData\\Roaming";
35022
+ const dir = mkdirp(path73.join(appData, "Unreal Engine", "UnrealBuildTool"));
35023
+ fs52.writeFileSync(path73.join(dir, "BuildConfiguration.xml"), ubtBuildConfiguration(jobs), "utf8");
35024
+ return jobs;
35025
+ }
34927
35026
  async function part(what, work, fallback) {
34928
35027
  try {
34929
35028
  return await work();
@@ -34942,6 +35041,8 @@ async function setupUnreal(env, config3, _ctx) {
34942
35041
  return;
34943
35042
  }
34944
35043
  resetUnrealStatus();
35044
+ const jobs = await part("capping parallel compiles", async () => capUnrealBuilds(), 0);
35045
+ if (jobs) log("Unreal: UnrealBuildTool runs at most " + jobs + " compiles at once (the build budget)");
34945
35046
  await part("the DirectX End-User Runtime", installDirectX, void 0);
34946
35047
  const launcher = await part("the Epic Games Launcher", ensureLauncher, null);
34947
35048
  const place = placeEngines(config3.persistEngines);
@@ -34977,7 +35078,7 @@ async function setupUnreal(env, config3, _ctx) {
34977
35078
  debugger: debuggers ? path73.join(debuggers, "cdb.exe") : null
34978
35079
  }));
34979
35080
  env.summary.push(
34980
- "unreal tools: " + (vs ? "Visual Studio " + vs.version + (check2.ok ? " (UnrealBuildTool finds it)" : " (UnrealBuildTool would NOT find it)") : "no Visual Studio") + ", DirectX End-User Runtime" + (launcher ? ", Epic Games Launcher" : "") + (debuggers ? ", cdb/windbg" : "")
35081
+ "unreal tools: " + (vs ? "Visual Studio " + vs.version + (check2.ok ? " (UnrealBuildTool finds it)" : " (UnrealBuildTool would NOT find it)") : "no Visual Studio") + (jobs ? ", at most " + jobs + " compiles at once" : "") + ", DirectX End-User Runtime" + (launcher ? ", Epic Games Launcher" : "") + (debuggers ? ", cdb/windbg" : "")
34981
35082
  );
34982
35083
  }
34983
35084
 
@@ -35406,7 +35507,7 @@ async function setupVcRuntime(env, summary = true) {
35406
35507
  log("Downloading the Visual C++ runtime (~25 MB once) ...");
35407
35508
  await downloadFile(VC_REDIST, installer2);
35408
35509
  }
35409
- if (!signedBy(installer2, SIGNERS.microsoft)) {
35510
+ if (!await signedByAsync(installer2, SIGNERS.microsoft)) {
35410
35511
  fs56.rmSync(installer2, { force: true });
35411
35512
  throw new Error("vc_redist.x64.exe is not signed by Microsoft; deleted it (it is downloaded again next boot)");
35412
35513
  }
@@ -38304,33 +38405,69 @@ import fs67 from "node:fs";
38304
38405
  import os11 from "node:os";
38305
38406
  import path94 from "node:path";
38306
38407
 
38307
- // src/shared/memory/policy.ts
38308
- function buildJobs(cpus, memoryGb) {
38309
- return Math.max(2, Math.min(cpus, Math.floor((memoryGb - 6) / 3)));
38310
- }
38311
- function cargoJobs(cpus, memoryGb, sessions) {
38312
- const byCpu = Math.max(2, Math.min(8, Math.floor(cpus / 4)));
38313
- const byMemory = Math.max(1, Math.floor(memoryGb / 4));
38314
- const jobs = Math.min(byCpu, byMemory);
38315
- return Math.max(1, Math.min(jobs, Math.floor(buildJobs(cpus, memoryGb) / Math.max(1, sessions))));
38316
- }
38317
- var LOCAL_TARGET_MIN_BYTES = 25 * 1024 ** 3;
38318
- function chooseTargetDir(targetDir, freeBytes2) {
38319
- if (targetDir !== "auto") return { targetDir };
38320
- if (freeBytes2 >= LOCAL_TARGET_MIN_BYTES) return { targetDir: "local" };
38321
- return {
38322
- targetDir: "separate",
38323
- warning: "Rust builds write through the mapped workspace; this can overload your PC's disk (C: has less than 25 GB free for a local build folder)"
38324
- };
38325
- }
38326
- function cargoLinkerVar(host) {
38327
- return "CARGO_TARGET_" + host.toUpperCase().replace(/[^A-Z0-9]/g, "_") + "_LINKER";
38328
- }
38329
-
38330
38408
  // src/guest/governor/decide.ts
38409
+ var COMPILERS = /* @__PURE__ */ new Set([
38410
+ "rustc",
38411
+ "clippy-driver",
38412
+ "rustdoc",
38413
+ "cc1",
38414
+ "cc1plus",
38415
+ "cc1obj",
38416
+ "collect2",
38417
+ "gcc",
38418
+ "g++",
38419
+ "c++",
38420
+ "cc",
38421
+ "clang",
38422
+ "clang++",
38423
+ "clang-cl",
38424
+ "cl",
38425
+ "link",
38426
+ "lld",
38427
+ "lld-link",
38428
+ "ld",
38429
+ "ld.lld",
38430
+ "as",
38431
+ "tsc",
38432
+ "javac"
38433
+ ]);
38434
+ var NEVER2 = /* @__PURE__ */ new Set([
38435
+ "claude",
38436
+ "codex",
38437
+ "gemini",
38438
+ "opencode",
38439
+ "aider",
38440
+ "cursor-agent",
38441
+ "cmd",
38442
+ "powershell",
38443
+ "pwsh",
38444
+ "bash",
38445
+ "sh",
38446
+ "zsh",
38447
+ "git",
38448
+ "conhost",
38449
+ "openconsole",
38450
+ "windowsterminal",
38451
+ "wt",
38452
+ "ptyhost",
38453
+ "memgov",
38454
+ "sandboxtools",
38455
+ "sbx-db",
38456
+ "explorer",
38457
+ "csrss",
38458
+ "wininit",
38459
+ "winlogon",
38460
+ "services",
38461
+ "svchost",
38462
+ "lsass",
38463
+ "system",
38464
+ "smss",
38465
+ "dwm"
38466
+ ]);
38331
38467
  function linkerSlots(totalMb, cpus) {
38332
38468
  return Math.max(1, Math.min(4, Math.max(1, Math.floor(cpus)), Math.floor(totalMb / 6144)));
38333
38469
  }
38470
+ var GATE_NAMES = [...COMPILERS, "shadercompileworker"].filter((n) => !NEVER2.has(n)).sort();
38334
38471
 
38335
38472
  // src/guest/toolchains/native/rust-extras.ts
38336
38473
  function resolveTargetDir(rust, free = freeBytes((process.env.SystemDrive ?? "C:") + "\\"), note = warn) {
@@ -38645,7 +38782,7 @@ function plan(config3, versions, o) {
38645
38782
 
38646
38783
  // src/guest/toolchains/step-scheduler.ts
38647
38784
  import { AsyncLocalStorage as AsyncLocalStorage4 } from "node:async_hooks";
38648
- var PARALLEL2 = 4;
38785
+ var PARALLEL2 = 3;
38649
38786
  async function runReported(sink2, name, env, work) {
38650
38787
  try {
38651
38788
  await work();
@@ -42350,8 +42487,37 @@ var OutputFilter = class {
42350
42487
  }
42351
42488
  };
42352
42489
 
42353
- // src/shared/pty/console-input.ts
42490
+ // src/shared/pty/clipboard-watch.ts
42354
42491
  import { spawn as spawn15 } from "node:child_process";
42492
+ var MAX_PENDING = 1024;
42493
+ function watchClipboard(onChange) {
42494
+ if (process.platform !== "win32") return { stop: () => void 0 };
42495
+ let child = null;
42496
+ try {
42497
+ child = spawn15(SYSTEM.powershell(), scriptArgs("watch-clipboard.ps1", ["-ProcessId", String(process.pid)]), {
42498
+ stdio: ["ignore", "pipe", "ignore"],
42499
+ windowsHide: true
42500
+ });
42501
+ } catch {
42502
+ return { stop: () => void 0 };
42503
+ }
42504
+ let pending = "";
42505
+ child.stdout?.setEncoding("latin1");
42506
+ child.stdout?.on("data", (text2) => {
42507
+ const lines = (pending + text2).split("\n");
42508
+ pending = (lines.pop() ?? "").slice(-MAX_PENDING);
42509
+ for (const line of lines) if (line.trim() === "changed") onChange();
42510
+ });
42511
+ child.on("error", () => void 0);
42512
+ return {
42513
+ stop: () => {
42514
+ if (child && child.exitCode === null && child.signalCode === null) child.kill();
42515
+ }
42516
+ };
42517
+ }
42518
+
42519
+ // src/shared/pty/console-input.ts
42520
+ import { spawn as spawn16 } from "node:child_process";
42355
42521
  function enableVtInput() {
42356
42522
  if (process.platform !== "win32" || !process.stdin.isTTY)
42357
42523
  return { ready: Promise.resolve(false), stop: () => Promise.resolve() };
@@ -42359,7 +42525,7 @@ function enableVtInput() {
42359
42525
  let exited2 = Promise.resolve();
42360
42526
  const ready = new Promise((resolve2) => {
42361
42527
  try {
42362
- child = spawn15(SYSTEM.powershell(), scriptArgs("enable-vt-input.ps1", ["-ProcessId", String(process.pid)]), {
42528
+ child = spawn16(SYSTEM.powershell(), scriptArgs("enable-vt-input.ps1", ["-ProcessId", String(process.pid)]), {
42363
42529
  stdio: "ignore",
42364
42530
  windowsHide: true
42365
42531
  });
@@ -42418,30 +42584,94 @@ function unfinishedSequence(data) {
42418
42584
  return data.length;
42419
42585
  }
42420
42586
 
42421
- // src/shared/pty/ctrl-c.ts
42587
+ // src/shared/web/protocol.ts
42588
+ var MAX_CLIENT_MESSAGE_BYTES = 256 * 1024;
42589
+
42590
+ // src/shared/web/ctrl-c.ts
42422
42591
  var CTRL_C_CONFIRM_MS = 1500;
42423
42592
  var CTRL_C_HINT = "Ctrl+C again to send it to the program (Esc stops an agent)";
42424
- var OSC_TITLE = /\x1b\][02];([^\x07\x1b]{0,256})(?:\x07|\x1b\\)/g;
42425
- var CtrlCGuard = class {
42593
+ var CTRL_C_COPY_HINT = "Copied: Ctrl+C after selecting text is not sent to the program (Esc stops an agent)";
42594
+ var CtrlCDecider = class {
42595
+ constructor(confirm = true) {
42596
+ this.confirm = confirm;
42597
+ }
42598
+ confirm;
42426
42599
  armedAt = Number.NEGATIVE_INFINITY;
42427
- lastTitle = "";
42428
- /** What to send for one read from the keyboard: a lone Ctrl+C only when another came just before. */
42429
- push(chunk, nowMs) {
42430
- if (chunk.length !== 1 || chunk[0] !== 3) return { send: chunk, held: false };
42600
+ selected = false;
42601
+ /** Text was selected in the program: Ctrl+C is "copy" from now on. */
42602
+ selection() {
42603
+ this.selected = true;
42604
+ this.armedAt = Number.NEGATIVE_INFINITY;
42605
+ }
42606
+ /** Any other key or a click: Ctrl+C is an interrupt again. */
42607
+ otherInput() {
42608
+ this.selected = false;
42609
+ }
42610
+ /** What to do with a lone Ctrl+C pressed at `nowMs`. */
42611
+ ctrlC(nowMs) {
42612
+ if (this.selected) return "copy";
42613
+ if (!this.confirm) return "send";
42431
42614
  if (nowMs - this.armedAt <= CTRL_C_CONFIRM_MS) {
42432
42615
  this.armedAt = Number.NEGATIVE_INFINITY;
42433
- return { send: chunk, held: false };
42616
+ return "send";
42434
42617
  }
42435
42618
  this.armedAt = nowMs;
42436
- return { send: Buffer.alloc(0), held: true };
42619
+ return "hold";
42620
+ }
42621
+ };
42622
+ var MOUSE_REPORT = /\x1b\[<(\d{1,4});\d{1,5};\d{1,5}([Mm])|\x1b\[M([\s\S])[\s\S]{2}/g;
42623
+ function classifyInput(text2) {
42624
+ let drag = false;
42625
+ let click = false;
42626
+ for (const m of text2.matchAll(MOUSE_REPORT)) {
42627
+ const sgr = m[1] !== void 0;
42628
+ const b = sgr ? Number(m[1]) : (m[3] ?? " ").charCodeAt(0) - 32;
42629
+ if (b & 64) continue;
42630
+ if (b & 32) {
42631
+ if ((b & 3) !== 3) drag = true;
42632
+ } else if (sgr ? m[2] === "M" : (b & 3) !== 3) click = true;
42633
+ }
42634
+ return { mouse: drag ? "drag" : click ? "click" : null, keys: text2.replace(MOUSE_REPORT, "").length > 0 };
42635
+ }
42636
+ function hasClipboardWrite(text2) {
42637
+ return /\x1b\]0{0,2}52;/.test(text2) || /\u009d0{0,2}52;/.test(text2);
42638
+ }
42639
+
42640
+ // src/shared/pty/ctrl-c.ts
42641
+ var OSC_TITLE = /\x1b\][02];([^\x07\x1b]{0,256})(?:\x07|\x1b\\)/g;
42642
+ var CtrlCGuard = class {
42643
+ decider;
42644
+ lastTitle = "";
42645
+ constructor(options = {}) {
42646
+ this.decider = new CtrlCDecider(options.confirm !== false);
42647
+ }
42648
+ /** What to send for one read from the keyboard: a lone Ctrl+C only when it is not held back or taken as "copy". */
42649
+ push(chunk, nowMs) {
42650
+ if (chunk.length === 1 && chunk[0] === 3) {
42651
+ const action = this.decider.ctrlC(nowMs);
42652
+ if (action === "send") return { send: chunk, held: false, copied: false };
42653
+ return { send: Buffer.alloc(0), held: action === "hold", copied: action === "copy" };
42654
+ }
42655
+ const c = classifyInput(chunk.toString("latin1"));
42656
+ if (c.keys || c.mouse === "click") this.decider.otherInput();
42657
+ else if (c.mouse === "drag") this.decider.selection();
42658
+ return { send: chunk, held: false, copied: false };
42659
+ }
42660
+ /** The clipboard changed since the last key: you just copied. */
42661
+ clipboardChanged() {
42662
+ this.decider.selection();
42663
+ }
42664
+ /** The program wrote to the clipboard: that is a selection too. */
42665
+ watchClipboard(raw) {
42666
+ if (hasClipboardWrite(raw)) this.decider.selection();
42437
42667
  }
42438
42668
  /** Remembers the title the program last set (from its output), to put it back after the hint. */
42439
42669
  watchOutput(text2) {
42440
42670
  for (const m of text2.matchAll(OSC_TITLE)) this.lastTitle = m[1];
42441
42671
  }
42442
42672
  /** The escape sequence that shows the hint, and the one that puts the program's title back. */
42443
- hintTitle() {
42444
- return "\x1B]0;" + CTRL_C_HINT + "\x07";
42673
+ hintTitle(kind = "hold") {
42674
+ return "\x1B]0;" + (kind === "copy" ? CTRL_C_COPY_HINT : CTRL_C_HINT) + "\x07";
42445
42675
  }
42446
42676
  restoreTitle() {
42447
42677
  return "\x1B]0;" + this.lastTitle.replace(/[\x00-\x1f\x7f]/g, "") + "\x07";
@@ -42645,6 +42875,7 @@ function runTerminalClient(socket, token, options = {}) {
42645
42875
  clearTimeout(joinTimer);
42646
42876
  clearTimeout(hintTimer);
42647
42877
  clearTimeout(vtTimer);
42878
+ clipboard.stop();
42648
42879
  socket.destroy();
42649
42880
  const tracked = mouse.tracking();
42650
42881
  const reset = "\x1B[0m\x1B[?25h" + MOUSE_OFF + (mouse.has(2004) ? "\x1B[?2004l" : "") + "\r\n";
@@ -42662,23 +42893,22 @@ function runTerminalClient(socket, token, options = {}) {
42662
42893
  if (data.length && !done) socket.write(encodeFrame2(FrameType.Input, data));
42663
42894
  };
42664
42895
  const sendInput = (data) => write(keys.push(passthrough ? mouse.filter(data) : data));
42665
- const guard = options.confirmCtrlC ? new CtrlCGuard() : null;
42896
+ const guard = new CtrlCGuard({ confirm: options.confirmCtrlC !== false });
42666
42897
  let hintTimer;
42898
+ const clipboard = watchClipboard(() => guard.clipboardChanged());
42667
42899
  const onInput = (chunk) => {
42668
42900
  if (options.detachKey !== void 0 && chunk.includes(options.detachKey)) {
42669
42901
  finish({ reason: "detach" });
42670
42902
  return;
42671
42903
  }
42672
- if (guard) {
42673
- const decided = guard.push(chunk, Date.now());
42674
- clearTimeout(hintTimer);
42675
- if (decided.held) {
42676
- stdout.write(guard.hintTitle());
42677
- hintTimer = setTimeout(() => stdout.write(guard.restoreTitle()), CTRL_C_CONFIRM_MS);
42678
- return;
42679
- }
42680
- if (decided.send.length === 1 && decided.send[0] === 3) stdout.write(guard.restoreTitle());
42904
+ const decided = guard.push(chunk, Date.now());
42905
+ clearTimeout(hintTimer);
42906
+ if (decided.held || decided.copied) {
42907
+ stdout.write(guard.hintTitle(decided.copied ? "copy" : "hold"));
42908
+ hintTimer = setTimeout(() => stdout.write(guard.restoreTitle()), CTRL_C_CONFIRM_MS);
42909
+ return;
42681
42910
  }
42911
+ if (decided.send.length === 1 && decided.send[0] === 3) stdout.write(guard.restoreTitle());
42682
42912
  clearTimeout(joinTimer);
42683
42913
  sendInput(joiner.push(chunk));
42684
42914
  if (joiner.holding()) joinTimer = setTimeout(() => sendInput(joiner.flush()), JOIN_MS);
@@ -42711,9 +42941,10 @@ function runTerminalClient(socket, token, options = {}) {
42711
42941
  }
42712
42942
  for (const frame of frames) {
42713
42943
  if (frame.type === FrameType.Output) {
42944
+ guard.watchClipboard(frame.payload.toString("latin1"));
42714
42945
  const out = filter.push(frame.payload);
42715
42946
  mouse.watch(out);
42716
- guard?.watchOutput(out.toString("latin1"));
42947
+ guard.watchOutput(out.toString("latin1"));
42717
42948
  stdout.write(out);
42718
42949
  } else if (frame.type === FrameType.Console) passthrough = frame.payload.toString("latin1") === "conpty.dll";
42719
42950
  else if (frame.type === FrameType.Restarting) {
@@ -43806,7 +44037,7 @@ var VSCODE_LANGUAGES = [
43806
44037
  "pwsh"
43807
44038
  ];
43808
44039
  function startDaemon(env, secrets) {
43809
- spawn16(process.execPath, [path126.join(path126.dirname(process.argv[1]), "daemon.js")], {
44040
+ spawn17(process.execPath, [path126.join(path126.dirname(process.argv[1]), "daemon.js")], {
43810
44041
  detached: true,
43811
44042
  stdio: "ignore",
43812
44043
  windowsHide: true,