agent-yes 1.121.0 → 1.122.1

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 (39) hide show
  1. package/default.config.yaml +27 -4
  2. package/dist/SUPPORTED_CLIS-DcWAr8NI.js +8 -0
  3. package/dist/{SUPPORTED_CLIS-O57LGUEG.js → SUPPORTED_CLIS-f50t1rrA.js} +2 -2
  4. package/dist/{agent-yes.config-kmtJKJHk.js → agent-yes.config-z-IPzH5U.js} +3 -2
  5. package/dist/cli.js +5 -5
  6. package/dist/index.js +2 -2
  7. package/dist/reaper-Dj8R7ltI.js +64 -0
  8. package/dist/reaper-HqcUms2d.js +3 -0
  9. package/dist/{remotes-DavR4Hca.js → remotes-CpGcTr7A.js} +1 -1
  10. package/dist/{remotes-BufkGk0e.js → remotes-D2fqaRU8.js} +1 -1
  11. package/dist/schedule-OJeQo0Da.js +144 -0
  12. package/dist/{serve-D2czcYNC.js → serve-O3e2YFfp.js} +137 -36
  13. package/dist/{setup-f1FIFcZm.js → setup-yKMfadhq.js} +5 -42
  14. package/dist/{share-B6QVr5D1.js → share-CksllWW-.js} +122 -16
  15. package/dist/{subcommands-DobVXouH.js → subcommands-BkR-nSAB.js} +2 -2
  16. package/dist/{subcommands-CzpZQHO6.js → subcommands-CT1z9Jl4.js} +15 -6
  17. package/dist/{tray-B8_rx1iu.js → tray-DjCIyakK.js} +22 -10
  18. package/dist/{ts-D91dm1E0.js → ts-DyDU_Dae.js} +76 -7
  19. package/dist/{versionChecker-CAtpgnoQ.js → versionChecker-DmCadDPY.js} +13 -19
  20. package/dist/workspaceConfig-XP2NEWmV.js +56 -0
  21. package/lab/ui/index.html +63 -32
  22. package/package.json +1 -1
  23. package/ts/autoRetry.spec.ts +19 -0
  24. package/ts/autoRetry.ts +16 -0
  25. package/ts/configShared.ts +4 -0
  26. package/ts/index.ts +102 -0
  27. package/ts/oxmgrService.ts +36 -0
  28. package/ts/pty.ts +19 -1
  29. package/ts/reaper.spec.ts +45 -0
  30. package/ts/reaper.ts +77 -0
  31. package/ts/schedule.spec.ts +30 -0
  32. package/ts/schedule.ts +161 -0
  33. package/ts/serve.ts +207 -44
  34. package/ts/share.ts +171 -22
  35. package/ts/subcommands.ts +0 -0
  36. package/ts/tray.spec.ts +9 -1
  37. package/ts/tray.ts +30 -14
  38. package/ts/versionChecker.ts +24 -27
  39. package/dist/SUPPORTED_CLIS-CegJgoEf.js +0 -8
@@ -36,8 +36,20 @@ clis:
36
36
  - pattern: Press Enter to continue
37
37
  flags: m
38
38
  fatal:
39
- - Claude usage limit reached
40
39
  - "^error: unknown option"
40
+ # Recoverable API errors: instead of exiting, agent-yes types "retry" with
41
+ # exponential backoff (8s, 16, 32 … capped) for up to 8h (claude's usage
42
+ # limit window is ~5h). Checked before `fatal`, so these never kill the run.
43
+ autoRetry:
44
+ - pattern: "API Error.*Overloaded"
45
+ flags: i
46
+ - Overloaded
47
+ - Claude usage limit reached
48
+ - hit your usage limit
49
+ - pattern: "rate.?limit"
50
+ flags: i
51
+ - pattern: "session limit"
52
+ flags: i
41
53
  restoreArgs:
42
54
  - --continue
43
55
  restartWithoutContinueArg:
@@ -78,10 +90,21 @@ clis:
78
90
  - ⏎ send
79
91
  - "› "
80
92
  - '\? for shortcuts'
93
+ working:
94
+ # codex shows "• Working (… • esc to interrupt)" while streaming/running.
95
+ # (Approval dialogs say "esc to cancel", so this only marks real work.)
96
+ - esc to interrupt
81
97
  enter:
82
- - "> 1. Yes,"
83
- - "> 1. Yes, allow Codex to work in this folder"
84
- - "> 1. Approve and run now"
98
+ # codex highlights the selected option with "›" (U+203A), NOT ASCII ">".
99
+ # Match the affirmative first option (Yes / Approve / Allow) when it is the
100
+ # highlighted choice. The "[›>]" class keeps matching the old ASCII bullet
101
+ # too, in case an older/newer codex reverts the glyph.
102
+ - pattern: '[›>] ?1\. ?Yes'
103
+ flags: m
104
+ - pattern: '[›>] ?1\. ?Approve'
105
+ flags: m
106
+ - pattern: '[›>] ?1\. ?Allow'
107
+ flags: m
85
108
  fatal:
86
109
  - "Error: The cursor position could not be read within"
87
110
  defaultArgs:
@@ -0,0 +1,8 @@
1
+ import "./ts-DyDU_Dae.js";
2
+ import "./logger-B9h0djqx.js";
3
+ import "./versionChecker-DmCadDPY.js";
4
+ import "./pidStore-B5vBu8Px.js";
5
+ import "./globalPidIndex-gZuTvTBs.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-f50t1rrA.js";
7
+
8
+ export { SUPPORTED_CLIS };
@@ -1,8 +1,8 @@
1
- import { t as CLIS_CONFIG } from "./ts-D91dm1E0.js";
1
+ import { t as CLIS_CONFIG } from "./ts-DyDU_Dae.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-O57LGUEG.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-f50t1rrA.js.map
@@ -26,7 +26,7 @@ function compileTypingRespond(typingRespond) {
26
26
  return Object.fromEntries(Object.entries(typingRespond).map(([message, patterns]) => [message, patterns.map(compileRegexSource)]));
27
27
  }
28
28
  function normalizeCliConfig(raw) {
29
- const { ready, fatal, working, enter, enterExclude, typingRespond, restartWithoutContinueArg, updateAvailable, exitCommands, exitCommand, ...rest } = raw;
29
+ const { ready, fatal, working, enter, enterExclude, typingRespond, restartWithoutContinueArg, updateAvailable, autoRetry, exitCommands, exitCommand, ...rest } = raw;
30
30
  return {
31
31
  ...rest,
32
32
  ready: compileRegexList(ready),
@@ -37,6 +37,7 @@ function normalizeCliConfig(raw) {
37
37
  typingRespond: compileTypingRespond(typingRespond),
38
38
  restartWithoutContinueArg: compileRegexList(restartWithoutContinueArg),
39
39
  updateAvailable: compileRegexList(updateAvailable),
40
+ autoRetry: compileRegexList(autoRetry),
40
41
  exitCommands: exitCommands ?? exitCommand
41
42
  };
42
43
  }
@@ -288,4 +289,4 @@ async function getDefaultConfig() {
288
289
 
289
290
  //#endregion
290
291
  export { agent_yes_config_default as default };
291
- //# sourceMappingURL=agent-yes.config-kmtJKJHk.js.map
292
+ //# sourceMappingURL=agent-yes.config-z-IPzH5U.js.map
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  import { n as logger } from "./logger-B9h0djqx.js";
3
- import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-CAtpgnoQ.js";
3
+ import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-DmCadDPY.js";
4
4
  import { argv } from "process";
5
5
  import { execFileSync, spawn } from "child_process";
6
6
  import ms from "ms";
@@ -482,7 +482,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
482
482
  {
483
483
  const rawArg = process.argv[2];
484
484
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
485
- const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-DobVXouH.js");
485
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-BkR-nSAB.js");
486
486
  if (isHelpFlag && process.argv.length === 3) {
487
487
  cmdHelp();
488
488
  process.exit(0);
@@ -496,12 +496,12 @@ await checkAndAutoUpdate();
496
496
  logger.info(versionString());
497
497
  const config = parseCliArgs(process.argv);
498
498
  if (config.tray) {
499
- const { startTray } = await import("./tray-B8_rx1iu.js");
499
+ const { startTray } = await import("./tray-DjCIyakK.js");
500
500
  await startTray();
501
501
  await new Promise(() => {});
502
502
  }
503
503
  {
504
- const { ensureTray } = await import("./tray-B8_rx1iu.js");
504
+ const { ensureTray } = await import("./tray-DjCIyakK.js");
505
505
  ensureTray();
506
506
  }
507
507
  if (config.useRust) {
@@ -515,7 +515,7 @@ if (config.useRust) {
515
515
  }
516
516
  }
517
517
  if (rustBinary) {
518
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-CegJgoEf.js");
518
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-DcWAr8NI.js");
519
519
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
520
520
  if (config.verbose) {
521
521
  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-D91dm1E0.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-DyDU_Dae.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import "./versionChecker-CAtpgnoQ.js";
3
+ import "./versionChecker-DmCadDPY.js";
4
4
  import "./pidStore-B5vBu8Px.js";
5
5
  import "./globalPidIndex-gZuTvTBs.js";
6
6
 
@@ -0,0 +1,64 @@
1
+ import { t as agentYesHome } from "./agentYesHome-BvaUOzCV.js";
2
+ import { appendFile, mkdir, readFile, rename, writeFile } from "fs/promises";
3
+ import path from "path";
4
+
5
+ //#region ts/reaper.ts
6
+ const registryPath = () => path.join(agentYesHome(), "reaper.jsonl");
7
+ function isAlive(pid) {
8
+ if (pid <= 1) return false;
9
+ try {
10
+ process.kill(pid, 0);
11
+ return true;
12
+ } catch (e) {
13
+ return e.code === "EPERM";
14
+ }
15
+ }
16
+ /** Record this wrapper + its agent's process group for later sweeping. */
17
+ async function register(wrapperPid, pgid) {
18
+ if (pgid <= 1) return;
19
+ try {
20
+ await mkdir(agentYesHome(), { recursive: true });
21
+ await appendFile(registryPath(), JSON.stringify({
22
+ wpid: wrapperPid,
23
+ pgid
24
+ }) + "\n");
25
+ } catch {}
26
+ }
27
+ /** SIGKILL the recorded group of every agent whose wrapper has exited, and
28
+ * rewrite the registry keeping only still-running agents. Best-effort. */
29
+ async function sweep() {
30
+ let content;
31
+ try {
32
+ content = await readFile(registryPath(), "utf8");
33
+ } catch {
34
+ return;
35
+ }
36
+ const keep = [];
37
+ for (const line of content.split("\n")) {
38
+ const t = line.trim();
39
+ if (!t) continue;
40
+ let entry;
41
+ try {
42
+ entry = JSON.parse(t);
43
+ } catch {
44
+ continue;
45
+ }
46
+ if (typeof entry.wpid !== "number" || typeof entry.pgid !== "number") continue;
47
+ if (isAlive(entry.wpid)) {
48
+ keep.push(t);
49
+ continue;
50
+ }
51
+ if (process.platform !== "win32" && entry.pgid > 1) try {
52
+ process.kill(-entry.pgid, "SIGKILL");
53
+ } catch {}
54
+ }
55
+ try {
56
+ const tmp = registryPath() + ".tmp";
57
+ await writeFile(tmp, keep.join("\n"));
58
+ await rename(tmp, registryPath());
59
+ } catch {}
60
+ }
61
+
62
+ //#endregion
63
+ export { sweep as n, register as t };
64
+ //# sourceMappingURL=reaper-Dj8R7ltI.js.map
@@ -0,0 +1,3 @@
1
+ import { n as sweep, t as register } from "./reaper-Dj8R7ltI.js";
2
+
3
+ export { sweep };
@@ -1,3 +1,3 @@
1
- import { a as resolveRemoteSpec, i as readRemotes, n as deleteRemoteAlias, o as writeRemoteAlias, r as parseDirectRemoteSpec, t as cmdRemote } from "./remotes-BufkGk0e.js";
1
+ import { a as resolveRemoteSpec, i as readRemotes, n as deleteRemoteAlias, o as writeRemoteAlias, r as parseDirectRemoteSpec, t as cmdRemote } from "./remotes-D2fqaRU8.js";
2
2
 
3
3
  export { cmdRemote };
@@ -147,4 +147,4 @@ async function cmdRemote(rest) {
147
147
 
148
148
  //#endregion
149
149
  export { resolveRemoteSpec as a, readRemotes as i, deleteRemoteAlias as n, writeRemoteAlias as o, parseDirectRemoteSpec as r, cmdRemote as t };
150
- //# sourceMappingURL=remotes-BufkGk0e.js.map
150
+ //# sourceMappingURL=remotes-D2fqaRU8.js.map
@@ -0,0 +1,144 @@
1
+ import "./ts-DyDU_Dae.js";
2
+ import "./logger-B9h0djqx.js";
3
+ import "./versionChecker-DmCadDPY.js";
4
+ import "./pidStore-B5vBu8Px.js";
5
+ import "./globalPidIndex-gZuTvTBs.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-f50t1rrA.js";
7
+ import { n as resolveSpawnCwd } from "./workspaceConfig-XP2NEWmV.js";
8
+ import { createHash } from "node:crypto";
9
+
10
+ //#region ts/oxmgrService.ts
11
+ async function ensureBootAutostart(oxmgrBin) {
12
+ try {
13
+ if (await Bun.spawn([
14
+ oxmgrBin,
15
+ "service",
16
+ "status"
17
+ ], { stdio: [
18
+ "ignore",
19
+ "ignore",
20
+ "ignore"
21
+ ] }).exited === 0) return true;
22
+ return await Bun.spawn([
23
+ oxmgrBin,
24
+ "service",
25
+ "install"
26
+ ], { stdio: [
27
+ "ignore",
28
+ "ignore",
29
+ "ignore"
30
+ ] }).exited === 0;
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+
36
+ //#endregion
37
+ //#region ts/schedule.ts
38
+ const SCHED_PREFIX = "agent-yes-cron-";
39
+ /** `HH:MM` → a daily cron; otherwise a raw 5-field cron passes through. null if neither. */
40
+ function toCron(spec) {
41
+ const s = spec.trim();
42
+ const hm = /^(\d{1,2}):(\d{2})$/.exec(s);
43
+ if (hm) {
44
+ const h = Number(hm[1]), m = Number(hm[2]);
45
+ return h < 24 && m < 60 ? `${m} ${h} * * *` : null;
46
+ }
47
+ return /^\S+(\s+\S+){4}$/.test(s) ? s : null;
48
+ }
49
+ /** Single-quote for oxmgr's shell-style command parsing (verified: it respects quotes). */
50
+ function shellQuote(s) {
51
+ return `'${s.replace(/'/g, `'\\''`)}'`;
52
+ }
53
+ function schedName(explicit, cli, key) {
54
+ if (explicit) return explicit.startsWith(SCHED_PREFIX) ? explicit : SCHED_PREFIX + explicit;
55
+ return SCHED_PREFIX + cli + "-" + createHash("sha1").update(key).digest("hex").slice(0, 6);
56
+ }
57
+ async function run(cmd, capture = false) {
58
+ const p = Bun.spawn(cmd, {
59
+ stdin: "ignore",
60
+ stdout: capture ? "pipe" : "inherit",
61
+ stderr: capture ? "pipe" : "inherit"
62
+ });
63
+ const out = capture ? await new Response(p.stdout).text() : "";
64
+ return {
65
+ code: await p.exited ?? 1,
66
+ out
67
+ };
68
+ }
69
+ async function cmdSchedule(rest) {
70
+ const oxmgrBin = Bun.which("oxmgr");
71
+ if (!oxmgrBin) {
72
+ process.stderr.write("ay schedule: oxmgr not found\n install with: cargo install oxmgr\n or: bun add -g oxmgr\n");
73
+ return 1;
74
+ }
75
+ const sub = rest[0];
76
+ if (!sub || sub === "-h" || sub === "--help") {
77
+ process.stdout.write("Usage:\n ay schedule <when> <cli> [--cwd DIR] [--name N] [-- <prompt>]\n schedule a recurring agent\n ay schedule list list scheduled agents\n ay schedule remove <name> remove one\n\n<when> is a daily HH:MM (e.g. 10:00) or a 5-field cron (\"0 10 * * *\").\nThe agent runs once now and then on every tick, and survives reboot.\n\nExample — a 10am daily QA pass:\n ay schedule 10:00 claude --cwd ~/ws/product -- \"run a full QA pass and write a report\"\n");
78
+ return 0;
79
+ }
80
+ if (sub === "list" || sub === "ls") {
81
+ process.stdout.write(`scheduled agents are named '${SCHED_PREFIX}*':\n`);
82
+ return (await run([oxmgrBin, "list"])).code;
83
+ }
84
+ if (sub === "remove" || sub === "rm" || sub === "delete") {
85
+ const name = rest[1];
86
+ if (!name) {
87
+ process.stderr.write("usage: ay schedule remove <name>\n");
88
+ return 1;
89
+ }
90
+ return (await run([
91
+ oxmgrBin,
92
+ "delete",
93
+ name.startsWith(SCHED_PREFIX) ? name : SCHED_PREFIX + name
94
+ ])).code;
95
+ }
96
+ const dashIdx = rest.indexOf("--");
97
+ const head = dashIdx >= 0 ? rest.slice(0, dashIdx) : rest;
98
+ const prompt = dashIdx >= 0 ? rest.slice(dashIdx + 1).join(" ") : "";
99
+ let nameFlag;
100
+ let cwdFlag;
101
+ const pos = [];
102
+ for (let i = 0; i < head.length; i++) if (head[i] === "--name") nameFlag = head[++i];
103
+ else if (head[i] === "--cwd") cwdFlag = head[++i];
104
+ else pos.push(head[i]);
105
+ const [when, cli] = pos;
106
+ if (!when || !cli) {
107
+ process.stderr.write("usage: ay schedule <when> <cli> [-- <prompt>]\n");
108
+ return 1;
109
+ }
110
+ const cron = toCron(when);
111
+ if (!cron) {
112
+ process.stderr.write(`ay schedule: bad <when> "${when}" — use HH:MM or a 5-field cron\n`);
113
+ return 1;
114
+ }
115
+ if (!SUPPORTED_CLIS.includes(cli)) {
116
+ process.stderr.write(`ay schedule: unsupported cli "${cli}"\n`);
117
+ return 1;
118
+ }
119
+ const cwd = resolveSpawnCwd(cwdFlag);
120
+ const ayBin = Bun.which("ay");
121
+ const agentCmd = `${ayBin ? `${shellQuote(process.execPath)} ${shellQuote(ayBin)}` : "ay"} ${cli}${prompt ? ` -- ${shellQuote(prompt)}` : ""}`;
122
+ const name = schedName(nameFlag, cli, cron + "\0" + prompt + "\0" + cwd);
123
+ const { code } = await run([
124
+ oxmgrBin,
125
+ "start",
126
+ agentCmd,
127
+ "--name",
128
+ name,
129
+ "--cwd",
130
+ cwd,
131
+ "--restart",
132
+ "never",
133
+ "--cron-restart",
134
+ cron
135
+ ]);
136
+ if (code !== 0) return code;
137
+ const onBoot = await ensureBootAutostart(oxmgrBin);
138
+ process.stdout.write(`\nscheduled '${name}'\n ${cli}${prompt ? ` -- "${prompt.slice(0, 60)}${prompt.length > 60 ? "…" : ""}"` : ""}\n when: ${cron} cwd: ${cwd}\n` + (onBoot ? ` runs now and on every tick; survives reboot.\n` : " runs now and on every tick.\n start-on-boot: not registered — run `oxmgr service install` to enable\n") + `\n ay schedule list # all scheduled agents\n ay schedule remove ${name.slice(15)}\n`);
139
+ return 0;
140
+ }
141
+
142
+ //#endregion
143
+ export { cmdSchedule };
144
+ //# sourceMappingURL=schedule-OJeQo0Da.js.map
@@ -1,11 +1,11 @@
1
- import "./ts-D91dm1E0.js";
1
+ import "./ts-DyDU_Dae.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import { r as getInstalledPackage } from "./versionChecker-CAtpgnoQ.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-DmCadDPY.js";
4
4
  import "./pidStore-B5vBu8Px.js";
5
5
  import "./globalPidIndex-gZuTvTBs.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-O57LGUEG.js";
7
- import "./remotes-BufkGk0e.js";
8
- import { c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, m as snapshotStatus, r as controlCodeFromName, u as readNotes } from "./subcommands-CzpZQHO6.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-f50t1rrA.js";
7
+ import "./remotes-D2fqaRU8.js";
8
+ import { c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, m as snapshotStatus, r as controlCodeFromName, u as readNotes } from "./subcommands-CT1z9Jl4.js";
9
9
  import yargs from "yargs";
10
10
  import { mkdir, open, readFile, writeFile } from "fs/promises";
11
11
  import { homedir, hostname, userInfo } from "os";
@@ -33,6 +33,20 @@ async function loadOrCreateToken(tokenFlag) {
33
33
  return token;
34
34
  }
35
35
  }
36
+ async function loadTokenReadOnly() {
37
+ try {
38
+ return (await readFile(tokenPath(), "utf-8")).trim();
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+ async function readShareLink() {
44
+ try {
45
+ return (await readFile(path.join(agentYesHome(), ".share-link"), "utf-8")).trim();
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
36
50
  function tokenEqual(provided, expectedToken) {
37
51
  const maxLen = Math.max(provided.length, expectedToken.length);
38
52
  return timingSafeEqual(Buffer.from(provided.padEnd(maxLen, "\0")), Buffer.from(expectedToken.padEnd(maxLen, "\0"))) && provided.length === expectedToken.length;
@@ -101,20 +115,41 @@ function ayServeArgv(args) {
101
115
  ...args
102
116
  ];
103
117
  }
118
+ const WIN_RUN_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
119
+ const WIN_RUN_VALUE = DAEMON_NAME;
104
120
  async function ensureBootAutostart(mgr) {
105
121
  try {
106
- if (mgr.id !== "oxmgr") return await spawnExit([mgr.bin, "save"]) === 0;
107
- const installed = await spawnExit([
108
- mgr.bin,
109
- "service",
110
- "install"
122
+ if (mgr.id === "oxmgr") {
123
+ const installed = await spawnExit([
124
+ mgr.bin,
125
+ "service",
126
+ "status"
127
+ ]) === 0 || await spawnExit([
128
+ mgr.bin,
129
+ "service",
130
+ "install"
131
+ ]) === 0;
132
+ if (installed && process.platform === "linux") await spawnExit([
133
+ "loginctl",
134
+ "enable-linger",
135
+ userInfo().username
136
+ ]);
137
+ return installed;
138
+ }
139
+ if (await spawnExit([mgr.bin, "save"]) !== 0) return false;
140
+ if (process.platform === "win32") return await spawnExit([
141
+ "reg",
142
+ "add",
143
+ WIN_RUN_KEY,
144
+ "/v",
145
+ WIN_RUN_VALUE,
146
+ "/t",
147
+ "REG_SZ",
148
+ "/d",
149
+ mgr.bin.includes(" ") ? `"${mgr.bin}" resurrect` : `${mgr.bin} resurrect`,
150
+ "/f"
111
151
  ]) === 0;
112
- if (installed && process.platform === "linux") await spawnExit([
113
- "loginctl",
114
- "enable-linger",
115
- userInfo().username
116
- ]);
117
- return installed;
152
+ return await spawnExit([mgr.bin, "startup"]) === 0;
118
153
  } catch {
119
154
  return false;
120
155
  }
@@ -240,7 +275,8 @@ async function cmdServeDaemon(sub, args) {
240
275
  const httpish = effArgs.some((a) => a.startsWith("--http") || a.startsWith("--share")) || !effArgs.some((a) => a.startsWith("--webrtc"));
241
276
  process.stdout.write(`\n${priorArgs !== null ? `rolled '${DAEMON_NAME}' forward to` : `installed '${DAEMON_NAME}' as a daemon via ${mgr.id} —`} v${current}\n`);
242
277
  if (mgr.id === "oxmgr") process.stdout.write(onBoot ? `start-on-boot: enabled (systemd --user + linger, starts at boot)\n` : `start-on-boot: not registered — needs a user systemd session; run \`oxmgr service install\` to enable\n`);
243
- else process.stdout.write(onBoot ? `start-on-login: enabled (pm2 list saved; run \`pm2 startup\` once if logon resurrect is not yet installed)\n` : `start-on-login: \`pm2 save\` failed run it manually to persist across logins\n`);
278
+ else if (process.platform === "win32") process.stdout.write(onBoot ? `start-on-login: enabled (a HKCU Run entry runs \`pm2 resurrect\`)\n` : `start-on-login: not registered — \`pm2 save\` or the registry write failed\n`);
279
+ else process.stdout.write(onBoot ? `start-on-boot: enabled (pm2 startup registered with the system init)\n` : `start-on-boot: not registered — run \`pm2 startup\` (may need sudo) to enable\n`);
244
280
  process.stdout.write(`token: ${token}\n\n`);
245
281
  if (httpish) {
246
282
  process.stdout.write(` ay ls ${token}@<host>:${port}\n`);
@@ -262,11 +298,17 @@ async function cmdServeDaemon(sub, args) {
262
298
  "inherit",
263
299
  "inherit"
264
300
  ] }).exited ?? 1;
265
- if (mgr.id === "pm2" && code === 0) await Bun.spawn([mgr.bin, "save"], { stdio: [
266
- "ignore",
267
- "ignore",
268
- "ignore"
269
- ] }).exited;
301
+ if (mgr.id === "pm2" && code === 0) {
302
+ await spawnExit([mgr.bin, "save"]);
303
+ if (process.platform === "win32") await spawnExit([
304
+ "reg",
305
+ "delete",
306
+ WIN_RUN_KEY,
307
+ "/v",
308
+ WIN_RUN_VALUE,
309
+ "/f"
310
+ ]);
311
+ }
270
312
  return code;
271
313
  }
272
314
  if (sub === "logs") return await Bun.spawn([
@@ -281,6 +323,56 @@ async function cmdServeDaemon(sub, args) {
281
323
  ] }).exited ?? 1;
282
324
  return 1;
283
325
  }
326
+ async function cmdServeStatus(args) {
327
+ const json = args.includes("--json");
328
+ const mgr = resolveDaemonManager();
329
+ const token = await loadTokenReadOnly();
330
+ const shareLink = await readShareLink();
331
+ const daemonArgs = mgr ? await readDaemonServeArgs(mgr) : null;
332
+ const installed = daemonArgs !== null;
333
+ const a = daemonArgs ?? [];
334
+ const port = portFromArgs(a);
335
+ const webrtcish = a.some((x) => x.startsWith("--webrtc") || x.startsWith("--share"));
336
+ const httpish = a.some((x) => x.startsWith("--http") || x.startsWith("--share")) || !a.some((x) => x.startsWith("--webrtc"));
337
+ const mode = httpish && webrtcish ? "http+webrtc" : webrtcish ? "webrtc" : "http";
338
+ const runningVersion = httpish && token ? await fetchDaemonVersion(port, token) : null;
339
+ const current = getInstalledPackage().version;
340
+ if (json) {
341
+ process.stdout.write(JSON.stringify({
342
+ manager: mgr?.id ?? null,
343
+ installed,
344
+ mode,
345
+ port: httpish ? port : null,
346
+ reachable: runningVersion !== null,
347
+ runningVersion,
348
+ currentVersion: current,
349
+ upToDate: runningVersion !== null && runningVersion === current,
350
+ args: a,
351
+ hasToken: !!token,
352
+ shareLink
353
+ }, null, 2) + "\n");
354
+ return 0;
355
+ }
356
+ const w = (s = "") => process.stdout.write(s + "\n");
357
+ w(`daemon name: ${DAEMON_NAME}`);
358
+ w(`manager: ${mgr ? mgr.id : "none — install pm2 or oxmgr to daemonize"}`);
359
+ if (installed) {
360
+ w(`installed: yes (via ${mgr.id})`);
361
+ w(`mode: ${mode}${httpish ? ` (port ${port})` : ""}`);
362
+ if (a.length) w(`args: ${a.join(" ")}`);
363
+ } else w(`installed: no — start a daemon with: ay serve install [--share]`);
364
+ if (runningVersion !== null) w(`http api: reachable on 127.0.0.1:${port} — v${runningVersion} (${runningVersion === current ? "up to date" : `outdated (current v${current})`})`);
365
+ else if (mode === "webrtc") w(`http api: none (webrtc-only)`);
366
+ else w(`http api: not reachable on 127.0.0.1:${port} (not running)`);
367
+ w(`token: ${token ?? "(none yet — created on first serve)"}`);
368
+ if (shareLink) w(`share link: ${shareLink}`);
369
+ if (token && httpish) {
370
+ w();
371
+ w(`connect: ay ls ${token}@<host>:${port}`);
372
+ w(` ay remote add <alias> http://${token}@<host>:${port}`);
373
+ }
374
+ return 0;
375
+ }
284
376
  async function cmdServe(rest) {
285
377
  if (rest.includes("-h") || rest.includes("--help")) {
286
378
  process.stdout.write(`Usage: ay serve [options]
@@ -298,10 +390,11 @@ Modes (default: --http):
298
390
  --share [URL] Legacy alias for --http --webrtc
299
391
 
300
392
  Options:
301
- --port N Port to listen on (default: ${DEFAULT_PORT})\n --host HOST Interface to bind (default: 127.0.0.1; use 0.0.0.0 to expose)\n --token TOKEN Auth token (auto-generated and saved if omitted)\n -d, --daemon Install these flags as a background daemon (pm2/oxmgr)\n (same as: ay serve install <flags>)\n --allow-spawn Deprecated no-op — the console can always spawn agents\n --tls-cert FILE TLS certificate PEM\n --tls-key FILE TLS private key PEM\n\nSubcommands:\n ay serve install install as background daemon (pm2 on Windows, else oxmgr)\n ay serve uninstall remove daemon\n ay serve logs view daemon logs\n\nOnce running, connect from another machine:\n ay ls <token>@<host>:${DEFAULT_PORT}\n ay remote add <alias> http://<token>@<host>:${DEFAULT_PORT}\n`);
393
+ --port N Port to listen on (default: ${DEFAULT_PORT})\n --host HOST Interface to bind (default: 127.0.0.1; use 0.0.0.0 to expose)\n --token TOKEN Auth token (auto-generated and saved if omitted)\n -d, --daemon Install these flags as a background daemon (pm2/oxmgr)\n (same as: ay serve install <flags>)\n --allow-spawn Deprecated no-op — the console can always spawn agents\n --tls-cert FILE TLS certificate PEM\n --tls-key FILE TLS private key PEM\n\nSubcommands:\n ay serve install install as background daemon (pm2 on Windows, else oxmgr)\n ay serve status show daemon/server status (add --json for scripts)\n ay serve uninstall remove daemon\n ay serve logs view daemon logs\n\nOnce running, connect from another machine:\n ay ls <token>@<host>:${DEFAULT_PORT}\n ay remote add <alias> http://<token>@<host>:${DEFAULT_PORT}\n`);
302
394
  return 0;
303
395
  }
304
396
  const sub = rest[0];
397
+ if (sub === "status") return cmdServeStatus(rest.slice(1));
305
398
  if (sub === "install" || sub === "uninstall" || sub === "logs") return cmdServeDaemon(sub, rest.slice(1));
306
399
  const argv = await yargs(rest).usage("Usage: ay serve [options]").option("port", {
307
400
  type: "number",
@@ -741,9 +834,11 @@ Options:
741
834
  const cwd = typeof body.cwd === "string" && body.cwd ? body.cwd : process.cwd();
742
835
  const prompt = String(body.prompt ?? "");
743
836
  process.stderr.write(`→ console spawned: ay ${cli}${prompt ? ` -- "${prompt.slice(0, 60)}"` : ""} (cwd: ${cwd})\n`);
837
+ const ayBin = Bun.which("ay") ?? process.argv[1];
838
+ const ayCmd = process.platform === "win32" && ayBin.toLowerCase().endsWith(".exe") ? [ayBin] : [process.execPath, ayBin];
744
839
  try {
745
840
  const child = Bun.spawn([
746
- "ay",
841
+ ...ayCmd,
747
842
  cli,
748
843
  ...prompt ? ["--", prompt] : []
749
844
  ], {
@@ -823,22 +918,28 @@ Options:
823
918
  const webrtcVal = argv.webrtc ?? argv.share;
824
919
  const explicitUrl = typeof webrtcVal === "string" && webrtcVal.startsWith("webrtc://") ? webrtcVal : void 0;
825
920
  try {
826
- const { startShare, loadOrCreateShareRoom } = await import("./share-B6QVr5D1.js");
921
+ const { startShare, loadOrCreateShareRoom } = await import("./share-CksllWW-.js");
922
+ const linkFile = path.join(process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes"), ".share-link");
923
+ const announce = async (room, link, rotated) => {
924
+ const lead = rotated ? "the room was rejected by signaling (stale generation) — rotated to a fresh link" : "shared over WebRTC — open this link (the token is eaten from the URL on open)";
925
+ if (process.stdout.isTTY) {
926
+ const persistNote = explicitUrl ? "\n" : ` (persistent room — same link across restarts; delete ~/.agent-yes/.share-room to rotate)\n\n`;
927
+ process.stdout.write(`${wantHttp ? "\n" : ""}${lead}:\n ${link}\n` + persistNote);
928
+ } else {
929
+ try {
930
+ await writeFile(linkFile, link + "\n", { mode: 384 });
931
+ } catch {}
932
+ process.stdout.write(`${wantHttp ? "\n" : ""}${rotated ? "rotated WebRTC room" : "shared over WebRTC"} · room ${room} — the link carries a secret, so it is NOT logged.\n read it from ${linkFile} (mode 0600); delete ~/.agent-yes/.share-room to rotate\n\n`);
933
+ }
934
+ };
827
935
  const { room, link, close } = await startShare({
828
936
  url: explicitUrl ?? await loadOrCreateShareRoom(),
829
937
  localFetch: apiFetch,
830
- apiToken: token
938
+ apiToken: token,
939
+ onRotate: explicitUrl ? void 0 : (info) => announce(info.room, info.link, true)
831
940
  });
832
941
  closeShare = close;
833
- const persistNote = explicitUrl ? "\n" : ` (persistent room — same link across restarts; delete ~/.agent-yes/.share-room to rotate)\n\n`;
834
- if (process.stdout.isTTY) process.stdout.write(`${wantHttp ? "\n" : ""}shared over WebRTC — open this link (the token is eaten from the URL on open):\n ${link}\n` + persistNote);
835
- else {
836
- const linkFile = path.join(process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes"), ".share-link");
837
- try {
838
- await writeFile(linkFile, link + "\n", { mode: 384 });
839
- } catch {}
840
- process.stdout.write(`${wantHttp ? "\n" : ""}shared over WebRTC · room ${room} — the link carries a secret, so it is NOT logged.\n read it from ${linkFile} (mode 0600); delete ~/.agent-yes/.share-room to rotate\n\n`);
841
- }
942
+ await announce(room, link, false);
842
943
  } catch (e) {
843
944
  process.stderr.write(`ay serve --webrtc failed: ${e.message}\n`);
844
945
  if (!wantHttp) return 1;
@@ -862,4 +963,4 @@ Options:
862
963
 
863
964
  //#endregion
864
965
  export { cmdServe };
865
- //# sourceMappingURL=serve-D2czcYNC.js.map
966
+ //# sourceMappingURL=serve-O3e2YFfp.js.map