@duyquangnvx/create-agent-cli 0.30.0 → 0.30.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.
- package/CHANGELOG.md +7 -0
- package/dist/bin.js +198 -184
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,13 @@ What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duy
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## v0.30.1 (2026-11-09)
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- `ctx.spawn` kills a child that ignores SIGTERM with SIGKILL 2 s after stopping it, so it no longer outlives the CLI and the Command's cleanup runs within the grace.
|
|
12
|
+
- Nothing of a run reaches stdout or stderr after its output, so the error a caller reads last stays last: a Side task's own progress, and progress from a Command still running past its grace, such as `finishing <name>` from a `runToEnd` it starts late, were printed after the output while the run waited for its Side task. A Side task's failure under `<NAME>_DEBUG=1` still prints after it.
|
|
13
|
+
|
|
7
14
|
## v0.30.0 (2026-11-09)
|
|
8
15
|
|
|
9
16
|
### Added
|
package/dist/bin.js
CHANGED
|
@@ -5740,6 +5740,40 @@ function untilAborted(signal) {
|
|
|
5740
5740
|
});
|
|
5741
5741
|
}
|
|
5742
5742
|
//#endregion
|
|
5743
|
+
//#region ../agent-cli/dist/clock.js
|
|
5744
|
+
const realClock = {
|
|
5745
|
+
now: () => Date.now(),
|
|
5746
|
+
sleep: (ms, signal) => new Promise((resolve, reject) => {
|
|
5747
|
+
if (signal?.aborted === true) {
|
|
5748
|
+
reject(signal.reason);
|
|
5749
|
+
return;
|
|
5750
|
+
}
|
|
5751
|
+
const onAbort = () => {
|
|
5752
|
+
clearTimeout(timer);
|
|
5753
|
+
reject(signal?.reason);
|
|
5754
|
+
};
|
|
5755
|
+
const timer = setTimeout(() => {
|
|
5756
|
+
signal?.removeEventListener("abort", onAbort);
|
|
5757
|
+
resolve();
|
|
5758
|
+
}, ms);
|
|
5759
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
5760
|
+
})
|
|
5761
|
+
};
|
|
5762
|
+
/** Runs `then` once `ms` have passed on `clock`; aborting `cancel` drops it. */
|
|
5763
|
+
function after(clock, ms, cancel, then) {
|
|
5764
|
+
clock.sleep(ms, cancel).then(then, (error) => {
|
|
5765
|
+
if (!cancel.aborted) throw error;
|
|
5766
|
+
});
|
|
5767
|
+
}
|
|
5768
|
+
async function poll(clock, check, intervalMs, signal) {
|
|
5769
|
+
for (;;) {
|
|
5770
|
+
signal.throwIfAborted();
|
|
5771
|
+
const value = await check();
|
|
5772
|
+
if (value !== void 0) return value;
|
|
5773
|
+
await clock.sleep(intervalMs, signal);
|
|
5774
|
+
}
|
|
5775
|
+
}
|
|
5776
|
+
//#endregion
|
|
5743
5777
|
//#region ../agent-cli/dist/command-line/tokenize.js
|
|
5744
5778
|
const NEGATIVE_NUMBER = /^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/;
|
|
5745
5779
|
function isOptionLike(word) {
|
|
@@ -6064,6 +6098,149 @@ function lastLines(output, count) {
|
|
|
6064
6098
|
return output.trim().split("\n").slice(-count).join("\n");
|
|
6065
6099
|
}
|
|
6066
6100
|
//#endregion
|
|
6101
|
+
//#region ../agent-cli/dist/context/interrupts.js
|
|
6102
|
+
const INTERRUPTED = "INTERRUPTED";
|
|
6103
|
+
const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
|
|
6104
|
+
/** How long an interrupted Command may take to settle before the run ends without it. */
|
|
6105
|
+
const GRACE_MS = 3e3;
|
|
6106
|
+
const GRACE_OVER = Symbol("grace over");
|
|
6107
|
+
function interruptedError(rerun) {
|
|
6108
|
+
return new CliError({
|
|
6109
|
+
code: INTERRUPTED,
|
|
6110
|
+
message: "interrupted before it finished",
|
|
6111
|
+
hint: "to finish it, run the Next step, unless it was stopped on purpose; if a tool timeout stopped it, give it longer",
|
|
6112
|
+
next: [rerun]
|
|
6113
|
+
});
|
|
6114
|
+
}
|
|
6115
|
+
const BOUND_WORDS = {
|
|
6116
|
+
deadline: "deadline",
|
|
6117
|
+
ignoreStop: "timeout"
|
|
6118
|
+
};
|
|
6119
|
+
function waitHint(bound) {
|
|
6120
|
+
return bound === "ignoreStop" ? "the call may have been billed: ask your user before running the Next step" : "run the Next step later, when the service it waits on may answer faster";
|
|
6121
|
+
}
|
|
6122
|
+
function deadlineError(commandLine, ms, advice, bound = "deadline") {
|
|
6123
|
+
return new CliError({
|
|
6124
|
+
code: DEADLINE_EXCEEDED,
|
|
6125
|
+
message: `${commandLine} ran past its ${ms} ms ${BOUND_WORDS[bound]}`,
|
|
6126
|
+
...advice
|
|
6127
|
+
});
|
|
6128
|
+
}
|
|
6129
|
+
/** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
|
|
6130
|
+
var RunControl = class {
|
|
6131
|
+
signal;
|
|
6132
|
+
#controller = new AbortController();
|
|
6133
|
+
#commandLine;
|
|
6134
|
+
#rerun;
|
|
6135
|
+
#outer;
|
|
6136
|
+
#onOuterAbort = () => this.#controller.abort(interruptedError(this.#rerun));
|
|
6137
|
+
/** Each running must-finish step, tracked until it settles, with its name. */
|
|
6138
|
+
#steps = /* @__PURE__ */ new Map();
|
|
6139
|
+
#progress;
|
|
6140
|
+
#clock;
|
|
6141
|
+
#disposed = new AbortController();
|
|
6142
|
+
/** The reasons this run's deadlines aborted with, to tell them in a Command's error. */
|
|
6143
|
+
#abortReasons = /* @__PURE__ */ new WeakSet();
|
|
6144
|
+
#graceCancel = new AbortController();
|
|
6145
|
+
#endGrace = () => {};
|
|
6146
|
+
#graceOver = new Promise((resolve) => {
|
|
6147
|
+
this.#endGrace = resolve;
|
|
6148
|
+
});
|
|
6149
|
+
constructor({ commandLine, rerun, outer, clock, progress, runDeadline }) {
|
|
6150
|
+
this.#commandLine = commandLine;
|
|
6151
|
+
this.#rerun = rerun;
|
|
6152
|
+
this.#outer = outer;
|
|
6153
|
+
this.#clock = clock;
|
|
6154
|
+
this.#progress = progress;
|
|
6155
|
+
this.signal = this.#controller.signal;
|
|
6156
|
+
this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
|
|
6157
|
+
if (outer?.aborted === true) this.#onOuterAbort();
|
|
6158
|
+
else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
|
|
6159
|
+
if (runDeadline !== void 0) {
|
|
6160
|
+
const { ms, advice } = runDeadline;
|
|
6161
|
+
after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, advice())));
|
|
6162
|
+
}
|
|
6163
|
+
}
|
|
6164
|
+
deadline(ms, options = {}) {
|
|
6165
|
+
return options.ignoreStop === true ? this.#abortsAfter(ms, "ignoreStop") : AbortSignal.any([this.signal, this.#abortsAfter(ms, "deadline")]);
|
|
6166
|
+
}
|
|
6167
|
+
#abortsAfter(ms, bound) {
|
|
6168
|
+
const controller = new AbortController();
|
|
6169
|
+
after(this.#clock, ms, this.#disposed.signal, () => {
|
|
6170
|
+
const advice = {
|
|
6171
|
+
hint: waitHint(bound),
|
|
6172
|
+
next: [this.#rerun]
|
|
6173
|
+
};
|
|
6174
|
+
const reason = deadlineError(this.#commandLine, ms, advice, bound);
|
|
6175
|
+
this.#abortReasons.add(reason);
|
|
6176
|
+
controller.abort(reason);
|
|
6177
|
+
});
|
|
6178
|
+
return controller.signal;
|
|
6179
|
+
}
|
|
6180
|
+
async runToEnd(name, work) {
|
|
6181
|
+
this.#graceCancel.abort();
|
|
6182
|
+
if (this.signal.aborted) this.#finishing(name);
|
|
6183
|
+
const step = work();
|
|
6184
|
+
const settled = step.then(() => {}, () => {});
|
|
6185
|
+
this.#steps.set(settled, name);
|
|
6186
|
+
try {
|
|
6187
|
+
return await step;
|
|
6188
|
+
} finally {
|
|
6189
|
+
this.#steps.delete(settled);
|
|
6190
|
+
this.#startGrace();
|
|
6191
|
+
}
|
|
6192
|
+
}
|
|
6193
|
+
/** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
|
|
6194
|
+
async settle(work) {
|
|
6195
|
+
try {
|
|
6196
|
+
const value = await Promise.race([work, this.#graceOver]);
|
|
6197
|
+
if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
|
|
6198
|
+
return value;
|
|
6199
|
+
} finally {
|
|
6200
|
+
await this.#stepsDone();
|
|
6201
|
+
this.close();
|
|
6202
|
+
}
|
|
6203
|
+
}
|
|
6204
|
+
/**
|
|
6205
|
+
* The error a run ends with: an interrupt or the run deadline outranks what the Command threw,
|
|
6206
|
+
* unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
|
|
6207
|
+
* Otherwise each leaf that is not a `CliError` and whose cause chain holds a deadline's abort is
|
|
6208
|
+
* reported as that abort, so a declared error keeps its own code.
|
|
6209
|
+
*/
|
|
6210
|
+
errorAfter(error) {
|
|
6211
|
+
if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
|
|
6212
|
+
const leaves = errorsOf(error).map((leaf) => this.#abortIn(leaf) ?? leaf);
|
|
6213
|
+
return leaves.length === 1 ? leaves[0] : new AggregateError(leaves);
|
|
6214
|
+
}
|
|
6215
|
+
#abortIn(error) {
|
|
6216
|
+
if (error instanceof CliError) return void 0;
|
|
6217
|
+
return causesOf(error).find((each) => each instanceof CliError && this.#abortReasons.has(each));
|
|
6218
|
+
}
|
|
6219
|
+
#onStop() {
|
|
6220
|
+
for (const name of this.#steps.values()) this.#finishing(name);
|
|
6221
|
+
this.#startGrace();
|
|
6222
|
+
}
|
|
6223
|
+
#finishing(name) {
|
|
6224
|
+
this.#progress(`finishing ${name}`);
|
|
6225
|
+
}
|
|
6226
|
+
#startGrace() {
|
|
6227
|
+
if (this.signal.aborted && this.#steps.size === 0) {
|
|
6228
|
+
this.#graceCancel.abort();
|
|
6229
|
+
this.#graceCancel = new AbortController();
|
|
6230
|
+
after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
|
|
6231
|
+
}
|
|
6232
|
+
}
|
|
6233
|
+
async #stepsDone() {
|
|
6234
|
+
while (this.#steps.size > 0) await Promise.all(this.#steps.keys());
|
|
6235
|
+
}
|
|
6236
|
+
/** Stops the run's timers and its hold on the caller's signal, for a run that never settles. */
|
|
6237
|
+
close() {
|
|
6238
|
+
this.#graceCancel.abort();
|
|
6239
|
+
this.#disposed.abort();
|
|
6240
|
+
this.#outer?.removeEventListener("abort", this.#onOuterAbort);
|
|
6241
|
+
}
|
|
6242
|
+
};
|
|
6243
|
+
//#endregion
|
|
6067
6244
|
//#region ../agent-cli/dist/define.js
|
|
6068
6245
|
function isCommand(node) {
|
|
6069
6246
|
return typeof node.effect === "string";
|
|
@@ -6146,6 +6323,7 @@ function toolRows(tools, commands, platform) {
|
|
|
6146
6323
|
const TOOL_FAILED = "TOOL_FAILED";
|
|
6147
6324
|
const STDERR_TAIL_LINES$1 = 20;
|
|
6148
6325
|
const SIGNAL_EXIT_BASE = 128;
|
|
6326
|
+
const KILL_AFTER_MS = 2e3;
|
|
6149
6327
|
const BATCH_FILE = /\.(?:cmd|bat)$/i;
|
|
6150
6328
|
const CMD_SHIM = /\.cmd$/i;
|
|
6151
6329
|
const CMD_META = /([()\][%!^"`<>&|;, *?])/g;
|
|
@@ -6182,9 +6360,10 @@ async function windowsLaunch(command, args, env) {
|
|
|
6182
6360
|
shell: true
|
|
6183
6361
|
};
|
|
6184
6362
|
}
|
|
6185
|
-
function stop(child) {
|
|
6363
|
+
function stop(child, exited) {
|
|
6186
6364
|
if (process.platform !== "win32" || child.pid === void 0) {
|
|
6187
6365
|
child.kill();
|
|
6366
|
+
after(realClock, KILL_AFTER_MS, exited, () => child.kill("SIGKILL"));
|
|
6188
6367
|
return Promise.resolve();
|
|
6189
6368
|
}
|
|
6190
6369
|
const pid = String(child.pid);
|
|
@@ -6280,8 +6459,9 @@ async function spawnChild(command, args, { cwd, env, signal, onLine, stdin, deta
|
|
|
6280
6459
|
detached: detached && process.platform !== "win32"
|
|
6281
6460
|
});
|
|
6282
6461
|
let stopped = Promise.resolve();
|
|
6462
|
+
const exited = new AbortController();
|
|
6283
6463
|
const kill = () => {
|
|
6284
|
-
stopped = stop(child);
|
|
6464
|
+
stopped = stop(child, exited.signal);
|
|
6285
6465
|
};
|
|
6286
6466
|
signal.addEventListener("abort", kill, { once: true });
|
|
6287
6467
|
let callerFailure;
|
|
@@ -6304,7 +6484,9 @@ async function spawnChild(command, args, { cwd, env, signal, onLine, stdin, deta
|
|
|
6304
6484
|
stderrLines.push(chunk);
|
|
6305
6485
|
});
|
|
6306
6486
|
if (stdin !== void 0 && child.stdin !== null) feed(child.stdin, stdin, signal).catch(fail);
|
|
6487
|
+
child.once("exit", () => exited.abort());
|
|
6307
6488
|
child.on("error", (error) => {
|
|
6489
|
+
exited.abort();
|
|
6308
6490
|
signal.removeEventListener("abort", kill);
|
|
6309
6491
|
reject(error);
|
|
6310
6492
|
});
|
|
@@ -6453,40 +6635,6 @@ function contextSpawnDetached(run, defaults, scope) {
|
|
|
6453
6635
|
};
|
|
6454
6636
|
}
|
|
6455
6637
|
//#endregion
|
|
6456
|
-
//#region ../agent-cli/dist/clock.js
|
|
6457
|
-
const realClock = {
|
|
6458
|
-
now: () => Date.now(),
|
|
6459
|
-
sleep: (ms, signal) => new Promise((resolve, reject) => {
|
|
6460
|
-
if (signal?.aborted === true) {
|
|
6461
|
-
reject(signal.reason);
|
|
6462
|
-
return;
|
|
6463
|
-
}
|
|
6464
|
-
const onAbort = () => {
|
|
6465
|
-
clearTimeout(timer);
|
|
6466
|
-
reject(signal?.reason);
|
|
6467
|
-
};
|
|
6468
|
-
const timer = setTimeout(() => {
|
|
6469
|
-
signal?.removeEventListener("abort", onAbort);
|
|
6470
|
-
resolve();
|
|
6471
|
-
}, ms);
|
|
6472
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
6473
|
-
})
|
|
6474
|
-
};
|
|
6475
|
-
/** Runs `then` once `ms` have passed on `clock`; aborting `cancel` drops it. */
|
|
6476
|
-
function after(clock, ms, cancel, then) {
|
|
6477
|
-
clock.sleep(ms, cancel).then(then, (error) => {
|
|
6478
|
-
if (!cancel.aborted) throw error;
|
|
6479
|
-
});
|
|
6480
|
-
}
|
|
6481
|
-
async function poll(clock, check, intervalMs, signal) {
|
|
6482
|
-
for (;;) {
|
|
6483
|
-
signal.throwIfAborted();
|
|
6484
|
-
const value = await check();
|
|
6485
|
-
if (value !== void 0) return value;
|
|
6486
|
-
await clock.sleep(intervalMs, signal);
|
|
6487
|
-
}
|
|
6488
|
-
}
|
|
6489
|
-
//#endregion
|
|
6490
6638
|
//#region ../agent-cli/dist/context/stdin.js
|
|
6491
6639
|
const STDIN_META = "stdin";
|
|
6492
6640
|
function isStdinSchema(schema) {
|
|
@@ -7067,149 +7215,6 @@ function classifyErrno(error, context) {
|
|
|
7067
7215
|
}
|
|
7068
7216
|
}
|
|
7069
7217
|
//#endregion
|
|
7070
|
-
//#region ../agent-cli/dist/context/interrupts.js
|
|
7071
|
-
const INTERRUPTED = "INTERRUPTED";
|
|
7072
|
-
const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
|
|
7073
|
-
/** How long an interrupted Command may take to settle before the run ends without it. */
|
|
7074
|
-
const GRACE_MS = 3e3;
|
|
7075
|
-
const GRACE_OVER = Symbol("grace over");
|
|
7076
|
-
function interruptedError(rerun) {
|
|
7077
|
-
return new CliError({
|
|
7078
|
-
code: INTERRUPTED,
|
|
7079
|
-
message: "interrupted before it finished",
|
|
7080
|
-
hint: "to finish it, run the Next step, unless it was stopped on purpose; if a tool timeout stopped it, give it longer",
|
|
7081
|
-
next: [rerun]
|
|
7082
|
-
});
|
|
7083
|
-
}
|
|
7084
|
-
const BOUND_WORDS = {
|
|
7085
|
-
deadline: "deadline",
|
|
7086
|
-
ignoreStop: "timeout"
|
|
7087
|
-
};
|
|
7088
|
-
function waitHint(bound) {
|
|
7089
|
-
return bound === "ignoreStop" ? "the call may have been billed: ask your user before running the Next step" : "run the Next step later, when the service it waits on may answer faster";
|
|
7090
|
-
}
|
|
7091
|
-
function deadlineError(commandLine, ms, advice, bound = "deadline") {
|
|
7092
|
-
return new CliError({
|
|
7093
|
-
code: DEADLINE_EXCEEDED,
|
|
7094
|
-
message: `${commandLine} ran past its ${ms} ms ${BOUND_WORDS[bound]}`,
|
|
7095
|
-
...advice
|
|
7096
|
-
});
|
|
7097
|
-
}
|
|
7098
|
-
/** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
|
|
7099
|
-
var RunControl = class {
|
|
7100
|
-
signal;
|
|
7101
|
-
#controller = new AbortController();
|
|
7102
|
-
#commandLine;
|
|
7103
|
-
#rerun;
|
|
7104
|
-
#outer;
|
|
7105
|
-
#onOuterAbort = () => this.#controller.abort(interruptedError(this.#rerun));
|
|
7106
|
-
/** Each running must-finish step, tracked until it settles, with its name. */
|
|
7107
|
-
#steps = /* @__PURE__ */ new Map();
|
|
7108
|
-
#progress;
|
|
7109
|
-
#clock;
|
|
7110
|
-
#disposed = new AbortController();
|
|
7111
|
-
/** The reasons this run's deadlines aborted with, to tell them in a Command's error. */
|
|
7112
|
-
#abortReasons = /* @__PURE__ */ new WeakSet();
|
|
7113
|
-
#graceCancel = new AbortController();
|
|
7114
|
-
#endGrace = () => {};
|
|
7115
|
-
#graceOver = new Promise((resolve) => {
|
|
7116
|
-
this.#endGrace = resolve;
|
|
7117
|
-
});
|
|
7118
|
-
constructor({ commandLine, rerun, outer, clock, progress, runDeadline }) {
|
|
7119
|
-
this.#commandLine = commandLine;
|
|
7120
|
-
this.#rerun = rerun;
|
|
7121
|
-
this.#outer = outer;
|
|
7122
|
-
this.#clock = clock;
|
|
7123
|
-
this.#progress = progress;
|
|
7124
|
-
this.signal = this.#controller.signal;
|
|
7125
|
-
this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
|
|
7126
|
-
if (outer?.aborted === true) this.#onOuterAbort();
|
|
7127
|
-
else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
|
|
7128
|
-
if (runDeadline !== void 0) {
|
|
7129
|
-
const { ms, advice } = runDeadline;
|
|
7130
|
-
after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, advice())));
|
|
7131
|
-
}
|
|
7132
|
-
}
|
|
7133
|
-
deadline(ms, options = {}) {
|
|
7134
|
-
return options.ignoreStop === true ? this.#abortsAfter(ms, "ignoreStop") : AbortSignal.any([this.signal, this.#abortsAfter(ms, "deadline")]);
|
|
7135
|
-
}
|
|
7136
|
-
#abortsAfter(ms, bound) {
|
|
7137
|
-
const controller = new AbortController();
|
|
7138
|
-
after(this.#clock, ms, this.#disposed.signal, () => {
|
|
7139
|
-
const advice = {
|
|
7140
|
-
hint: waitHint(bound),
|
|
7141
|
-
next: [this.#rerun]
|
|
7142
|
-
};
|
|
7143
|
-
const reason = deadlineError(this.#commandLine, ms, advice, bound);
|
|
7144
|
-
this.#abortReasons.add(reason);
|
|
7145
|
-
controller.abort(reason);
|
|
7146
|
-
});
|
|
7147
|
-
return controller.signal;
|
|
7148
|
-
}
|
|
7149
|
-
async runToEnd(name, work) {
|
|
7150
|
-
this.#graceCancel.abort();
|
|
7151
|
-
if (this.signal.aborted) this.#finishing(name);
|
|
7152
|
-
const step = work();
|
|
7153
|
-
const settled = step.then(() => {}, () => {});
|
|
7154
|
-
this.#steps.set(settled, name);
|
|
7155
|
-
try {
|
|
7156
|
-
return await step;
|
|
7157
|
-
} finally {
|
|
7158
|
-
this.#steps.delete(settled);
|
|
7159
|
-
this.#startGrace();
|
|
7160
|
-
}
|
|
7161
|
-
}
|
|
7162
|
-
/** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
|
|
7163
|
-
async settle(work) {
|
|
7164
|
-
try {
|
|
7165
|
-
const value = await Promise.race([work, this.#graceOver]);
|
|
7166
|
-
if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
|
|
7167
|
-
return value;
|
|
7168
|
-
} finally {
|
|
7169
|
-
await this.#stepsDone();
|
|
7170
|
-
this.close();
|
|
7171
|
-
}
|
|
7172
|
-
}
|
|
7173
|
-
/**
|
|
7174
|
-
* The error a run ends with: an interrupt or the run deadline outranks what the Command threw,
|
|
7175
|
-
* unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
|
|
7176
|
-
* Otherwise each leaf that is not a `CliError` and whose cause chain holds a deadline's abort is
|
|
7177
|
-
* reported as that abort, so a declared error keeps its own code.
|
|
7178
|
-
*/
|
|
7179
|
-
errorAfter(error) {
|
|
7180
|
-
if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
|
|
7181
|
-
const leaves = errorsOf(error).map((leaf) => this.#abortIn(leaf) ?? leaf);
|
|
7182
|
-
return leaves.length === 1 ? leaves[0] : new AggregateError(leaves);
|
|
7183
|
-
}
|
|
7184
|
-
#abortIn(error) {
|
|
7185
|
-
if (error instanceof CliError) return void 0;
|
|
7186
|
-
return causesOf(error).find((each) => each instanceof CliError && this.#abortReasons.has(each));
|
|
7187
|
-
}
|
|
7188
|
-
#onStop() {
|
|
7189
|
-
for (const name of this.#steps.values()) this.#finishing(name);
|
|
7190
|
-
this.#startGrace();
|
|
7191
|
-
}
|
|
7192
|
-
#finishing(name) {
|
|
7193
|
-
this.#progress(`finishing ${name}`);
|
|
7194
|
-
}
|
|
7195
|
-
#startGrace() {
|
|
7196
|
-
if (this.signal.aborted && this.#steps.size === 0) {
|
|
7197
|
-
this.#graceCancel.abort();
|
|
7198
|
-
this.#graceCancel = new AbortController();
|
|
7199
|
-
after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
|
|
7200
|
-
}
|
|
7201
|
-
}
|
|
7202
|
-
async #stepsDone() {
|
|
7203
|
-
while (this.#steps.size > 0) await Promise.all(this.#steps.keys());
|
|
7204
|
-
}
|
|
7205
|
-
/** Stops the run's timers and its hold on the caller's signal, for a run that never settles. */
|
|
7206
|
-
close() {
|
|
7207
|
-
this.#graceCancel.abort();
|
|
7208
|
-
this.#disposed.abort();
|
|
7209
|
-
this.#outer?.removeEventListener("abort", this.#onOuterAbort);
|
|
7210
|
-
}
|
|
7211
|
-
};
|
|
7212
|
-
//#endregion
|
|
7213
7218
|
//#region ../agent-cli/dist/context/operator.js
|
|
7214
7219
|
const NEEDS_OPERATOR = "NEEDS_OPERATOR";
|
|
7215
7220
|
/** Fails with `NEEDS_OPERATOR` when a Coding agent runs an Operator Command. */
|
|
@@ -7743,9 +7748,9 @@ function cliErrorOf(code, init, options) {
|
|
|
7743
7748
|
/** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
|
|
7744
7749
|
function buildStamp() {
|
|
7745
7750
|
return {
|
|
7746
|
-
"sha": "
|
|
7751
|
+
"sha": "7f79c40",
|
|
7747
7752
|
"dirty": false,
|
|
7748
|
-
"builtAt": "2026-11-
|
|
7753
|
+
"builtAt": "2026-11-09T07:03:37.000Z"
|
|
7749
7754
|
};
|
|
7750
7755
|
}
|
|
7751
7756
|
/** The first line of `--version`: the version, and the commit and time of a stamped build. */
|
|
@@ -11599,7 +11604,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11599
11604
|
}, {
|
|
11600
11605
|
clock,
|
|
11601
11606
|
failed: (error) => {
|
|
11602
|
-
if (isDebug(baseFormat))
|
|
11607
|
+
if (isDebug(baseFormat)) write.debug(progressLine(`side task failed: ${traceOf(error)}`, routed.json));
|
|
11603
11608
|
}
|
|
11604
11609
|
});
|
|
11605
11610
|
const ran = ({ result, end }, sideTask) => {
|
|
@@ -11739,7 +11744,16 @@ async function whileStopping(signal, onStop, work) {
|
|
|
11739
11744
|
}
|
|
11740
11745
|
/** Runs the CLI, writes its output, then waits for the Side task it started. */
|
|
11741
11746
|
async function runWithProgress(cli, argv, options, write, stopNotice) {
|
|
11742
|
-
|
|
11747
|
+
let resultWritten = false;
|
|
11748
|
+
const gated = (writeTo) => (text) => {
|
|
11749
|
+
if (!resultWritten) writeTo(text);
|
|
11750
|
+
};
|
|
11751
|
+
const { result, finishSideTask } = await routeAndRun(cli, argv, options, {
|
|
11752
|
+
stdout: gated(write.stdout),
|
|
11753
|
+
stderr: gated(write.stderr),
|
|
11754
|
+
debug: write.stderr
|
|
11755
|
+
}, stopNotice);
|
|
11756
|
+
resultWritten = true;
|
|
11743
11757
|
await write.result(result);
|
|
11744
11758
|
await finishSideTask();
|
|
11745
11759
|
return result;
|
|
@@ -11842,7 +11856,7 @@ async function runProcess(cli) {
|
|
|
11842
11856
|
//#region package.json
|
|
11843
11857
|
var package_default = {
|
|
11844
11858
|
name: "@duyquangnvx/create-agent-cli",
|
|
11845
|
-
version: "0.30.
|
|
11859
|
+
version: "0.30.1",
|
|
11846
11860
|
description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
11847
11861
|
type: "module",
|
|
11848
11862
|
engines: { "node": ">=22.12.0" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duyquangnvx/create-agent-cli",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.1",
|
|
4
4
|
"description": "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"access": "public"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@duyquangnvx/agent-cli": "0.30.
|
|
24
|
+
"@duyquangnvx/agent-cli": "0.30.1",
|
|
25
25
|
"tsdown": "0.23.0",
|
|
26
26
|
"zod": "4.6.7"
|
|
27
27
|
},
|