agent-yes 1.62.1 → 1.63.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.
@@ -2,15 +2,16 @@ import { t as logger } from "./logger-CX77vJDA.js";
2
2
  import { arch, platform } from "process";
3
3
  import { execSync } from "child_process";
4
4
  import { execaCommandSync, parseCommandString } from "execa";
5
- import { fromWritable } from "from-node-stream";
6
- import { appendFile, mkdir, readFile, readdir, rename, writeFile } from "fs/promises";
5
+ import { fromReadable, fromWritable } from "from-node-stream";
6
+ import { appendFile, mkdir, readFile, readdir, rename, unlink, writeFile } from "fs/promises";
7
7
  import path, { dirname, join } from "path";
8
8
  import DIE from "phpdie";
9
9
  import sflow from "sflow";
10
10
  import { TerminalRenderStream } from "terminal-render";
11
11
  import { homedir } from "os";
12
12
  import winston from "winston";
13
- import { closeSync, existsSync, fsyncSync, mkdirSync, openSync } from "fs";
13
+ import { closeSync, constants, createReadStream, existsSync, fsyncSync, mkdirSync, openSync } from "fs";
14
+ import { createServer } from "net";
14
15
  import { lock } from "proper-lockfile";
15
16
  import { execSync as execSync$1 } from "node:child_process";
16
17
  import { fileURLToPath } from "url";
@@ -422,6 +423,163 @@ function shouldUseLock(_cwd) {
422
423
  return true;
423
424
  }
424
425
 
426
+ //#endregion
427
+ //#region ts/beta/fifo.ts
428
+ /**
429
+ * Creates an IPC stream (FIFO on Linux, Named Pipes on Windows) for additional stdin input
430
+ * @param cli - The CLI name for logging purposes
431
+ * @param customPath - Optional custom path for the IPC file; if provided, uses this instead of generating a path
432
+ * @returns An object with stream and cleanup function, or null if failed
433
+ */
434
+ async function createFifoStream(cli, customPath) {
435
+ if (process.platform === "win32") return await createWindowsNamedPipe(cli, customPath);
436
+ else if (process.platform === "linux") return await createLinuxFifo(cli, customPath);
437
+ else {
438
+ logger.warn(`[${cli}-yes] IPC not supported on platform: ${process.platform}`);
439
+ return null;
440
+ }
441
+ }
442
+ /**
443
+ * Creates a Windows named pipe for IPC
444
+ */
445
+ async function createWindowsNamedPipe(cli, customPath) {
446
+ try {
447
+ let pipePath;
448
+ if (customPath) pipePath = customPath;
449
+ else pipePath = `\\\\.\\pipe\\agent-yes-${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 17)}${Math.random().toString(36).substring(2, 5)}`;
450
+ logger.info(`[${cli}-yes] Creating Windows named pipe at ${pipePath}`);
451
+ const server = createServer();
452
+ let connection = null;
453
+ let isClosing = false;
454
+ const stream = new ReadableStream({
455
+ start(controller) {
456
+ server.on("connection", (socket) => {
457
+ connection = socket;
458
+ logger.info(`[${cli}-yes] Client connected to named pipe`);
459
+ socket.on("data", (chunk) => {
460
+ const data = chunk.toString();
461
+ logger.debug(`[${cli}-yes] Received data via named pipe: ${data}`);
462
+ controller.enqueue(data);
463
+ });
464
+ socket.on("end", () => {
465
+ logger.debug(`[${cli}-yes] Client disconnected from named pipe`);
466
+ connection = null;
467
+ });
468
+ socket.on("error", (error) => {
469
+ logger.warn(`[${cli}-yes] Named pipe socket error:`, error);
470
+ if (!isClosing) controller.error(error);
471
+ });
472
+ });
473
+ server.on("error", (error) => {
474
+ logger.warn(`[${cli}-yes] Named pipe server error:`, error);
475
+ if (!isClosing) controller.error(error);
476
+ });
477
+ server.listen(pipePath, () => {
478
+ logger.info(`[${cli}-yes] Named pipe server listening at ${pipePath}`);
479
+ });
480
+ },
481
+ cancel() {
482
+ isClosing = true;
483
+ if (connection) connection.end();
484
+ server.close();
485
+ }
486
+ });
487
+ const cleanup = async () => {
488
+ isClosing = true;
489
+ if (connection) connection.end();
490
+ server.close();
491
+ logger.info(`[${cli}-yes] Cleaned up Windows named pipe at ${pipePath}`);
492
+ };
493
+ process.on("exit", () => cleanup().catch(() => null));
494
+ process.on("SIGINT", () => cleanup().catch(() => null));
495
+ process.on("SIGTERM", () => cleanup().catch(() => null));
496
+ return {
497
+ stream,
498
+ [Symbol.asyncDispose]: cleanup
499
+ };
500
+ } catch (error) {
501
+ logger.warn(`[${cli}-yes] Failed to create Windows named pipe:`, error);
502
+ return null;
503
+ }
504
+ }
505
+ /**
506
+ * Creates a Linux FIFO for IPC (original implementation)
507
+ */
508
+ async function createLinuxFifo(cli, customPath) {
509
+ let fifoPath = null;
510
+ let fifoStream = null;
511
+ logger.debug(`[${cli}-yes] Creating Linux FIFO with customPath: ${customPath}`);
512
+ try {
513
+ if (customPath) fifoPath = customPath;
514
+ else fifoPath = `/tmp/agent-yes-${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 17)}${Math.random().toString(36).substring(2, 5)}.stdin`;
515
+ try {
516
+ mkdirSync(dirname(fifoPath), { recursive: true });
517
+ } catch (dirError) {
518
+ logger.warn(`[${cli}-yes] Failed to create FIFO directory: ${dirError}`);
519
+ return null;
520
+ }
521
+ const escapedPath = fifoPath.replace(/'/g, "'\"'\"'");
522
+ const mkfifoResult = execaCommandSync(`mkfifo '${escapedPath}'`, { reject: false });
523
+ if (mkfifoResult.exitCode !== 0) {
524
+ logger.warn(`[${cli}-yes] mkfifo command failed with exit code ${mkfifoResult.exitCode}`);
525
+ logger.warn(`[${cli}-yes] Command: mkfifo '${escapedPath}'`);
526
+ if (mkfifoResult.stderr) logger.warn(`[${cli}-yes] mkfifo stderr: ${mkfifoResult.stderr}`);
527
+ if (mkfifoResult.stdout) logger.warn(`[${cli}-yes] mkfifo stdout: ${mkfifoResult.stdout}`);
528
+ return null;
529
+ }
530
+ logger.info(`[${cli}-yes] Created FIFO at ${fifoPath}`);
531
+ try {
532
+ fifoStream = createReadStream("", {
533
+ fd: openSync(fifoPath, constants.O_RDONLY | constants.O_NONBLOCK),
534
+ autoClose: true
535
+ });
536
+ const dummyWriteFd = openSync(fifoPath, constants.O_WRONLY | constants.O_NONBLOCK);
537
+ logger.info(`[${cli}-yes] FIFO opened for reading`);
538
+ const cleanupFifo = async () => {
539
+ try {
540
+ closeSync(dummyWriteFd);
541
+ } catch {}
542
+ if (fifoStream) try {
543
+ fifoStream.close();
544
+ logger.debug(`[${cli}-yes] Closed FIFO stream`);
545
+ } catch (error) {
546
+ logger.debug(`[${cli}-yes] Error closing FIFO stream:`, { error });
547
+ }
548
+ if (fifoPath) try {
549
+ await unlink(fifoPath).catch(() => null);
550
+ logger.info(`[${cli}-yes] Cleaned up FIFO at ${fifoPath}`);
551
+ } catch {}
552
+ };
553
+ process.on("exit", () => {
554
+ if (fifoPath) unlink(fifoPath).catch(() => null);
555
+ });
556
+ process.on("SIGINT", async () => {
557
+ await cleanupFifo();
558
+ });
559
+ process.on("SIGTERM", async () => {
560
+ await cleanupFifo();
561
+ });
562
+ return {
563
+ stream: sflow(fromReadable(fifoStream)).map((buffer) => buffer.toString()),
564
+ [Symbol.asyncDispose]: cleanupFifo
565
+ };
566
+ } catch (error) {
567
+ logger.warn(`[${cli}-yes] Failed to open FIFO at ${fifoPath}:`, { error });
568
+ if (error instanceof Error) {
569
+ logger.warn(`[${cli}-yes] Error details: ${error.message}`);
570
+ if (error.stack) logger.debug(`[${cli}-yes] Stack trace: ${error.stack}`);
571
+ }
572
+ if (fifoPath) unlink(fifoPath).catch(() => null);
573
+ return null;
574
+ }
575
+ } catch (error) {
576
+ logger.warn(`[${cli}-yes] Failed to create FIFO:`, { error });
577
+ if (error instanceof Error) logger.warn(`[${cli}-yes] Error details: ${error.message}`);
578
+ if (fifoPath) unlink(fifoPath).catch(() => null);
579
+ return null;
580
+ }
581
+ }
582
+
425
583
  //#endregion
426
584
  //#region ts/JsonlStore.ts
427
585
  /**
@@ -798,11 +956,11 @@ async function sendEnter(context, waitms = 1e3) {
798
956
  context.shell.write("\r");
799
957
  logger.debug(`enterSent| idleWaiter.wait(${String(waitms)}) took ${String(et - st)}ms`);
800
958
  await Promise.race([context.nextStdout.wait(), new Promise((resolve) => setTimeout(() => {
801
- if (!context.nextStdout.ready) context.shell.write("\r");
959
+ if (!context.nextStdout.isReady) context.shell.write("\r");
802
960
  resolve();
803
961
  }, 1e3))]);
804
962
  await Promise.race([context.nextStdout.wait(), new Promise((resolve) => setTimeout(() => {
805
- if (!context.nextStdout.ready) context.shell.write("\r");
963
+ if (!context.nextStdout.isReady) context.shell.write("\r");
806
964
  resolve();
807
965
  }, 3e3))]);
808
966
  }
@@ -819,7 +977,10 @@ async function sendMessage(context, message, { waitForReady = true } = {}) {
819
977
  context.shell.write(message);
820
978
  context.idleWaiter.ping();
821
979
  logger.debug(`waiting next stdout|${message}`);
822
- await context.nextStdout.wait();
980
+ await Promise.race([context.nextStdout.wait(), new Promise((resolve) => setTimeout(() => {
981
+ logger.warn(`nextStdout.wait() timed out after 30s for message: ${message}`);
982
+ resolve();
983
+ }, 3e4))]);
823
984
  logger.debug(`sending enter`);
824
985
  await sendEnter(context, 1e3);
825
986
  logger.debug(`sent enter`);
@@ -904,7 +1065,7 @@ function tryCatch(catchFn, fn) {
904
1065
  //#endregion
905
1066
  //#region package.json
906
1067
  var name = "agent-yes";
907
- var version = "1.62.1";
1068
+ var version = "1.63.0";
908
1069
 
909
1070
  //#endregion
910
1071
  //#region ts/pty-fix.ts
@@ -1360,7 +1521,7 @@ const CLIS_CONFIG = config.clis;
1360
1521
  * });
1361
1522
  * ```
1362
1523
  */
1363
- async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, exitOnIdle, logFile, removeControlCharactersFromStdout = false, verbose = false, queue = false, install = false, resume = false, useSkills = false, useStdinAppend: _useStdinAppend = false, autoYes = true }) {
1524
+ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, exitOnIdle, logFile, removeControlCharactersFromStdout = false, verbose = false, queue = false, install = false, resume = false, useSkills = false, useStdinAppend = false, autoYes = true }) {
1364
1525
  if (!cli) throw new Error(`cli is required`);
1365
1526
  const conf = CLIS_CONFIG[cli] || DIE(`Unsupported cli tool: ${cli}, current process.argv: ${process.argv.join(" ")}`);
1366
1527
  const workingDir = cwd ?? process.cwd();
@@ -1777,7 +1938,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
1777
1938
  cancel(_reason) {
1778
1939
  process.stdin.pause();
1779
1940
  }
1780
- });
1941
+ }, { highWaterMark: 16 });
1781
1942
  let aborted = false;
1782
1943
  await sflow(stdinStream).map((buffer) => {
1783
1944
  const str = buffer.toString();
@@ -1839,7 +2000,14 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
1839
2000
  }
1840
2001
  return out;
1841
2002
  };
1842
- })()).onStart(async function promptOnStart() {
2003
+ })()).by(async (s) => {
2004
+ if (!useStdinAppend) return s;
2005
+ const ipcResult = await createFifoStream(cli, pidStore.getFifoPath(shell.pid));
2006
+ if (!ipcResult) return s;
2007
+ pendingExitCode.promise.finally(async () => await ipcResult[Symbol.asyncDispose]());
2008
+ process.stderr.write(`\n Append prompts: ${cli}-yes --append-prompt '...'\n\n`);
2009
+ return s.merge(ipcResult.stream);
2010
+ }).confluenceByConcat().onStart(async function promptOnStart() {
1843
2011
  logger.debug("Sending prompt message: " + JSON.stringify(prompt));
1844
2012
  if (prompt) await sendMessage(ctx.messageContext, prompt);
1845
2013
  }).by({
@@ -1950,4 +2118,4 @@ const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
1950
2118
 
1951
2119
  //#endregion
1952
2120
  export { AgentContext as a, PidStore as c, config as i, removeControlCharacters as l, CLIS_CONFIG as n, name as o, agentYes as r, version as s, SUPPORTED_CLIS as t };
1953
- //# sourceMappingURL=SUPPORTED_CLIS-BWhzaf_k.js.map
2121
+ //# sourceMappingURL=SUPPORTED_CLIS-BFlBIzu3.js.map
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bun
2
- import { c as PidStore, o as name, s as version, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BWhzaf_k.js";
2
+ import { c as PidStore, o as name, s as version, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BFlBIzu3.js";
3
3
  import { t as logger } from "./logger-CX77vJDA.js";
4
4
  import { argv } from "process";
5
5
  import { spawn } from "child_process";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as AgentContext, i as config, l as removeControlCharacters, n as CLIS_CONFIG, r as agentYes } from "./SUPPORTED_CLIS-BWhzaf_k.js";
1
+ import { a as AgentContext, i as config, l as removeControlCharacters, n as CLIS_CONFIG, r as agentYes } from "./SUPPORTED_CLIS-BFlBIzu3.js";
2
2
  import "./logger-CX77vJDA.js";
3
3
 
4
4
  export { AgentContext, CLIS_CONFIG, config, agentYes as default, removeControlCharacters };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.62.1",
3
+ "version": "1.63.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
@@ -14,7 +14,6 @@ import pty, { ptyPackage } from "./pty.ts";
14
14
  import { removeControlCharacters } from "./removeControlCharacters.ts";
15
15
  import { acquireLock, releaseLock, shouldUseLock } from "./runningLock.ts";
16
16
  import { logger } from "./logger.ts";
17
- // oxlint-disable-next-line no-unused-vars -- kept for FIFO re-enable (see TODO at index.ts:833)
18
17
  import { createFifoStream } from "./beta/fifo.ts";
19
18
  import { PidStore } from "./pidStore.ts";
20
19
  import { SUPPORTED_CLIS } from "./SUPPORTED_CLIS.ts";
@@ -118,7 +117,7 @@ export default async function agentYes({
118
117
  install = false,
119
118
  resume = false,
120
119
  useSkills = false,
121
- useStdinAppend: _useStdinAppend = false,
120
+ useStdinAppend = false,
122
121
  autoYes = true,
123
122
  }: {
124
123
  cli: SUPPORTED_CLIS;
@@ -687,52 +686,55 @@ export default async function agentYes({
687
686
  // CRITICAL FIX: fromReadable() from 'from-node-stream' doesn't work properly with stdin
688
687
  // because it doesn't handle Node.js stream modes correctly. We create a custom ReadableStream
689
688
  // that properly manages stdin's flowing mode and event listeners.
690
- const stdinStream = new ReadableStream<Buffer>({
691
- start(controller) {
692
- // Set up stdin in flowing mode so 'data' events fire
693
- process.stdin.resume();
694
-
695
- let closed = false;
696
-
697
- // Handle data events
698
- const dataHandler = (chunk: Buffer) => {
699
- try {
700
- controller.enqueue(chunk);
701
- } catch {
702
- // Ignore enqueue errors (stream may be closed)
703
- }
704
- };
689
+ const stdinStream = new ReadableStream<Buffer>(
690
+ {
691
+ start(controller) {
692
+ // Set up stdin in flowing mode so 'data' events fire
693
+ process.stdin.resume();
694
+
695
+ let closed = false;
696
+
697
+ // Handle data events
698
+ const dataHandler = (chunk: Buffer) => {
699
+ try {
700
+ controller.enqueue(chunk);
701
+ } catch {
702
+ // Ignore enqueue errors (stream may be closed)
703
+ }
704
+ };
705
705
 
706
- // Handle end/close - both events can fire, so track state
707
- const endHandler = () => {
708
- if (closed) return;
709
- closed = true;
710
- try {
711
- controller.close();
712
- } catch {
713
- // Ignore close errors (already closed)
714
- }
715
- };
706
+ // Handle end/close - both events can fire, so track state
707
+ const endHandler = () => {
708
+ if (closed) return;
709
+ closed = true;
710
+ try {
711
+ controller.close();
712
+ } catch {
713
+ // Ignore close errors (already closed)
714
+ }
715
+ };
716
716
 
717
- const errorHandler = (err: Error) => {
718
- if (closed) return;
719
- closed = true;
720
- try {
721
- controller.error(err);
722
- } catch {
723
- // Ignore error after close
724
- }
725
- };
717
+ const errorHandler = (err: Error) => {
718
+ if (closed) return;
719
+ closed = true;
720
+ try {
721
+ controller.error(err);
722
+ } catch {
723
+ // Ignore error after close
724
+ }
725
+ };
726
726
 
727
- process.stdin.on("data", dataHandler);
728
- process.stdin.on("end", endHandler);
729
- process.stdin.on("close", endHandler);
730
- process.stdin.on("error", errorHandler);
731
- },
732
- cancel(_reason) {
733
- process.stdin.pause();
727
+ process.stdin.on("data", dataHandler);
728
+ process.stdin.on("end", endHandler);
729
+ process.stdin.on("close", endHandler);
730
+ process.stdin.on("error", errorHandler);
731
+ },
732
+ cancel(_reason) {
733
+ process.stdin.pause();
734
+ },
734
735
  },
735
- });
736
+ { highWaterMark: 16 },
737
+ );
736
738
 
737
739
  let aborted = false;
738
740
  await sflow(stdinStream)
@@ -830,18 +832,17 @@ export default async function agentYes({
830
832
  })(),
831
833
  )
832
834
 
833
- // TODO(sno): Read from IPC stream if available (FIFO on Linux, Named Pipes on Windows)
834
- // .by(async (s) => {
835
- // if (!useStdinAppend) return s;
836
- // const fifoPath = pidStore.getFifoPath(shell.pid);
837
- // if (!fifoPath) return s; // Skip if no valid path
838
- // const ipcResult = await createFifoStream(cli, fifoPath);
839
- // if (!ipcResult) return s;
840
- // pendingExitCode.promise.finally(async() => await ipcResult[Symbol.asyncDispose]());
841
- // process.stderr.write(`\n Append prompts: ${cli}-yes --append-prompt '...'\n\n`);
842
- // return s.merge(ipcResult.stream);
843
- // })
844
- // .confluenceByConcat() // necessary when .by() above is async
835
+ // Read from IPC stream if available (FIFO on Linux, Named Pipes on Windows)
836
+ .by(async (s) => {
837
+ if (!useStdinAppend) return s;
838
+ const fifoPath = pidStore.getFifoPath(shell.pid);
839
+ const ipcResult = await createFifoStream(cli, fifoPath);
840
+ if (!ipcResult) return s;
841
+ pendingExitCode.promise.finally(async () => await ipcResult[Symbol.asyncDispose]());
842
+ process.stderr.write(`\n Append prompts: ${cli}-yes --append-prompt '...'\n\n`);
843
+ return s.merge(ipcResult.stream);
844
+ })
845
+ .confluenceByConcat() // necessary because .by() above is async
845
846
 
846
847
  // .map((e) => e.replaceAll('\x1a', '')) // remove ctrl+z from user's input, to prevent bug (but this seems bug)
847
848
  // .forEach(e => appendFile('.cache/io.log', "input |" + JSON.stringify(e) + '\n')) // for debugging