claude-yes 1.160.3 → 1.162.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
- import { t as CLIS_CONFIG } from "./ts-DcZvAsIp.js";
1
+ import { t as CLIS_CONFIG } from "./ts--UKFJGU-.js";
2
2
 
3
3
  //#region ts/SUPPORTED_CLIS.ts
4
4
  const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
5
5
 
6
6
  //#endregion
7
7
  export { SUPPORTED_CLIS as t };
8
- //# sourceMappingURL=SUPPORTED_CLIS-WcYvfnJQ.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-BNLYYzBZ.js.map
@@ -0,0 +1,8 @@
1
+ import "./ts--UKFJGU-.js";
2
+ import "./logger-CDIsZ-Pp.js";
3
+ import "./versionChecker-DJObC8O_.js";
4
+ import "./pidStore-BfoBWUjc.js";
5
+ import "./globalPidIndex-DlmmJlO8.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BNLYYzBZ.js";
7
+
8
+ export { SUPPORTED_CLIS };
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  import { t as invokedCliName } from "./invokedCli-uqM2YYA7.js";
3
3
  import { n as logger } from "./logger-CDIsZ-Pp.js";
4
- import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-DOse329y.js";
4
+ import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-DJObC8O_.js";
5
5
  import { argv } from "process";
6
6
  import { execFileSync, spawn } from "child_process";
7
7
  import ms from "ms";
@@ -481,7 +481,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
481
481
  const rawArg = process.argv[2];
482
482
  const managerCommands = !invokedCliName(process.argv);
483
483
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
484
- const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-BJ2xKtDu.js");
484
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-DeW-4FGU.js");
485
485
  if (isHelpFlag && process.argv.length === 3) {
486
486
  cmdHelp(managerCommands);
487
487
  process.exit(0);
@@ -514,7 +514,7 @@ if (config.useRust) {
514
514
  }
515
515
  }
516
516
  if (rustBinary) {
517
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-pKjUYMIj.js");
517
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-C9TQK7ME.js");
518
518
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
519
519
  if (config.verbose) {
520
520
  console.log(`[rust] Using binary: ${rustBinary}`);
@@ -544,7 +544,7 @@ if (config.showVersion) {
544
544
  process.exit(0);
545
545
  }
546
546
  if (config.appendPrompt) {
547
- const { PidStore } = await import("./pidStore-DfHh3Yt0.js");
547
+ const { PidStore } = await import("./pidStore-5qyLX3Fh.js");
548
548
  const ipcPath = await PidStore.findActiveFifo(process.cwd());
549
549
  if (!ipcPath) {
550
550
  console.error("No active agent with IPC found in current directory.");
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-DcZvAsIp.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts--UKFJGU-.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DOse329y.js";
4
- import "./pidStore-fqXqTKkh.js";
3
+ import "./versionChecker-DJObC8O_.js";
4
+ import "./pidStore-BfoBWUjc.js";
5
5
  import "./globalPidIndex-DlmmJlO8.js";
6
6
 
7
7
  export { AgentContext, CLIS_CONFIG, config, agentYes as default, removeControlCharacters };
@@ -0,0 +1,5 @@
1
+ import "./logger-CDIsZ-Pp.js";
2
+ import { t as PidStore } from "./pidStore-BfoBWUjc.js";
3
+ import "./globalPidIndex-DlmmJlO8.js";
4
+
5
+ export { PidStore };
@@ -209,8 +209,9 @@ var PidStore = class PidStore {
209
209
  const argsJson = JSON.stringify(args);
210
210
  const logFile = this.getRawLogPath(pid);
211
211
  const fifoFile = this.getFifoPath(pid);
212
+ const injected = process.env.AGENT_YES_AGENT_ID;
212
213
  const existing = this.store.findOne((doc) => doc.pid === pid);
213
- const agentId = existing?.agentId ?? randomBytes(6).toString("hex");
214
+ const agentId = existing?.agentId ?? (injected && /^[0-9a-f]{6,32}$/i.test(injected) ? injected : randomBytes(6).toString("hex"));
214
215
  const record = {
215
216
  pid,
216
217
  cli,
@@ -367,4 +368,4 @@ pid-db/
367
368
 
368
369
  //#endregion
369
370
  export { PidStore as t };
370
- //# sourceMappingURL=pidStore-fqXqTKkh.js.map
371
+ //# sourceMappingURL=pidStore-BfoBWUjc.js.map
@@ -1,9 +1,9 @@
1
- import "./ts-DcZvAsIp.js";
1
+ import "./ts--UKFJGU-.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DOse329y.js";
4
- import "./pidStore-fqXqTKkh.js";
3
+ import "./versionChecker-DJObC8O_.js";
4
+ import "./pidStore-BfoBWUjc.js";
5
5
  import "./globalPidIndex-DlmmJlO8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-WcYvfnJQ.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BNLYYzBZ.js";
7
7
  import { o as resolveSpawnCwd } from "./workspaceConfig-B0Q9-q2B.js";
8
8
  import { createHash } from "node:crypto";
9
9
 
@@ -141,4 +141,4 @@ async function cmdSchedule(rest) {
141
141
 
142
142
  //#endregion
143
143
  export { cmdSchedule };
144
- //# sourceMappingURL=schedule-YIA3BrZV.js.map
144
+ //# sourceMappingURL=schedule-DzFEVEoq.js.map
@@ -1,16 +1,16 @@
1
- import "./ts-DcZvAsIp.js";
1
+ import "./ts--UKFJGU-.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import { r as getInstalledPackage } from "./versionChecker-DOse329y.js";
4
- import "./pidStore-fqXqTKkh.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-DJObC8O_.js";
4
+ import "./pidStore-BfoBWUjc.js";
5
5
  import { a as updateGlobalPidStatus } from "./globalPidIndex-DlmmJlO8.js";
6
6
  import { t as pgidForWrapper } from "./reaper-C-eWAxIj.js";
7
7
  import "./configShared-CEyhl0WH.js";
8
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-WcYvfnJQ.js";
8
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BNLYYzBZ.js";
9
9
  import "./e2e-Bfw7qL9O.js";
10
10
  import "./webrtcLink-CBZkZ-LT.js";
11
11
  import "./remotes-DBCvpp3B.js";
12
12
  import { a as isProvisionAllowed, i as hasSpawnHook, n as getSpawnHook, o as resolveSpawnCwd, t as getProvisionRoot } from "./workspaceConfig-B0Q9-q2B.js";
13
- import { D as writeToIpc, T as snapshotStatus, _ as readPtysize, b as resolveOne, c as extractTaskCounts, g as readNotes, i as controlCodeFromName, m as listRecords, o as deriveLiveStatus, v as renderRawLog } from "./subcommands-Cz17HvuK.js";
13
+ import { D as writeToIpc, T as snapshotStatus, _ as readPtysize, b as resolveOne, c as extractTaskCounts, g as readNotes, i as controlCodeFromName, m as listRecords, o as deriveLiveStatus, v as renderRawLog } from "./subcommands-CKMEWv6U.js";
14
14
  import yargs from "yargs";
15
15
  import { mkdir, open, readFile, stat, unlink, writeFile } from "fs/promises";
16
16
  import { homedir, hostname, userInfo } from "os";
@@ -1200,6 +1200,7 @@ Options:
1200
1200
  ...prompt ? ["--", prompt] : []
1201
1201
  ];
1202
1202
  const agentEnv = freshAgentEnv();
1203
+ const agentId = randomBytes(6).toString("hex");
1203
1204
  try {
1204
1205
  const hook = getSpawnHook();
1205
1206
  if (hook) {
@@ -1218,7 +1219,8 @@ Options:
1218
1219
  env: {
1219
1220
  ...agentEnv,
1220
1221
  AGENT_YES_CWD: cwd,
1221
- AGENT_YES_CLI: cli
1222
+ AGENT_YES_CLI: cli,
1223
+ AGENT_YES_AGENT_ID: agentId
1222
1224
  },
1223
1225
  stdin: "ignore",
1224
1226
  stdout: "ignore",
@@ -1239,6 +1241,7 @@ Options:
1239
1241
  return Response.json({
1240
1242
  ok: true,
1241
1243
  pid: child.pid,
1244
+ agentId,
1242
1245
  cli,
1243
1246
  cwd,
1244
1247
  hook: true,
@@ -1248,7 +1251,10 @@ Options:
1248
1251
  const child = Bun.spawn(agentArgv, {
1249
1252
  cwd,
1250
1253
  detached: true,
1251
- env: agentEnv,
1254
+ env: {
1255
+ ...agentEnv,
1256
+ AGENT_YES_AGENT_ID: agentId
1257
+ },
1252
1258
  stdin: "ignore",
1253
1259
  stdout: "ignore",
1254
1260
  stderr: "ignore"
@@ -1257,6 +1263,7 @@ Options:
1257
1263
  return Response.json({
1258
1264
  ok: true,
1259
1265
  pid: child.pid,
1266
+ agentId,
1260
1267
  cli,
1261
1268
  cwd,
1262
1269
  ...provisioned ? { provisioned } : {}
@@ -1389,4 +1396,4 @@ Options:
1389
1396
 
1390
1397
  //#endregion
1391
1398
  export { cmdServe };
1392
- //# sourceMappingURL=serve-D3vy56FC.js.map
1399
+ //# sourceMappingURL=serve-BFnjq7JW.js.map
@@ -32,7 +32,7 @@ async function cmdSetup(rest) {
32
32
  if (!existsSync(abs)) process.stderr.write(` note: that directory doesn't exist yet — create it, or agents spawned there will fail\n`);
33
33
  if (noShare) return 0;
34
34
  process.stdout.write(`\nsharing this machine to agent-yes.com…\n`);
35
- const { cmdServe } = await import("./serve-D3vy56FC.js");
35
+ const { cmdServe } = await import("./serve-BFnjq7JW.js");
36
36
  return cmdServe([
37
37
  "install",
38
38
  "--share",
@@ -42,4 +42,4 @@ async function cmdSetup(rest) {
42
42
 
43
43
  //#endregion
44
44
  export { cmdSetup };
45
- //# sourceMappingURL=setup-B1meh4do.js.map
45
+ //# sourceMappingURL=setup-BmUQ_Zbt.js.map
@@ -500,6 +500,7 @@ const SUBCOMMANDS = new Set([
500
500
  "tail",
501
501
  "head",
502
502
  "send",
503
+ "spawn",
503
504
  "attach",
504
505
  "stop",
505
506
  "exit",
@@ -548,21 +549,22 @@ async function runSubcommand(argv) {
548
549
  case "tail": return await cmdRead(rest, { mode: "tail" });
549
550
  case "head": return await cmdRead(rest, { mode: "head" });
550
551
  case "send": return await cmdSend(rest);
552
+ case "spawn": return await cmdSpawn(rest);
551
553
  case "attach": return await cmdAttach(rest);
552
554
  case "stop": return await cmdStop(rest);
553
555
  case "exit": return await cmdExit(rest);
554
556
  case "restart": return await cmdRestart(rest);
555
557
  case "note": return await cmdNote(rest);
556
558
  case "serve": {
557
- const { cmdServe } = await import("./serve-D3vy56FC.js");
559
+ const { cmdServe } = await import("./serve-BFnjq7JW.js");
558
560
  return cmdServe(rest);
559
561
  }
560
562
  case "setup": {
561
- const { cmdSetup } = await import("./setup-B1meh4do.js");
563
+ const { cmdSetup } = await import("./setup-BmUQ_Zbt.js");
562
564
  return cmdSetup(rest);
563
565
  }
564
566
  case "schedule": {
565
- const { cmdSchedule } = await import("./schedule-YIA3BrZV.js");
567
+ const { cmdSchedule } = await import("./schedule-DzFEVEoq.js");
566
568
  return cmdSchedule(rest);
567
569
  }
568
570
  case "remote": {
@@ -716,14 +718,15 @@ async function resolveOne(keyword, opts) {
716
718
  async function remoteGet(remote, pathname) {
717
719
  return fetch(`${remote.url}${pathname}`, { headers: { Authorization: `Bearer ${remote.token}` } });
718
720
  }
719
- async function remotePost(remote, pathname, body) {
721
+ async function remotePost(remote, pathname, body, signal) {
720
722
  return fetch(`${remote.url}${pathname}`, {
721
723
  method: "POST",
722
724
  headers: {
723
725
  Authorization: `Bearer ${remote.token}`,
724
726
  "Content-Type": "application/json"
725
727
  },
726
- body: JSON.stringify(body)
728
+ body: JSON.stringify(body),
729
+ ...signal ? { signal } : {}
727
730
  });
728
731
  }
729
732
  async function runRemoteLs(remote, opts) {
@@ -875,6 +878,45 @@ async function runRemoteSend(remote, msg, code) {
875
878
  process.stdout.write(`sent to remote pid ${data.pid} (${remote.url} ${keyword})\n`);
876
879
  return 0;
877
880
  }
881
+ /**
882
+ * Spawn an agent on a remote host by POSTing its existing `/api/spawn`. The
883
+ * remote applies ITS OWN spawn hook + provision allowlist server-side (the hook
884
+ * never crosses the wire). `hint` is the user-typed target (alias or
885
+ * token@host:port) so the printed follow-ups are copy-pasteable.
886
+ *
887
+ * NO retry: a POST that already spawned must never be re-sent (double-spawn). A
888
+ * generous timeout still bounds a half-open/stalled connection — on timeout the
889
+ * result is UNKNOWN, so we say so and point at `ay ls` rather than retrying.
890
+ */
891
+ async function runRemoteSpawn(remote, hint, spec) {
892
+ const SPAWN_TIMEOUT_MS = Number(process.env.AGENT_YES_REMOTE_SPAWN_TIMEOUT_MS) || 12e4;
893
+ let res;
894
+ try {
895
+ res = await remotePost(remote, "/api/spawn", {
896
+ cli: spec.cli,
897
+ cwd: spec.cwd || void 0,
898
+ from: spec.from || void 0,
899
+ prompt: spec.prompt || void 0
900
+ }, AbortSignal.timeout(SPAWN_TIMEOUT_MS));
901
+ } catch (e) {
902
+ const name = e?.name;
903
+ if (name === "TimeoutError" || name === "AbortError") {
904
+ process.stderr.write(`remote spawn: no response from ${remote.url} within ${Math.round(SPAWN_TIMEOUT_MS / 1e3)}s — result UNKNOWN (not retried).\n ay ls ${hint} # check whether it started\n`);
905
+ return 2;
906
+ }
907
+ process.stderr.write(`remote spawn failed: ${e.message}\n`);
908
+ return 1;
909
+ }
910
+ if (!res.ok) {
911
+ process.stderr.write(`remote spawn failed ${res.status}: ${await res.text()}\n`);
912
+ return 1;
913
+ }
914
+ const r = await res.json();
915
+ process.stdout.write(`spawned ${r.cli} on ${remote.url} in ${r.cwd}${r.hook ? " (via spawn hook)" : ""}${r.provisioned ? ` (${r.provisioned.action})` : ""}\n`);
916
+ if (r.agentId && !hint.includes("://")) process.stderr.write(`\n ay tail ${hint}:${r.agentId} # watch its output\n ay status ${hint}:${r.agentId} --wait # block until it needs you\n`);
917
+ else process.stderr.write(`\n ay ls ${hint} # the new ${r.cli} agent appears here\n`);
918
+ return 0;
919
+ }
878
920
  async function runRemoteStatus(remote) {
879
921
  const keyword = remote.keyword ?? "";
880
922
  if (!keyword) {
@@ -1819,6 +1861,40 @@ function extractActivityFromLines(lines) {
1819
1861
  }
1820
1862
  return null;
1821
1863
  }
1864
+ async function cmdSpawn(rest) {
1865
+ const argv = await yargs(rest).usage("Usage: ay spawn <remote> [--cli claude] [--cwd <dir>] [--from <src>] -- <prompt>").option("cli", {
1866
+ type: "string",
1867
+ default: "claude",
1868
+ description: "CLI to wrap (claude|codex|gemini|…)"
1869
+ }).option("cwd", {
1870
+ type: "string",
1871
+ description: "Working dir ON THE REMOTE (resolved against the remote's workspace root)"
1872
+ }).option("from", {
1873
+ type: "string",
1874
+ description: "Provision a worktree from a GitHub source (owner/repo@branch) on the remote"
1875
+ }).option("prompt", {
1876
+ type: "string",
1877
+ description: "Initial prompt (or pass it after `--`)"
1878
+ }).help(false).version(false).exitProcess(false).parseAsync();
1879
+ const target = argv._[0] !== void 0 ? String(argv._[0]) : void 0;
1880
+ if (!target) throw new Error("usage: ay spawn <remote> [--cli X] [--cwd D] [--from S] -- <prompt>");
1881
+ const prompt = String(argv.prompt ?? argv._.slice(1).map(String).join(" "));
1882
+ const remote = await resolveRemoteSpec(target);
1883
+ if (!remote) {
1884
+ process.stderr.write(`ay spawn: '${target}' is not a known remote (token@host:port or a saved alias).\n to spawn locally: ay ${argv.cli}${prompt ? ` -- "${prompt}"` : ""}\n to add a remote: ay remote add <alias> http://<token>@<host>:<port>\n`);
1885
+ return 1;
1886
+ }
1887
+ if (remote.keyword) {
1888
+ process.stderr.write(`ay spawn: target '${target}' carries a ':${remote.keyword}' keyword — spawn takes a remote, not an existing agent. Drop the ':${remote.keyword}'.\n`);
1889
+ return 1;
1890
+ }
1891
+ return runRemoteSpawn(remote, target, {
1892
+ cli: String(argv.cli || "claude"),
1893
+ cwd: argv.cwd ? String(argv.cwd) : void 0,
1894
+ from: argv.from ? String(argv.from) : void 0,
1895
+ prompt: prompt || void 0
1896
+ });
1897
+ }
1822
1898
  async function cmdSend(rest) {
1823
1899
  const argv = await yargs(rest).usage("Usage: ay send <keyword> <msg|-> [options]").option("code", {
1824
1900
  type: "string",
@@ -1858,7 +1934,7 @@ async function cmdSend(rest) {
1858
1934
  const trailing = controlCodeFromName(codeName);
1859
1935
  const record = await resolveOne(keyword, opts);
1860
1936
  const fifoPath = record.fifo_file;
1861
- if (!fifoPath) throw new Error(`pid ${record.pid}: no fifo_file recorded — agent was not started with --stdpush (or was spawned by Rust which doesn't yet support FIFO IPC; see ROADMAP item 10)`);
1937
+ if (!fifoPath) throw new Error(`pid ${record.pid}: no fifo_file recorded — this agent didn't register a stdin FIFO (an older agent, or one not started with --stdpush). Restarting it (ay restart ${record.pid}) re-registers one.`);
1862
1938
  let body;
1863
1939
  if (rawMessage === "-") {
1864
1940
  const chunks = [];
@@ -2719,4 +2795,4 @@ async function cmdResultSet(rest) {
2719
2795
 
2720
2796
  //#endregion
2721
2797
  export { restartHintLines as C, writeToIpc as D, stopTipForCli as E, resolveResumeArgs as S, snapshotStatus as T, readPtysize as _, cursorAbs as a, resolveOne as b, extractTaskCounts as c, isExitRequest as d, isPidAlive as f, readNotes as g, matchKeyword as h, controlCodeFromName as i, finalizedLines as l, listRecords as m, READ_PAGE_DEFAULT as n, deriveLiveStatus as o, isSubcommand as p, cmdHelp as r, extractNeedsInput as s, GRACEFUL_EXIT_COMMANDS as t, isAgentStuck as u, renderRawLog as v, runSubcommand as w, resolveReadWindow as x, renderRawLogLines as y };
2722
- //# sourceMappingURL=subcommands-Cz17HvuK.js.map
2798
+ //# sourceMappingURL=subcommands-CKMEWv6U.js.map
@@ -4,6 +4,6 @@ import "./configShared-CEyhl0WH.js";
4
4
  import "./e2e-Bfw7qL9O.js";
5
5
  import "./webrtcLink-CBZkZ-LT.js";
6
6
  import "./remotes-DBCvpp3B.js";
7
- import { C as restartHintLines, D as writeToIpc, E as stopTipForCli, S as resolveResumeArgs, T as snapshotStatus, _ as readPtysize, a as cursorAbs, b as resolveOne, c as extractTaskCounts, d as isExitRequest, f as isPidAlive, g as readNotes, h as matchKeyword, i as controlCodeFromName, l as finalizedLines, m as listRecords, n as READ_PAGE_DEFAULT, o as deriveLiveStatus, p as isSubcommand, r as cmdHelp, s as extractNeedsInput, t as GRACEFUL_EXIT_COMMANDS, u as isAgentStuck, v as renderRawLog, w as runSubcommand, x as resolveReadWindow, y as renderRawLogLines } from "./subcommands-Cz17HvuK.js";
7
+ import { C as restartHintLines, D as writeToIpc, E as stopTipForCli, S as resolveResumeArgs, T as snapshotStatus, _ as readPtysize, a as cursorAbs, b as resolveOne, c as extractTaskCounts, d as isExitRequest, f as isPidAlive, g as readNotes, h as matchKeyword, i as controlCodeFromName, l as finalizedLines, m as listRecords, n as READ_PAGE_DEFAULT, o as deriveLiveStatus, p as isSubcommand, r as cmdHelp, s as extractNeedsInput, t as GRACEFUL_EXIT_COMMANDS, u as isAgentStuck, v as renderRawLog, w as runSubcommand, x as resolveReadWindow, y as renderRawLogLines } from "./subcommands-CKMEWv6U.js";
8
8
 
9
9
  export { cmdHelp, isSubcommand, runSubcommand };
@@ -1,8 +1,8 @@
1
1
  import { n as logger, t as addTransport } from "./logger-CDIsZ-Pp.js";
2
- import { r as getInstalledPackage } from "./versionChecker-DOse329y.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-DJObC8O_.js";
3
3
  import { t as agentYesHome } from "./agentYesHome-_eJa3DaX.js";
4
4
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-V4qvXgAw.js";
5
- import { t as PidStore } from "./pidStore-fqXqTKkh.js";
5
+ import { t as PidStore } from "./pidStore-BfoBWUjc.js";
6
6
  import { i as readGlobalPids } from "./globalPidIndex-DlmmJlO8.js";
7
7
  import { n as register, r as sweep } from "./reaper-C-eWAxIj.js";
8
8
  import { arch, platform } from "process";
@@ -1216,6 +1216,7 @@ async function agentYes({ cli, cliArgs = [], skipPermissions = false, prompt, ro
1216
1216
  const inheritedAyPid = Number(ptyEnv.AGENT_YES_PID);
1217
1217
  const parentPid = Number.isInteger(inheritedAyPid) && inheritedAyPid > 0 ? inheritedAyPid : void 0;
1218
1218
  ptyEnv.AGENT_YES_PID = String(process.pid);
1219
+ delete ptyEnv.AGENT_YES_AGENT_ID;
1219
1220
  if (cliConf?.env) for (const [key, raw] of Object.entries(cliConf.env)) {
1220
1221
  let unresolved = false;
1221
1222
  const value = raw.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g, (_, name, fallback) => {
@@ -1802,4 +1803,4 @@ function sleep(ms) {
1802
1803
 
1803
1804
  //#endregion
1804
1805
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1805
- //# sourceMappingURL=ts-DcZvAsIp.js.map
1806
+ //# sourceMappingURL=ts--UKFJGU-.js.map
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
7
7
 
8
8
  //#region package.json
9
9
  var name = "claude-yes";
10
- var version = "1.160.3";
10
+ var version = "1.162.0";
11
11
 
12
12
  //#endregion
13
13
  //#region ts/versionChecker.ts
@@ -215,4 +215,4 @@ async function displayVersion() {
215
215
 
216
216
  //#endregion
217
217
  export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
218
- //# sourceMappingURL=versionChecker-DOse329y.js.map
218
+ //# sourceMappingURL=versionChecker-DJObC8O_.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.160.3",
3
+ "version": "1.162.0",
4
4
  "description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
5
5
  "keywords": [
6
6
  "ai",
package/ts/index.ts CHANGED
@@ -374,6 +374,12 @@ export default async function agentYes({
374
374
  const parentPid =
375
375
  Number.isInteger(inheritedAyPid) && inheritedAyPid > 0 ? inheritedAyPid : undefined;
376
376
  ptyEnv.AGENT_YES_PID = String(process.pid);
377
+ // A caller-injected AGENT_YES_AGENT_ID (from `ay serve`'s /api/spawn) is meant
378
+ // for THIS agent's record only — pidStore.registerProcess reads it from our own
379
+ // env. Strip it from the wrapped CLI's env so the agent's subagents (a nested
380
+ // `ay`) don't inherit it and register under the same id (which would make that
381
+ // id ambiguous). The wrapper's own process.env still carries it for pidStore.
382
+ delete ptyEnv.AGENT_YES_AGENT_ID;
377
383
  // Inject per-CLI env (e.g. glm → Z.AI endpoint). Expand ${VAR} against the
378
384
  // launching env; skip entries whose vars are unset so we never blank out an
379
385
  // inherited value (e.g. ANTHROPIC_AUTH_TOKEN when ZAI_API_KEY isn't exported).
package/ts/pidStore.ts CHANGED
@@ -77,9 +77,15 @@ export class PidStore {
77
77
  const fifoFile = this.getFifoPath(pid);
78
78
 
79
79
  // Upsert by pid. Reuse an existing record's agent id so re-registration
80
- // (e.g. status churn) keeps the id stable; mint a fresh 12-hex id otherwise.
80
+ // (e.g. status churn) keeps the id stable; else adopt a caller-injected
81
+ // AGENT_YES_AGENT_ID (so `ay serve`'s /api/spawn can hand back an id that
82
+ // addresses this exact agent — index.ts strips it from the wrapped CLI's env
83
+ // so subagents don't inherit it); else mint a fresh 12-hex id.
84
+ const injected = process.env.AGENT_YES_AGENT_ID;
81
85
  const existing = this.store.findOne((doc) => doc.pid === pid);
82
- const agentId = existing?.agentId ?? randomBytes(6).toString("hex");
86
+ const agentId =
87
+ existing?.agentId ??
88
+ (injected && /^[0-9a-f]{6,32}$/i.test(injected) ? injected : randomBytes(6).toString("hex"));
83
89
 
84
90
  const record: Omit<PidRecord, "_id"> = {
85
91
  pid,
package/ts/serve.ts CHANGED
@@ -1763,6 +1763,14 @@ export async function cmdServe(rest: string[]): Promise<number> {
1763
1763
  const agentArgv = [...ayCmd, cli, ...(prompt ? ["--", prompt] : [])];
1764
1764
  // don't leak our Claude Code session into the agent
1765
1765
  const agentEnv = freshAgentEnv();
1766
+ // Correlation id: the spawn response returns the `ay` LAUNCHER pid, which is
1767
+ // NOT the agent's registered pid — so the caller can't find the agent it just
1768
+ // spawned. Mint a 12-hex id (the agent_id format), inject it as
1769
+ // AGENT_YES_AGENT_ID, and return it. Both runtimes adopt it as their agent_id
1770
+ // (instead of minting a random one) and strip it from the wrapped CLI's env so
1771
+ // subagents don't inherit and collide. The caller can then address the agent
1772
+ // immediately: `ay <verb> <remote>:<agentId>`.
1773
+ const agentId = randomBytes(6).toString("hex");
1766
1774
  // Detach the agent into its OWN session (setsid). When `ay serve` runs WITH a
1767
1775
  // controlling terminal (started in a shell rather than as a headless daemon),
1768
1776
  // an undetached child inherits the daemon's session + controlling tty and lands
@@ -1792,7 +1800,7 @@ export async function cmdServe(rest: string[]): Promise<number> {
1792
1800
  const child = Bun.spawn([shell, "-c", script, "ay-spawn", ...agentArgv], {
1793
1801
  cwd,
1794
1802
  detached: true,
1795
- env: { ...agentEnv, AGENT_YES_CWD: cwd, AGENT_YES_CLI: cli },
1803
+ env: { ...agentEnv, AGENT_YES_CWD: cwd, AGENT_YES_CLI: cli, AGENT_YES_AGENT_ID: agentId },
1796
1804
  stdin: "ignore",
1797
1805
  stdout: "ignore",
1798
1806
  stderr: Bun.file(errPath),
@@ -1829,6 +1837,7 @@ export async function cmdServe(rest: string[]): Promise<number> {
1829
1837
  return Response.json({
1830
1838
  ok: true,
1831
1839
  pid: child.pid,
1840
+ agentId,
1832
1841
  cli,
1833
1842
  cwd,
1834
1843
  hook: true,
@@ -1838,7 +1847,7 @@ export async function cmdServe(rest: string[]): Promise<number> {
1838
1847
  const child = Bun.spawn(agentArgv, {
1839
1848
  cwd,
1840
1849
  detached: true,
1841
- env: agentEnv,
1850
+ env: { ...agentEnv, AGENT_YES_AGENT_ID: agentId },
1842
1851
  stdin: "ignore",
1843
1852
  stdout: "ignore",
1844
1853
  stderr: "ignore",
@@ -1847,6 +1856,7 @@ export async function cmdServe(rest: string[]): Promise<number> {
1847
1856
  return Response.json({
1848
1857
  ok: true,
1849
1858
  pid: child.pid,
1859
+ agentId,
1850
1860
  cli,
1851
1861
  cwd,
1852
1862
  ...(provisioned ? { provisioned } : {}),
package/ts/subcommands.ts CHANGED
@@ -251,6 +251,7 @@ const SUBCOMMANDS = new Set([
251
251
  "tail",
252
252
  "head",
253
253
  "send",
254
+ "spawn",
254
255
  "attach",
255
256
  "stop",
256
257
  "exit",
@@ -325,6 +326,8 @@ export async function runSubcommand(argv: string[]): Promise<number | null> {
325
326
  return await cmdRead(rest, { mode: "head" });
326
327
  case "send":
327
328
  return await cmdSend(rest);
329
+ case "spawn":
330
+ return await cmdSpawn(rest);
328
331
  case "attach":
329
332
  return await cmdAttach(rest);
330
333
  case "stop":
@@ -648,11 +651,13 @@ async function remotePost(
648
651
  remote: ResolvedRemote,
649
652
  pathname: string,
650
653
  body: unknown,
654
+ signal?: AbortSignal,
651
655
  ): Promise<Response> {
652
656
  return fetch(`${remote.url}${pathname}`, {
653
657
  method: "POST",
654
658
  headers: { Authorization: `Bearer ${remote.token}`, "Content-Type": "application/json" },
655
659
  body: JSON.stringify(body),
660
+ ...(signal ? { signal } : {}),
656
661
  });
657
662
  }
658
663
 
@@ -839,6 +844,84 @@ async function runRemoteSend(remote: ResolvedRemote, msg: string, code: string):
839
844
  return 0;
840
845
  }
841
846
 
847
+ /**
848
+ * Spawn an agent on a remote host by POSTing its existing `/api/spawn`. The
849
+ * remote applies ITS OWN spawn hook + provision allowlist server-side (the hook
850
+ * never crosses the wire). `hint` is the user-typed target (alias or
851
+ * token@host:port) so the printed follow-ups are copy-pasteable.
852
+ *
853
+ * NO retry: a POST that already spawned must never be re-sent (double-spawn). A
854
+ * generous timeout still bounds a half-open/stalled connection — on timeout the
855
+ * result is UNKNOWN, so we say so and point at `ay ls` rather than retrying.
856
+ */
857
+ async function runRemoteSpawn(
858
+ remote: ResolvedRemote,
859
+ hint: string,
860
+ spec: { cli: string; cwd?: string; from?: string; prompt?: string },
861
+ ): Promise<number> {
862
+ // A hooked `/api/spawn` (#126) can legitimately block up to the remote's
863
+ // handshake window, so the timeout is generous; it only guards a dead/half-open
864
+ // connection that would otherwise hang the CLI forever.
865
+ const SPAWN_TIMEOUT_MS = Number(process.env.AGENT_YES_REMOTE_SPAWN_TIMEOUT_MS) || 120_000;
866
+ let res: Response;
867
+ try {
868
+ res = await remotePost(
869
+ remote,
870
+ "/api/spawn",
871
+ {
872
+ cli: spec.cli,
873
+ cwd: spec.cwd || undefined,
874
+ from: spec.from || undefined,
875
+ prompt: spec.prompt || undefined,
876
+ },
877
+ AbortSignal.timeout(SPAWN_TIMEOUT_MS),
878
+ );
879
+ } catch (e) {
880
+ const name = (e as Error)?.name;
881
+ if (name === "TimeoutError" || name === "AbortError") {
882
+ // The request may or may not have spawned — do NOT retry. Let the operator check.
883
+ process.stderr.write(
884
+ `remote spawn: no response from ${remote.url} within ${Math.round(SPAWN_TIMEOUT_MS / 1000)}s — ` +
885
+ `result UNKNOWN (not retried).\n ay ls ${hint} # check whether it started\n`,
886
+ );
887
+ return 2;
888
+ }
889
+ process.stderr.write(`remote spawn failed: ${(e as Error).message}\n`);
890
+ return 1;
891
+ }
892
+ if (!res.ok) {
893
+ process.stderr.write(`remote spawn failed ${res.status}: ${await res.text()}\n`);
894
+ return 1;
895
+ }
896
+ const r = (await res.json()) as {
897
+ pid: number;
898
+ cli: string;
899
+ cwd: string;
900
+ agentId?: string;
901
+ hook?: boolean;
902
+ provisioned?: { action: string };
903
+ };
904
+ process.stdout.write(
905
+ `spawned ${r.cli} on ${remote.url} in ${r.cwd}` +
906
+ `${r.hook ? " (via spawn hook)" : ""}` +
907
+ `${r.provisioned ? ` (${r.provisioned.action})` : ""}\n`,
908
+ );
909
+ // `/api/spawn` returns a correlation `agentId` that the agent adopts as its
910
+ // agent_id — so we address the EXACT agent by it (no pid guessing, no race). A
911
+ // webrtc:// / share-link target isn't `:keyword`-addressable, so the keyword
912
+ // hint is only for an alias / token@host:port. Older remotes omit agentId →
913
+ // fall back to pointing at `ay ls`.
914
+ if (r.agentId && !hint.includes("://")) {
915
+ process.stderr.write(
916
+ `\n ay tail ${hint}:${r.agentId} # watch its output\n` +
917
+ ` ay status ${hint}:${r.agentId} --wait # block until it needs you\n`,
918
+ );
919
+ } else {
920
+ process.stderr.write(`\n ay ls ${hint} # the new ${r.cli} agent appears here\n`);
921
+ }
922
+ return 0;
923
+ }
924
+
842
925
  async function runRemoteStatus(remote: ResolvedRemote): Promise<number> {
843
926
  const keyword = remote.keyword ?? "";
844
927
  if (!keyword) {
@@ -2072,6 +2155,65 @@ function extractActivityFromLines(lines: string[]): string | null {
2072
2155
  return null;
2073
2156
  }
2074
2157
 
2158
+ // ---------------------------------------------------------------------------
2159
+ // ay spawn — launch an agent on a REMOTE host (POSTs the remote's /api/spawn)
2160
+ // ---------------------------------------------------------------------------
2161
+
2162
+ async function cmdSpawn(rest: string[]): Promise<number> {
2163
+ const y = yargs(rest)
2164
+ .usage("Usage: ay spawn <remote> [--cli claude] [--cwd <dir>] [--from <src>] -- <prompt>")
2165
+ .option("cli", {
2166
+ type: "string",
2167
+ default: "claude",
2168
+ description: "CLI to wrap (claude|codex|gemini|…)",
2169
+ })
2170
+ .option("cwd", {
2171
+ type: "string",
2172
+ description: "Working dir ON THE REMOTE (resolved against the remote's workspace root)",
2173
+ })
2174
+ .option("from", {
2175
+ type: "string",
2176
+ description: "Provision a worktree from a GitHub source (owner/repo@branch) on the remote",
2177
+ })
2178
+ .option("prompt", { type: "string", description: "Initial prompt (or pass it after `--`)" })
2179
+ .help(false)
2180
+ .version(false)
2181
+ .exitProcess(false);
2182
+
2183
+ const argv = await y.parseAsync();
2184
+ const target = argv._[0] !== undefined ? String(argv._[0]) : undefined;
2185
+ if (!target)
2186
+ throw new Error("usage: ay spawn <remote> [--cli X] [--cwd D] [--from S] -- <prompt>");
2187
+ const prompt = String(argv.prompt ?? argv._.slice(1).map(String).join(" "));
2188
+
2189
+ // v1 is remote-only: local spawning is just running the agent directly. A
2190
+ // target that doesn't resolve as a remote is almost certainly a mistake (e.g.
2191
+ // passing a cli name), so fail loudly with the local equivalent.
2192
+ const remote = await resolveRemoteSpec(target);
2193
+ if (!remote) {
2194
+ process.stderr.write(
2195
+ `ay spawn: '${target}' is not a known remote (token@host:port or a saved alias).\n` +
2196
+ ` to spawn locally: ay ${argv.cli}${prompt ? ` -- "${prompt}"` : ""}\n` +
2197
+ ` to add a remote: ay remote add <alias> http://<token>@<host>:<port>\n`,
2198
+ );
2199
+ return 1;
2200
+ }
2201
+ if (remote.keyword) {
2202
+ process.stderr.write(
2203
+ `ay spawn: target '${target}' carries a ':${remote.keyword}' keyword — spawn takes a remote, ` +
2204
+ `not an existing agent. Drop the ':${remote.keyword}'.\n`,
2205
+ );
2206
+ return 1;
2207
+ }
2208
+
2209
+ return runRemoteSpawn(remote, target, {
2210
+ cli: String(argv.cli || "claude"),
2211
+ cwd: argv.cwd ? String(argv.cwd) : undefined,
2212
+ from: argv.from ? String(argv.from) : undefined,
2213
+ prompt: prompt || undefined,
2214
+ });
2215
+ }
2216
+
2075
2217
  // ---------------------------------------------------------------------------
2076
2218
  // ay send
2077
2219
  // ---------------------------------------------------------------------------
@@ -2121,7 +2263,7 @@ async function cmdSend(rest: string[]): Promise<number> {
2121
2263
  const fifoPath = record.fifo_file;
2122
2264
  if (!fifoPath) {
2123
2265
  throw new Error(
2124
- `pid ${record.pid}: no fifo_file recorded — agent was not started with --stdpush (or was spawned by Rust which doesn't yet support FIFO IPC; see ROADMAP item 10)`,
2266
+ `pid ${record.pid}: no fifo_file recorded — this agent didn't register a stdin FIFO (an older agent, or one not started with --stdpush). Restarting it (ay restart ${record.pid}) re-registers one.`,
2125
2267
  );
2126
2268
  }
2127
2269
 
@@ -1,8 +0,0 @@
1
- import "./ts-DcZvAsIp.js";
2
- import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DOse329y.js";
4
- import "./pidStore-fqXqTKkh.js";
5
- import "./globalPidIndex-DlmmJlO8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-WcYvfnJQ.js";
7
-
8
- export { SUPPORTED_CLIS };
@@ -1,5 +0,0 @@
1
- import "./logger-CDIsZ-Pp.js";
2
- import { t as PidStore } from "./pidStore-fqXqTKkh.js";
3
- import "./globalPidIndex-DlmmJlO8.js";
4
-
5
- export { PidStore };