agent-yes 1.96.0 → 1.97.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-BAc4Jcrw.js";
2
+ import "./logger-B9h0djqx.js";
3
+ import "./versionChecker-MNvA73o9.js";
4
+ import "./pidStore-DBjlqzo8.js";
5
+ import "./globalPidIndex-yVd3mbsV.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-eD-UlqO_.js";
7
+
8
+ export { SUPPORTED_CLIS };
@@ -1,8 +1,8 @@
1
- import { t as CLIS_CONFIG } from "./ts-DkjQJTcB.js";
1
+ import { t as CLIS_CONFIG } from "./ts-BAc4Jcrw.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-B2FAlgXF.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-eD-UlqO_.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-xqnqyGKE.js";
3
+ import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-MNvA73o9.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-EieqoC9Z.js");
485
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-B4gXEu5I.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-D4cJA4UH.js");
499
+ const { startTray } = await import("./tray-CWQe9DMY.js");
500
500
  await startTray();
501
501
  await new Promise(() => {});
502
502
  }
503
503
  {
504
- const { ensureTray } = await import("./tray-D4cJA4UH.js");
504
+ const { ensureTray } = await import("./tray-CWQe9DMY.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-G8izHOJP.js");
518
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-CNO_pj9f.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}`);
@@ -545,7 +545,7 @@ if (config.showVersion) {
545
545
  process.exit(0);
546
546
  }
547
547
  if (config.appendPrompt) {
548
- const { PidStore } = await import("./pidStore-iJY3JFTn.js");
548
+ const { PidStore } = await import("./pidStore-9b3YTuf4.js");
549
549
  const ipcPath = await PidStore.findActiveFifo(process.cwd());
550
550
  if (!ipcPath) {
551
551
  console.error("No active agent with IPC found in current directory.");
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-DkjQJTcB.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-BAc4Jcrw.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import "./versionChecker-xqnqyGKE.js";
4
- import "./pidStore-DTzl6zeh.js";
3
+ import "./versionChecker-MNvA73o9.js";
4
+ import "./pidStore-DBjlqzo8.js";
5
5
  import "./globalPidIndex-yVd3mbsV.js";
6
6
 
7
7
  export { AgentContext, CLIS_CONFIG, config, agentYes as default, removeControlCharacters };
@@ -0,0 +1,5 @@
1
+ import "./logger-B9h0djqx.js";
2
+ import { t as PidStore } from "./pidStore-DBjlqzo8.js";
3
+ import "./globalPidIndex-yVd3mbsV.js";
4
+
5
+ export { PidStore };
@@ -6,6 +6,26 @@ import { homedir } from "os";
6
6
  import path from "path";
7
7
  import { lock } from "proper-lockfile";
8
8
 
9
+ //#region ts/agentYesHome.ts
10
+ /**
11
+ * Root directory for cross-runtime, machine-global agent-yes state:
12
+ * the pid index (`pids.jsonl`), FIFO/named-pipe IPC endpoints (`fifo/`),
13
+ * winsize signals, notes, and the serve token.
14
+ *
15
+ * Durable per-session *logs* deliberately do NOT live here — they go under
16
+ * `<cwd>/.agent-yes/` so they stay colocated with the project that produced
17
+ * them (see `PidStore`). Only ephemeral IPC + the discovery index are global,
18
+ * which keeps FIFOs on the local home filesystem (reliable `mkfifo`) and lets
19
+ * `ay ls`/`ay attach` find every agent regardless of the caller's cwd.
20
+ *
21
+ * Resolved at call time (not module load) so tests and callers can override
22
+ * via `$AGENT_YES_HOME` without juggling the module cache.
23
+ */
24
+ function agentYesHome() {
25
+ return process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
26
+ }
27
+
28
+ //#endregion
9
29
  //#region ts/JsonlStore.ts
10
30
  /**
11
31
  * A lightweight NeDB-style JSONL persistence layer.
@@ -184,26 +204,6 @@ function generateId() {
184
204
  return Date.now().toString(36) + (idCounter++).toString(36) + Math.random().toString(36).slice(2, 6);
185
205
  }
186
206
 
187
- //#endregion
188
- //#region ts/agentYesHome.ts
189
- /**
190
- * Root directory for cross-runtime, machine-global agent-yes state:
191
- * the pid index (`pids.jsonl`), FIFO/named-pipe IPC endpoints (`fifo/`),
192
- * winsize signals, notes, and the serve token.
193
- *
194
- * Durable per-session *logs* deliberately do NOT live here — they go under
195
- * `<cwd>/.agent-yes/` so they stay colocated with the project that produced
196
- * them (see `PidStore`). Only ephemeral IPC + the discovery index are global,
197
- * which keeps FIFOs on the local home filesystem (reliable `mkfifo`) and lets
198
- * `ay ls`/`ay attach` find every agent regardless of the caller's cwd.
199
- *
200
- * Resolved at call time (not module load) so tests and callers can override
201
- * via `$AGENT_YES_HOME` without juggling the module cache.
202
- */
203
- function agentYesHome() {
204
- return process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
205
- }
206
-
207
207
  //#endregion
208
208
  //#region ts/pidStore.ts
209
209
  var PidStore = class PidStore {
@@ -380,5 +380,5 @@ pid-db/
380
380
  };
381
381
 
382
382
  //#endregion
383
- export { PidStore as t };
384
- //# sourceMappingURL=pidStore-DTzl6zeh.js.map
383
+ export { agentYesHome as n, PidStore as t };
384
+ //# sourceMappingURL=pidStore-DBjlqzo8.js.map
@@ -276,4 +276,4 @@ function shouldUseLock(_cwd) {
276
276
 
277
277
  //#endregion
278
278
  export { shouldUseLock as i, getRunningAgentCount as n, releaseLock as r, acquireLock as t };
279
- //# sourceMappingURL=runningLock-C22d9SRJ.js.map
279
+ //# sourceMappingURL=runningLock-CJxsoGdb.js.map
@@ -1,15 +1,16 @@
1
- import "./ts-DkjQJTcB.js";
1
+ import "./ts-BAc4Jcrw.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import "./versionChecker-xqnqyGKE.js";
4
- import "./pidStore-DTzl6zeh.js";
3
+ import "./versionChecker-MNvA73o9.js";
4
+ import "./pidStore-DBjlqzo8.js";
5
5
  import "./globalPidIndex-yVd3mbsV.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-B2FAlgXF.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-eD-UlqO_.js";
7
7
  import "./remotes-C3xPRtfg.js";
8
- import { c as readNotes, f as snapshotStatus, l as renderRawLog, m as writeToIpc, o as listRecords, r as controlCodeFromName, u as resolveOne } from "./subcommands-CcOYsLYD.js";
8
+ import { c as readNotes, f as snapshotStatus, l as renderRawLog, m as writeToIpc, o as listRecords, r as controlCodeFromName, u as resolveOne } from "./subcommands-K242usI5.js";
9
9
  import yargs from "yargs";
10
- import { mkdir, readFile, writeFile } from "fs/promises";
10
+ import { mkdir, open, readFile, writeFile } from "fs/promises";
11
11
  import { homedir } from "os";
12
12
  import path from "path";
13
+ import { watch } from "node:fs";
13
14
  import { randomBytes, timingSafeEqual } from "crypto";
14
15
 
15
16
  //#region ts/serve.ts
@@ -111,7 +112,7 @@ async function cmdServe(rest) {
111
112
  Start an HTTP API server so remote machines can list/tail/send agents.
112
113
 
113
114
  Options:
114
- --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 --share [URL] Share over WebRTC to agent-yes.com (bare flag mints a room+link)\n --allow-spawn Let the shared console launch new agents (asks y/N per request)\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 via 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`);
115
+ --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 --share [URL] Share over WebRTC to agent-yes.com (bare flag mints a room+link)\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 via 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`);
115
116
  return 0;
116
117
  }
117
118
  const sub = rest[0];
@@ -139,7 +140,7 @@ Options:
139
140
  }).option("allow-spawn", {
140
141
  type: "boolean",
141
142
  default: false,
142
- description: "Allow the shared console to spawn new agents (asks y/N per request on a TTY)"
143
+ description: "Deprecated no-op — the console can always spawn agents"
143
144
  }).help(false).version(false).exitProcess(false).parseAsync();
144
145
  const port = argv.port ?? DEFAULT_PORT;
145
146
  const host = argv.host ?? "127.0.0.1";
@@ -154,20 +155,6 @@ Options:
154
155
  const scheme = useHttps ? "https" : "http";
155
156
  if (host !== "127.0.0.1" && host !== "localhost") process.stderr.write("ay serve: warning: binding to non-loopback — ensure your network is trusted or use Tailscale/VPN\n");
156
157
  const token = await loadOrCreateToken(tokenFlag);
157
- const allowSpawn = argv["allow-spawn"] === true;
158
- const spawnQueue = [];
159
- let stdinWired = false;
160
- const confirmSpawn = (cli, cwd, prompt) => {
161
- if (!process.stdin.isTTY) return Promise.resolve(true);
162
- if (!stdinWired) {
163
- stdinWired = true;
164
- process.stdin.setEncoding("utf8");
165
- process.stdin.on("data", (d) => spawnQueue.shift()?.(/^y/i.test(d.trim())));
166
- process.stdin.resume();
167
- }
168
- process.stdout.write(`\n⚠ console requests spawn: ay ${cli}${prompt ? ` -- "${prompt.slice(0, 60)}"` : ""}\n cwd: ${cwd}\n allow? [y/N] `);
169
- return new Promise((res) => spawnQueue.push(res));
170
- };
171
158
  const serverOpts = {
172
159
  hostname: host,
173
160
  port,
@@ -219,6 +206,30 @@ Options:
219
206
  return new Response(e.message, { status: 404 });
220
207
  }
221
208
  }
209
+ const sizeM = /^\/api\/size\/(.+)$/.exec(p);
210
+ if (req.method === "GET" && sizeM) {
211
+ const keyword = decodeURIComponent(sizeM[1]);
212
+ try {
213
+ const record = await resolveOne(keyword, defaultOpts());
214
+ const ayHome = process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
215
+ let cols = null;
216
+ let rows = null;
217
+ try {
218
+ const [c, r] = (await readFile(path.join(ayHome, "ptysize", String(record.pid)), "utf-8")).trim().split(/\s+/).map(Number);
219
+ if (c > 0 && r > 0) {
220
+ cols = c;
221
+ rows = r;
222
+ }
223
+ } catch {}
224
+ return Response.json({
225
+ pid: record.pid,
226
+ cols,
227
+ rows
228
+ });
229
+ } catch (e) {
230
+ return new Response(e.message, { status: 404 });
231
+ }
232
+ }
222
233
  const tailM = /^\/api\/tail\/(.+)$/.exec(p);
223
234
  if (req.method === "GET" && tailM) {
224
235
  const keyword = decodeURIComponent(tailM[1]);
@@ -248,27 +259,43 @@ Options:
248
259
  }, 15e3);
249
260
  const ansiRe = /\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;
250
261
  const ctrlRe = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
251
- const poller = setInterval(async () => {
252
- if (closed) {
253
- clearInterval(poller);
254
- return;
255
- }
262
+ const fh = await open(logPath, "r").catch(() => null);
263
+ let reading = false;
264
+ const flush = async () => {
265
+ if (closed || reading || !fh) return;
266
+ reading = true;
256
267
  try {
257
- const full = await readFile(logPath);
258
- if (full.length <= offset) return;
259
- const chunk = full.slice(offset);
260
- offset = full.length;
261
- if (raw) send(new TextDecoder().decode(chunk));
262
- else {
263
- const text = new TextDecoder().decode(chunk).replace(ansiRe, "").replace(ctrlRe, "");
264
- if (text.trim()) send(text.trimStart());
268
+ const { size } = await fh.stat();
269
+ if (size < offset) offset = size;
270
+ if (size > offset) {
271
+ const len = size - offset;
272
+ const buf = Buffer.allocUnsafe(len);
273
+ const { bytesRead } = await fh.read(buf, 0, len, offset);
274
+ offset += bytesRead;
275
+ const chunk = buf.subarray(0, bytesRead);
276
+ if (raw) send(new TextDecoder().decode(chunk));
277
+ else {
278
+ const text = new TextDecoder().decode(chunk).replace(ansiRe, "").replace(ctrlRe, "");
279
+ if (text.trim()) send(text.trimStart());
280
+ }
265
281
  }
266
- } catch {}
267
- }, 300);
282
+ } catch {} finally {
283
+ reading = false;
284
+ }
285
+ };
286
+ let watcher = null;
287
+ try {
288
+ watcher = watch(logPath, () => void flush());
289
+ } catch {}
290
+ const poller = setInterval(() => void flush(), 60);
268
291
  req.signal.addEventListener("abort", () => {
269
292
  closed = true;
270
293
  clearInterval(heartbeat);
271
294
  clearInterval(poller);
295
+ try {
296
+ watcher?.close();
297
+ } catch {}
298
+ fh?.close().catch(() => {});
272
299
  try {
273
300
  ctrl.close();
274
301
  } catch {}
@@ -341,7 +368,6 @@ Options:
341
368
  }
342
369
  }
343
370
  if (req.method === "POST" && p === "/api/spawn") {
344
- if (!allowSpawn) return new Response("spawning disabled — start: ay serve --share --allow-spawn", { status: 403 });
345
371
  let body;
346
372
  try {
347
373
  body = await req.json();
@@ -352,7 +378,7 @@ Options:
352
378
  if (!SUPPORTED_CLIS.includes(cli)) return new Response(`unsupported cli: ${cli}`, { status: 400 });
353
379
  const cwd = typeof body.cwd === "string" && body.cwd ? body.cwd : process.cwd();
354
380
  const prompt = String(body.prompt ?? "");
355
- if (!await confirmSpawn(cli, cwd, prompt)) return new Response("denied by host", { status: 403 });
381
+ process.stderr.write(`→ console spawned: ay ${cli}${prompt ? ` -- "${prompt.slice(0, 60)}"` : ""} (cwd: ${cwd})\n`);
356
382
  try {
357
383
  const child = Bun.spawn([
358
384
  "ay",
@@ -422,4 +448,4 @@ Options:
422
448
 
423
449
  //#endregion
424
450
  export { cmdServe };
425
- //# sourceMappingURL=serve-CuAPBK4y.js.map
451
+ //# sourceMappingURL=serve-CKcbVPy6.js.map
@@ -1,6 +1,6 @@
1
1
  import "./logger-B9h0djqx.js";
2
2
  import "./globalPidIndex-yVd3mbsV.js";
3
3
  import "./remotes-C3xPRtfg.js";
4
- import { a as isSubcommand, c as readNotes, d as runSubcommand, f as snapshotStatus, i as isPidAlive, l as renderRawLog, m as writeToIpc, n as cmdHelp, o as listRecords, p as stopTipForCli, r as controlCodeFromName, s as matchKeyword, t as GRACEFUL_EXIT_COMMANDS, u as resolveOne } from "./subcommands-CcOYsLYD.js";
4
+ import { a as isSubcommand, c as readNotes, d as runSubcommand, f as snapshotStatus, i as isPidAlive, l as renderRawLog, m as writeToIpc, n as cmdHelp, o as listRecords, p as stopTipForCli, r as controlCodeFromName, s as matchKeyword, t as GRACEFUL_EXIT_COMMANDS, u as resolveOne } from "./subcommands-K242usI5.js";
5
5
 
6
6
  export { cmdHelp, isSubcommand, runSubcommand };
@@ -162,7 +162,7 @@ async function runSubcommand(argv) {
162
162
  case "restart": return await cmdRestart(rest);
163
163
  case "note": return await cmdNote(rest);
164
164
  case "serve": {
165
- const { cmdServe } = await import("./serve-CuAPBK4y.js");
165
+ const { cmdServe } = await import("./serve-CKcbVPy6.js");
166
166
  return cmdServe(rest);
167
167
  }
168
168
  case "remote": {
@@ -1447,4 +1447,4 @@ async function cmdStatus(rest) {
1447
1447
 
1448
1448
  //#endregion
1449
1449
  export { isSubcommand as a, readNotes as c, runSubcommand as d, snapshotStatus as f, isPidAlive as i, renderRawLog as l, writeToIpc as m, cmdHelp as n, listRecords as o, stopTipForCli as p, controlCodeFromName as r, matchKeyword as s, GRACEFUL_EXIT_COMMANDS as t, resolveOne as u };
1450
- //# sourceMappingURL=subcommands-CcOYsLYD.js.map
1450
+ //# sourceMappingURL=subcommands-K242usI5.js.map
@@ -1,4 +1,4 @@
1
- import { n as getRunningAgentCount } from "./runningLock-C22d9SRJ.js";
1
+ import { n as getRunningAgentCount } from "./runningLock-CJxsoGdb.js";
2
2
  import { existsSync } from "fs";
3
3
  import { mkdir, readFile, unlink, writeFile } from "fs/promises";
4
4
  import { homedir } from "os";
@@ -175,4 +175,4 @@ async function startTray() {
175
175
 
176
176
  //#endregion
177
177
  export { ensureTray, startTray };
178
- //# sourceMappingURL=tray-D4cJA4UH.js.map
178
+ //# sourceMappingURL=tray-CWQe9DMY.js.map
@@ -1,7 +1,7 @@
1
1
  import { n as logger, t as addTransport } from "./logger-B9h0djqx.js";
2
- import { r as getInstalledPackage } from "./versionChecker-xqnqyGKE.js";
3
- import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-C22d9SRJ.js";
4
- import { t as PidStore } from "./pidStore-DTzl6zeh.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-MNvA73o9.js";
3
+ import { n as agentYesHome, t as PidStore } from "./pidStore-DBjlqzo8.js";
4
+ import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CJxsoGdb.js";
5
5
  import { i as readGlobalPids } from "./globalPidIndex-yVd3mbsV.js";
6
6
  import { arch, platform } from "process";
7
7
  import { execSync } from "child_process";
@@ -1423,10 +1423,19 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
1423
1423
  notifyWebhook("EXIT", `${exitReason} exitCode=${exitCode ?? "?"}`, workingDir).catch(() => null);
1424
1424
  return pendingExitCode.resolve(exitCode);
1425
1425
  });
1426
+ const writeCurrentPtysize = (cols, rows) => {
1427
+ const dir = path.join(agentYesHome(), "ptysize");
1428
+ mkdir(dir, { recursive: true }).then(() => writeFile(path.join(dir, String(process.pid)), `${cols} ${rows}\n`)).catch(() => null);
1429
+ };
1430
+ {
1431
+ const { cols, rows } = getTerminalDimensions();
1432
+ writeCurrentPtysize(cols, rows);
1433
+ }
1426
1434
  process.stdout.on("resize", () => {
1427
1435
  const { cols, rows } = getTerminalDimensions();
1428
1436
  shell.resize(cols, rows);
1429
1437
  xtermProxy.resize(cols, rows);
1438
+ writeCurrentPtysize(cols, rows);
1430
1439
  });
1431
1440
  const isStillWorkingQ = () => {
1432
1441
  const rendered = xtermProxy.tail(24).replace(/\s+/g, " ");
@@ -1705,4 +1714,4 @@ function sleep(ms) {
1705
1714
 
1706
1715
  //#endregion
1707
1716
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1708
- //# sourceMappingURL=ts-DkjQJTcB.js.map
1717
+ //# sourceMappingURL=ts-BAc4Jcrw.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.96.0";
10
+ var version = "1.97.0";
11
11
 
12
12
  //#endregion
13
13
  //#region ts/versionChecker.ts
@@ -221,4 +221,4 @@ async function displayVersion() {
221
221
 
222
222
  //#endregion
223
223
  export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
224
- //# sourceMappingURL=versionChecker-xqnqyGKE.js.map
224
+ //# sourceMappingURL=versionChecker-MNvA73o9.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.96.0",
3
+ "version": "1.97.0",
4
4
  "description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
5
5
  "keywords": [
6
6
  "ai",
package/ts/index.ts CHANGED
@@ -5,6 +5,7 @@ import path from "path";
5
5
  import DIE from "phpdie";
6
6
  import sflow from "sflow";
7
7
  import { XtermProxy } from "./xterm-proxy.ts";
8
+ import { agentYesHome } from "./agentYesHome.ts";
8
9
  import {
9
10
  extractSessionId,
10
11
  getSessionForCwd,
@@ -643,11 +644,26 @@ export default async function agentYes({
643
644
  return pendingExitCode.resolve(exitCode);
644
645
  });
645
646
 
647
+ // Record the agent's current PTY size to ~/.agent-yes/ptysize/<pid> so `ay serve`
648
+ // / the web console can render the existing buffer at the agent's real width
649
+ // before adapting. Mirrors the Rust runtime (rs/src/pty_spawner.rs).
650
+ const writeCurrentPtysize = (cols: number, rows: number) => {
651
+ const dir = path.join(agentYesHome(), "ptysize");
652
+ void mkdir(dir, { recursive: true })
653
+ .then(() => writeFile(path.join(dir, String(process.pid)), `${cols} ${rows}\n`))
654
+ .catch(() => null);
655
+ };
656
+ {
657
+ const { cols, rows } = getTerminalDimensions();
658
+ writeCurrentPtysize(cols, rows);
659
+ }
660
+
646
661
  // when current tty resized, resize both pty and xterm proxy
647
662
  process.stdout.on("resize", () => {
648
663
  const { cols, rows } = getTerminalDimensions();
649
664
  shell.resize(cols, rows);
650
665
  xtermProxy.resize(cols, rows);
666
+ writeCurrentPtysize(cols, rows);
651
667
  });
652
668
 
653
669
  const isStillWorkingQ = () => {
package/ts/serve.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { mkdir, readFile, writeFile } from "fs/promises";
1
+ import { mkdir, open, readFile, writeFile } from "fs/promises";
2
+ import { watch } from "node:fs";
2
3
  import { createHash, randomBytes, timingSafeEqual } from "crypto";
3
4
  import { homedir } from "os";
4
5
  import path from "path";
@@ -125,7 +126,7 @@ export async function cmdServe(rest: string[]): Promise<number> {
125
126
  ` --host HOST Interface to bind (default: 127.0.0.1; use 0.0.0.0 to expose)\n` +
126
127
  ` --token TOKEN Auth token (auto-generated and saved if omitted)\n` +
127
128
  ` --share [URL] Share over WebRTC to agent-yes.com (bare flag mints a room+link)\n` +
128
- ` --allow-spawn Let the shared console launch new agents (asks y/N per request)\n` +
129
+ ` --allow-spawn Deprecated no-op — the console can always spawn agents\n` +
129
130
  ` --tls-cert FILE TLS certificate PEM\n` +
130
131
  ` --tls-key FILE TLS private key PEM\n\n` +
131
132
  `Subcommands:\n` +
@@ -164,7 +165,7 @@ export async function cmdServe(rest: string[]): Promise<number> {
164
165
  .option("allow-spawn", {
165
166
  type: "boolean",
166
167
  default: false,
167
- description: "Allow the shared console to spawn new agents (asks y/N per request on a TTY)",
168
+ description: "Deprecated no-op — the console can always spawn agents",
168
169
  })
169
170
  .help(false)
170
171
  .version(false)
@@ -191,26 +192,11 @@ export async function cmdServe(rest: string[]): Promise<number> {
191
192
  }
192
193
 
193
194
  const token = await loadOrCreateToken(tokenFlag);
194
- const allowSpawn = argv["allow-spawn"] === true;
195
-
196
- // Spawn confirmation: launch requests are gated by --allow-spawn AND, on a TTY,
197
- // an interactive y/N per request (a leaked launch link can't silently spawn).
198
- const spawnQueue: Array<(ok: boolean) => void> = [];
199
- let stdinWired = false;
200
- const confirmSpawn = (cli: string, cwd: string, prompt: string): Promise<boolean> => {
201
- if (!process.stdin.isTTY) return Promise.resolve(true); // flag is the consent when headless
202
- if (!stdinWired) {
203
- stdinWired = true;
204
- process.stdin.setEncoding("utf8");
205
- process.stdin.on("data", (d: string) => spawnQueue.shift()?.(/^y/i.test(d.trim())));
206
- process.stdin.resume();
207
- }
208
- process.stdout.write(
209
- `\n⚠ console requests spawn: ay ${cli}${prompt ? ` -- "${prompt.slice(0, 60)}"` : ""}\n` +
210
- ` cwd: ${cwd}\n allow? [y/N] `,
211
- );
212
- return new Promise((res) => spawnQueue.push(res));
213
- };
195
+ // Spawning is always allowed: a connected console already has full read-write
196
+ // control over every running agent (it writes straight to their stdin), so it
197
+ // can already make an agent do anything — gating /api/spawn behind a flag or a
198
+ // y/N prompt bought no real safety. We just log each spawn so the host sees it.
199
+ // (--allow-spawn is still accepted as a no-op for older invocations.)
214
200
 
215
201
  const serverOpts: any = {
216
202
  hostname: host,
@@ -275,6 +261,32 @@ export async function cmdServe(rest: string[]): Promise<number> {
275
261
  }
276
262
  }
277
263
 
264
+ // GET /api/size/:keyword — the agent's current PTY size, so the console can
265
+ // render the existing buffer at the agent's real width before adapting.
266
+ const sizeM = /^\/api\/size\/(.+)$/.exec(p);
267
+ if (req.method === "GET" && sizeM) {
268
+ const keyword = decodeURIComponent(sizeM[1]!);
269
+ try {
270
+ const record = await resolveOne(keyword, defaultOpts());
271
+ const ayHome = process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
272
+ let cols: number | null = null;
273
+ let rows: number | null = null;
274
+ try {
275
+ const txt = await readFile(path.join(ayHome, "ptysize", String(record.pid)), "utf-8");
276
+ const [c, r] = txt.trim().split(/\s+/).map(Number);
277
+ if (c > 0 && r > 0) {
278
+ cols = c;
279
+ rows = r;
280
+ }
281
+ } catch {
282
+ /* no ptysize sidecar (older agent or not yet written) */
283
+ }
284
+ return Response.json({ pid: record.pid, cols, rows });
285
+ } catch (e) {
286
+ return new Response((e as Error).message, { status: 404 });
287
+ }
288
+ }
289
+
278
290
  // GET /api/tail/:keyword — SSE streaming
279
291
  const tailM = /^\/api\/tail\/(.+)$/.exec(p);
280
292
  if (req.method === "GET" && tailM) {
@@ -319,34 +331,59 @@ export async function cmdServe(rest: string[]): Promise<number> {
319
331
  // eslint-disable-next-line no-control-regex
320
332
  const ctrlRe = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
321
333
 
322
- const poller = setInterval(async () => {
323
- if (closed) {
324
- clearInterval(poller);
325
- return;
326
- }
334
+ // Stream only the bytes appended since `offset` (incremental read,
335
+ // not a full re-read), driven by fs.watch for near-instant echo with
336
+ // a short fallback poll in case the watcher misses an event. The old
337
+ // 300 ms full-file poll was the dominant typing-echo latency.
338
+ const fh = await open(logPath, "r").catch(() => null);
339
+ let reading = false;
340
+ const flush = async () => {
341
+ if (closed || reading || !fh) return;
342
+ reading = true;
327
343
  try {
328
- const full = await readFile(logPath);
329
- if (full.length <= offset) return;
330
- const chunk = full.slice(offset);
331
- offset = full.length;
332
- if (raw) {
333
- send(new TextDecoder().decode(chunk));
334
- } else {
335
- const text = new TextDecoder()
336
- .decode(chunk)
337
- .replace(ansiRe, "")
338
- .replace(ctrlRe, "");
339
- if (text.trim()) send(text.trimStart());
344
+ const { size } = await fh.stat();
345
+ if (size < offset) offset = size; // truncated/rotated
346
+ if (size > offset) {
347
+ const len = size - offset;
348
+ const buf = Buffer.allocUnsafe(len);
349
+ const { bytesRead } = await fh.read(buf, 0, len, offset);
350
+ offset += bytesRead;
351
+ const chunk = buf.subarray(0, bytesRead);
352
+ if (raw) {
353
+ send(new TextDecoder().decode(chunk));
354
+ } else {
355
+ const text = new TextDecoder()
356
+ .decode(chunk)
357
+ .replace(ansiRe, "")
358
+ .replace(ctrlRe, "");
359
+ if (text.trim()) send(text.trimStart());
360
+ }
340
361
  }
341
362
  } catch {
342
363
  /* log gone */
364
+ } finally {
365
+ reading = false;
343
366
  }
344
- }, 300);
367
+ };
368
+
369
+ let watcher: ReturnType<typeof watch> | null = null;
370
+ try {
371
+ watcher = watch(logPath, () => void flush());
372
+ } catch {
373
+ /* fs.watch unsupported — the fallback poll below still works */
374
+ }
375
+ const poller = setInterval(() => void flush(), 60);
345
376
 
346
377
  req.signal.addEventListener("abort", () => {
347
378
  closed = true;
348
379
  clearInterval(heartbeat);
349
380
  clearInterval(poller);
381
+ try {
382
+ watcher?.close();
383
+ } catch {
384
+ /* already closed */
385
+ }
386
+ void fh?.close().catch(() => {});
350
387
  try {
351
388
  ctrl.close();
352
389
  } catch {
@@ -433,12 +470,8 @@ export async function cmdServe(rest: string[]): Promise<number> {
433
470
  }
434
471
  }
435
472
 
436
- // POST /api/spawn body {cli, cwd, prompt} — launch a new agent (gated)
473
+ // POST /api/spawn body {cli, cwd, prompt} — launch a new agent
437
474
  if (req.method === "POST" && p === "/api/spawn") {
438
- if (!allowSpawn)
439
- return new Response("spawning disabled — start: ay serve --share --allow-spawn", {
440
- status: 403,
441
- });
442
475
  let body: { cli?: string; cwd?: string; prompt?: string };
443
476
  try {
444
477
  body = await req.json();
@@ -450,8 +483,9 @@ export async function cmdServe(rest: string[]): Promise<number> {
450
483
  return new Response(`unsupported cli: ${cli}`, { status: 400 });
451
484
  const cwd = typeof body.cwd === "string" && body.cwd ? body.cwd : process.cwd();
452
485
  const prompt = String(body.prompt ?? "");
453
- if (!(await confirmSpawn(cli, cwd, prompt)))
454
- return new Response("denied by host", { status: 403 });
486
+ process.stderr.write(
487
+ `→ console spawned: ay ${cli}${prompt ? ` -- "${prompt.slice(0, 60)}"` : ""} (cwd: ${cwd})\n`,
488
+ );
455
489
  try {
456
490
  const child = Bun.spawn(["ay", cli, ...(prompt ? ["--", prompt] : [])], {
457
491
  cwd,
@@ -1,8 +0,0 @@
1
- import "./ts-DkjQJTcB.js";
2
- import "./logger-B9h0djqx.js";
3
- import "./versionChecker-xqnqyGKE.js";
4
- import "./pidStore-DTzl6zeh.js";
5
- import "./globalPidIndex-yVd3mbsV.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-B2FAlgXF.js";
7
-
8
- export { SUPPORTED_CLIS };
@@ -1,5 +0,0 @@
1
- import "./logger-B9h0djqx.js";
2
- import { t as PidStore } from "./pidStore-DTzl6zeh.js";
3
- import "./globalPidIndex-yVd3mbsV.js";
4
-
5
- export { PidStore };