@herdingchats/friday 0.1.651 → 0.1.660

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/herd.mjs +510 -492
  2. package/package.json +2 -2
  3. package/terminal-host.mjs +37 -3
package/herd.mjs CHANGED
@@ -18,7 +18,7 @@ var __export = (target, all) => {
18
18
  var define_HERD_PACKAGE_default;
19
19
  var init_define_HERD_PACKAGE = __esm({
20
20
  "<define:HERD_PACKAGE>"() {
21
- define_HERD_PACKAGE_default = { version: "0.1.651", gitHead: "60dd42f8309cea2c6700c788c92ef3732e1d517c" };
21
+ define_HERD_PACKAGE_default = { version: "0.1.660", gitHead: "21101252ff98a4dc317139a36c286b347f5f5933" };
22
22
  }
23
23
  });
24
24
 
@@ -3208,10 +3208,10 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
3208
3208
  // src/friday/main.ts
3209
3209
  init_define_HERD_PACKAGE();
3210
3210
  init_src();
3211
- import { spawn as spawn7 } from "node:child_process";
3211
+ import { spawn as spawn8 } from "node:child_process";
3212
3212
  import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
3213
3213
  import { homedir as homedir5 } from "node:os";
3214
- import { dirname as dirname10, join as join15 } from "node:path";
3214
+ import { dirname as dirname10, join as join16 } from "node:path";
3215
3215
  import { createInterface } from "node:readline/promises";
3216
3216
  import { z as z22 } from "zod";
3217
3217
 
@@ -3414,7 +3414,7 @@ var HERD_FLAGS = {
3414
3414
  login: [],
3415
3415
  logout: [],
3416
3416
  daemon: ["--repo"],
3417
- attach: [],
3417
+ attach: ["--watch"],
3418
3418
  builds: [],
3419
3419
  inbox: [],
3420
3420
  send: ["--json"],
@@ -4519,15 +4519,15 @@ async function readSetup(fetch2, init) {
4519
4519
  // src/friday/agents.ts
4520
4520
  init_define_HERD_PACKAGE();
4521
4521
  init_src();
4522
- import { existsSync as existsSync5, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
4523
- import { dirname as dirname6, join as join8 } from "node:path";
4522
+ import { existsSync as existsSync5, mkdirSync as mkdirSync7, writeFileSync as writeFileSync5 } from "node:fs";
4523
+ import { dirname as dirname6, join as join9 } from "node:path";
4524
4524
 
4525
4525
  // src/activation.ts
4526
4526
  init_define_HERD_PACKAGE();
4527
4527
  init_src();
4528
4528
  import { randomBytes as randomBytes4, randomUUID as randomUUID3 } from "node:crypto";
4529
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync as renameSync2, rmSync as rmSync5, statSync as statSync4, writeFileSync as writeFileSync4 } from "node:fs";
4530
- import { dirname as dirname5, join as join7 } from "node:path";
4529
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, renameSync as renameSync2, rmSync as rmSync6, statSync as statSync4, writeFileSync as writeFileSync4 } from "node:fs";
4530
+ import { dirname as dirname5, join as join8 } from "node:path";
4531
4531
  import { z as z21 } from "zod";
4532
4532
 
4533
4533
  // src/permissions.ts
@@ -4930,6 +4930,243 @@ async function permissionHook(raw, options = {}) {
4930
4930
  }
4931
4931
  }
4932
4932
 
4933
+ // src/terminals.ts
4934
+ init_define_HERD_PACKAGE();
4935
+ import { spawn as spawn4 } from "node:child_process";
4936
+ import { mkdirSync as mkdirSync5, readdirSync as readdirSync3, rmSync as rmSync5 } from "node:fs";
4937
+ import { connect as connect2 } from "node:net";
4938
+ import { join as join7 } from "node:path";
4939
+ init_self();
4940
+ import {
4941
+ HOST_PROTOCOL,
4942
+ hostVersion
4943
+ } from "./terminal-host.mjs";
4944
+ var hostsDir = () => join7(herdHome(), "terminals");
4945
+ var legacyHostSocketPath = () => join7(herdHome(), "terminals.sock");
4946
+ var currentHostSocketPath = (version = hostVersion()) => join7(hostsDir(), `${version}.sock`);
4947
+ var speaksProtocol = (protocol) => protocol === HOST_PROTOCOL || protocol === HOST_PROTOCOL - 1;
4948
+ var sleep = (ms2) => new Promise((resolve8) => setTimeout(resolve8, ms2));
4949
+ function askHost(path, request) {
4950
+ return new Promise((resolve8, reject) => {
4951
+ const socket = connect2(path);
4952
+ let data = "";
4953
+ socket.on("connect", () => socket.write(`${JSON.stringify(request)}
4954
+ `));
4955
+ socket.on("data", (chunk) => data += chunk);
4956
+ socket.on("end", () => {
4957
+ try {
4958
+ const reply = JSON.parse(data);
4959
+ if (reply.ok) resolve8(reply);
4960
+ else reject(new HerdError(reply.error ?? "The terminal host refused that."));
4961
+ } catch {
4962
+ reject(new HerdError("The terminal host's answer wasn't readable."));
4963
+ }
4964
+ });
4965
+ socket.on("error", reject);
4966
+ });
4967
+ }
4968
+ var isDead = (error) => {
4969
+ const code = error?.code;
4970
+ return code === "ECONNREFUSED" || code === "ENOENT";
4971
+ };
4972
+ var HostTerminals = class {
4973
+ hosts = [];
4974
+ /** Which host each terminal name was last found in. */
4975
+ where = /* @__PURE__ */ new Map();
4976
+ version;
4977
+ log;
4978
+ constructor(options = {}) {
4979
+ this.version = options.version ?? hostVersion();
4980
+ this.log = options.log ?? (() => {
4981
+ });
4982
+ }
4983
+ currentVersion() {
4984
+ return this.version;
4985
+ }
4986
+ unreachable(version) {
4987
+ return this.hosts.some((host) => host.hello.version === version && !host.speaks);
4988
+ }
4989
+ /** Every host that answers, with sockets nothing answers on removed. */
4990
+ async discover() {
4991
+ const dir = hostsDir();
4992
+ let names = [];
4993
+ try {
4994
+ names = readdirSync3(dir).filter((name) => name.endsWith(".sock"));
4995
+ } catch {
4996
+ }
4997
+ const paths = [legacyHostSocketPath(), ...names.map((name) => join7(dir, name))];
4998
+ const found = [];
4999
+ for (const path of paths) {
5000
+ try {
5001
+ const hello = await askHost(path, { op: "hello" });
5002
+ found.push({
5003
+ path,
5004
+ hello,
5005
+ current: hello.version === this.version && speaksProtocol(hello.protocol),
5006
+ speaks: speaksProtocol(hello.protocol)
5007
+ });
5008
+ } catch (error) {
5009
+ if (isDead(error) && path !== legacyHostSocketPath()) rmSync5(path, { force: true });
5010
+ }
5011
+ }
5012
+ this.hosts = found;
5013
+ return found;
5014
+ }
5015
+ current() {
5016
+ return this.hosts.find((host) => host.current);
5017
+ }
5018
+ /** Starts a host of the daemon's own version if none is running, detached so it outlives it. */
5019
+ async ensureHost() {
5020
+ const hosts = await this.discover();
5021
+ for (const host of hosts) {
5022
+ const version = host.hello.version ?? "unversioned";
5023
+ const role = host.current ? "current" : host.speaks ? "older" : "not driveable";
5024
+ this.log(
5025
+ `Terminal host ${version} (protocol ${host.hello.protocol}, pid ${host.hello.pid}): ${role}.`
5026
+ );
5027
+ }
5028
+ if (this.current()) return;
5029
+ const path = currentHostSocketPath(this.version);
5030
+ mkdirSync5(hostsDir(), { recursive: true, mode: 448 });
5031
+ const child = spawn4(process.execPath, [herdBin, "terminal-host", "--socket", path], {
5032
+ detached: true,
5033
+ stdio: "ignore",
5034
+ env: { ...process.env, HERD_HOME: herdHome() }
5035
+ });
5036
+ child.unref();
5037
+ for (let attempt = 0; attempt < 50 && !this.current(); attempt++) {
5038
+ await sleep(100);
5039
+ const hello = await askHost(path, { op: "hello" }).catch(() => null);
5040
+ if (!hello) continue;
5041
+ if (hello.version !== this.version) {
5042
+ throw new HerdError(
5043
+ `The terminal host started as version ${hello.version}, not ${this.version}, so its files changed as it started. Starting the daemon again picks this up.`
5044
+ );
5045
+ }
5046
+ this.hosts.push({ path, hello, current: true, speaks: speaksProtocol(hello.protocol) });
5047
+ }
5048
+ if (!this.current()) throw new HerdError("The terminal host didn't start.");
5049
+ this.log(`Started terminal host ${this.version}.`);
5050
+ }
5051
+ /**
5052
+ * Every driveable host's terminals. A name in more than one host, as when a session moved, is
5053
+ * the running one, or else the newest.
5054
+ */
5055
+ async list() {
5056
+ if (this.hosts.length === 0) await this.discover();
5057
+ const byName = /* @__PURE__ */ new Map();
5058
+ for (const host of this.hosts.filter((candidate) => candidate.speaks)) {
5059
+ const terminals = await askHost(host.path, { op: "list" }).then((reply) => reply.terminals).catch(() => []);
5060
+ for (const info of terminals) {
5061
+ const seen = byName.get(info.name);
5062
+ const better = !seen || seen.info.exited && !info.exited || seen.info.exited === info.exited && info.createdAt > seen.info.createdAt;
5063
+ if (better) byName.set(info.name, { info, path: host.path });
5064
+ }
5065
+ }
5066
+ this.where = new Map([...byName].map(([name, { path }]) => [name, path]));
5067
+ return [...byName.values()].map(({ info }) => info);
5068
+ }
5069
+ /** The socket of the host holding a terminal. */
5070
+ async pathOf(name) {
5071
+ let path = this.where.get(name);
5072
+ if (!path) {
5073
+ await this.list();
5074
+ path = this.where.get(name);
5075
+ }
5076
+ if (!path) throw new HerdError(`There's no terminal named ${name}.`);
5077
+ return path;
5078
+ }
5079
+ async askFor(name, request) {
5080
+ return askHost(await this.pathOf(name), request);
5081
+ }
5082
+ async create(opts) {
5083
+ const running = (await this.list()).find((t) => t.name === opts.name && !t.exited);
5084
+ if (running) throw new HerdError(`A terminal named ${opts.name} is already running.`);
5085
+ let host = this.current();
5086
+ if (!host) {
5087
+ await this.ensureHost();
5088
+ host = this.current();
5089
+ }
5090
+ if (!host) throw new HerdError("The terminal host didn't start.");
5091
+ const { terminal } = await askHost(host.path, {
5092
+ op: "create",
5093
+ ...opts
5094
+ });
5095
+ this.where.set(opts.name, host.path);
5096
+ return terminal;
5097
+ }
5098
+ async screen(name) {
5099
+ return (await this.askFor(name, { op: "screen", name })).screen;
5100
+ }
5101
+ async snapshot(name) {
5102
+ return (await this.askFor(name, { op: "snapshot", name })).snapshot;
5103
+ }
5104
+ async type(name, text4) {
5105
+ await this.askFor(name, { op: "type", name, text: text4 });
5106
+ }
5107
+ async press(name, key) {
5108
+ await this.askFor(name, { op: "press", name, key });
5109
+ }
5110
+ async exitStatus(name) {
5111
+ const terminal = (await this.list()).find((candidate) => candidate.name === name);
5112
+ if (!terminal) throw new HerdError(`There's no terminal named ${name}.`);
5113
+ return terminal.exited ? terminal.exitCode : null;
5114
+ }
5115
+ async kill(name) {
5116
+ await this.askFor(name, { op: "kill", name });
5117
+ }
5118
+ async retire(canStop) {
5119
+ const stopped2 = [];
5120
+ for (const host of await this.discover()) {
5121
+ if (host.current) continue;
5122
+ const version = host.hello.version ?? "unversioned";
5123
+ if (host.speaks) {
5124
+ const terminals = await askHost(host.path, { op: "list" }).then((reply) => reply.terminals).catch(() => null);
5125
+ if (!terminals) continue;
5126
+ if (terminals.some((t) => !t.exited || (t.viewers ?? 0) > 0)) continue;
5127
+ } else if (!host.hello.version || !canStop(host.hello.version)) {
5128
+ continue;
5129
+ }
5130
+ if (await this.stop(host)) {
5131
+ stopped2.push(version);
5132
+ this.log(`Retired terminal host ${version}, which had nothing left running.`);
5133
+ }
5134
+ }
5135
+ if (stopped2.length > 0) await this.discover();
5136
+ return stopped2;
5137
+ }
5138
+ /** Stops a host: politely where it knows how, and otherwise by its process. */
5139
+ async stop(host) {
5140
+ if (host.speaks && host.hello.ops?.includes("shutdown")) {
5141
+ return askHost(host.path, { op: "shutdown" }).then(
5142
+ () => true,
5143
+ () => false
5144
+ );
5145
+ }
5146
+ const hello = await askHost(host.path, { op: "hello" }).catch(() => null);
5147
+ if (!hello || hello.pid !== host.hello.pid) return false;
5148
+ try {
5149
+ process.kill(hello.pid, "SIGTERM");
5150
+ } catch {
5151
+ return false;
5152
+ }
5153
+ if (host.path === legacyHostSocketPath()) {
5154
+ for (let attempt = 0; attempt < 20; attempt++) {
5155
+ await sleep(100);
5156
+ if (!await askHost(host.path, { op: "hello" }).catch(() => null)) break;
5157
+ }
5158
+ rmSync5(host.path, { force: true });
5159
+ }
5160
+ return true;
5161
+ }
5162
+ };
5163
+ var DRIVER_QUIET_MS = 10 * 1e3;
5164
+ function someoneDriving(terminal, quietMs = DRIVER_QUIET_MS, now = Date.now()) {
5165
+ if (!terminal || (terminal.viewers ?? 0) === 0 || !terminal.drivenAt) return false;
5166
+ return now - terminal.drivenAt < quietMs;
5167
+ }
5168
+ var lookingAt = (terminal) => (terminal?.viewers ?? 0) + (terminal?.watchers ?? 0);
5169
+
4933
5170
  // src/timing.ts
4934
5171
  init_define_HERD_PACKAGE();
4935
5172
  var CHECK_IN_MS = 15e3;
@@ -4943,6 +5180,7 @@ var VARIABLES = {
4943
5180
  agyIdleSettleMs: "HERD_AGY_IDLE_SETTLE_MS",
4944
5181
  promptPollMs: "HERD_PROMPT_POLL_MS",
4945
5182
  noticeRetryMs: "HERD_NOTICE_RETRY_MS",
5183
+ driverQuietMs: "HERD_DRIVER_QUIET_MS",
4946
5184
  investigateAfterMs: "HERD_INVESTIGATE_AFTER_MS",
4947
5185
  idleMs: "HERD_IDLE_MS",
4948
5186
  watchMs: "HERD_WATCH_MS",
@@ -4986,14 +5224,14 @@ function newActivationCode() {
4986
5224
  return [...randomBytes4(8)].map((byte) => CODE_LETTERS[byte % CODE_LETTERS.length]).join("");
4987
5225
  }
4988
5226
  var activationTerminal = (agent) => `herd-activate-${agentShortName(agent)}`;
4989
- var activationFolder = (root, agent) => join7(convosHome(root), ".activate", agentShortName(agent));
5227
+ var activationFolder = (root, agent) => join8(convosHome(root), ".activate", agentShortName(agent));
4990
5228
  var recordSchema = agentActivationSchema.extend({
4991
5229
  /** While it's waiting: the terminal it runs in, which `friday start` can attach to. */
4992
5230
  terminal: z21.string().optional()
4993
5231
  });
4994
5232
  var storeSchema = z21.object({ agents: z21.record(z21.string(), recordSchema) });
4995
- var activationsPath = (home = herdHome()) => join7(home, "activations.json");
4996
- var activationRequestPath = (home = herdHome()) => join7(home, "activate-request");
5233
+ var activationsPath = (home = herdHome()) => join8(home, "activations.json");
5234
+ var activationRequestPath = (home = herdHome()) => join8(home, "activate-request");
4997
5235
  function readActivations(path = activationsPath()) {
4998
5236
  try {
4999
5237
  return storeSchema.parse(JSON.parse(readFileSync5(path, "utf8"))).agents;
@@ -5002,7 +5240,7 @@ function readActivations(path = activationsPath()) {
5002
5240
  }
5003
5241
  }
5004
5242
  function requestActivation(home = herdHome(), now = Date.now()) {
5005
- mkdirSync5(home, { recursive: true, mode: 448 });
5243
+ mkdirSync6(home, { recursive: true, mode: 448 });
5006
5244
  writeFileSync4(activationRequestPath(home), String(now), { mode: 384 });
5007
5245
  }
5008
5246
  function needsActivation(record, version) {
@@ -5019,7 +5257,7 @@ function activateDecision(request, code) {
5019
5257
  }
5020
5258
  };
5021
5259
  }
5022
- var sleep = (ms2) => new Promise((resolve8) => setTimeout(resolve8, ms2));
5260
+ var sleep2 = (ms2) => new Promise((resolve8) => setTimeout(resolve8, ms2));
5023
5261
  var label = (agent) => AGENTS[agent]?.label ?? agent;
5024
5262
  var Activator = class {
5025
5263
  constructor(options) {
@@ -5047,7 +5285,7 @@ var Activator = class {
5047
5285
  }
5048
5286
  record(agent, record) {
5049
5287
  const agents = { ...readActivations(this.path), [agent]: record };
5050
- mkdirSync5(dirname5(this.path), { recursive: true, mode: 448 });
5288
+ mkdirSync6(dirname5(this.path), { recursive: true, mode: 448 });
5051
5289
  const temporary = `${this.path}.${process.pid}`;
5052
5290
  writeFileSync4(temporary, JSON.stringify({ agents }, null, 2), { mode: 384 });
5053
5291
  renameSync2(temporary, this.path);
@@ -5094,7 +5332,7 @@ var Activator = class {
5094
5332
  this.log(`Activating ${label(next.agent)} once its conversations here settle.`);
5095
5333
  }
5096
5334
  while (this.options.busy(next.agent) && !this.stopped) {
5097
- await sleep(this.options.busyPollMs ?? BUSY_POLL_MS);
5335
+ await sleep2(this.options.busyPollMs ?? BUSY_POLL_MS);
5098
5336
  }
5099
5337
  if (this.stopped) break;
5100
5338
  await this.activate(next.agent, next.version).catch(
@@ -5140,8 +5378,8 @@ var Activator = class {
5140
5378
  const code = newActivationCode();
5141
5379
  const terminal = activationTerminal(agent);
5142
5380
  const folder = activationFolder(this.options.root(), agent);
5143
- rmSync5(folder, { recursive: true, force: true });
5144
- mkdirSync5(folder, { recursive: true });
5381
+ rmSync6(folder, { recursive: true, force: true });
5382
+ mkdirSync6(folder, { recursive: true });
5145
5383
  const current = { agent, code, terminal, checkedIn: false, wake: () => {
5146
5384
  } };
5147
5385
  this.current = current;
@@ -5167,7 +5405,7 @@ var Activator = class {
5167
5405
  );
5168
5406
  if (open) await terminals.kill(terminal).catch(() => {
5169
5407
  });
5170
- rmSync5(folder, { recursive: true, force: true });
5408
+ rmSync6(folder, { recursive: true, force: true });
5171
5409
  }
5172
5410
  this.record(agent, outcome);
5173
5411
  this.log(
@@ -5228,7 +5466,9 @@ var Activator = class {
5228
5466
  terminal: current.terminal
5229
5467
  });
5230
5468
  }
5231
- const viewers = (await terminals.list()).find((t) => t.name === current.terminal)?.viewers ?? 0;
5469
+ const viewers = lookingAt(
5470
+ (await terminals.list()).find((t) => t.name === current.terminal)
5471
+ );
5232
5472
  if (viewers === 0 && Date.now() - startedAt >= restartMs) await start();
5233
5473
  } else if (reading?.state === "idle" && reading.program !== "shell" && !prompted) {
5234
5474
  prompted = true;
@@ -5330,7 +5570,7 @@ async function chooseAgents(options, print) {
5330
5570
  Object.fromEntries(choices.map((choice) => [choice.cli, choice.share]));
5331
5571
  const choosing = options.ask || options.given !== void 0;
5332
5572
  if (choosing && (!sameChoice(saved, shares) || !existsSync5(options.configPath))) {
5333
- mkdirSync6(dirname6(options.configPath), { recursive: true, mode: 448 });
5573
+ mkdirSync7(dirname6(options.configPath), { recursive: true, mode: 448 });
5334
5574
  writeFileSync5(options.configPath, configSource(withChoice(saved, shares)), { mode: 384 });
5335
5575
  }
5336
5576
  const chosen = ADAPTERS.map(({ cli }) => cli).filter((cli) => (shares[cli] ?? "off") !== "off");
@@ -5344,7 +5584,7 @@ function askToActivate(herdHome2, now = Date.now()) {
5344
5584
  requestActivation(herdHome2, now);
5345
5585
  return now;
5346
5586
  }
5347
- var sleep2 = (ms2) => new Promise((resolve8) => setTimeout(resolve8, ms2));
5587
+ var sleep3 = (ms2) => new Promise((resolve8) => setTimeout(resolve8, ms2));
5348
5588
  function settledLine(cli, activation) {
5349
5589
  if (activation.state === "activated") return `${label2(cli)} is activated.`;
5350
5590
  return `${label2(cli)} ${activationText(cli, activation).replace(" there", " on this computer")}.`;
@@ -5395,11 +5635,11 @@ async function awaitActivations(options, chosen, since, print) {
5395
5635
  );
5396
5636
  break;
5397
5637
  }
5398
- await sleep2(pollMs);
5638
+ await sleep3(pollMs);
5399
5639
  }
5400
5640
  return settled;
5401
5641
  }
5402
- var herdConfigIn = (herdHome2) => join8(herdHome2, "herd.config.ts");
5642
+ var herdConfigIn = (herdHome2) => join9(herdHome2, "herd.config.ts");
5403
5643
 
5404
5644
  // src/friday/home.ts
5405
5645
  init_define_HERD_PACKAGE();
@@ -5408,32 +5648,32 @@ import { spawnSync } from "node:child_process";
5408
5648
  import {
5409
5649
  closeSync as closeSync2,
5410
5650
  existsSync as existsSync6,
5411
- mkdirSync as mkdirSync7,
5651
+ mkdirSync as mkdirSync8,
5412
5652
  openSync as openSync2,
5413
5653
  readFileSync as readFileSync6,
5414
- rmSync as rmSync6,
5654
+ rmSync as rmSync7,
5415
5655
  statSync as statSync5,
5416
5656
  writeFileSync as writeFileSync6
5417
5657
  } from "node:fs";
5418
- import { dirname as dirname7, join as join9, resolve as resolve2 } from "node:path";
5658
+ import { dirname as dirname7, join as join10, resolve as resolve2 } from "node:path";
5419
5659
  var PACKAGE_NAME = FRIDAY_PACKAGE;
5420
5660
  function fridayPaths(herdHome2) {
5421
- const logs = join9(herdHome2, "logs");
5661
+ const logs = join10(herdHome2, "logs");
5422
5662
  return {
5423
5663
  herdHome: herdHome2,
5424
- config: join9(herdHome2, "config.json"),
5425
- versions: join9(herdHome2, "versions"),
5426
- current: join9(herdHome2, "current"),
5427
- pin: join9(herdHome2, "pin"),
5664
+ config: join10(herdHome2, "config.json"),
5665
+ versions: join10(herdHome2, "versions"),
5666
+ current: join10(herdHome2, "current"),
5667
+ pin: join10(herdHome2, "pin"),
5428
5668
  logs,
5429
- daemonLog: join9(logs, "daemon.log"),
5430
- supervisorLog: join9(logs, "supervisor.log"),
5431
- updateLock: join9(herdHome2, "update.lock"),
5432
- status: join9(herdHome2, "supervisor.json"),
5433
- terminals: join9(herdHome2, "terminals")
5669
+ daemonLog: join10(logs, "daemon.log"),
5670
+ supervisorLog: join10(logs, "supervisor.log"),
5671
+ updateLock: join10(herdHome2, "update.lock"),
5672
+ status: join10(herdHome2, "supervisor.json"),
5673
+ terminals: join10(herdHome2, "terminals")
5434
5674
  };
5435
5675
  }
5436
- var packageDir = (versionDir) => join9(versionDir, "node_modules", ...PACKAGE_NAME.split("/"));
5676
+ var packageDir = (versionDir) => join10(versionDir, "node_modules", ...PACKAGE_NAME.split("/"));
5437
5677
  function alive2(pid) {
5438
5678
  if (!Number.isInteger(pid) || pid <= 0) return false;
5439
5679
  try {
@@ -5444,11 +5684,11 @@ function alive2(pid) {
5444
5684
  }
5445
5685
  }
5446
5686
  function takeLock(path, staleMs = 10 * 6e4) {
5447
- mkdirSync7(dirname7(path), { recursive: true });
5687
+ mkdirSync8(dirname7(path), { recursive: true });
5448
5688
  if (existsSync6(path)) {
5449
5689
  const holder2 = Number(readFileSync6(path, "utf8").trim());
5450
5690
  const old = Date.now() - statSync5(path).mtimeMs > staleMs;
5451
- if (old || !alive2(holder2)) rmSync6(path, { force: true });
5691
+ if (old || !alive2(holder2)) rmSync7(path, { force: true });
5452
5692
  }
5453
5693
  try {
5454
5694
  const fd = openSync2(path, "wx");
@@ -5457,7 +5697,7 @@ function takeLock(path, staleMs = 10 * 6e4) {
5457
5697
  } catch {
5458
5698
  return null;
5459
5699
  }
5460
- return () => rmSync6(path, { force: true });
5700
+ return () => rmSync7(path, { force: true });
5461
5701
  }
5462
5702
  function readStatus(paths) {
5463
5703
  try {
@@ -5467,7 +5707,7 @@ function readStatus(paths) {
5467
5707
  }
5468
5708
  }
5469
5709
  function writeStatus(paths, status2) {
5470
- mkdirSync7(paths.herdHome, { recursive: true, mode: 448 });
5710
+ mkdirSync8(paths.herdHome, { recursive: true, mode: 448 });
5471
5711
  writeFileSync6(paths.status, `${JSON.stringify(status2, null, 2)}
5472
5712
  `);
5473
5713
  }
@@ -5490,7 +5730,7 @@ function writeRoot(paths, root) {
5490
5730
  } catch {
5491
5731
  config = {};
5492
5732
  }
5493
- mkdirSync7(paths.herdHome, { recursive: true, mode: 448 });
5733
+ mkdirSync8(paths.herdHome, { recursive: true, mode: 448 });
5494
5734
  writeFileSync6(paths.config, JSON.stringify({ ...config, root }, null, 2), { mode: 384 });
5495
5735
  }
5496
5736
  function insideWorkTree(folder) {
@@ -5583,10 +5823,10 @@ function pickInTerminal(choices, input = process.stdin, output2 = process.stdout
5583
5823
 
5584
5824
  // src/friday/service.ts
5585
5825
  init_define_HERD_PACKAGE();
5586
- import { spawn as spawn4 } from "node:child_process";
5587
- import { existsSync as existsSync7, mkdirSync as mkdirSync8, openSync as openSync3, readFileSync as readFileSync7, rmSync as rmSync7, writeFileSync as writeFileSync7 } from "node:fs";
5826
+ import { spawn as spawn5 } from "node:child_process";
5827
+ import { existsSync as existsSync7, mkdirSync as mkdirSync9, openSync as openSync3, readFileSync as readFileSync7, rmSync as rmSync8, writeFileSync as writeFileSync7 } from "node:fs";
5588
5828
  import { userInfo } from "node:os";
5589
- import { dirname as dirname8, join as join10, win32 } from "node:path";
5829
+ import { dirname as dirname8, join as join11, win32 } from "node:path";
5590
5830
  var systemdBooted = () => existsSync7("/run/systemd/system");
5591
5831
  var LAUNCH_AGENT = "com.herdingchats.friday";
5592
5832
  var SYSTEMD_UNIT = "herdingchats-friday.service";
@@ -5674,16 +5914,16 @@ WantedBy=default.target
5674
5914
  `;
5675
5915
  }
5676
5916
  function makeLogFolder(context2) {
5677
- mkdirSync8(dirname8(context2.paths.supervisorLog), { recursive: true });
5917
+ mkdirSync9(dirname8(context2.paths.supervisorLog), { recursive: true });
5678
5918
  }
5679
5919
  function writeIfChanged(path, text4) {
5680
5920
  if (existsSync7(path) && readFileSync7(path, "utf8") === text4) return false;
5681
- mkdirSync8(dirname8(path), { recursive: true });
5921
+ mkdirSync9(dirname8(path), { recursive: true });
5682
5922
  writeFileSync7(path, text4);
5683
5923
  return true;
5684
5924
  }
5685
5925
  function launchd(context2) {
5686
- const plist = join10(context2.home, "Library", "LaunchAgents", `${LAUNCH_AGENT}.plist`);
5926
+ const plist = join11(context2.home, "Library", "LaunchAgents", `${LAUNCH_AGENT}.plist`);
5687
5927
  const domain = `gui/${userInfo().uid}`;
5688
5928
  const target = `${domain}/${LAUNCH_AGENT}`;
5689
5929
  const loaded = async () => (await context2.run("launchctl", ["print", target])).status === 0;
@@ -5707,12 +5947,12 @@ function launchd(context2) {
5707
5947
  },
5708
5948
  async remove() {
5709
5949
  await context2.run("launchctl", ["bootout", target]);
5710
- rmSync7(plist, { force: true });
5950
+ rmSync8(plist, { force: true });
5711
5951
  }
5712
5952
  };
5713
5953
  }
5714
5954
  function systemd(context2) {
5715
- const unit = join10(context2.home, ".config", "systemd", "user", SYSTEMD_UNIT);
5955
+ const unit = join11(context2.home, ".config", "systemd", "user", SYSTEMD_UNIT);
5716
5956
  const systemctl = async (...args) => {
5717
5957
  const ran = await context2.run("systemctl", ["--user", ...args]);
5718
5958
  if (ran.status !== 0)
@@ -5734,15 +5974,15 @@ function systemd(context2) {
5734
5974
  },
5735
5975
  async remove() {
5736
5976
  await context2.run("systemctl", ["--user", "disable", "--now", SYSTEMD_UNIT]);
5737
- rmSync7(unit, { force: true });
5977
+ rmSync8(unit, { force: true });
5738
5978
  }
5739
5979
  };
5740
5980
  }
5741
5981
  function spawnInBackground(command, env, log) {
5742
- mkdirSync8(dirname8(log), { recursive: true });
5982
+ mkdirSync9(dirname8(log), { recursive: true });
5743
5983
  const out = openSync3(log, "a");
5744
5984
  const [program = process.execPath, ...args] = command;
5745
- spawn4(program, args, {
5985
+ spawn5(program, args, {
5746
5986
  detached: true,
5747
5987
  // On Windows, a detached process otherwise opens a console window of its own.
5748
5988
  windowsHide: true,
@@ -5794,35 +6034,35 @@ function serviceFor(kind, context2) {
5794
6034
  // src/friday/start.ts
5795
6035
  init_define_HERD_PACKAGE();
5796
6036
  init_src();
5797
- import { mkdirSync as mkdirSync10, realpathSync as realpathSync4 } from "node:fs";
5798
- import { join as join14, resolve as resolve3 } from "node:path";
6037
+ import { mkdirSync as mkdirSync11, realpathSync as realpathSync4 } from "node:fs";
6038
+ import { join as join15, resolve as resolve3 } from "node:path";
5799
6039
 
5800
6040
  // src/package-manager.ts
5801
6041
  init_define_HERD_PACKAGE();
5802
6042
  import { existsSync as existsSync9 } from "node:fs";
5803
6043
  import { homedir as homedir4 } from "node:os";
5804
- import { basename as basename3, delimiter, dirname as dirname9, isAbsolute as isAbsolute2, join as join12 } from "node:path";
6044
+ import { basename as basename3, delimiter, dirname as dirname9, isAbsolute as isAbsolute2, join as join13 } from "node:path";
5805
6045
 
5806
6046
  // src/friday/versions.ts
5807
6047
  init_define_HERD_PACKAGE();
5808
- import { spawn as spawn5 } from "node:child_process";
6048
+ import { spawn as spawn6 } from "node:child_process";
5809
6049
  import {
5810
6050
  existsSync as existsSync8,
5811
- mkdirSync as mkdirSync9,
5812
- readdirSync as readdirSync3,
6051
+ mkdirSync as mkdirSync10,
6052
+ readdirSync as readdirSync4,
5813
6053
  readFileSync as readFileSync8,
5814
6054
  readlinkSync,
5815
6055
  realpathSync as realpathSync3,
5816
6056
  renameSync as renameSync3,
5817
- rmSync as rmSync8,
6057
+ rmSync as rmSync9,
5818
6058
  symlinkSync,
5819
6059
  writeFileSync as writeFileSync8
5820
6060
  } from "node:fs";
5821
- import { basename as basename2, join as join11 } from "node:path";
6061
+ import { basename as basename2, join as join12 } from "node:path";
5822
6062
  var exec = (command, args, options = {}) => new Promise((resolve8) => {
5823
6063
  let stdout = "";
5824
6064
  let stderr = "";
5825
- const child = spawn5(command, args, {
6065
+ const child = spawn6(command, args, {
5826
6066
  cwd: options.cwd,
5827
6067
  env: options.env ?? process.env,
5828
6068
  stdio: ["ignore", "pipe", "pipe"]
@@ -5848,9 +6088,9 @@ function currentVersion(paths) {
5848
6088
  }
5849
6089
  function installedVersions(paths) {
5850
6090
  if (!existsSync8(paths.versions)) return [];
5851
- return readdirSync3(paths.versions).filter((name) => VERSION.test(name)).sort(compareVersions);
6091
+ return readdirSync4(paths.versions).filter((name) => VERSION.test(name)).sort(compareVersions);
5852
6092
  }
5853
- var binOf = (versionDir, bin) => join11(packageDir(versionDir), `${bin}.mjs`);
6093
+ var binOf = (versionDir, bin) => join12(packageDir(versionDir), `${bin}.mjs`);
5854
6094
  async function versionRuns(versionDir, version, run4 = exec) {
5855
6095
  const ran = await run4(process.execPath, [binOf(versionDir, "herd"), "--version"], {
5856
6096
  timeoutMs: 3e4
@@ -5866,7 +6106,7 @@ function recordedIntegrity(dir) {
5866
6106
  for (const file of LOCKFILES[manager]) {
5867
6107
  let lock;
5868
6108
  try {
5869
- lock = readFileSync8(join11(dir, file), "utf8");
6109
+ lock = readFileSync8(join12(dir, file), "utf8");
5870
6110
  } catch {
5871
6111
  continue;
5872
6112
  }
@@ -5884,7 +6124,7 @@ function npmFreeEnv() {
5884
6124
  async function installVersion(paths, version, { spec, integrity, run: run4 = exec, env = {}, log = () => {
5885
6125
  }, manager } = {}) {
5886
6126
  if (!VERSION.test(version)) return { ok: false, reason: `${version} isn't a version.` };
5887
- const dir = join11(paths.versions, version);
6127
+ const dir = join12(paths.versions, version);
5888
6128
  if (existsSync8(dir)) {
5889
6129
  const installed2 = recordedIntegrity(dir);
5890
6130
  if (integrity && installed2 !== integrity)
@@ -5900,19 +6140,19 @@ async function installVersion(paths, version, { spec, integrity, run: run4 = exe
5900
6140
  reason: "there's no package manager to install it with: herd looked for npm, pnpm and Yarn 1 beside Node, on the PATH and in their usual folders."
5901
6141
  };
5902
6142
  sweepStaging(paths);
5903
- const named = join11(paths.versions, `.staging-${version}-${process.pid}`);
5904
- rmSync8(named, { recursive: true, force: true });
5905
- mkdirSync9(named, { recursive: true });
6143
+ const named = join12(paths.versions, `.staging-${version}-${process.pid}`);
6144
+ rmSync9(named, { recursive: true, force: true });
6145
+ mkdirSync10(named, { recursive: true });
5906
6146
  const staging = realpathSync3(named);
5907
6147
  writeFileSync8(
5908
- join11(staging, "package.json"),
6148
+ join12(staging, "package.json"),
5909
6149
  `${JSON.stringify(allowScriptsManifest(["node-pty"]), null, 2)}
5910
6150
  `
5911
6151
  );
5912
6152
  if (using.name === "pnpm")
5913
- writeFileSync8(join11(staging, "pnpm-workspace.yaml"), allowScriptsWorkspace(["node-pty"]));
6153
+ writeFileSync8(join12(staging, "pnpm-workspace.yaml"), allowScriptsWorkspace(["node-pty"]));
5914
6154
  const fail = (reason) => {
5915
- rmSync8(staging, { recursive: true, force: true });
6155
+ rmSync9(staging, { recursive: true, force: true });
5916
6156
  return { ok: false, reason };
5917
6157
  };
5918
6158
  log(`Installing ${PACKAGE_NAME}@${version} with ${using.name} ${using.version}.`);
@@ -5935,21 +6175,21 @@ async function installVersion(paths, version, { spec, integrity, run: run4 = exe
5935
6175
  }
5936
6176
  const broken = await versionRuns(staging, version, run4);
5937
6177
  if (broken) return fail(broken);
5938
- renameSync3(staging, join11(realpathSync3(paths.versions), version));
6178
+ renameSync3(staging, join12(realpathSync3(paths.versions), version));
5939
6179
  return { ok: true, dir };
5940
6180
  }
5941
6181
  function sweepStaging(paths) {
5942
6182
  if (!existsSync8(paths.versions)) return;
5943
- for (const name of readdirSync3(paths.versions)) {
6183
+ for (const name of readdirSync4(paths.versions)) {
5944
6184
  const match = /^\.staging-.+-(\d+)$/.exec(name);
5945
6185
  if (match && !alive2(Number(match[1])))
5946
- rmSync8(join11(paths.versions, name), { recursive: true, force: true });
6186
+ rmSync9(join12(paths.versions, name), { recursive: true, force: true });
5947
6187
  }
5948
6188
  }
5949
6189
  function activate(paths, version) {
5950
6190
  const next = `${paths.current}.next-${process.pid}`;
5951
- rmSync8(next, { force: true });
5952
- symlinkSync(join11("versions", version), next);
6191
+ rmSync9(next, { force: true });
6192
+ symlinkSync(join12("versions", version), next);
5953
6193
  renameSync3(next, paths.current);
5954
6194
  }
5955
6195
  function pruneVersions(paths, { inUse = [], keep = 3 } = {}) {
@@ -5964,7 +6204,7 @@ function pruneVersions(paths, { inUse = [], keep = 3 } = {}) {
5964
6204
  const removed = [];
5965
6205
  for (const version of versions) {
5966
6206
  if (kept.has(version)) continue;
5967
- rmSync8(join11(paths.versions, version), { recursive: true, force: true });
6207
+ rmSync9(join12(paths.versions, version), { recursive: true, force: true });
5968
6208
  removed.push(version);
5969
6209
  }
5970
6210
  return removed;
@@ -5997,19 +6237,19 @@ var nameOf = (command) => {
5997
6237
  return MANAGERS.includes(name) ? name : null;
5998
6238
  };
5999
6239
  function homesOf(name, env, home) {
6000
- const common = [join12(home, ".volta", "bin"), join12(home, ".local", "bin")];
6240
+ const common = [join13(home, ".volta", "bin"), join13(home, ".local", "bin")];
6001
6241
  switch (name) {
6002
6242
  case "pnpm":
6003
6243
  return [
6004
6244
  ...env.PNPM_HOME ? [env.PNPM_HOME] : [],
6005
- join12(home, "Library", "pnpm"),
6006
- join12(home, ".local", "share", "pnpm"),
6245
+ join13(home, "Library", "pnpm"),
6246
+ join13(home, ".local", "share", "pnpm"),
6007
6247
  ...common
6008
6248
  ];
6009
6249
  case "bun":
6010
- return [join12(env.BUN_INSTALL || join12(home, ".bun"), "bin"), ...common];
6250
+ return [join13(env.BUN_INSTALL || join13(home, ".bun"), "bin"), ...common];
6011
6251
  case "yarn":
6012
- return [join12(home, ".yarn", "bin"), ...common];
6252
+ return [join13(home, ".yarn", "bin"), ...common];
6013
6253
  case "npm":
6014
6254
  return common;
6015
6255
  }
@@ -6032,7 +6272,7 @@ function candidatePaths(name, {
6032
6272
  const files = platform2 === "win32" ? [`${name}.cmd`, `${name}.exe`] : [name];
6033
6273
  const seen = /* @__PURE__ */ new Set();
6034
6274
  for (const dir of dirs.filter(Boolean)) {
6035
- for (const file of files) seen.add(join12(dir, file));
6275
+ for (const file of files) seen.add(join13(dir, file));
6036
6276
  }
6037
6277
  return [...seen];
6038
6278
  }
@@ -6192,22 +6432,22 @@ function frozenInstallArgs(name, { offline, yarnMajor }) {
6192
6432
 
6193
6433
  // src/friday/legacy.ts
6194
6434
  init_define_HERD_PACKAGE();
6195
- import { existsSync as existsSync10, readdirSync as readdirSync4, rmSync as rmSync9 } from "node:fs";
6435
+ import { existsSync as existsSync10, readdirSync as readdirSync5, rmSync as rmSync10 } from "node:fs";
6196
6436
  import { userInfo as userInfo2 } from "node:os";
6197
- import { join as join13 } from "node:path";
6437
+ import { join as join14 } from "node:path";
6198
6438
  var OLD_LAUNCH_AGENT = "com.herdingchats.herd";
6199
6439
  var OLD_APPS = ["herd-daemon", "herd-updater"];
6200
6440
  function oldPm2s(paths) {
6201
- const dir = join13(paths.herdHome, "daemon");
6441
+ const dir = join14(paths.herdHome, "daemon");
6202
6442
  if (!existsSync10(dir)) return [];
6203
- return readdirSync4(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join13(dir, entry.name, "node_modules", "pm2", "bin", "pm2")).filter((pm2) => existsSync10(pm2));
6443
+ return readdirSync5(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join14(dir, entry.name, "node_modules", "pm2", "bin", "pm2")).filter((pm2) => existsSync10(pm2));
6204
6444
  }
6205
6445
  async function retireOldDaemon(paths, { home, run: run4 }) {
6206
6446
  const removed = [];
6207
- const plist = join13(home, "Library", "LaunchAgents", `${OLD_LAUNCH_AGENT}.plist`);
6447
+ const plist = join14(home, "Library", "LaunchAgents", `${OLD_LAUNCH_AGENT}.plist`);
6208
6448
  if (existsSync10(plist)) {
6209
6449
  await run4("launchctl", ["bootout", `gui/${userInfo2().uid}/${OLD_LAUNCH_AGENT}`]);
6210
- rmSync9(plist, { force: true });
6450
+ rmSync10(plist, { force: true });
6211
6451
  removed.push(`the login item ${OLD_LAUNCH_AGENT}`);
6212
6452
  }
6213
6453
  for (const pm2 of oldPm2s(paths)) {
@@ -6283,7 +6523,7 @@ var real = (path) => {
6283
6523
  }
6284
6524
  };
6285
6525
  function chooseRoot(cwd, home) {
6286
- return real(cwd) === real(home) ? join14(home, "friday") : resolve3(cwd);
6526
+ return real(cwd) === real(home) ? join15(home, "friday") : resolve3(cwd);
6287
6527
  }
6288
6528
  async function runStart(options) {
6289
6529
  const { paths, run: run4 } = options;
@@ -6363,7 +6603,7 @@ async function runStart(options) {
6363
6603
  await service.stop();
6364
6604
  await stopped(options);
6365
6605
  }
6366
- mkdirSync10(root, { recursive: true });
6606
+ mkdirSync11(root, { recursive: true });
6367
6607
  writeRoot(paths, root);
6368
6608
  verbose2(`installing the ${service.kind} service`);
6369
6609
  const changed = await service.install();
@@ -6459,11 +6699,11 @@ async function ensureInstalled(options, print) {
6459
6699
  // src/friday/supervisor.ts
6460
6700
  init_define_HERD_PACKAGE();
6461
6701
  init_src();
6462
- import { spawn as spawn6 } from "node:child_process";
6702
+ import { spawn as spawn7 } from "node:child_process";
6463
6703
  import {
6464
6704
  closeSync as closeSync3,
6465
6705
  existsSync as existsSync11,
6466
- mkdirSync as mkdirSync11,
6706
+ mkdirSync as mkdirSync12,
6467
6707
  openSync as openSync4,
6468
6708
  readFileSync as readFileSync9,
6469
6709
  realpathSync as realpathSync5,
@@ -6558,7 +6798,7 @@ var Supervisor = class {
6558
6798
  spawnDaemon() {
6559
6799
  if (this.stopping) return;
6560
6800
  const { daemonLog, logs } = this.options.paths;
6561
- mkdirSync11(logs, { recursive: true });
6801
+ mkdirSync12(logs, { recursive: true });
6562
6802
  try {
6563
6803
  if (existsSync11(daemonLog) && statSync6(daemonLog).size > MAX_LOG_BYTES2)
6564
6804
  renameSync4(daemonLog, `${daemonLog}.1`);
@@ -6567,7 +6807,7 @@ var Supervisor = class {
6567
6807
  const out = openSync4(daemonLog, "a");
6568
6808
  const [program = process.execPath, ...args] = this.daemonCommand();
6569
6809
  const startedAt = Date.now();
6570
- const child = spawn6(program, args, { stdio: ["ignore", out, out], env: process.env });
6810
+ const child = spawn7(program, args, { stdio: ["ignore", out, out], env: process.env });
6571
6811
  closeSync3(out);
6572
6812
  this.daemon = child;
6573
6813
  this.status.daemonPid = child.pid ?? null;
@@ -6719,7 +6959,7 @@ Agents use the herd command, which comes with this one. herd setup shows what's
6719
6959
  function defaults() {
6720
6960
  const home = homedir5();
6721
6961
  return {
6722
- paths: fridayPaths(process.env.HERD_HOME ?? join15(home, ".herd")),
6962
+ paths: fridayPaths(process.env.HERD_HOME ?? join16(home, ".herd")),
6723
6963
  home,
6724
6964
  cwd: process.cwd(),
6725
6965
  run: exec,
@@ -6850,8 +7090,8 @@ function agentsFlag(args) {
6850
7090
  return parsed.shares;
6851
7091
  }
6852
7092
  function attachTo(paths, terminal) {
6853
- const herd = existsSync12(binOf(paths.current, "herd")) ? binOf(paths.current, "herd") : join15(dirname10(process.argv[1] ?? ""), "herd.mjs");
6854
- const child = spawn7(process.execPath, [herd, "attach", terminal], {
7093
+ const herd = existsSync12(binOf(paths.current, "herd")) ? binOf(paths.current, "herd") : join16(dirname10(process.argv[1] ?? ""), "herd.mjs");
7094
+ const child = spawn8(process.execPath, [herd, "attach", terminal], {
6855
7095
  stdio: "inherit",
6856
7096
  env: { ...process.env, HERD_HOME: paths.herdHome }
6857
7097
  });
@@ -6914,7 +7154,7 @@ function runStatus(deps) {
6914
7154
  function readMissingFolder(herdHome2) {
6915
7155
  try {
6916
7156
  return folderUnavailableSchema.parse(
6917
- JSON.parse(readFileSync10(join15(herdHome2, "folder-unavailable.json"), "utf8"))
7157
+ JSON.parse(readFileSync10(join16(herdHome2, "folder-unavailable.json"), "utf8"))
6918
7158
  );
6919
7159
  } catch {
6920
7160
  return null;
@@ -6935,7 +7175,7 @@ async function runLogs(args, deps) {
6935
7175
  return 0;
6936
7176
  }
6937
7177
  if (follow) {
6938
- const child = spawn7("tail", ["-n", "50", "-F", ...files], { stdio: "inherit" });
7178
+ const child = spawn8("tail", ["-n", "50", "-F", ...files], { stdio: "inherit" });
6939
7179
  return await new Promise((resolve8) => child.on("close", (code) => resolve8(code ?? 0)));
6940
7180
  }
6941
7181
  for (const file of files) {
@@ -6977,16 +7217,16 @@ init_define_HERD_PACKAGE();
6977
7217
 
6978
7218
  // src/agent-sandbox.ts
6979
7219
  init_define_HERD_PACKAGE();
6980
- import { spawn as spawn9 } from "node:child_process";
6981
- import { existsSync as existsSync13, mkdirSync as mkdirSync12, readdirSync as readdirSync5, readFileSync as readFileSync12, statSync as statSync7, writeFileSync as writeFileSync9 } from "node:fs";
7220
+ import { spawn as spawn10 } from "node:child_process";
7221
+ import { existsSync as existsSync13, mkdirSync as mkdirSync13, readdirSync as readdirSync6, readFileSync as readFileSync12, statSync as statSync7, writeFileSync as writeFileSync9 } from "node:fs";
6982
7222
  import { homedir as homedir6, tmpdir } from "node:os";
6983
- import { dirname as dirname12, isAbsolute as isAbsolute3, join as join17, resolve as resolve4 } from "node:path";
7223
+ import { dirname as dirname12, isAbsolute as isAbsolute3, join as join18, resolve as resolve4 } from "node:path";
6984
7224
 
6985
7225
  // src/build-sandbox.ts
6986
7226
  init_define_HERD_PACKAGE();
6987
- import { spawn as spawn8 } from "node:child_process";
7227
+ import { spawn as spawn9 } from "node:child_process";
6988
7228
  import { appendFileSync, readFileSync as readFileSync11 } from "node:fs";
6989
- import { dirname as dirname11, join as join16 } from "node:path";
7229
+ import { dirname as dirname11, join as join17 } from "node:path";
6990
7230
  var DEPLOY_HOSTS = ["api.cloudflare.com", "api.github.com", "registry.npmjs.org"];
6991
7231
  function buildPolicy(input) {
6992
7232
  const platform2 = input.platform ?? process.platform;
@@ -7021,7 +7261,7 @@ var shellQuote = (args) => args.map((arg) => `'${arg.replace(/'/g, "'\\''")}'`).
7021
7261
  var BLOCKED_LOG = "blocked.log";
7022
7262
  async function runSandboxed(args, {
7023
7263
  usage = "Usage: herd build-sandbox <policy> -- <command\u2026>",
7024
- blockedLog = (_policyPath) => join16(process.env.HOME ?? ".", BLOCKED_LOG)
7264
+ blockedLog = (_policyPath) => join17(process.env.HOME ?? ".", BLOCKED_LOG)
7025
7265
  } = {}) {
7026
7266
  const { SandboxManager } = await import("@anthropic-ai/sandbox-runtime");
7027
7267
  const [policyPath, separator, ...command] = args;
@@ -7049,7 +7289,7 @@ async function runSandboxed(args, {
7049
7289
  }
7050
7290
  });
7051
7291
  const wrapped = await SandboxManager.wrapWithSandbox(shellQuote(command));
7052
- const child = spawn8(wrapped, { shell: true, stdio: "inherit" });
7292
+ const child = spawn9(wrapped, { shell: true, stdio: "inherit" });
7053
7293
  const forward = (signal2) => () => child.kill(signal2);
7054
7294
  process.once("SIGTERM", forward("SIGTERM"));
7055
7295
  process.once("SIGINT", forward("SIGINT"));
@@ -7190,13 +7430,13 @@ var AGENT_STATE = [
7190
7430
  ".config/gh"
7191
7431
  ];
7192
7432
  function agentAccess(input) {
7193
- const home = (path) => join17(input.ownerHome, path);
7433
+ const home = (path) => join18(input.ownerHome, path);
7194
7434
  return {
7195
7435
  read: [
7196
7436
  ...SYSTEM_READ,
7197
7437
  input.nodePrefix,
7198
7438
  ...HOME_READ.map(home),
7199
- ...HERD_READ.map((path) => join17(input.herdHome, path))
7439
+ ...HERD_READ.map((path) => join18(input.herdHome, path))
7200
7440
  ],
7201
7441
  write: [
7202
7442
  .../* @__PURE__ */ new Set([
@@ -7214,7 +7454,7 @@ function seatbeltPolicy(input) {
7214
7454
  const access = agentAccess(input);
7215
7455
  return {
7216
7456
  network: {
7217
- allowUnixSockets: [join17(input.herdHome, "daemon.sock"), input.tmp, input.folder],
7457
+ allowUnixSockets: [join18(input.herdHome, "daemon.sock"), input.tmp, input.folder],
7218
7458
  allowLocalBinding: true
7219
7459
  },
7220
7460
  filesystem: {
@@ -7229,7 +7469,7 @@ function seatbeltPolicy(input) {
7229
7469
  function worktreeGitDirs(folder) {
7230
7470
  const candidates2 = [folder];
7231
7471
  try {
7232
- for (const name of readdirSync5(folder)) candidates2.push(join17(folder, name));
7472
+ for (const name of readdirSync6(folder)) candidates2.push(join18(folder, name));
7233
7473
  } catch {
7234
7474
  return [];
7235
7475
  }
@@ -7241,13 +7481,13 @@ function worktreeGitDirs(folder) {
7241
7481
  return [...dirs];
7242
7482
  }
7243
7483
  function commonGitDir(worktree) {
7244
- const dotGit = join17(worktree, ".git");
7484
+ const dotGit = join18(worktree, ".git");
7245
7485
  try {
7246
7486
  if (!statSync7(dotGit).isFile()) return null;
7247
7487
  const line2 = /^gitdir:\s*(.+)$/m.exec(readFileSync12(dotGit, "utf8"))?.[1]?.trim();
7248
7488
  if (!line2) return null;
7249
7489
  const gitDir = isAbsolute3(line2) ? line2 : resolve4(worktree, line2);
7250
- const common = join17(gitDir, "commondir");
7490
+ const common = join18(gitDir, "commondir");
7251
7491
  if (!existsSync13(common)) return gitDir;
7252
7492
  const relative3 = readFileSync12(common, "utf8").trim();
7253
7493
  return isAbsolute3(relative3) ? relative3 : resolve4(gitDir, relative3);
@@ -7255,10 +7495,10 @@ function commonGitDir(worktree) {
7255
7495
  return null;
7256
7496
  }
7257
7497
  }
7258
- var policyPathFor = (conversationId) => join17(herdHome(), "sandbox", `${conversationId}.json`);
7498
+ var policyPathFor = (conversationId) => join18(herdHome(), "sandbox", `${conversationId}.json`);
7259
7499
  function writeAgentPolicy(conversationId, paths, platform2 = process.platform) {
7260
7500
  const path = policyPathFor(conversationId);
7261
- mkdirSync12(dirname12(path), { recursive: true, mode: 448 });
7501
+ mkdirSync13(dirname12(path), { recursive: true, mode: 448 });
7262
7502
  const input = {
7263
7503
  ownerHome: homedir6(),
7264
7504
  herdHome: herdHome(),
@@ -7273,7 +7513,7 @@ function writeAgentPolicy(conversationId, paths, platform2 = process.platform) {
7273
7513
  }
7274
7514
  var confinementOn = (env = process.env) => env.HERD_CONFINE === "on";
7275
7515
  function runLandlocked(access, command) {
7276
- const child = spawn9(
7516
+ const child = spawn10(
7277
7517
  "python3",
7278
7518
  ["-I", "-c", LANDLOCK_LAUNCHER, JSON.stringify(access), ...command],
7279
7519
  { stdio: "inherit" }
@@ -7779,8 +8019,8 @@ init_define_HERD_PACKAGE();
7779
8019
  // src/build-runner-key.ts
7780
8020
  init_define_HERD_PACKAGE();
7781
8021
  init_src();
7782
- import { chmodSync as chmodSync3, existsSync as existsSync14, mkdirSync as mkdirSync13, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "node:fs";
7783
- import { join as join18 } from "node:path";
8022
+ import { chmodSync as chmodSync3, existsSync as existsSync14, mkdirSync as mkdirSync14, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "node:fs";
8023
+ import { join as join19 } from "node:path";
7784
8024
  import { z as z24 } from "zod";
7785
8025
  var BUILDS_URL = "https://builds.herdingchats.com";
7786
8026
  var runnerKeySchema = z24.object({
@@ -7788,7 +8028,7 @@ var runnerKeySchema = z24.object({
7788
8028
  key: z24.string().min(20),
7789
8029
  url: z24.string().url()
7790
8030
  });
7791
- var runnerKeyPath = () => join18(herdHome(), "build-runner.json");
8031
+ var runnerKeyPath = () => join19(herdHome(), "build-runner.json");
7792
8032
  function readRunnerKey() {
7793
8033
  const path = runnerKeyPath();
7794
8034
  if (!existsSync14(path)) return null;
@@ -7801,7 +8041,7 @@ function readRunnerKey() {
7801
8041
  }
7802
8042
  }
7803
8043
  function saveRunnerKey(key) {
7804
- mkdirSync13(herdHome(), { recursive: true, mode: 448 });
8044
+ mkdirSync14(herdHome(), { recursive: true, mode: 448 });
7805
8045
  const path = runnerKeyPath();
7806
8046
  writeFileSync10(path, JSON.stringify(runnerKeySchema.parse(key), null, 2), { mode: 384 });
7807
8047
  chmodSync3(path, 384);
@@ -7969,8 +8209,8 @@ function formatSpeed(report) {
7969
8209
 
7970
8210
  // src/builds-admin.ts
7971
8211
  init_define_HERD_PACKAGE();
7972
- import { mkdirSync as mkdirSync14, readFileSync as readFileSync15, rmSync as rmSync10, writeFileSync as writeFileSync11 } from "node:fs";
7973
- import { join as join19 } from "node:path";
8212
+ import { mkdirSync as mkdirSync15, readFileSync as readFileSync15, rmSync as rmSync11, writeFileSync as writeFileSync11 } from "node:fs";
8213
+ import { join as join20 } from "node:path";
7974
8214
  var NOT_ADMIN = "builds are only for the admin's machines";
7975
8215
  async function isAdmin(fetch2) {
7976
8216
  const response = await fetch2("/api/people/me").catch(() => null);
@@ -7978,13 +8218,13 @@ async function isAdmin(fetch2) {
7978
8218
  const body = await response.json().catch(() => ({}));
7979
8219
  return body.admin === true;
7980
8220
  }
7981
- var refusalPath = () => join19(herdHome(), "builds", "refused.json");
8221
+ var refusalPath = () => join20(herdHome(), "builds", "refused.json");
7982
8222
  function writeBuildsRefusal(reason) {
7983
8223
  if (!reason) {
7984
- rmSync10(refusalPath(), { force: true });
8224
+ rmSync11(refusalPath(), { force: true });
7985
8225
  return;
7986
8226
  }
7987
- mkdirSync14(join19(herdHome(), "builds"), { recursive: true });
8227
+ mkdirSync15(join20(herdHome(), "builds"), { recursive: true });
7988
8228
  writeFileSync11(refusalPath(), JSON.stringify({ reason }));
7989
8229
  }
7990
8230
  function readBuildsRefusal() {
@@ -8258,12 +8498,12 @@ function readClaudeScreen(raw) {
8258
8498
  // src/worktrees.ts
8259
8499
  init_define_HERD_PACKAGE();
8260
8500
  import { existsSync as existsSync15, realpathSync as realpathSync6 } from "node:fs";
8261
- import { isAbsolute as isAbsolute4, join as join20, relative } from "node:path";
8262
- var worktreesRoot = () => join20(herdHome(), "worktrees");
8501
+ import { isAbsolute as isAbsolute4, join as join21, relative } from "node:path";
8502
+ var worktreesRoot = () => join21(herdHome(), "worktrees");
8263
8503
  var WORD_ID = /^[a-z]+-[a-z]+-[a-z]+$/;
8264
8504
  var conversationPrefix = (conversationId) => WORD_ID.test(conversationId) ? conversationId : conversationId.replace(/[^A-Za-z0-9]/g, "").slice(0, 12).toLowerCase();
8265
8505
  var conversationBranch = (conversationId) => `herd/${conversationPrefix(conversationId)}`;
8266
- var conversationFolder = (convos, conversationId) => join20(convos, conversationPrefix(conversationId));
8506
+ var conversationFolder = (convos, conversationId) => join21(convos, conversationPrefix(conversationId));
8267
8507
  function folderForConversation(root, conversationId) {
8268
8508
  const earlier = conversationFolder(legacyConvosDir(root), conversationId);
8269
8509
  return existsSync15(earlier) ? earlier : conversationFolder(convosHome(root), conversationId);
@@ -8310,7 +8550,7 @@ function withStartKey(command, startKey, confine) {
8310
8550
  return startKey ? `HERD_START=${startKey} ${line2}` : line2;
8311
8551
  }
8312
8552
  var DEFAULT_TIMING = { settleMs: 800, pollMs: 150, startMs: 6e4, confirmMs: 3e3 };
8313
- var sleep3 = (ms2) => new Promise((resolve8) => setTimeout(resolve8, ms2));
8553
+ var sleep4 = (ms2) => new Promise((resolve8) => setTimeout(resolve8, ms2));
8314
8554
  var EXIT_WAIT_MS = 5e3;
8315
8555
  var AgentDriver = class {
8316
8556
  constructor(terminals, timing = {}) {
@@ -8338,7 +8578,7 @@ var AgentDriver = class {
8338
8578
  const reading = await this.read(name);
8339
8579
  if (check2(reading)) return reading;
8340
8580
  if (Date.now() > end) return null;
8341
- await sleep3(this.timing.pollMs);
8581
+ await sleep4(this.timing.pollMs);
8342
8582
  }
8343
8583
  }
8344
8584
  /** Waits for a terminal's output to stop changing, as a shell does once it's ready. */
@@ -8354,7 +8594,7 @@ var AgentDriver = class {
8354
8594
  } else if (Date.now() - stableSince >= this.timing.settleMs) {
8355
8595
  return;
8356
8596
  }
8357
- await sleep3(this.timing.pollMs);
8597
+ await sleep4(this.timing.pollMs);
8358
8598
  }
8359
8599
  }
8360
8600
  /**
@@ -8394,7 +8634,7 @@ var AgentDriver = class {
8394
8634
  for (let attempt = 0; attempt < 5; attempt++) {
8395
8635
  if ((await this.read(name)).trustPrompt?.yesHighlighted) break;
8396
8636
  await this.terminals.press(name, "down");
8397
- await sleep3(this.timing.pollMs * 2);
8637
+ await sleep4(this.timing.pollMs * 2);
8398
8638
  }
8399
8639
  if (!(await this.read(name)).trustPrompt?.yesHighlighted) {
8400
8640
  throw new HerdError(
@@ -8472,7 +8712,7 @@ var AgentDriver = class {
8472
8712
  }
8473
8713
  });
8474
8714
  });
8475
- const confirmed = () => Promise.race([hooked, sleep3(this.timing.confirmMs)]);
8715
+ const confirmed = () => Promise.race([hooked, sleep4(this.timing.confirmMs)]);
8476
8716
  try {
8477
8717
  if (!leftover) await this.terminals.type(name, text4);
8478
8718
  const head = sent.slice(0, 40);
@@ -8576,9 +8816,9 @@ function readAgyScreen(raw) {
8576
8816
 
8577
8817
  // src/agy-settings.ts
8578
8818
  init_define_HERD_PACKAGE();
8579
- import { lstatSync as lstatSync2, mkdirSync as mkdirSync15, realpathSync as realpathSync7, writeFileSync as writeFileSync12 } from "node:fs";
8580
- import { join as join21, sep } from "node:path";
8581
- var AGY_SETTINGS_FILE = join21(".herd", "agy-settings.json");
8819
+ import { lstatSync as lstatSync2, mkdirSync as mkdirSync16, realpathSync as realpathSync7, writeFileSync as writeFileSync12 } from "node:fs";
8820
+ import { join as join22, sep } from "node:path";
8821
+ var AGY_SETTINGS_FILE = join22(".herd", "agy-settings.json");
8582
8822
  var AGY_HOOK_EVENTS = ["SessionStart", "PreInvocation", "Stop"];
8583
8823
  var agyHookCommand = (event2, herd) => `${herd} hook agy ${event2} || true`;
8584
8824
  function agyHooks(herd) {
@@ -8595,11 +8835,11 @@ function agySettings(herd) {
8595
8835
  return { statusLine: { type: "command", command: `${herd} statusline agy` } };
8596
8836
  }
8597
8837
  function ownFolder(worktree, name) {
8598
- const folder = join21(worktree, name);
8838
+ const folder = join22(worktree, name);
8599
8839
  if (lstatSync2(folder, { throwIfNoEntry: false })?.isSymbolicLink()) {
8600
8840
  throw new Error(`${name} in the worktree is a link`);
8601
8841
  }
8602
- mkdirSync15(folder, { recursive: true });
8842
+ mkdirSync16(folder, { recursive: true });
8603
8843
  if (!realpathSync7(folder).startsWith(realpathSync7(worktree) + sep)) {
8604
8844
  throw new Error(`${name} leads outside the worktree`);
8605
8845
  }
@@ -8607,13 +8847,13 @@ function ownFolder(worktree, name) {
8607
8847
  }
8608
8848
  function writeAgyFiles(worktree, herd) {
8609
8849
  const agents = ownFolder(worktree, ".agents");
8610
- writeFileSync12(join21(agents, "hooks.json"), `${JSON.stringify(agyHooks(herd), null, 2)}
8850
+ writeFileSync12(join22(agents, "hooks.json"), `${JSON.stringify(agyHooks(herd), null, 2)}
8611
8851
  `);
8612
- writeFileSync12(join21(agents, ".gitignore"), "*\n");
8852
+ writeFileSync12(join22(agents, ".gitignore"), "*\n");
8613
8853
  const herdFolder = ownFolder(worktree, ".herd");
8614
- writeFileSync12(join21(herdFolder, ".gitignore"), "*\n");
8854
+ writeFileSync12(join22(herdFolder, ".gitignore"), "*\n");
8615
8855
  writeFileSync12(
8616
- join21(worktree, AGY_SETTINGS_FILE),
8856
+ join22(worktree, AGY_SETTINGS_FILE),
8617
8857
  `${JSON.stringify(agySettings(herd), null, 2)}
8618
8858
  `
8619
8859
  );
@@ -8622,7 +8862,7 @@ function writeAgyFiles(worktree, herd) {
8622
8862
  // src/agy-driver.ts
8623
8863
  var AGY_FLAGS = `--dangerously-skip-permissions --settings ${AGY_SETTINGS_FILE}`;
8624
8864
  var DEFAULT_TIMING2 = { settleMs: 800, pollMs: 150, startMs: 6e4, confirmMs: 3e3 };
8625
- var sleep4 = (ms2) => new Promise((resolve8) => setTimeout(resolve8, ms2));
8865
+ var sleep5 = (ms2) => new Promise((resolve8) => setTimeout(resolve8, ms2));
8626
8866
  var EXIT_WAIT_MS2 = 5e3;
8627
8867
  var AgyDriver = class {
8628
8868
  constructor(terminals, timing = {}) {
@@ -8645,7 +8885,7 @@ var AgyDriver = class {
8645
8885
  const reading = await this.read(name);
8646
8886
  if (check2(reading)) return reading;
8647
8887
  if (Date.now() > end) return null;
8648
- await sleep4(this.timing.pollMs);
8888
+ await sleep5(this.timing.pollMs);
8649
8889
  }
8650
8890
  }
8651
8891
  /** Waits for a terminal's output to stop changing, as a shell does once it's ready. */
@@ -8661,7 +8901,7 @@ var AgyDriver = class {
8661
8901
  } else if (Date.now() - stableSince >= this.timing.settleMs) {
8662
8902
  return;
8663
8903
  }
8664
- await sleep4(this.timing.pollMs);
8904
+ await sleep5(this.timing.pollMs);
8665
8905
  }
8666
8906
  }
8667
8907
  /**
@@ -8727,7 +8967,7 @@ var AgyDriver = class {
8727
8967
  }
8728
8968
  });
8729
8969
  });
8730
- const confirmed = () => Promise.race([reported, sleep4(this.timing.confirmMs)]);
8970
+ const confirmed = () => Promise.race([reported, sleep5(this.timing.confirmMs)]);
8731
8971
  const head = sent.slice(0, 40);
8732
8972
  const queuedOnScreen = async () => {
8733
8973
  const screen = await this.terminals.screen(name);
@@ -8774,12 +9014,12 @@ var AgyDriver = class {
8774
9014
  // src/bare-clones.ts
8775
9015
  init_define_HERD_PACKAGE();
8776
9016
  import { randomBytes as randomBytes5 } from "node:crypto";
8777
- import { existsSync as existsSync16, mkdirSync as mkdirSync16, readdirSync as readdirSync6, renameSync as renameSync5, rmSync as rmSync11, statSync as statSync8 } from "node:fs";
8778
- import { dirname as dirname13, join as join22 } from "node:path";
9017
+ import { existsSync as existsSync16, mkdirSync as mkdirSync17, readdirSync as readdirSync7, renameSync as renameSync5, rmSync as rmSync12, statSync as statSync8 } from "node:fs";
9018
+ import { dirname as dirname13, join as join23 } from "node:path";
8779
9019
  function bareClonePath(dir, remote) {
8780
9020
  const key = repoKey(remote);
8781
9021
  if (!key) throw new StoreError(`${remote} isn't a repo's URL.`);
8782
- return join22(dir, `${key}.git`);
9022
+ return join23(dir, `${key}.git`);
8783
9023
  }
8784
9024
  async function ensureBareClone(store, remote, env) {
8785
9025
  if (existsSync16(store)) {
@@ -8793,7 +9033,7 @@ async function ensureBareClone(store, remote, env) {
8793
9033
  return { store, created: false, fetchFailed };
8794
9034
  }
8795
9035
  const building = `${store}.tmp-${process.pid}-${randomBytes5(4).toString("hex")}`;
8796
- mkdirSync16(dirname13(store), { recursive: true });
9036
+ mkdirSync17(dirname13(store), { recursive: true });
8797
9037
  try {
8798
9038
  await git(env, dirname13(store), "init", "--quiet", "--bare", building);
8799
9039
  await git(env, building, "remote", "add", "origin", remote);
@@ -8808,7 +9048,7 @@ async function ensureBareClone(store, remote, env) {
8808
9048
  await git(env, building, "remote", "set-head", "origin", "--auto").catch(() => "");
8809
9049
  renameSync5(building, store);
8810
9050
  } catch (error) {
8811
- rmSync11(building, { recursive: true, force: true });
9051
+ rmSync12(building, { recursive: true, force: true });
8812
9052
  throw error instanceof StoreError ? error : new StoreError(gitError(error));
8813
9053
  }
8814
9054
  return { store, created: true, fetchFailed: null };
@@ -8818,12 +9058,12 @@ function bareClones(dir) {
8818
9058
  const walk = (at, depth) => {
8819
9059
  let entries;
8820
9060
  try {
8821
- entries = readdirSync6(at);
9061
+ entries = readdirSync7(at);
8822
9062
  } catch {
8823
9063
  return;
8824
9064
  }
8825
9065
  for (const name of entries.sort()) {
8826
- const path = join22(at, name);
9066
+ const path = join23(at, name);
8827
9067
  if (name.endsWith(".git")) {
8828
9068
  try {
8829
9069
  const remote = gitSync(path, "config", "--get", "remote.origin.url");
@@ -9001,28 +9241,28 @@ var BuildConnection = class {
9001
9241
 
9002
9242
  // src/builds.ts
9003
9243
  init_define_HERD_PACKAGE();
9004
- import { execFile as execFile2, execFileSync as execFileSync5, spawn as spawn10 } from "node:child_process";
9244
+ import { execFile as execFile2, execFileSync as execFileSync5, spawn as spawn11 } from "node:child_process";
9005
9245
  import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
9006
9246
  import {
9007
9247
  existsSync as existsSync17,
9008
- mkdirSync as mkdirSync18,
9248
+ mkdirSync as mkdirSync19,
9009
9249
  mkdtempSync,
9010
- readdirSync as readdirSync8,
9250
+ readdirSync as readdirSync9,
9011
9251
  readFileSync as readFileSync17,
9012
9252
  realpathSync as realpathSync8,
9013
- rmSync as rmSync13,
9253
+ rmSync as rmSync14,
9014
9254
  writeFileSync as writeFileSync14
9015
9255
  } from "node:fs";
9016
9256
  import { homedir as homedir8, tmpdir as tmpdir2 } from "node:os";
9017
- import { join as join24 } from "node:path";
9257
+ import { join as join25 } from "node:path";
9018
9258
  import { promisify as promisify2 } from "node:util";
9019
9259
 
9020
9260
  // src/check-registry.ts
9021
9261
  init_define_HERD_PACKAGE();
9022
- import { mkdirSync as mkdirSync17, readdirSync as readdirSync7, readFileSync as readFileSync16, rmSync as rmSync12, writeFileSync as writeFileSync13 } from "node:fs";
9262
+ import { mkdirSync as mkdirSync18, readdirSync as readdirSync8, readFileSync as readFileSync16, rmSync as rmSync13, writeFileSync as writeFileSync13 } from "node:fs";
9023
9263
  import { homedir as homedir7 } from "node:os";
9024
- import { join as join23 } from "node:path";
9025
- var checkRegistryDir = () => process.env.HERDINGCHATS_CHECK_RUNNING ?? join23(homedir7(), ".cache", "herdingchats", "check", "running");
9264
+ import { join as join24 } from "node:path";
9265
+ var checkRegistryDir = () => process.env.HERDINGCHATS_CHECK_RUNNING ?? join24(homedir7(), ".cache", "herdingchats", "check", "running");
9026
9266
  var RESERVED_MB = 6 * 1024;
9027
9267
  var MIN_MEMORY_MB = 1024;
9028
9268
  var alive3 = (pid) => {
@@ -9034,15 +9274,15 @@ var alive3 = (pid) => {
9034
9274
  }
9035
9275
  };
9036
9276
  function registerCheck(dir, pid, startedAt = Date.now()) {
9037
- mkdirSync17(dir, { recursive: true });
9038
- const file = join23(dir, `${pid}.json`);
9277
+ mkdirSync18(dir, { recursive: true });
9278
+ const file = join24(dir, `${pid}.json`);
9039
9279
  writeFileSync13(file, JSON.stringify({ pid, startedAt }));
9040
- return () => rmSync12(file, { force: true });
9280
+ return () => rmSync13(file, { force: true });
9041
9281
  }
9042
9282
  function runningChecks(dir, isAlive = alive3) {
9043
9283
  let names = [];
9044
9284
  try {
9045
- names = readdirSync7(dir).filter((name) => /^\d+\.json$/.test(name));
9285
+ names = readdirSync8(dir).filter((name) => /^\d+\.json$/.test(name));
9046
9286
  } catch {
9047
9287
  return 0;
9048
9288
  }
@@ -9051,12 +9291,12 @@ function runningChecks(dir, isAlive = alive3) {
9051
9291
  const pid = Number(name.slice(0, -".json".length));
9052
9292
  let entry = null;
9053
9293
  try {
9054
- entry = JSON.parse(readFileSync16(join23(dir, name), "utf8"));
9294
+ entry = JSON.parse(readFileSync16(join24(dir, name), "utf8"));
9055
9295
  } catch {
9056
9296
  continue;
9057
9297
  }
9058
9298
  if (entry?.pid === pid && isAlive(pid)) count += 1;
9059
- else rmSync12(join23(dir, name), { force: true });
9299
+ else rmSync13(join24(dir, name), { force: true });
9060
9300
  }
9061
9301
  return count;
9062
9302
  }
@@ -9064,9 +9304,9 @@ var memoryShareOf = (totalMb, running) => Math.max(MIN_MEMORY_MB, Math.floor((to
9064
9304
 
9065
9305
  // src/builds.ts
9066
9306
  init_self();
9067
- var sandboxStatusPath = () => join24(herdHome(), "builds", "sandbox.json");
9307
+ var sandboxStatusPath = () => join25(herdHome(), "builds", "sandbox.json");
9068
9308
  function writeSandboxStatus(status2, now = Date.now()) {
9069
- mkdirSync18(join24(herdHome(), "builds"), { recursive: true });
9309
+ mkdirSync19(join25(herdHome(), "builds"), { recursive: true });
9070
9310
  writeFileSync14(sandboxStatusPath(), JSON.stringify({ ...status2, at: now }));
9071
9311
  }
9072
9312
  function readSandboxStatus() {
@@ -9098,7 +9338,7 @@ var real3 = (path) => {
9098
9338
  }
9099
9339
  };
9100
9340
  function worktreeOf(dir, store) {
9101
- if (!existsSync17(join24(dir, ".git"))) return false;
9341
+ if (!existsSync17(join25(dir, ".git"))) return false;
9102
9342
  try {
9103
9343
  return real3(git2(dir, "rev-parse", "--path-format=absolute", "--git-common-dir")) === real3(store);
9104
9344
  } catch {
@@ -9110,11 +9350,11 @@ function cloneFolder(from, to) {
9110
9350
  execFileSync5("cp", [...args, from, to]);
9111
9351
  }
9112
9352
  function moduleFolders(dir) {
9113
- const found = existsSync17(join24(dir, "node_modules")) ? ["node_modules"] : [];
9114
- for (const entry of readdirSync8(dir, { withFileTypes: true })) {
9353
+ const found = existsSync17(join25(dir, "node_modules")) ? ["node_modules"] : [];
9354
+ for (const entry of readdirSync9(dir, { withFileTypes: true })) {
9115
9355
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
9116
- if (existsSync17(join24(dir, entry.name, "node_modules")))
9117
- found.push(join24(entry.name, "node_modules"));
9356
+ if (existsSync17(join25(dir, entry.name, "node_modules")))
9357
+ found.push(join25(entry.name, "node_modules"));
9118
9358
  }
9119
9359
  }
9120
9360
  return found;
@@ -9122,11 +9362,11 @@ function moduleFolders(dir) {
9122
9362
  function checkoutManager(dir) {
9123
9363
  let field;
9124
9364
  try {
9125
- const pkg = JSON.parse(readFileSync17(join24(dir, "package.json"), "utf8"));
9365
+ const pkg = JSON.parse(readFileSync17(join25(dir, "package.json"), "utf8"));
9126
9366
  if (typeof pkg.packageManager === "string") field = pkg.packageManager;
9127
9367
  } catch {
9128
9368
  }
9129
- const found = managerOfCheckout(field, (name) => existsSync17(join24(dir, name)));
9369
+ const found = managerOfCheckout(field, (name) => existsSync17(join25(dir, name)));
9130
9370
  if (!found?.lockfile)
9131
9371
  throw new Error(
9132
9372
  `${dir} has no lockfile to install node_modules from: herd looked for ${Object.values(LOCKFILES).flat().join(", ")}.`
@@ -9134,7 +9374,7 @@ function checkoutManager(dir) {
9134
9374
  return { ...found, lockfile: found.lockfile };
9135
9375
  }
9136
9376
  function lockHash(dir) {
9137
- const lock = readFileSync17(join24(dir, checkoutManager(dir).lockfile));
9377
+ const lock = readFileSync17(join25(dir, checkoutManager(dir).lockfile));
9138
9378
  const major = process.versions.node.split(".")[0];
9139
9379
  return createHash3("sha256").update(lock).update(`${process.platform}-${process.arch}-${major}`).digest("hex").slice(0, 32);
9140
9380
  }
@@ -9146,7 +9386,7 @@ var frozenInstall = (gitEnv) => async (dir) => {
9146
9386
  throw new Error(`${dir} installs with ${name}, which isn't on this machine, or doesn't run.`);
9147
9387
  const args = frozenInstallArgs(name, { offline: insideBuildSandbox(), yarnMajor: major });
9148
9388
  await new Promise((resolve8, reject) => {
9149
- const child = spawn10(manager.command, args, {
9389
+ const child = spawn11(manager.command, args, {
9150
9390
  cwd: dir,
9151
9391
  env: { ...env, COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" },
9152
9392
  stdio: "ignore"
@@ -9165,11 +9405,11 @@ var BuildRunner = class _BuildRunner {
9165
9405
  options;
9166
9406
  running = /* @__PURE__ */ new Map();
9167
9407
  home() {
9168
- return this.options.home ?? join24(herdHome(), "builds");
9408
+ return this.options.home ?? join25(herdHome(), "builds");
9169
9409
  }
9170
9410
  /** Where builds' own bare clones are, apart from the root's checkouts. */
9171
9411
  clonesDir() {
9172
- return join24(this.home(), "clones");
9412
+ return join25(this.home(), "clones");
9173
9413
  }
9174
9414
  /** Herd's bare clone of a build's repo, for builds, whether or not it's there yet. */
9175
9415
  storeFor(build) {
@@ -9190,8 +9430,8 @@ var BuildRunner = class _BuildRunner {
9190
9430
  const store = bareClonePath(this.clonesDir(), remote);
9191
9431
  const env = this.options.gitEnv?.();
9192
9432
  const name = (repoKey(build.repo) ?? build.repo).replace(/[^A-Za-z0-9._-]+/g, "_");
9193
- const dir = join24(this.home(), name, String(slot));
9194
- mkdirSync18(join24(this.home(), name), { recursive: true });
9433
+ const dir = join25(this.home(), name, String(slot));
9434
+ mkdirSync19(join25(this.home(), name), { recursive: true });
9195
9435
  await withStoreLock(
9196
9436
  store,
9197
9437
  async () => {
@@ -9205,7 +9445,7 @@ var BuildRunner = class _BuildRunner {
9205
9445
  await storeGit(env, store, "fetch", "--quiet", "origin", build.sha);
9206
9446
  }
9207
9447
  if (!worktreeOf(dir, store)) {
9208
- rmSync13(dir, { recursive: true, force: true });
9448
+ rmSync14(dir, { recursive: true, force: true });
9209
9449
  await storeGit(env, store, "worktree", "prune");
9210
9450
  await storeGit(
9211
9451
  env,
@@ -9230,30 +9470,30 @@ var BuildRunner = class _BuildRunner {
9230
9470
  /** Makes `node_modules` match the checkout's lockfile: kept, copied from the cache, or installed. */
9231
9471
  async ensureModules(dir) {
9232
9472
  const wanted = lockHash(dir);
9233
- const marker = join24(dir, "node_modules", ".herd-lock");
9473
+ const marker = join25(dir, "node_modules", ".herd-lock");
9234
9474
  if (existsSync17(marker) && readFileSync17(marker, "utf8") === wanted) return;
9235
- const cache = join24(this.home(), "node_modules", wanted);
9475
+ const cache = join25(this.home(), "node_modules", wanted);
9236
9476
  for (const folder of moduleFolders(dir))
9237
- rmSync13(join24(dir, folder), { recursive: true, force: true });
9238
- if (existsSync17(join24(cache, ".complete"))) {
9239
- for (const folder of readFileSync17(join24(cache, ".complete"), "utf8").split("\n").filter(Boolean)) {
9240
- cloneFolder(join24(cache, folder), join24(dir, folder));
9477
+ rmSync14(join25(dir, folder), { recursive: true, force: true });
9478
+ if (existsSync17(join25(cache, ".complete"))) {
9479
+ for (const folder of readFileSync17(join25(cache, ".complete"), "utf8").split("\n").filter(Boolean)) {
9480
+ cloneFolder(join25(cache, folder), join25(dir, folder));
9241
9481
  }
9242
9482
  } else {
9243
9483
  this.options.log?.(`Installing node_modules for lockfile ${wanted.slice(0, 8)}.`);
9244
9484
  await (this.options.install ?? frozenInstall(this.options.gitEnv))(dir);
9245
- const staging = mkdtempSync(join24(this.home(), "node_modules-"));
9485
+ const staging = mkdtempSync(join25(this.home(), "node_modules-"));
9246
9486
  const folders = moduleFolders(dir);
9247
9487
  for (const folder of folders) {
9248
- mkdirSync18(join24(staging, folder, ".."), { recursive: true });
9249
- cloneFolder(join24(dir, folder), join24(staging, folder));
9488
+ mkdirSync19(join25(staging, folder, ".."), { recursive: true });
9489
+ cloneFolder(join25(dir, folder), join25(staging, folder));
9250
9490
  }
9251
- writeFileSync14(join24(staging, ".complete"), folders.join("\n"));
9252
- mkdirSync18(join24(this.home(), "node_modules"), { recursive: true });
9253
- rmSync13(cache, { recursive: true, force: true });
9491
+ writeFileSync14(join25(staging, ".complete"), folders.join("\n"));
9492
+ mkdirSync19(join25(this.home(), "node_modules"), { recursive: true });
9493
+ rmSync14(cache, { recursive: true, force: true });
9254
9494
  execFileSync5("mv", [staging, cache]);
9255
9495
  }
9256
- mkdirSync18(join24(dir, "node_modules"), { recursive: true });
9496
+ mkdirSync19(join25(dir, "node_modules"), { recursive: true });
9257
9497
  writeFileSync14(marker, wanted);
9258
9498
  }
9259
9499
  /**
@@ -9264,7 +9504,7 @@ var BuildRunner = class _BuildRunner {
9264
9504
  const env = {
9265
9505
  PATH: process.env.PATH ?? "/usr/bin:/bin",
9266
9506
  HOME: home,
9267
- TMPDIR: join24(home, "tmp"),
9507
+ TMPDIR: join25(home, "tmp"),
9268
9508
  LANG: process.env.LANG ?? "en_US.UTF-8",
9269
9509
  HERD_BUILD_ID: build.id,
9270
9510
  HERD_BUILD_SECRET: build.secret,
@@ -9276,7 +9516,7 @@ var BuildRunner = class _BuildRunner {
9276
9516
  // The sandbox's proxy is the only way out, and Node's own requests use it only when asked.
9277
9517
  NODE_USE_ENV_PROXY: "1",
9278
9518
  // Where `srt` points TMPDIR inside the sandbox: the build's own temporary folder.
9279
- CLAUDE_CODE_TMPDIR: join24(home, "tmp")
9519
+ CLAUDE_CODE_TMPDIR: join25(home, "tmp")
9280
9520
  };
9281
9521
  return env;
9282
9522
  }
@@ -9287,8 +9527,8 @@ var BuildRunner = class _BuildRunner {
9287
9527
  /** Runs `scripts/build.mjs` from the build's own commit, in a process group of its own. */
9288
9528
  async run(build, slot) {
9289
9529
  const dir = await this.prepare(build, slot);
9290
- const home = mkdtempSync(join24(this.options.scratch ?? scratchRoot(), "hb-"));
9291
- mkdirSync18(join24(home, "tmp"), { recursive: true });
9530
+ const home = mkdtempSync(join25(this.options.scratch ?? scratchRoot(), "hb-"));
9531
+ mkdirSync19(join25(home, "tmp"), { recursive: true });
9292
9532
  const run4 = randomUUID4();
9293
9533
  const env = {
9294
9534
  ..._BuildRunner.environment(build, home),
@@ -9298,7 +9538,7 @@ var BuildRunner = class _BuildRunner {
9298
9538
  env.HERD_CHECK_CPUS = String(Math.max(1, Math.floor(this.options.cpus())));
9299
9539
  if (this.options.memoryMb)
9300
9540
  env.HERD_CHECK_MEMORY_MB = String(Math.max(1, Math.floor(this.options.memoryMb())));
9301
- let command = [process.execPath, join24(dir, "scripts", "build.mjs")];
9541
+ let command = [process.execPath, join25(dir, "scripts", "build.mjs")];
9302
9542
  let policyPath = null;
9303
9543
  if (this.sandboxed()) {
9304
9544
  const policy = buildPolicy({
@@ -9313,8 +9553,8 @@ var BuildRunner = class _BuildRunner {
9313
9553
  main: build.main,
9314
9554
  buildsUrl: build.url
9315
9555
  });
9316
- mkdirSync18(join24(this.home(), "policies"), { recursive: true });
9317
- policyPath = join24(this.home(), "policies", `${build.id}.json`);
9556
+ mkdirSync19(join25(this.home(), "policies"), { recursive: true });
9557
+ policyPath = join25(this.home(), "policies", `${build.id}.json`);
9318
9558
  writeFileSync14(policyPath, JSON.stringify(policy), { mode: 384 });
9319
9559
  env.HERD_BUILD_SANDBOX = describePolicy(policy);
9320
9560
  command = [process.execPath, HERD_BIN, "build-sandbox", policyPath, "--", ...command];
@@ -9322,7 +9562,7 @@ var BuildRunner = class _BuildRunner {
9322
9562
  env.HERD_BUILD_SANDBOX = insideBuildSandbox() ? "Inside the sandbox of the build that started this one." : "";
9323
9563
  }
9324
9564
  const [executable, ...args] = command;
9325
- const child = spawn10(executable, args, { cwd: dir, env, stdio: "ignore", detached: true });
9565
+ const child = spawn11(executable, args, { cwd: dir, env, stdio: "ignore", detached: true });
9326
9566
  const done = new Promise((resolve8) => {
9327
9567
  child.on("error", () => resolve8(1));
9328
9568
  child.on("close", (code) => resolve8(code ?? 1));
@@ -9342,8 +9582,8 @@ var BuildRunner = class _BuildRunner {
9342
9582
  unregister();
9343
9583
  await this.stopBuildProcesses(run4, dir, entry.pid);
9344
9584
  if (this.running.get(build.id) === entry) this.running.delete(build.id);
9345
- rmSync13(home, { recursive: true, force: true });
9346
- if (policyPath) rmSync13(policyPath, { force: true });
9585
+ rmSync14(home, { recursive: true, force: true });
9586
+ if (policyPath) rmSync14(policyPath, { force: true });
9347
9587
  markEnded();
9348
9588
  }
9349
9589
  }
@@ -9363,7 +9603,7 @@ var BuildRunner = class _BuildRunner {
9363
9603
  * It goes only by where they run from, never by a build ID, since the daemon a build's own tests
9364
9604
  * start must never stop the build it runs in.
9365
9605
  */
9366
- static async sweep(home = join24(herdHome(), "builds"), list = listProcesses) {
9606
+ static async sweep(home = join25(herdHome(), "builds"), list = listProcesses) {
9367
9607
  const left = buildProcesses(list(), null, home);
9368
9608
  await stopPids(left);
9369
9609
  return left.length;
@@ -9375,37 +9615,37 @@ var BuildRunner = class _BuildRunner {
9375
9615
  */
9376
9616
  static async probe(options = { buildsUrl: "" }) {
9377
9617
  if (insideBuildSandbox()) return { ok: true };
9378
- const herd = options.home ?? join24(herdHome(), "builds");
9379
- const dir = mkdtempSync(join24(scratchRoot(), "hp-"));
9380
- const secret = join24(dir, "owner", "secret");
9618
+ const herd = options.home ?? join25(herdHome(), "builds");
9619
+ const dir = mkdtempSync(join25(scratchRoot(), "hp-"));
9620
+ const secret = join25(dir, "owner", "secret");
9381
9621
  try {
9382
- mkdirSync18(herd, { recursive: true });
9383
- for (const sub of ["work", "git", "home", "owner"]) mkdirSync18(join24(dir, sub));
9622
+ mkdirSync19(herd, { recursive: true });
9623
+ for (const sub of ["work", "git", "home", "owner"]) mkdirSync19(join25(dir, sub));
9384
9624
  writeFileSync14(secret, "probe");
9385
9625
  const policy = buildPolicy({
9386
- ownerHome: join24(dir, "owner"),
9387
- root: join24(dir, "owner"),
9388
- convos: join24(dir, "owner"),
9389
- worktree: join24(dir, "work"),
9390
- store: join24(dir, "git"),
9391
- worktreeGitDir: join24(dir, "git"),
9392
- home: join24(dir, "home"),
9626
+ ownerHome: join25(dir, "owner"),
9627
+ root: join25(dir, "owner"),
9628
+ convos: join25(dir, "owner"),
9629
+ worktree: join25(dir, "work"),
9630
+ store: join25(dir, "git"),
9631
+ worktreeGitDir: join25(dir, "git"),
9632
+ home: join25(dir, "home"),
9393
9633
  nodePrefix: nodePrefix(),
9394
9634
  main: false,
9395
9635
  buildsUrl: options.buildsUrl || "https://builds.herdingchats.com"
9396
9636
  });
9397
- const policyPath = join24(herd, "probe-policy.json");
9637
+ const policyPath = join25(herd, "probe-policy.json");
9398
9638
  writeFileSync14(policyPath, JSON.stringify(policy), { mode: 384 });
9399
9639
  const script = [
9400
9640
  'const fs = require("node:fs");',
9401
9641
  `try { fs.readFileSync(${JSON.stringify(secret)}); process.exit(10); } catch {}`,
9402
- `fs.writeFileSync(${JSON.stringify(join24(dir, "home", "ok"))}, "ok");`
9642
+ `fs.writeFileSync(${JSON.stringify(join25(dir, "home", "ok"))}, "ok");`
9403
9643
  ].join(" ");
9404
- const child = spawn10(
9644
+ const child = spawn11(
9405
9645
  process.execPath,
9406
9646
  [HERD_BIN, "build-sandbox", policyPath, "--", process.execPath, "-e", script],
9407
9647
  {
9408
- env: { PATH: process.env.PATH ?? "/usr/bin:/bin", HOME: join24(dir, "home") },
9648
+ env: { PATH: process.env.PATH ?? "/usr/bin:/bin", HOME: join25(dir, "home") },
9409
9649
  stdio: ["ignore", "ignore", "pipe"]
9410
9650
  }
9411
9651
  );
@@ -9419,7 +9659,7 @@ var BuildRunner = class _BuildRunner {
9419
9659
  clearTimeout(timer);
9420
9660
  if (status2 === 10)
9421
9661
  return { ok: false, reason: "the sandbox let a build read the owner's files" };
9422
- if (status2 !== 0 || !existsSync17(join24(dir, "home", "ok"))) {
9662
+ if (status2 !== 0 || !existsSync17(join25(dir, "home", "ok"))) {
9423
9663
  const why = stderr.trim().split("\n").at(-1) || `the probe exited with ${status2}`;
9424
9664
  return { ok: false, reason: why.slice(0, 300) };
9425
9665
  }
@@ -9427,7 +9667,7 @@ var BuildRunner = class _BuildRunner {
9427
9667
  } catch (error) {
9428
9668
  return { ok: false, reason: String(error).slice(0, 300) };
9429
9669
  } finally {
9430
- rmSync13(dir, { recursive: true, force: true });
9670
+ rmSync14(dir, { recursive: true, force: true });
9431
9671
  }
9432
9672
  }
9433
9673
  /** Stops a build's process group and every process it started, however it got away. */
@@ -9456,7 +9696,7 @@ function listProcesses() {
9456
9696
  function listFromProc() {
9457
9697
  let pids = [];
9458
9698
  try {
9459
- pids = readdirSync8("/proc").filter((name) => /^\d+$/.test(name));
9699
+ pids = readdirSync9("/proc").filter((name) => /^\d+$/.test(name));
9460
9700
  } catch {
9461
9701
  return [];
9462
9702
  }
@@ -9634,7 +9874,7 @@ var DEFAULT_TIMING3 = {
9634
9874
  confirmMs: 8e3,
9635
9875
  pasteMs: 600
9636
9876
  };
9637
- var sleep5 = (ms2) => new Promise((resolve8) => setTimeout(resolve8, ms2));
9877
+ var sleep6 = (ms2) => new Promise((resolve8) => setTimeout(resolve8, ms2));
9638
9878
  var EXIT_WAIT_MS3 = 5e3;
9639
9879
  var CodexDriver = class {
9640
9880
  constructor(terminals, timing = {}) {
@@ -9657,7 +9897,7 @@ var CodexDriver = class {
9657
9897
  const reading = await this.read(name);
9658
9898
  if (check2(reading)) return reading;
9659
9899
  if (Date.now() > end) return null;
9660
- await sleep5(this.timing.pollMs);
9900
+ await sleep6(this.timing.pollMs);
9661
9901
  }
9662
9902
  }
9663
9903
  /** Waits for a terminal's output to stop changing, as a shell does once it's ready. */
@@ -9673,7 +9913,7 @@ var CodexDriver = class {
9673
9913
  } else if (Date.now() - stableSince >= this.timing.settleMs) {
9674
9914
  return;
9675
9915
  }
9676
- await sleep5(this.timing.pollMs);
9916
+ await sleep6(this.timing.pollMs);
9677
9917
  }
9678
9918
  }
9679
9919
  /**
@@ -9739,10 +9979,10 @@ var CodexDriver = class {
9739
9979
  }
9740
9980
  });
9741
9981
  });
9742
- const confirmed = () => Promise.race([reported, sleep5(this.timing.confirmMs)]);
9982
+ const confirmed = () => Promise.race([reported, sleep6(this.timing.confirmMs)]);
9743
9983
  try {
9744
9984
  if (!leftover) await this.terminals.type(name, text4);
9745
- await sleep5(this.timing.pasteMs);
9985
+ await sleep6(this.timing.pasteMs);
9746
9986
  const head = sent.slice(0, 40);
9747
9987
  let inBox = true;
9748
9988
  for (let attempt = 0; attempt <= SUBMIT_RETRIES && !taken; attempt++) {
@@ -9772,7 +10012,7 @@ var CodexDriver = class {
9772
10012
  async exit(name) {
9773
10013
  if ((await this.read(name)).program !== "codex") return;
9774
10014
  await this.terminals.type(name, "/exit");
9775
- await sleep5(this.timing.pasteMs);
10015
+ await sleep6(this.timing.pasteMs);
9776
10016
  await this.terminals.press(name, "enter");
9777
10017
  await this.until(name, (reading) => reading.program !== "codex", EXIT_WAIT_MS3);
9778
10018
  }
@@ -9780,7 +10020,7 @@ var CodexDriver = class {
9780
10020
 
9781
10021
  // src/daemon.ts
9782
10022
  init_define_HERD_PACKAGE();
9783
- import { join as join25 } from "node:path";
10023
+ import { join as join26 } from "node:path";
9784
10024
 
9785
10025
  // src/dispatch-connection.ts
9786
10026
  init_define_HERD_PACKAGE();
@@ -9895,7 +10135,7 @@ var DispatchConnection = class {
9895
10135
 
9896
10136
  // src/daemon.ts
9897
10137
  var CONFIG_POLL_MS = 2e3;
9898
- var daemonSocketPath = () => join25(herdHome(), "daemon.sock");
10138
+ var daemonSocketPath = () => join26(herdHome(), "daemon.sock");
9899
10139
  var Daemon = class {
9900
10140
  constructor(options) {
9901
10141
  this.options = options;
@@ -10108,7 +10348,7 @@ var Daemon = class {
10108
10348
 
10109
10349
  // src/folders.ts
10110
10350
  init_define_HERD_PACKAGE();
10111
- import { existsSync as existsSync18, lstatSync as lstatSync3, readlinkSync as readlinkSync2, rmSync as rmSync14, writeFileSync as writeFileSync15 } from "node:fs";
10351
+ import { existsSync as existsSync18, lstatSync as lstatSync3, readlinkSync as readlinkSync2, rmSync as rmSync15, writeFileSync as writeFileSync15 } from "node:fs";
10112
10352
  import { dirname as dirname14, resolve as resolve5 } from "node:path";
10113
10353
  var FOLDER_WAIT_MS = 2 * 60 * 1e3;
10114
10354
  var FOLDER_CHECK_MS = 30 * 1e3;
@@ -10191,7 +10431,7 @@ var FolderWatch = class {
10191
10431
  write() {
10192
10432
  try {
10193
10433
  if (this.current) writeFileSync15(this.options.statusPath, JSON.stringify(this.current));
10194
- else rmSync14(this.options.statusPath, { force: true });
10434
+ else rmSync15(this.options.statusPath, { force: true });
10195
10435
  } catch {
10196
10436
  }
10197
10437
  }
@@ -10200,7 +10440,7 @@ var FolderWatch = class {
10200
10440
  // src/gh.ts
10201
10441
  init_define_HERD_PACKAGE();
10202
10442
  init_src();
10203
- import { execFile as execFile4, spawn as spawn11 } from "node:child_process";
10443
+ import { execFile as execFile4, spawn as spawn12 } from "node:child_process";
10204
10444
  import { promisify as promisify3 } from "node:util";
10205
10445
  var GH_CHECK_MS = 10 * 60 * 1e3;
10206
10446
  var GH_CODE_WAIT_MS = 30 * 1e3;
@@ -10271,7 +10511,7 @@ function readDeviceCode(output2) {
10271
10511
  return code && url === GITHUB_DEVICE_URL ? { code, url: GITHUB_DEVICE_URL } : null;
10272
10512
  }
10273
10513
  async function signInGh(options) {
10274
- const spawnGh = options.spawnGh ?? ((args) => spawn11("gh", args, {
10514
+ const spawnGh = options.spawnGh ?? ((args) => spawn12("gh", args, {
10275
10515
  env: options.env?.() ?? process.env,
10276
10516
  stdio: ["ignore", "pipe", "pipe"]
10277
10517
  }));
@@ -10324,20 +10564,20 @@ import { execFileSync as execFileSync6 } from "node:child_process";
10324
10564
  import {
10325
10565
  appendFileSync as appendFileSync2,
10326
10566
  existsSync as existsSync19,
10327
- mkdirSync as mkdirSync19,
10567
+ mkdirSync as mkdirSync20,
10328
10568
  readFileSync as readFileSync18,
10329
10569
  renameSync as renameSync6,
10330
10570
  writeFileSync as writeFileSync16
10331
10571
  } from "node:fs";
10332
- import { isAbsolute as isAbsolute5, join as join26 } from "node:path";
10333
- var LOCAL_SETTINGS = join26(".claude", "settings.local.json");
10572
+ import { isAbsolute as isAbsolute5, join as join27 } from "node:path";
10573
+ var LOCAL_SETTINGS = join27(".claude", "settings.local.json");
10334
10574
  var git3 = (cwd, ...args) => execFileSync6("git", ["-C", cwd, ...args], {
10335
10575
  encoding: "utf8",
10336
10576
  stdio: ["ignore", "pipe", "pipe"]
10337
10577
  }).trim();
10338
10578
  var shellQuote2 = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
10339
10579
  function writeWorktreeSettings(worktree, settings, ownCommand) {
10340
- const path = join26(worktree, LOCAL_SETTINGS);
10580
+ const path = join27(worktree, LOCAL_SETTINGS);
10341
10581
  let current = {};
10342
10582
  if (existsSync19(path)) {
10343
10583
  try {
@@ -10361,7 +10601,7 @@ function writeWorktreeSettings(worktree, settings, ownCommand) {
10361
10601
  }
10362
10602
  next.hooks = hooks;
10363
10603
  }
10364
- mkdirSync19(join26(worktree, ".claude"), { recursive: true });
10604
+ mkdirSync20(join27(worktree, ".claude"), { recursive: true });
10365
10605
  const temporary = `${path}.herd-${process.pid}`;
10366
10606
  writeFileSync16(temporary, `${JSON.stringify(next, null, 2)}
10367
10607
  `);
@@ -10381,12 +10621,12 @@ function ignoreLocalSettings(worktree) {
10381
10621
  } catch {
10382
10622
  return;
10383
10623
  }
10384
- const info = join26(isAbsolute5(common) ? common : join26(worktree, common), "info");
10385
- const exclude = join26(info, "exclude");
10624
+ const info = join27(isAbsolute5(common) ? common : join27(worktree, common), "info");
10625
+ const exclude = join27(info, "exclude");
10386
10626
  const line2 = "/.claude/settings.local.json";
10387
10627
  const existing = existsSync19(exclude) ? readFileSync18(exclude, "utf8") : "";
10388
10628
  if (existing.split("\n").includes(line2)) return;
10389
- mkdirSync19(info, { recursive: true });
10629
+ mkdirSync20(info, { recursive: true });
10390
10630
  appendFileSync2(exclude, `${existing && !existing.endsWith("\n") ? "\n" : ""}${line2}
10391
10631
  `);
10392
10632
  }
@@ -10429,7 +10669,7 @@ function loginShellEnv() {
10429
10669
 
10430
10670
  // src/machine-checks.ts
10431
10671
  init_define_HERD_PACKAGE();
10432
- import { existsSync as existsSync20, rmSync as rmSync15 } from "node:fs";
10672
+ import { existsSync as existsSync20, rmSync as rmSync16 } from "node:fs";
10433
10673
  var REQUEST_POLL_MS = 2e3;
10434
10674
  var MachineChecks = class {
10435
10675
  constructor(options) {
@@ -10476,7 +10716,7 @@ var MachineChecks = class {
10476
10716
  takeRequest() {
10477
10717
  const path = activationRequestPath(this.options.herdHome);
10478
10718
  if (!existsSync20(path)) return;
10479
- rmSync15(path, { force: true });
10719
+ rmSync16(path, { force: true });
10480
10720
  this.activateChosen(true);
10481
10721
  }
10482
10722
  start() {
@@ -10522,237 +10762,6 @@ var MachineChecks = class {
10522
10762
  }
10523
10763
  };
10524
10764
 
10525
- // src/terminals.ts
10526
- init_define_HERD_PACKAGE();
10527
- import { spawn as spawn12 } from "node:child_process";
10528
- import { mkdirSync as mkdirSync20, readdirSync as readdirSync9, rmSync as rmSync16 } from "node:fs";
10529
- import { connect as connect2 } from "node:net";
10530
- import { join as join27 } from "node:path";
10531
- init_self();
10532
- import {
10533
- HOST_PROTOCOL,
10534
- hostVersion
10535
- } from "./terminal-host.mjs";
10536
- var hostsDir = () => join27(herdHome(), "terminals");
10537
- var legacyHostSocketPath = () => join27(herdHome(), "terminals.sock");
10538
- var currentHostSocketPath = (version = hostVersion()) => join27(hostsDir(), `${version}.sock`);
10539
- var speaksProtocol = (protocol) => protocol === HOST_PROTOCOL || protocol === HOST_PROTOCOL - 1;
10540
- var sleep6 = (ms2) => new Promise((resolve8) => setTimeout(resolve8, ms2));
10541
- function askHost(path, request) {
10542
- return new Promise((resolve8, reject) => {
10543
- const socket = connect2(path);
10544
- let data = "";
10545
- socket.on("connect", () => socket.write(`${JSON.stringify(request)}
10546
- `));
10547
- socket.on("data", (chunk) => data += chunk);
10548
- socket.on("end", () => {
10549
- try {
10550
- const reply = JSON.parse(data);
10551
- if (reply.ok) resolve8(reply);
10552
- else reject(new HerdError(reply.error ?? "The terminal host refused that."));
10553
- } catch {
10554
- reject(new HerdError("The terminal host's answer wasn't readable."));
10555
- }
10556
- });
10557
- socket.on("error", reject);
10558
- });
10559
- }
10560
- var isDead = (error) => {
10561
- const code = error?.code;
10562
- return code === "ECONNREFUSED" || code === "ENOENT";
10563
- };
10564
- var HostTerminals = class {
10565
- hosts = [];
10566
- /** Which host each terminal name was last found in. */
10567
- where = /* @__PURE__ */ new Map();
10568
- version;
10569
- log;
10570
- constructor(options = {}) {
10571
- this.version = options.version ?? hostVersion();
10572
- this.log = options.log ?? (() => {
10573
- });
10574
- }
10575
- currentVersion() {
10576
- return this.version;
10577
- }
10578
- unreachable(version) {
10579
- return this.hosts.some((host) => host.hello.version === version && !host.speaks);
10580
- }
10581
- /** Every host that answers, with sockets nothing answers on removed. */
10582
- async discover() {
10583
- const dir = hostsDir();
10584
- let names = [];
10585
- try {
10586
- names = readdirSync9(dir).filter((name) => name.endsWith(".sock"));
10587
- } catch {
10588
- }
10589
- const paths = [legacyHostSocketPath(), ...names.map((name) => join27(dir, name))];
10590
- const found = [];
10591
- for (const path of paths) {
10592
- try {
10593
- const hello = await askHost(path, { op: "hello" });
10594
- found.push({
10595
- path,
10596
- hello,
10597
- current: hello.version === this.version && speaksProtocol(hello.protocol),
10598
- speaks: speaksProtocol(hello.protocol)
10599
- });
10600
- } catch (error) {
10601
- if (isDead(error) && path !== legacyHostSocketPath()) rmSync16(path, { force: true });
10602
- }
10603
- }
10604
- this.hosts = found;
10605
- return found;
10606
- }
10607
- current() {
10608
- return this.hosts.find((host) => host.current);
10609
- }
10610
- /** Starts a host of the daemon's own version if none is running, detached so it outlives it. */
10611
- async ensureHost() {
10612
- const hosts = await this.discover();
10613
- for (const host of hosts) {
10614
- const version = host.hello.version ?? "unversioned";
10615
- const role = host.current ? "current" : host.speaks ? "older" : "not driveable";
10616
- this.log(
10617
- `Terminal host ${version} (protocol ${host.hello.protocol}, pid ${host.hello.pid}): ${role}.`
10618
- );
10619
- }
10620
- if (this.current()) return;
10621
- const path = currentHostSocketPath(this.version);
10622
- mkdirSync20(hostsDir(), { recursive: true, mode: 448 });
10623
- const child = spawn12(process.execPath, [herdBin, "terminal-host", "--socket", path], {
10624
- detached: true,
10625
- stdio: "ignore",
10626
- env: { ...process.env, HERD_HOME: herdHome() }
10627
- });
10628
- child.unref();
10629
- for (let attempt = 0; attempt < 50 && !this.current(); attempt++) {
10630
- await sleep6(100);
10631
- const hello = await askHost(path, { op: "hello" }).catch(() => null);
10632
- if (!hello) continue;
10633
- if (hello.version !== this.version) {
10634
- throw new HerdError(
10635
- `The terminal host started as version ${hello.version}, not ${this.version}, so its files changed as it started. Starting the daemon again picks this up.`
10636
- );
10637
- }
10638
- this.hosts.push({ path, hello, current: true, speaks: speaksProtocol(hello.protocol) });
10639
- }
10640
- if (!this.current()) throw new HerdError("The terminal host didn't start.");
10641
- this.log(`Started terminal host ${this.version}.`);
10642
- }
10643
- /**
10644
- * Every driveable host's terminals. A name in more than one host, as when a session moved, is
10645
- * the running one, or else the newest.
10646
- */
10647
- async list() {
10648
- if (this.hosts.length === 0) await this.discover();
10649
- const byName = /* @__PURE__ */ new Map();
10650
- for (const host of this.hosts.filter((candidate) => candidate.speaks)) {
10651
- const terminals = await askHost(host.path, { op: "list" }).then((reply) => reply.terminals).catch(() => []);
10652
- for (const info of terminals) {
10653
- const seen = byName.get(info.name);
10654
- const better = !seen || seen.info.exited && !info.exited || seen.info.exited === info.exited && info.createdAt > seen.info.createdAt;
10655
- if (better) byName.set(info.name, { info, path: host.path });
10656
- }
10657
- }
10658
- this.where = new Map([...byName].map(([name, { path }]) => [name, path]));
10659
- return [...byName.values()].map(({ info }) => info);
10660
- }
10661
- /** The socket of the host holding a terminal. */
10662
- async pathOf(name) {
10663
- let path = this.where.get(name);
10664
- if (!path) {
10665
- await this.list();
10666
- path = this.where.get(name);
10667
- }
10668
- if (!path) throw new HerdError(`There's no terminal named ${name}.`);
10669
- return path;
10670
- }
10671
- async askFor(name, request) {
10672
- return askHost(await this.pathOf(name), request);
10673
- }
10674
- async create(opts) {
10675
- const running = (await this.list()).find((t) => t.name === opts.name && !t.exited);
10676
- if (running) throw new HerdError(`A terminal named ${opts.name} is already running.`);
10677
- let host = this.current();
10678
- if (!host) {
10679
- await this.ensureHost();
10680
- host = this.current();
10681
- }
10682
- if (!host) throw new HerdError("The terminal host didn't start.");
10683
- const { terminal } = await askHost(host.path, {
10684
- op: "create",
10685
- ...opts
10686
- });
10687
- this.where.set(opts.name, host.path);
10688
- return terminal;
10689
- }
10690
- async screen(name) {
10691
- return (await this.askFor(name, { op: "screen", name })).screen;
10692
- }
10693
- async snapshot(name) {
10694
- return (await this.askFor(name, { op: "snapshot", name })).snapshot;
10695
- }
10696
- async type(name, text4) {
10697
- await this.askFor(name, { op: "type", name, text: text4 });
10698
- }
10699
- async press(name, key) {
10700
- await this.askFor(name, { op: "press", name, key });
10701
- }
10702
- async exitStatus(name) {
10703
- const terminal = (await this.list()).find((candidate) => candidate.name === name);
10704
- if (!terminal) throw new HerdError(`There's no terminal named ${name}.`);
10705
- return terminal.exited ? terminal.exitCode : null;
10706
- }
10707
- async kill(name) {
10708
- await this.askFor(name, { op: "kill", name });
10709
- }
10710
- async retire(canStop) {
10711
- const stopped2 = [];
10712
- for (const host of await this.discover()) {
10713
- if (host.current) continue;
10714
- const version = host.hello.version ?? "unversioned";
10715
- if (host.speaks) {
10716
- const terminals = await askHost(host.path, { op: "list" }).then((reply) => reply.terminals).catch(() => null);
10717
- if (!terminals) continue;
10718
- if (terminals.some((t) => !t.exited || (t.viewers ?? 0) > 0)) continue;
10719
- } else if (!host.hello.version || !canStop(host.hello.version)) {
10720
- continue;
10721
- }
10722
- if (await this.stop(host)) {
10723
- stopped2.push(version);
10724
- this.log(`Retired terminal host ${version}, which had nothing left running.`);
10725
- }
10726
- }
10727
- if (stopped2.length > 0) await this.discover();
10728
- return stopped2;
10729
- }
10730
- /** Stops a host: politely where it knows how, and otherwise by its process. */
10731
- async stop(host) {
10732
- if (host.speaks && host.hello.ops?.includes("shutdown")) {
10733
- return askHost(host.path, { op: "shutdown" }).then(
10734
- () => true,
10735
- () => false
10736
- );
10737
- }
10738
- const hello = await askHost(host.path, { op: "hello" }).catch(() => null);
10739
- if (!hello || hello.pid !== host.hello.pid) return false;
10740
- try {
10741
- process.kill(hello.pid, "SIGTERM");
10742
- } catch {
10743
- return false;
10744
- }
10745
- if (host.path === legacyHostSocketPath()) {
10746
- for (let attempt = 0; attempt < 20; attempt++) {
10747
- await sleep6(100);
10748
- if (!await askHost(host.path, { op: "hello" }).catch(() => null)) break;
10749
- }
10750
- rmSync16(host.path, { force: true });
10751
- }
10752
- return true;
10753
- }
10754
- };
10755
-
10756
10765
  // src/usage-reporter.ts
10757
10766
  init_define_HERD_PACKAGE();
10758
10767
  var USAGE_REPORT_MS = 5 * 60 * 1e3;
@@ -11962,7 +11971,7 @@ async function watchOne(ws, conversationId, terminals, rows = []) {
11962
11971
  const dialog = listed?.dialog ?? reading?.dialog ?? null;
11963
11972
  await followInvestigation(ws, workspace);
11964
11973
  workspace = ws.get(conversationId) ?? workspace;
11965
- if (needsInvestigator(ws, workspace, reading)) {
11974
+ if ((lead?.viewers ?? 0) === 0 && needsInvestigator(ws, workspace, reading)) {
11966
11975
  ws.inBackground(
11967
11976
  openInvestigator(ws, workspace).catch(
11968
11977
  (error) => ws.log(`${conversationId}: the investigator didn't start: ${error}`)
@@ -12015,6 +12024,7 @@ async function watchOne(ws, conversationId, terminals, rows = []) {
12015
12024
  const owed = workspace.terminalNotice;
12016
12025
  const retryAt = (workspace.noticeFailedAt ?? 0) + (ws.options.noticeRetryMs ?? NOTICE_RETRY_MS);
12017
12026
  if (!owed && Date.now() < retryAt) return;
12027
+ if (someoneDriving(lead, ws.options.driverQuietMs)) return;
12018
12028
  ws.update(conversationId, { deferred: false, terminalNotice: false });
12019
12029
  const claimed = workspace.deferred && await claim(ws, workspace);
12020
12030
  if (claimed || owed) {
@@ -12033,7 +12043,7 @@ async function watchOne(ws, conversationId, terminals, rows = []) {
12033
12043
  if (idle.idleSince === null && !idle.claimId) {
12034
12044
  idle = ws.update(conversationId, { idleSince: Date.now() });
12035
12045
  }
12036
- await shutDownIfDone(ws, idle, reading.draft, lead.viewers);
12046
+ await shutDownIfDone(ws, idle, reading.draft, lookingAt(lead));
12037
12047
  }
12038
12048
  }
12039
12049
  function unreachable(ws, workspace) {
@@ -12095,7 +12105,7 @@ async function endTurn(ws, workspace) {
12095
12105
  async function shutDownIfDone(ws, workspace, draft, viewers) {
12096
12106
  if (workspace.idleSince === null || workspace.claimId) return;
12097
12107
  if (!workspace.exitRequested && Date.now() - workspace.idleSince < ws.idleMs) return;
12098
- if (draft || (viewers ?? 0) > 0) return;
12108
+ if (draft || viewers > 0) return;
12099
12109
  await ws.driverFor(workspace).exit(workspace.lead);
12100
12110
  await ws.options.terminals.kill(workspace.lead);
12101
12111
  await ws.report(workspace.conversationId, "exited");
@@ -12161,9 +12171,13 @@ async function deliver(ws, conversationId, delivery) {
12161
12171
  if (!workspace.startProven) workspace = ws.update(conversationId, { checkInFrom: Date.now() });
12162
12172
  return afterSubmit(ws, workspace, result2);
12163
12173
  }
12174
+ const lead = (await ws.options.terminals.list()).find((t) => t.name === workspace.lead);
12175
+ if (someoneDriving(lead, ws.options.driverQuietMs)) {
12176
+ ws.update(conversationId, { deferred: true });
12177
+ return;
12178
+ }
12164
12179
  let reading = await ws.driverFor(workspace).read(workspace.lead);
12165
12180
  if (reading.knownDialog) {
12166
- const lead = (await ws.options.terminals.list()).find((t) => t.name === workspace.lead);
12167
12181
  if (await closeKnownDialog(ws, workspace, reading.knownDialog, lead?.viewers ?? 0)) {
12168
12182
  reading = await ws.driverFor(workspace).read(workspace.lead);
12169
12183
  }
@@ -13883,6 +13897,7 @@ async function runDaemon(args) {
13883
13897
  ...timing.agyIdleSettleMs === void 0 ? {} : { agyIdleSettleMs: timing.agyIdleSettleMs },
13884
13898
  ...timing.promptPollMs === void 0 ? {} : { promptPollMs: timing.promptPollMs },
13885
13899
  ...timing.noticeRetryMs === void 0 ? {} : { noticeRetryMs: timing.noticeRetryMs },
13900
+ ...timing.driverQuietMs === void 0 ? {} : { driverQuietMs: timing.driverQuietMs },
13886
13901
  ...timing.investigateAfterMs === void 0 ? {} : { investigateAfterMs: timing.investigateAfterMs },
13887
13902
  config: () => loadConfig(),
13888
13903
  root: () => machineRoot(),
@@ -14580,7 +14595,7 @@ init_define_HERD_PACKAGE();
14580
14595
  init_define_HERD_PACKAGE();
14581
14596
  import { connect as connect4 } from "node:net";
14582
14597
  var DETACH_KEY = "";
14583
- function attach(name, { stdin, stdout } = { stdin: process.stdin, stdout: process.stdout }, path = currentHostSocketPath()) {
14598
+ function attach(name, { stdin, stdout } = { stdin: process.stdin, stdout: process.stdout }, path = currentHostSocketPath(), { watch: watch2 = false } = {}) {
14584
14599
  return new Promise((resolve8, reject) => {
14585
14600
  const socket = connect4(path);
14586
14601
  const size = () => ({ cols: stdout.columns || 120, rows: stdout.rows || 40 });
@@ -14601,7 +14616,7 @@ function attach(name, { stdin, stdout } = { stdin: process.stdin, stdout: proces
14601
14616
  const data = chunk.toString("utf8");
14602
14617
  const detach = data.indexOf(DETACH_KEY);
14603
14618
  const before = detach >= 0 ? data.slice(0, detach) : data;
14604
- if (before) socket.write(`${JSON.stringify({ type: "input", data: before })}
14619
+ if (before && !watch2) socket.write(`${JSON.stringify({ type: "input", data: before })}
14605
14620
  `);
14606
14621
  if (detach >= 0) {
14607
14622
  stdout.write("\r\n[detached; it's still running]\r\n");
@@ -14612,8 +14627,10 @@ function attach(name, { stdin, stdout } = { stdin: process.stdin, stdout: proces
14612
14627
  `);
14613
14628
  socket.on(
14614
14629
  "connect",
14615
- () => socket.write(`${JSON.stringify({ op: "attach", name, ...size() })}
14616
- `)
14630
+ () => socket.write(
14631
+ `${JSON.stringify(watch2 ? { op: "watch", name } : { op: "attach", name, ...size() })}
14632
+ `
14633
+ )
14617
14634
  );
14618
14635
  socket.on("data", (chunk) => {
14619
14636
  buffer += chunk;
@@ -14634,7 +14651,7 @@ function attach(name, { stdin, stdout } = { stdin: process.stdin, stdout: proces
14634
14651
  if (stdin.isTTY) stdin.setRawMode(true);
14635
14652
  stdin.on("data", onInput);
14636
14653
  stdin.resume();
14637
- stdout.on("resize", onResize);
14654
+ if (!watch2) stdout.on("resize", onResize);
14638
14655
  }
14639
14656
  } else if (message.type === "data") {
14640
14657
  stdout.write(message.data ?? "");
@@ -14657,7 +14674,8 @@ function attach(name, { stdin, stdout } = { stdin: process.stdin, stdout: proces
14657
14674
  import { serveHost } from "./terminal-host.mjs";
14658
14675
  async function runAttach(rest) {
14659
14676
  const terminals = new HostTerminals();
14660
- const name = rest[0];
14677
+ const watch2 = rest.includes("--watch");
14678
+ const name = rest.find((arg) => arg !== "--watch");
14661
14679
  if (!name) {
14662
14680
  const running = (await terminals.list().catch(() => [])).filter((t) => !t.exited);
14663
14681
  console.log(
@@ -14666,7 +14684,7 @@ ${running.map((t) => ` ${t.name} (${t.cwd})`).join("\n")}` : "No terminals are
14666
14684
  );
14667
14685
  return 0;
14668
14686
  }
14669
- const result = await attach(name, void 0, await terminals.pathOf(name));
14687
+ const result = await attach(name, void 0, await terminals.pathOf(name), { watch: watch2 });
14670
14688
  return result === "detached" ? 0 : result.exitCode ?? 1;
14671
14689
  }
14672
14690
  async function runTerminalHost(rest) {
@@ -15241,7 +15259,7 @@ On this computer:
15241
15259
  daemon [--repo <path>]...
15242
15260
  Run this machine's daemon, working from its root folder. Repos given with
15243
15261
  --repo are only listed as known, since herd works in its own clones
15244
- attach [<terminal>] Watch or take over a terminal. Ctrl+\\ detaches. With no name, lists them
15262
+ attach [<terminal>] Take over a terminal, or only watch it with --watch. Ctrl+\\ detaches. With no name, lists them
15245
15263
  builds [register|speed]
15246
15264
  Whether this machine is a build runner, register it, with its owner's
15247
15265
  approval in the browser, or show how long builds take. See builds --help