@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.
- package/CHANGELOG.md +12 -0
- package/dist/bin.js +110 -26
- 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(
|
|
6667
|
+
const lines = (rest + text).split(LINE_END);
|
|
6665
6668
|
rest = lines.pop() ?? "";
|
|
6666
|
-
for (const line of lines)
|
|
6669
|
+
for (const line of lines) onLine(line);
|
|
6667
6670
|
},
|
|
6668
6671
|
end() {
|
|
6669
|
-
if (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
|
|
6702
|
-
const
|
|
6703
|
-
|
|
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
|
|
6759
|
+
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
6708
6760
|
stderr += chunk;
|
|
6709
6761
|
stderrLines.push(chunk);
|
|
6710
6762
|
});
|
|
6711
|
-
|
|
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.
|
|
6849
|
+
onLine: reportingLines(options.onLine, caller),
|
|
6850
|
+
stdin: reporting(options.stdin, caller)
|
|
6762
6851
|
});
|
|
6763
6852
|
} catch (error) {
|
|
6764
|
-
|
|
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.
|
|
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": "
|
|
9767
|
+
"sha": "1c3a29e",
|
|
9684
9768
|
"dirty": false,
|
|
9685
|
-
"builtAt": "2026-10-
|
|
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.
|
|
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.
|
|
22
|
+
"@duyquangnvx/agent-cli": "0.13.0",
|
|
23
23
|
"tsdown": "0.23.0",
|
|
24
24
|
"zod": "4.6.7"
|
|
25
25
|
},
|