agent-yes 1.191.0 → 1.192.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.
@@ -1,8 +1,8 @@
1
- import { t as CLIS_CONFIG } from "./ts-BQh1-ljE.js";
1
+ import { t as CLIS_CONFIG } from "./ts-7XqYx0eW.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-mUaQHk4M.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-5N015orJ.js.map
@@ -0,0 +1,8 @@
1
+ import "./ts-7XqYx0eW.js";
2
+ import "./logger-CDIsZ-Pp.js";
3
+ import "./versionChecker-CUMd5SWt.js";
4
+ import "./pidStore-BIvsBQ8X.js";
5
+ import "./globalPidIndex-CoNr7tS8.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-5N015orJ.js";
7
+
8
+ export { SUPPORTED_CLIS };
@@ -1,7 +1,7 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-CoNr7tS8.js";
3
3
  import "./configShared-0MnIQ652.js";
4
- import { j as resolveOne } from "./subcommands-CNCgJ48t.js";
4
+ import { j as resolveOne } from "./subcommands-DMe0U83W.js";
5
5
  import "./e2e-BeKjLhmO.js";
6
6
  import "./webrtcLink-BG0Xc4-W.js";
7
7
  import "./remotes-CgT91bMo.js";
@@ -228,4 +228,4 @@ function transformEvent(rawEvent, agentId, forwarded) {
228
228
 
229
229
  //#endregion
230
230
  export { createScopedShare, listShares, revokeAllShares, revokeShare };
231
- //# sourceMappingURL=agentShare-Boa9P2dK.js.map
231
+ //# sourceMappingURL=agentShare-C4LxU2xT.js.map
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
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, t as checkAndAutoUpdate } from "./versionChecker-DhCi7ncH.js";
5
- import { n as getRustBinary } from "./rustBinary-Cu8q2m9s.js";
4
+ import { i as versionString, n as displayVersion, t as checkAndAutoUpdate } from "./versionChecker-CUMd5SWt.js";
5
+ import { n as getRustBinary } from "./rustBinary-DFoLw-3W.js";
6
6
  import { argv } from "process";
7
7
  import { spawn } from "child_process";
8
8
  import ms from "ms";
@@ -285,7 +285,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
285
285
  const rawArg = process.argv[2];
286
286
  const managerCommands = !invokedCliName(process.argv);
287
287
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
288
- const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-Uerol8Tz.js");
288
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-czdlkI5W.js");
289
289
  if (isHelpFlag && process.argv.length === 3) {
290
290
  await cmdHelp(managerCommands);
291
291
  process.exit(0);
@@ -327,7 +327,7 @@ if (config.useRust) {
327
327
  }
328
328
  }
329
329
  if (rustBinary) {
330
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-P3I0D3Ka.js");
330
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-W067Yn4T.js");
331
331
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
332
332
  if (config.verbose) {
333
333
  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-BQh1-ljE.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-7XqYx0eW.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DhCi7ncH.js";
3
+ import "./versionChecker-CUMd5SWt.js";
4
4
  import "./pidStore-BIvsBQ8X.js";
5
5
  import "./globalPidIndex-CoNr7tS8.js";
6
6
 
@@ -1,7 +1,7 @@
1
1
  import { n as logger } from "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-CoNr7tS8.js";
3
3
  import "./configShared-0MnIQ652.js";
4
- import { G as hostId, J as readInbox, K as listInboxParents, O as renderLogTailLines, Q as notifyDir, S as listRecords, U as appendEvent, W as gcInboxes, X as daemonLockDir, Y as shouldStealLock, Z as daemonLockOwnerPath, _ as isPidAlive, c as deriveLiveState, q as liveWatchers } from "./subcommands-CNCgJ48t.js";
4
+ import { G as hostId, J as readInbox, K as listInboxParents, O as renderLogTailLines, Q as notifyDir, S as listRecords, U as appendEvent, W as gcInboxes, X as daemonLockDir, Y as shouldStealLock, Z as daemonLockOwnerPath, _ as isPidAlive, c as deriveLiveState, q as liveWatchers } from "./subcommands-DMe0U83W.js";
5
5
  import "./e2e-BeKjLhmO.js";
6
6
  import "./webrtcLink-BG0Xc4-W.js";
7
7
  import "./remotes-CgT91bMo.js";
@@ -588,4 +588,4 @@ async function ensureDaemon() {
588
588
 
589
589
  //#endregion
590
590
  export { daemonStatus, ensureDaemon, requestDaemonStop, runDaemon };
591
- //# sourceMappingURL=notifyDaemon-hKT38cBs.js.map
591
+ //# sourceMappingURL=notifyDaemon--OxBe8nG.js.map
@@ -1,4 +1,4 @@
1
- import { r as getInstalledPackage } from "./versionChecker-DhCi7ncH.js";
1
+ import { r as getInstalledPackage } from "./versionChecker-CUMd5SWt.js";
2
2
  import { execFileSync } from "child_process";
3
3
  import { existsSync, mkdirSync, unlinkSync } from "fs";
4
4
  import { chmod, copyFile } from "fs/promises";
@@ -225,4 +225,4 @@ async function getRustBinary(options = {}) {
225
225
 
226
226
  //#endregion
227
227
  export { getRustBinary as n, findSpawnHiddenLauncher as t };
228
- //# sourceMappingURL=rustBinary-Cu8q2m9s.js.map
228
+ //# sourceMappingURL=rustBinary-DFoLw-3W.js.map
@@ -1,9 +1,9 @@
1
- import "./ts-BQh1-ljE.js";
1
+ import "./ts-7XqYx0eW.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DhCi7ncH.js";
3
+ import "./versionChecker-CUMd5SWt.js";
4
4
  import "./pidStore-BIvsBQ8X.js";
5
5
  import "./globalPidIndex-CoNr7tS8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-mUaQHk4M.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-5N015orJ.js";
7
7
  import { d as resolveSpawnCwd } from "./workspaceConfig-_GtAZtsi.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-Bb98iG9W.js.map
144
+ //# sourceMappingURL=schedule-AZACZY6R.js.map
@@ -1,20 +1,20 @@
1
- import "./ts-BQh1-ljE.js";
1
+ import "./ts-7XqYx0eW.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import { r as getInstalledPackage } from "./versionChecker-DhCi7ncH.js";
4
- import { t as findSpawnHiddenLauncher } from "./rustBinary-Cu8q2m9s.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-CUMd5SWt.js";
4
+ import { t as findSpawnHiddenLauncher } from "./rustBinary-DFoLw-3W.js";
5
5
  import "./pidStore-BIvsBQ8X.js";
6
6
  import { a as updateGlobalPidStatus } from "./globalPidIndex-CoNr7tS8.js";
7
7
  import { t as pgidForWrapper } from "./reaper-CWF2_ATd.js";
8
8
  import "./configShared-0MnIQ652.js";
9
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-mUaQHk4M.js";
10
- import { $ as TYPING_BADGE, D as recentReadEdges, E as readPtysize, H as writeToIpc, I as snapshotStatus, O as renderLogTailLines, S as listRecords, T as readNotes, b as isUserTyping, f as extractNeedsInput, j as resolveOne, k as renderRawLog, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, u as extractBadges } from "./subcommands-CNCgJ48t.js";
9
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-5N015orJ.js";
10
+ import { $ as TYPING_BADGE, D as recentReadEdges, E as readPtysize, H as writeToIpc, I as snapshotStatus, O as renderLogTailLines, S as listRecords, T as readNotes, b as isUserTyping, f as extractNeedsInput, j as resolveOne, k as renderRawLog, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, u as extractBadges } from "./subcommands-DMe0U83W.js";
11
11
  import "./e2e-BeKjLhmO.js";
12
12
  import "./webrtcLink-BG0Xc4-W.js";
13
13
  import "./remotes-CgT91bMo.js";
14
14
  import { a as getSpawnHook, c as hasProvisionHook, d as resolveSpawnCwd, i as getProvisionRoot, l as hasSpawnHook, r as getProvisionHook, u as isProvisionAllowed } from "./workspaceConfig-_GtAZtsi.js";
15
15
  import { r as spawnRejectionReason } from "./spawnGate-CA5QD7D4.js";
16
16
  import yargs from "yargs";
17
- import { mkdir, open, readFile, stat, unlink, writeFile } from "fs/promises";
17
+ import { chmod, mkdir, open, readFile, stat, unlink, writeFile } from "fs/promises";
18
18
  import { arch, cpus, freemem, homedir, hostname, loadavg, platform, totalmem, uptime, userInfo } from "os";
19
19
  import path from "path";
20
20
  import { execFileSync } from "node:child_process";
@@ -289,16 +289,55 @@ async function globalBinDir(installer) {
289
289
  if (!out) return null;
290
290
  return isBun ? out : path.join(out, "bin");
291
291
  }
292
- async function managerRunnable(mgr) {
292
+ async function ensureNodeRuntime(which = Bun.which) {
293
+ if (process.platform === "win32") return null;
294
+ if (which("node")) return null;
295
+ const bun = which("bun");
296
+ if (!bun) return null;
297
+ const binDir = path.join(agentYesHome(), "bin");
298
+ const shim = path.join(binDir, "node");
299
+ const body = `#!/bin/sh\nexec '${bun.replace(/'/g, `'\\''`)}' "$@"\n`;
293
300
  try {
294
- return (await Bun.spawn([mgr.bin, "--version"], {
295
- stdout: "ignore",
296
- stderr: "ignore"
297
- }).exited ?? 1) === 0;
301
+ if (await readFile(shim, "utf-8").catch(() => null) !== body) {
302
+ await mkdir(binDir, { recursive: true });
303
+ await writeFile(shim, body);
304
+ await chmod(shim, 493);
305
+ }
306
+ if (!(process.env.PATH ?? "").split(path.delimiter).includes(binDir)) process.env.PATH = `${binDir}${path.delimiter}${process.env.PATH ?? ""}`;
307
+ return shim;
298
308
  } catch {
299
- return false;
309
+ return null;
310
+ }
311
+ }
312
+ function isNoNodeExecError(stderr) {
313
+ return /env: '?node'?: No such file or directory/.test(stderr) || /'node' is not recognized/.test(stderr);
314
+ }
315
+ async function managerProbe(mgr) {
316
+ try {
317
+ const p = Bun.spawn([mgr.bin, "--version"], {
318
+ stdout: "ignore",
319
+ stderr: "pipe",
320
+ env: {
321
+ ...process.env,
322
+ LC_ALL: "C"
323
+ }
324
+ });
325
+ const [code, stderr] = await Promise.all([p.exited, new Response(p.stderr).text()]);
326
+ return {
327
+ ok: (code ?? 1) === 0,
328
+ stderr
329
+ };
330
+ } catch (e) {
331
+ return {
332
+ ok: false,
333
+ stderr: String(e)
334
+ };
300
335
  }
301
336
  }
337
+ async function managerRunnable(mgr) {
338
+ await ensureNodeRuntime();
339
+ return (await managerProbe(mgr)).ok;
340
+ }
302
341
  async function resolveActiveManager() {
303
342
  const oxmgr = Bun.which("oxmgr");
304
343
  const pm2 = Bun.which("pm2");
@@ -352,8 +391,12 @@ async function installAndVerify(pkg) {
352
391
  id: pkg,
353
392
  bin
354
393
  };
355
- if (!await managerRunnable(mgr)) {
356
- const why = pkg === "oxmgr" ? "a native/glibc mismatch, e.g. glibc < 2.39" : "pm2 needs a Node.js runtime and none was found";
394
+ await ensureNodeRuntime();
395
+ const probe = await managerProbe(mgr);
396
+ if (!probe.ok) {
397
+ const detailLine = probe.stderr.trim().split("\n").filter(Boolean).pop()?.slice(0, 160);
398
+ const detail = detailLine ? ` — ${detailLine}` : "";
399
+ const why = isNoNodeExecError(probe.stderr) ? `${pkg} needs a Node.js runtime (its bin is a '#!/usr/bin/env node' script) and none was found` : pkg === "oxmgr" ? `likely a native binary mismatch, e.g. glibc < 2.39 on Linux${detail}` : `exec probe failed${detail}`;
357
400
  process.stderr.write(`ay serve install: ${pkg} installed but can't exec here (${why})` + (pkg === "oxmgr" ? " — falling back to pm2\n" : "\n"));
358
401
  return null;
359
402
  }
@@ -489,12 +532,12 @@ async function fetchDaemonVersion(port, token) {
489
532
  async function cmdServeDaemon(sub, args) {
490
533
  let mgr = resolveDaemonManager();
491
534
  if (mgr && !await managerRunnable(mgr)) {
492
- process.stderr.write(`ay serve install: ${mgr.id} is on PATH but can't exec here (likely a native/glibc mismatch) — bootstrapping a working manager…\n`);
535
+ process.stderr.write(`ay serve install: ${mgr.id} is on PATH but can't exec here — bootstrapping a working manager…\n`);
493
536
  mgr = null;
494
537
  }
495
538
  if (!mgr) mgr = await bootstrapDaemonManager();
496
539
  if (!mgr) {
497
- process.stderr.write("ay serve install: no usable process manager (need oxmgr or pm2)\n - oxmgr: bun add -g oxmgr (native binary; needs glibc ≥ 2.39 on Linux)\n - pm2: bun add -g pm2 (needs a Node.js runtime on PATH)\n On a minimal bun-only box, install node (for pm2) or a newer glibc (for oxmgr).\n");
540
+ process.stderr.write("ay serve install: no usable process manager (need oxmgr or pm2)\n - oxmgr: bun add -g oxmgr (native binary; needs glibc ≥ 2.39 on Linux)\n - pm2: bun add -g pm2 (pure JS; needs a node runtime on a bun-only box\n ay auto-writes a node→bun shim into ~/.agent-yes/bin, so if you still\n see this, check that dir is writable or install node)\n");
498
541
  return 1;
499
542
  }
500
543
  if (sub === "install") {
@@ -1851,7 +1894,7 @@ Options:
1851
1894
  const perm = body.perm ?? "r";
1852
1895
  if (perm !== "r" && perm !== "rw") return new Response(`invalid perm ${perm} (want r or rw)`, { status: 400 });
1853
1896
  try {
1854
- const { createScopedShare } = await import("./agentShare-Boa9P2dK.js");
1897
+ const { createScopedShare } = await import("./agentShare-C4LxU2xT.js");
1855
1898
  const share = await createScopedShare({
1856
1899
  agent: body.agent,
1857
1900
  perm,
@@ -1866,12 +1909,12 @@ Options:
1866
1909
  }
1867
1910
  }
1868
1911
  if (req.method === "GET" && p === "/api/shares") {
1869
- const { listShares } = await import("./agentShare-Boa9P2dK.js");
1912
+ const { listShares } = await import("./agentShare-C4LxU2xT.js");
1870
1913
  return Response.json(listShares());
1871
1914
  }
1872
1915
  const revokeM = /^\/api\/share\/([^/]+)$/.exec(p);
1873
1916
  if (req.method === "DELETE" && revokeM) {
1874
- const { revokeShare } = await import("./agentShare-Boa9P2dK.js");
1917
+ const { revokeShare } = await import("./agentShare-C4LxU2xT.js");
1875
1918
  const ok = revokeShare(decodeURIComponent(revokeM[1]));
1876
1919
  return new Response(ok ? "revoked" : "no such share", { status: ok ? 200 : 404 });
1877
1920
  }
@@ -2052,7 +2095,7 @@ Options:
2052
2095
  const shutdown = (resolve) => {
2053
2096
  if (heartbeat) clearInterval(heartbeat);
2054
2097
  closeShare?.();
2055
- import("./agentShare-Boa9P2dK.js").then((m) => m.revokeAllShares()).catch(() => {});
2098
+ import("./agentShare-C4LxU2xT.js").then((m) => m.revokeAllShares()).catch(() => {});
2056
2099
  server?.stop();
2057
2100
  resolve();
2058
2101
  };
@@ -2065,4 +2108,4 @@ Options:
2065
2108
 
2066
2109
  //#endregion
2067
2110
  export { cmdServe };
2068
- //# sourceMappingURL=serve-DVhbtjR4.js.map
2111
+ //# sourceMappingURL=serve-CSijvUDO.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-DVhbtjR4.js");
35
+ const { cmdServe } = await import("./serve-CSijvUDO.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-Dj8Grg0K.js.map
45
+ //# sourceMappingURL=setup-ChMQrv6t.js.map
@@ -12,6 +12,7 @@ import { homedir } from "os";
12
12
  import path from "path";
13
13
  import { randomUUID } from "node:crypto";
14
14
  import os from "node:os";
15
+ import { randomBytes as randomBytes$1 } from "crypto";
15
16
 
16
17
  //#region ts/agentTree.ts
17
18
  /**
@@ -1146,15 +1147,15 @@ async function runSubcommand(argv) {
1146
1147
  case "restart": return await cmdRestart(rest);
1147
1148
  case "note": return await cmdNote(rest);
1148
1149
  case "serve": {
1149
- const { cmdServe } = await import("./serve-DVhbtjR4.js");
1150
+ const { cmdServe } = await import("./serve-CSijvUDO.js");
1150
1151
  return cmdServe(rest);
1151
1152
  }
1152
1153
  case "setup": {
1153
- const { cmdSetup } = await import("./setup-Dj8Grg0K.js");
1154
+ const { cmdSetup } = await import("./setup-ChMQrv6t.js");
1154
1155
  return cmdSetup(rest);
1155
1156
  }
1156
1157
  case "schedule": {
1157
- const { cmdSchedule } = await import("./schedule-Bb98iG9W.js");
1158
+ const { cmdSchedule } = await import("./schedule-AZACZY6R.js");
1158
1159
  return cmdSchedule(rest);
1159
1160
  }
1160
1161
  case "remote": {
@@ -2729,7 +2730,7 @@ async function cmdSpawn(rest) {
2729
2730
  * (`send`, `key`, `select`): refuse a self-targeting loop, and require that THIS
2730
2731
  * sender actually looked at THIS target recently — an agent is blocked, an
2731
2732
  * interactive human is only warned — unless `force`. Returns the sender context
2732
- * so a caller can reuse it (e.g. `send`'s `[from …]` prefix). Extracted from
2733
+ * so a caller can reuse it (e.g. `send`'s `[ay-msg …]` header). Extracted from
2733
2734
  * cmdSend so the action commands enforce the identical guard.
2734
2735
  */
2735
2736
  async function enforceSendGuards(record, force) {
@@ -2926,7 +2927,14 @@ async function cmdSend(rest) {
2926
2927
  return 0;
2927
2928
  }
2928
2929
  const replyTarget = sender.agent?.agent_id || sender.agent?.pid;
2929
- const fullBody = (sender.agent && !isSlashCommand(body) ? `[from ${sender.agent.cli} #${sender.agent.pid} @ ${shortenPath(sender.agent.cwd)} — reply: ay send ${replyTarget} "..."]\n` : "") + body;
2930
+ let prefix = "";
2931
+ let suffix = "";
2932
+ if (sender.agent && !isSlashCommand(body)) {
2933
+ const nonce = randomBytes$1(4).toString("hex");
2934
+ prefix = `[ay-msg ${nonce} from ${sender.agent.cli} #${sender.agent.pid} @ ${shortenPath(sender.agent.cwd)} — reply: ay send ${replyTarget} "..."]\n`;
2935
+ suffix = `\n[/ay-msg ${nonce}]`;
2936
+ }
2937
+ const fullBody = prefix + body + suffix;
2930
2938
  const noWait = Boolean(argv.noWait) || process.env.AGENT_YES_SEND_NO_WAIT === "1";
2931
2939
  if (fullBody && !noWait && !force) {
2932
2940
  const { clear, waitedMs } = await backoffWhileTyping(record.pid, SEND_TYPING_MAX_WAIT_MS);
@@ -3996,7 +4004,7 @@ async function cmdNotify(rest) {
3996
4004
  }
3997
4005
  const ensure = async () => {
3998
4006
  if (!argv["ensure-daemon"]) return;
3999
- const { ensureDaemon } = await import("./notifyDaemon-hKT38cBs.js");
4007
+ const { ensureDaemon } = await import("./notifyDaemon--OxBe8nG.js");
4000
4008
  await ensureDaemon().catch(() => null);
4001
4009
  };
4002
4010
  await heartbeatWatcher(parent, selfStartedAt);
@@ -4070,7 +4078,7 @@ async function cmdNotifyCursor(args) {
4070
4078
  }
4071
4079
  async function cmdNotifyd(rest) {
4072
4080
  const sub = rest[0] ?? "status";
4073
- const daemon = await import("./notifyDaemon-hKT38cBs.js");
4081
+ const daemon = await import("./notifyDaemon--OxBe8nG.js");
4074
4082
  switch (sub) {
4075
4083
  case "run": return daemon.runDaemon();
4076
4084
  case "once": return daemon.runDaemon({ once: true });
@@ -4097,4 +4105,4 @@ async function cmdNotifyd(rest) {
4097
4105
 
4098
4106
  //#endregion
4099
4107
  export { TYPING_BADGE as $, renderRawLogLines as A, waitForLogQuiet as B, matchKeyword as C, recentReadEdges as D, readPtysize as E, runSubcommand as F, hostId as G, writeToIpc as H, snapshotStatus as I, readInbox as J, listInboxParents as K, stdinActivityPath as L, resolveReadWindow as M, resolveResumeArgs as N, renderLogTailLines as O, restartHintLines as P, notifyDir as Q, stopTipForCli as R, listRecords as S, readNotes as T, appendEvent as U, writeKeysPaced as V, gcInboxes as W, daemonLockDir as X, shouldStealLock as Y, daemonLockOwnerPath as Z, isPidAlive as _, cmdHelp as a, isUserTyping as b, deriveLiveState as c, extractMenu as d, extractNeedsInput as f, isExitRequest as g, isAgentStuck as h, backoffWhileTyping as i, resolveOne as j, renderRawLog as k, deriveLiveStatus as l, finalizedLines as m, READ_PAGE_DEFAULT as n, controlCodeFromName as o, extractTaskCounts as p, liveWatchers as q, TYPING_WINDOW_MS as r, cursorAbs as s, GRACEFUL_EXIT_COMMANDS as t, extractBadges as u, isSlashCommand as v, menuSelectKeys as w, lastStdinAt as x, isSubcommand as y, submitAndConfirm as z };
4100
- //# sourceMappingURL=subcommands-CNCgJ48t.js.map
4108
+ //# sourceMappingURL=subcommands-DMe0U83W.js.map
@@ -1,7 +1,7 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-CoNr7tS8.js";
3
3
  import "./configShared-0MnIQ652.js";
4
- import { A as renderRawLogLines, B as waitForLogQuiet, C as matchKeyword, D as recentReadEdges, E as readPtysize, F as runSubcommand, H as writeToIpc, I as snapshotStatus, L as stdinActivityPath, M as resolveReadWindow, N as resolveResumeArgs, O as renderLogTailLines, P as restartHintLines, R as stopTipForCli, S as listRecords, T as readNotes, V as writeKeysPaced, _ as isPidAlive, a as cmdHelp, b as isUserTyping, c as deriveLiveState, d as extractMenu, f as extractNeedsInput, g as isExitRequest, h as isAgentStuck, i as backoffWhileTyping, j as resolveOne, k as renderRawLog, l as deriveLiveStatus, m as finalizedLines, n as READ_PAGE_DEFAULT, o as controlCodeFromName, p as extractTaskCounts, r as TYPING_WINDOW_MS, s as cursorAbs, t as GRACEFUL_EXIT_COMMANDS, u as extractBadges, v as isSlashCommand, w as menuSelectKeys, x as lastStdinAt, y as isSubcommand, z as submitAndConfirm } from "./subcommands-CNCgJ48t.js";
4
+ import { A as renderRawLogLines, B as waitForLogQuiet, C as matchKeyword, D as recentReadEdges, E as readPtysize, F as runSubcommand, H as writeToIpc, I as snapshotStatus, L as stdinActivityPath, M as resolveReadWindow, N as resolveResumeArgs, O as renderLogTailLines, P as restartHintLines, R as stopTipForCli, S as listRecords, T as readNotes, V as writeKeysPaced, _ as isPidAlive, a as cmdHelp, b as isUserTyping, c as deriveLiveState, d as extractMenu, f as extractNeedsInput, g as isExitRequest, h as isAgentStuck, i as backoffWhileTyping, j as resolveOne, k as renderRawLog, l as deriveLiveStatus, m as finalizedLines, n as READ_PAGE_DEFAULT, o as controlCodeFromName, p as extractTaskCounts, r as TYPING_WINDOW_MS, s as cursorAbs, t as GRACEFUL_EXIT_COMMANDS, u as extractBadges, v as isSlashCommand, w as menuSelectKeys, x as lastStdinAt, y as isSubcommand, z as submitAndConfirm } from "./subcommands-DMe0U83W.js";
5
5
  import "./e2e-BeKjLhmO.js";
6
6
  import "./webrtcLink-BG0Xc4-W.js";
7
7
  import "./remotes-CgT91bMo.js";
@@ -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-DhCi7ncH.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-CUMd5SWt.js";
3
3
  import { t as agentYesHome } from "./agentYesHome-CtHb5b71.js";
4
4
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CNMl13dC.js";
5
5
  import { t as PidStore } from "./pidStore-BIvsBQ8X.js";
@@ -1824,4 +1824,4 @@ function sleep(ms) {
1824
1824
 
1825
1825
  //#endregion
1826
1826
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1827
- //# sourceMappingURL=ts-BQh1-ljE.js.map
1827
+ //# sourceMappingURL=ts-7XqYx0eW.js.map
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
7
7
 
8
8
  //#region package.json
9
9
  var name = "agent-yes";
10
- var version = "1.191.0";
10
+ var version = "1.192.1";
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-DhCi7ncH.js.map
218
+ //# sourceMappingURL=versionChecker-CUMd5SWt.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.191.0",
3
+ "version": "1.192.1",
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/serve.spec.ts CHANGED
@@ -1,5 +1,8 @@
1
- import { describe, expect, it } from "vitest";
2
- import { oxmgrVersionHasWindowsFix } from "./serve.ts";
1
+ import { mkdtemp, readFile, rm, stat } from "fs/promises";
2
+ import { tmpdir } from "os";
3
+ import path from "path";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import { ensureNodeRuntime, isNoNodeExecError, oxmgrVersionHasWindowsFix } from "./serve.ts";
3
6
 
4
7
  // Guards the Windows daemon-manager selection: on Windows we only PREFER oxmgr
5
8
  // when the installed build carries the daemon-socket-inheritance fix. Stock
@@ -31,3 +34,106 @@ describe("oxmgrVersionHasWindowsFix", () => {
31
34
  expect(oxmgrVersionHasWindowsFix("oxmgr (unknown)")).toBe(false);
32
35
  });
33
36
  });
37
+
38
+ // The bun-only-no-node path (sym003): pm2/oxmgr bins are `#!/usr/bin/env node`
39
+ // scripts, so with no node on PATH they die at the shebang. ensureNodeRuntime
40
+ // must bridge that with a node→bun shim in ay's own bin dir.
41
+ describe("ensureNodeRuntime", () => {
42
+ let home: string;
43
+ let savedPath: string | undefined;
44
+ let savedHome: string | undefined;
45
+
46
+ beforeEach(async () => {
47
+ home = await mkdtemp(path.join(tmpdir(), "ay-node-shim-"));
48
+ savedPath = process.env.PATH;
49
+ savedHome = process.env.AGENT_YES_HOME;
50
+ process.env.AGENT_YES_HOME = home;
51
+ });
52
+
53
+ afterEach(async () => {
54
+ process.env.PATH = savedPath;
55
+ if (savedHome === undefined) delete process.env.AGENT_YES_HOME;
56
+ else process.env.AGENT_YES_HOME = savedHome;
57
+ await rm(home, { recursive: true, force: true });
58
+ });
59
+
60
+ const noNode = (cmd: string) => (cmd === "bun" ? "/fake/bin/bun" : null);
61
+
62
+ it.skipIf(process.platform === "win32")(
63
+ "writes an executable node→bun shim and prepends it to PATH when node is absent",
64
+ async () => {
65
+ const shim = await ensureNodeRuntime(noNode);
66
+ expect(shim).toBe(path.join(home, "bin", "node"));
67
+ const body = await readFile(shim!, "utf-8");
68
+ expect(body).toBe("#!/bin/sh\nexec '/fake/bin/bun' \"$@\"\n");
69
+ expect((await stat(shim!)).mode & 0o111).not.toBe(0); // executable
70
+ expect(process.env.PATH!.split(path.delimiter)[0]).toBe(path.join(home, "bin"));
71
+ },
72
+ );
73
+
74
+ it.skipIf(process.platform === "win32")("does not duplicate the PATH entry", async () => {
75
+ await ensureNodeRuntime(noNode);
76
+ await ensureNodeRuntime(noNode);
77
+ const binDir = path.join(home, "bin");
78
+ const hits = process.env.PATH!.split(path.delimiter).filter((p) => p === binDir);
79
+ expect(hits).toHaveLength(1);
80
+ });
81
+
82
+ it.skipIf(process.platform === "win32")(
83
+ "leaves an up-to-date shim untouched on repeat calls (read-only paths stay read-only)",
84
+ async () => {
85
+ const shim = (await ensureNodeRuntime(noNode))!;
86
+ const before = await stat(shim);
87
+ await new Promise((r) => setTimeout(r, 20));
88
+ await ensureNodeRuntime(noNode);
89
+ const after = await stat(shim);
90
+ expect(after.mtimeMs).toBe(before.mtimeMs); // not rewritten
91
+ },
92
+ );
93
+
94
+ it.skipIf(process.platform === "win32")(
95
+ "single-quotes a bun path containing sh-special characters",
96
+ async () => {
97
+ const shim = await ensureNodeRuntime((cmd) =>
98
+ cmd === "bun" ? "/opt/we$ird `dir'/bun" : null,
99
+ );
100
+ const body = await readFile(shim!, "utf-8");
101
+ expect(body).toBe("#!/bin/sh\nexec '/opt/we$ird `dir'\\''/bun' \"$@\"\n");
102
+ },
103
+ );
104
+
105
+ it("is a no-op when a real node exists", async () => {
106
+ expect(await ensureNodeRuntime(() => "/usr/bin/whatever")).toBeNull();
107
+ });
108
+
109
+ it("is a no-op when bun is missing too (nothing to shim to)", async () => {
110
+ expect(await ensureNodeRuntime(() => null)).toBeNull();
111
+ });
112
+ });
113
+
114
+ // Guards the serve-install diagnostic: a missing node runtime must be reported
115
+ // as such, never as a "glibc mismatch" (glibc doesn't even exist on macOS).
116
+ describe("isNoNodeExecError", () => {
117
+ it("matches macOS/BSD env output", () => {
118
+ expect(isNoNodeExecError("env: node: No such file or directory")).toBe(true);
119
+ });
120
+
121
+ it("matches GNU coreutils quoted env output", () => {
122
+ expect(isNoNodeExecError("env: 'node': No such file or directory")).toBe(true);
123
+ });
124
+
125
+ it("matches Windows cmd output", () => {
126
+ expect(
127
+ isNoNodeExecError("'node' is not recognized as an internal or external command"),
128
+ ).toBe(true);
129
+ });
130
+
131
+ it("does not match a real native/glibc failure", () => {
132
+ expect(
133
+ isNoNodeExecError(
134
+ "oxmgr: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.39' not found",
135
+ ),
136
+ ).toBe(false);
137
+ expect(isNoNodeExecError("")).toBe(false);
138
+ });
139
+ });
package/ts/serve.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { mkdir, open, readFile, stat, unlink, writeFile } from "fs/promises";
1
+ import { chmod, mkdir, open, readFile, stat, unlink, writeFile } from "fs/promises";
2
2
  import { renameSync, watch, writeFileSync } from "node:fs";
3
3
  import { execFileSync } from "node:child_process";
4
4
  import { fileURLToPath } from "node:url";
@@ -405,18 +405,83 @@ async function globalBinDir(installer: string[]): Promise<string | null> {
405
405
  return isBun ? out : path.join(out, "bin");
406
406
  }
407
407
 
408
+ // Both oxmgr's and pm2's global bins are `#!/usr/bin/env node` JS scripts, so on
409
+ // a bun-only box (setup.sh installs no node) exec dies with "env: node: No such
410
+ // file or directory" — even though pm2 is pure JS and runs perfectly under bun.
411
+ // When node is missing but bun exists, write a node→bun shim into ay's own bin
412
+ // dir and prepend it to PATH so `env node` resolves. NOT solvable with
413
+ // `bun add -g node`: that package (node-bin-gen) lands a broken arch-specific
414
+ // stub and `Bun.which("node")` still finds nothing. POSIX-only — on Windows the
415
+ // npm .cmd shims invoke node.exe directly and an sh script can't stand in.
416
+ // Called from managerRunnable, which read-only paths (`ay serve status`) also
417
+ // reach: an up-to-date shim is left untouched there (a read, not a write), so
418
+ // only the very first run on a node-less box materializes the file.
419
+ // Returns the shim path when the shim is in effect, null when unneeded/impossible.
420
+ export async function ensureNodeRuntime(
421
+ which: (cmd: string) => string | null = Bun.which,
422
+ ): Promise<string | null> {
423
+ if (process.platform === "win32") return null;
424
+ if (which("node")) return null;
425
+ const bun = which("bun");
426
+ if (!bun) return null;
427
+ const binDir = path.join(agentYesHome(), "bin");
428
+ const shim = path.join(binDir, "node");
429
+ // Single-quote the bun path: inside sh double quotes `$`, backtick and `\`
430
+ // would still expand, corrupting the shim for a path containing them.
431
+ const body = `#!/bin/sh\nexec '${bun.replace(/'/g, `'\\''`)}' "$@"\n`;
432
+ try {
433
+ if ((await readFile(shim, "utf-8").catch(() => null)) !== body) {
434
+ await mkdir(binDir, { recursive: true });
435
+ await writeFile(shim, body);
436
+ await chmod(shim, 0o755);
437
+ }
438
+ if (!(process.env.PATH ?? "").split(path.delimiter).includes(binDir)) {
439
+ process.env.PATH = `${binDir}${path.delimiter}${process.env.PATH ?? ""}`;
440
+ }
441
+ return shim;
442
+ } catch {
443
+ return null; // best-effort: the exec probe will fail and name the reason
444
+ }
445
+ }
446
+
447
+ // Did an exec attempt die because `env` couldn't find a node runtime? macOS/BSD
448
+ // env says `env: node: No such file or directory`, GNU coreutils quotes it
449
+ // (`env: 'node': …`), Windows cmd says `'node' is not recognized`.
450
+ export function isNoNodeExecError(stderr: string): boolean {
451
+ return (
452
+ /env: '?node'?: No such file or directory/.test(stderr) ||
453
+ /'node' is not recognized/.test(stderr)
454
+ );
455
+ }
456
+
457
+ // Probe one manager binary with `--version`, capturing stderr so a failure can
458
+ // be diagnosed (glibc mismatch vs missing node runtime vs anything else).
459
+ // LC_ALL=C pins env(1)/libc error text to English so isNoNodeExecError's
460
+ // patterns hold on localized systems.
461
+ async function managerProbe(mgr: DaemonManager): Promise<{ ok: boolean; stderr: string }> {
462
+ try {
463
+ const p = Bun.spawn([mgr.bin, "--version"], {
464
+ stdout: "ignore",
465
+ stderr: "pipe",
466
+ env: { ...process.env, LC_ALL: "C" },
467
+ });
468
+ const [code, stderr] = await Promise.all([p.exited, new Response(p.stderr).text()]);
469
+ return { ok: (code ?? 1) === 0, stderr };
470
+ } catch (e) {
471
+ return { ok: false, stderr: String(e) };
472
+ }
473
+ }
474
+
408
475
  // Does this manager's binary actually execute? A `bun add -g oxmgr` can succeed
409
476
  // yet leave a binary that can't run: oxmgr vendors a native binary requiring
410
477
  // GLIBC_2.39, which a box like Debian 12 (glibc 2.36) lacks, so exec dies with
411
- // "GLIBC_2.39 not found". Probe with `--version`: exit 0 means runnable; a
412
- // non-zero exit or spawn failure means the shim is on PATH but unusable.
478
+ // "GLIBC_2.39 not found"; and on a node-less box the `env node` shebang itself
479
+ // fails (see ensureNodeRuntime, applied here so every probe self-heals). Probe
480
+ // with `--version`: exit 0 means runnable; a non-zero exit or spawn failure
481
+ // means the shim is on PATH but unusable.
413
482
  async function managerRunnable(mgr: DaemonManager): Promise<boolean> {
414
- try {
415
- const p = Bun.spawn([mgr.bin, "--version"], { stdout: "ignore", stderr: "ignore" });
416
- return ((await p.exited) ?? 1) === 0;
417
- } catch {
418
- return false;
419
- }
483
+ await ensureNodeRuntime();
484
+ return (await managerProbe(mgr)).ok;
420
485
  }
421
486
 
422
487
  // Which manager is ACTUALLY running our daemon? `resolveDaemonManager` picks by
@@ -464,14 +529,23 @@ async function installAndVerify(pkg: "oxmgr" | "pm2"): Promise<DaemonManager | n
464
529
  const bin = Bun.which(pkg);
465
530
  if (!bin) return null;
466
531
  const mgr: DaemonManager = { id: pkg, bin };
467
- if (!(await managerRunnable(mgr))) {
468
- // Name the actual reason: oxmgr ships a native binary (glibc), pm2 is a
469
- // Node.js app that needs `node` on PATH — a bun-only box has neither by
470
- // default, so a generic "glibc mismatch" would mislead for pm2.
471
- const why =
472
- pkg === "oxmgr"
473
- ? "a native/glibc mismatch, e.g. glibc < 2.39"
474
- : "pm2 needs a Node.js runtime and none was found";
532
+ await ensureNodeRuntime();
533
+ const probe = await managerProbe(mgr);
534
+ if (!probe.ok) {
535
+ // Name the ACTUAL reason from the probe's stderr. A missing node runtime
536
+ // hits both managers identically (`env: node: No such file or directory` —
537
+ // their bins are `#!/usr/bin/env node` scripts), and reporting that as a
538
+ // "glibc mismatch" is nonsense on macOS where glibc doesn't exist. Only
539
+ // blame glibc for an oxmgr that got PAST the shebang and died natively;
540
+ // any other failure just gets the probe's own last stderr line — claiming
541
+ // "no node runtime" here would be false, ensureNodeRuntime just provided one.
542
+ const detailLine = probe.stderr.trim().split("\n").filter(Boolean).pop()?.slice(0, 160);
543
+ const detail = detailLine ? ` — ${detailLine}` : "";
544
+ const why = isNoNodeExecError(probe.stderr)
545
+ ? `${pkg} needs a Node.js runtime (its bin is a '#!/usr/bin/env node' script) and none was found`
546
+ : pkg === "oxmgr"
547
+ ? `likely a native binary mismatch, e.g. glibc < 2.39 on Linux${detail}`
548
+ : `exec probe failed${detail}`;
475
549
  process.stderr.write(
476
550
  `ay serve install: ${pkg} installed but can't exec here (${why})` +
477
551
  (pkg === "oxmgr" ? " — falling back to pm2\n" : "\n"),
@@ -670,7 +744,7 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
670
744
  let mgr = resolveDaemonManager();
671
745
  if (mgr && !(await managerRunnable(mgr))) {
672
746
  process.stderr.write(
673
- `ay serve install: ${mgr.id} is on PATH but can't exec here (likely a native/glibc mismatch) — bootstrapping a working manager…\n`,
747
+ `ay serve install: ${mgr.id} is on PATH but can't exec here — bootstrapping a working manager…\n`,
674
748
  );
675
749
  mgr = null;
676
750
  }
@@ -679,8 +753,9 @@ async function cmdServeDaemon(sub: string, args: string[]): Promise<number> {
679
753
  process.stderr.write(
680
754
  "ay serve install: no usable process manager (need oxmgr or pm2)\n" +
681
755
  " - oxmgr: bun add -g oxmgr (native binary; needs glibc ≥ 2.39 on Linux)\n" +
682
- " - pm2: bun add -g pm2 (needs a Node.js runtime on PATH)\n" +
683
- " On a minimal bun-only box, install node (for pm2) or a newer glibc (for oxmgr).\n",
756
+ " - pm2: bun add -g pm2 (pure JS; needs a node runtime on a bun-only box\n" +
757
+ " ay auto-writes a node→bun shim into ~/.agent-yes/bin, so if you still\n" +
758
+ " see this, check that dir is writable or install node)\n",
684
759
  );
685
760
  return 1;
686
761
  }
package/ts/subcommands.ts CHANGED
@@ -11,6 +11,7 @@
11
11
  * to the normal agent-spawning flow.
12
12
  */
13
13
 
14
+ import { randomBytes } from "crypto";
14
15
  import { appendFile, mkdir, open, readFile, stat, writeFile } from "fs/promises";
15
16
  import ms from "ms";
16
17
  import { homedir } from "os";
@@ -2653,7 +2654,7 @@ async function cmdSpawn(rest: string[]): Promise<number> {
2653
2654
  * (`send`, `key`, `select`): refuse a self-targeting loop, and require that THIS
2654
2655
  * sender actually looked at THIS target recently — an agent is blocked, an
2655
2656
  * interactive human is only warned — unless `force`. Returns the sender context
2656
- * so a caller can reuse it (e.g. `send`'s `[from …]` prefix). Extracted from
2657
+ * so a caller can reuse it (e.g. `send`'s `[ay-msg …]` header). Extracted from
2657
2658
  * cmdSend so the action commands enforce the identical guard.
2658
2659
  */
2659
2660
  async function enforceSendGuards(
@@ -2962,13 +2963,21 @@ async function cmdSend(rest: string[]): Promise<number> {
2962
2963
  // prefix would bump it to line 2 and the CLI would type the command as plain
2963
2964
  // text. So skip the prefix for a command body and send it verbatim —
2964
2965
  // attribution is dropped for the command, but it actually runs.
2966
+ // The header/footer pair shares a random nonce so the recipient can trust the
2967
+ // block's boundaries: text INSIDE the body can't forge a matching open/close
2968
+ // marker (the nonce is generated here, after the body was authored), so a
2969
+ // spoofed "[from …]" line or a premature "[/ay-msg]" embedded in a message
2970
+ // can't impersonate another sender or truncate/extend the trusted region.
2965
2971
  const replyTarget = sender.agent?.agent_id || sender.agent?.pid;
2966
- const prefix =
2967
- sender.agent && !isSlashCommand(body)
2968
- ? `[from ${sender.agent.cli} #${sender.agent.pid} @ ${shortenPath(sender.agent.cwd)} — reply: ay send ${replyTarget} "..."]\n`
2969
- : "";
2972
+ let prefix = "";
2973
+ let suffix = "";
2974
+ if (sender.agent && !isSlashCommand(body)) {
2975
+ const nonce = randomBytes(4).toString("hex");
2976
+ prefix = `[ay-msg ${nonce} from ${sender.agent.cli} #${sender.agent.pid} @ ${shortenPath(sender.agent.cwd)} — reply: ay send ${replyTarget} "..."]\n`;
2977
+ suffix = `\n[/ay-msg ${nonce}]`;
2978
+ }
2970
2979
 
2971
- const fullBody = prefix + body;
2980
+ const fullBody = prefix + body + suffix;
2972
2981
  const noWait = Boolean(argv.noWait) || process.env.AGENT_YES_SEND_NO_WAIT === "1";
2973
2982
 
2974
2983
  // Back off while the user is typing at the target's terminal — injecting our
@@ -1,8 +0,0 @@
1
- import "./ts-BQh1-ljE.js";
2
- import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DhCi7ncH.js";
4
- import "./pidStore-BIvsBQ8X.js";
5
- import "./globalPidIndex-CoNr7tS8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-mUaQHk4M.js";
7
-
8
- export { SUPPORTED_CLIS };