@duyquangnvx/create-agent-cli 0.29.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 +22 -0
- package/dist/bin.js +365 -260
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,28 @@ 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
|
+
|
|
14
|
+
## v0.30.0 (2026-11-09)
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- A Side task learns how its run ended: `SideTaskRun` has `ended`, a promise of `RunEnd`, `{ exitCode, errors: [{ code, message }] }` with no errors on success, as the caller sees it, the Runtime's codes such as `INTERRUPTED` included. `RunEnd` and `RunEndError` are exported.
|
|
19
|
+
- `defineCli({ skill })` of a Global install takes `add`, the whole line that installs the skill from its Skill Hub, so the Hub sees who installed it. `setup` then copies no skill and ends with `run: <add>`, its `--json` `skill` being `{ add }`, on every run; `update` leaves the Skill copy alone, its `skill` being `null`; and `doctor`'s `skill` row passes on any one home copy and gives `add` as the fix when there is none. `defineCli` throws on an empty or multi-line `add`, and on `add` beside any install but a Global install. Users whose copy came from the package move to the Hub when they run `setup` again, so a CLI that adds `add` asks them to.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **Breaking:** `defineCli({ sideTask })` also runs for a Command whose input is rejected, such as a missing arg or an unknown option after its name, with `dryRun` false, so a usage count also counts those runs.
|
|
24
|
+
|
|
25
|
+
### Upgrade from v0.29.0
|
|
26
|
+
|
|
27
|
+
- A Side task that must not count a run whose input was rejected awaits `ended` and skips that run by its error code, such as `INVALID_INPUT`.
|
|
28
|
+
|
|
7
29
|
## v0.29.0 (2026-11-09)
|
|
8
30
|
|
|
9
31
|
### 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) {
|
|
@@ -5972,10 +6006,19 @@ function bugHint(format) {
|
|
|
5972
6006
|
const rerun = `rerun with ${debugVariable(format.cliName)}=1 to see the stack trace`;
|
|
5973
6007
|
return bugs === void 0 ? rerun : `${rerun}, then report it at ${bugs}`;
|
|
5974
6008
|
}
|
|
6009
|
+
/** The code and message the caller sees for one error a run failed with. */
|
|
6010
|
+
function runEndErrorOf(error) {
|
|
6011
|
+
return error instanceof CliError ? {
|
|
6012
|
+
code: error.code,
|
|
6013
|
+
message: error.message
|
|
6014
|
+
} : {
|
|
6015
|
+
code: INTERNAL,
|
|
6016
|
+
message: messageOf(error)
|
|
6017
|
+
};
|
|
6018
|
+
}
|
|
5975
6019
|
function fieldsOf$1({ error, exitClass }, format) {
|
|
5976
6020
|
if (!(error instanceof CliError)) return {
|
|
5977
|
-
|
|
5978
|
-
message: messageOf(error),
|
|
6021
|
+
...runEndErrorOf(error),
|
|
5979
6022
|
hint: bugHint(format)
|
|
5980
6023
|
};
|
|
5981
6024
|
const { code, message, hint, details, retryAt, next } = error;
|
|
@@ -6055,6 +6098,149 @@ function lastLines(output, count) {
|
|
|
6055
6098
|
return output.trim().split("\n").slice(-count).join("\n");
|
|
6056
6099
|
}
|
|
6057
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
|
|
6058
6244
|
//#region ../agent-cli/dist/define.js
|
|
6059
6245
|
function isCommand(node) {
|
|
6060
6246
|
return typeof node.effect === "string";
|
|
@@ -6137,6 +6323,7 @@ function toolRows(tools, commands, platform) {
|
|
|
6137
6323
|
const TOOL_FAILED = "TOOL_FAILED";
|
|
6138
6324
|
const STDERR_TAIL_LINES$1 = 20;
|
|
6139
6325
|
const SIGNAL_EXIT_BASE = 128;
|
|
6326
|
+
const KILL_AFTER_MS = 2e3;
|
|
6140
6327
|
const BATCH_FILE = /\.(?:cmd|bat)$/i;
|
|
6141
6328
|
const CMD_SHIM = /\.cmd$/i;
|
|
6142
6329
|
const CMD_META = /([()\][%!^"`<>&|;, *?])/g;
|
|
@@ -6173,9 +6360,10 @@ async function windowsLaunch(command, args, env) {
|
|
|
6173
6360
|
shell: true
|
|
6174
6361
|
};
|
|
6175
6362
|
}
|
|
6176
|
-
function stop(child) {
|
|
6363
|
+
function stop(child, exited) {
|
|
6177
6364
|
if (process.platform !== "win32" || child.pid === void 0) {
|
|
6178
6365
|
child.kill();
|
|
6366
|
+
after(realClock, KILL_AFTER_MS, exited, () => child.kill("SIGKILL"));
|
|
6179
6367
|
return Promise.resolve();
|
|
6180
6368
|
}
|
|
6181
6369
|
const pid = String(child.pid);
|
|
@@ -6271,8 +6459,9 @@ async function spawnChild(command, args, { cwd, env, signal, onLine, stdin, deta
|
|
|
6271
6459
|
detached: detached && process.platform !== "win32"
|
|
6272
6460
|
});
|
|
6273
6461
|
let stopped = Promise.resolve();
|
|
6462
|
+
const exited = new AbortController();
|
|
6274
6463
|
const kill = () => {
|
|
6275
|
-
stopped = stop(child);
|
|
6464
|
+
stopped = stop(child, exited.signal);
|
|
6276
6465
|
};
|
|
6277
6466
|
signal.addEventListener("abort", kill, { once: true });
|
|
6278
6467
|
let callerFailure;
|
|
@@ -6295,7 +6484,9 @@ async function spawnChild(command, args, { cwd, env, signal, onLine, stdin, deta
|
|
|
6295
6484
|
stderrLines.push(chunk);
|
|
6296
6485
|
});
|
|
6297
6486
|
if (stdin !== void 0 && child.stdin !== null) feed(child.stdin, stdin, signal).catch(fail);
|
|
6487
|
+
child.once("exit", () => exited.abort());
|
|
6298
6488
|
child.on("error", (error) => {
|
|
6489
|
+
exited.abort();
|
|
6299
6490
|
signal.removeEventListener("abort", kill);
|
|
6300
6491
|
reject(error);
|
|
6301
6492
|
});
|
|
@@ -6444,40 +6635,6 @@ function contextSpawnDetached(run, defaults, scope) {
|
|
|
6444
6635
|
};
|
|
6445
6636
|
}
|
|
6446
6637
|
//#endregion
|
|
6447
|
-
//#region ../agent-cli/dist/clock.js
|
|
6448
|
-
const realClock = {
|
|
6449
|
-
now: () => Date.now(),
|
|
6450
|
-
sleep: (ms, signal) => new Promise((resolve, reject) => {
|
|
6451
|
-
if (signal?.aborted === true) {
|
|
6452
|
-
reject(signal.reason);
|
|
6453
|
-
return;
|
|
6454
|
-
}
|
|
6455
|
-
const onAbort = () => {
|
|
6456
|
-
clearTimeout(timer);
|
|
6457
|
-
reject(signal?.reason);
|
|
6458
|
-
};
|
|
6459
|
-
const timer = setTimeout(() => {
|
|
6460
|
-
signal?.removeEventListener("abort", onAbort);
|
|
6461
|
-
resolve();
|
|
6462
|
-
}, ms);
|
|
6463
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
6464
|
-
})
|
|
6465
|
-
};
|
|
6466
|
-
/** Runs `then` once `ms` have passed on `clock`; aborting `cancel` drops it. */
|
|
6467
|
-
function after(clock, ms, cancel, then) {
|
|
6468
|
-
clock.sleep(ms, cancel).then(then, (error) => {
|
|
6469
|
-
if (!cancel.aborted) throw error;
|
|
6470
|
-
});
|
|
6471
|
-
}
|
|
6472
|
-
async function poll(clock, check, intervalMs, signal) {
|
|
6473
|
-
for (;;) {
|
|
6474
|
-
signal.throwIfAborted();
|
|
6475
|
-
const value = await check();
|
|
6476
|
-
if (value !== void 0) return value;
|
|
6477
|
-
await clock.sleep(intervalMs, signal);
|
|
6478
|
-
}
|
|
6479
|
-
}
|
|
6480
|
-
//#endregion
|
|
6481
6638
|
//#region ../agent-cli/dist/context/stdin.js
|
|
6482
6639
|
const STDIN_META = "stdin";
|
|
6483
6640
|
function isStdinSchema(schema) {
|
|
@@ -7058,149 +7215,6 @@ function classifyErrno(error, context) {
|
|
|
7058
7215
|
}
|
|
7059
7216
|
}
|
|
7060
7217
|
//#endregion
|
|
7061
|
-
//#region ../agent-cli/dist/context/interrupts.js
|
|
7062
|
-
const INTERRUPTED = "INTERRUPTED";
|
|
7063
|
-
const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
|
|
7064
|
-
/** How long an interrupted Command may take to settle before the run ends without it. */
|
|
7065
|
-
const GRACE_MS = 3e3;
|
|
7066
|
-
const GRACE_OVER = Symbol("grace over");
|
|
7067
|
-
function interruptedError(rerun) {
|
|
7068
|
-
return new CliError({
|
|
7069
|
-
code: INTERRUPTED,
|
|
7070
|
-
message: "interrupted before it finished",
|
|
7071
|
-
hint: "to finish it, run the Next step, unless it was stopped on purpose; if a tool timeout stopped it, give it longer",
|
|
7072
|
-
next: [rerun]
|
|
7073
|
-
});
|
|
7074
|
-
}
|
|
7075
|
-
const BOUND_WORDS = {
|
|
7076
|
-
deadline: "deadline",
|
|
7077
|
-
ignoreStop: "timeout"
|
|
7078
|
-
};
|
|
7079
|
-
function waitHint(bound) {
|
|
7080
|
-
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";
|
|
7081
|
-
}
|
|
7082
|
-
function deadlineError(commandLine, ms, advice, bound = "deadline") {
|
|
7083
|
-
return new CliError({
|
|
7084
|
-
code: DEADLINE_EXCEEDED,
|
|
7085
|
-
message: `${commandLine} ran past its ${ms} ms ${BOUND_WORDS[bound]}`,
|
|
7086
|
-
...advice
|
|
7087
|
-
});
|
|
7088
|
-
}
|
|
7089
|
-
/** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
|
|
7090
|
-
var RunControl = class {
|
|
7091
|
-
signal;
|
|
7092
|
-
#controller = new AbortController();
|
|
7093
|
-
#commandLine;
|
|
7094
|
-
#rerun;
|
|
7095
|
-
#outer;
|
|
7096
|
-
#onOuterAbort = () => this.#controller.abort(interruptedError(this.#rerun));
|
|
7097
|
-
/** Each running must-finish step, tracked until it settles, with its name. */
|
|
7098
|
-
#steps = /* @__PURE__ */ new Map();
|
|
7099
|
-
#progress;
|
|
7100
|
-
#clock;
|
|
7101
|
-
#disposed = new AbortController();
|
|
7102
|
-
/** The reasons this run's deadlines aborted with, to tell them in a Command's error. */
|
|
7103
|
-
#abortReasons = /* @__PURE__ */ new WeakSet();
|
|
7104
|
-
#graceCancel = new AbortController();
|
|
7105
|
-
#endGrace = () => {};
|
|
7106
|
-
#graceOver = new Promise((resolve) => {
|
|
7107
|
-
this.#endGrace = resolve;
|
|
7108
|
-
});
|
|
7109
|
-
constructor({ commandLine, rerun, outer, clock, progress, runDeadline }) {
|
|
7110
|
-
this.#commandLine = commandLine;
|
|
7111
|
-
this.#rerun = rerun;
|
|
7112
|
-
this.#outer = outer;
|
|
7113
|
-
this.#clock = clock;
|
|
7114
|
-
this.#progress = progress;
|
|
7115
|
-
this.signal = this.#controller.signal;
|
|
7116
|
-
this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
|
|
7117
|
-
if (outer?.aborted === true) this.#onOuterAbort();
|
|
7118
|
-
else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
|
|
7119
|
-
if (runDeadline !== void 0) {
|
|
7120
|
-
const { ms, advice } = runDeadline;
|
|
7121
|
-
after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, advice())));
|
|
7122
|
-
}
|
|
7123
|
-
}
|
|
7124
|
-
deadline(ms, options = {}) {
|
|
7125
|
-
return options.ignoreStop === true ? this.#abortsAfter(ms, "ignoreStop") : AbortSignal.any([this.signal, this.#abortsAfter(ms, "deadline")]);
|
|
7126
|
-
}
|
|
7127
|
-
#abortsAfter(ms, bound) {
|
|
7128
|
-
const controller = new AbortController();
|
|
7129
|
-
after(this.#clock, ms, this.#disposed.signal, () => {
|
|
7130
|
-
const advice = {
|
|
7131
|
-
hint: waitHint(bound),
|
|
7132
|
-
next: [this.#rerun]
|
|
7133
|
-
};
|
|
7134
|
-
const reason = deadlineError(this.#commandLine, ms, advice, bound);
|
|
7135
|
-
this.#abortReasons.add(reason);
|
|
7136
|
-
controller.abort(reason);
|
|
7137
|
-
});
|
|
7138
|
-
return controller.signal;
|
|
7139
|
-
}
|
|
7140
|
-
async runToEnd(name, work) {
|
|
7141
|
-
this.#graceCancel.abort();
|
|
7142
|
-
if (this.signal.aborted) this.#finishing(name);
|
|
7143
|
-
const step = work();
|
|
7144
|
-
const settled = step.then(() => {}, () => {});
|
|
7145
|
-
this.#steps.set(settled, name);
|
|
7146
|
-
try {
|
|
7147
|
-
return await step;
|
|
7148
|
-
} finally {
|
|
7149
|
-
this.#steps.delete(settled);
|
|
7150
|
-
this.#startGrace();
|
|
7151
|
-
}
|
|
7152
|
-
}
|
|
7153
|
-
/** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
|
|
7154
|
-
async settle(work) {
|
|
7155
|
-
try {
|
|
7156
|
-
const value = await Promise.race([work, this.#graceOver]);
|
|
7157
|
-
if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
|
|
7158
|
-
return value;
|
|
7159
|
-
} finally {
|
|
7160
|
-
await this.#stepsDone();
|
|
7161
|
-
this.close();
|
|
7162
|
-
}
|
|
7163
|
-
}
|
|
7164
|
-
/**
|
|
7165
|
-
* The error a run ends with: an interrupt or the run deadline outranks what the Command threw,
|
|
7166
|
-
* unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
|
|
7167
|
-
* Otherwise each leaf that is not a `CliError` and whose cause chain holds a deadline's abort is
|
|
7168
|
-
* reported as that abort, so a declared error keeps its own code.
|
|
7169
|
-
*/
|
|
7170
|
-
errorAfter(error) {
|
|
7171
|
-
if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
|
|
7172
|
-
const leaves = errorsOf(error).map((leaf) => this.#abortIn(leaf) ?? leaf);
|
|
7173
|
-
return leaves.length === 1 ? leaves[0] : new AggregateError(leaves);
|
|
7174
|
-
}
|
|
7175
|
-
#abortIn(error) {
|
|
7176
|
-
if (error instanceof CliError) return void 0;
|
|
7177
|
-
return causesOf(error).find((each) => each instanceof CliError && this.#abortReasons.has(each));
|
|
7178
|
-
}
|
|
7179
|
-
#onStop() {
|
|
7180
|
-
for (const name of this.#steps.values()) this.#finishing(name);
|
|
7181
|
-
this.#startGrace();
|
|
7182
|
-
}
|
|
7183
|
-
#finishing(name) {
|
|
7184
|
-
this.#progress(`finishing ${name}`);
|
|
7185
|
-
}
|
|
7186
|
-
#startGrace() {
|
|
7187
|
-
if (this.signal.aborted && this.#steps.size === 0) {
|
|
7188
|
-
this.#graceCancel.abort();
|
|
7189
|
-
this.#graceCancel = new AbortController();
|
|
7190
|
-
after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
|
|
7191
|
-
}
|
|
7192
|
-
}
|
|
7193
|
-
async #stepsDone() {
|
|
7194
|
-
while (this.#steps.size > 0) await Promise.all(this.#steps.keys());
|
|
7195
|
-
}
|
|
7196
|
-
/** Stops the run's timers and its hold on the caller's signal, for a run that never settles. */
|
|
7197
|
-
close() {
|
|
7198
|
-
this.#graceCancel.abort();
|
|
7199
|
-
this.#disposed.abort();
|
|
7200
|
-
this.#outer?.removeEventListener("abort", this.#onOuterAbort);
|
|
7201
|
-
}
|
|
7202
|
-
};
|
|
7203
|
-
//#endregion
|
|
7204
7218
|
//#region ../agent-cli/dist/context/operator.js
|
|
7205
7219
|
const NEEDS_OPERATOR = "NEEDS_OPERATOR";
|
|
7206
7220
|
/** Fails with `NEEDS_OPERATOR` when a Coding agent runs an Operator Command. */
|
|
@@ -7734,9 +7748,9 @@ function cliErrorOf(code, init, options) {
|
|
|
7734
7748
|
/** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
|
|
7735
7749
|
function buildStamp() {
|
|
7736
7750
|
return {
|
|
7737
|
-
"sha": "
|
|
7751
|
+
"sha": "7f79c40",
|
|
7738
7752
|
"dirty": false,
|
|
7739
|
-
"builtAt": "2026-11-
|
|
7753
|
+
"builtAt": "2026-11-09T07:03:37.000Z"
|
|
7740
7754
|
};
|
|
7741
7755
|
}
|
|
7742
7756
|
/** The first line of `--version`: the version, and the commit and time of a stamped build. */
|
|
@@ -8398,12 +8412,19 @@ const homeSyncSchema = object({
|
|
|
8398
8412
|
written: array(string()),
|
|
8399
8413
|
removed: array(string())
|
|
8400
8414
|
});
|
|
8415
|
+
function copiedFromPackage(skill) {
|
|
8416
|
+
return skill !== void 0 && skill.add === void 0;
|
|
8417
|
+
}
|
|
8418
|
+
/** The home Skill copies `setup` and `update` write: none when the skill comes from its Skill Hub. */
|
|
8419
|
+
async function syncedCopiesOf(cli, ctx) {
|
|
8420
|
+
return copiedFromPackage(cli.skill) ? homeCopiesOf(ctx.files, cli.name, ctx.env) : [];
|
|
8421
|
+
}
|
|
8401
8422
|
/**
|
|
8402
8423
|
* Makes each home Skill copy match the folder in the package at `packageDir`. Null when the CLI
|
|
8403
|
-
* declares no skill, or that package ships none, which must not empty a copy.
|
|
8424
|
+
* declares no skill, its Skill Hub installs it, or that package ships none, which must not empty a copy.
|
|
8404
8425
|
*/
|
|
8405
8426
|
async function syncHome(cli, packageDir, ctx) {
|
|
8406
|
-
if (cli.skill
|
|
8427
|
+
if (!copiedFromPackage(cli.skill)) return null;
|
|
8407
8428
|
const source = join(packageDir, cli.skill.folder);
|
|
8408
8429
|
if ((await filesIn(ctx.files, source)).length === 0) return null;
|
|
8409
8430
|
const copies = await homeCopiesOf(ctx.files, cli.name, ctx.env);
|
|
@@ -8463,14 +8484,27 @@ async function findingOf$1(cli, folder, install, ctx) {
|
|
|
8463
8484
|
fix: `${install.runAs} setup`
|
|
8464
8485
|
};
|
|
8465
8486
|
}
|
|
8466
|
-
|
|
8467
|
-
|
|
8487
|
+
async function hubFindingOf(cli, add, ctx) {
|
|
8488
|
+
const paths = await homeCopiesOf(ctx.files, cli.name, ctx.env);
|
|
8489
|
+
const copies = await Promise.all(paths.map((path) => homeCopyAt(ctx.files, path)));
|
|
8490
|
+
const found = copies.filter((copy) => copy.state !== "missing");
|
|
8491
|
+
return found.length === 0 ? {
|
|
8492
|
+
state: "problem",
|
|
8493
|
+
message: copies.map(describeCopy).join("; "),
|
|
8494
|
+
fix: add
|
|
8495
|
+
} : {
|
|
8496
|
+
state: "ok",
|
|
8497
|
+
message: found.map((copy) => copy.path).join("; ")
|
|
8498
|
+
};
|
|
8499
|
+
}
|
|
8500
|
+
/** A skill the package ships, which `setup` and `update` copy under home unless its Skill Hub installs it. */
|
|
8501
|
+
function homeSkill(cli, { folder, add }, install) {
|
|
8468
8502
|
return {
|
|
8469
8503
|
builtIns: {},
|
|
8470
8504
|
rows: [{
|
|
8471
8505
|
name: "skill",
|
|
8472
8506
|
advisory: true,
|
|
8473
|
-
run: (ctx) => findingOf$1(cli, folder, install, ctx)
|
|
8507
|
+
run: (ctx) => add === void 0 ? findingOf$1(cli, folder, install, ctx) : hubFindingOf(cli, add, ctx)
|
|
8474
8508
|
}]
|
|
8475
8509
|
};
|
|
8476
8510
|
}
|
|
@@ -8543,7 +8577,7 @@ const setupSchema = object({
|
|
|
8543
8577
|
version: string(),
|
|
8544
8578
|
prefix: string(),
|
|
8545
8579
|
installed: boolean(),
|
|
8546
|
-
skill: homeSyncSchema.nullable(),
|
|
8580
|
+
skill: union([homeSyncSchema, object({ add: string() })]).nullable(),
|
|
8547
8581
|
upgrades: array(upgradeSchema),
|
|
8548
8582
|
docs: string().nullable()
|
|
8549
8583
|
});
|
|
@@ -8603,13 +8637,23 @@ function withSetupLine(error, install) {
|
|
|
8603
8637
|
next: [wholeLine(setupLineOf(install))]
|
|
8604
8638
|
}, { cause: error });
|
|
8605
8639
|
}
|
|
8640
|
+
function renderNext(name, docs, skill) {
|
|
8641
|
+
const read = docs === null ? [] : [`docs ${docs}`];
|
|
8642
|
+
if (skill === null || !("add" in skill)) return docs === null ? read : [...read, `Read that folder before using ${name}.`];
|
|
8643
|
+
const then = docs === null ? "" : `, then read that folder before using ${name}`;
|
|
8644
|
+
return [
|
|
8645
|
+
...read,
|
|
8646
|
+
`Install the ${name} skill from its Skill Hub${then}.`,
|
|
8647
|
+
`run: ${skill.add}`
|
|
8648
|
+
];
|
|
8649
|
+
}
|
|
8606
8650
|
function renderSetup(cli, install, setup) {
|
|
8607
|
-
const { version, prefix, docs } = setup;
|
|
8651
|
+
const { version, prefix, docs, skill } = setup;
|
|
8608
8652
|
return [
|
|
8609
8653
|
setup.installed ? `installed ${install.package}@${version} with the npm under ${prefix}` : `${install.package}@${version} is already installed under ${prefix}`,
|
|
8610
|
-
...renderHomeSync(cli.name,
|
|
8654
|
+
...skill !== null && "copies" in skill ? renderHomeSync(cli.name, skill) : [],
|
|
8611
8655
|
...setup.upgrades.map(renderUpgrade),
|
|
8612
|
-
...
|
|
8656
|
+
...renderNext(cli.name, docs, skill)
|
|
8613
8657
|
].join("\n");
|
|
8614
8658
|
}
|
|
8615
8659
|
/** The built-in `setup`, which runs from any copy, such as npx's, so it never asks to be the global one. */
|
|
@@ -8645,7 +8689,7 @@ function setupCommand(cli, install, launch) {
|
|
|
8645
8689
|
latest: await latestVersion(npm),
|
|
8646
8690
|
prefix,
|
|
8647
8691
|
packageDir,
|
|
8648
|
-
copies:
|
|
8692
|
+
copies: await syncedCopiesOf(cli, ctx)
|
|
8649
8693
|
};
|
|
8650
8694
|
},
|
|
8651
8695
|
apply: async (plan, ctx) => {
|
|
@@ -8664,16 +8708,25 @@ function setupCommand(cli, install, launch) {
|
|
|
8664
8708
|
version: installs || from === null ? latest : from,
|
|
8665
8709
|
prefix,
|
|
8666
8710
|
installed: installs,
|
|
8667
|
-
skill,
|
|
8711
|
+
skill: cli.skill?.add === void 0 ? skill : { add: cli.skill.add },
|
|
8668
8712
|
upgrades: installs && from !== null ? await upgradesOf(ctx.files, packageDir, from, latest) : [],
|
|
8669
8713
|
docs: cli.docs === void 0 ? null : join(packageDir, cli.docs)
|
|
8670
8714
|
};
|
|
8671
8715
|
ctx.deliver(setup);
|
|
8672
|
-
if (ctx.signal.aborted)
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8716
|
+
if (ctx.signal.aborted) {
|
|
8717
|
+
const add = cli.skill?.add;
|
|
8718
|
+
if (add === void 0) throw new CliError({
|
|
8719
|
+
code: INTERRUPTED,
|
|
8720
|
+
message: `interrupted, but ${plan.package}@${setup.version} and its skill are already in place`,
|
|
8721
|
+
hint: "nothing to run again"
|
|
8722
|
+
});
|
|
8723
|
+
throw runtimeError({
|
|
8724
|
+
code: INTERRUPTED,
|
|
8725
|
+
message: `interrupted, but ${plan.package}@${setup.version} is already installed`,
|
|
8726
|
+
hint: "run the Next step to install its skill from its Skill Hub",
|
|
8727
|
+
next: [wholeLine(add)]
|
|
8728
|
+
});
|
|
8729
|
+
}
|
|
8677
8730
|
await assertOnPath(cli, prefix, launch, ctx);
|
|
8678
8731
|
return setup;
|
|
8679
8732
|
},
|
|
@@ -8734,7 +8787,7 @@ function updateCommand(cli, install, launch) {
|
|
|
8734
8787
|
latest: await latestVersion(npm),
|
|
8735
8788
|
prefix: copy.prefix,
|
|
8736
8789
|
packageDir: copy.packageDir,
|
|
8737
|
-
copies:
|
|
8790
|
+
copies: await syncedCopiesOf(cli, ctx)
|
|
8738
8791
|
};
|
|
8739
8792
|
},
|
|
8740
8793
|
apply: async (plan, ctx) => {
|
|
@@ -9635,6 +9688,11 @@ function checkTree(commands, path, table) {
|
|
|
9635
9688
|
}
|
|
9636
9689
|
function checkSkill({ name, skill, install }) {
|
|
9637
9690
|
if (skill?.agents !== void 0 && install?.mode === "global") throw new Error(`${name} declares an Agents section on a Global install, which has no repo to write it to`);
|
|
9691
|
+
const add = skill?.add;
|
|
9692
|
+
if (add === void 0) return;
|
|
9693
|
+
if (add.trim() === "") throw new Error(`${name} declares an empty Skill Hub install line`);
|
|
9694
|
+
if (/[\r\n]/.test(add)) throw new Error(`${name} declares a Skill Hub install line holding a line break`);
|
|
9695
|
+
if (install?.mode !== "global") throw new Error(`${name} declares a Skill Hub install line, which only a Global install uses: any other's Skill copy goes in the project`);
|
|
9638
9696
|
}
|
|
9639
9697
|
function checkDocs({ name, docs }) {
|
|
9640
9698
|
if (docs === void 0) return;
|
|
@@ -9732,17 +9790,25 @@ function agentCli(values = {}) {
|
|
|
9732
9790
|
/** How long a run waits for its Side task once its output is written. */
|
|
9733
9791
|
const SIDE_TASK_WAIT_MS = 1e3;
|
|
9734
9792
|
const noSideTask = async () => {};
|
|
9793
|
+
const NO_SIDE_TASK = {
|
|
9794
|
+
end: () => {},
|
|
9795
|
+
finish: noSideTask
|
|
9796
|
+
};
|
|
9735
9797
|
function waitOver() {
|
|
9736
9798
|
return new DOMException(`the side task ran past its ${SIDE_TASK_WAIT_MS} ms wait`, "TimeoutError");
|
|
9737
9799
|
}
|
|
9738
9800
|
function startSideTask(sideTask, runWith, { clock, failed }) {
|
|
9739
|
-
if (sideTask === void 0) return
|
|
9801
|
+
if (sideTask === void 0) return NO_SIDE_TASK;
|
|
9802
|
+
let end = () => {};
|
|
9803
|
+
const ended = new Promise((resolve) => {
|
|
9804
|
+
end = resolve;
|
|
9805
|
+
});
|
|
9740
9806
|
const controller = new AbortController();
|
|
9741
9807
|
const { signal } = controller;
|
|
9742
|
-
const running = (async () => sideTask(runWith(signal)))().catch((error) => {
|
|
9808
|
+
const running = (async () => sideTask(runWith(signal, ended)))().catch((error) => {
|
|
9743
9809
|
if (!(signal.aborted && error === signal.reason)) failed(error);
|
|
9744
9810
|
});
|
|
9745
|
-
|
|
9811
|
+
const finish = async () => {
|
|
9746
9812
|
const waited = new AbortController();
|
|
9747
9813
|
const capped = new Promise((resolve) => after(clock, SIDE_TASK_WAIT_MS, waited.signal, () => {
|
|
9748
9814
|
const over = waitOver();
|
|
@@ -9753,6 +9819,10 @@ function startSideTask(sideTask, runWith, { clock, failed }) {
|
|
|
9753
9819
|
await Promise.race([running, capped]);
|
|
9754
9820
|
waited.abort();
|
|
9755
9821
|
};
|
|
9822
|
+
return {
|
|
9823
|
+
end,
|
|
9824
|
+
finish
|
|
9825
|
+
};
|
|
9756
9826
|
}
|
|
9757
9827
|
//#endregion
|
|
9758
9828
|
//#region ../agent-cli/dist/command-line/suggest.js
|
|
@@ -10492,11 +10562,24 @@ function errorText({ leaves }, format) {
|
|
|
10492
10562
|
const { usageHelp, json } = format;
|
|
10493
10563
|
return usageHelp !== void 0 && !json && leaves.some((leaf) => leaf.error instanceof UsageError) ? `${text}\n${usageHelp}` : text;
|
|
10494
10564
|
}
|
|
10565
|
+
function endOf(failure) {
|
|
10566
|
+
return {
|
|
10567
|
+
exitCode: failure.exitCode,
|
|
10568
|
+
errors: failure.leaves.map((leaf) => runEndErrorOf(leaf.error))
|
|
10569
|
+
};
|
|
10570
|
+
}
|
|
10571
|
+
const SUCCEEDED = {
|
|
10572
|
+
exitCode: SUCCESS_EXIT_CODE,
|
|
10573
|
+
errors: []
|
|
10574
|
+
};
|
|
10495
10575
|
function failedResult(failure, format) {
|
|
10496
10576
|
return {
|
|
10497
|
-
|
|
10498
|
-
|
|
10499
|
-
|
|
10577
|
+
result: {
|
|
10578
|
+
stdout: "",
|
|
10579
|
+
stderr: errorText(failure, format),
|
|
10580
|
+
exitCode: failure.exitCode
|
|
10581
|
+
},
|
|
10582
|
+
end: endOf(failure)
|
|
10500
10583
|
};
|
|
10501
10584
|
}
|
|
10502
10585
|
function stdoutOf({ value, render }, format, spill) {
|
|
@@ -10511,10 +10594,13 @@ function startedOutput(outcome, format, spill) {
|
|
|
10511
10594
|
}, spill);
|
|
10512
10595
|
}
|
|
10513
10596
|
/** A serve Command stopped, or ended itself, after it started: that is how it is meant to end. */
|
|
10514
|
-
const
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
|
|
10597
|
+
const STOPPED = {
|
|
10598
|
+
result: {
|
|
10599
|
+
stdout: "",
|
|
10600
|
+
stderr: "",
|
|
10601
|
+
exitCode: SUCCESS_EXIT_CODE
|
|
10602
|
+
},
|
|
10603
|
+
end: SUCCEEDED
|
|
10518
10604
|
};
|
|
10519
10605
|
async function errorResult(error, format) {
|
|
10520
10606
|
return failedResult(await failureOf(error, format), format);
|
|
@@ -10522,9 +10608,12 @@ async function errorResult(error, format) {
|
|
|
10522
10608
|
async function commandResult(settled, format, spill) {
|
|
10523
10609
|
if (settled.ok) try {
|
|
10524
10610
|
return {
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10611
|
+
result: {
|
|
10612
|
+
stdout: await stdoutOf(settled.outcome, format, spill),
|
|
10613
|
+
stderr: "",
|
|
10614
|
+
exitCode: SUCCESS_EXIT_CODE
|
|
10615
|
+
},
|
|
10616
|
+
end: SUCCEEDED
|
|
10528
10617
|
};
|
|
10529
10618
|
} catch (spillError) {
|
|
10530
10619
|
return errorResult(spillError, format);
|
|
@@ -10538,16 +10627,26 @@ async function commandResult(settled, format, spill) {
|
|
|
10538
10627
|
} catch (rejected) {
|
|
10539
10628
|
return failedResult(withLeaves(failure, rejected, format), format);
|
|
10540
10629
|
}
|
|
10541
|
-
const result = failedResult(failure, format);
|
|
10630
|
+
const { result, end } = failedResult(failure, format);
|
|
10542
10631
|
try {
|
|
10543
10632
|
return {
|
|
10544
|
-
|
|
10545
|
-
|
|
10633
|
+
result: {
|
|
10634
|
+
...result,
|
|
10635
|
+
stdout: await stdoutOf(outcome, format, spill)
|
|
10636
|
+
},
|
|
10637
|
+
end
|
|
10546
10638
|
};
|
|
10547
10639
|
} catch (spillError) {
|
|
10640
|
+
const lost = withLeaves(failure, spillError, format);
|
|
10548
10641
|
return {
|
|
10549
|
-
|
|
10550
|
-
|
|
10642
|
+
result: {
|
|
10643
|
+
...result,
|
|
10644
|
+
stderr: errorText(lost, format)
|
|
10645
|
+
},
|
|
10646
|
+
end: {
|
|
10647
|
+
...endOf(lost),
|
|
10648
|
+
exitCode: result.exitCode
|
|
10649
|
+
}
|
|
10551
10650
|
};
|
|
10552
10651
|
}
|
|
10553
10652
|
}
|
|
@@ -11490,13 +11589,35 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11490
11589
|
json: routed.json,
|
|
11491
11590
|
invocation
|
|
11492
11591
|
};
|
|
11493
|
-
const
|
|
11494
|
-
|
|
11592
|
+
const sideTaskOf = (dryRun) => startSideTask(builtIn ? void 0 : cli.sideTask?.bind(cli), (sideSignal, ended) => {
|
|
11593
|
+
const sideBase = scope.portsBase(sideSignal);
|
|
11594
|
+
const sidePorts = once(() => scope.ports(sideBase));
|
|
11595
|
+
return {
|
|
11596
|
+
...sideBase,
|
|
11597
|
+
command: routed.spec.path,
|
|
11598
|
+
dryRun,
|
|
11599
|
+
ended,
|
|
11600
|
+
get ports() {
|
|
11601
|
+
return sidePorts();
|
|
11602
|
+
}
|
|
11603
|
+
};
|
|
11604
|
+
}, {
|
|
11605
|
+
clock,
|
|
11606
|
+
failed: (error) => {
|
|
11607
|
+
if (isDebug(baseFormat)) write.debug(progressLine(`side task failed: ${traceOf(error)}`, routed.json));
|
|
11608
|
+
}
|
|
11609
|
+
});
|
|
11610
|
+
const ran = ({ result, end }, sideTask) => {
|
|
11611
|
+
sideTask.end(end);
|
|
11495
11612
|
return {
|
|
11496
|
-
result
|
|
11497
|
-
finishSideTask
|
|
11613
|
+
result,
|
|
11614
|
+
finishSideTask: sideTask.finish
|
|
11498
11615
|
};
|
|
11499
11616
|
};
|
|
11617
|
+
const failed = (error) => {
|
|
11618
|
+
control.close();
|
|
11619
|
+
return errorResult(error, format);
|
|
11620
|
+
};
|
|
11500
11621
|
let parsed;
|
|
11501
11622
|
try {
|
|
11502
11623
|
checkOperator(routed.spec, env, rerun);
|
|
@@ -11508,27 +11629,11 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11508
11629
|
});
|
|
11509
11630
|
});
|
|
11510
11631
|
} catch (error) {
|
|
11511
|
-
return failed(error,
|
|
11632
|
+
return ran(await failed(error), sideTaskOf(false));
|
|
11512
11633
|
}
|
|
11513
11634
|
const { input, flags } = parsed;
|
|
11514
11635
|
resumed = command.deadline?.resume(input, rerunOf);
|
|
11515
|
-
const
|
|
11516
|
-
const sideBase = scope.portsBase(sideSignal);
|
|
11517
|
-
const sidePorts = once(() => scope.ports(sideBase));
|
|
11518
|
-
return {
|
|
11519
|
-
...sideBase,
|
|
11520
|
-
command: routed.spec.path,
|
|
11521
|
-
dryRun: flags.dryRun,
|
|
11522
|
-
get ports() {
|
|
11523
|
-
return sidePorts();
|
|
11524
|
-
}
|
|
11525
|
-
};
|
|
11526
|
-
}, {
|
|
11527
|
-
clock,
|
|
11528
|
-
failed: (error) => {
|
|
11529
|
-
if (isDebug(baseFormat)) progress(`side task failed: ${traceOf(error)}`);
|
|
11530
|
-
}
|
|
11531
|
-
});
|
|
11636
|
+
const sideTask = sideTaskOf(flags.dryRun);
|
|
11532
11637
|
let project;
|
|
11533
11638
|
try {
|
|
11534
11639
|
project = builtIn || cli.project === void 0 ? void 0 : projectOf(cli.project, cwd, {
|
|
@@ -11536,7 +11641,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11536
11641
|
rerun
|
|
11537
11642
|
});
|
|
11538
11643
|
} catch (error) {
|
|
11539
|
-
return failed(error,
|
|
11644
|
+
return ran(await failed(error), sideTask);
|
|
11540
11645
|
}
|
|
11541
11646
|
let delivered;
|
|
11542
11647
|
const ctx = contextOf(scope, control, {
|
|
@@ -11572,32 +11677,23 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11572
11677
|
},
|
|
11573
11678
|
outputs
|
|
11574
11679
|
})));
|
|
11575
|
-
if (served) return
|
|
11576
|
-
result: STOPPED_RESULT,
|
|
11577
|
-
finishSideTask
|
|
11578
|
-
};
|
|
11680
|
+
if (served) return ran(STOPPED, sideTask);
|
|
11579
11681
|
settled = {
|
|
11580
11682
|
ok: true,
|
|
11581
11683
|
outcome
|
|
11582
11684
|
};
|
|
11583
11685
|
} catch (error) {
|
|
11584
|
-
if (served && control.signal.aborted) return
|
|
11585
|
-
result: STOPPED_RESULT,
|
|
11586
|
-
finishSideTask
|
|
11587
|
-
};
|
|
11686
|
+
if (served && control.signal.aborted) return ran(STOPPED, sideTask);
|
|
11588
11687
|
settled = {
|
|
11589
11688
|
ok: false,
|
|
11590
11689
|
error,
|
|
11591
11690
|
delivered
|
|
11592
11691
|
};
|
|
11593
11692
|
}
|
|
11594
|
-
return {
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
}, spill),
|
|
11599
|
-
finishSideTask
|
|
11600
|
-
};
|
|
11693
|
+
return ran(await commandResult(settled, {
|
|
11694
|
+
...format,
|
|
11695
|
+
fields: flags.fields
|
|
11696
|
+
}, spill), sideTask);
|
|
11601
11697
|
}
|
|
11602
11698
|
async function noticesOf(cli, launch, ctx) {
|
|
11603
11699
|
await installOf(cli).notice(launch, ctx);
|
|
@@ -11648,7 +11744,16 @@ async function whileStopping(signal, onStop, work) {
|
|
|
11648
11744
|
}
|
|
11649
11745
|
/** Runs the CLI, writes its output, then waits for the Side task it started. */
|
|
11650
11746
|
async function runWithProgress(cli, argv, options, write, stopNotice) {
|
|
11651
|
-
|
|
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;
|
|
11652
11757
|
await write.result(result);
|
|
11653
11758
|
await finishSideTask();
|
|
11654
11759
|
return result;
|
|
@@ -11685,10 +11790,10 @@ async function routeAndRun(cli, argv, options, write, stopNotice) {
|
|
|
11685
11790
|
...format,
|
|
11686
11791
|
json: routed.json
|
|
11687
11792
|
}));
|
|
11688
|
-
case "usage error": return ranAlone(errorResult(routed.error, {
|
|
11793
|
+
case "usage error": return ranAlone((await errorResult(routed.error, {
|
|
11689
11794
|
...misused(routed.node),
|
|
11690
11795
|
json: routed.json
|
|
11691
|
-
}));
|
|
11796
|
+
})).result);
|
|
11692
11797
|
case "command": return whileStopping(options.signal, stopNotice === void 0 ? void 0 : () => write.stderr(stopLine(stopNotice, routed.json)), () => runCommand(cli, routed, options, write, misused(routed.spec)));
|
|
11693
11798
|
}
|
|
11694
11799
|
}
|
|
@@ -11751,7 +11856,7 @@ async function runProcess(cli) {
|
|
|
11751
11856
|
//#region package.json
|
|
11752
11857
|
var package_default = {
|
|
11753
11858
|
name: "@duyquangnvx/create-agent-cli",
|
|
11754
|
-
version: "0.
|
|
11859
|
+
version: "0.30.1",
|
|
11755
11860
|
description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
11756
11861
|
type: "module",
|
|
11757
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.
|
|
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.
|
|
24
|
+
"@duyquangnvx/agent-cli": "0.30.1",
|
|
25
25
|
"tsdown": "0.23.0",
|
|
26
26
|
"zod": "4.6.7"
|
|
27
27
|
},
|