claude-yes 1.160.3 → 1.161.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.
@@ -0,0 +1,8 @@
1
+ import "./ts-CRo3qQWb.js";
2
+ import "./logger-CDIsZ-Pp.js";
3
+ import "./versionChecker-DrjR4P0K.js";
4
+ import "./pidStore-fqXqTKkh.js";
5
+ import "./globalPidIndex-DlmmJlO8.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DYTnLX8C.js";
7
+
8
+ export { SUPPORTED_CLIS };
@@ -1,8 +1,8 @@
1
- import { t as CLIS_CONFIG } from "./ts-DcZvAsIp.js";
1
+ import { t as CLIS_CONFIG } from "./ts-CRo3qQWb.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-DYTnLX8C.js.map
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-DrjR4P0K.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-5AXvxB0S.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-CHmQfHCh.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}`);
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
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-CRo3qQWb.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DOse329y.js";
3
+ import "./versionChecker-DrjR4P0K.js";
4
4
  import "./pidStore-fqXqTKkh.js";
5
5
  import "./globalPidIndex-DlmmJlO8.js";
6
6
 
@@ -1,9 +1,9 @@
1
- import "./ts-DcZvAsIp.js";
1
+ import "./ts-CRo3qQWb.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DOse329y.js";
3
+ import "./versionChecker-DrjR4P0K.js";
4
4
  import "./pidStore-fqXqTKkh.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-DYTnLX8C.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-CCvB7SCT.js.map
@@ -1,16 +1,16 @@
1
- import "./ts-DcZvAsIp.js";
1
+ import "./ts-CRo3qQWb.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import { r as getInstalledPackage } from "./versionChecker-DOse329y.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-DrjR4P0K.js";
4
4
  import "./pidStore-fqXqTKkh.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-DYTnLX8C.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-DGkBxWS5.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";
@@ -1389,4 +1389,4 @@ Options:
1389
1389
 
1390
1390
  //#endregion
1391
1391
  export { cmdServe };
1392
- //# sourceMappingURL=serve-D3vy56FC.js.map
1392
+ //# sourceMappingURL=serve-BRARQjsC.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-BRARQjsC.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-Cx7jDKtC.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-DGkBxWS5.js";
8
8
 
9
9
  export { cmdHelp, isSubcommand, runSubcommand };
@@ -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-BRARQjsC.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-Cx7jDKtC.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-CCvB7SCT.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,44 @@ 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
+ process.stderr.write(`\n ay ls ${hint} # the new ${r.cli} agent appears here\n`);
917
+ return 0;
918
+ }
878
919
  async function runRemoteStatus(remote) {
879
920
  const keyword = remote.keyword ?? "";
880
921
  if (!keyword) {
@@ -1819,6 +1860,40 @@ function extractActivityFromLines(lines) {
1819
1860
  }
1820
1861
  return null;
1821
1862
  }
1863
+ async function cmdSpawn(rest) {
1864
+ const argv = await yargs(rest).usage("Usage: ay spawn <remote> [--cli claude] [--cwd <dir>] [--from <src>] -- <prompt>").option("cli", {
1865
+ type: "string",
1866
+ default: "claude",
1867
+ description: "CLI to wrap (claude|codex|gemini|…)"
1868
+ }).option("cwd", {
1869
+ type: "string",
1870
+ description: "Working dir ON THE REMOTE (resolved against the remote's workspace root)"
1871
+ }).option("from", {
1872
+ type: "string",
1873
+ description: "Provision a worktree from a GitHub source (owner/repo@branch) on the remote"
1874
+ }).option("prompt", {
1875
+ type: "string",
1876
+ description: "Initial prompt (or pass it after `--`)"
1877
+ }).help(false).version(false).exitProcess(false).parseAsync();
1878
+ const target = argv._[0] !== void 0 ? String(argv._[0]) : void 0;
1879
+ if (!target) throw new Error("usage: ay spawn <remote> [--cli X] [--cwd D] [--from S] -- <prompt>");
1880
+ const prompt = String(argv.prompt ?? argv._.slice(1).map(String).join(" "));
1881
+ const remote = await resolveRemoteSpec(target);
1882
+ if (!remote) {
1883
+ 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`);
1884
+ return 1;
1885
+ }
1886
+ if (remote.keyword) {
1887
+ 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`);
1888
+ return 1;
1889
+ }
1890
+ return runRemoteSpawn(remote, target, {
1891
+ cli: String(argv.cli || "claude"),
1892
+ cwd: argv.cwd ? String(argv.cwd) : void 0,
1893
+ from: argv.from ? String(argv.from) : void 0,
1894
+ prompt: prompt || void 0
1895
+ });
1896
+ }
1822
1897
  async function cmdSend(rest) {
1823
1898
  const argv = await yargs(rest).usage("Usage: ay send <keyword> <msg|-> [options]").option("code", {
1824
1899
  type: "string",
@@ -2719,4 +2794,4 @@ async function cmdResultSet(rest) {
2719
2794
 
2720
2795
  //#endregion
2721
2796
  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
2797
+ //# sourceMappingURL=subcommands-DGkBxWS5.js.map
@@ -1,5 +1,5 @@
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-DrjR4P0K.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
5
  import { t as PidStore } from "./pidStore-fqXqTKkh.js";
@@ -1802,4 +1802,4 @@ function sleep(ms) {
1802
1802
 
1803
1803
  //#endregion
1804
1804
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1805
- //# sourceMappingURL=ts-DcZvAsIp.js.map
1805
+ //# sourceMappingURL=ts-CRo3qQWb.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.161.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-DrjR4P0K.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.161.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/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,79 @@ 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
+ hook?: boolean;
901
+ provisioned?: { action: string };
902
+ };
903
+ process.stdout.write(
904
+ `spawned ${r.cli} on ${remote.url} in ${r.cwd}` +
905
+ `${r.hook ? " (via spawn hook)" : ""}` +
906
+ `${r.provisioned ? ` (${r.provisioned.action})` : ""}\n`,
907
+ );
908
+ // We deliberately do NOT print the agent's own pid. `/api/spawn` returns the
909
+ // `ay` LAUNCHER pid, which is not the agent's registered pid (the record's
910
+ // pid/wrapper_pid are the runtime process, not the launcher) — and picking the
911
+ // freshly-registered agent by "newest" would, under a concurrent spawn,
912
+ // surface a sibling's (or another user's) pid. Point at the remote's list,
913
+ // where the new agent reliably appears, instead of guessing.
914
+ // (A one-step `ay tail <remote>:<pid>` would need /api/spawn to return a
915
+ // correlatable agent id — a server change left for a follow-up.)
916
+ process.stderr.write(`\n ay ls ${hint} # the new ${r.cli} agent appears here\n`);
917
+ return 0;
918
+ }
919
+
842
920
  async function runRemoteStatus(remote: ResolvedRemote): Promise<number> {
843
921
  const keyword = remote.keyword ?? "";
844
922
  if (!keyword) {
@@ -2072,6 +2150,65 @@ function extractActivityFromLines(lines: string[]): string | null {
2072
2150
  return null;
2073
2151
  }
2074
2152
 
2153
+ // ---------------------------------------------------------------------------
2154
+ // ay spawn — launch an agent on a REMOTE host (POSTs the remote's /api/spawn)
2155
+ // ---------------------------------------------------------------------------
2156
+
2157
+ async function cmdSpawn(rest: string[]): Promise<number> {
2158
+ const y = yargs(rest)
2159
+ .usage("Usage: ay spawn <remote> [--cli claude] [--cwd <dir>] [--from <src>] -- <prompt>")
2160
+ .option("cli", {
2161
+ type: "string",
2162
+ default: "claude",
2163
+ description: "CLI to wrap (claude|codex|gemini|…)",
2164
+ })
2165
+ .option("cwd", {
2166
+ type: "string",
2167
+ description: "Working dir ON THE REMOTE (resolved against the remote's workspace root)",
2168
+ })
2169
+ .option("from", {
2170
+ type: "string",
2171
+ description: "Provision a worktree from a GitHub source (owner/repo@branch) on the remote",
2172
+ })
2173
+ .option("prompt", { type: "string", description: "Initial prompt (or pass it after `--`)" })
2174
+ .help(false)
2175
+ .version(false)
2176
+ .exitProcess(false);
2177
+
2178
+ const argv = await y.parseAsync();
2179
+ const target = argv._[0] !== undefined ? String(argv._[0]) : undefined;
2180
+ if (!target)
2181
+ throw new Error("usage: ay spawn <remote> [--cli X] [--cwd D] [--from S] -- <prompt>");
2182
+ const prompt = String(argv.prompt ?? argv._.slice(1).map(String).join(" "));
2183
+
2184
+ // v1 is remote-only: local spawning is just running the agent directly. A
2185
+ // target that doesn't resolve as a remote is almost certainly a mistake (e.g.
2186
+ // passing a cli name), so fail loudly with the local equivalent.
2187
+ const remote = await resolveRemoteSpec(target);
2188
+ if (!remote) {
2189
+ process.stderr.write(
2190
+ `ay spawn: '${target}' is not a known remote (token@host:port or a saved alias).\n` +
2191
+ ` to spawn locally: ay ${argv.cli}${prompt ? ` -- "${prompt}"` : ""}\n` +
2192
+ ` to add a remote: ay remote add <alias> http://<token>@<host>:<port>\n`,
2193
+ );
2194
+ return 1;
2195
+ }
2196
+ if (remote.keyword) {
2197
+ process.stderr.write(
2198
+ `ay spawn: target '${target}' carries a ':${remote.keyword}' keyword — spawn takes a remote, ` +
2199
+ `not an existing agent. Drop the ':${remote.keyword}'.\n`,
2200
+ );
2201
+ return 1;
2202
+ }
2203
+
2204
+ return runRemoteSpawn(remote, target, {
2205
+ cli: String(argv.cli || "claude"),
2206
+ cwd: argv.cwd ? String(argv.cwd) : undefined,
2207
+ from: argv.from ? String(argv.from) : undefined,
2208
+ prompt: prompt || undefined,
2209
+ });
2210
+ }
2211
+
2075
2212
  // ---------------------------------------------------------------------------
2076
2213
  // ay send
2077
2214
  // ---------------------------------------------------------------------------
@@ -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 };