windows-development-sandbox 1.13.2 → 1.15.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 (153) hide show
  1. package/config.example.json +2 -1
  2. package/dist/app/features/defs.d.ts +1 -1
  3. package/dist/app/features/projects/projects.def.d.ts +1 -1
  4. package/dist/app/features/projects/projects.run.d.ts +1 -1
  5. package/dist/app/features/projects/projects.run.js +10 -2
  6. package/dist/app/features/sandbox/sandbox.def.js +3 -1
  7. package/dist/app/features/settings/categories.def.js +1 -0
  8. package/dist/app/features/storage/storage.run.js +1 -1
  9. package/dist/bin/agent-sandbox.js +2 -2
  10. package/dist/bin/{chunk-VENWSGX2.js → chunk-D7CM63BS.js} +2303 -1462
  11. package/dist/bin/{chunk-K4SU3MHJ.js → chunk-HTQT5SBY.js} +1 -1
  12. package/dist/bin/{chunk-IL7BXJF2.js → chunk-RQ4BWBYX.js} +1 -1
  13. package/dist/bin/claude-host.js +2 -2
  14. package/dist/bin/claude-sbx.js +2 -2
  15. package/dist/bin/codex-sbx.js +2 -2
  16. package/dist/bin/gemini-sbx.js +2 -2
  17. package/dist/bin/sandbox.js +2 -2
  18. package/dist/bin/sbx-hide.js +2 -2
  19. package/dist/bin/sbx-show.js +2 -2
  20. package/dist/cli/commands/launch/launch.js +1 -1
  21. package/dist/cli/commands/projects/here.js +2 -0
  22. package/dist/cli/commands/repair/checks/contained.js +1 -1
  23. package/dist/cli/services/health-service.js +15 -3
  24. package/dist/cli/services/projects-service.d.ts +8 -3
  25. package/dist/cli/services/projects-service.js +21 -4
  26. package/dist/core/config/defaults.js +1 -0
  27. package/dist/core/schema/config.d.ts +4 -0
  28. package/dist/core/schema/projects.d.ts +4 -0
  29. package/dist/core/schema/projects.js +3 -0
  30. package/dist/guest/boot/boot.js +8 -2
  31. package/dist/guest/daemon/daemon.js +3 -0
  32. package/dist/guest/daemon/live-folders.d.ts +48 -0
  33. package/dist/guest/daemon/live-folders.js +245 -0
  34. package/dist/guest/daemon/live.d.ts +4 -0
  35. package/dist/guest/daemon/live.js +57 -0
  36. package/dist/guest/daemon/sessions.d.ts +7 -0
  37. package/dist/guest/daemon/sessions.js +23 -2
  38. package/dist/guest/entries/daemon.js +5 -2
  39. package/dist/guest/runtime/csc.d.ts +1 -0
  40. package/dist/guest/runtime/csc.js +1 -0
  41. package/dist/guest/runtime/secrets.d.ts +5 -2
  42. package/dist/guest/runtime/secrets.js +12 -5
  43. package/dist/guest/sessions/pty-host.d.ts +10 -0
  44. package/dist/guest/sessions/pty-host.js +48 -3
  45. package/dist/host/channel/channel.js +1 -1
  46. package/dist/host/engines/hcs/live-folders.d.ts +117 -0
  47. package/dist/host/engines/hcs/live-folders.js +347 -0
  48. package/dist/host/engines/index.d.ts +1 -0
  49. package/dist/host/engines/index.js +1 -0
  50. package/dist/host/install/install-record.d.ts +1 -0
  51. package/dist/host/install/install-record.js +2 -0
  52. package/dist/host/launch/generate.js +8 -1
  53. package/dist/host/projects/join.d.ts +4 -0
  54. package/dist/host/projects/join.js +11 -3
  55. package/dist/host/projects/mapper.d.ts +34 -5
  56. package/dist/host/projects/mapper.js +88 -20
  57. package/dist/host/projects/registry.d.ts +2 -0
  58. package/dist/host/projects/registry.js +1 -1
  59. package/dist/host/sandbox/contained-deps.d.ts +2 -1
  60. package/dist/host/sandbox/contained-deps.js +6 -4
  61. package/dist/host/secrets/launch-secrets.js +2 -1
  62. package/dist/host/sharing/lifecycle.js +9 -1
  63. package/dist/host/storage/contained/contained-storage.js +13 -0
  64. package/dist/host/storage/contained/disk-port.d.ts +5 -0
  65. package/dist/host/storage/contained/helper-port.d.ts +72 -0
  66. package/dist/host/storage/contained/helper-port.js +162 -0
  67. package/dist/host/storage/contained/index.d.ts +1 -0
  68. package/dist/host/storage/contained/index.js +1 -0
  69. package/dist/host/web/bridge.d.ts +5 -0
  70. package/dist/host/web/bridge.js +11 -1
  71. package/dist/host/web/server.d.ts +2 -0
  72. package/dist/host/web/server.js +11 -1
  73. package/dist/runtime/agent.js +23 -0
  74. package/dist/runtime/daemon.js +523 -135
  75. package/dist/runtime/db.js +23 -0
  76. package/dist/runtime/main.js +221 -15
  77. package/dist/runtime/netproxy.js +23 -0
  78. package/dist/runtime/view.js +209 -12
  79. package/dist/selftest/channel.test.js +72 -1
  80. package/dist/selftest/contained.test.js +1 -37
  81. package/dist/selftest/hcs.test.js +5 -2
  82. package/dist/selftest/index.js +4 -0
  83. package/dist/selftest/install.test.js +11 -0
  84. package/dist/selftest/layout.test.js +1 -0
  85. package/dist/selftest/live-daemon.test.js +51 -1
  86. package/dist/selftest/live-folders.test.d.ts +2 -0
  87. package/dist/selftest/live-folders.test.js +588 -0
  88. package/dist/selftest/live-terminal.test.js +112 -4
  89. package/dist/selftest/projects.test.js +7 -7
  90. package/dist/selftest/storage-helper.test.d.ts +2 -0
  91. package/dist/selftest/storage-helper.test.js +493 -0
  92. package/dist/selftest/support/engine-golden.js +1 -0
  93. package/dist/selftest/support/fixtures.d.ts +1 -0
  94. package/dist/selftest/support/live.js +2 -0
  95. package/dist/selftest/support/vhdx.d.ts +14 -0
  96. package/dist/selftest/support/vhdx.js +72 -0
  97. package/dist/selftest/web.test.js +14 -2
  98. package/dist/shared/channel/index.d.ts +1 -0
  99. package/dist/shared/channel/index.js +1 -0
  100. package/dist/shared/channel/live.d.ts +53 -0
  101. package/dist/shared/channel/live.js +92 -0
  102. package/dist/shared/channel/run-dir.d.ts +11 -0
  103. package/dist/shared/channel/run-dir.js +20 -0
  104. package/dist/shared/channel/secrets.d.ts +1 -0
  105. package/dist/shared/channel/secrets.js +5 -0
  106. package/dist/shared/paths/projects.d.ts +6 -1
  107. package/dist/shared/paths/projects.js +21 -1
  108. package/dist/shared/pty/index.d.ts +3 -1
  109. package/dist/shared/pty/index.js +3 -1
  110. package/dist/shared/pty/key-gate.d.ts +24 -0
  111. package/dist/shared/pty/key-gate.js +45 -0
  112. package/dist/shared/pty/mouse-modes.d.ts +27 -0
  113. package/dist/shared/pty/mouse-modes.js +155 -0
  114. package/dist/shared/pty/terminal.d.ts +0 -5
  115. package/dist/shared/pty/terminal.js +51 -20
  116. package/dist/sync/watcher.js +10 -1
  117. package/dist/web/index.html +1 -1
  118. package/dist/web/{main-NBQ3LF4E.js → main-7DJZ2M27.js} +34 -34
  119. package/native/csharp/HcsHost/AssemblyInfo.cs +4 -4
  120. package/native/csharp/HcsHost/FolderAccess.cs +361 -0
  121. package/native/csharp/HcsHost/Hcs.cs +31 -3
  122. package/native/csharp/HcsHost/Program.cs +54 -6
  123. package/native/csharp/HcsHost/Share.cs +64 -11
  124. package/native/csharp/HcsHost/WsbGuard.cs +130 -0
  125. package/native/csharp/HcsHost/WsbShare.cs +295 -0
  126. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  127. package/native/csharp/StorageHelper/AssemblyInfo.cs +15 -0
  128. package/native/csharp/StorageHelper/Client.cs +297 -0
  129. package/native/csharp/StorageHelper/Log.cs +177 -0
  130. package/native/csharp/StorageHelper/MountRecords.cs +160 -0
  131. package/native/csharp/StorageHelper/Operations.cs +596 -0
  132. package/native/csharp/StorageHelper/Pins.cs +270 -0
  133. package/native/csharp/StorageHelper/Privileges.cs +78 -0
  134. package/native/csharp/StorageHelper/Program.cs +349 -0
  135. package/native/csharp/StorageHelper/Protocol.cs +107 -0
  136. package/native/csharp/StorageHelper/RateLimiter.cs +97 -0
  137. package/native/csharp/StorageHelper/Rules.cs +156 -0
  138. package/native/csharp/StorageHelper/Server.cs +656 -0
  139. package/native/csharp/StorageHelper/Settings.cs +297 -0
  140. package/native/csharp/StorageHelper/StorageService.cs +53 -0
  141. package/native/csharp/StorageHelper/UserSettings.cs +206 -0
  142. package/native/csharp/StorageHelper/Vhdx.cs +230 -0
  143. package/native/csharp/StorageHelper/VirtualDisk.cs +241 -0
  144. package/native/csharp/StorageHelper/Volumes.cs +379 -0
  145. package/native/csharp/StorageHelper/Watchdog.cs +128 -0
  146. package/native/csharp/StorageHelper/Win32.cs +250 -0
  147. package/native/scripts/contained-common.ps1 +4 -0
  148. package/native/scripts/hcs-live-share.ps1 +191 -0
  149. package/native/scripts/set-storage-helper.ps1 +203 -0
  150. package/package.json +1 -1
  151. package/sandbox/csharp/BindMount.cs +120 -0
  152. package/sandbox/csharp/PtyHost.cs +236 -1
  153. package/schemas/config.schema.json +8 -0
@@ -26621,6 +26621,9 @@ var ProjectsSchema = external_exports.strictObject({
26621
26621
  ),
26622
26622
  wsbShare: external_exports.boolean().describe(
26623
26623
  "On Windows 11 24H2+ with the Windows Sandbox command line (wsb), add other folders to the running sandbox with `wsb share` (untested). Otherwise starting a project outside every root offers a restart."
26624
+ ),
26625
+ liveFolders: external_exports.enum(["off", "hcs"]).describe(
26626
+ 'ALPHA, UNTESTED ON A REAL PC: "hcs" adds a project folder to the running sandbox through the Host Compute Service where `wsb share` is missing (one administrator prompt per add or remove; always read-only in this alpha). If it fails, the sandbox may close: save your work first. Turn it on before the sandbox starts. "off" (default) offers a restart instead. The real-PC tests still to pass are in docs/design/hcs-live-folders.md ("Test plan").'
26624
26627
  )
26625
26628
  });
26626
26629
 
@@ -27211,8 +27214,16 @@ var RunDir = class {
27211
27214
  projectsDir;
27212
27215
  /** audit.processes: programs the sandbox started, in small .jsonl files the host takes in and deletes. */
27213
27216
  auditDir;
27217
+ /**
27218
+ * Each running session's process tree, recorded by its ptyhost (guest only: the daemon ends what a ptyhost that was
27219
+ * itself ended left running).
27220
+ */
27221
+ treesDir;
27214
27222
  /** One-shot secrets the host hands over (see secrets.ts). Next to run, not in it: shared-use users can change run. */
27215
27223
  secretsDir;
27224
+ /** Live folders (live.ts): signed host requests, and the daemon's replies. */
27225
+ liveDir;
27226
+ liveRepliesDir;
27216
27227
  constructor(dataDir2) {
27217
27228
  this.root = path5.join(dataDir2, "run");
27218
27229
  this.sandboxFile = path5.join(this.root, "sandbox.json");
@@ -27224,7 +27235,16 @@ var RunDir = class {
27224
27235
  this.sessionsDir = path5.join(this.root, "sessions");
27225
27236
  this.projectsDir = path5.join(this.root, "projects");
27226
27237
  this.auditDir = path5.join(this.root, "audit");
27238
+ this.treesDir = path5.join(this.root, "trees");
27227
27239
  this.secretsDir = path5.join(dataDir2, "secrets");
27240
+ this.liveDir = path5.join(this.root, "live");
27241
+ this.liveRepliesDir = path5.join(this.root, "live-replies");
27242
+ }
27243
+ liveFile(id) {
27244
+ return path5.join(this.liveDir, safeId(id) + ".json");
27245
+ }
27246
+ liveReplyFile(id) {
27247
+ return path5.join(this.liveRepliesDir, safeId(id) + ".json");
27228
27248
  }
27229
27249
  requestFile(id) {
27230
27250
  return path5.join(this.requestsDir, safeId(id) + ".json");
@@ -27235,6 +27255,9 @@ var RunDir = class {
27235
27255
  sessionFile(id) {
27236
27256
  return path5.join(this.sessionsDir, safeId(id) + ".json");
27237
27257
  }
27258
+ treeFile(id) {
27259
+ return path5.join(this.treesDir, safeId(id) + ".json");
27260
+ }
27238
27261
  projectFile(name) {
27239
27262
  return path5.join(this.projectsDir, safeId(name) + ".json");
27240
27263
  }
@@ -26750,6 +26750,9 @@ var ProjectsSchema = external_exports.strictObject({
26750
26750
  ),
26751
26751
  wsbShare: external_exports.boolean().describe(
26752
26752
  "On Windows 11 24H2+ with the Windows Sandbox command line (wsb), add other folders to the running sandbox with `wsb share` (untested). Otherwise starting a project outside every root offers a restart."
26753
+ ),
26754
+ liveFolders: external_exports.enum(["off", "hcs"]).describe(
26755
+ 'ALPHA, UNTESTED ON A REAL PC: "hcs" adds a project folder to the running sandbox through the Host Compute Service where `wsb share` is missing (one administrator prompt per add or remove; always read-only in this alpha). If it fails, the sandbox may close: save your work first. Turn it on before the sandbox starts. "off" (default) offers a restart instead. The real-PC tests still to pass are in docs/design/hcs-live-folders.md ("Test plan").'
26753
26756
  )
26754
26757
  });
26755
26758
 
@@ -27322,8 +27325,16 @@ var RunDir = class {
27322
27325
  projectsDir;
27323
27326
  /** audit.processes: programs the sandbox started, in small .jsonl files the host takes in and deletes. */
27324
27327
  auditDir;
27328
+ /**
27329
+ * Each running session's process tree, recorded by its ptyhost (guest only: the daemon ends what a ptyhost that was
27330
+ * itself ended left running).
27331
+ */
27332
+ treesDir;
27325
27333
  /** One-shot secrets the host hands over (see secrets.ts). Next to run, not in it: shared-use users can change run. */
27326
27334
  secretsDir;
27335
+ /** Live folders (live.ts): signed host requests, and the daemon's replies. */
27336
+ liveDir;
27337
+ liveRepliesDir;
27327
27338
  constructor(dataDir2) {
27328
27339
  this.root = path6.join(dataDir2, "run");
27329
27340
  this.sandboxFile = path6.join(this.root, "sandbox.json");
@@ -27335,7 +27346,16 @@ var RunDir = class {
27335
27346
  this.sessionsDir = path6.join(this.root, "sessions");
27336
27347
  this.projectsDir = path6.join(this.root, "projects");
27337
27348
  this.auditDir = path6.join(this.root, "audit");
27349
+ this.treesDir = path6.join(this.root, "trees");
27338
27350
  this.secretsDir = path6.join(dataDir2, "secrets");
27351
+ this.liveDir = path6.join(this.root, "live");
27352
+ this.liveRepliesDir = path6.join(this.root, "live-replies");
27353
+ }
27354
+ liveFile(id) {
27355
+ return path6.join(this.liveDir, safeId(id) + ".json");
27356
+ }
27357
+ liveReplyFile(id) {
27358
+ return path6.join(this.liveRepliesDir, safeId(id) + ".json");
27339
27359
  }
27340
27360
  requestFile(id) {
27341
27361
  return path6.join(this.requestsDir, safeId(id) + ".json");
@@ -27346,6 +27366,9 @@ var RunDir = class {
27346
27366
  sessionFile(id) {
27347
27367
  return path6.join(this.sessionsDir, safeId(id) + ".json");
27348
27368
  }
27369
+ treeFile(id) {
27370
+ return path6.join(this.treesDir, safeId(id) + ".json");
27371
+ }
27349
27372
  projectFile(name) {
27350
27373
  return path6.join(this.projectsDir, safeId(name) + ".json");
27351
27374
  }
@@ -29290,7 +29313,8 @@ var SOURCES = {
29290
29313
  cargoshim: "CargoShim.cs",
29291
29314
  linkgate: "LinkGate.cs",
29292
29315
  pathsync: "PathSync.cs",
29293
- memgov: "MemGov.cs"
29316
+ memgov: "MemGov.cs",
29317
+ bindmount: "BindMount.cs"
29294
29318
  };
29295
29319
  function sourceOf(name) {
29296
29320
  return path31.join(launchDir, "sandbox", "csharp", SOURCES[name]);
@@ -35339,12 +35363,175 @@ var CtrlCGuard = class {
35339
35363
  }
35340
35364
  };
35341
35365
 
35342
- // src/shared/pty/terminal.ts
35343
- var MOUSE_OFF = "\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1005l\x1B[?1006l\x1B[?1015l";
35366
+ // src/shared/pty/key-gate.ts
35344
35367
  function fixWindowsKeys(chunk) {
35345
35368
  if (!chunk.includes(8) && !chunk.includes(127)) return chunk;
35346
35369
  return Buffer.from(chunk.map((b) => b === 8 ? 127 : b === 127 ? 8 : b));
35347
35370
  }
35371
+ var KeyGate = class {
35372
+ pending = [];
35373
+ decided;
35374
+ swap = false;
35375
+ /** windowsConsole: input comes from a Windows console in raw mode (its mode is decided later); else it goes as is. */
35376
+ constructor(windowsConsole) {
35377
+ this.decided = !windowsConsole;
35378
+ }
35379
+ /** The bytes to send now (empty while the mode is not known yet). */
35380
+ push(data) {
35381
+ if (!data.length) return data;
35382
+ if (!this.decided) {
35383
+ this.pending.push(data);
35384
+ return Buffer.alloc(0);
35385
+ }
35386
+ return this.swap ? fixWindowsKeys(data) : data;
35387
+ }
35388
+ /** The console's mode is known: what was held, ready to send. Later calls only change how new input is read. */
35389
+ decide(vtInput) {
35390
+ this.swap = !vtInput;
35391
+ this.decided = true;
35392
+ const held = Buffer.concat(this.pending);
35393
+ this.pending = [];
35394
+ return this.swap ? fixWindowsKeys(held) : held;
35395
+ }
35396
+ };
35397
+
35398
+ // src/shared/pty/mouse-modes.ts
35399
+ var TRACKING = [9, 1e3, 1002, 1003];
35400
+ var MAX_PARAMS = 64;
35401
+ var MOTION = 32;
35402
+ var NO_BUTTON = 3;
35403
+ var MouseModes = class {
35404
+ on = /* @__PURE__ */ new Set();
35405
+ /** 0 text, 1 after ESC, 2 after ESC[, 3 in ESC[? parameters, 4 after ESC[! */
35406
+ state = 0;
35407
+ params = "";
35408
+ /** Reads the program's output (after OutputFilter): the mode changes in it. */
35409
+ watch(output3) {
35410
+ for (const b of output3) {
35411
+ if (b === 27) {
35412
+ this.state = 1;
35413
+ continue;
35414
+ }
35415
+ if (this.state === 1) {
35416
+ if (b === 99) this.on.clear();
35417
+ this.state = b === 91 ? 2 : 0;
35418
+ } else if (this.state === 2) {
35419
+ this.state = b === 63 ? 3 : b === 33 ? 4 : 0;
35420
+ this.params = "";
35421
+ } else if (this.state === 4) {
35422
+ if (b === 112) this.on.clear();
35423
+ this.state = 0;
35424
+ } else if (this.state === 3) {
35425
+ if (b >= 48 && b <= 57 || b === 59) {
35426
+ this.params += String.fromCharCode(b);
35427
+ if (this.params.length > MAX_PARAMS) this.state = 0;
35428
+ } else {
35429
+ if (b === 104 || b === 108) this.apply(this.params, b === 104);
35430
+ this.state = 0;
35431
+ }
35432
+ }
35433
+ }
35434
+ }
35435
+ /** The program is gone (exited or restarting): its modes went with it. */
35436
+ reset() {
35437
+ this.on.clear();
35438
+ this.state = 0;
35439
+ this.params = "";
35440
+ }
35441
+ /** True while the program tracks the mouse in any way. */
35442
+ tracking() {
35443
+ return TRACKING.some((m) => this.on.has(m));
35444
+ }
35445
+ /** True while the program has a mode on (2004: bracketed paste). */
35446
+ has(mode) {
35447
+ return this.on.has(mode);
35448
+ }
35449
+ /** The mode numbers on now (for the report and tests). */
35450
+ modes() {
35451
+ return [...this.on].sort((a, b) => a - b);
35452
+ }
35453
+ /** `input` without the mouse reports the program has not asked for. Sequences must be whole (InputJoiner). */
35454
+ filter(input2) {
35455
+ if (!input2.includes(27)) return input2;
35456
+ const parts2 = [];
35457
+ let from = 0;
35458
+ let i = input2.indexOf(27);
35459
+ while (i !== -1) {
35460
+ const report = mouseReport(input2, i, this.on.has(1005), this.on.has(1015));
35461
+ if (report && !this.allows(report.button)) {
35462
+ parts2.push(input2.subarray(from, i));
35463
+ from = i + report.length;
35464
+ i = input2.indexOf(27, from);
35465
+ } else i = input2.indexOf(27, i + 1);
35466
+ }
35467
+ if (from === 0) return input2;
35468
+ parts2.push(input2.subarray(from));
35469
+ return Buffer.concat(parts2);
35470
+ }
35471
+ allows(button) {
35472
+ if (!this.tracking()) return false;
35473
+ if ((button & MOTION) === 0) return true;
35474
+ if (this.on.has(1003)) return true;
35475
+ return this.on.has(1002) && (button & NO_BUTTON) !== NO_BUTTON;
35476
+ }
35477
+ apply(params, set2) {
35478
+ for (const p of params.split(";")) {
35479
+ const mode = Number(p);
35480
+ if (!p || !Number.isSafeInteger(mode)) continue;
35481
+ if (set2) this.on.add(mode);
35482
+ else this.on.delete(mode);
35483
+ }
35484
+ }
35485
+ };
35486
+ function mouseReport(data, at, utf8, urxvt) {
35487
+ if (data[at + 1] !== 91) return null;
35488
+ const sgr = data[at + 2] === 60;
35489
+ if (sgr || urxvt && data[at + 2] >= 48 && data[at + 2] <= 57) {
35490
+ let i = at + (sgr ? 3 : 2);
35491
+ let digits = "";
35492
+ let fields = 1;
35493
+ for (; i < data.length && i - at < 32; i++) {
35494
+ const b = data[i];
35495
+ if (b >= 48 && b <= 57) {
35496
+ if (fields === 1) digits += String.fromCharCode(b);
35497
+ } else if (b === 59) fields++;
35498
+ else break;
35499
+ }
35500
+ if (i >= data.length || fields !== 3 || !digits) return null;
35501
+ if (sgr ? data[i] !== 77 && data[i] !== 109 : data[i] !== 77) return null;
35502
+ const button = Number(digits) - (sgr ? 0 : 32);
35503
+ return button >= 0 ? { button, length: i - at + 1 } : null;
35504
+ }
35505
+ if (data[at + 2] === 77 && !utf8 && data.length - at >= 6) {
35506
+ const button = data[at + 3] - 32;
35507
+ return button >= 0 ? { button, length: 6 } : null;
35508
+ }
35509
+ return null;
35510
+ }
35511
+
35512
+ // src/shared/pty/terminal.ts
35513
+ var MOUSE_OFF = "\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1005l\x1B[?1006l\x1B[?1015l";
35514
+ var SETTLE_QUIET_MS = 50;
35515
+ var SETTLE_MAX_MS = 300;
35516
+ function drainInput(stream) {
35517
+ return new Promise((done) => {
35518
+ let quiet;
35519
+ const end = () => {
35520
+ clearTimeout(quiet);
35521
+ clearTimeout(cap);
35522
+ stream.off("data", onData);
35523
+ done();
35524
+ };
35525
+ const onData = () => {
35526
+ clearTimeout(quiet);
35527
+ quiet = setTimeout(end, SETTLE_QUIET_MS);
35528
+ };
35529
+ const cap = setTimeout(end, SETTLE_MAX_MS);
35530
+ stream.on("data", onData);
35531
+ onData();
35532
+ });
35533
+ }
35534
+ var VT_WAIT_MS = 1e4;
35348
35535
  function runTerminalClient(socket, token, options = {}) {
35349
35536
  const stdin = process.stdin;
35350
35537
  const stdout = process.stdout;
@@ -35352,14 +35539,16 @@ function runTerminalClient(socket, token, options = {}) {
35352
35539
  const [c, r] = stdout.isTTY ? stdout.getWindowSize() : [0, 0];
35353
35540
  return [c || 120, r || 30];
35354
35541
  };
35355
- let swapKeys = process.platform === "win32" && stdin.isTTY;
35542
+ const keys = new KeyGate(process.platform === "win32" && Boolean(stdin.isTTY));
35356
35543
  return new Promise((resolve2) => {
35357
35544
  let done = false;
35358
35545
  const reader = new FrameReader();
35359
35546
  const filter = new OutputFilter({ clipboardWrites: options.clipboardWrites });
35360
35547
  let sent = size();
35361
35548
  let resizeTimer;
35362
- let vtInput;
35549
+ let vtInput, vtTimer;
35550
+ const mouse = new MouseModes();
35551
+ let passthrough = false;
35363
35552
  const finish = (result) => {
35364
35553
  if (done) return;
35365
35554
  done = true;
@@ -35370,9 +35559,13 @@ function runTerminalClient(socket, token, options = {}) {
35370
35559
  clearInterval(sizeWatch);
35371
35560
  clearTimeout(joinTimer);
35372
35561
  clearTimeout(hintTimer);
35562
+ clearTimeout(vtTimer);
35373
35563
  socket.destroy();
35374
- stdout.write("\x1B[0m\x1B[?25h" + MOUSE_OFF + "\r\n");
35375
- void (vtInput?.stop() ?? Promise.resolve()).then(() => {
35564
+ const tracked = mouse.tracking();
35565
+ const reset = "\x1B[0m\x1B[?25h" + MOUSE_OFF + (mouse.has(2004) ? "\x1B[?2004l" : "") + "\r\n";
35566
+ const flushed = new Promise((written) => stdout.write(reset, () => written()));
35567
+ const settled = tracked ? flushed.then(() => drainInput(stdin)) : Promise.resolve();
35568
+ void Promise.all([settled, vtInput?.stop()]).then(() => {
35376
35569
  if (stdin.isTTY) stdin.setRawMode(false);
35377
35570
  stdin.pause();
35378
35571
  resolve2(result);
@@ -35380,9 +35573,10 @@ function runTerminalClient(socket, token, options = {}) {
35380
35573
  };
35381
35574
  const joiner = new InputJoiner();
35382
35575
  let joinTimer;
35383
- const sendInput = (data) => {
35384
- if (data.length && !done) socket.write(encodeFrame(FrameType.Input, swapKeys ? fixWindowsKeys(data) : data));
35576
+ const write = (data) => {
35577
+ if (data.length && !done) socket.write(encodeFrame(FrameType.Input, data));
35385
35578
  };
35579
+ const sendInput = (data) => write(keys.push(passthrough ? mouse.filter(data) : data));
35386
35580
  const guard = options.confirmCtrlC ? new CtrlCGuard() : null;
35387
35581
  let hintTimer;
35388
35582
  const onInput = (chunk) => {
@@ -35404,7 +35598,6 @@ function runTerminalClient(socket, token, options = {}) {
35404
35598
  sendInput(joiner.push(chunk));
35405
35599
  if (joiner.holding()) joinTimer = setTimeout(() => sendInput(joiner.flush()), JOIN_MS);
35406
35600
  };
35407
- let passthrough = false;
35408
35601
  const sendSize = () => {
35409
35602
  const [c2, r2] = size();
35410
35603
  if (c2 === sent[0] && r2 === sent[1]) return;
@@ -35434,11 +35627,14 @@ function runTerminalClient(socket, token, options = {}) {
35434
35627
  for (const frame of frames) {
35435
35628
  if (frame.type === FrameType.Output) {
35436
35629
  const out = filter.push(frame.payload);
35630
+ mouse.watch(out);
35437
35631
  guard?.watchOutput(out.toString("latin1"));
35438
35632
  stdout.write(out);
35439
35633
  } else if (frame.type === FrameType.Console) passthrough = frame.payload.toString("latin1") === "conpty.dll";
35440
- else if (frame.type === FrameType.Restarting) stdout.write(MOUSE_OFF + "\x1B[2J\x1B[3J\x1B[H");
35441
- else if (frame.type === FrameType.Exit)
35634
+ else if (frame.type === FrameType.Restarting) {
35635
+ mouse.reset();
35636
+ stdout.write(MOUSE_OFF + "\x1B[2J\x1B[3J\x1B[H");
35637
+ } else if (frame.type === FrameType.Exit)
35442
35638
  finish({ reason: "exit", exitCode: frame.payload.length >= 4 ? frame.payload.readInt32LE(0) : 1 });
35443
35639
  else if (frame.type === FrameType.Error)
35444
35640
  finish({
@@ -35454,8 +35650,10 @@ function runTerminalClient(socket, token, options = {}) {
35454
35650
  socket.write(helloFrame(token, c, r));
35455
35651
  if (stdin.isTTY) stdin.setRawMode(true);
35456
35652
  vtInput = enableVtInput();
35653
+ vtTimer = setTimeout(() => write(keys.decide(false)), VT_WAIT_MS);
35457
35654
  void vtInput.ready.then((on) => {
35458
- if (on) swapKeys = false;
35655
+ clearTimeout(vtTimer);
35656
+ write(keys.decide(on));
35459
35657
  });
35460
35658
  stdin.on("data", onInput);
35461
35659
  stdin.resume();
@@ -35565,7 +35763,9 @@ var SecretsFileSchema = external_exports.strictObject({
35565
35763
  /** The checked WireGuard config of vpn.provider wireguard-file (holds the private key). */
35566
35764
  wireguardConfig: external_exports.string().min(1).max(32 * 1024).optional(),
35567
35765
  /** The GitHub token you saved for sandboxes (github.shareToken), only with git access "full". */
35568
- githubToken: GithubTokenSchema.optional()
35766
+ githubToken: GithubTokenSchema.optional(),
35767
+ /** projects.liveFolders "hcs": this launch's key for signed live-folder requests (live.ts). */
35768
+ liveFoldersKey: external_exports.string().regex(/^[0-9a-f]{64}$/).optional()
35569
35769
  });
35570
35770
 
35571
35771
  // src/guest/runtime/secrets.ts
@@ -35626,7 +35826,13 @@ function startDaemon(env, secrets) {
35626
35826
  ...env,
35627
35827
  AGENT_LOG_PREFIX: "daemon",
35628
35828
  // The agent's environment (it may hold API keys): the daemon moves it into memory and removes this at once.
35629
- ...secrets.harnessEnvironment ? { [BOOT_SECRETS_VARIABLE]: JSON.stringify({ harnessEnvironment: secrets.harnessEnvironment }) } : {}
35829
+ // The live-folder key (projects.liveFolders "hcs") goes the same way into the daemon's own state.
35830
+ ...secrets.harnessEnvironment || secrets.liveFoldersKey ? {
35831
+ [BOOT_SECRETS_VARIABLE]: JSON.stringify({
35832
+ ...secrets.harnessEnvironment ? { harnessEnvironment: secrets.harnessEnvironment } : {},
35833
+ ...secrets.liveFoldersKey ? { liveFoldersKey: secrets.liveFoldersKey } : {}
35834
+ })
35835
+ } : {}
35630
35836
  }
35631
35837
  }).unref();
35632
35838
  }
@@ -26674,6 +26674,9 @@ var ProjectsSchema = external_exports.strictObject({
26674
26674
  ),
26675
26675
  wsbShare: external_exports.boolean().describe(
26676
26676
  "On Windows 11 24H2+ with the Windows Sandbox command line (wsb), add other folders to the running sandbox with `wsb share` (untested). Otherwise starting a project outside every root offers a restart."
26677
+ ),
26678
+ liveFolders: external_exports.enum(["off", "hcs"]).describe(
26679
+ 'ALPHA, UNTESTED ON A REAL PC: "hcs" adds a project folder to the running sandbox through the Host Compute Service where `wsb share` is missing (one administrator prompt per add or remove; always read-only in this alpha). If it fails, the sandbox may close: save your work first. Turn it on before the sandbox starts. "off" (default) offers a restart instead. The real-PC tests still to pass are in docs/design/hcs-live-folders.md ("Test plan").'
26677
26680
  )
26678
26681
  });
26679
26682
 
@@ -27202,8 +27205,16 @@ var RunDir = class {
27202
27205
  projectsDir;
27203
27206
  /** audit.processes: programs the sandbox started, in small .jsonl files the host takes in and deletes. */
27204
27207
  auditDir;
27208
+ /**
27209
+ * Each running session's process tree, recorded by its ptyhost (guest only: the daemon ends what a ptyhost that was
27210
+ * itself ended left running).
27211
+ */
27212
+ treesDir;
27205
27213
  /** One-shot secrets the host hands over (see secrets.ts). Next to run, not in it: shared-use users can change run. */
27206
27214
  secretsDir;
27215
+ /** Live folders (live.ts): signed host requests, and the daemon's replies. */
27216
+ liveDir;
27217
+ liveRepliesDir;
27207
27218
  constructor(dataDir2) {
27208
27219
  this.root = path3.join(dataDir2, "run");
27209
27220
  this.sandboxFile = path3.join(this.root, "sandbox.json");
@@ -27215,7 +27226,16 @@ var RunDir = class {
27215
27226
  this.sessionsDir = path3.join(this.root, "sessions");
27216
27227
  this.projectsDir = path3.join(this.root, "projects");
27217
27228
  this.auditDir = path3.join(this.root, "audit");
27229
+ this.treesDir = path3.join(this.root, "trees");
27218
27230
  this.secretsDir = path3.join(dataDir2, "secrets");
27231
+ this.liveDir = path3.join(this.root, "live");
27232
+ this.liveRepliesDir = path3.join(this.root, "live-replies");
27233
+ }
27234
+ liveFile(id) {
27235
+ return path3.join(this.liveDir, safeId(id) + ".json");
27236
+ }
27237
+ liveReplyFile(id) {
27238
+ return path3.join(this.liveRepliesDir, safeId(id) + ".json");
27219
27239
  }
27220
27240
  requestFile(id) {
27221
27241
  return path3.join(this.requestsDir, safeId(id) + ".json");
@@ -27226,6 +27246,9 @@ var RunDir = class {
27226
27246
  sessionFile(id) {
27227
27247
  return path3.join(this.sessionsDir, safeId(id) + ".json");
27228
27248
  }
27249
+ treeFile(id) {
27250
+ return path3.join(this.treesDir, safeId(id) + ".json");
27251
+ }
27229
27252
  projectFile(name) {
27230
27253
  return path3.join(this.projectsDir, safeId(name) + ".json");
27231
27254
  }