@slayzone/computer 0.36.2 → 0.36.4

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.
package/dist/bin.cjs CHANGED
@@ -1453,7 +1453,7 @@ var require_receiver = __commonJS({
1453
1453
  var require_sender = __commonJS({
1454
1454
  "../../../node_modules/ws/lib/sender.js"(exports2, module2) {
1455
1455
  "use strict";
1456
- var { Duplex } = require("stream");
1456
+ var { Duplex: Duplex2 } = require("stream");
1457
1457
  var { randomFillSync } = require("crypto");
1458
1458
  var {
1459
1459
  types: { isUint8Array }
@@ -2330,11 +2330,11 @@ var require_websocket = __commonJS({
2330
2330
  "use strict";
2331
2331
  var EventEmitter = require("events");
2332
2332
  var https = require("https");
2333
- var http2 = require("http");
2333
+ var http3 = require("http");
2334
2334
  var net = require("net");
2335
2335
  var tls = require("tls");
2336
- var { randomBytes, createHash } = require("crypto");
2337
- var { Duplex, Readable } = require("stream");
2336
+ var { randomBytes: randomBytes2, createHash } = require("crypto");
2337
+ var { Duplex: Duplex2, Readable } = require("stream");
2338
2338
  var { URL: URL2 } = require("url");
2339
2339
  var PerMessageDeflate2 = require_permessage_deflate();
2340
2340
  var Receiver2 = require_receiver();
@@ -2884,8 +2884,8 @@ var require_websocket = __commonJS({
2884
2884
  }
2885
2885
  }
2886
2886
  const defaultPort = isSecure ? 443 : 80;
2887
- const key = randomBytes(16).toString("base64");
2888
- const request = isSecure ? https.request : http2.request;
2887
+ const key = randomBytes2(16).toString("base64");
2888
+ const request = isSecure ? https.request : http3.request;
2889
2889
  const protocolSet = /* @__PURE__ */ new Set();
2890
2890
  let perMessageDeflate;
2891
2891
  opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
@@ -3238,7 +3238,7 @@ var require_stream = __commonJS({
3238
3238
  "../../../node_modules/ws/lib/stream.js"(exports2, module2) {
3239
3239
  "use strict";
3240
3240
  var WebSocket2 = require_websocket();
3241
- var { Duplex } = require("stream");
3241
+ var { Duplex: Duplex2 } = require("stream");
3242
3242
  function emitClose(stream) {
3243
3243
  stream.emit("close");
3244
3244
  }
@@ -3256,7 +3256,7 @@ var require_stream = __commonJS({
3256
3256
  }
3257
3257
  function createWebSocketStream2(ws, options) {
3258
3258
  let terminateOnDestroy = true;
3259
- const duplex = new Duplex({
3259
+ const duplex = new Duplex2({
3260
3260
  ...options,
3261
3261
  autoDestroy: false,
3262
3262
  emitClose: false,
@@ -3381,8 +3381,8 @@ var require_websocket_server = __commonJS({
3381
3381
  "../../../node_modules/ws/lib/websocket-server.js"(exports2, module2) {
3382
3382
  "use strict";
3383
3383
  var EventEmitter = require("events");
3384
- var http2 = require("http");
3385
- var { Duplex } = require("stream");
3384
+ var http3 = require("http");
3385
+ var { Duplex: Duplex2 } = require("stream");
3386
3386
  var { createHash } = require("crypto");
3387
3387
  var extension2 = require_extension();
3388
3388
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -3462,8 +3462,8 @@ var require_websocket_server = __commonJS({
3462
3462
  );
3463
3463
  }
3464
3464
  if (options.port != null) {
3465
- this._server = http2.createServer((req, res) => {
3466
- const body = http2.STATUS_CODES[426];
3465
+ this._server = http3.createServer((req, res) => {
3466
+ const body = http3.STATUS_CODES[426];
3467
3467
  res.writeHead(426, {
3468
3468
  "Content-Length": body.length,
3469
3469
  "Content-Type": "text/plain"
@@ -3752,7 +3752,7 @@ var require_websocket_server = __commonJS({
3752
3752
  this.destroy();
3753
3753
  }
3754
3754
  function abortHandshake(socket, code, message, headers) {
3755
- message = message || http2.STATUS_CODES[code];
3755
+ message = message || http3.STATUS_CODES[code];
3756
3756
  headers = {
3757
3757
  Connection: "close",
3758
3758
  "Content-Type": "text/html",
@@ -3761,7 +3761,7 @@ var require_websocket_server = __commonJS({
3761
3761
  };
3762
3762
  socket.once("finish", socket.destroy);
3763
3763
  socket.end(
3764
- `HTTP/1.1 ${code} ${http2.STATUS_CODES[code]}\r
3764
+ `HTTP/1.1 ${code} ${http3.STATUS_CODES[code]}\r
3765
3765
  ` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
3766
3766
  );
3767
3767
  }
@@ -4415,26 +4415,26 @@ var require_ignore = __commonJS({
4415
4415
  const index = body.indexOf(SLASH);
4416
4416
  return index < 0 || index === body.length - 1;
4417
4417
  };
4418
- var basenameOf = (path9) => {
4419
- const end = path9.length - 1;
4420
- const index = path9.lastIndexOf(
4418
+ var basenameOf = (path11) => {
4419
+ const end = path11.length - 1;
4420
+ const index = path11.lastIndexOf(
4421
4421
  SLASH,
4422
- path9[end] === SLASH ? end - 1 : end
4422
+ path11[end] === SLASH ? end - 1 : end
4423
4423
  );
4424
- return index < 0 ? path9 : path9.slice(index + 1);
4424
+ return index < 0 ? path11 : path11.slice(index + 1);
4425
4425
  };
4426
- var parentOf = (path9) => {
4427
- if (path9.charCodeAt(0) === SLASH_CODE || path9.indexOf(DOUBLE_SLASH) >= 0) {
4428
- const slices = path9.split(SLASH).filter(Boolean);
4426
+ var parentOf = (path11) => {
4427
+ if (path11.charCodeAt(0) === SLASH_CODE || path11.indexOf(DOUBLE_SLASH) >= 0) {
4428
+ const slices = path11.split(SLASH).filter(Boolean);
4429
4429
  slices.pop();
4430
4430
  return slices.length ? slices.join(SLASH) + SLASH : EMPTY;
4431
4431
  }
4432
- const end = path9.length - 1;
4433
- const cut = path9.lastIndexOf(
4432
+ const end = path11.length - 1;
4433
+ const cut = path11.lastIndexOf(
4434
4434
  SLASH,
4435
- path9.charCodeAt(end) === SLASH_CODE ? end - 1 : end
4435
+ path11.charCodeAt(end) === SLASH_CODE ? end - 1 : end
4436
4436
  );
4437
- return cut < 0 ? EMPTY : path9.slice(0, cut + 1);
4437
+ return cut < 0 ? EMPTY : path11.slice(0, cut + 1);
4438
4438
  };
4439
4439
  var isString2 = (subject) => typeof subject === "string";
4440
4440
  var checkPattern = (pattern) => pattern && isString2(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
@@ -4543,20 +4543,20 @@ var require_ignore = __commonJS({
4543
4543
  // path matching.
4544
4544
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
4545
4545
  // @returns {TestResult} true if a file is ignored
4546
- test(path9, checkUnignored, mode) {
4546
+ test(path11, checkUnignored, mode) {
4547
4547
  let ignored = false;
4548
4548
  let unignored = false;
4549
4549
  let matchedRule;
4550
4550
  const rules = this._rules;
4551
4551
  const { length } = rules;
4552
4552
  const shortcut = this._basenameCount * 2 >= length;
4553
- const basename5 = shortcut ? basenameOf(path9) : path9;
4553
+ const basename5 = shortcut ? basenameOf(path11) : path11;
4554
4554
  for (let index = 0; index < length; index++) {
4555
4555
  const rule = rules[index];
4556
4556
  const { negative } = rule;
4557
4557
  const skip = unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored;
4558
4558
  if (!skip && rule[mode].test(
4559
- shortcut && rule._basenameOnly ? basename5 : path9
4559
+ shortcut && rule._basenameOnly ? basename5 : path11
4560
4560
  )) {
4561
4561
  ignored = !negative;
4562
4562
  unignored = negative;
@@ -4576,17 +4576,17 @@ var require_ignore = __commonJS({
4576
4576
  var throwError = (message, Ctor) => {
4577
4577
  throw new Ctor(message);
4578
4578
  };
4579
- var checkPath = (path9, originalPath, doThrow) => {
4580
- if (!isString2(path9)) {
4579
+ var checkPath = (path11, originalPath, doThrow) => {
4580
+ if (!isString2(path11)) {
4581
4581
  return doThrow(
4582
4582
  `path must be a string, but got \`${originalPath}\``,
4583
4583
  TypeError
4584
4584
  );
4585
4585
  }
4586
- if (!path9) {
4586
+ if (!path11) {
4587
4587
  return doThrow(`path must not be empty`, TypeError);
4588
4588
  }
4589
- if (checkPath.isNotRelative(path9)) {
4589
+ if (checkPath.isNotRelative(path11)) {
4590
4590
  const r = "`path.relative()`d";
4591
4591
  return doThrow(
4592
4592
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -4595,25 +4595,25 @@ var require_ignore = __commonJS({
4595
4595
  }
4596
4596
  return true;
4597
4597
  };
4598
- var isNotRelative = (path9) => {
4599
- const first = path9.charCodeAt(0);
4598
+ var isNotRelative = (path11) => {
4599
+ const first = path11.charCodeAt(0);
4600
4600
  if (first === SLASH_CODE) {
4601
4601
  return true;
4602
4602
  }
4603
4603
  if (first !== DOT_CODE) {
4604
4604
  return false;
4605
4605
  }
4606
- if (path9.length === 1) {
4606
+ if (path11.length === 1) {
4607
4607
  return true;
4608
4608
  }
4609
- const second = path9.charCodeAt(1);
4609
+ const second = path11.charCodeAt(1);
4610
4610
  if (second === SLASH_CODE) {
4611
4611
  return true;
4612
4612
  }
4613
4613
  if (second !== DOT_CODE) {
4614
4614
  return false;
4615
4615
  }
4616
- return path9.length === 2 || path9.charCodeAt(2) === SLASH_CODE;
4616
+ return path11.length === 2 || path11.charCodeAt(2) === SLASH_CODE;
4617
4617
  };
4618
4618
  checkPath.isNotRelative = isNotRelative;
4619
4619
  checkPath.convert = (p) => p;
@@ -4644,56 +4644,56 @@ var require_ignore = __commonJS({
4644
4644
  }
4645
4645
  // @returns {TestResult}
4646
4646
  _test(originalPath, cache, checkUnignored) {
4647
- const path9 = originalPath && checkPath.convert(originalPath);
4647
+ const path11 = originalPath && checkPath.convert(originalPath);
4648
4648
  checkPath(
4649
- path9,
4649
+ path11,
4650
4650
  originalPath,
4651
4651
  this._strictPathCheck ? throwError : RETURN_FALSE
4652
4652
  );
4653
- return this._t(path9, cache, checkUnignored);
4653
+ return this._t(path11, cache, checkUnignored);
4654
4654
  }
4655
- checkIgnore(path9) {
4656
- if (path9.charCodeAt(path9.length - 1) !== SLASH_CODE) {
4657
- return this.test(path9);
4655
+ checkIgnore(path11) {
4656
+ if (path11.charCodeAt(path11.length - 1) !== SLASH_CODE) {
4657
+ return this.test(path11);
4658
4658
  }
4659
- const parentPath = parentOf(path9);
4659
+ const parentPath = parentOf(path11);
4660
4660
  if (parentPath) {
4661
4661
  const parent = this._t(parentPath, this._testCache, true);
4662
4662
  if (parent.ignored) {
4663
4663
  return parent;
4664
4664
  }
4665
4665
  }
4666
- return this._rules.test(path9, false, MODE_CHECK_IGNORE);
4666
+ return this._rules.test(path11, false, MODE_CHECK_IGNORE);
4667
4667
  }
4668
- _t(path9, cache, checkUnignored) {
4669
- if (path9 in cache) {
4670
- return cache[path9];
4668
+ _t(path11, cache, checkUnignored) {
4669
+ if (path11 in cache) {
4670
+ return cache[path11];
4671
4671
  }
4672
- const parentPath = parentOf(path9);
4672
+ const parentPath = parentOf(path11);
4673
4673
  const parent = parentPath ? this._t(parentPath, cache, checkUnignored) : UNDEFINED;
4674
- return cache[path9] = parent && parent.ignored ? parent : this._rules.test(path9, checkUnignored, MODE_IGNORE);
4674
+ return cache[path11] = parent && parent.ignored ? parent : this._rules.test(path11, checkUnignored, MODE_IGNORE);
4675
4675
  }
4676
- ignores(path9) {
4677
- return this._test(path9, this._ignoreCache, false).ignored;
4676
+ ignores(path11) {
4677
+ return this._test(path11, this._ignoreCache, false).ignored;
4678
4678
  }
4679
4679
  createFilter() {
4680
- return (path9) => !this.ignores(path9);
4680
+ return (path11) => !this.ignores(path11);
4681
4681
  }
4682
4682
  filter(paths) {
4683
4683
  return makeArray(paths).filter(this.createFilter());
4684
4684
  }
4685
4685
  // @returns {TestResult}
4686
- test(path9) {
4687
- return this._test(path9, this._testCache, true);
4686
+ test(path11) {
4687
+ return this._test(path11, this._testCache, true);
4688
4688
  }
4689
4689
  };
4690
4690
  var factory = (options) => new Ignore(options);
4691
- var isPathValid = (path9) => checkPath(path9 && checkPath.convert(path9), path9, RETURN_FALSE);
4691
+ var isPathValid = (path11) => checkPath(path11 && checkPath.convert(path11), path11, RETURN_FALSE);
4692
4692
  var setupWindows = () => {
4693
4693
  const makePosix = (str2) => /^\\\\\?\\/.test(str2) || /["<>|\u0000-\u001F]+/u.test(str2) ? str2 : str2.replace(/\\/g, "/");
4694
4694
  checkPath.convert = makePosix;
4695
4695
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
4696
- checkPath.isNotRelative = (path9) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path9) || isNotRelative(path9);
4696
+ checkPath.isNotRelative = (path11) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path11) || isNotRelative(path11);
4697
4697
  };
4698
4698
  if (
4699
4699
  // Detect `process` so that it can run in browsers.
@@ -5220,10 +5220,10 @@ var init_agent_hooks = __esm({
5220
5220
  }
5221
5221
  });
5222
5222
 
5223
- // raw-import:/tmp/slz-publish-wt.AbcjgE/packages/shared/hooks/src/notify.sh
5223
+ // raw-import:/tmp/slz-publish-wt.a2Pyg4/packages/shared/hooks/src/notify.sh
5224
5224
  var notify_default;
5225
5225
  var init_notify = __esm({
5226
- "raw-import:/tmp/slz-publish-wt.AbcjgE/packages/shared/hooks/src/notify.sh"() {
5226
+ "raw-import:/tmp/slz-publish-wt.a2Pyg4/packages/shared/hooks/src/notify.sh"() {
5227
5227
  notify_default = `#!/bin/sh
5228
5228
  # SlayZone agent lifecycle hook \u2014 BENIGN DUMB FORWARDER.
5229
5229
  #
@@ -5385,10 +5385,10 @@ exit 0
5385
5385
  }
5386
5386
  });
5387
5387
 
5388
- // raw-import:/tmp/slz-publish-wt.AbcjgE/packages/shared/hooks/src/opencode-plugin.js
5388
+ // raw-import:/tmp/slz-publish-wt.a2Pyg4/packages/shared/hooks/src/opencode-plugin.js
5389
5389
  var opencode_plugin_default;
5390
5390
  var init_opencode_plugin = __esm({
5391
- "raw-import:/tmp/slz-publish-wt.AbcjgE/packages/shared/hooks/src/opencode-plugin.js"() {
5391
+ "raw-import:/tmp/slz-publish-wt.a2Pyg4/packages/shared/hooks/src/opencode-plugin.js"() {
5392
5392
  opencode_plugin_default = "// SlayZone opencode plugin v1\n//\n// Forwards OpenCode SDK session lifecycle events to the SlayZone agent-hook\n// notify script over a local shell exec. Plugin is loaded by OpenCode from\n// ${XDG_CONFIG_HOME:-~/.config}/opencode/plugin/slayzone-notify.js\n//\n// The {{NOTIFY_PATH}} placeholder is substituted by the SlayZone installer\n// (packages/apps/app/src/main/agent-hooks/opencode-plugin-installer.ts) at\n// install time with the absolute path to `~/.slayzone/hooks/notify.sh`.\n//\n// Behavior:\n// - No-op when SLAYZONE_TASK_ID is unset (plugin only fires inside SlayZone PTYs).\n// - Subagent (child session) events are suppressed \u2014 oh-my-opencode + similar\n// tools spawn many child sessions and would otherwise flap Start/Stop.\n// - On client.session.list() error, sessions are assumed CHILD (safer to\n// suppress than emit a false-positive Start/Stop).\n// - Handles both modern (session.status w/ nested type) and legacy\n// (session.busy / session.idle) event shapes for forward compat.\n// - Singleton guard prevents double-load if OpenCode re-imports the plugin.\n\nexport const SlayzoneNotifyPlugin = async ({ $, client }) => {\n if (globalThis.__slayzoneOpencodePluginV1) return {}\n globalThis.__slayzoneOpencodePluginV1 = true\n if (!process?.env?.SLAYZONE_TASK_ID) return {}\n\n const notifyPath = '{{NOTIFY_PATH}}'\n let currentState = 'idle' // 'idle' | 'busy'\n let rootSessionID = null // first non-child session we see\n let stopSent = false // dedup Stop emits\n const childSessionCache = new Map() // sessionID -> isChild bool\n\n const notify = async (eventName) => {\n const payload = JSON.stringify({ hook_event_name: eventName })\n try {\n await $`bash ${notifyPath} ${payload}`\n } catch {\n // Hook failures must never bubble into the OpenCode TUI.\n }\n }\n\n const isChildSession = async (sessionID) => {\n if (!sessionID) return true\n if (!client?.session?.list) return true\n if (childSessionCache.has(sessionID)) return childSessionCache.get(sessionID)\n try {\n const sessions = await client.session.list()\n const session = sessions.data?.find((s) => s.id === sessionID)\n const isChild = !!session?.parentID\n childSessionCache.set(sessionID, isChild)\n return isChild\n } catch {\n // On error, assume CHILD \u2014 safer than emitting a false-positive Start/Stop.\n return true\n }\n }\n\n const handleBusy = async (sessionID) => {\n if (!rootSessionID) rootSessionID = sessionID\n if (sessionID !== rootSessionID) return\n if (currentState === 'idle') {\n currentState = 'busy'\n stopSent = false\n await notify('Start')\n }\n }\n\n const handleStop = async (sessionID) => {\n if (rootSessionID && sessionID !== rootSessionID) return\n if (currentState === 'busy' && !stopSent) {\n currentState = 'idle'\n stopSent = true\n await notify('Stop')\n rootSessionID = null // reset for next session\n }\n }\n\n return {\n event: async ({ event }) => {\n const sessionID = event.properties?.sessionID ?? event.properties?.info?.id ?? null\n\n if (event.type === 'session.created') {\n const isChild = Boolean(event.properties?.info?.parentID)\n if (sessionID) childSessionCache.set(sessionID, isChild)\n if (!isChild) await notify('SessionStart')\n return\n }\n\n if (event.type === 'session.deleted') {\n const cached = sessionID ? childSessionCache.get(sessionID) : undefined\n const isChild = cached !== undefined ? cached : await isChildSession(sessionID)\n if (!isChild) await notify('SessionEnd')\n if (sessionID) childSessionCache.delete(sessionID)\n return\n }\n\n if (await isChildSession(sessionID)) return\n\n if (event.type === 'session.status') {\n const status = event.properties?.status\n if (status?.type === 'busy') await handleBusy(sessionID)\n else if (status?.type === 'idle') await handleStop(sessionID)\n }\n // Backwards-compat: some OpenCode versions emit busy/idle as top-level events.\n if (event.type === 'session.busy') await handleBusy(sessionID)\n if (event.type === 'session.idle') await handleStop(sessionID)\n if (event.type === 'session.error') await handleStop(sessionID)\n },\n 'permission.ask': async (_permission, output) => {\n if (output.status === 'ask') await notify('PermissionRequest')\n }\n }\n}\n";
5393
5393
  }
5394
5394
  });
@@ -5431,8 +5431,8 @@ var init_agent_hooks2 = __esm({
5431
5431
  });
5432
5432
 
5433
5433
  // src/bin.ts
5434
- var import_node_os9 = require("node:os");
5435
- var import_node_path15 = require("node:path");
5434
+ var import_node_os11 = require("node:os");
5435
+ var import_node_path18 = require("node:path");
5436
5436
 
5437
5437
  // ../../shared/platform/src/config-prompt.ts
5438
5438
  var import_promises = require("node:readline/promises");
@@ -5467,14 +5467,14 @@ function coerceComputerConfig(raw) {
5467
5467
  }
5468
5468
  return cfg;
5469
5469
  }
5470
- function readJsonObject(path9) {
5470
+ function readJsonObject(path11) {
5471
5471
  let raw;
5472
5472
  try {
5473
- raw = (0, import_node_fs.readFileSync)(path9, "utf8");
5473
+ raw = (0, import_node_fs.readFileSync)(path11, "utf8");
5474
5474
  } catch (err) {
5475
5475
  if (err.code === "ENOENT") return null;
5476
5476
  process.stderr.write(
5477
- `[slayzone-config] cannot read ${path9}: ${err instanceof Error ? err.message : String(err)} \u2014 using empty config
5477
+ `[slayzone-config] cannot read ${path11}: ${err instanceof Error ? err.message : String(err)} \u2014 using empty config
5478
5478
  `
5479
5479
  );
5480
5480
  return null;
@@ -5484,61 +5484,61 @@ function readJsonObject(path9) {
5484
5484
  parsed = JSON.parse(raw);
5485
5485
  } catch (err) {
5486
5486
  process.stderr.write(
5487
- `[slayzone-config] ${path9} is not valid JSON: ${err instanceof Error ? err.message : String(err)} \u2014 using empty config
5487
+ `[slayzone-config] ${path11} is not valid JSON: ${err instanceof Error ? err.message : String(err)} \u2014 using empty config
5488
5488
  `
5489
5489
  );
5490
5490
  return null;
5491
5491
  }
5492
5492
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
5493
- process.stderr.write(`[slayzone-config] ${path9} is not a JSON object \u2014 using empty config
5493
+ process.stderr.write(`[slayzone-config] ${path11} is not a JSON object \u2014 using empty config
5494
5494
  `);
5495
5495
  return null;
5496
5496
  }
5497
5497
  return parsed;
5498
5498
  }
5499
- function writeJsonAtomic(path9, data) {
5500
- const dir = (0, import_node_path.dirname)(path9);
5499
+ function writeJsonAtomic(path11, data) {
5500
+ const dir = (0, import_node_path.dirname)(path11);
5501
5501
  (0, import_node_fs.mkdirSync)(dir, { recursive: true, mode: 448 });
5502
- const tmpPath = `${path9}.tmp-${process.pid}-${Date.now()}`;
5502
+ const tmpPath = `${path11}.tmp-${process.pid}-${Date.now()}`;
5503
5503
  (0, import_node_fs.writeFileSync)(tmpPath, `${JSON.stringify(data, null, 2)}
5504
5504
  `, { mode: 384 });
5505
5505
  try {
5506
- (0, import_node_fs.renameSync)(tmpPath, path9);
5506
+ (0, import_node_fs.renameSync)(tmpPath, path11);
5507
5507
  } catch (err) {
5508
5508
  (0, import_node_fs.rmSync)(tmpPath, { force: true });
5509
5509
  throw err;
5510
5510
  }
5511
5511
  }
5512
- function loadComputerConfigFile(path9 = getComputerConfigFilePath()) {
5513
- const raw = readJsonObject(path9);
5512
+ function loadComputerConfigFile(path11 = getComputerConfigFilePath()) {
5513
+ const raw = readJsonObject(path11);
5514
5514
  if (raw) return coerceComputerConfig(raw);
5515
- const legacy = readJsonObject(legacyConfigFilePathFor(path9));
5515
+ const legacy = readJsonObject(legacyConfigFilePathFor(path11));
5516
5516
  return legacy ? coerceComputerConfig(legacy) : {};
5517
5517
  }
5518
- function ignoredComputerConfigKeys(path9 = getComputerConfigFilePath()) {
5519
- const raw = readJsonObject(path9) ?? readJsonObject(legacyConfigFilePathFor(path9));
5518
+ function ignoredComputerConfigKeys(path11 = getComputerConfigFilePath()) {
5519
+ const raw = readJsonObject(path11) ?? readJsonObject(legacyConfigFilePathFor(path11));
5520
5520
  if (!raw) return [];
5521
5521
  return IGNORED_COMPUTER_CONFIG_KEYS.filter((key) => raw[key] !== void 0);
5522
5522
  }
5523
- function saveComputerConfigFile(cfg, path9 = getComputerConfigFilePath()) {
5524
- writeJsonAtomic(path9, cfg);
5523
+ function saveComputerConfigFile(cfg, path11 = getComputerConfigFilePath()) {
5524
+ writeJsonAtomic(path11, cfg);
5525
5525
  }
5526
- function updateComputerConfigFile(patch, path9 = getComputerConfigFilePath()) {
5527
- const current = readJsonObject(path9);
5526
+ function updateComputerConfigFile(patch, path11 = getComputerConfigFilePath()) {
5527
+ const current = readJsonObject(path11);
5528
5528
  const merged = current ? coerceComputerConfig(current) : {};
5529
5529
  for (const [k, v] of Object.entries(patch)) {
5530
5530
  if (v !== void 0) merged[k] = v;
5531
5531
  }
5532
- saveComputerConfigFile(merged, path9);
5532
+ saveComputerConfigFile(merged, path11);
5533
5533
  return merged;
5534
5534
  }
5535
- function updateJsonFile(patch, path9) {
5536
- const current = readJsonObject(path9) ?? {};
5535
+ function updateJsonFile(patch, path11) {
5536
+ const current = readJsonObject(path11) ?? {};
5537
5537
  const merged = { ...current };
5538
5538
  for (const [k, v] of Object.entries(patch)) {
5539
5539
  if (v !== void 0) merged[k] = v;
5540
5540
  }
5541
- writeJsonAtomic(path9, merged);
5541
+ writeJsonAtomic(path11, merged);
5542
5542
  }
5543
5543
 
5544
5544
  // ../../shared/platform/src/config-prompt.ts
@@ -5610,10 +5610,10 @@ function isLoopbackAuthority(addr) {
5610
5610
  return false;
5611
5611
  }
5612
5612
  }
5613
- function hubUrlFromAddr(addr, kind, path9 = "") {
5613
+ function hubUrlFromAddr(addr, kind, path11 = "") {
5614
5614
  const secure = !isLoopbackAuthority(addr);
5615
5615
  const scheme = kind === "ws" ? secure ? "wss" : "ws" : secure ? "https" : "http";
5616
- return `${scheme}://${addr}${path9}`;
5616
+ return `${scheme}://${addr}${path11}`;
5617
5617
  }
5618
5618
  function hubAddrFromUrl(url2) {
5619
5619
  let u;
@@ -6715,10 +6715,10 @@ function mergeDefs(...defs) {
6715
6715
  function cloneDef(schema) {
6716
6716
  return mergeDefs(schema._zod.def);
6717
6717
  }
6718
- function getElementAtPath(obj, path9) {
6719
- if (!path9)
6718
+ function getElementAtPath(obj, path11) {
6719
+ if (!path11)
6720
6720
  return obj;
6721
- return path9.reduce((acc, key) => acc?.[key], obj);
6721
+ return path11.reduce((acc, key) => acc?.[key], obj);
6722
6722
  }
6723
6723
  function promiseAllObject(promisesObj) {
6724
6724
  const keys = Object.keys(promisesObj);
@@ -7058,11 +7058,11 @@ function explicitlyAborted(x, startIndex = 0) {
7058
7058
  }
7059
7059
  return false;
7060
7060
  }
7061
- function prefixIssues(path9, issues) {
7061
+ function prefixIssues(path11, issues) {
7062
7062
  return issues.map((iss) => {
7063
7063
  var _a3;
7064
7064
  (_a3 = iss).path ?? (_a3.path = []);
7065
- iss.path.unshift(path9);
7065
+ iss.path.unshift(path11);
7066
7066
  return iss;
7067
7067
  });
7068
7068
  }
@@ -7512,16 +7512,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
7512
7512
  }
7513
7513
  function formatError(error62, mapper = (issue2) => issue2.message) {
7514
7514
  const fieldErrors = { _errors: [] };
7515
- const processError = (error63, path9 = []) => {
7515
+ const processError = (error63, path11 = []) => {
7516
7516
  for (const issue2 of error63.issues) {
7517
7517
  if (issue2.code === "invalid_union" && issue2.errors.length) {
7518
- issue2.errors.map((issues) => processError({ issues }, [...path9, ...issue2.path]));
7518
+ issue2.errors.map((issues) => processError({ issues }, [...path11, ...issue2.path]));
7519
7519
  } else if (issue2.code === "invalid_key") {
7520
- processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
7520
+ processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
7521
7521
  } else if (issue2.code === "invalid_element") {
7522
- processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
7522
+ processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
7523
7523
  } else {
7524
- const fullpath = [...path9, ...issue2.path];
7524
+ const fullpath = [...path11, ...issue2.path];
7525
7525
  if (fullpath.length === 0) {
7526
7526
  fieldErrors._errors.push(mapper(issue2));
7527
7527
  } else {
@@ -7560,17 +7560,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
7560
7560
  }
7561
7561
  function treeifyError(error62, mapper = (issue2) => issue2.message) {
7562
7562
  const result = { errors: [] };
7563
- const processError = (error63, path9 = []) => {
7563
+ const processError = (error63, path11 = []) => {
7564
7564
  var _a3;
7565
7565
  for (const issue2 of error63.issues) {
7566
7566
  if (issue2.code === "invalid_union" && issue2.errors.length) {
7567
- issue2.errors.map((issues) => processError({ issues }, [...path9, ...issue2.path]));
7567
+ issue2.errors.map((issues) => processError({ issues }, [...path11, ...issue2.path]));
7568
7568
  } else if (issue2.code === "invalid_key") {
7569
- processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
7569
+ processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
7570
7570
  } else if (issue2.code === "invalid_element") {
7571
- processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
7571
+ processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
7572
7572
  } else {
7573
- const fullpath = [...path9, ...issue2.path];
7573
+ const fullpath = [...path11, ...issue2.path];
7574
7574
  if (fullpath.length === 0) {
7575
7575
  result.errors.push(mapper(issue2));
7576
7576
  continue;
@@ -7609,8 +7609,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
7609
7609
  }
7610
7610
  function toDotPath(_path) {
7611
7611
  const segs = [];
7612
- const path9 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
7613
- for (const seg of path9) {
7612
+ const path11 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
7613
+ for (const seg of path11) {
7614
7614
  if (typeof seg === "number")
7615
7615
  segs.push(`[${seg}]`);
7616
7616
  else if (typeof seg === "symbol")
@@ -7896,8 +7896,8 @@ function emoji() {
7896
7896
  }
7897
7897
  var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
7898
7898
  var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
7899
- var mac = (delimiter3) => {
7900
- const escapedDelim = escapeRegex(delimiter3 ?? ":");
7899
+ var mac = (delimiter4) => {
7900
+ const escapedDelim = escapeRegex(delimiter4 ?? ":");
7901
7901
  return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
7902
7902
  };
7903
7903
  var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
@@ -22585,14 +22585,14 @@ var nonoptionalProcessor = (schema, ctx, _json, params) => {
22585
22585
  var UNREPRESENTABLE_DEFAULT = /* @__PURE__ */ Symbol();
22586
22586
  function serializeDefaultValue(value, schema, ctx, json2, params) {
22587
22587
  let unrepresentable = false;
22588
- const serialized = JSON.stringify(value, (_, val) => {
22588
+ const serialized2 = JSON.stringify(value, (_, val) => {
22589
22589
  if (typeof val !== "bigint")
22590
22590
  return val;
22591
22591
  unrepresentable = true;
22592
22592
  return null;
22593
22593
  });
22594
22594
  if (!unrepresentable)
22595
- return JSON.parse(serialized);
22595
+ return JSON.parse(serialized2);
22596
22596
  handleUnrepresentable(schema, ctx, json2, params, "BigInt defaults cannot be represented in JSON Schema");
22597
22597
  return UNREPRESENTABLE_DEFAULT;
22598
22598
  }
@@ -24712,13 +24712,13 @@ function resolveRef(ref, ctx) {
24712
24712
  if (!ref.startsWith("#")) {
24713
24713
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
24714
24714
  }
24715
- const path9 = ref.slice(1).split("/").filter(Boolean);
24716
- if (path9.length === 0) {
24715
+ const path11 = ref.slice(1).split("/").filter(Boolean);
24716
+ if (path11.length === 0) {
24717
24717
  return ctx.rootSchema;
24718
24718
  }
24719
24719
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
24720
- if (path9[0] === defsKey) {
24721
- const key = path9[1] === void 0 ? void 0 : decodeJSONPointerSegment(path9[1]);
24720
+ if (path11[0] === defsKey) {
24721
+ const key = path11[1] === void 0 ? void 0 : decodeJSONPointerSegment(path11[1]);
24722
24722
  if (!key || !ctx.defs[key]) {
24723
24723
  throw new Error(`Reference not found: ${ref}`);
24724
24724
  }
@@ -25569,7 +25569,7 @@ function date4(params) {
25569
25569
 
25570
25570
  // ../../domains/computer-transport/src/shared/frames.ts
25571
25571
  var COMPUTER_EXIT_NEEDS_RE_ENROLLMENT = 78;
25572
- var COMPUTER_PROTOCOL_VERSION = 3;
25572
+ var COMPUTER_PROTOCOL_VERSION = 4;
25573
25573
  var ComputerTransportErrorCodes = {
25574
25574
  /** Command is part of the contract but not implemented by this peer. */
25575
25575
  unimplemented: -32001,
@@ -25592,8 +25592,20 @@ var ComputerTransportErrorCodes = {
25592
25592
  * transient — retrying the same request is refused again. The hub audits it as
25593
25593
  * `denied` and the message says what to do on the device.
25594
25594
  */
25595
- refusedByComputer: -32006
25595
+ refusedByComputer: -32006,
25596
+ /**
25597
+ * An agent for a project this computer holds no folder for. Not transient, and
25598
+ * not a policy refusal: the user picks the project's folder on this computer
25599
+ * (or the hub delivers it), then the same spawn works.
25600
+ */
25601
+ projectNotPlaced: -32007
25596
25602
  };
25603
+ function projectNotPlacedError(projectId) {
25604
+ return new RpcError(
25605
+ ComputerTransportErrorCodes.projectNotPlaced,
25606
+ `This computer has no folder for project ${projectId}. Pick the project's folder on this computer, then try again.`
25607
+ );
25608
+ }
25597
25609
  var requestActorSchema = external_exports.object({
25598
25610
  userId: external_exports.string().min(1).max(200).nullable(),
25599
25611
  /** `trpc <path>` / `METHOD /path` the request entered the hub through. */
@@ -25659,12 +25671,14 @@ var ComputerNotificationMethods = {
25659
25671
  };
25660
25672
  var ptyDataParamsSchema = external_exports.object({
25661
25673
  sessionId: external_exports.string().min(1),
25674
+ spawnId: external_exports.string().min(1).optional(),
25662
25675
  /** Monotonic per-session sequence number, preserved end-to-end. */
25663
25676
  seq: external_exports.number().int().nonnegative(),
25664
25677
  data: external_exports.string()
25665
25678
  });
25666
25679
  var ptyExitParamsSchema = external_exports.object({
25667
25680
  sessionId: external_exports.string().min(1),
25681
+ spawnId: external_exports.string().min(1).optional(),
25668
25682
  exitCode: external_exports.number().int().nullable(),
25669
25683
  signal: external_exports.string().nullable().optional(),
25670
25684
  /**
@@ -25684,13 +25698,19 @@ var procDataParamsSchema = external_exports.object({
25684
25698
  /** Monotonic per-session sequence number for `stdout`. Absent on stderr. */
25685
25699
  seq: external_exports.number().int().nonnegative().optional(),
25686
25700
  data: external_exports.string(),
25687
- stream: external_exports.enum(["stdout", "stderr"]).optional()
25701
+ stream: external_exports.enum(["stdout", "stderr"]).optional(),
25702
+ /** The spawn attempt that produced this output; the hub drops a replaced attempt's. */
25703
+ spawnId: external_exports.string().min(1).optional()
25688
25704
  });
25689
25705
  var procExitParamsSchema = external_exports.object({
25690
25706
  sessionId: external_exports.string().min(1),
25691
25707
  exitCode: external_exports.number().int().nullable(),
25692
25708
  signal: external_exports.string().nullable().optional(),
25693
- reason: external_exports.string().min(1).optional()
25709
+ reason: external_exports.string().min(1).optional(),
25710
+ /** The spawn attempt that exited. */
25711
+ spawnId: external_exports.string().min(1).optional(),
25712
+ /** Why the process could not run (e.g. the OS refused to start it). */
25713
+ error: external_exports.string().optional()
25694
25714
  });
25695
25715
  var fsChangeParamsSchema = external_exports.object({
25696
25716
  watchId: external_exports.string().min(1),
@@ -25717,6 +25737,8 @@ var HubToComputerMethods = {
25717
25737
  ptyPrioritize: "pty.prioritize",
25718
25738
  /** Which of claude's background jobs still have a live process under a session. */
25719
25739
  ptyBackgroundJobs: "pty.backgroundJobs",
25740
+ /** Doctor's facts about this computer: node-pty install, the shell, agent binaries. */
25741
+ ptyDoctor: "pty.doctor",
25720
25742
  // warm pool (pre-warmed agents live ON the computer — see server/warm-*)
25721
25743
  ptyWarmSpawn: "pty.warmSpawn",
25722
25744
  ptyWarmAdopt: "pty.warmAdopt",
@@ -25775,7 +25797,13 @@ var HubToComputerMethods = {
25775
25797
  /** Edit the computer's own path-jail (Settings → Computers). */
25776
25798
  computerSetAllowedRoots: "computer.setAllowedRoots",
25777
25799
  /** Run `gh` on the computer (PR panel). One frame, not one per gh subcommand. */
25778
- ghExec: "gh.exec"
25800
+ ghExec: "gh.exec",
25801
+ /**
25802
+ * Read the active branch of a Claude Code session file on the computer (a
25803
+ * resumed or handed-over conversation's history for the chat timeline). Only
25804
+ * that file: the path is built from the cwd and a uuid, nothing else.
25805
+ */
25806
+ claudeSessionRead: "claudeSession.read"
25779
25807
  };
25780
25808
  var spawnIdField = external_exports.string().min(1).optional();
25781
25809
  var exitedSessionSchema = external_exports.object({
@@ -25802,6 +25830,9 @@ var ptySpawnParamsSchema = external_exports.object({
25802
25830
  startupTimeoutMs: external_exports.number().int().positive().optional(),
25803
25831
  command: external_exports.string().min(1),
25804
25832
  args: external_exports.array(external_exports.string()).optional(),
25833
+ /** New computers resolve their own login shell; command/args remain for older peers. */
25834
+ computerShell: external_exports.boolean().optional(),
25835
+ computerShellCommand: external_exports.string().optional(),
25805
25836
  /**
25806
25837
  * Absolute cwd. Optional now: when absent the COMPUTER resolves it from
25807
25838
  * `projectId` against its own `project.*` mapping, because the hub cannot —
@@ -25820,6 +25851,19 @@ var ptySpawnParamsSchema = external_exports.object({
25820
25851
  }).refine((p) => p.cwd !== void 0 || p.projectId !== void 0, {
25821
25852
  message: "pty.spawn needs either cwd or projectId"
25822
25853
  });
25854
+ var HUB_PTY_ENV_KEYS = [
25855
+ "SLAYZONE_TASK_ID",
25856
+ "SLAYZONE_PROJECT_ID",
25857
+ "SLAYZONE_AGENT_ID",
25858
+ "SLAYZONE_AGENT_HOOK_CONTEXT"
25859
+ ];
25860
+ function pickHubPtyEnv(env) {
25861
+ const picked = {};
25862
+ for (const key of HUB_PTY_ENV_KEYS) {
25863
+ if (env?.[key] !== void 0) picked[key] = env[key];
25864
+ }
25865
+ return picked;
25866
+ }
25823
25867
  var ptySpawnResultSchema = external_exports.object({
25824
25868
  pid: external_exports.number().int(),
25825
25869
  /** The computer armed the requested `startupTimeoutMs`. Absent from a computer
@@ -25828,10 +25872,12 @@ var ptySpawnResultSchema = external_exports.object({
25828
25872
  });
25829
25873
  var ptyKillParamsSchema = external_exports.object({
25830
25874
  sessionId: external_exports.string().min(1),
25875
+ spawnId: spawnIdField,
25831
25876
  signal: external_exports.string().optional()
25832
25877
  });
25833
25878
  var ptyPrioritizeParamsSchema = external_exports.object({
25834
- sessionId: external_exports.string().min(1)
25879
+ sessionId: external_exports.string().min(1),
25880
+ spawnId: spawnIdField
25835
25881
  });
25836
25882
  var ptyPrioritizeResultSchema = external_exports.object({
25837
25883
  ok: external_exports.literal(true),
@@ -25839,6 +25885,7 @@ var ptyPrioritizeResultSchema = external_exports.object({
25839
25885
  });
25840
25886
  var ptyBackgroundJobsParamsSchema = external_exports.object({
25841
25887
  sessionId: external_exports.string().min(1),
25888
+ spawnId: spawnIdField,
25842
25889
  jobs: external_exports.array(
25843
25890
  external_exports.object({
25844
25891
  id: external_exports.string().min(1),
@@ -25852,17 +25899,33 @@ var ptyBackgroundJobsResultSchema = external_exports.object({
25852
25899
  supported: external_exports.boolean(),
25853
25900
  alive: external_exports.array(external_exports.string())
25854
25901
  });
25902
+ var ptyDoctorParamsSchema = external_exports.object({
25903
+ shell: external_exports.string().min(1).max(4096),
25904
+ /** Plain command names only (`claude`, `cursor-agent`), never paths or syntax. */
25905
+ binaries: external_exports.array(
25906
+ external_exports.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._+-]*$/)
25907
+ ).max(16)
25908
+ }).strict();
25909
+ var ptyDoctorResultSchema = external_exports.object({
25910
+ ok: external_exports.literal(true),
25911
+ problems: external_exports.array(external_exports.object({ check: external_exports.string(), detail: external_exports.string(), fix: external_exports.string() })),
25912
+ shellExists: external_exports.boolean(),
25913
+ binaries: external_exports.array(external_exports.object({ binary: external_exports.string(), path: external_exports.string().nullable() }))
25914
+ });
25855
25915
  var ptyResizeParamsSchema = external_exports.object({
25856
25916
  sessionId: external_exports.string().min(1),
25917
+ spawnId: spawnIdField,
25857
25918
  cols: external_exports.number().int().positive(),
25858
25919
  rows: external_exports.number().int().positive()
25859
25920
  });
25860
25921
  var ptyWriteParamsSchema = external_exports.object({
25861
25922
  sessionId: external_exports.string().min(1),
25923
+ spawnId: spawnIdField,
25862
25924
  data: external_exports.string()
25863
25925
  });
25864
25926
  var ptyGetBufferSinceParamsSchema = external_exports.object({
25865
25927
  sessionId: external_exports.string().min(1),
25928
+ spawnId: spawnIdField,
25866
25929
  /**
25867
25930
  * Exclusive lower bound — frames with `seq > this` are replayed.
25868
25931
  *
@@ -25904,6 +25967,7 @@ var ptyWarmSpawnParamsSchema = external_exports.object({
25904
25967
  warmId: external_exports.string().min(1),
25905
25968
  command: external_exports.string().min(1),
25906
25969
  args: external_exports.array(external_exports.string()).optional(),
25970
+ computerShell: external_exports.boolean().optional(),
25907
25971
  cwd: external_exports.string().min(1),
25908
25972
  env: external_exports.record(external_exports.string(), external_exports.string()).optional(),
25909
25973
  cols: external_exports.number().int().positive().optional(),
@@ -26005,6 +26069,22 @@ var ghExecResultSchema = external_exports.object({
26005
26069
  stdout: external_exports.string(),
26006
26070
  stderr: external_exports.string()
26007
26071
  });
26072
+ var claudeSessionReadParamsSchema = external_exports.object({
26073
+ cwd: external_exports.string().min(1).optional(),
26074
+ projectId: external_exports.string().min(1).optional(),
26075
+ sessionId: external_exports.string().uuid(),
26076
+ /** Newest records to return at most. */
26077
+ maxRecords: external_exports.number().int().positive().max(5e3),
26078
+ /** Their total JSON size at most (large tool results are shortened first). */
26079
+ maxBytes: external_exports.number().int().positive().max(16 * 1024 * 1024)
26080
+ });
26081
+ var claudeSessionReadResultSchema = external_exports.object({
26082
+ /** False: no session file for that id in that cwd's project dir. */
26083
+ found: external_exports.boolean(),
26084
+ records: external_exports.array(external_exports.record(external_exports.string(), external_exports.unknown())),
26085
+ /** Older records of the branch that were not sent (the caps). */
26086
+ omitted: external_exports.number().int().nonnegative()
26087
+ });
26008
26088
  var computerShutdownParamsSchema = external_exports.object({
26009
26089
  reason: external_exports.string().optional(),
26010
26090
  /** Grace period before the computer may hard-exit. */
@@ -26252,7 +26332,9 @@ var procSpawnParamsSchema = external_exports.object({
26252
26332
  */
26253
26333
  shell: external_exports.boolean().optional(),
26254
26334
  cwd: external_exports.string().min(1).optional(),
26255
- env: external_exports.record(external_exports.string(), external_exports.string()).optional()
26335
+ env: external_exports.record(external_exports.string(), external_exports.string()).optional(),
26336
+ /** Agent-start admission lane (chat agents), as for `pty.spawn`. Absent: start at once. */
26337
+ admission: ptySpawnAdmissionSchema.optional()
26256
26338
  });
26257
26339
  var procSpawnResultSchema = external_exports.object({
26258
26340
  /** `null` when the OS never assigned one (immediate spawn failure). The computer
@@ -26262,12 +26344,38 @@ var procSpawnResultSchema = external_exports.object({
26262
26344
  });
26263
26345
  var procKillParamsSchema = external_exports.object({
26264
26346
  sessionId: external_exports.string().min(1),
26265
- signal: external_exports.string().optional()
26347
+ signal: external_exports.string().optional(),
26348
+ /** Only kill this attempt: a kill meant for a replaced process must not hit its successor. */
26349
+ spawnId: spawnIdField
26266
26350
  });
26267
26351
  var procWriteParamsSchema = external_exports.object({
26268
26352
  sessionId: external_exports.string().min(1),
26269
- data: external_exports.string()
26353
+ data: external_exports.string(),
26354
+ /** Only write to this attempt; a replaced process is refused (`stale-spawn`). */
26355
+ spawnId: spawnIdField,
26356
+ /**
26357
+ * Idempotency key: a write the hub retries (its reply was lost) is written once.
26358
+ * The reply is sent only after the bytes reached the process's stdin, and a
26359
+ * write that cannot be delivered (unknown session, replaced attempt, broken pipe)
26360
+ * is refused rather than acked — so `{ok:true}` means delivered.
26361
+ */
26362
+ writeId: external_exports.string().min(1).max(200).optional()
26270
26363
  });
26364
+ var procWriteResultSchema = external_exports.object({
26365
+ ok: external_exports.literal(true),
26366
+ delivered: external_exports.literal(true).optional(),
26367
+ duplicate: external_exports.boolean().optional()
26368
+ });
26369
+ var ProcRefusalReasons = {
26370
+ /** `proc.spawn`: the command is not on the computer's PATH. `data.binary` names it. */
26371
+ binaryNotFound: "binary-not-found",
26372
+ /** `proc.write`: no process under this session id. */
26373
+ unknownSession: "unknown-session",
26374
+ /** `proc.write`: the session now runs a different spawn attempt. */
26375
+ staleSpawn: "stale-spawn",
26376
+ /** `proc.write`: the process's stdin is closed or broken. */
26377
+ stdinClosed: "stdin-closed"
26378
+ };
26271
26379
  var procGetBufferSinceParamsSchema = external_exports.object({
26272
26380
  sessionId: external_exports.string().min(1),
26273
26381
  seq: external_exports.number().int()
@@ -26349,6 +26457,20 @@ function wsDataToText(data) {
26349
26457
  return data.toString("utf8");
26350
26458
  }
26351
26459
 
26460
+ // ../../domains/computer-transport/src/shared/http-tunnel.ts
26461
+ var HTTP_TUNNEL_PREFIX = "cli.v1.";
26462
+ var HTTP_TUNNEL_CHUNK = 32 * 1024;
26463
+ var httpTunnelParamsSchema = external_exports.object({
26464
+ id: external_exports.string().min(1).max(100),
26465
+ data: external_exports.string().max(Math.ceil(HTTP_TUNNEL_CHUNK / 3) * 4).optional(),
26466
+ end: external_exports.boolean().optional()
26467
+ }).strict();
26468
+ var httpTunnelOpenResultSchema = external_exports.object({ id: external_exports.string().min(1).max(100) });
26469
+ var httpTunnelReadResultSchema = external_exports.object({
26470
+ data: external_exports.string().max(Math.ceil(HTTP_TUNNEL_CHUNK / 3) * 4),
26471
+ end: external_exports.boolean()
26472
+ });
26473
+
26352
26474
  // ../../domains/computer-transport/src/client/backoff.ts
26353
26475
  var DEFAULT_BACKOFF = {
26354
26476
  initialDelayMs: 100,
@@ -26842,6 +26964,64 @@ var HubDialer = class {
26842
26964
  }
26843
26965
  };
26844
26966
 
26967
+ // ../../domains/computer-transport/src/client/http-tunnel.ts
26968
+ var import_node_stream = require("node:stream");
26969
+ async function openHttpTunnel(requester) {
26970
+ const { id } = httpTunnelOpenResultSchema.parse(
26971
+ await requester.request(`${HTTP_TUNNEL_PREFIX}open`)
26972
+ );
26973
+ let reading = false;
26974
+ const socket = new import_node_stream.Duplex({
26975
+ read() {
26976
+ if (reading) return;
26977
+ reading = true;
26978
+ void (async () => {
26979
+ try {
26980
+ while (!socket.destroyed) {
26981
+ const result = httpTunnelReadResultSchema.parse(
26982
+ await requester.request(`${HTTP_TUNNEL_PREFIX}read`, { id })
26983
+ );
26984
+ const more = result.data ? socket.push(Buffer.from(result.data, "base64")) : true;
26985
+ if (result.end) {
26986
+ socket.push(null);
26987
+ break;
26988
+ }
26989
+ if (!more) break;
26990
+ }
26991
+ } catch (error62) {
26992
+ socket.destroy(error62 instanceof Error ? error62 : new Error(String(error62)));
26993
+ } finally {
26994
+ reading = false;
26995
+ }
26996
+ })();
26997
+ },
26998
+ write(chunk, _encoding, done) {
26999
+ void (async () => {
27000
+ for (let offset = 0; offset < chunk.length; offset += HTTP_TUNNEL_CHUNK) {
27001
+ await requester.request(`${HTTP_TUNNEL_PREFIX}write`, {
27002
+ id,
27003
+ data: chunk.subarray(offset, offset + HTTP_TUNNEL_CHUNK).toString("base64")
27004
+ });
27005
+ }
27006
+ })().then(() => done(), done);
27007
+ },
27008
+ final(done) {
27009
+ void requester.request(`${HTTP_TUNNEL_PREFIX}write`, { id, end: true }).then(() => done(), done);
27010
+ },
27011
+ destroy(error62, done) {
27012
+ void requester.request(`${HTTP_TUNNEL_PREFIX}close`, { id }).catch(() => {
27013
+ });
27014
+ done(error62);
27015
+ }
27016
+ });
27017
+ Object.assign(socket, {
27018
+ setTimeout: () => socket,
27019
+ setNoDelay: () => socket,
27020
+ setKeepAlive: () => socket
27021
+ });
27022
+ return socket;
27023
+ }
27024
+
26845
27025
  // src/config.ts
26846
27026
  var import_node_fs2 = require("node:fs");
26847
27027
  var import_node_os2 = require("node:os");
@@ -26917,7 +27097,17 @@ var computerConfigSchema = external_exports.object({
26917
27097
  * projects the user adds. No file key and no env var sets it, so nothing a hub
26918
27098
  * can write turns it on.
26919
27099
  */
26920
- hubMayEditRoots: external_exports.boolean().optional()
27100
+ hubMayEditRoots: external_exports.boolean().optional(),
27101
+ /**
27102
+ * A directory whose executables take precedence over PATH for every process this
27103
+ * computer starts — prepended in `buildSpawnEnv`, the one place both the pty and
27104
+ * the proc handler build a child's env. Exists so tests can swap an agent CLI for
27105
+ * a scripted fake (`@slayzone/test-utils/fake-claude`): a dir merely prepended to
27106
+ * the inherited PATH loses, because the computer adopts the login-shell PATH with
27107
+ * its entries FIRST at boot (login-shell-path.ts). Absolute: a relative dir would
27108
+ * resolve against each spawn's cwd. Env channel only (`ENV_VARS.binOverrideDir`).
27109
+ */
27110
+ binOverrideDir: external_exports.string().min(1).refine((dir) => (0, import_node_path3.isAbsolute)(dir), { message: "must be an absolute path" }).optional()
26921
27111
  });
26922
27112
  var ENV_VARS = {
26923
27113
  // Authority ONLY — `host[:port]`, no scheme, no path. The dial scheme
@@ -26936,7 +27126,9 @@ var ENV_VARS = {
26936
27126
  // named its CONSUMER, which is what CLAUDE.md rule 2 forbids. `_TOKEN` suffix +
26937
27127
  // `HUB_` family per rule 3, matching the domain term used everywhere else
26938
27128
  // (`szjt1.`, the `join_tokens` table, mintJoinToken, POST /api/computers/join-token).
26939
- joinToken: "SLAYZONE_HUB_JOIN_TOKEN"
27129
+ joinToken: "SLAYZONE_HUB_JOIN_TOKEN",
27130
+ // Read only by the computer (rule 2), a directory (rule 3). See `binOverrideDir`.
27131
+ binOverrideDir: "SLAYZONE_COMPUTER_BIN_OVERRIDE_DIR"
26940
27132
  // No alias for the pre-rename `SLAYZONE_RUNNER_JOIN_TOKEN`: it never shipped.
26941
27133
  // The published computer betas (0.36.0-beta.2/3) read `SLAYZONE_JOIN_TOKEN`, and
26942
27134
  // the name it was renamed FROM only ever existed on unreleased main, so there is
@@ -26979,8 +27171,8 @@ var protectedPaths = [];
26979
27171
  function setProtectedPaths(paths) {
26980
27172
  protectedPaths = paths.map((p) => realpathBoundary(p));
26981
27173
  }
26982
- function isInside(path9, dir) {
26983
- return path9 === dir || path9.startsWith(dir + import_node_path3.sep);
27174
+ function isInside(path11, dir) {
27175
+ return path11 === dir || path11.startsWith(dir + import_node_path3.sep);
26984
27176
  }
26985
27177
  function isProtectedPath(absolute) {
26986
27178
  return protectedPaths.some((guarded) => isInside(absolute, guarded));
@@ -27055,7 +27247,8 @@ function loadComputerConfig(env = process.env, shared = env === process.env && e
27055
27247
  // <ROOT>/computer.config.json — base under env (spread after). The single config file.
27056
27248
  ...fromShared,
27057
27249
  ...hubUrlFromEnv !== void 0 ? { hubUrl: hubUrlFromEnv } : {},
27058
- ...envJoinToken !== void 0 ? { joinToken: envJoinToken } : {}
27250
+ ...envJoinToken !== void 0 ? { joinToken: envJoinToken } : {},
27251
+ ...env[ENV_VARS.binOverrideDir] !== void 0 ? { binOverrideDir: env[ENV_VARS.binOverrideDir] } : {}
27059
27252
  };
27060
27253
  const result = computerConfigSchema.safeParse(merged);
27061
27254
  if (result.success) {
@@ -27350,8 +27543,10 @@ function assertHubLocalExecEnabled(refusal) {
27350
27543
 
27351
27544
  // ../../shared/platform/src/env-manifest.ts
27352
27545
  var ENV_MANIFEST = {
27353
- // --- secret: credentials/tokens, never in a terminal ---
27546
+ // --- secret: credentials/tokens, never inherited into a terminal ---
27354
27547
  SLAYZONE_HUB_TOKEN: "secret",
27548
+ SLAYZONE_CLI_RELAY_TOKEN: "secret",
27549
+ // computer-issued capability, re-added per session
27355
27550
  // HMAC signing key for ALL of hub-auth (better-auth's session/cookie signer AND
27356
27551
  // the computer enroll/api-key credentials) — hence `HUB_AUTH`, not the former
27357
27552
  // `HUB_COMPUTER_TRANSPORT_SECRET`, which named just one of its consumers. Rule 2
@@ -27386,6 +27581,8 @@ var ENV_MANIFEST = {
27386
27581
  // name refuses to boot. Stripping both here keeps a terminal's dial address from
27387
27582
  // ever being mistaken for another process's listen address.
27388
27583
  SLAYZONE_HUB_ADDRESS: "infra",
27584
+ SLAYZONE_CLI_RELAY_ADDRESS: "infra",
27585
+ // computer loopback; re-added per session
27389
27586
  SLAYZONE_HUB_LISTEN_ADDRESS: "infra",
27390
27587
  // hub-only; where the hub listens
27391
27588
  SLAYZONE_HUB_PUBLIC_ADDRESS: "infra",
@@ -27452,6 +27649,11 @@ var ENV_MANIFEST = {
27452
27649
  // overrides below. `infra`: a spawned terminal must never inherit it, or an
27453
27650
  // agent would resolve a different notify.sh than the one wired into its hooks.
27454
27651
  SLAYZONE_MACHINE_DIR: "infra",
27652
+ // Computer-only: a directory whose executables take precedence over PATH for
27653
+ // every process the computer starts (spawn-env.ts). The e2e chat group puts its
27654
+ // fake `claude` there — a dir merely prepended to the inherited PATH loses to the
27655
+ // login-shell PATH the computer adopts at boot. Infra: an agent must not inherit it.
27656
+ SLAYZONE_COMPUTER_BIN_OVERRIDE_DIR: "infra",
27455
27657
  // per-provider config/hook/plugin path overrides (test/sandbox redirects)
27456
27658
  SLAYZONE_CLAUDE_SETTINGS_PATH: "infra",
27457
27659
  SLAYZONE_GEMINI_SETTINGS_PATH: "infra",
@@ -27499,16 +27701,16 @@ var NON_PREFIXED_INFRA = /* @__PURE__ */ new Set([
27499
27701
  function sanitizeSpawnEnv(base) {
27500
27702
  const out = {};
27501
27703
  for (const [key, value] of Object.entries(base)) {
27502
- if (typeof value !== "string") continue;
27503
- if (NON_PREFIXED_INFRA.has(key)) continue;
27504
- if (key.startsWith("SLAYZONE_")) {
27505
- if (ENV_MANIFEST[key] === "global") out[key] = value;
27506
- continue;
27507
- }
27508
- out[key] = value;
27704
+ if (keptInSpawnEnv(key, value)) out[key] = value;
27509
27705
  }
27510
27706
  return out;
27511
27707
  }
27708
+ function keptInSpawnEnv(key, value) {
27709
+ if (typeof value !== "string") return false;
27710
+ if (NON_PREFIXED_INFRA.has(key)) return false;
27711
+ if (key.startsWith("SLAYZONE_")) return ENV_MANIFEST[key] === "global";
27712
+ return true;
27713
+ }
27512
27714
 
27513
27715
  // ../../shared/platform/src/index.ts
27514
27716
  init_fs_utils();
@@ -27569,6 +27771,15 @@ function getShellStartupArgs(shellPath) {
27569
27771
  }
27570
27772
  return [];
27571
27773
  }
27774
+ function quoteForShell(arg) {
27775
+ if ((0, import_os2.platform)() === "win32") {
27776
+ if (arg.length === 0) return '""';
27777
+ if (!/[\s"&|<>^%!]/.test(arg)) return arg;
27778
+ return `"${arg.replace(/"/g, '""')}"`;
27779
+ }
27780
+ if (arg.length === 0) return "''";
27781
+ return `'${arg.replace(/'/g, `'"'"'`)}'`;
27782
+ }
27572
27783
  function buildShellInvocation(command) {
27573
27784
  const shell = resolveUserShell();
27574
27785
  if ((0, import_os2.platform)() === "win32") return { file: shell, args: ["/c", command] };
@@ -27645,6 +27856,16 @@ async function probeLoginShellPath(opts = {}) {
27645
27856
  }
27646
27857
  return null;
27647
27858
  }
27859
+ var FIND_ON_PATH_TIMEOUT_MS = 3e3;
27860
+ function findOnPath(name, env) {
27861
+ const [file2, args] = (0, import_node_os3.platform)() === "win32" ? ["where", [name]] : ["/bin/sh", ["-c", `command -v ${quoteForShell(name)}`]];
27862
+ return new Promise((resolve9) => {
27863
+ (0, import_node_child_process2.execFile)(file2, args, { timeout: FIND_ON_PATH_TIMEOUT_MS, env }, (err, stdout2) => {
27864
+ const found = err ? "" : String(stdout2).trim().split("\n")[0]?.trim();
27865
+ resolve9(found ? found : null);
27866
+ });
27867
+ });
27868
+ }
27648
27869
 
27649
27870
  // ../../shared/platform/src/slz-file-url.ts
27650
27871
  var SLZ_FILE_HOST = "app";
@@ -27664,8 +27885,8 @@ async function adoptLoginShellPath(opts = {}) {
27664
27885
  }
27665
27886
 
27666
27887
  // src/main.ts
27667
- var import_node_fs11 = require("node:fs");
27668
- var import_node_path14 = require("node:path");
27888
+ var import_node_fs14 = require("node:fs");
27889
+ var import_node_path17 = require("node:path");
27669
27890
 
27670
27891
  // src/request-log.ts
27671
27892
  var import_node_fs4 = require("node:fs");
@@ -27969,6 +28190,155 @@ function createAgentHookServer(ctx) {
27969
28190
  });
27970
28191
  }
27971
28192
 
28193
+ // src/cli-relay.ts
28194
+ var import_node_http2 = __toESM(require("node:http"), 1);
28195
+ var import_node_crypto3 = require("node:crypto");
28196
+ function responseHeaders(headers) {
28197
+ const excluded = /* @__PURE__ */ new Set([
28198
+ "connection",
28199
+ "keep-alive",
28200
+ "proxy-authenticate",
28201
+ "proxy-authorization",
28202
+ "proxy-connection",
28203
+ "te",
28204
+ "trailer",
28205
+ "transfer-encoding",
28206
+ "upgrade",
28207
+ ...(headers.connection ?? "").split(",").map((name) => name.trim().toLowerCase())
28208
+ ]);
28209
+ return Object.fromEntries(Object.entries(headers).filter(([name]) => !excluded.has(name)));
28210
+ }
28211
+ async function createComputerCliRelay(requester) {
28212
+ const tokens = /* @__PURE__ */ new Map();
28213
+ const responses = /* @__PURE__ */ new Set();
28214
+ const active = /* @__PURE__ */ new Set();
28215
+ const server = import_node_http2.default.createServer((req, res) => {
28216
+ const token = req.headers.authorization?.replace(/^Bearer /, "") ?? "";
28217
+ const requests = tokens.get(token);
28218
+ if (!requests) {
28219
+ res.writeHead(401, { "content-type": "application/json" }).end(
28220
+ JSON.stringify({
28221
+ error: "CLI relay capability is missing or expired. Open a new task terminal."
28222
+ })
28223
+ );
28224
+ return;
28225
+ }
28226
+ if (responses.size >= 32) {
28227
+ res.writeHead(429).end("Too many CLI relay requests");
28228
+ return;
28229
+ }
28230
+ let upstream;
28231
+ let receivedResponse = false;
28232
+ let cancelled = false;
28233
+ responses.add(res);
28234
+ requests.add(res);
28235
+ res.on("close", () => {
28236
+ responses.delete(res);
28237
+ requests.delete(res);
28238
+ cancelled = true;
28239
+ upstream?.destroy();
28240
+ });
28241
+ void openHttpTunnel(requester).then((socket) => {
28242
+ if (cancelled || !tokens.has(token)) {
28243
+ socket.destroy();
28244
+ return;
28245
+ }
28246
+ const headers = { connection: "close" };
28247
+ for (const name of ["content-type", "content-length", "accept", "last-event-id"]) {
28248
+ if (req.headers[name] !== void 0) headers[name] = req.headers[name];
28249
+ }
28250
+ const agent = new import_node_http2.default.Agent({ keepAlive: false });
28251
+ agent.createConnection = () => socket;
28252
+ upstream = import_node_http2.default.request(
28253
+ {
28254
+ host: "relay",
28255
+ path: req.url,
28256
+ method: req.method,
28257
+ headers,
28258
+ agent
28259
+ },
28260
+ (response) => {
28261
+ receivedResponse = true;
28262
+ const stopUpload = () => {
28263
+ req.unpipe(upstream);
28264
+ req.resume();
28265
+ };
28266
+ if ((response.statusCode ?? 502) >= 400) stopUpload();
28267
+ response.on("end", stopUpload);
28268
+ res.writeHead(response.statusCode ?? 502, responseHeaders(response.headers));
28269
+ response.on("error", () => res.destroy());
28270
+ response.pipe(res);
28271
+ }
28272
+ );
28273
+ active.add(upstream);
28274
+ const current = upstream;
28275
+ upstream.on("close", () => {
28276
+ active.delete(current);
28277
+ agent.destroy();
28278
+ });
28279
+ upstream.on("error", () => {
28280
+ if (receivedResponse) return;
28281
+ if (res.headersSent) res.destroy();
28282
+ else
28283
+ res.writeHead(503, { "content-type": "application/json" }).end(
28284
+ JSON.stringify({
28285
+ error: "CLI relay lost its hub connection. Check the computer connection, then retry."
28286
+ })
28287
+ );
28288
+ });
28289
+ req.on("aborted", () => upstream?.destroy());
28290
+ req.pipe(upstream);
28291
+ }).catch(() => {
28292
+ if (!res.headersSent)
28293
+ res.writeHead(503, { "content-type": "application/json" }).end(
28294
+ JSON.stringify({
28295
+ error: "CLI relay unavailable. Check the computer connection and update hub and computer together."
28296
+ })
28297
+ );
28298
+ });
28299
+ });
28300
+ await new Promise((resolve9, reject) => {
28301
+ server.once("error", reject);
28302
+ server.listen(0, "127.0.0.1", resolve9);
28303
+ });
28304
+ const address = server.address();
28305
+ if (!address || typeof address === "string") throw new Error("CLI relay failed to bind");
28306
+ const disconnect = () => {
28307
+ for (const response of responses) {
28308
+ if (response.headersSent) response.destroy();
28309
+ else
28310
+ response.writeHead(503, { "content-type": "application/json" }).end(
28311
+ JSON.stringify({
28312
+ error: "CLI relay lost its hub connection. Check the computer connection, then retry."
28313
+ })
28314
+ );
28315
+ }
28316
+ for (const request of active) request.destroy(new Error("hub disconnected"));
28317
+ };
28318
+ return {
28319
+ url: `http://127.0.0.1:${address.port}`,
28320
+ issue() {
28321
+ const token = (0, import_node_crypto3.randomBytes)(32).toString("hex");
28322
+ const requests = /* @__PURE__ */ new Set();
28323
+ tokens.set(token, requests);
28324
+ return {
28325
+ token,
28326
+ revoke() {
28327
+ tokens.delete(token);
28328
+ for (const response of requests) response.destroy();
28329
+ }
28330
+ };
28331
+ },
28332
+ disconnect,
28333
+ async close() {
28334
+ tokens.clear();
28335
+ disconnect();
28336
+ server.closeAllConnections();
28337
+ await new Promise((resolve9) => server.close(() => resolve9()));
28338
+ }
28339
+ };
28340
+ }
28341
+
27972
28342
  // src/handlers/fs.ts
27973
28343
  var import_node_fs7 = require("node:fs");
27974
28344
  var import_promises7 = require("node:fs/promises");
@@ -28465,9 +28835,9 @@ var WORKSPACE_MAX_SEARCH_RESULTS = 2e3;
28465
28835
  function createLocalWorkspaceFs() {
28466
28836
  return refuseOnSwitchedOffHub(createUnguardedLocalWorkspaceFs());
28467
28837
  }
28468
- function refuseOnSwitchedOffHub(fs7) {
28838
+ function refuseOnSwitchedOffHub(fs8) {
28469
28839
  const guarded = {};
28470
- for (const [op, fn] of Object.entries(fs7)) {
28840
+ for (const [op, fn] of Object.entries(fs8)) {
28471
28841
  guarded[op] = (...args) => {
28472
28842
  assertHubLocalExecEnabled({ op: `fs.${op}` });
28473
28843
  return fn(...args);
@@ -28492,8 +28862,8 @@ function createUnguardedLocalWorkspaceFs() {
28492
28862
  sep: import_node_path9.sep
28493
28863
  };
28494
28864
  },
28495
- async listDir(path9, options) {
28496
- const abs = import_node_fs6.realpathSync.native((0, import_node_path9.resolve)(path9));
28865
+ async listDir(path11, options) {
28866
+ const abs = import_node_fs6.realpathSync.native((0, import_node_path9.resolve)(path11));
28497
28867
  const dirents = await (0, import_promises6.readdir)(abs, { withFileTypes: true });
28498
28868
  const entries = [];
28499
28869
  for (const e of dirents) {
@@ -28525,13 +28895,13 @@ function createUnguardedLocalWorkspaceFs() {
28525
28895
  const parent = (0, import_node_path9.dirname)(abs);
28526
28896
  return { path: abs, parent: parent === abs ? null : parent, entries };
28527
28897
  },
28528
- async mkdir(path9) {
28529
- const abs = (0, import_node_path9.resolve)(path9);
28898
+ async mkdir(path11) {
28899
+ const abs = (0, import_node_path9.resolve)(path11);
28530
28900
  await (0, import_promises6.mkdir)(abs, { recursive: true });
28531
28901
  return { path: import_node_fs6.realpathSync.native(abs) };
28532
28902
  },
28533
- pathExists(path9) {
28534
- return (0, import_node_fs6.existsSync)((0, import_node_path9.resolve)(path9));
28903
+ pathExists(path11) {
28904
+ return (0, import_node_fs6.existsSync)((0, import_node_path9.resolve)(path11));
28535
28905
  },
28536
28906
  // A hub has no path-jail — `allowedRoots` is a computer concept. Saying so is
28537
28907
  // better than accepting the edit and discarding it, which would leave the
@@ -28546,12 +28916,12 @@ function createUnguardedLocalWorkspaceFs() {
28546
28916
  // the fork sidecar and tests, neither of which should be minting a new
28547
28917
  // persistent state file for a mapping a real computer owns.
28548
28918
  resolveProjectPath(projectId) {
28549
- const path9 = localProjectPaths.get(projectId);
28550
- if (!path9) return { path: null };
28551
- return { path: path9, exists: (0, import_node_fs6.existsSync)(path9) };
28919
+ const path11 = localProjectPaths.get(projectId);
28920
+ if (!path11) return { path: null };
28921
+ return { path: path11, exists: (0, import_node_fs6.existsSync)(path11) };
28552
28922
  },
28553
- setProjectPath(projectId, path9) {
28554
- const abs = import_node_fs6.realpathSync.native((0, import_node_path9.resolve)(path9));
28923
+ setProjectPath(projectId, path11) {
28924
+ const abs = import_node_fs6.realpathSync.native((0, import_node_path9.resolve)(path11));
28555
28925
  localProjectPaths.set(projectId, abs);
28556
28926
  return { path: abs };
28557
28927
  },
@@ -28591,12 +28961,12 @@ function createUnguardedLocalWorkspaceFs() {
28591
28961
  function createLocalWorkspaceFsAdapters(local = createLocalWorkspaceFs()) {
28592
28962
  return {
28593
28963
  listRoots: async () => local.listRoots(),
28594
- listDir: async (_r, path9, options) => local.listDir(path9, options),
28595
- mkdir: async (_r, path9) => local.mkdir(path9),
28596
- pathExists: async (_r, path9) => local.pathExists(path9),
28964
+ listDir: async (_r, path11, options) => local.listDir(path11, options),
28965
+ mkdir: async (_r, path11) => local.mkdir(path11),
28966
+ pathExists: async (_r, path11) => local.pathExists(path11),
28597
28967
  setAllowedRoots: async (_r, roots) => local.setAllowedRoots(roots),
28598
28968
  resolveProjectPath: async (_r, projectId) => local.resolveProjectPath(projectId),
28599
- setProjectPath: async (_r, projectId, path9) => local.setProjectPath(projectId, path9),
28969
+ setProjectPath: async (_r, projectId, path11) => local.setProjectPath(projectId, path11),
28600
28970
  forgetProjectPath: async (_r, projectId) => local.forgetProjectPath(projectId),
28601
28971
  readDir: async (_r, rootPath, dirPath) => local.readDir(rootPath, dirPath),
28602
28972
  readFile: async (_r, rootPath, filePath, force) => local.readFile(rootPath, filePath, force),
@@ -28686,28 +29056,28 @@ var noop = () => {
28686
29056
  var freezeIfAvailable = (obj) => {
28687
29057
  if (Object.freeze) Object.freeze(obj);
28688
29058
  };
28689
- function createInnerProxy(callback, path9, memo2) {
29059
+ function createInnerProxy(callback, path11, memo2) {
28690
29060
  var _memo$cacheKey;
28691
- const cacheKey = path9.join(".");
29061
+ const cacheKey = path11.join(".");
28692
29062
  (_memo$cacheKey = memo2[cacheKey]) !== null && _memo$cacheKey !== void 0 || (memo2[cacheKey] = new Proxy(noop, {
28693
29063
  get(_obj, key) {
28694
29064
  if (typeof key !== "string" || key === "then") return;
28695
- return createInnerProxy(callback, [...path9, key], memo2);
29065
+ return createInnerProxy(callback, [...path11, key], memo2);
28696
29066
  },
28697
29067
  apply(_1, _2, args) {
28698
- const lastOfPath = path9[path9.length - 1];
28699
- if (lastOfPath === "valueOf" || lastOfPath === "toString" || lastOfPath === "toJSON") return `tRPC.proxy(${path9.slice(0, -1).join(".")})`;
29068
+ const lastOfPath = path11[path11.length - 1];
29069
+ if (lastOfPath === "valueOf" || lastOfPath === "toString" || lastOfPath === "toJSON") return `tRPC.proxy(${path11.slice(0, -1).join(".")})`;
28700
29070
  let opts = {
28701
29071
  args,
28702
- path: path9
29072
+ path: path11
28703
29073
  };
28704
29074
  if (lastOfPath === "call") opts = {
28705
29075
  args: args.length >= 2 ? [args[1]] : [],
28706
- path: path9.slice(0, -1)
29076
+ path: path11.slice(0, -1)
28707
29077
  };
28708
29078
  else if (lastOfPath === "apply") opts = {
28709
29079
  args: args.length >= 2 ? args[1] : [],
28710
- path: path9.slice(0, -1)
29080
+ path: path11.slice(0, -1)
28711
29081
  };
28712
29082
  freezeIfAvailable(opts.args);
28713
29083
  freezeIfAvailable(opts.path);
@@ -28886,12 +29256,12 @@ function createRouterFactory(config2) {
28886
29256
  })
28887
29257
  };
28888
29258
  }
28889
- function step(from, path9 = []) {
29259
+ function step(from, path11 = []) {
28890
29260
  const aggregate = emptyObject();
28891
29261
  for (const [key, item] of Object.entries(from !== null && from !== void 0 ? from : {})) {
28892
29262
  if (isLazy(item)) {
28893
- lazy3[[...path9, key].join(".")] = createLazyLoader({
28894
- path: path9,
29263
+ lazy3[[...path11, key].join(".")] = createLazyLoader({
29264
+ path: path11,
28895
29265
  ref: item,
28896
29266
  key,
28897
29267
  aggregate
@@ -28899,14 +29269,14 @@ function createRouterFactory(config2) {
28899
29269
  continue;
28900
29270
  }
28901
29271
  if (isRouter(item)) {
28902
- aggregate[key] = step(item._def.record, [...path9, key]);
29272
+ aggregate[key] = step(item._def.record, [...path11, key]);
28903
29273
  continue;
28904
29274
  }
28905
29275
  if (!isProcedure(item)) {
28906
- aggregate[key] = step(item, [...path9, key]);
29276
+ aggregate[key] = step(item, [...path11, key]);
28907
29277
  continue;
28908
29278
  }
28909
- const newPath = [...path9, key].join(".");
29279
+ const newPath = [...path11, key].join(".");
28910
29280
  if (procedures[newPath]) throw new Error(`Duplicate key: ${newPath}`);
28911
29281
  procedures[newPath] = item;
28912
29282
  aggregate[key] = item;
@@ -28930,14 +29300,14 @@ function createRouterFactory(config2) {
28930
29300
  function isProcedure(procedureOrRouter) {
28931
29301
  return typeof procedureOrRouter === "function";
28932
29302
  }
28933
- async function getProcedureAtPath(router2, path9) {
29303
+ async function getProcedureAtPath(router2, path11) {
28934
29304
  const { _def } = router2;
28935
- let procedure = _def.procedures[path9];
29305
+ let procedure = _def.procedures[path11];
28936
29306
  while (!procedure) {
28937
- const key = Object.keys(_def.lazy).find((key2) => path9 === key2 || path9.startsWith(key2 + "."));
29307
+ const key = Object.keys(_def.lazy).find((key2) => path11 === key2 || path11.startsWith(key2 + "."));
28938
29308
  if (!key) return null;
28939
29309
  await _def.lazy[key].load();
28940
- procedure = _def.procedures[path9];
29310
+ procedure = _def.procedures[path11];
28941
29311
  }
28942
29312
  return procedure;
28943
29313
  }
@@ -28946,15 +29316,15 @@ function createCallerFactory() {
28946
29316
  const { _def } = router2;
28947
29317
  return function createCaller(ctxOrCallback, opts) {
28948
29318
  return createRecursiveProxy(async (innerOpts) => {
28949
- const { path: path9, args } = innerOpts;
28950
- const fullPath = path9.join(".");
28951
- if (path9.length === 1 && path9[0] === "_def") return _def;
29319
+ const { path: path11, args } = innerOpts;
29320
+ const fullPath = path11.join(".");
29321
+ if (path11.length === 1 && path11[0] === "_def") return _def;
28952
29322
  const procedure = await getProcedureAtPath(router2, fullPath);
28953
29323
  let ctx = void 0;
28954
29324
  try {
28955
29325
  if (!procedure) throw new TRPCError({
28956
29326
  code: "NOT_FOUND",
28957
- message: `No procedure found on path "${path9}"`
29327
+ message: `No procedure found on path "${path11}"`
28958
29328
  });
28959
29329
  ctx = isFunction(ctxOrCallback) ? await Promise.resolve(ctxOrCallback()) : ctxOrCallback;
28960
29330
  return await procedure({
@@ -29558,7 +29928,7 @@ var isURL = (payload) => payload instanceof URL;
29558
29928
 
29559
29929
  // ../../../node_modules/superjson/dist/pathstringifier.js
29560
29930
  var escapeKey = (key) => key.replace(/\\/g, "\\\\").replace(/\./g, "\\.");
29561
- var stringifyPath = (path9) => path9.map(String).map(escapeKey).join(".");
29931
+ var stringifyPath = (path11) => path11.map(String).map(escapeKey).join(".");
29562
29932
  var parsePath = (string4, legacyPaths) => {
29563
29933
  const result = [];
29564
29934
  let segment = "";
@@ -29805,26 +30175,26 @@ var getNthKey = (value, n) => {
29805
30175
  }
29806
30176
  return keys.next().value;
29807
30177
  };
29808
- function validatePath(path9) {
29809
- if (includes(path9, "__proto__")) {
30178
+ function validatePath(path11) {
30179
+ if (includes(path11, "__proto__")) {
29810
30180
  throw new Error("__proto__ is not allowed as a property");
29811
30181
  }
29812
- if (includes(path9, "prototype")) {
30182
+ if (includes(path11, "prototype")) {
29813
30183
  throw new Error("prototype is not allowed as a property");
29814
30184
  }
29815
- if (includes(path9, "constructor")) {
30185
+ if (includes(path11, "constructor")) {
29816
30186
  throw new Error("constructor is not allowed as a property");
29817
30187
  }
29818
30188
  }
29819
- var getDeep = (object2, path9) => {
29820
- validatePath(path9);
29821
- for (let i = 0; i < path9.length; i++) {
29822
- const key = path9[i];
30189
+ var getDeep = (object2, path11) => {
30190
+ validatePath(path11);
30191
+ for (let i = 0; i < path11.length; i++) {
30192
+ const key = path11[i];
29823
30193
  if (isSet(object2)) {
29824
30194
  object2 = getNthKey(object2, +key);
29825
30195
  } else if (isMap(object2)) {
29826
30196
  const row = +key;
29827
- const type = +path9[++i] === 0 ? "key" : "value";
30197
+ const type = +path11[++i] === 0 ? "key" : "value";
29828
30198
  const keyOfRow = getNthKey(object2, row);
29829
30199
  switch (type) {
29830
30200
  case "key":
@@ -29840,14 +30210,14 @@ var getDeep = (object2, path9) => {
29840
30210
  }
29841
30211
  return object2;
29842
30212
  };
29843
- var setDeep = (object2, path9, mapper) => {
29844
- validatePath(path9);
29845
- if (path9.length === 0) {
30213
+ var setDeep = (object2, path11, mapper) => {
30214
+ validatePath(path11);
30215
+ if (path11.length === 0) {
29846
30216
  return mapper(object2);
29847
30217
  }
29848
30218
  let parent = object2;
29849
- for (let i = 0; i < path9.length - 1; i++) {
29850
- const key = path9[i];
30219
+ for (let i = 0; i < path11.length - 1; i++) {
30220
+ const key = path11[i];
29851
30221
  if (isArray(parent)) {
29852
30222
  const index = +key;
29853
30223
  parent = parent[index];
@@ -29857,12 +30227,12 @@ var setDeep = (object2, path9, mapper) => {
29857
30227
  const row = +key;
29858
30228
  parent = getNthKey(parent, row);
29859
30229
  } else if (isMap(parent)) {
29860
- const isEnd = i === path9.length - 2;
30230
+ const isEnd = i === path11.length - 2;
29861
30231
  if (isEnd) {
29862
30232
  break;
29863
30233
  }
29864
30234
  const row = +key;
29865
- const type = +path9[++i] === 0 ? "key" : "value";
30235
+ const type = +path11[++i] === 0 ? "key" : "value";
29866
30236
  const keyOfRow = getNthKey(parent, row);
29867
30237
  switch (type) {
29868
30238
  case "key":
@@ -29874,7 +30244,7 @@ var setDeep = (object2, path9, mapper) => {
29874
30244
  }
29875
30245
  }
29876
30246
  }
29877
- const lastKey = path9[path9.length - 1];
30247
+ const lastKey = path11[path11.length - 1];
29878
30248
  if (isArray(parent)) {
29879
30249
  parent[+lastKey] = mapper(parent[+lastKey]);
29880
30250
  } else if (isPlainObject2(parent)) {
@@ -29889,7 +30259,7 @@ var setDeep = (object2, path9, mapper) => {
29889
30259
  }
29890
30260
  }
29891
30261
  if (isMap(parent)) {
29892
- const row = +path9[path9.length - 2];
30262
+ const row = +path11[path11.length - 2];
29893
30263
  const keyToRow = getNthKey(parent, row);
29894
30264
  const type = +lastKey === 0 ? "key" : "value";
29895
30265
  switch (type) {
@@ -29936,16 +30306,16 @@ function traverse(tree, walker2, version2, origin = []) {
29936
30306
  walker2(nodeValue, origin);
29937
30307
  }
29938
30308
  function applyValueAnnotations(plain, annotations, version2, superJson) {
29939
- traverse(annotations, (type, path9) => {
29940
- plain = setDeep(plain, path9, (v) => untransformValue(v, type, superJson));
30309
+ traverse(annotations, (type, path11) => {
30310
+ plain = setDeep(plain, path11, (v) => untransformValue(v, type, superJson));
29941
30311
  }, version2);
29942
30312
  return plain;
29943
30313
  }
29944
30314
  function applyReferentialEqualityAnnotations(plain, annotations, version2) {
29945
30315
  const legacyPaths = enableLegacyPaths(version2);
29946
- function apply(identicalPaths, path9) {
29947
- const object2 = getDeep(plain, parsePath(path9, legacyPaths));
29948
- identicalPaths.map((path10) => parsePath(path10, legacyPaths)).forEach((identicalObjectPath) => {
30316
+ function apply(identicalPaths, path11) {
30317
+ const object2 = getDeep(plain, parsePath(path11, legacyPaths));
30318
+ identicalPaths.map((path12) => parsePath(path12, legacyPaths)).forEach((identicalObjectPath) => {
29949
30319
  plain = setDeep(plain, identicalObjectPath, () => object2);
29950
30320
  });
29951
30321
  }
@@ -29963,12 +30333,12 @@ function applyReferentialEqualityAnnotations(plain, annotations, version2) {
29963
30333
  return plain;
29964
30334
  }
29965
30335
  var isDeep = (object2, superJson) => isPlainObject2(object2) || isArray(object2) || isMap(object2) || isSet(object2) || isError(object2) || isInstanceOfRegisteredClass(object2, superJson);
29966
- function addIdentity(object2, path9, identities) {
30336
+ function addIdentity(object2, path11, identities) {
29967
30337
  const existingSet = identities.get(object2);
29968
30338
  if (existingSet) {
29969
- existingSet.push(path9);
30339
+ existingSet.push(path11);
29970
30340
  } else {
29971
- identities.set(object2, [path9]);
30341
+ identities.set(object2, [path11]);
29972
30342
  }
29973
30343
  }
29974
30344
  function generateReferentialEqualityAnnotations(identitites, dedupe) {
@@ -29979,7 +30349,7 @@ function generateReferentialEqualityAnnotations(identitites, dedupe) {
29979
30349
  return;
29980
30350
  }
29981
30351
  if (!dedupe) {
29982
- paths = paths.map((path9) => path9.map(String)).sort((a, b) => a.length - b.length);
30352
+ paths = paths.map((path11) => path11.map(String)).sort((a, b) => a.length - b.length);
29983
30353
  }
29984
30354
  const [representativePath, ...identicalPaths] = paths;
29985
30355
  if (representativePath.length === 0) {
@@ -29998,10 +30368,10 @@ function generateReferentialEqualityAnnotations(identitites, dedupe) {
29998
30368
  return isEmptyObject(result) ? void 0 : result;
29999
30369
  }
30000
30370
  }
30001
- var walker = (object2, identities, superJson, dedupe, path9 = [], objectsInThisPath = [], seenObjects = /* @__PURE__ */ new Map()) => {
30371
+ var walker = (object2, identities, superJson, dedupe, path11 = [], objectsInThisPath = [], seenObjects = /* @__PURE__ */ new Map()) => {
30002
30372
  const primitive = isPrimitive(object2);
30003
30373
  if (!primitive) {
30004
- addIdentity(object2, path9, identities);
30374
+ addIdentity(object2, path11, identities);
30005
30375
  const seen = seenObjects.get(object2);
30006
30376
  if (seen) {
30007
30377
  return dedupe ? {
@@ -30035,7 +30405,7 @@ var walker = (object2, identities, superJson, dedupe, path9 = [], objectsInThisP
30035
30405
  if (index === "__proto__" || index === "constructor" || index === "prototype") {
30036
30406
  throw new Error(`Detected property ${index}. This is a prototype pollution risk, please remove it from your object.`);
30037
30407
  }
30038
- const recursiveResult = walker(value, identities, superJson, dedupe, [...path9, index], [...objectsInThisPath, object2], seenObjects);
30408
+ const recursiveResult = walker(value, identities, superJson, dedupe, [...path11, index], [...objectsInThisPath, object2], seenObjects);
30039
30409
  transformedValue[index] = recursiveResult.transformedValue;
30040
30410
  if (isArray(recursiveResult.annotations)) {
30041
30411
  innerAnnotations[escapeKey(index)] = recursiveResult.annotations;
@@ -30281,12 +30651,12 @@ function auditAdminDenied(opts) {
30281
30651
  detail: { reason: "not_admin", code: ADMIN_ONLY, ...opts.detail }
30282
30652
  });
30283
30653
  }
30284
- function adminOnlyTrpcError(path9) {
30285
- auditAdminDenied({ targetKind: "procedure", targetId: path9 });
30654
+ function adminOnlyTrpcError(path11) {
30655
+ auditAdminDenied({ targetKind: "procedure", targetId: path11 });
30286
30656
  return new TRPCError({
30287
30657
  code: "FORBIDDEN",
30288
- message: `${path9} needs a hub admin account`,
30289
- cause: new AdminOnlyError(path9)
30658
+ message: `${path11} needs a hub admin account`,
30659
+ cause: new AdminOnlyError(path11)
30290
30660
  });
30291
30661
  }
30292
30662
 
@@ -30339,45 +30709,45 @@ var router = t.router;
30339
30709
  var middleware = t.middleware;
30340
30710
  var mergeRouters2 = t.mergeRouters;
30341
30711
  var securityContext = t.middleware(
30342
- ({ ctx, next, path: path9 }) => runWithSecurityContext(
30343
- { actor: ctx.principal?.userId ?? null, ip: peerAddress(ctx.req), route: `trpc ${path9}` },
30712
+ ({ ctx, next, path: path11 }) => runWithSecurityContext(
30713
+ { actor: ctx.principal?.userId ?? null, ip: peerAddress(ctx.req), route: `trpc ${path11}` },
30344
30714
  () => next()
30345
30715
  )
30346
30716
  );
30347
- function auditDenied(path9, detail) {
30717
+ function auditDenied(path11, detail) {
30348
30718
  securityEvent({
30349
30719
  event: "authz.denied",
30350
30720
  outcome: "denied",
30351
30721
  targetKind: "procedure",
30352
- targetId: path9,
30722
+ targetId: path11,
30353
30723
  detail
30354
30724
  });
30355
30725
  }
30356
- var authGate2 = t.middleware(({ ctx, next, path: path9 }) => {
30726
+ var authGate2 = t.middleware(({ ctx, next, path: path11 }) => {
30357
30727
  if (getAuthGate() && !ctx.principal) {
30358
- auditDenied(path9, { reason: "unauthenticated" });
30728
+ auditDenied(path11, { reason: "unauthenticated" });
30359
30729
  throw new TRPCError({ code: "UNAUTHORIZED", message: "hub requires authentication" });
30360
30730
  }
30361
30731
  return next();
30362
30732
  });
30363
- var scopeGate = t.middleware(({ ctx, next, path: path9 }) => {
30733
+ var scopeGate = t.middleware(({ ctx, next, path: path11 }) => {
30364
30734
  if (!getAuthGate()) return next();
30365
30735
  const scopes = ctx.principal?.scopes ?? [];
30366
- if (!procedurePolicy().scopeAllows(scopes, path9)) {
30367
- auditDenied(path9, { reason: "scope", scopes });
30368
- throw new TRPCError({ code: "FORBIDDEN", message: `insufficient scope for ${path9}` });
30736
+ if (!procedurePolicy().scopeAllows(scopes, path11)) {
30737
+ auditDenied(path11, { reason: "scope", scopes });
30738
+ throw new TRPCError({ code: "FORBIDDEN", message: `insufficient scope for ${path11}` });
30369
30739
  }
30370
30740
  return next();
30371
30741
  });
30372
- var adminGate = t.middleware(({ ctx, next, path: path9 }) => {
30742
+ var adminGate = t.middleware(({ ctx, next, path: path11 }) => {
30373
30743
  if (!getAuthGate()) return next();
30374
- if (!roleAllows(procedurePolicy().adminClassOf(path9), ctx.principal?.role ?? "user")) {
30375
- throw adminOnlyTrpcError(path9);
30744
+ if (!roleAllows(procedurePolicy().adminClassOf(path11), ctx.principal?.role ?? "user")) {
30745
+ throw adminOnlyTrpcError(path11);
30376
30746
  }
30377
30747
  return next();
30378
30748
  });
30379
- var ownerGate = t.middleware(async ({ ctx, next, path: path9, type, getRawInput }) => {
30380
- const filter = getAuthGate() ? await procedurePolicy().ownerCheck({ ctx, path: path9, type, getRawInput }) : null;
30749
+ var ownerGate = t.middleware(async ({ ctx, next, path: path11, type, getRawInput }) => {
30750
+ const filter = getAuthGate() ? await procedurePolicy().ownerCheck({ ctx, path: path11, type, getRawInput }) : null;
30381
30751
  const result = await next();
30382
30752
  if (!filter || !result.ok) return result;
30383
30753
  return { ...result, data: await filter(result.data) };
@@ -30405,8 +30775,8 @@ var unsafePathRefusal = t.middleware(async ({ next }) => {
30405
30775
  return result;
30406
30776
  });
30407
30777
  var publicProcedure = t.procedure.use(securityContext).use(authGate2).use(adminGate).use(scopeGate).use(ownerGate).use(switchRefusal).use(unsafePathRefusal);
30408
- var hubLocalProcedure = publicProcedure.use(({ next, path: path9 }) => {
30409
- assertHubLocalExecEnabled({ op: path9 });
30778
+ var hubLocalProcedure = publicProcedure.use(({ next, path: path11 }) => {
30779
+ assertHubLocalExecEnabled({ op: path11 });
30410
30780
  return next();
30411
30781
  });
30412
30782
  var openProcedure = t.procedure.use(securityContext);
@@ -30496,8 +30866,8 @@ var fileEditorRouter = router({
30496
30866
  * should only offer this when the workspace is co-resident — the caller decides.
30497
30867
  */
30498
30868
  showInFinder: publicProcedure.input(external_exports.object({ rootPath: external_exports.string(), targetPath: external_exports.string() })).mutation(async ({ input: input2 }) => {
30499
- const path9 = await import("node:path");
30500
- const abs = input2.targetPath ? path9.resolve(input2.rootPath, input2.targetPath) : path9.resolve(input2.rootPath);
30869
+ const path11 = await import("node:path");
30870
+ const abs = input2.targetPath ? path11.resolve(input2.rootPath, input2.targetPath) : path11.resolve(input2.rootPath);
30501
30871
  getFileEditorRouterDeps().shellShowItemInFolder(abs);
30502
30872
  }),
30503
30873
  /**
@@ -30549,9 +30919,9 @@ var workspaceRouter = router({
30549
30919
  * surfaced as such rather than rendered as an empty directory.
30550
30920
  */
30551
30921
  listRoots: publicProcedure.input(targetInput).query(async ({ input: input2 }) => {
30552
- const { fs: fs7, resolveComputer, isLocalComputer } = getWorkspaceDeps();
30922
+ const { fs: fs8, resolveComputer, isLocalComputer } = getWorkspaceDeps();
30553
30923
  const computerId = await resolveComputer(input2);
30554
- return { computerId, isLocal: isLocalComputer(computerId), ...await fs7.listRoots(computerId) };
30924
+ return { computerId, isLocal: isLocalComputer(computerId), ...await fs8.listRoots(computerId) };
30555
30925
  }),
30556
30926
  listDir: publicProcedure.input(
30557
30927
  targetInput.extend({
@@ -30561,18 +30931,18 @@ var workspaceRouter = router({
30561
30931
  includeHidden: external_exports.boolean().optional()
30562
30932
  })
30563
30933
  ).query(async ({ input: input2 }) => {
30564
- const { fs: fs7, resolveComputer } = getWorkspaceDeps();
30934
+ const { fs: fs8, resolveComputer } = getWorkspaceDeps();
30565
30935
  const computerId = await resolveComputer(input2);
30566
- const res = await fs7.listDir(computerId, input2.path, {
30936
+ const res = await fs8.listDir(computerId, input2.path, {
30567
30937
  dirsOnly: input2.dirsOnly,
30568
30938
  includeHidden: input2.includeHidden
30569
30939
  });
30570
30940
  return { computerId, ...res };
30571
30941
  }),
30572
30942
  mkdir: publicProcedure.input(targetInput.extend({ path: external_exports.string().min(1) })).mutation(async ({ input: input2 }) => {
30573
- const { fs: fs7, resolveComputer } = getWorkspaceDeps();
30943
+ const { fs: fs8, resolveComputer } = getWorkspaceDeps();
30574
30944
  const computerId = await resolveComputer(input2);
30575
- const res = await fs7.mkdir(computerId, input2.path);
30945
+ const res = await fs8.mkdir(computerId, input2.path);
30576
30946
  return { computerId, ...res };
30577
30947
  }),
30578
30948
  /**
@@ -30588,9 +30958,9 @@ var workspaceRouter = router({
30588
30958
  * from the enforced one.
30589
30959
  */
30590
30960
  setAllowedRoots: publicProcedure.input(targetInput.extend({ roots: external_exports.array(external_exports.string().min(1)) })).mutation(async ({ input: input2 }) => {
30591
- const { fs: fs7, resolveComputer } = getWorkspaceDeps();
30961
+ const { fs: fs8, resolveComputer } = getWorkspaceDeps();
30592
30962
  const computerId = await resolveComputer(input2);
30593
- return { computerId, ...await fs7.setAllowedRoots(computerId, input2.roots) };
30963
+ return { computerId, ...await fs8.setAllowedRoots(computerId, input2.roots) };
30594
30964
  }),
30595
30965
  /**
30596
30966
  * Where does the target machine keep this project?
@@ -30601,13 +30971,13 @@ var workspaceRouter = router({
30601
30971
  * is different again: recorded, but the folder is gone.
30602
30972
  */
30603
30973
  resolveProjectPath: publicProcedure.input(targetInput.extend({ projectId: external_exports.string().min(1) })).query(async ({ input: input2 }) => {
30604
- const { fs: fs7, resolveComputer } = getWorkspaceDeps();
30974
+ const { fs: fs8, resolveComputer } = getWorkspaceDeps();
30605
30975
  const computerId = await resolveComputer(input2);
30606
30976
  try {
30607
30977
  return {
30608
30978
  computerId,
30609
30979
  reachable: true,
30610
- ...await fs7.resolveProjectPath(computerId, input2.projectId)
30980
+ ...await fs8.resolveProjectPath(computerId, input2.projectId)
30611
30981
  };
30612
30982
  } catch (err) {
30613
30983
  return {
@@ -30626,18 +30996,18 @@ var workspaceRouter = router({
30626
30996
  path: external_exports.string().min(1)
30627
30997
  })
30628
30998
  ).mutation(async ({ input: input2 }) => {
30629
- const { fs: fs7, resolveComputer } = getWorkspaceDeps();
30999
+ const { fs: fs8, resolveComputer } = getWorkspaceDeps();
30630
31000
  const computerId = await resolveComputer(input2);
30631
31001
  return {
30632
31002
  computerId,
30633
- ...await fs7.setProjectPath(computerId, input2.projectId, input2.path)
31003
+ ...await fs8.setProjectPath(computerId, input2.projectId, input2.path)
30634
31004
  };
30635
31005
  }),
30636
31006
  /** Drop the mapping on that machine. The checkout itself is left alone. */
30637
31007
  forgetProjectPath: publicProcedure.input(targetInput.extend({ projectId: external_exports.string().min(1) })).mutation(async ({ input: input2 }) => {
30638
- const { fs: fs7, resolveComputer } = getWorkspaceDeps();
31008
+ const { fs: fs8, resolveComputer } = getWorkspaceDeps();
30639
31009
  const computerId = await resolveComputer(input2);
30640
- await fs7.forgetProjectPath(computerId, input2.projectId);
31010
+ await fs8.forgetProjectPath(computerId, input2.projectId);
30641
31011
  return { computerId, ok: true };
30642
31012
  }),
30643
31013
  /**
@@ -30649,10 +31019,10 @@ var workspaceRouter = router({
30649
31019
  * "No repository path configured" for a path that was fine.
30650
31020
  */
30651
31021
  pathExists: publicProcedure.input(targetInput.extend({ path: external_exports.string().min(1) })).query(async ({ input: input2 }) => {
30652
- const { fs: fs7, resolveComputer } = getWorkspaceDeps();
31022
+ const { fs: fs8, resolveComputer } = getWorkspaceDeps();
30653
31023
  const computerId = await resolveComputer(input2);
30654
31024
  try {
30655
- return { computerId, exists: await fs7.pathExists(computerId, input2.path), reachable: true };
31025
+ return { computerId, exists: await fs8.pathExists(computerId, input2.path), reachable: true };
30656
31026
  } catch (err) {
30657
31027
  return {
30658
31028
  computerId,
@@ -30676,13 +31046,13 @@ function createFsHandlers(ctx) {
30676
31046
  };
30677
31047
  const watches = /* @__PURE__ */ new Map();
30678
31048
  function pathExists(rawParams) {
30679
- const { path: path9 } = fsPathExistsParamsSchema.parse(rawParams);
30680
- const resolved = allow(path9);
31049
+ const { path: path11 } = fsPathExistsParamsSchema.parse(rawParams);
31050
+ const resolved = allow(path11);
30681
31051
  return { exists: (0, import_node_fs7.existsSync)(resolved) };
30682
31052
  }
30683
31053
  async function removeDir(rawParams) {
30684
- const { path: path9 } = fsRemoveDirParamsSchema.parse(rawParams);
30685
- const resolved = allow(path9, true);
31054
+ const { path: path11 } = fsRemoveDirParamsSchema.parse(rawParams);
31055
+ const resolved = allow(path11, true);
30686
31056
  await (0, import_promises7.rm)(resolved, { recursive: true, force: true });
30687
31057
  ctx.log("fs removeDir", { path: resolved });
30688
31058
  return { ok: true };
@@ -30745,8 +31115,8 @@ function createFsHandlers(ctx) {
30745
31115
  return { path: abs, parent: isRoot ? null : (0, import_node_path10.dirname)(abs), entries };
30746
31116
  }
30747
31117
  async function mkdirHandler(rawParams) {
30748
- const { path: path9 } = fsMkdirParamsSchema.parse(rawParams);
30749
- const abs = allow(path9);
31118
+ const { path: path11 } = fsMkdirParamsSchema.parse(rawParams);
31119
+ const abs = allow(path11);
30750
31120
  await (0, import_promises7.mkdir)(abs, { recursive: true });
30751
31121
  ctx.log("fs mkdir", { path: abs });
30752
31122
  return { path: import_node_fs7.realpathSync.native(abs) };
@@ -30906,11 +31276,132 @@ function createGhHandlers(ctx) {
30906
31276
  return { [HubToComputerMethods.ghExec]: ghExec };
30907
31277
  }
30908
31278
 
31279
+ // src/handlers/claude-session.ts
31280
+ var import_node_fs8 = require("node:fs");
31281
+ var import_node_os7 = require("node:os");
31282
+ var import_node_path11 = require("node:path");
31283
+ var import_node_readline = require("node:readline");
31284
+ var LARGE_RECORD_BYTES = 32 * 1024;
31285
+ var KEEP_TOOL_TEXT = 8 * 1024;
31286
+ function encodeProjectDir(cwd) {
31287
+ return cwd.replace(/[^a-zA-Z0-9]/g, "-");
31288
+ }
31289
+ function claudeConfigDir() {
31290
+ return process.env.CLAUDE_CONFIG_DIR || (0, import_node_path11.join)((0, import_node_os7.homedir)(), ".claude");
31291
+ }
31292
+ function sessionFile(cwd, sessionId) {
31293
+ const candidates = /* @__PURE__ */ new Set();
31294
+ try {
31295
+ candidates.add((0, import_node_fs8.realpathSync)(cwd));
31296
+ } catch {
31297
+ }
31298
+ candidates.add(cwd);
31299
+ for (const dir of candidates) {
31300
+ const file2 = (0, import_node_path11.join)(claudeConfigDir(), "projects", encodeProjectDir(dir), `${sessionId}.jsonl`);
31301
+ if ((0, import_node_fs8.existsSync)(file2)) return file2;
31302
+ }
31303
+ return null;
31304
+ }
31305
+ async function* records(file2) {
31306
+ const input2 = (0, import_node_fs8.createReadStream)(file2, { encoding: "utf8" });
31307
+ const rl = (0, import_node_readline.createInterface)({ input: input2, crlfDelay: Infinity });
31308
+ try {
31309
+ for await (const line of rl) {
31310
+ const trimmed = line.trim();
31311
+ if (!trimmed) continue;
31312
+ try {
31313
+ const rec = JSON.parse(trimmed);
31314
+ if (rec && typeof rec === "object" && !Array.isArray(rec)) {
31315
+ yield rec;
31316
+ }
31317
+ } catch {
31318
+ }
31319
+ }
31320
+ } finally {
31321
+ rl.close();
31322
+ input2.destroy();
31323
+ }
31324
+ }
31325
+ function isContent(rec) {
31326
+ if (rec.isSidechain === true) return false;
31327
+ if (rec.type === "user" || rec.type === "assistant") return true;
31328
+ return rec.type === "system" && rec.subtype === "compact_boundary";
31329
+ }
31330
+ function shortened(rec) {
31331
+ if (JSON.stringify(rec).length <= LARGE_RECORD_BYTES) return rec;
31332
+ const message = rec.message;
31333
+ if (!message || !Array.isArray(message.content)) return rec;
31334
+ const cut = (text) => text.length > KEEP_TOOL_TEXT ? `${text.slice(0, KEEP_TOOL_TEXT)}
31335
+ \u2026 (shortened)` : text;
31336
+ const content = message.content.map((block) => {
31337
+ const b = block;
31338
+ if (b?.type !== "tool_result") return block;
31339
+ if (typeof b.content === "string") return { ...b, content: cut(b.content) };
31340
+ if (Array.isArray(b.content)) {
31341
+ return {
31342
+ ...b,
31343
+ content: b.content.map((part) => {
31344
+ const p = part;
31345
+ return p?.type === "text" && typeof p.text === "string" ? { ...p, text: cut(p.text) } : part;
31346
+ })
31347
+ };
31348
+ }
31349
+ return block;
31350
+ });
31351
+ const { toolUseResult: _structured, ...rest } = rec;
31352
+ void _structured;
31353
+ return { ...rest, message: { ...message, content } };
31354
+ }
31355
+ function createClaudeSessionHandlers(ctx, getProjectPaths) {
31356
+ async function read(rawParams) {
31357
+ const params = claudeSessionReadParamsSchema.parse(rawParams);
31358
+ const resolved = params.cwd ?? (params.projectId ? getProjectPaths()?.get(params.projectId)?.path : void 0);
31359
+ if (!resolved) throw new Error("claudeSession.read: no cwd for this session");
31360
+ const cwd = assertPathAllowed(resolved, ctx.config.allowedRoots);
31361
+ const file2 = sessionFile(cwd, params.sessionId);
31362
+ if (!file2) return { found: false, records: [], omitted: 0 };
31363
+ const links = /* @__PURE__ */ new Map();
31364
+ let leaf = null;
31365
+ for await (const rec of records(file2)) {
31366
+ if (typeof rec.uuid !== "string") continue;
31367
+ const parent = typeof rec.parentUuid === "string" ? rec.parentUuid : typeof rec.logicalParentUuid === "string" ? rec.logicalParentUuid : null;
31368
+ const content = isContent(rec);
31369
+ links.set(rec.uuid, { parent, content });
31370
+ if (content && rec.type !== "system") leaf = rec.uuid;
31371
+ }
31372
+ const branch = [];
31373
+ const seen = /* @__PURE__ */ new Set();
31374
+ for (let at = leaf; at && !seen.has(at); at = links.get(at)?.parent ?? null) {
31375
+ seen.add(at);
31376
+ if (links.get(at)?.content) branch.push(at);
31377
+ }
31378
+ branch.reverse();
31379
+ const wanted = new Set(branch.slice(-params.maxRecords));
31380
+ const byUuid = /* @__PURE__ */ new Map();
31381
+ for await (const rec of records(file2)) {
31382
+ if (typeof rec.uuid === "string" && wanted.has(rec.uuid)) byUuid.set(rec.uuid, shortened(rec));
31383
+ }
31384
+ const picked = [];
31385
+ let bytes = 0;
31386
+ for (let i = branch.length - 1; i >= 0 && picked.length < params.maxRecords; i--) {
31387
+ const rec = byUuid.get(branch[i]);
31388
+ if (!rec) continue;
31389
+ const size = JSON.stringify(rec).length;
31390
+ if (bytes + size > params.maxBytes) break;
31391
+ bytes += size;
31392
+ picked.push(rec);
31393
+ }
31394
+ picked.reverse();
31395
+ return { found: true, records: picked, omitted: branch.length - picked.length };
31396
+ }
31397
+ return { [HubToComputerMethods.claudeSessionRead]: read };
31398
+ }
31399
+
30909
31400
  // src/handlers/git.ts
30910
31401
  var import_node_child_process7 = require("node:child_process");
30911
- var import_node_fs8 = require("node:fs");
31402
+ var import_node_fs9 = require("node:fs");
30912
31403
  var import_promises8 = require("node:fs/promises");
30913
- var import_node_path11 = require("node:path");
31404
+ var import_node_path12 = require("node:path");
30914
31405
 
30915
31406
  // src/handlers/exec.ts
30916
31407
  var import_node_child_process6 = require("node:child_process");
@@ -30968,9 +31459,9 @@ var SETUP_SCRIPT_TIMEOUT_MS = 5 * 6e4;
30968
31459
  function createGitHandlers(ctx) {
30969
31460
  const allow = (candidate) => assertPathAllowed(candidate, ctx.config.allowedRoots);
30970
31461
  async function isGitRepo(rawParams) {
30971
- const { path: path9 } = isGitRepoParams.parse(rawParams);
30972
- const repoPath = allow(path9);
30973
- if (!(0, import_node_fs8.existsSync)(repoPath)) return { isGitRepo: false };
31462
+ const { path: path11 } = isGitRepoParams.parse(rawParams);
31463
+ const repoPath = allow(path11);
31464
+ if (!(0, import_node_fs9.existsSync)(repoPath)) return { isGitRepo: false };
30974
31465
  const result = await execCapture("git", ["rev-parse", "--git-dir"], { cwd: repoPath });
30975
31466
  if (result.spawnError) throw new Error(`Could not start git: ${result.spawnError}`);
30976
31467
  return { isGitRepo: result.status === 0 };
@@ -31003,7 +31494,7 @@ function createGitHandlers(ctx) {
31003
31494
  try {
31004
31495
  await execGit(["worktree", "remove", worktreePath, "--force"], repoPath);
31005
31496
  } catch (err) {
31006
- if (!(0, import_node_fs8.existsSync)(worktreePath)) {
31497
+ if (!(0, import_node_fs9.existsSync)(worktreePath)) {
31007
31498
  await execGit(["worktree", "prune"], repoPath);
31008
31499
  } else {
31009
31500
  throw err;
@@ -31031,13 +31522,13 @@ function createGitHandlers(ctx) {
31031
31522
  const params = runWorktreeSetupScriptParams.parse(rawParams);
31032
31523
  const worktreePath = allow(params.worktreePath);
31033
31524
  const repoPath = allow(params.repoPath);
31034
- const scriptPath = (0, import_node_path11.resolve)(worktreePath, SETUP_SCRIPT);
31035
- if (!(0, import_node_fs8.existsSync)(scriptPath)) return Promise.resolve({ ran: false });
31525
+ const scriptPath = (0, import_node_path12.resolve)(worktreePath, SETUP_SCRIPT);
31526
+ if (!(0, import_node_fs9.existsSync)(scriptPath)) return Promise.resolve({ ran: false });
31036
31527
  try {
31037
- (0, import_node_fs8.accessSync)(scriptPath, import_node_fs8.constants.X_OK);
31528
+ (0, import_node_fs9.accessSync)(scriptPath, import_node_fs9.constants.X_OK);
31038
31529
  } catch {
31039
31530
  try {
31040
- (0, import_node_fs8.chmodSync)(scriptPath, 493);
31531
+ (0, import_node_fs9.chmodSync)(scriptPath, 493);
31041
31532
  } catch {
31042
31533
  return Promise.resolve({ ran: false });
31043
31534
  }
@@ -31088,19 +31579,19 @@ function createGitHandlers(ctx) {
31088
31579
  } else {
31089
31580
  relPaths = params.customPaths ?? [];
31090
31581
  }
31091
- const repoRoot = (0, import_node_path11.resolve)(repoPath);
31092
- const wtRoot = (0, import_node_path11.resolve)(worktreePath);
31582
+ const repoRoot = (0, import_node_path12.resolve)(repoPath);
31583
+ const wtRoot = (0, import_node_path12.resolve)(worktreePath);
31093
31584
  let copied = 0;
31094
31585
  for (const rel of relPaths) {
31095
- const src = (0, import_node_path11.resolve)(repoPath, rel);
31096
- const dest = (0, import_node_path11.resolve)(worktreePath, rel);
31586
+ const src = (0, import_node_path12.resolve)(repoPath, rel);
31587
+ const dest = (0, import_node_path12.resolve)(worktreePath, rel);
31097
31588
  if (!isInside2(src, repoRoot) || !isInside2(dest, wtRoot)) {
31098
31589
  ctx.log("copyIgnoredFiles skipped traversal", { rel });
31099
31590
  continue;
31100
31591
  }
31101
- if (!(0, import_node_fs8.existsSync)(src)) continue;
31592
+ if (!(0, import_node_fs9.existsSync)(src)) continue;
31102
31593
  try {
31103
- await (0, import_promises8.mkdir)((0, import_node_path11.dirname)(dest), { recursive: true });
31594
+ await (0, import_promises8.mkdir)((0, import_node_path12.dirname)(dest), { recursive: true });
31104
31595
  await (0, import_promises8.cp)(src, dest, { recursive: true, verbatimSymlinks: true });
31105
31596
  copied += 1;
31106
31597
  } catch (err) {
@@ -31119,12 +31610,13 @@ function createGitHandlers(ctx) {
31119
31610
  };
31120
31611
  }
31121
31612
  function isInside2(child, root) {
31122
- const c = (0, import_node_path11.resolve)(child);
31123
- return c === root || c.startsWith(root + import_node_path11.sep);
31613
+ const c = (0, import_node_path12.resolve)(child);
31614
+ return c === root || c.startsWith(root + import_node_path12.sep);
31124
31615
  }
31125
31616
 
31126
31617
  // src/handlers/proc.ts
31127
31618
  var import_node_child_process8 = require("node:child_process");
31619
+ var import_node_string_decoder = require("node:string_decoder");
31128
31620
 
31129
31621
  // src/exit-tombstones.ts
31130
31622
  var TOMBSTONE_TTL_MS = 10 * 6e4;
@@ -31223,107 +31715,375 @@ var RingBuffer = class {
31223
31715
  }
31224
31716
  };
31225
31717
 
31226
- // src/handlers/spawn-env.ts
31227
- function buildSpawnEnv(ctx, overrides) {
31228
- const merged = overrides ? { ...sanitizeSpawnEnv(process.env), ...overrides } : sanitizeSpawnEnv(process.env);
31229
- if (ctx.agentHookUrl) {
31230
- merged.SLAYZONE_AGENT_HOOK_URL = ctx.agentHookUrl;
31231
- delete merged.SLAYZONE_HUB_TOKEN;
31232
- }
31233
- if (ctx.hubAddress) merged.SLAYZONE_HUB_ADDRESS = ctx.hubAddress;
31234
- return merged;
31718
+ // src/spawn-admission.ts
31719
+ var import_node_os8 = require("node:os");
31720
+ var DEFAULT_START_CAP_MS = 5e3;
31721
+ var DEFAULT_WARM_QUIET_MS = 3e3;
31722
+ function defaultAdmissionConcurrency(cores = (0, import_node_os8.availableParallelism)()) {
31723
+ return Math.max(2, Math.min(4, Math.floor(cores / 2)));
31235
31724
  }
31236
-
31237
- // src/handlers/proc.ts
31238
- var MAX_BUFFER_SIZE = 750 * 1024;
31239
- var ProcMethods = {
31240
- procSpawn: HubToComputerMethods.procSpawn,
31241
- procKill: HubToComputerMethods.procKill,
31242
- procWrite: HubToComputerMethods.procWrite,
31243
- procGetBufferSince: HubToComputerMethods.procGetBufferSince,
31244
- procList: HubToComputerMethods.procList
31245
- };
31246
- var ProcNotifications = {
31247
- procData: ComputerNotificationMethods.procData,
31248
- procExit: ComputerNotificationMethods.procExit
31249
- };
31250
- function createProcHandlers(ctx, getProjectPaths) {
31251
- const procs = /* @__PURE__ */ new Map();
31252
- const tombstones = new ExitTombstones();
31253
- function killTree(child, signal = "SIGTERM") {
31254
- if (typeof child.pid === "number") killPidTree(child.pid, signal);
31255
- }
31256
- function procSpawn(rawParams) {
31257
- const params = procSpawnParamsSchema.parse(rawParams);
31258
- if (params.spawnId !== void 0) {
31259
- const live = procs.get(params.sessionId);
31260
- if (live && live.spawnId === params.spawnId) return { pid: live.child.pid ?? null };
31261
- if (tombstones.ran(params.sessionId, params.spawnId)) {
31262
- throw new Error(
31263
- `proc.spawn ${params.sessionId}: attempt ${params.spawnId} already ran and exited \u2014 not starting it twice`
31264
- );
31725
+ var LANE_RANK = { foreground: 0, background: 1, warm: 2 };
31726
+ function createSpawnAdmission(opts = {}) {
31727
+ const concurrency = Math.max(1, opts.concurrency ?? defaultAdmissionConcurrency());
31728
+ const startCapMs = opts.startCapMs ?? DEFAULT_START_CAP_MS;
31729
+ const warmQuietMs = opts.warmQuietMs ?? DEFAULT_WARM_QUIET_MS;
31730
+ const now = opts.now ?? Date.now;
31731
+ const setTimer = opts.setTimer ?? ((fn, ms) => {
31732
+ const t2 = setTimeout(fn, ms);
31733
+ t2.unref?.();
31734
+ return t2;
31735
+ });
31736
+ const clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h));
31737
+ const queue = [];
31738
+ const active = /* @__PURE__ */ new Set();
31739
+ let arrivals = 0;
31740
+ let promotions = 0;
31741
+ let lastAgentActivity = Number.NEGATIVE_INFINITY;
31742
+ let warmRetry = null;
31743
+ let pumping = false;
31744
+ let pumpAgain = false;
31745
+ const activeAgents = () => [...active].filter((s) => !s.warm).length;
31746
+ const activeWarm = () => [...active].filter((s) => s.warm).length;
31747
+ function next() {
31748
+ if (active.size >= concurrency || queue.length === 0) return null;
31749
+ let best = null;
31750
+ for (const t2 of queue) {
31751
+ if (!best || LANE_RANK[t2.lane] < LANE_RANK[best.lane] || t2.lane === best.lane && t2.order < best.order) {
31752
+ best = t2;
31265
31753
  }
31266
31754
  }
31267
- const resolved = params.cwd ?? (params.projectId ? getProjectPaths?.()?.get(params.projectId)?.path : void 0);
31268
- const cwd = spawnCwd(resolved, ctx.config.allowedRoots);
31269
- const existing = procs.get(params.sessionId);
31270
- if (existing) {
31271
- killTree(existing.child);
31272
- procs.delete(params.sessionId);
31755
+ if (!best || best.lane !== "warm") return best;
31756
+ if (activeAgents() > 0 || activeWarm() > 0) return null;
31757
+ const quietLeft = lastAgentActivity + warmQuietMs - now();
31758
+ if (quietLeft > 0) {
31759
+ if (warmRetry === null) {
31760
+ warmRetry = setTimer(() => {
31761
+ warmRetry = null;
31762
+ pump();
31763
+ }, quietLeft);
31764
+ }
31765
+ return null;
31273
31766
  }
31274
- const invocation = params.shell ? buildShellInvocation(params.command) : { file: params.command, args: params.args ?? [] };
31275
- const child = (0, import_node_child_process8.spawn)(invocation.file, invocation.args, {
31276
- ...cwd ? { cwd } : {},
31277
- env: buildSpawnEnv(ctx, params.env),
31278
- // stdin PIPED (was 'ignore') so `proc.write` can reach the child.
31279
- stdio: ["pipe", "pipe", "pipe"],
31280
- // Its own process group, so a kill reaches everything it started (`pnpm dev`
31281
- // → node → …), not just the shell — see `killTree`.
31282
- detached: PROCESS_TREE_DETACHED
31283
- });
31284
- const buffer = new RingBuffer(MAX_BUFFER_SIZE);
31285
- const session = {
31286
- child,
31287
- buffer,
31288
- ...params.spawnId !== void 0 ? { spawnId: params.spawnId } : {}
31767
+ return best;
31768
+ }
31769
+ function admit(ticket) {
31770
+ queue.splice(queue.indexOf(ticket), 1);
31771
+ const warm = ticket.lane === "warm";
31772
+ const held = { warm, released: false, cap: null };
31773
+ const release = () => {
31774
+ if (held.released) return;
31775
+ held.released = true;
31776
+ clearTimer(held.cap);
31777
+ active.delete(held);
31778
+ if (!warm) lastAgentActivity = now();
31779
+ pump();
31289
31780
  };
31290
- procs.set(params.sessionId, session);
31291
- const settle2 = (payload) => {
31292
- if (procs.get(params.sessionId) !== session) return;
31293
- procs.delete(params.sessionId);
31294
- const delivered = ctx.dialer.notify(ProcNotifications.procExit, {
31295
- sessionId: params.sessionId,
31296
- ...payload
31297
- });
31298
- tombstones.add(params.sessionId, {
31299
- spawnId: session.spawnId,
31300
- ...typeof child.pid === "number" ? { pid: child.pid } : {},
31301
- exitCode: payload.exitCode,
31302
- signal: payload.signal,
31303
- // The hub never got this exit, so it may be missing stdout too.
31304
- ...delivered ? {} : { buffer: session.buffer }
31305
- });
31781
+ held.cap = setTimer(release, startCapMs);
31782
+ active.add(held);
31783
+ try {
31784
+ ticket.start({ release });
31785
+ } catch {
31786
+ release();
31787
+ }
31788
+ }
31789
+ function pump() {
31790
+ if (pumping) {
31791
+ pumpAgain = true;
31792
+ return;
31793
+ }
31794
+ pumping = true;
31795
+ try {
31796
+ do {
31797
+ pumpAgain = false;
31798
+ for (let t2 = next(); t2; t2 = next()) admit(t2);
31799
+ } while (pumpAgain);
31800
+ } finally {
31801
+ pumping = false;
31802
+ }
31803
+ }
31804
+ return {
31805
+ enqueue(lane, start) {
31806
+ const ticket = { lane, order: ++arrivals, start };
31807
+ queue.push(ticket);
31808
+ if (lane !== "warm") lastAgentActivity = now();
31809
+ pump();
31810
+ return ticket;
31811
+ },
31812
+ cancel(ticket) {
31813
+ const i = queue.indexOf(ticket);
31814
+ if (i < 0) return false;
31815
+ queue.splice(i, 1);
31816
+ pump();
31817
+ return true;
31818
+ },
31819
+ prioritize(ticket) {
31820
+ const t2 = ticket;
31821
+ if (!queue.includes(t2)) return false;
31822
+ t2.lane = "foreground";
31823
+ t2.order = -++promotions;
31824
+ pump();
31825
+ return true;
31826
+ },
31827
+ isQueued(ticket) {
31828
+ return queue.includes(ticket);
31829
+ },
31830
+ stats() {
31831
+ return { queued: queue.length, active: active.size, concurrency };
31832
+ }
31833
+ };
31834
+ }
31835
+
31836
+ // src/handlers/resolve-command.ts
31837
+ var import_node_fs10 = require("node:fs");
31838
+ var import_node_path13 = require("node:path");
31839
+ function isExecutableFile(file2) {
31840
+ try {
31841
+ if (!(0, import_node_fs10.statSync)(file2).isFile()) return false;
31842
+ if (process.platform !== "win32") (0, import_node_fs10.accessSync)(file2, import_node_fs10.constants.X_OK);
31843
+ return true;
31844
+ } catch {
31845
+ return false;
31846
+ }
31847
+ }
31848
+ function resolveCommand(command, env, cwd) {
31849
+ const suffixes = process.platform === "win32" ? ["", ...(env.PATHEXT ?? process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";")] : [""];
31850
+ const hasDir = command.includes("/") || process.platform === "win32" && command.includes("\\");
31851
+ if (hasDir) {
31852
+ const base = (0, import_node_path13.isAbsolute)(command) ? command : (0, import_node_path13.join)(cwd ?? process.cwd(), command);
31853
+ return suffixes.map((s) => base + s).find(isExecutableFile) ?? null;
31854
+ }
31855
+ for (const dir of (env.PATH ?? env.Path ?? "").split(import_node_path13.delimiter)) {
31856
+ if (!dir) continue;
31857
+ for (const suffix of suffixes) {
31858
+ const candidate = (0, import_node_path13.join)(dir, command + suffix);
31859
+ if (isExecutableFile(candidate)) return candidate;
31860
+ }
31861
+ }
31862
+ return null;
31863
+ }
31864
+
31865
+ // src/handlers/spawn-env.ts
31866
+ var import_node_os9 = require("node:os");
31867
+ function buildSpawnEnv(ctx, overrides, capability) {
31868
+ const merged = overrides ? { ...sanitizeSpawnEnv(process.env), ...overrides } : sanitizeSpawnEnv(process.env);
31869
+ if (ctx.agentHookUrl) {
31870
+ merged.SLAYZONE_AGENT_HOOK_URL = ctx.agentHookUrl;
31871
+ delete merged.SLAYZONE_HUB_TOKEN;
31872
+ }
31873
+ if (ctx.hubAddress) merged.SLAYZONE_HUB_ADDRESS = ctx.hubAddress;
31874
+ delete merged.SLAYZONE_CLI_RELAY_ADDRESS;
31875
+ delete merged.SLAYZONE_CLI_RELAY_TOKEN;
31876
+ if (capability && ctx.cliRelay) {
31877
+ delete merged.SLAYZONE_HUB_TOKEN;
31878
+ merged.SLAYZONE_CLI_RELAY_ADDRESS = new URL(ctx.cliRelay.url).host;
31879
+ merged.SLAYZONE_CLI_RELAY_TOKEN = capability.token;
31880
+ }
31881
+ if (ctx.config.binOverrideDir) {
31882
+ merged.PATH = mergePathLists(ctx.config.binOverrideDir, merged.PATH);
31883
+ }
31884
+ return merged;
31885
+ }
31886
+ function buildPtySpawnEnv(ctx, hubEnv, capability) {
31887
+ const env = buildSpawnEnv(ctx, pickHubPtyEnv(hubEnv), capability);
31888
+ const account = (0, import_node_os9.userInfo)();
31889
+ env.HOME = account.homedir;
31890
+ env.USER = account.username;
31891
+ env.LOGNAME = account.username;
31892
+ env.SHELL = resolveUserShell();
31893
+ env.TERM = "xterm-256color";
31894
+ env.COLORTERM = "truecolor";
31895
+ return env;
31896
+ }
31897
+
31898
+ // src/handlers/proc.ts
31899
+ var MAX_BUFFER_SIZE = 750 * 1024;
31900
+ var ProcMethods = {
31901
+ procSpawn: HubToComputerMethods.procSpawn,
31902
+ procKill: HubToComputerMethods.procKill,
31903
+ procWrite: HubToComputerMethods.procWrite,
31904
+ procGetBufferSince: HubToComputerMethods.procGetBufferSince,
31905
+ procList: HubToComputerMethods.procList
31906
+ };
31907
+ var ProcNotifications = {
31908
+ procData: ComputerNotificationMethods.procData,
31909
+ procExit: ComputerNotificationMethods.procExit
31910
+ };
31911
+ var WRITE_ID_MEMORY = 256;
31912
+ function refuse2(reason, message, data = {}) {
31913
+ return new RpcError(ComputerTransportErrorCodes.refusedByComputer, message, {
31914
+ reason,
31915
+ ...data
31916
+ });
31917
+ }
31918
+ function createProcHandlers(ctx, getProjectPaths, options = {}) {
31919
+ const procs = /* @__PURE__ */ new Map();
31920
+ const tombstones = new ExitTombstones();
31921
+ const admission = options.admission ?? createSpawnAdmission();
31922
+ const queued = /* @__PURE__ */ new Map();
31923
+ function killTree(child, signal = "SIGTERM") {
31924
+ if (typeof child.pid === "number") killPidTree(child.pid, signal);
31925
+ }
31926
+ function releaseSlot(session) {
31927
+ const slot = session.slot;
31928
+ session.slot = null;
31929
+ slot?.release();
31930
+ }
31931
+ function procSpawn(rawParams) {
31932
+ const params = procSpawnParamsSchema.parse(rawParams);
31933
+ if (!params.admission) return startProc(params, null);
31934
+ const prior = queued.get(params.sessionId);
31935
+ if (prior && params.spawnId !== void 0 && prior.spawnId === params.spawnId) {
31936
+ return prior.promise;
31937
+ }
31938
+ let resolve9;
31939
+ let reject;
31940
+ const promise2 = new Promise((res, rej) => {
31941
+ resolve9 = res;
31942
+ reject = rej;
31943
+ });
31944
+ const item = {
31945
+ ...params.spawnId !== void 0 ? { spawnId: params.spawnId } : {},
31946
+ ticket: void 0,
31947
+ promise: promise2,
31948
+ resolve: resolve9,
31949
+ reject
31306
31950
  };
31307
- child.stdout?.on("data", (d) => {
31951
+ if (prior) {
31952
+ admission.cancel(prior.ticket);
31953
+ prior.resolve(promise2);
31954
+ }
31955
+ queued.set(params.sessionId, item);
31956
+ item.ticket = admission.enqueue(params.admission, (slot) => {
31957
+ if (queued.get(params.sessionId) !== item) {
31958
+ slot.release();
31959
+ return;
31960
+ }
31961
+ queued.delete(params.sessionId);
31962
+ try {
31963
+ item.resolve(startProc(params, slot));
31964
+ } catch (err) {
31965
+ slot.release();
31966
+ item.reject(err);
31967
+ }
31968
+ });
31969
+ return promise2;
31970
+ }
31971
+ function startProc(params, slot) {
31972
+ if (params.spawnId !== void 0) {
31973
+ const live = procs.get(params.sessionId);
31974
+ if (live && live.spawnId === params.spawnId) return { pid: live.child.pid ?? null };
31975
+ if (tombstones.ran(params.sessionId, params.spawnId)) {
31976
+ throw new Error(
31977
+ `proc.spawn ${params.sessionId}: attempt ${params.spawnId} already ran and exited \u2014 not starting it twice`
31978
+ );
31979
+ }
31980
+ }
31981
+ let resolved = params.cwd || void 0;
31982
+ if (!resolved && params.projectId) {
31983
+ resolved = getProjectPaths?.()?.get(params.projectId)?.path;
31984
+ if (!resolved) throw projectNotPlacedError(params.projectId);
31985
+ }
31986
+ const cwd = spawnCwd(resolved, ctx.config.allowedRoots);
31987
+ const existing = procs.get(params.sessionId);
31988
+ if (existing) {
31989
+ existing.revokeCli?.();
31990
+ killTree(existing.child);
31991
+ procs.delete(params.sessionId);
31992
+ }
31993
+ const capability = ctx.cliRelay?.issue();
31994
+ const env = buildSpawnEnv(ctx, params.env, capability);
31995
+ if (!params.shell && resolveCommand(params.command, env, cwd) === null) {
31996
+ capability?.revoke();
31997
+ throw refuse2(
31998
+ ProcRefusalReasons.binaryNotFound,
31999
+ `${params.command} was not found on this computer's PATH`,
32000
+ { binary: params.command }
32001
+ );
32002
+ }
32003
+ const invocation = params.shell ? buildShellInvocation(params.command) : { file: params.command, args: params.args ?? [] };
32004
+ let child;
32005
+ try {
32006
+ child = (0, import_node_child_process8.spawn)(invocation.file, invocation.args, {
32007
+ ...cwd ? { cwd } : {},
32008
+ env,
32009
+ // stdin PIPED (was 'ignore') so `proc.write` can reach the child.
32010
+ stdio: ["pipe", "pipe", "pipe"],
32011
+ // Its own process group, so a kill reaches everything it started (`pnpm dev`
32012
+ // → node → …), not just the shell — see `killTree`.
32013
+ detached: PROCESS_TREE_DETACHED
32014
+ });
32015
+ } catch (error62) {
32016
+ capability?.revoke();
32017
+ throw error62;
32018
+ }
32019
+ const buffer = new RingBuffer(MAX_BUFFER_SIZE);
32020
+ const session = {
32021
+ revokeCli: capability?.revoke,
32022
+ child,
32023
+ buffer,
32024
+ ...params.spawnId !== void 0 ? { spawnId: params.spawnId } : {},
32025
+ stdinBroken: null,
32026
+ writeIds: [],
32027
+ slot
32028
+ };
32029
+ procs.set(params.sessionId, session);
32030
+ const tag = params.spawnId !== void 0 ? { spawnId: params.spawnId } : {};
32031
+ child.stdin?.on("error", (err) => {
32032
+ session.stdinBroken = err.message;
32033
+ ctx.log("proc stdin error", { sessionId: params.sessionId, error: err.message });
32034
+ });
32035
+ const settle2 = (payload) => {
32036
+ capability?.revoke();
32037
+ releaseSlot(session);
31308
32038
  if (procs.get(params.sessionId) !== session) return;
31309
- const data = d.toString();
32039
+ procs.delete(params.sessionId);
32040
+ const delivered = ctx.dialer.notify(ProcNotifications.procExit, {
32041
+ sessionId: params.sessionId,
32042
+ ...tag,
32043
+ ...payload
32044
+ });
32045
+ tombstones.add(params.sessionId, {
32046
+ spawnId: session.spawnId,
32047
+ ...typeof child.pid === "number" ? { pid: child.pid } : {},
32048
+ exitCode: payload.exitCode,
32049
+ signal: payload.signal,
32050
+ // The hub never got this exit, so it may be missing stdout too.
32051
+ ...delivered ? {} : { buffer: session.buffer }
32052
+ });
32053
+ };
32054
+ const stdoutDecoder = new import_node_string_decoder.StringDecoder("utf8");
32055
+ const stderrDecoder = new import_node_string_decoder.StringDecoder("utf8");
32056
+ const emitStdout = (data) => {
32057
+ if (!data || procs.get(params.sessionId) !== session) return;
31310
32058
  const seq = buffer.append(data);
31311
32059
  ctx.dialer.notify(ProcNotifications.procData, {
31312
32060
  sessionId: params.sessionId,
32061
+ ...tag,
31313
32062
  seq,
31314
32063
  stream: "stdout",
31315
32064
  data
31316
32065
  });
31317
- });
31318
- child.stderr?.on("data", (d) => {
31319
- if (procs.get(params.sessionId) !== session) return;
32066
+ };
32067
+ const emitStderr = (data) => {
32068
+ if (!data || procs.get(params.sessionId) !== session) return;
31320
32069
  ctx.dialer.notify(ProcNotifications.procData, {
31321
32070
  sessionId: params.sessionId,
32071
+ ...tag,
31322
32072
  stream: "stderr",
31323
- data: d.toString()
32073
+ data
31324
32074
  });
32075
+ };
32076
+ child.stdout?.on("data", (d) => {
32077
+ releaseSlot(session);
32078
+ emitStdout(stdoutDecoder.write(d));
32079
+ });
32080
+ child.stderr?.on("data", (d) => {
32081
+ releaseSlot(session);
32082
+ emitStderr(stderrDecoder.write(d));
31325
32083
  });
31326
32084
  child.on("close", (code, signal) => {
32085
+ emitStdout(stdoutDecoder.end());
32086
+ emitStderr(stderrDecoder.end());
31327
32087
  settle2({
31328
32088
  exitCode: typeof code === "number" ? code : null,
31329
32089
  signal: signal != null ? String(signal) : null
@@ -31346,6 +32106,10 @@ function createProcHandlers(ctx, getProjectPaths) {
31346
32106
  ctx.log("proc kill on unknown session", { sessionId: params.sessionId });
31347
32107
  return { ok: true };
31348
32108
  }
32109
+ if (params.spawnId !== void 0 && session.spawnId !== params.spawnId) {
32110
+ ctx.log("proc kill for a replaced attempt", { sessionId: params.sessionId });
32111
+ return { ok: true };
32112
+ }
31349
32113
  killTree(session.child, params.signal ?? "SIGTERM");
31350
32114
  return { ok: true };
31351
32115
  }
@@ -31354,14 +32118,55 @@ function createProcHandlers(ctx, getProjectPaths) {
31354
32118
  const session = procs.get(params.sessionId);
31355
32119
  if (!session) {
31356
32120
  ctx.log("proc write on unknown session", { sessionId: params.sessionId });
31357
- return { ok: true };
32121
+ return Promise.reject(
32122
+ refuse2(ProcRefusalReasons.unknownSession, `proc.write: no process ${params.sessionId}`)
32123
+ );
31358
32124
  }
31359
- try {
31360
- session.child.stdin?.write(params.data);
31361
- } catch (err) {
31362
- ctx.log("proc write failed", { sessionId: params.sessionId, error: String(err) });
32125
+ if (params.spawnId !== void 0 && session.spawnId !== params.spawnId) {
32126
+ return Promise.reject(
32127
+ refuse2(
32128
+ ProcRefusalReasons.staleSpawn,
32129
+ `proc.write: ${params.sessionId} now runs a different process`
32130
+ )
32131
+ );
31363
32132
  }
31364
- return { ok: true };
32133
+ if (params.writeId !== void 0 && session.writeIds.includes(params.writeId)) {
32134
+ return Promise.resolve({ ok: true, delivered: true, duplicate: true });
32135
+ }
32136
+ const stdin2 = session.child.stdin;
32137
+ if (!stdin2 || session.stdinBroken !== null || stdin2.destroyed || stdin2.writableEnded) {
32138
+ return Promise.reject(
32139
+ refuse2(
32140
+ ProcRefusalReasons.stdinClosed,
32141
+ `proc.write: ${params.sessionId} stdin is closed${session.stdinBroken ? ` (${session.stdinBroken})` : ""}`
32142
+ )
32143
+ );
32144
+ }
32145
+ if (params.writeId !== void 0) {
32146
+ session.writeIds.push(params.writeId);
32147
+ if (session.writeIds.length > WRITE_ID_MEMORY) session.writeIds.shift();
32148
+ }
32149
+ return new Promise((resolve9, reject) => {
32150
+ const fail = (err) => {
32151
+ if (params.writeId !== void 0) {
32152
+ session.writeIds = session.writeIds.filter((id) => id !== params.writeId);
32153
+ }
32154
+ const message = err instanceof Error ? err.message : String(err);
32155
+ session.stdinBroken ??= message;
32156
+ ctx.log("proc write failed", { sessionId: params.sessionId, error: message });
32157
+ reject(
32158
+ refuse2(ProcRefusalReasons.stdinClosed, `proc.write: ${params.sessionId}: ${message}`)
32159
+ );
32160
+ };
32161
+ try {
32162
+ stdin2.write(
32163
+ params.data,
32164
+ (err) => err ? fail(err) : resolve9({ ok: true, delivered: true })
32165
+ );
32166
+ } catch (err) {
32167
+ fail(err);
32168
+ }
32169
+ });
31365
32170
  }
31366
32171
  function procGetBufferSince(rawParams) {
31367
32172
  const params = procGetBufferSinceParamsSchema.parse(rawParams);
@@ -31383,7 +32188,14 @@ function createProcHandlers(ctx, getProjectPaths) {
31383
32188
  return { sessions, exited: tombstones.list() };
31384
32189
  }
31385
32190
  function disposeAll() {
32191
+ for (const item of queued.values()) {
32192
+ admission.cancel(item.ticket);
32193
+ item.reject(new Error("computer shutting down"));
32194
+ }
32195
+ queued.clear();
31386
32196
  for (const [sessionId, session] of procs) {
32197
+ session.revokeCli?.();
32198
+ releaseSlot(session);
31387
32199
  killTree(session.child);
31388
32200
  ctx.log("proc disposed on shutdown", { sessionId });
31389
32201
  }
@@ -31400,7 +32212,7 @@ function createProcHandlers(ctx, getProjectPaths) {
31400
32212
  }
31401
32213
 
31402
32214
  // src/handlers/project.ts
31403
- var import_node_fs9 = require("node:fs");
32215
+ var import_node_fs11 = require("node:fs");
31404
32216
  function createProjectHandlers(ctx, getStore) {
31405
32217
  const allow = (candidate) => assertPathAllowed(candidate, ctx.config.allowedRoots);
31406
32218
  const store = () => {
@@ -31412,11 +32224,11 @@ function createProjectHandlers(ctx, getStore) {
31412
32224
  const { projectId } = projectResolvePathParamsSchema.parse(rawParams);
31413
32225
  const entry = store().get(projectId);
31414
32226
  if (!entry) return { path: null };
31415
- return { path: entry.path, exists: (0, import_node_fs9.existsSync)(entry.path) };
32227
+ return { path: entry.path, exists: (0, import_node_fs11.existsSync)(entry.path) };
31416
32228
  }
31417
32229
  async function setPath(rawParams) {
31418
- const { projectId, path: path9 } = projectSetPathParamsSchema.parse(rawParams);
31419
- const resolved = allow(path9);
32230
+ const { projectId, path: path11 } = projectSetPathParamsSchema.parse(rawParams);
32231
+ const resolved = allow(path11);
31420
32232
  const entry = await store().set(projectId, resolved);
31421
32233
  ctx.log("project path recorded", { projectId, path: entry.path });
31422
32234
  return { path: entry.path };
@@ -31433,7 +32245,7 @@ function createProjectHandlers(ctx, getStore) {
31433
32245
  projects: store().list().map((e) => ({
31434
32246
  projectId: e.projectId,
31435
32247
  path: e.path,
31436
- exists: (0, import_node_fs9.existsSync)(e.path)
32248
+ exists: (0, import_node_fs11.existsSync)(e.path)
31437
32249
  }))
31438
32250
  };
31439
32251
  }
@@ -31446,8 +32258,77 @@ function createProjectHandlers(ctx, getStore) {
31446
32258
  }
31447
32259
 
31448
32260
  // src/handlers/pty.ts
32261
+ var import_node_module = require("node:module");
32262
+ var path8 = __toESM(require("node:path"), 1);
31449
32263
  var pty = __toESM(require("node-pty"), 1);
31450
32264
 
32265
+ // ../../shared/platform/src/node-pty-install.ts
32266
+ var import_node_fs12 = __toESM(require("node:fs"), 1);
32267
+ var import_node_path14 = __toESM(require("node:path"), 1);
32268
+ function pendingBuildsFrom(modulesYaml) {
32269
+ try {
32270
+ const parsed = JSON.parse(modulesYaml);
32271
+ const pending = parsed?.pendingBuilds;
32272
+ return Array.isArray(pending) && pending.every((p) => typeof p === "string") ? pending : null;
32273
+ } catch {
32274
+ return null;
32275
+ }
32276
+ }
32277
+ function spawnHelperCandidates(nodePtyDir2, platform3 = process.platform, arch = process.arch) {
32278
+ if (platform3 === "win32") return [];
32279
+ return ["build/Release", "build/Debug", `prebuilds/${platform3}-${arch}`].map(
32280
+ (dir) => import_node_path14.default.join(nodePtyDir2, dir, "spawn-helper")
32281
+ );
32282
+ }
32283
+ function unpackedAsarPath(file2) {
32284
+ return file2.replace(/app\.asar(?!\.unpacked)/, "app.asar.unpacked").replace(/node_modules\.asar(?!\.unpacked)/, "node_modules.asar.unpacked");
32285
+ }
32286
+ var shellQuote = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
32287
+ function installProblems(state) {
32288
+ const problems = [];
32289
+ if (state.repoRoot !== null && state.pendingBuilds && state.pendingBuilds.length > 0) {
32290
+ problems.push({
32291
+ check: "Install scripts ran",
32292
+ detail: `pnpm skipped the install scripts of: ${state.pendingBuilds.join(", ")}.`,
32293
+ fix: `cd ${shellQuote(state.repoRoot)} && pnpm rebuild --pending`
32294
+ });
32295
+ }
32296
+ const broken = state.helpers.filter((h) => !h.executable).map((h) => h.path);
32297
+ if (broken.length > 0) {
32298
+ problems.push({
32299
+ check: "spawn-helper executable",
32300
+ detail: `${broken.join(", ")} ${broken.length === 1 ? "is" : "are"} not executable, so every terminal fails to start ("posix_spawnp failed").`,
32301
+ fix: `chmod +x ${broken.map(shellQuote).join(" ")}`
32302
+ });
32303
+ }
32304
+ return problems;
32305
+ }
32306
+ function readNodePtyInstall(nodePtyDir2) {
32307
+ const nodeModules = import_node_path14.default.dirname(nodePtyDir2);
32308
+ let pendingBuilds = null;
32309
+ let repoRoot = null;
32310
+ try {
32311
+ pendingBuilds = pendingBuildsFrom(
32312
+ import_node_fs12.default.readFileSync(import_node_path14.default.join(nodeModules, ".modules.yaml"), "utf8")
32313
+ );
32314
+ repoRoot = import_node_path14.default.dirname(nodeModules);
32315
+ } catch {
32316
+ }
32317
+ const helpers = spawnHelperCandidates(nodePtyDir2).map(unpackedAsarPath).filter((p) => import_node_fs12.default.existsSync(p)).map((p) => ({ path: p, executable: isExecutable(p) }));
32318
+ return { repoRoot, pendingBuilds, helpers };
32319
+ }
32320
+ function nodePtyInstallProblems(nodePtyDir2) {
32321
+ return installProblems(readNodePtyInstall(nodePtyDir2));
32322
+ }
32323
+ function isExecutable(file2) {
32324
+ try {
32325
+ import_node_fs12.default.accessSync(file2, import_node_fs12.default.constants.X_OK);
32326
+ return true;
32327
+ } catch {
32328
+ return false;
32329
+ }
32330
+ }
32331
+
31451
32332
  // ../../shared/platform/src/startup-deadline.ts
31452
32333
  function armStartupDeadline({
31453
32334
  timeoutMs,
@@ -31515,7 +32396,7 @@ function matchLiveJobs(jobs, descendants, stdoutByPid) {
31515
32396
  const alive = [];
31516
32397
  for (const job of jobs) {
31517
32398
  const outputFile = `/tasks/${job.id}.output`;
31518
- const byOutput = job.kind === "shell" && [...outputs].some((path9) => path9.endsWith(outputFile));
32399
+ const byOutput = job.kind === "shell" && [...outputs].some((path11) => path11.endsWith(outputFile));
31519
32400
  const evalArg = job.command ? `eval ${claudeEvalArg(job.command)}` : null;
31520
32401
  const byCommand = evalArg !== null && descendants.some((d) => d.args.includes(evalArg));
31521
32402
  if (byOutput || byCommand) alive.push(job.id);
@@ -31529,7 +32410,7 @@ async function readStdoutPaths(pids) {
31529
32410
  await Promise.all(
31530
32411
  pids.map(
31531
32412
  (pid2) => (0, import_promises9.readlink)(`/proc/${pid2}/fd/1`).then(
31532
- (path9) => void out.set(pid2, path9),
32413
+ (path11) => void out.set(pid2, path11),
31533
32414
  () => void 0
31534
32415
  )
31535
32416
  )
@@ -31564,167 +32445,121 @@ async function probeBackgroundJobs(rootPid, jobs) {
31564
32445
  return matchLiveJobs(jobs, descendants, stdoutByPid);
31565
32446
  }
31566
32447
 
31567
- // src/spawn-admission.ts
31568
- var import_node_os7 = require("node:os");
31569
- var DEFAULT_START_CAP_MS = 5e3;
31570
- var DEFAULT_WARM_QUIET_MS = 3e3;
31571
- function defaultAdmissionConcurrency(cores = (0, import_node_os7.availableParallelism)()) {
31572
- return Math.max(2, Math.min(4, Math.floor(cores / 2)));
31573
- }
31574
- var LANE_RANK = { foreground: 0, background: 1, warm: 2 };
31575
- function createSpawnAdmission(opts = {}) {
31576
- const concurrency = Math.max(1, opts.concurrency ?? defaultAdmissionConcurrency());
31577
- const startCapMs = opts.startCapMs ?? DEFAULT_START_CAP_MS;
31578
- const warmQuietMs = opts.warmQuietMs ?? DEFAULT_WARM_QUIET_MS;
31579
- const now = opts.now ?? Date.now;
31580
- const setTimer = opts.setTimer ?? ((fn, ms) => {
31581
- const t2 = setTimeout(fn, ms);
31582
- t2.unref?.();
31583
- return t2;
31584
- });
31585
- const clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h));
31586
- const queue = [];
31587
- const active = /* @__PURE__ */ new Set();
31588
- let arrivals = 0;
31589
- let promotions = 0;
31590
- let lastAgentActivity = Number.NEGATIVE_INFINITY;
31591
- let warmRetry = null;
31592
- let pumping = false;
31593
- let pumpAgain = false;
31594
- const activeAgents = () => [...active].filter((s) => !s.warm).length;
31595
- const activeWarm = () => [...active].filter((s) => s.warm).length;
31596
- function next() {
31597
- if (active.size >= concurrency || queue.length === 0) return null;
31598
- let best = null;
31599
- for (const t2 of queue) {
31600
- if (!best || LANE_RANK[t2.lane] < LANE_RANK[best.lane] || t2.lane === best.lane && t2.order < best.order) {
31601
- best = t2;
31602
- }
31603
- }
31604
- if (!best || best.lane !== "warm") return best;
31605
- if (activeAgents() > 0 || activeWarm() > 0) return null;
31606
- const quietLeft = lastAgentActivity + warmQuietMs - now();
31607
- if (quietLeft > 0) {
31608
- if (warmRetry === null) {
31609
- warmRetry = setTimer(() => {
31610
- warmRetry = null;
31611
- pump();
31612
- }, quietLeft);
31613
- }
31614
- return null;
31615
- }
31616
- return best;
31617
- }
31618
- function admit(ticket) {
31619
- queue.splice(queue.indexOf(ticket), 1);
31620
- const warm = ticket.lane === "warm";
31621
- const held = { warm, released: false, cap: null };
31622
- const release = () => {
31623
- if (held.released) return;
31624
- held.released = true;
31625
- clearTimer(held.cap);
31626
- active.delete(held);
31627
- if (!warm) lastAgentActivity = now();
31628
- pump();
31629
- };
31630
- held.cap = setTimer(release, startCapMs);
31631
- active.add(held);
31632
- try {
31633
- ticket.start({ release });
31634
- } catch {
31635
- release();
31636
- }
31637
- }
31638
- function pump() {
31639
- if (pumping) {
31640
- pumpAgain = true;
31641
- return;
31642
- }
31643
- pumping = true;
31644
- try {
31645
- do {
31646
- pumpAgain = false;
31647
- for (let t2 = next(); t2; t2 = next()) admit(t2);
31648
- } while (pumpAgain);
31649
- } finally {
31650
- pumping = false;
31651
- }
31652
- }
31653
- return {
31654
- enqueue(lane, start) {
31655
- const ticket = { lane, order: ++arrivals, start };
31656
- queue.push(ticket);
31657
- if (lane !== "warm") lastAgentActivity = now();
31658
- pump();
31659
- return ticket;
31660
- },
31661
- cancel(ticket) {
31662
- const i = queue.indexOf(ticket);
31663
- if (i < 0) return false;
31664
- queue.splice(i, 1);
31665
- pump();
31666
- return true;
31667
- },
31668
- prioritize(ticket) {
31669
- const t2 = ticket;
31670
- if (!queue.includes(t2)) return false;
31671
- t2.lane = "foreground";
31672
- t2.order = -++promotions;
31673
- pump();
31674
- return true;
31675
- },
31676
- isQueued(ticket) {
31677
- return queue.includes(ticket);
31678
- },
31679
- stats() {
31680
- return { queued: queue.length, active: active.size, concurrency };
31681
- }
31682
- };
31683
- }
31684
-
31685
32448
  // src/handlers/pty.ts
31686
32449
  var MAX_BUFFER_SIZE2 = 750 * 1024;
31687
32450
  var STARTUP_KILL_REAP_MS = 2e3;
32451
+ function nodePtyDir() {
32452
+ const resolve9 = typeof require === "function" ? require.resolve : (0, import_node_module.createRequire)(process.argv[1] ?? path8.join(process.cwd(), "index.js")).resolve;
32453
+ return path8.dirname(resolve9("node-pty/package.json"));
32454
+ }
32455
+ var installProblemsOnDisk = () => nodePtyInstallProblems(nodePtyDir());
31688
32456
  function createPtyHandlers(ctx, getProjectPaths, options = {}) {
31689
32457
  const sessions = /* @__PURE__ */ new Map();
31690
32458
  const tombstones = new ExitTombstones();
31691
32459
  const admission = options.admission ?? createSpawnAdmission();
32460
+ const spawnPty = options.spawnPty ?? pty.spawn;
32461
+ const installProblems2 = options.nodePtyInstallProblems ?? installProblemsOnDisk;
31692
32462
  const queued = /* @__PURE__ */ new Map();
32463
+ const starts = /* @__PURE__ */ new Map();
32464
+ function serializeStart(id, spawnId, run) {
32465
+ const prior = starts.get(id)?.promise ?? Promise.resolve();
32466
+ const record2 = { spawnId, promise: void 0, cancelled: false };
32467
+ const assertActive = () => {
32468
+ if (record2.cancelled) throw new Error(`pty ${id}: spawn killed before start`);
32469
+ };
32470
+ const current = prior.catch(() => {
32471
+ }).then(() => {
32472
+ assertActive();
32473
+ return run(assertActive);
32474
+ });
32475
+ record2.promise = current;
32476
+ starts.set(id, record2);
32477
+ void current.finally(() => {
32478
+ if (starts.get(id)?.promise === current) starts.delete(id);
32479
+ }).catch(() => {
32480
+ });
32481
+ return current;
32482
+ }
32483
+ async function waitForExit(session, timeoutMs) {
32484
+ let timer;
32485
+ try {
32486
+ return await Promise.race([
32487
+ session.exited.then(() => true),
32488
+ new Promise((resolve9) => {
32489
+ timer = setTimeout(() => resolve9(false), timeoutMs);
32490
+ timer.unref?.();
32491
+ })
32492
+ ]);
32493
+ } finally {
32494
+ if (timer) clearTimeout(timer);
32495
+ }
32496
+ }
32497
+ function stopPty(session, signal) {
32498
+ if (session.stopping) return session.stopping;
32499
+ session.stopping = (async () => {
32500
+ try {
32501
+ session.proc.kill(signal);
32502
+ } catch {
32503
+ }
32504
+ if (await waitForExit(session, 2e3)) return;
32505
+ try {
32506
+ session.proc.kill("SIGKILL");
32507
+ } catch {
32508
+ }
32509
+ if (!await waitForExit(session, 2e3)) {
32510
+ throw new Error(`pty ${session.id}: process ${session.proc.pid} did not exit`);
32511
+ }
32512
+ })();
32513
+ void session.stopping.catch(() => {
32514
+ session.stopping = void 0;
32515
+ });
32516
+ return session.stopping;
32517
+ }
32518
+ async function stopBeforeReplacement(id) {
32519
+ const old = sessions.get(id);
32520
+ if (!old) return;
32521
+ old.replacing = true;
32522
+ try {
32523
+ await stopPty(old);
32524
+ } catch (err) {
32525
+ old.replacing = false;
32526
+ throw err;
32527
+ }
32528
+ }
31693
32529
  function resolveCwd(opts) {
31694
32530
  if (opts.cwd) return spawnCwd(opts.cwd, ctx.config.allowedRoots);
31695
32531
  if (!opts.projectId) throw new Error("pty.spawn needs either cwd or projectId");
31696
32532
  const entry = getProjectPaths?.()?.get(opts.projectId);
31697
- if (!entry) {
31698
- throw new Error(
31699
- `project ${opts.projectId} is not checked out on this computer \u2014 adopt a folder or enable remote git for it`
31700
- );
31701
- }
32533
+ if (!entry) throw projectNotPlacedError(opts.projectId);
31702
32534
  return spawnCwd(entry.path, ctx.config.allowedRoots);
31703
32535
  }
31704
32536
  function spawnSession(id, opts) {
31705
32537
  const cwd = resolveCwd(opts);
31706
- const existing = sessions.get(id);
31707
- if (existing) {
31708
- existing.deadline?.cancel();
31709
- releaseAdmission(existing);
31710
- try {
31711
- existing.proc.kill();
31712
- } catch {
31713
- }
31714
- sessions.delete(id);
32538
+ if (sessions.has(id)) throw new Error(`pty ${id}: old process still live`);
32539
+ const capability = ctx.cliRelay?.issue();
32540
+ let proc;
32541
+ try {
32542
+ proc = spawnPty(opts.command, opts.args ?? [], {
32543
+ name: "xterm-color",
32544
+ cwd,
32545
+ env: buildPtySpawnEnv(ctx, opts.env, capability),
32546
+ cols: opts.cols ?? 80,
32547
+ rows: opts.rows ?? 24
32548
+ });
32549
+ } catch (err) {
32550
+ capability?.revoke();
32551
+ throw explainSpawnFailure(err);
31715
32552
  }
31716
- const proc = pty.spawn(opts.command, opts.args ?? [], {
31717
- name: "xterm-color",
31718
- cwd,
31719
- env: buildSpawnEnv(ctx, opts.env),
31720
- cols: opts.cols ?? 80,
31721
- rows: opts.rows ?? 24
31722
- });
31723
32553
  const buffer = new RingBuffer(MAX_BUFFER_SIZE2);
32554
+ let resolveExit;
32555
+ const exited = new Promise((resolve9) => resolveExit = resolve9);
31724
32556
  const session = {
32557
+ revokeCli: capability?.revoke,
31725
32558
  proc,
31726
32559
  buffer,
31727
32560
  id,
32561
+ exited,
32562
+ resolveExit,
31728
32563
  ...opts.warm ? { warm: opts.warm } : {},
31729
32564
  ...opts.spawnId !== void 0 ? { spawnId: opts.spawnId } : {}
31730
32565
  };
@@ -31743,11 +32578,14 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
31743
32578
  if (session.warm) return;
31744
32579
  ctx.dialer.notify(ComputerNotificationMethods.ptyData, {
31745
32580
  sessionId: session.id,
32581
+ ...session.spawnId ? { spawnId: session.spawnId } : {},
31746
32582
  seq,
31747
32583
  data
31748
32584
  });
31749
32585
  });
31750
32586
  proc.onExit(({ exitCode, signal }) => {
32587
+ capability?.revoke();
32588
+ session.resolveExit();
31751
32589
  reap(
31752
32590
  session,
31753
32591
  typeof exitCode === "number" ? exitCode : null,
@@ -31756,11 +32594,33 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
31756
32594
  });
31757
32595
  return session;
31758
32596
  }
32597
+ function explainSpawnFailure(err) {
32598
+ const original = err instanceof Error ? err : new Error(String(err));
32599
+ let problems;
32600
+ try {
32601
+ problems = installProblems2();
32602
+ } catch {
32603
+ return original;
32604
+ }
32605
+ if (problems.length === 0) return original;
32606
+ const why = problems.map((p) => `${p.detail} Fix: ${p.fix}`).join(" ");
32607
+ return new Error(`${original.message} ${why}`, { cause: original });
32608
+ }
31759
32609
  function reap(session, exitCode, signal) {
31760
32610
  if (sessions.get(session.id) !== session) return;
31761
32611
  sessions.delete(session.id);
31762
32612
  session.deadline?.cancel();
31763
32613
  releaseAdmission(session);
32614
+ if (session.replacing) {
32615
+ tombstones.add(session.id, {
32616
+ spawnId: session.spawnId,
32617
+ pid: session.proc.pid,
32618
+ exitCode,
32619
+ signal,
32620
+ reason: "replaced"
32621
+ });
32622
+ return;
32623
+ }
31764
32624
  if (session.warm) {
31765
32625
  ctx.log("warm pty exited before adoption", { warmId: session.id });
31766
32626
  return;
@@ -31768,6 +32628,7 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
31768
32628
  const reason = session.exitReason;
31769
32629
  const delivered = ctx.dialer.notify(ComputerNotificationMethods.ptyExit, {
31770
32630
  sessionId: session.id,
32631
+ ...session.spawnId ? { spawnId: session.spawnId } : {},
31771
32632
  exitCode,
31772
32633
  signal,
31773
32634
  ...reason ? { reason } : {}
@@ -31803,11 +32664,20 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
31803
32664
  slot.release();
31804
32665
  }
31805
32666
  function startSession(params) {
31806
- const session = spawnSession(params.sessionId, params);
32667
+ const shell = params.computerShell ? resolveUserShell() : params.command;
32668
+ const args = params.computerShell ? [
32669
+ ...getShellStartupArgs(shell),
32670
+ ...params.computerShellCommand ? ["-c", params.computerShellCommand] : []
32671
+ ] : params.args;
32672
+ const session = spawnSession(params.sessionId, {
32673
+ ...params,
32674
+ command: shell,
32675
+ args
32676
+ });
31807
32677
  ctx.log("pty spawned", {
31808
32678
  sessionId: params.sessionId,
31809
32679
  pid: session.proc.pid,
31810
- command: params.command
32680
+ command: shell
31811
32681
  });
31812
32682
  return {
31813
32683
  pid: session.proc.pid,
@@ -31859,16 +32729,26 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
31859
32729
  }
31860
32730
  const waitedMs = Date.now() - enqueuedAt;
31861
32731
  if (waitedMs >= 1e3) ctx.log("agent start admitted after queueing", { id, lane, waitedMs });
31862
- item.resolve(reply);
32732
+ Promise.resolve(reply).then(item.resolve, (err) => {
32733
+ slot.release();
32734
+ item.reject(err);
32735
+ });
31863
32736
  });
31864
32737
  return promise2;
31865
32738
  }
31866
32739
  function spawn6(rawParams) {
31867
32740
  const params = ptySpawnParamsSchema.parse(rawParams);
32741
+ const starting = starts.get(params.sessionId);
32742
+ if (starting && params.spawnId !== void 0 && starting.spawnId === params.spawnId) {
32743
+ return starting.promise;
32744
+ }
31868
32745
  if (params.spawnId !== void 0) {
31869
32746
  const live = sessions.get(params.sessionId);
31870
32747
  if (live && !live.warm && live.spawnId === params.spawnId) {
31871
- return { pid: live.proc.pid, ...live.deadline ? { startupDeadline: true } : {} };
32748
+ return {
32749
+ pid: live.proc.pid,
32750
+ ...live.deadline ? { startupDeadline: true } : {}
32751
+ };
31872
32752
  }
31873
32753
  if (tombstones.ran(params.sessionId, params.spawnId)) {
31874
32754
  throw new Error(
@@ -31878,7 +32758,11 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
31878
32758
  }
31879
32759
  if (params.admission === void 0) {
31880
32760
  const prior = queued.get(params.sessionId);
31881
- const reply = startSession(params);
32761
+ const reply = sessions.has(params.sessionId) || starts.has(params.sessionId) ? serializeStart(params.sessionId, params.spawnId, async (assertActive) => {
32762
+ await stopBeforeReplacement(params.sessionId);
32763
+ assertActive();
32764
+ return startSession(params);
32765
+ }) : startSession(params);
31882
32766
  if (prior) {
31883
32767
  queued.delete(params.sessionId);
31884
32768
  admission.cancel(prior.ticket);
@@ -31886,11 +32770,15 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
31886
32770
  }
31887
32771
  return reply;
31888
32772
  }
31889
- return enqueueSpawn(params.sessionId, params.admission, params, (slot, item) => {
31890
- const reply = startSession({
31891
- ...params,
31892
- ...item.cols !== void 0 ? { cols: item.cols } : {},
31893
- ...item.rows !== void 0 ? { rows: item.rows } : {}
32773
+ return enqueueSpawn(params.sessionId, params.admission, params, async (slot, item) => {
32774
+ const reply = await serializeStart(params.sessionId, params.spawnId, async (assertActive) => {
32775
+ await stopBeforeReplacement(params.sessionId);
32776
+ assertActive();
32777
+ return startSession({
32778
+ ...params,
32779
+ ...item.cols !== void 0 ? { cols: item.cols } : {},
32780
+ ...item.rows !== void 0 ? { rows: item.rows } : {}
32781
+ });
31894
32782
  });
31895
32783
  const session = sessions.get(params.sessionId);
31896
32784
  session.admissionSlot = slot;
@@ -31899,18 +32787,28 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
31899
32787
  });
31900
32788
  }
31901
32789
  function startWarm(params, cols, rows) {
32790
+ const shell = params.computerShell ? resolveUserShell() : params.command;
31902
32791
  const session = spawnSession(params.warmId, {
31903
32792
  ...params,
32793
+ command: shell,
32794
+ args: params.computerShell ? getShellStartupArgs(shell) : params.args,
31904
32795
  ...cols !== void 0 ? { cols } : {},
31905
32796
  ...rows !== void 0 ? { rows } : {},
31906
32797
  warm: { cwd: params.cwd, startedAt: Date.now() }
31907
32798
  });
31908
32799
  if (params.postSpawnCommand) session.proc.write(`${params.postSpawnCommand}\r`);
31909
- ctx.log("warm pty spawned", { warmId: params.warmId, pid: session.proc.pid });
32800
+ ctx.log("warm pty spawned", {
32801
+ warmId: params.warmId,
32802
+ pid: session.proc.pid
32803
+ });
31910
32804
  return session;
31911
32805
  }
31912
32806
  function warmSpawn(rawParams) {
31913
32807
  const params = ptyWarmSpawnParamsSchema.parse(rawParams);
32808
+ const live = sessions.get(params.warmId);
32809
+ if (live?.warm) return Promise.resolve({ pid: live.proc.pid });
32810
+ const waiting = queued.get(params.warmId);
32811
+ if (waiting) return waiting.promise;
31914
32812
  return enqueueSpawn(params.warmId, "warm", params, (slot, item) => {
31915
32813
  const session = startWarm(params, item.cols, item.rows);
31916
32814
  session.admissionSlot = slot;
@@ -31929,20 +32827,49 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
31929
32827
  function prioritize(rawParams) {
31930
32828
  const params = ptyPrioritizeParamsSchema.parse(rawParams);
31931
32829
  const item = queued.get(params.sessionId);
31932
- return { ok: true, queued: item ? admission.prioritize(item.ticket) : false };
32830
+ return {
32831
+ ok: true,
32832
+ queued: item && (params.spawnId === void 0 || item.spawnId === params.spawnId) ? admission.prioritize(item.ticket) : false
32833
+ };
31933
32834
  }
31934
32835
  async function backgroundJobs(rawParams) {
31935
32836
  const params = ptyBackgroundJobsParamsSchema.parse(rawParams);
31936
- const pid = sessions.get(params.sessionId)?.proc.pid;
32837
+ const session = sessions.get(params.sessionId);
32838
+ const pid = session && (params.spawnId === void 0 || session.spawnId === params.spawnId) ? session.proc.pid : void 0;
31937
32839
  if (pid === void 0) return { ok: true, supported: true, alive: [] };
31938
32840
  const alive = await probeBackgroundJobs(pid, params.jobs);
31939
32841
  return alive === null ? { ok: true, supported: false, alive: [] } : { ok: true, supported: true, alive };
31940
32842
  }
32843
+ async function doctor(rawParams) {
32844
+ const params = ptyDoctorParamsSchema.parse(rawParams);
32845
+ await options.pathReady;
32846
+ const env = buildPtySpawnEnv(ctx);
32847
+ const binaries = await Promise.all(
32848
+ params.binaries.map(async (binary) => ({
32849
+ binary,
32850
+ path: await findOnPath(binary, env)
32851
+ }))
32852
+ );
32853
+ return {
32854
+ ok: true,
32855
+ problems: installProblems2(),
32856
+ shellExists: shellExists(resolveUserShell()),
32857
+ binaries
32858
+ };
32859
+ }
31941
32860
  function dropQueued(reason) {
31942
32861
  for (const id of [...queued.keys()]) cancelQueued(id, reason);
31943
32862
  }
31944
32863
  function warmAdopt(rawParams) {
31945
32864
  const params = ptyWarmAdoptParamsSchema.parse(rawParams);
32865
+ const adopted2 = sessions.get(params.sessionId);
32866
+ if (adopted2?.spawnId === params.warmId) {
32867
+ return {
32868
+ pid: adopted2.proc.pid,
32869
+ data: adopted2.buffer.toString(),
32870
+ seq: adopted2.buffer.getCurrentSeq()
32871
+ };
32872
+ }
31946
32873
  const session = sessions.get(params.warmId);
31947
32874
  if (!session || !session.warm) {
31948
32875
  throw new RpcError(
@@ -31952,16 +32879,15 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
31952
32879
  }
31953
32880
  const displaced = sessions.get(params.sessionId);
31954
32881
  if (displaced) {
31955
- ctx.log("warm adopt displaced a live session", {
31956
- warmId: params.warmId,
31957
- sessionId: params.sessionId,
31958
- displacedPid: displaced.proc.pid,
31959
- displacedWarm: !!displaced.warm
31960
- });
32882
+ throw new RpcError(
32883
+ ComputerTransportErrorCodes.unknownComputer,
32884
+ `session ${params.sessionId} already has live process ${displaced.proc.pid}`
32885
+ );
31961
32886
  }
31962
32887
  sessions.delete(params.warmId);
31963
32888
  session.id = params.sessionId;
31964
32889
  delete session.warm;
32890
+ session.spawnId = params.warmId;
31965
32891
  sessions.set(params.sessionId, session);
31966
32892
  releaseAdmission(session);
31967
32893
  const data = session.buffer.toString();
@@ -31973,16 +32899,12 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
31973
32899
  });
31974
32900
  return { pid: session.proc.pid, data, seq };
31975
32901
  }
31976
- function warmKill(rawParams) {
32902
+ async function warmKill(rawParams) {
31977
32903
  const params = ptyWarmKillParamsSchema.parse(rawParams);
31978
32904
  cancelQueued(params.warmId, "killed");
31979
32905
  const session = sessions.get(params.warmId);
31980
32906
  if (!session) return { ok: true };
31981
- sessions.delete(params.warmId);
31982
- try {
31983
- session.proc.kill();
31984
- } catch {
31985
- }
32907
+ await stopPty(session);
31986
32908
  return { ok: true };
31987
32909
  }
31988
32910
  function warmList() {
@@ -31998,58 +32920,55 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
31998
32920
  }
31999
32921
  return { warms };
32000
32922
  }
32001
- function kill(rawParams) {
32923
+ async function kill(rawParams) {
32002
32924
  const params = ptyKillParamsSchema.parse(rawParams);
32003
- const waiting = queued.get(params.sessionId);
32004
- if (waiting) {
32925
+ const starting = starts.get(params.sessionId);
32926
+ if (starting && (params.spawnId === void 0 || starting.spawnId === params.spawnId)) {
32927
+ starting.cancelled = true;
32928
+ }
32929
+ const live = sessions.get(params.sessionId);
32930
+ const pending = queued.get(params.sessionId);
32931
+ if (pending && (params.spawnId === void 0 || pending.spawnId === params.spawnId)) {
32005
32932
  const delivered = ctx.dialer.notify(ComputerNotificationMethods.ptyExit, {
32006
32933
  sessionId: params.sessionId,
32934
+ ...pending.spawnId ? { spawnId: pending.spawnId } : {},
32007
32935
  exitCode: null,
32008
32936
  signal: params.signal ?? "SIGHUP",
32009
32937
  reason: "killed-before-start"
32010
32938
  });
32011
32939
  tombstones.add(params.sessionId, {
32012
- spawnId: waiting.spawnId,
32940
+ spawnId: pending.spawnId,
32013
32941
  exitCode: null,
32014
32942
  signal: params.signal ?? "SIGHUP",
32015
32943
  reason: "killed-before-start"
32016
32944
  });
32017
32945
  if (!delivered)
32018
- ctx.log("kill of a queued start not delivered", { sessionId: params.sessionId });
32946
+ ctx.log("kill of a queued start not delivered", {
32947
+ sessionId: params.sessionId
32948
+ });
32019
32949
  cancelQueued(params.sessionId, "killed");
32020
- const superseded = sessions.get(params.sessionId);
32021
- if (superseded) {
32022
- sessions.delete(params.sessionId);
32023
- superseded.deadline?.cancel();
32024
- releaseAdmission(superseded);
32025
- try {
32026
- superseded.proc.kill(params.signal);
32027
- } catch {
32028
- }
32029
- }
32950
+ if (live && params.spawnId === void 0) await stopPty(live, params.signal);
32030
32951
  return { ok: true };
32031
32952
  }
32032
- const session = sessions.get(params.sessionId);
32033
- if (!session) {
32953
+ if (!live || params.spawnId !== void 0 && live.spawnId !== params.spawnId) {
32034
32954
  ctx.log("pty kill on unknown session", { sessionId: params.sessionId });
32035
32955
  return { ok: true };
32036
32956
  }
32037
- try {
32038
- session.proc.kill(params.signal);
32039
- } catch (err) {
32040
- ctx.log("pty kill failed", { sessionId: params.sessionId, error: String(err) });
32041
- }
32957
+ await stopPty(live, params.signal);
32042
32958
  return { ok: true };
32043
32959
  }
32044
32960
  function resize(rawParams) {
32045
32961
  const params = ptyResizeParamsSchema.parse(rawParams);
32046
32962
  const waiting = queued.get(params.sessionId);
32047
32963
  if (waiting) {
32964
+ if (params.spawnId !== void 0 && waiting.spawnId !== params.spawnId) return { ok: true };
32048
32965
  waiting.cols = params.cols;
32049
32966
  waiting.rows = params.rows;
32050
32967
  return { ok: true };
32051
32968
  }
32052
32969
  const session = sessions.get(params.sessionId);
32970
+ if (session && params.spawnId !== void 0 && session.spawnId !== params.spawnId)
32971
+ return { ok: true };
32053
32972
  if (!session) {
32054
32973
  ctx.log("pty resize on unknown session", { sessionId: params.sessionId });
32055
32974
  return { ok: true };
@@ -32061,10 +32980,13 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
32061
32980
  const params = ptyWriteParamsSchema.parse(rawParams);
32062
32981
  const waiting = queued.get(params.sessionId);
32063
32982
  if (waiting) {
32983
+ if (params.spawnId !== void 0 && waiting.spawnId !== params.spawnId) return { ok: true };
32064
32984
  waiting.writes.push(params.data);
32065
32985
  return { ok: true };
32066
32986
  }
32067
32987
  const session = sessions.get(params.sessionId);
32988
+ if (session && params.spawnId !== void 0 && session.spawnId !== params.spawnId)
32989
+ return { ok: true };
32068
32990
  if (!session) {
32069
32991
  ctx.log("pty write on unknown session", { sessionId: params.sessionId });
32070
32992
  return { ok: true };
@@ -32090,7 +33012,9 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
32090
33012
  }
32091
33013
  function getBufferSince(rawParams) {
32092
33014
  const params = ptyGetBufferSinceParamsSchema.parse(rawParams);
32093
- const buffer = sessions.get(params.sessionId)?.buffer ?? tombstones.get(params.sessionId)?.buffer;
33015
+ const live = sessions.get(params.sessionId);
33016
+ const exited = tombstones.get(params.sessionId);
33017
+ const buffer = live && (params.spawnId === void 0 || live.spawnId === params.spawnId) ? live.buffer : exited && (params.spawnId === void 0 || exited.spawnId === params.spawnId) ? exited.buffer : void 0;
32094
33018
  if (!buffer) return { frames: [] };
32095
33019
  const frames = buffer.getChunksSince(params.seq).map((c) => ({ seq: c.seq, data: c.data }));
32096
33020
  return { frames };
@@ -32098,6 +33022,7 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
32098
33022
  function disposeAll() {
32099
33023
  dropQueued("computer shutting down");
32100
33024
  for (const [sessionId, session] of sessions) {
33025
+ session.revokeCli?.();
32101
33026
  session.deadline?.cancel();
32102
33027
  releaseAdmission(session);
32103
33028
  try {
@@ -32117,6 +33042,7 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
32117
33042
  [HubToComputerMethods.ptyList]: list,
32118
33043
  [HubToComputerMethods.ptyPrioritize]: prioritize,
32119
33044
  [HubToComputerMethods.ptyBackgroundJobs]: backgroundJobs,
33045
+ [HubToComputerMethods.ptyDoctor]: doctor,
32120
33046
  [HubToComputerMethods.ptyWarmSpawn]: warmSpawn,
32121
33047
  [HubToComputerMethods.ptyWarmAdopt]: warmAdopt,
32122
33048
  [HubToComputerMethods.ptyWarmKill]: warmKill,
@@ -32126,19 +33052,19 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
32126
33052
  }
32127
33053
 
32128
33054
  // src/handlers/computer-config.ts
32129
- var import_node_fs10 = require("node:fs");
32130
- var import_node_path12 = require("node:path");
33055
+ var import_node_fs13 = require("node:fs");
33056
+ var import_node_path15 = require("node:path");
32131
33057
  function canonicalRoots(roots) {
32132
33058
  const applied = [];
32133
33059
  const rejected = [];
32134
33060
  for (const root of roots) {
32135
- if (!(0, import_node_path12.isAbsolute)(root)) {
33061
+ if (!(0, import_node_path15.isAbsolute)(root)) {
32136
33062
  rejected.push({ path: root, reason: "not an absolute path" });
32137
33063
  continue;
32138
33064
  }
32139
33065
  let real;
32140
33066
  try {
32141
- real = import_node_fs10.realpathSync.native((0, import_node_path12.resolve)(root));
33067
+ real = import_node_fs13.realpathSync.native((0, import_node_path15.resolve)(root));
32142
33068
  } catch {
32143
33069
  rejected.push({ path: root, reason: "does not exist on this computer" });
32144
33070
  continue;
@@ -32183,12 +33109,13 @@ function createComputerConfigHandlers(ctx) {
32183
33109
 
32184
33110
  // src/project-paths.ts
32185
33111
  var import_promises10 = require("node:fs/promises");
32186
- var import_node_os8 = require("node:os");
32187
- var import_node_path13 = require("node:path");
33112
+ var import_node_crypto4 = require("node:crypto");
33113
+ var import_node_os10 = require("node:os");
33114
+ var import_node_path16 = require("node:path");
32188
33115
  function slayzoneRootDir3() {
32189
33116
  if (process.env.SLAYZONE_ROOT) return process.env.SLAYZONE_ROOT;
32190
- const home = process.env.HOME ?? process.env.USERPROFILE ?? (0, import_node_os8.homedir)();
32191
- return (0, import_node_path13.join)(home, ".slayzone");
33117
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? (0, import_node_os10.homedir)();
33118
+ return (0, import_node_path16.join)(home, ".slayzone");
32192
33119
  }
32193
33120
  var projectPathEntrySchema = external_exports.object({
32194
33121
  /** Absolute, realpath-resolved at write time. */
@@ -32197,7 +33124,7 @@ var projectPathEntrySchema = external_exports.object({
32197
33124
  });
32198
33125
  var projectMapSchema = external_exports.record(external_exports.string(), projectPathEntrySchema);
32199
33126
  function projectsFilePath(baseDir) {
32200
- return (0, import_node_path13.join)(baseDir ?? slayzoneRootDir3(), "computer.projects.json");
33127
+ return (0, import_node_path16.join)(baseDir ?? slayzoneRootDir3(), "computer.projects.json");
32201
33128
  }
32202
33129
  var fileSchema = external_exports.record(external_exports.string(), projectMapSchema);
32203
33130
  async function readFileMap(filePath) {
@@ -32228,8 +33155,8 @@ async function readFileMap(filePath) {
32228
33155
  return fileSchema.parse(out);
32229
33156
  }
32230
33157
  async function writeFileMap(filePath, map2) {
32231
- await (0, import_promises10.mkdir)((0, import_node_path13.dirname)(filePath), { recursive: true, mode: 448 });
32232
- const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
33158
+ await (0, import_promises10.mkdir)((0, import_node_path16.dirname)(filePath), { recursive: true, mode: 448 });
33159
+ const tmpPath = `${filePath}.tmp-${process.pid}-${(0, import_node_crypto4.randomUUID)()}`;
32233
33160
  await (0, import_promises10.writeFile)(tmpPath, `${JSON.stringify(map2, null, 2)}
32234
33161
  `, { mode: 384 });
32235
33162
  try {
@@ -32239,6 +33166,16 @@ async function writeFileMap(filePath, map2) {
32239
33166
  throw err;
32240
33167
  }
32241
33168
  }
33169
+ var writeQueues = /* @__PURE__ */ new Map();
33170
+ function serialized(filePath, write) {
33171
+ const previous = writeQueues.get(filePath) ?? Promise.resolve();
33172
+ const next = previous.then(write, write);
33173
+ writeQueues.set(
33174
+ filePath,
33175
+ next.catch(() => void 0)
33176
+ );
33177
+ return next;
33178
+ }
32242
33179
  async function createProjectPathStore(hubHost, options = {}) {
32243
33180
  if (!hubHost.trim()) throw new Error(`invalid hub host for project map: '${hubHost}'`);
32244
33181
  const filePath = projectsFilePath(options.baseDir);
@@ -32248,21 +33185,25 @@ async function createProjectPathStore(hubHost, options = {}) {
32248
33185
  get(projectId) {
32249
33186
  return cache[projectId] ?? null;
32250
33187
  },
32251
- async set(projectId, path9) {
32252
- const entry = { path: path9, updatedAt: Date.now() };
32253
- const all = await readFileMap(filePath);
32254
- all[hubHost] = { ...all[hubHost] ?? {}, [projectId]: entry };
32255
- await writeFileMap(filePath, all);
32256
- cache = all[hubHost];
32257
- return entry;
33188
+ set(projectId, path11) {
33189
+ return serialized(filePath, async () => {
33190
+ const entry = { path: path11, updatedAt: Date.now() };
33191
+ const all = await readFileMap(filePath);
33192
+ all[hubHost] = { ...all[hubHost] ?? {}, [projectId]: entry };
33193
+ await writeFileMap(filePath, all);
33194
+ cache = all[hubHost];
33195
+ return entry;
33196
+ });
32258
33197
  },
32259
- async forget(projectId) {
32260
- const all = await readFileMap(filePath);
32261
- const forHub = { ...all[hubHost] ?? {} };
32262
- delete forHub[projectId];
32263
- all[hubHost] = forHub;
32264
- await writeFileMap(filePath, all);
32265
- cache = forHub;
33198
+ forget(projectId) {
33199
+ return serialized(filePath, async () => {
33200
+ const all = await readFileMap(filePath);
33201
+ const forHub = { ...all[hubHost] ?? {} };
33202
+ delete forHub[projectId];
33203
+ all[hubHost] = forHub;
33204
+ await writeFileMap(filePath, all);
33205
+ cache = forHub;
33206
+ });
32266
33207
  },
32267
33208
  list() {
32268
33209
  return Object.entries(cache).map(([projectId, entry]) => ({ projectId, ...entry }));
@@ -32282,13 +33223,20 @@ function createHubRequestHandler(deps2) {
32282
33223
  ...deps2.agentHookUrl ? { agentHookUrl: deps2.agentHookUrl } : {},
32283
33224
  ...deps2.hubAddress ? { hubAddress: deps2.hubAddress } : {}
32284
33225
  };
32285
- const pty2 = createPtyHandlers(
33226
+ const cliReady = deps2.cliRelayReady?.then((relay) => {
33227
+ ctx.cliRelay = relay;
33228
+ });
33229
+ void cliReady?.catch(() => log("CLI relay is unavailable"));
33230
+ const pty2 = createPtyHandlers(ctx, deps2.getProjectPaths, {
33231
+ ...deps2.admission ? { admission: deps2.admission } : {},
33232
+ ...deps2.pathReady ? { pathReady: deps2.pathReady } : {}
33233
+ });
33234
+ const proc = createProcHandlers(
32286
33235
  ctx,
32287
33236
  deps2.getProjectPaths,
32288
33237
  deps2.admission ? { admission: deps2.admission } : {}
32289
33238
  );
32290
- const proc = createProcHandlers(ctx, deps2.getProjectPaths);
32291
- const fs7 = createFsHandlers(ctx);
33239
+ const fs8 = createFsHandlers(ctx);
32292
33240
  const shutdownHandler = async (params) => {
32293
33241
  const parsed = computerShutdownParamsSchema.safeParse(params ?? {});
32294
33242
  const reason = parsed.success ? parsed.data.reason ?? "hub-requested" : "hub-requested";
@@ -32300,9 +33248,10 @@ function createHubRequestHandler(deps2) {
32300
33248
  ...pty2.handlers,
32301
33249
  ...proc.handlers,
32302
33250
  ...createGitHandlers(ctx),
32303
- ...fs7.handlers,
33251
+ ...fs8.handlers,
32304
33252
  ...createComputerConfigHandlers(ctx),
32305
33253
  ...createGhHandlers(ctx),
33254
+ ...createClaudeSessionHandlers(ctx, deps2.getProjectPaths ?? (() => null)),
32306
33255
  ...createProjectHandlers(ctx, deps2.getProjectPaths ?? (() => null))
32307
33256
  };
32308
33257
  const hub = deps2.hubHost ?? "unknown";
@@ -32337,6 +33286,8 @@ function createHubRequestHandler(deps2) {
32337
33286
  throw err;
32338
33287
  }
32339
33288
  try {
33289
+ if (method === HubToComputerMethods.ptySpawn || method === HubToComputerMethods.ptyWarmSpawn || method === HubToComputerMethods.procSpawn)
33290
+ await cliReady;
32340
33291
  if (deps2.pathReady && !ptyMethods.has(method)) await deps2.pathReady;
32341
33292
  const result = await entry(params);
32342
33293
  record2("ok");
@@ -32360,7 +33311,7 @@ function createHubRequestHandler(deps2) {
32360
33311
  const dispose = () => {
32361
33312
  pty2.disposeAll();
32362
33313
  proc.disposeAll();
32363
- fs7.disposeAll();
33314
+ fs8.disposeAll();
32364
33315
  };
32365
33316
  const setAgentHookUrl = (url2) => {
32366
33317
  ctx.agentHookUrl = url2;
@@ -32389,14 +33340,14 @@ function startComputer(config2, deps2 = {}) {
32389
33340
  });
32390
33341
  });
32391
33342
  let handle;
32392
- const stateDir = deps2.stateDir ?? (0, import_node_path14.dirname)(getComputerConfigFilePath());
32393
- const configFile = (0, import_node_path14.join)(stateDir, "computer.config.json");
33343
+ const stateDir = deps2.stateDir ?? (0, import_node_path17.dirname)(getComputerConfigFilePath());
33344
+ const configFile = (0, import_node_path17.join)(stateDir, "computer.config.json");
32394
33345
  setProtectedPaths([
32395
33346
  configFile,
32396
33347
  // The legacy file computer.config.json falls back to when absent.
32397
- (0, import_node_path14.join)(stateDir, "config.json"),
33348
+ (0, import_node_path17.join)(stateDir, "config.json"),
32398
33349
  credentialsFilePath(stateDir),
32399
- (0, import_node_path14.join)(stateDir, REQUEST_LOG_DIR_NAME)
33350
+ (0, import_node_path17.join)(stateDir, REQUEST_LOG_DIR_NAME)
32400
33351
  ]);
32401
33352
  const requestLog = createRequestLog({
32402
33353
  stateDir,
@@ -32405,7 +33356,7 @@ function startComputer(config2, deps2 = {}) {
32405
33356
  log("local request log", { file: requestLog.filePath });
32406
33357
  const watchLocalRoots = !config2.hubMayEditRoots;
32407
33358
  if (watchLocalRoots) {
32408
- (0, import_node_fs11.watchFile)(configFile, { interval: 2e3, persistent: false }, () => {
33359
+ (0, import_node_fs14.watchFile)(configFile, { interval: 2e3, persistent: false }, () => {
32409
33360
  const declared = loadComputerConfigFile(configFile).allowedRoots;
32410
33361
  const applied = reloadLocalRoots(config2, declared);
32411
33362
  if (!applied) return;
@@ -32441,7 +33392,10 @@ function startComputer(config2, deps2 = {}) {
32441
33392
  count: store.list().length
32442
33393
  });
32443
33394
  }).catch((err) => log("project path map failed to load", { hubUrl, error: String(err) }));
33395
+ const cliRelayReady = createComputerCliRelay(dialer);
33396
+ void cliRelayReady.catch(() => log("CLI relay failed to start", { hubUrl }));
32444
33397
  dispatch = createHubRequestHandler({
33398
+ cliRelayReady,
32445
33399
  shutdown: (reason) => {
32446
33400
  log("computer shutdown requested by hub", { hubUrl, reason });
32447
33401
  void handle.stop().then(() => deps2.onShutdown?.(reason));
@@ -32462,6 +33416,11 @@ function startComputer(config2, deps2 = {}) {
32462
33416
  ({ computerId, mode }) => log("connected to hub", { hubUrl, computerId, mode })
32463
33417
  );
32464
33418
  dialer.events.on("disconnected", ({ reason }) => {
33419
+ void cliRelayReady.then(
33420
+ (relay) => relay.disconnect(),
33421
+ () => {
33422
+ }
33423
+ );
32465
33424
  log("disconnected from hub", { hubUrl, reason });
32466
33425
  dispatch.hubDisconnected(String(reason));
32467
33426
  });
@@ -32490,6 +33449,11 @@ function startComputer(config2, deps2 = {}) {
32490
33449
  dialer,
32491
33450
  dispose: async () => {
32492
33451
  dispatch.dispose();
33452
+ await cliRelayReady.then(
33453
+ (relay) => relay.close(),
33454
+ () => {
33455
+ }
33456
+ );
32493
33457
  if (agentHookServer) await agentHookServer.close();
32494
33458
  await dialer.stop();
32495
33459
  }
@@ -32505,7 +33469,7 @@ function startComputer(config2, deps2 = {}) {
32505
33469
  dialers: connections.map((c) => c.dialer),
32506
33470
  stop: async () => {
32507
33471
  hostIdStop?.();
32508
- if (watchLocalRoots) (0, import_node_fs11.unwatchFile)(configFile);
33472
+ if (watchLocalRoots) (0, import_node_fs14.unwatchFile)(configFile);
32509
33473
  await Promise.all(connections.map((c) => c.dispose()));
32510
33474
  }
32511
33475
  };
@@ -32547,7 +33511,7 @@ async function maybeInteractiveSetup() {
32547
33511
  default: defaultRoot,
32548
33512
  transform: (raw) => {
32549
33513
  const roots = raw.split(",").map((r) => r.trim()).filter((r) => r.length > 0);
32550
- return roots.length > 0 ? { config: roots, env: roots.join(import_node_path15.delimiter) } : null;
33514
+ return roots.length > 0 ? { config: roots, env: roots.join(import_node_path18.delimiter) } : null;
32551
33515
  }
32552
33516
  },
32553
33517
  {
@@ -32559,7 +33523,7 @@ async function maybeInteractiveSetup() {
32559
33523
  configKey: "computerName",
32560
33524
  envKey: "SLAYZONE_COMPUTER_NAME",
32561
33525
  label: "Computer display name",
32562
- hint: `default: ${(0, import_node_os9.hostname)()}`
33526
+ hint: `default: ${(0, import_node_os11.hostname)()}`
32563
33527
  }
32564
33528
  ]
32565
33529
  });