@duyquangnvx/create-agent-cli 0.11.1 → 0.13.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/bin.js +110 -26
  3. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duy
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## v0.13.0 (2026-10-23)
8
+
9
+ ### Changed
10
+
11
+ - **Breaking:** `ctx.spawn`'s `progress: true` is now `onLine: ctx.progress`. `onLine` takes any callback, which receives each line of the child's stdout and stderr as it arrives, so a Command can parse a tool's progress output (`ffmpeg -progress pipe:1`) into its own `ctx.progress` lines; an `onLine` that throws kills the child and fails the run with its error. A lone `\r` now ends a line too, as in ffmpeg's `-stats` and curl's progress, instead of joining every status update into one line.
12
+
13
+ ## v0.12.0 (2026-10-23)
14
+
15
+ ### Added
16
+
17
+ - `ctx.spawn`'s `stdin` option writes a child's stdin: a string or bytes, or an `AsyncIterable<Uint8Array>` written as the child reads it, so a large input streams without being held in memory. Writing stops on `ctx.signal`; a stream that throws kills the child and fails the run with its error; a child that exits before reading it all is judged by its exit code. A fake `spawn` given to `runCli` receives it as `stdin`.
18
+
7
19
  ## v0.11.1 (2026-10-22)
8
20
 
9
21
  ### Changed
package/dist/bin.js CHANGED
@@ -6607,6 +6607,10 @@ const CMD_SHIM = /\.cmd$/i;
6607
6607
  const CMD_META = /([()\][%!^"`<>&|;, *?])/g;
6608
6608
  const PATH_SEPARATOR = /[\\/]/;
6609
6609
  const TRAILING_CR = /\r$/;
6610
+ const LINE_END = /\r\n|\r(?!$)|\n/;
6611
+ function isWhole(input) {
6612
+ return typeof input === "string" || input instanceof Uint8Array;
6613
+ }
6610
6614
  function escapedForCmd(text) {
6611
6615
  return text.replace(CMD_META, "^$1");
6612
6616
  }
@@ -6657,25 +6661,61 @@ function stop(child) {
6657
6661
  /** Calls `onLine` with each complete line pushed, and with the unterminated rest at the end. */
6658
6662
  function lineSplitter(onLine) {
6659
6663
  let rest = "";
6660
- const emit = (line) => onLine?.(line.replace(TRAILING_CR, ""));
6661
6664
  return {
6662
6665
  push(text) {
6663
6666
  if (onLine === void 0) return;
6664
- const lines = (rest + text).split("\n");
6667
+ const lines = (rest + text).split(LINE_END);
6665
6668
  rest = lines.pop() ?? "";
6666
- for (const line of lines) emit(line);
6669
+ for (const line of lines) onLine(line);
6667
6670
  },
6668
6671
  end() {
6669
- if (rest !== "") emit(rest);
6672
+ if (rest !== "") onLine?.(rest.replace(TRAILING_CR, ""));
6670
6673
  rest = "";
6671
6674
  }
6672
6675
  };
6673
6676
  }
6677
+ /** `onLine`, passing what it throws to `fail` and called no more once `failed()`. */
6678
+ function guarded(onLine, failed, fail) {
6679
+ if (onLine === void 0) return void 0;
6680
+ return (line) => {
6681
+ if (failed()) return;
6682
+ try {
6683
+ onLine(line);
6684
+ } catch (error) {
6685
+ fail(error);
6686
+ }
6687
+ };
6688
+ }
6689
+ /** Settles once the child has taken what was written, or its stdin has closed. */
6690
+ function drained(stdin) {
6691
+ return new Promise((resolve) => {
6692
+ const done = () => {
6693
+ stdin.off("drain", done);
6694
+ stdin.off("close", done);
6695
+ resolve();
6696
+ };
6697
+ stdin.on("drain", done);
6698
+ stdin.on("close", done);
6699
+ });
6700
+ }
6701
+ /**
6702
+ * Writes `input` to the child's stdin and ends it, stopping once the child is gone or the run
6703
+ * aborts.
6704
+ */
6705
+ async function feed(stdin, input, signal) {
6706
+ stdin.on("error", () => void 0);
6707
+ const chunks = isWhole(input) ? [input] : input;
6708
+ for await (const chunk of chunks) {
6709
+ if (signal.aborted || stdin.destroyed) return;
6710
+ if (!stdin.write(chunk)) await drained(stdin);
6711
+ }
6712
+ stdin.end();
6713
+ }
6674
6714
  /**
6675
6715
  * Runs a child process to its end; it is killed on abort, and the promise settles only once it has
6676
6716
  * exited. A `detached` child leaves the terminal's process group on POSIX, and so its Ctrl-C.
6677
6717
  */
6678
- async function spawnChild(command, args, { cwd, env, signal, onLine, detached = false }) {
6718
+ async function spawnChild(command, args, { cwd, env, signal, onLine, stdin, detached = false }) {
6679
6719
  signal.throwIfAborted();
6680
6720
  const launch = process.platform === "win32" ? await windowsLaunch(command, args, env) : {
6681
6721
  command,
@@ -6690,29 +6730,37 @@ async function spawnChild(command, args, { cwd, env, signal, onLine, detached =
6690
6730
  shell: launch.shell,
6691
6731
  windowsHide: true,
6692
6732
  stdio: [
6693
- "ignore",
6733
+ stdin === void 0 ? "ignore" : "pipe",
6694
6734
  "pipe",
6695
6735
  "pipe"
6696
6736
  ],
6697
6737
  detached: detached && process.platform !== "win32"
6698
6738
  });
6739
+ let stopped = Promise.resolve();
6740
+ const kill = () => {
6741
+ stopped = stop(child);
6742
+ };
6743
+ signal.addEventListener("abort", kill, { once: true });
6744
+ let callerFailure;
6745
+ const fail = (error) => {
6746
+ if (callerFailure !== void 0) return;
6747
+ callerFailure = { error };
6748
+ kill();
6749
+ };
6699
6750
  let stdout = "";
6700
6751
  let stderr = "";
6701
- const stdoutLines = lineSplitter(onLine);
6702
- const stderrLines = lineSplitter(onLine);
6703
- child.stdout.setEncoding("utf8").on("data", (chunk) => {
6752
+ const lines = guarded(onLine, () => callerFailure !== void 0, fail);
6753
+ const stdoutLines = lineSplitter(lines);
6754
+ const stderrLines = lineSplitter(lines);
6755
+ child.stdout?.setEncoding("utf8").on("data", (chunk) => {
6704
6756
  stdout += chunk;
6705
6757
  stdoutLines.push(chunk);
6706
6758
  });
6707
- child.stderr.setEncoding("utf8").on("data", (chunk) => {
6759
+ child.stderr?.setEncoding("utf8").on("data", (chunk) => {
6708
6760
  stderr += chunk;
6709
6761
  stderrLines.push(chunk);
6710
6762
  });
6711
- let stopped = Promise.resolve();
6712
- const kill = () => {
6713
- stopped = stop(child);
6714
- };
6715
- signal.addEventListener("abort", kill, { once: true });
6763
+ if (stdin !== void 0 && child.stdin !== null) feed(child.stdin, stdin, signal).catch(fail);
6716
6764
  child.on("error", (error) => {
6717
6765
  signal.removeEventListener("abort", kill);
6718
6766
  reject(error);
@@ -6725,6 +6773,11 @@ async function spawnChild(command, args, { cwd, env, signal, onLine, detached =
6725
6773
  }
6726
6774
  stdoutLines.end();
6727
6775
  stderrLines.end();
6776
+ if (callerFailure !== void 0) {
6777
+ const { error } = callerFailure;
6778
+ stopped.then(() => reject(error));
6779
+ return;
6780
+ }
6728
6781
  resolve({
6729
6782
  stdout,
6730
6783
  stderr,
@@ -6749,23 +6802,55 @@ function missingError(command, cause) {
6749
6802
  hint: `install ${command}, then run the same command again`
6750
6803
  }, { cause });
6751
6804
  }
6805
+ function startError(command, error, fileError) {
6806
+ if (isMissingFile(error)) return missingError(command, error);
6807
+ return fileError(error, {
6808
+ path: command,
6809
+ access: "read"
6810
+ });
6811
+ }
6812
+ async function* reported(stream, onError) {
6813
+ try {
6814
+ yield* stream;
6815
+ } catch (error) {
6816
+ onError(error);
6817
+ throw error;
6818
+ }
6819
+ }
6820
+ /** `input`, a stream passing each error it throws to `onError` before the reader sees it. */
6821
+ function reporting(input, onError) {
6822
+ return input === void 0 || isWhole(input) ? input : reported(input, onError);
6823
+ }
6824
+ /** `onLine`, passing each error it throws to `onError` before its caller sees it. */
6825
+ function reportingLines(onLine, onError) {
6826
+ if (onLine === void 0) return void 0;
6827
+ return (line) => {
6828
+ try {
6829
+ onLine(line);
6830
+ } catch (error) {
6831
+ onError(error);
6832
+ throw error;
6833
+ }
6834
+ };
6835
+ }
6752
6836
  /** `ctx.spawn`: `spawn` bound to the run, its start failures classified by errno. */
6753
6837
  function contextSpawn(run, defaults, fileError) {
6754
6838
  return async (command, args = [], options = {}) => {
6839
+ let callerFailure;
6840
+ const caller = (error) => {
6841
+ callerFailure = { error };
6842
+ };
6755
6843
  let result;
6756
6844
  try {
6757
6845
  result = await run(command, args, {
6758
6846
  cwd: options.cwd ?? defaults.cwd,
6759
6847
  env: options.env ?? defaults.env,
6760
6848
  signal: defaults.signal,
6761
- onLine: options.progress === true ? defaults.progress : void 0
6849
+ onLine: reportingLines(options.onLine, caller),
6850
+ stdin: reporting(options.stdin, caller)
6762
6851
  });
6763
6852
  } catch (error) {
6764
- if (isMissingFile(error)) throw missingError(command, error);
6765
- throw fileError(error, {
6766
- path: command,
6767
- access: "read"
6768
- });
6853
+ throw callerFailure?.error === error ? error : startError(command, error, fileError);
6769
6854
  }
6770
6855
  if (options.check === true && result.exitCode !== 0) throw failedError(command, result);
6771
6856
  return result;
@@ -9340,8 +9425,7 @@ function portsBaseOf(scope, signal) {
9340
9425
  spawn: contextSpawn(options.spawn ?? realSpawn, {
9341
9426
  cwd,
9342
9427
  env,
9343
- signal,
9344
- progress
9428
+ signal
9345
9429
  }, fileError)
9346
9430
  };
9347
9431
  }
@@ -9675,14 +9759,14 @@ async function runProcess(cli) {
9675
9759
  }
9676
9760
  //#endregion
9677
9761
  //#region package.json
9678
- var version$1 = "0.11.1";
9762
+ var version$1 = "0.13.0";
9679
9763
  var description = "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.";
9680
9764
  //#endregion
9681
9765
  //#region src/build.ts
9682
9766
  const build = {
9683
- "sha": "92df9c6",
9767
+ "sha": "1c3a29e",
9684
9768
  "dirty": false,
9685
- "builtAt": "2026-10-22T10:48:39.000Z"
9769
+ "builtAt": "2026-10-23T05:43:49.000Z"
9686
9770
  };
9687
9771
  //#endregion
9688
9772
  //#region src/steps.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duyquangnvx/create-agent-cli",
3
- "version": "0.11.1",
3
+ "version": "0.13.0",
4
4
  "description": "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -19,7 +19,7 @@
19
19
  "access": "public"
20
20
  },
21
21
  "devDependencies": {
22
- "@duyquangnvx/agent-cli": "0.11.1",
22
+ "@duyquangnvx/agent-cli": "0.13.0",
23
23
  "tsdown": "0.23.0",
24
24
  "zod": "4.6.7"
25
25
  },