@duyquangnvx/create-agent-cli 0.20.0 → 0.21.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 +15 -0
- package/dist/bin.js +1032 -978
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { constants, homedir, tmpdir } from "node:os";
|
|
2
4
|
import { parseArgs, parseEnv } from "node:util";
|
|
3
|
-
import {
|
|
4
|
-
import { constants, existsSync, readFileSync, realpathSync } from "node:fs";
|
|
5
|
-
import { constants as constants$1, homedir, tmpdir } from "node:os";
|
|
5
|
+
import { constants as constants$1, existsSync, readFileSync, realpathSync } from "node:fs";
|
|
6
6
|
import { access, chmod, link, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
7
|
-
import {
|
|
7
|
+
import { basename, delimiter, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
8
8
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
//#region ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/util.js
|
|
@@ -5740,40 +5740,6 @@ 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
|
|
5777
5743
|
//#region ../agent-cli/dist/command-line/tokenize.js
|
|
5778
5744
|
const NEGATIVE_NUMBER = /^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/;
|
|
5779
5745
|
function isOptionLike(word) {
|
|
@@ -5926,6 +5892,9 @@ function wholeLine(line) {
|
|
|
5926
5892
|
function isWords(step) {
|
|
5927
5893
|
return Array.isArray(step);
|
|
5928
5894
|
}
|
|
5895
|
+
function isRerun(step) {
|
|
5896
|
+
return !isWords(step) && step.kind === "rerun";
|
|
5897
|
+
}
|
|
5929
5898
|
/** The words after the Run-as, or `undefined` for a line given whole. */
|
|
5930
5899
|
function wordsOf(step) {
|
|
5931
5900
|
if (isWords(step)) return step;
|
|
@@ -6081,1006 +6050,1047 @@ function formatRuntimeError(error, format) {
|
|
|
6081
6050
|
return errors.map((each) => errorLines(each, format)).join("");
|
|
6082
6051
|
}
|
|
6083
6052
|
//#endregion
|
|
6084
|
-
//#region ../agent-cli/dist/
|
|
6085
|
-
|
|
6086
|
-
function
|
|
6087
|
-
return
|
|
6088
|
-
}
|
|
6089
|
-
function endOfInputKeys(platform) {
|
|
6090
|
-
return platform === "win32" ? "Ctrl-Z then Enter" : "Ctrl-D";
|
|
6091
|
-
}
|
|
6092
|
-
/** The progress line shown before reading a terminal, so a person isn't left facing silence. */
|
|
6093
|
-
function readingLine(name, platform) {
|
|
6094
|
-
return `reading <${name}> from stdin; end it with ${endOfInputKeys(platform)}`;
|
|
6095
|
-
}
|
|
6096
|
-
function isTerminal(source) {
|
|
6097
|
-
return typeof source !== "string" && "isTTY" in source && source.isTTY === true;
|
|
6053
|
+
//#region ../agent-cli/dist/tail.js
|
|
6054
|
+
/** The last `count` lines of a child's output, trimmed; empty when it wrote nothing. */
|
|
6055
|
+
function lastLines(output, count) {
|
|
6056
|
+
return output.trim().split("\n").slice(-count).join("\n");
|
|
6098
6057
|
}
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
return new CliError({
|
|
6104
|
-
code: STDIN_STALLED,
|
|
6105
|
-
message: `stdin sent nothing for ${STDIN_IDLE_MS} ms`,
|
|
6106
|
-
hint: `stdin never ended: close the pipe or heredoc that feeds \`${callerLine}\`, pass the text as an argument instead, or write a slow producer's output to a file and redirect it in`
|
|
6107
|
-
});
|
|
6058
|
+
//#endregion
|
|
6059
|
+
//#region ../agent-cli/dist/define.js
|
|
6060
|
+
function isCommand(node) {
|
|
6061
|
+
return typeof node.effect === "string";
|
|
6108
6062
|
}
|
|
6063
|
+
//#endregion
|
|
6064
|
+
//#region ../agent-cli/dist/context/declared.js
|
|
6109
6065
|
/**
|
|
6110
|
-
*
|
|
6111
|
-
* `
|
|
6066
|
+
* What the CLI declares itself (`own`) and every Command under `commands` declares, each name once,
|
|
6067
|
+
* for a `doctor` row: a Command requires only its own, but `doctor` checks them all.
|
|
6112
6068
|
*/
|
|
6113
|
-
function
|
|
6114
|
-
|
|
6115
|
-
return new
|
|
6116
|
-
const chunks = [];
|
|
6117
|
-
let idleWindow = new AbortController();
|
|
6118
|
-
const watchIdle = () => {
|
|
6119
|
-
idleWindow.abort();
|
|
6120
|
-
idleWindow = new AbortController();
|
|
6121
|
-
if (isTerminal(source)) return;
|
|
6122
|
-
after(idle.clock, STDIN_IDLE_MS, idleWindow.signal, () => {
|
|
6123
|
-
stop();
|
|
6124
|
-
reject(idle.stalled());
|
|
6125
|
-
});
|
|
6126
|
-
};
|
|
6127
|
-
const onData = (chunk) => {
|
|
6128
|
-
chunks.push(chunk instanceof Uint8Array ? chunk : Buffer.from(String(chunk)));
|
|
6129
|
-
watchIdle();
|
|
6130
|
-
};
|
|
6131
|
-
const onEnd = () => {
|
|
6132
|
-
stop();
|
|
6133
|
-
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
6134
|
-
};
|
|
6135
|
-
const onError = (error) => {
|
|
6136
|
-
stop();
|
|
6137
|
-
reject(error);
|
|
6138
|
-
};
|
|
6139
|
-
const onAbort = () => {
|
|
6140
|
-
stop();
|
|
6141
|
-
reject(signal.reason);
|
|
6142
|
-
};
|
|
6143
|
-
const stop = () => {
|
|
6144
|
-
source.off("data", onData).off("end", onEnd).off("error", onError).pause();
|
|
6145
|
-
signal.removeEventListener("abort", onAbort);
|
|
6146
|
-
idleWindow.abort();
|
|
6147
|
-
};
|
|
6148
|
-
if (signal.aborted) {
|
|
6149
|
-
reject(signal.reason);
|
|
6150
|
-
return;
|
|
6151
|
-
}
|
|
6152
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
6153
|
-
watchIdle();
|
|
6154
|
-
source.on("data", onData).once("end", onEnd).once("error", onError);
|
|
6155
|
-
});
|
|
6069
|
+
function declaredIn(own, commands, needsOf) {
|
|
6070
|
+
const walk = (node) => isCommand(node) ? [...needsOf(node) ?? []] : Object.values(node).flatMap(walk);
|
|
6071
|
+
return [...new Map([...own, ...walk(commands)].map((need) => [need.name, need])).values()];
|
|
6156
6072
|
}
|
|
6157
6073
|
//#endregion
|
|
6158
|
-
//#region ../agent-cli/dist/
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
return key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
6074
|
+
//#region ../agent-cli/dist/context/tools.js
|
|
6075
|
+
const TOOL_MISSING = "TOOL_MISSING";
|
|
6076
|
+
const WINDOWS_EXTENSIONS = ".COM;.EXE;.BAT;.CMD";
|
|
6077
|
+
function pathOf(env) {
|
|
6078
|
+
return env[Object.keys(env).find((name) => name.toUpperCase() === "PATH") ?? "PATH"] ?? "";
|
|
6164
6079
|
}
|
|
6165
|
-
function
|
|
6166
|
-
|
|
6167
|
-
return schema instanceof ZodBoolean ? "boolean" : "string";
|
|
6080
|
+
function extensionsOf(env) {
|
|
6081
|
+
return process.platform === "win32" ? (env.PATHEXT ?? WINDOWS_EXTENSIONS).split(";") : [""];
|
|
6168
6082
|
}
|
|
6169
|
-
function
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
if (current instanceof ZodDefault || current instanceof ZodPrefault) defaultValue = current.def.defaultValue;
|
|
6177
|
-
required = false;
|
|
6178
|
-
current = current.unwrap();
|
|
6179
|
-
description ??= current.description;
|
|
6180
|
-
stdin ||= isStdinSchema(current);
|
|
6083
|
+
async function isExecutable(file) {
|
|
6084
|
+
try {
|
|
6085
|
+
await access(file, constants$1.X_OK);
|
|
6086
|
+
return true;
|
|
6087
|
+
} catch (error) {
|
|
6088
|
+
if (isNothingThere(error) || errnoOf(error) === "EACCES") return false;
|
|
6089
|
+
throw error;
|
|
6181
6090
|
}
|
|
6182
|
-
const variadic = current instanceof ZodArray;
|
|
6183
|
-
if (current instanceof ZodArray) current = current.element;
|
|
6184
|
-
return {
|
|
6185
|
-
type: typeOf(current),
|
|
6186
|
-
required,
|
|
6187
|
-
defaultValue,
|
|
6188
|
-
description,
|
|
6189
|
-
choices: current instanceof ZodEnum ? current.options.map(String) : void 0,
|
|
6190
|
-
variadic,
|
|
6191
|
-
stdin
|
|
6192
|
-
};
|
|
6193
6091
|
}
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6092
|
+
/** The file names `name` may have on `PATH`: itself when it already ends in one of the extensions. */
|
|
6093
|
+
function fileNamesOf(name, env) {
|
|
6094
|
+
const extensions = extensionsOf(env);
|
|
6095
|
+
const own = extname(name).toUpperCase();
|
|
6096
|
+
return own !== "" && extensions.some((extension) => extension.toUpperCase() === own) ? [name] : extensions.map((extension) => `${name}${extension}`);
|
|
6198
6097
|
}
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
const
|
|
6202
|
-
return
|
|
6203
|
-
key,
|
|
6204
|
-
name: key,
|
|
6205
|
-
schema,
|
|
6206
|
-
...described,
|
|
6207
|
-
label,
|
|
6208
|
-
usage: label
|
|
6209
|
-
};
|
|
6098
|
+
/** Where `name` is found on `PATH` from `env`, or undefined. */
|
|
6099
|
+
async function findTool(name, env) {
|
|
6100
|
+
const candidates = pathOf(env).split(delimiter).filter((folder) => folder !== "").flatMap((folder) => fileNamesOf(name, env).map((file) => join(folder, file)));
|
|
6101
|
+
for (const file of candidates) if (await isExecutable(file)) return file;
|
|
6210
6102
|
}
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
const
|
|
6214
|
-
const
|
|
6215
|
-
|
|
6216
|
-
const
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
label,
|
|
6224
|
-
usage
|
|
6225
|
-
};
|
|
6103
|
+
/** Fails with `TOOL_MISSING`, naming every missing tool at once, unless all are on `PATH`. */
|
|
6104
|
+
async function checkTools(tools, env, rerun) {
|
|
6105
|
+
const found = await Promise.all(tools.map((tool) => findTool(tool.name, env)));
|
|
6106
|
+
const missing = tools.filter((_tool, index) => found[index] === void 0);
|
|
6107
|
+
if (missing.length === 0) return;
|
|
6108
|
+
const names = missing.map(({ name }) => name).join(", ");
|
|
6109
|
+
throw runtimeError({
|
|
6110
|
+
code: TOOL_MISSING,
|
|
6111
|
+
message: `${missing.length === 1 ? "a tool it runs is" : "tools it runs are"} not on PATH: ${names}`,
|
|
6112
|
+
hint: "run the Next steps in order: each install, then the command again",
|
|
6113
|
+
next: [...missing.map(({ install }) => wholeLine(install)), rerun]
|
|
6114
|
+
});
|
|
6226
6115
|
}
|
|
6227
|
-
|
|
6228
|
-
|
|
6116
|
+
/** One `doctor` row per distinct tool the CLI and its Commands declare, with its install command as the fix. */
|
|
6117
|
+
function toolRows(tools, commands) {
|
|
6118
|
+
return declaredIn(tools, commands, (command) => command.tools).map(({ name, install }) => ({
|
|
6119
|
+
name,
|
|
6120
|
+
run: async (ctx) => {
|
|
6121
|
+
const file = await findTool(name, ctx.env);
|
|
6122
|
+
return file === void 0 ? {
|
|
6123
|
+
state: "problem",
|
|
6124
|
+
message: `${name} is not on PATH`,
|
|
6125
|
+
fix: install
|
|
6126
|
+
} : {
|
|
6127
|
+
state: "ok",
|
|
6128
|
+
message: `found at ${file}`
|
|
6129
|
+
};
|
|
6130
|
+
}
|
|
6131
|
+
}));
|
|
6229
6132
|
}
|
|
6230
|
-
|
|
6231
|
-
|
|
6133
|
+
//#endregion
|
|
6134
|
+
//#region ../agent-cli/dist/context/spawn.js
|
|
6135
|
+
const TOOL_FAILED = "TOOL_FAILED";
|
|
6136
|
+
const STDERR_TAIL_LINES$1 = 20;
|
|
6137
|
+
const SIGNAL_EXIT_BASE = 128;
|
|
6138
|
+
const BATCH_FILE = /\.(?:cmd|bat)$/i;
|
|
6139
|
+
const CMD_SHIM = /\.cmd$/i;
|
|
6140
|
+
const CMD_META = /([()\][%!^"`<>&|;, *?])/g;
|
|
6141
|
+
const PATH_SEPARATOR = /[\\/]/;
|
|
6142
|
+
const TRAILING_CR = /\r$/;
|
|
6143
|
+
const LINE_END = /\r\n|\r(?!$)|\n/;
|
|
6144
|
+
function isWhole(input) {
|
|
6145
|
+
return typeof input === "string" || input instanceof Uint8Array;
|
|
6232
6146
|
}
|
|
6233
|
-
function
|
|
6234
|
-
return
|
|
6147
|
+
function escapedForCmd(text) {
|
|
6148
|
+
return text.replace(CMD_META, "^$1");
|
|
6235
6149
|
}
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
if (misplaced.length > 0) throw new Error(`Command '${name}' declares array arg ${keysOf(misplaced)} before its last arg; only the last arg may be an array`);
|
|
6150
|
+
function batchArgument(arg, shim) {
|
|
6151
|
+
const escaped = escapedForCmd(`"${arg.replace(/(\\*)"/g, "$1$1\\\"").replace(/(\\*)$/, "$1$1")}"`);
|
|
6152
|
+
return shim ? escapedForCmd(escaped) : escaped;
|
|
6240
6153
|
}
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6154
|
+
function batchCommandLine(command, file, args) {
|
|
6155
|
+
const shim = CMD_SHIM.test(file);
|
|
6156
|
+
return [escapedForCmd(command), ...args.map((arg) => batchArgument(arg, shim))].join(" ");
|
|
6157
|
+
}
|
|
6158
|
+
async function windowsLaunch(command, args, env) {
|
|
6159
|
+
const file = PATH_SEPARATOR.test(command) ? command : await findTool(command, env);
|
|
6160
|
+
if (file === void 0 || !BATCH_FILE.test(file)) return {
|
|
6161
|
+
command,
|
|
6162
|
+
args,
|
|
6163
|
+
shell: false
|
|
6164
|
+
};
|
|
6165
|
+
return {
|
|
6166
|
+
command: batchCommandLine(command, file, args),
|
|
6167
|
+
args: [],
|
|
6168
|
+
shell: true
|
|
6169
|
+
};
|
|
6170
|
+
}
|
|
6171
|
+
function stop(child) {
|
|
6172
|
+
if (process.platform !== "win32" || child.pid === void 0) {
|
|
6173
|
+
child.kill();
|
|
6174
|
+
return Promise.resolve();
|
|
6249
6175
|
}
|
|
6176
|
+
const pid = String(child.pid);
|
|
6177
|
+
return new Promise((resolve) => {
|
|
6178
|
+
const taskkill = spawn("taskkill", [
|
|
6179
|
+
"/pid",
|
|
6180
|
+
pid,
|
|
6181
|
+
"/t",
|
|
6182
|
+
"/f"
|
|
6183
|
+
], {
|
|
6184
|
+
stdio: "ignore",
|
|
6185
|
+
windowsHide: true
|
|
6186
|
+
});
|
|
6187
|
+
taskkill.once("error", () => {
|
|
6188
|
+
child.kill();
|
|
6189
|
+
resolve();
|
|
6190
|
+
});
|
|
6191
|
+
taskkill.once("exit", () => resolve());
|
|
6192
|
+
});
|
|
6250
6193
|
}
|
|
6251
|
-
|
|
6252
|
-
|
|
6194
|
+
/** Calls `onLine` with each complete line pushed, and with the unterminated rest at the end. */
|
|
6195
|
+
function lineSplitter(onLine) {
|
|
6196
|
+
let rest = "";
|
|
6197
|
+
return {
|
|
6198
|
+
push(text) {
|
|
6199
|
+
if (onLine === void 0) return;
|
|
6200
|
+
const lines = (rest + text).split(LINE_END);
|
|
6201
|
+
rest = lines.pop() ?? "";
|
|
6202
|
+
for (const line of lines) onLine(line);
|
|
6203
|
+
},
|
|
6204
|
+
end() {
|
|
6205
|
+
if (rest !== "") onLine?.(rest.replace(TRAILING_CR, ""));
|
|
6206
|
+
rest = "";
|
|
6207
|
+
}
|
|
6208
|
+
};
|
|
6253
6209
|
}
|
|
6254
|
-
/**
|
|
6255
|
-
function
|
|
6256
|
-
|
|
6257
|
-
|
|
6210
|
+
/** `onLine`, passing what it throws to `fail` and called no more once `failed()`. */
|
|
6211
|
+
function guarded(onLine, failed, fail) {
|
|
6212
|
+
if (onLine === void 0) return void 0;
|
|
6213
|
+
return (line) => {
|
|
6214
|
+
if (failed()) return;
|
|
6215
|
+
try {
|
|
6216
|
+
onLine(line);
|
|
6217
|
+
} catch (error) {
|
|
6218
|
+
fail(error);
|
|
6219
|
+
}
|
|
6220
|
+
};
|
|
6258
6221
|
}
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
const
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6222
|
+
/** Settles once the child has taken what was written, or its stdin has closed. */
|
|
6223
|
+
function drained(stdin) {
|
|
6224
|
+
return new Promise((resolve) => {
|
|
6225
|
+
const done = () => {
|
|
6226
|
+
stdin.off("drain", done);
|
|
6227
|
+
stdin.off("close", done);
|
|
6228
|
+
resolve();
|
|
6229
|
+
};
|
|
6230
|
+
stdin.on("drain", done);
|
|
6231
|
+
stdin.on("close", done);
|
|
6269
6232
|
});
|
|
6270
6233
|
}
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6234
|
+
/**
|
|
6235
|
+
* Writes `input` to the child's stdin and ends it, stopping once the child is gone or the run
|
|
6236
|
+
* aborts.
|
|
6237
|
+
*/
|
|
6238
|
+
async function feed(stdin, input, signal) {
|
|
6239
|
+
stdin.on("error", () => void 0);
|
|
6240
|
+
const chunks = isWhole(input) ? [input] : input;
|
|
6241
|
+
for await (const chunk of chunks) {
|
|
6242
|
+
if (signal.aborted || stdin.destroyed) return;
|
|
6243
|
+
if (!stdin.write(chunk)) await drained(stdin);
|
|
6244
|
+
}
|
|
6245
|
+
stdin.end();
|
|
6246
|
+
}
|
|
6247
|
+
/**
|
|
6248
|
+
* Runs a child process to its end; it is killed on abort, and the promise settles only once it has
|
|
6249
|
+
* exited. A `detached` child leaves the terminal's process group on POSIX, and so its Ctrl-C.
|
|
6250
|
+
*/
|
|
6251
|
+
async function spawnChild(command, args, { cwd, env, signal, onLine, stdin, detached = false }) {
|
|
6252
|
+
signal.throwIfAborted();
|
|
6253
|
+
const launch = await launchOf(command, args, env);
|
|
6254
|
+
signal.throwIfAborted();
|
|
6255
|
+
return new Promise((resolve, reject) => {
|
|
6256
|
+
const child = spawn(launch.command, launch.args, {
|
|
6257
|
+
cwd,
|
|
6258
|
+
env,
|
|
6259
|
+
shell: launch.shell,
|
|
6260
|
+
windowsHide: true,
|
|
6261
|
+
stdio: [
|
|
6262
|
+
stdin === void 0 ? "ignore" : "pipe",
|
|
6263
|
+
"pipe",
|
|
6264
|
+
"pipe"
|
|
6265
|
+
],
|
|
6266
|
+
detached: detached && process.platform !== "win32"
|
|
6267
|
+
});
|
|
6268
|
+
let stopped = Promise.resolve();
|
|
6269
|
+
const kill = () => {
|
|
6270
|
+
stopped = stop(child);
|
|
6271
|
+
};
|
|
6272
|
+
signal.addEventListener("abort", kill, { once: true });
|
|
6273
|
+
let callerFailure;
|
|
6274
|
+
const fail = (error) => {
|
|
6275
|
+
if (callerFailure !== void 0) return;
|
|
6276
|
+
callerFailure = { error };
|
|
6277
|
+
kill();
|
|
6278
|
+
};
|
|
6279
|
+
let stdout = "";
|
|
6280
|
+
let stderr = "";
|
|
6281
|
+
const lines = guarded(onLine, () => callerFailure !== void 0, fail);
|
|
6282
|
+
const stdoutLines = lineSplitter(lines);
|
|
6283
|
+
const stderrLines = lineSplitter(lines);
|
|
6284
|
+
child.stdout?.setEncoding("utf8").on("data", (chunk) => {
|
|
6285
|
+
stdout += chunk;
|
|
6286
|
+
stdoutLines.push(chunk);
|
|
6287
|
+
});
|
|
6288
|
+
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
6289
|
+
stderr += chunk;
|
|
6290
|
+
stderrLines.push(chunk);
|
|
6291
|
+
});
|
|
6292
|
+
if (stdin !== void 0 && child.stdin !== null) feed(child.stdin, stdin, signal).catch(fail);
|
|
6293
|
+
child.on("error", (error) => {
|
|
6294
|
+
signal.removeEventListener("abort", kill);
|
|
6295
|
+
reject(error);
|
|
6296
|
+
});
|
|
6297
|
+
child.on("close", (code, killedBy) => {
|
|
6298
|
+
signal.removeEventListener("abort", kill);
|
|
6299
|
+
if (signal.aborted) {
|
|
6300
|
+
stopped.then(() => reject(signal.reason));
|
|
6301
|
+
return;
|
|
6302
|
+
}
|
|
6303
|
+
stdoutLines.end();
|
|
6304
|
+
stderrLines.end();
|
|
6305
|
+
if (callerFailure !== void 0) {
|
|
6306
|
+
const { error } = callerFailure;
|
|
6307
|
+
stopped.then(() => reject(error));
|
|
6308
|
+
return;
|
|
6309
|
+
}
|
|
6310
|
+
resolve({
|
|
6311
|
+
stdout,
|
|
6312
|
+
stderr,
|
|
6313
|
+
exitCode: code ?? SIGNAL_EXIT_BASE + (killedBy === null ? 0 : constants.signals[killedBy])
|
|
6314
|
+
});
|
|
6315
|
+
});
|
|
6278
6316
|
});
|
|
6279
6317
|
}
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6318
|
+
async function launchOf(command, args, env) {
|
|
6319
|
+
return process.platform === "win32" ? windowsLaunch(command, args, env) : {
|
|
6320
|
+
command,
|
|
6321
|
+
args,
|
|
6322
|
+
shell: false
|
|
6323
|
+
};
|
|
6324
|
+
}
|
|
6325
|
+
/**
|
|
6326
|
+
* Starts a child in its own process group (its own console on Windows) with no stdio, and settles
|
|
6327
|
+
* once it has started; the parent neither waits for it nor keeps it from exiting.
|
|
6328
|
+
*/
|
|
6329
|
+
const realSpawnDetached = async (command, args, { cwd, env }) => {
|
|
6330
|
+
const launch = await launchOf(command, args, env);
|
|
6331
|
+
return new Promise((resolve, reject) => {
|
|
6332
|
+
const child = spawn(launch.command, launch.args, {
|
|
6333
|
+
cwd,
|
|
6334
|
+
env,
|
|
6335
|
+
shell: launch.shell,
|
|
6336
|
+
windowsHide: true,
|
|
6337
|
+
stdio: "ignore",
|
|
6338
|
+
detached: true
|
|
6339
|
+
});
|
|
6340
|
+
child.unref();
|
|
6341
|
+
child.once("error", reject);
|
|
6342
|
+
child.once("spawn", () => {
|
|
6343
|
+
child.off("error", reject);
|
|
6344
|
+
const { pid } = child;
|
|
6345
|
+
if (pid === void 0) reject(/* @__PURE__ */ new Error(`${command} started without a pid`));
|
|
6346
|
+
else resolve({ pid });
|
|
6347
|
+
});
|
|
6285
6348
|
});
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6349
|
+
};
|
|
6350
|
+
const realSpawn = (command, args, options) => spawnChild(command, args, options);
|
|
6351
|
+
function failedError(command, { exitCode, stderr }) {
|
|
6352
|
+
const tail = lastLines(stderr, STDERR_TAIL_LINES$1);
|
|
6353
|
+
return new CliError({
|
|
6354
|
+
code: TOOL_FAILED,
|
|
6355
|
+
message: tail === "" ? `${command} exited with ${exitCode} and wrote nothing to stderr` : `${command} exited with ${exitCode}; its stderr ends:\n${tail}`,
|
|
6356
|
+
hint: `the output of ${command} above says why it failed`
|
|
6290
6357
|
});
|
|
6291
6358
|
}
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6359
|
+
var MissingToolError = class extends CliError {
|
|
6360
|
+
tool;
|
|
6361
|
+
constructor(tool, rerun, cause) {
|
|
6362
|
+
super({
|
|
6363
|
+
code: TOOL_MISSING,
|
|
6364
|
+
message: `${tool} could not be started: it is not on PATH`,
|
|
6365
|
+
hint: `install ${tool}, then run the Next step`,
|
|
6366
|
+
next: [rerun]
|
|
6367
|
+
}, { cause });
|
|
6368
|
+
this.tool = tool;
|
|
6369
|
+
}
|
|
6370
|
+
};
|
|
6371
|
+
function missingToolOf(error) {
|
|
6372
|
+
return error instanceof MissingToolError ? error.tool : void 0;
|
|
6373
|
+
}
|
|
6374
|
+
function startError(command, error, { fileError, rerun }) {
|
|
6375
|
+
if (isMissingFile(error)) return new MissingToolError(command, rerun, error);
|
|
6376
|
+
return fileError(error, {
|
|
6377
|
+
path: command,
|
|
6378
|
+
access: "read"
|
|
6299
6379
|
});
|
|
6300
6380
|
}
|
|
6301
|
-
function
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
}
|
|
6308
|
-
//#endregion
|
|
6309
|
-
//#region ../agent-cli/dist/list.js
|
|
6310
|
-
const UNKNOWN_FIELD = "UNKNOWN_FIELD";
|
|
6311
|
-
const DEFAULT_PAGE_LIMIT = 20;
|
|
6312
|
-
const FIELDS_FLAG = "--fields";
|
|
6313
|
-
const SEPARATOR = ",";
|
|
6314
|
-
const COLUMN_SEPARATOR = " ";
|
|
6315
|
-
const UNSAFE_IN_COLUMN = /[\t\n\r]/;
|
|
6316
|
-
const PAGE_INPUT = object({
|
|
6317
|
-
limit: number().int().positive().default(DEFAULT_PAGE_LIMIT),
|
|
6318
|
-
cursor: string().min(1).optional()
|
|
6319
|
-
});
|
|
6320
|
-
var Page = class {
|
|
6321
|
-
items;
|
|
6322
|
-
nextCursor;
|
|
6323
|
-
constructor(items, nextCursor) {
|
|
6324
|
-
this.items = items;
|
|
6325
|
-
this.nextCursor = nextCursor;
|
|
6381
|
+
async function* reported(stream, onError) {
|
|
6382
|
+
try {
|
|
6383
|
+
yield* stream;
|
|
6384
|
+
} catch (error) {
|
|
6385
|
+
onError(error);
|
|
6386
|
+
throw error;
|
|
6326
6387
|
}
|
|
6327
|
-
};
|
|
6328
|
-
const PAGE_FLAG_DESCRIPTIONS = {
|
|
6329
|
-
limit: `how many rows to show, a positive whole number (default: ${DEFAULT_PAGE_LIMIT})`,
|
|
6330
|
-
cursor: "where to start, as given by the previous page's next"
|
|
6331
|
-
};
|
|
6332
|
-
function pageFlag(key) {
|
|
6333
|
-
const field = optionField(key, PAGE_INPUT.shape[key]);
|
|
6334
|
-
return {
|
|
6335
|
-
flag: field.label,
|
|
6336
|
-
usage: field.usage,
|
|
6337
|
-
description: PAGE_FLAG_DESCRIPTIONS[key],
|
|
6338
|
-
takes: "optional value",
|
|
6339
|
-
schema: field.schema
|
|
6340
|
-
};
|
|
6341
6388
|
}
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6389
|
+
/** `input`, a stream passing each error it throws to `onError` before the reader sees it. */
|
|
6390
|
+
function reporting(input, onError) {
|
|
6391
|
+
return input === void 0 || isWhole(input) ? input : reported(input, onError);
|
|
6392
|
+
}
|
|
6393
|
+
/** `onLine`, passing each error it throws to `onError` before its caller sees it. */
|
|
6394
|
+
function reportingLines(onLine, onError) {
|
|
6395
|
+
if (onLine === void 0) return void 0;
|
|
6396
|
+
return (line) => {
|
|
6397
|
+
try {
|
|
6398
|
+
onLine(line);
|
|
6399
|
+
} catch (error) {
|
|
6400
|
+
onError(error);
|
|
6401
|
+
throw error;
|
|
6402
|
+
}
|
|
6349
6403
|
};
|
|
6350
6404
|
}
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
};
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6405
|
+
function contextSpawn(run, defaults, scope) {
|
|
6406
|
+
return async (command, args = [], options = {}) => {
|
|
6407
|
+
let callerFailure;
|
|
6408
|
+
const caller = (error) => {
|
|
6409
|
+
callerFailure = { error };
|
|
6410
|
+
};
|
|
6411
|
+
let result;
|
|
6412
|
+
try {
|
|
6413
|
+
result = await run(command, args, {
|
|
6414
|
+
cwd: options.cwd ?? defaults.cwd,
|
|
6415
|
+
env: options.env ?? defaults.env,
|
|
6416
|
+
signal: defaults.signal,
|
|
6417
|
+
onLine: reportingLines(options.onLine, caller),
|
|
6418
|
+
stdin: reporting(options.stdin, caller)
|
|
6419
|
+
});
|
|
6420
|
+
} catch (error) {
|
|
6421
|
+
throw callerFailure?.error === error ? error : startError(command, error, scope);
|
|
6422
|
+
}
|
|
6423
|
+
if (options.check === true && result.exitCode !== 0) throw failedError(command, result);
|
|
6424
|
+
return result;
|
|
6368
6425
|
};
|
|
6369
6426
|
}
|
|
6370
|
-
|
|
6371
|
-
|
|
6427
|
+
/** `ctx.spawnDetached`: `run` bound to the run's cwd and env, its start failures classified by errno. */
|
|
6428
|
+
function contextSpawnDetached(run, defaults, scope) {
|
|
6429
|
+
return async (command, args = [], options = {}) => {
|
|
6430
|
+
defaults.signal.throwIfAborted();
|
|
6431
|
+
try {
|
|
6432
|
+
return await run(command, args, {
|
|
6433
|
+
cwd: options.cwd ?? defaults.cwd,
|
|
6434
|
+
env: options.env ?? defaults.env
|
|
6435
|
+
});
|
|
6436
|
+
} catch (error) {
|
|
6437
|
+
throw startError(command, error, scope);
|
|
6438
|
+
}
|
|
6439
|
+
};
|
|
6372
6440
|
}
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6441
|
+
//#endregion
|
|
6442
|
+
//#region ../agent-cli/dist/clock.js
|
|
6443
|
+
const realClock = {
|
|
6444
|
+
now: () => Date.now(),
|
|
6445
|
+
sleep: (ms, signal) => new Promise((resolve, reject) => {
|
|
6446
|
+
if (signal?.aborted === true) {
|
|
6447
|
+
reject(signal.reason);
|
|
6448
|
+
return;
|
|
6449
|
+
}
|
|
6450
|
+
const onAbort = () => {
|
|
6451
|
+
clearTimeout(timer);
|
|
6452
|
+
reject(signal?.reason);
|
|
6453
|
+
};
|
|
6454
|
+
const timer = setTimeout(() => {
|
|
6455
|
+
signal?.removeEventListener("abort", onAbort);
|
|
6456
|
+
resolve();
|
|
6457
|
+
}, ms);
|
|
6458
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
6459
|
+
})
|
|
6460
|
+
};
|
|
6461
|
+
/** Runs `then` once `ms` have passed on `clock`; aborting `cancel` drops it. */
|
|
6462
|
+
function after(clock, ms, cancel, then) {
|
|
6463
|
+
clock.sleep(ms, cancel).then(then, (error) => {
|
|
6464
|
+
if (!cancel.aborted) throw error;
|
|
6389
6465
|
});
|
|
6390
|
-
return names;
|
|
6391
|
-
}
|
|
6392
|
-
function fieldOf(row, field) {
|
|
6393
|
-
return typeof row === "object" && row !== null ? row[field] : void 0;
|
|
6394
|
-
}
|
|
6395
|
-
function projected(rows, fields) {
|
|
6396
|
-
return rows.map((row) => Object.fromEntries(fields.map((field) => [field, fieldOf(row, field)])));
|
|
6397
|
-
}
|
|
6398
|
-
function cell(value) {
|
|
6399
|
-
if (value === void 0 || value === null) return "";
|
|
6400
|
-
return typeof value === "string" && !UNSAFE_IN_COLUMN.test(value) ? value : JSON.stringify(value);
|
|
6401
|
-
}
|
|
6402
|
-
/** Selected rows as text: a header line of the field names, then one tab-separated line per row. */
|
|
6403
|
-
function renderRows(rows, fields) {
|
|
6404
|
-
return `${[fields, ...rows.map((row) => fields.map((field) => cell(row[field])))].map((line) => line.join(COLUMN_SEPARATOR)).join("\n")}\n`;
|
|
6405
6466
|
}
|
|
6406
|
-
function
|
|
6407
|
-
|
|
6408
|
-
|
|
6467
|
+
async function poll(clock, check, intervalMs, signal) {
|
|
6468
|
+
for (;;) {
|
|
6469
|
+
signal.throwIfAborted();
|
|
6470
|
+
const value = await check();
|
|
6471
|
+
if (value !== void 0) return value;
|
|
6472
|
+
await clock.sleep(intervalMs, signal);
|
|
6473
|
+
}
|
|
6409
6474
|
}
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
words: changedArgv(invocation, {
|
|
6416
|
-
remove: [PAGE_FLAGS.cursor.flag],
|
|
6417
|
-
add: cursorWords(page.nextCursor)
|
|
6418
|
-
}),
|
|
6419
|
-
stdinArg: invocation.stdinArg
|
|
6420
|
-
}, invocation.runAs)
|
|
6421
|
-
};
|
|
6475
|
+
//#endregion
|
|
6476
|
+
//#region ../agent-cli/dist/context/stdin.js
|
|
6477
|
+
const STDIN_META = "stdin";
|
|
6478
|
+
function isStdinSchema(schema) {
|
|
6479
|
+
return globalRegistry$1.get(schema)?.[STDIN_META] === true;
|
|
6422
6480
|
}
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
return next === void 0 ? `${items.length} shown; no more pages\n` : `${items.length} shown; run the Next step for the next page\n${style.cyan("run:")} ${next}\n`;
|
|
6481
|
+
function endOfInputKeys(platform) {
|
|
6482
|
+
return platform === "win32" ? "Ctrl-Z then Enter" : "Ctrl-D";
|
|
6426
6483
|
}
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
function hasPlan(command) {
|
|
6431
|
-
return command.effect === "write" || command.effect === "destructive";
|
|
6484
|
+
/** The progress line shown before reading a terminal, so a person isn't left facing silence. */
|
|
6485
|
+
function readingLine(name, platform) {
|
|
6486
|
+
return `reading <${name}> from stdin; end it with ${endOfInputKeys(platform)}`;
|
|
6432
6487
|
}
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
const HELP_FLAG = "--help";
|
|
6436
|
-
const VERSION_FLAG = "--version";
|
|
6437
|
-
const ROUTING_FLAGS = [
|
|
6438
|
-
HELP_FLAG,
|
|
6439
|
-
"-h",
|
|
6440
|
-
VERSION_FLAG
|
|
6441
|
-
];
|
|
6442
|
-
function nameOf$1(flag) {
|
|
6443
|
-
return flag.replace(/^--?/, "");
|
|
6488
|
+
function isTerminal(source) {
|
|
6489
|
+
return typeof source !== "string" && "isTTY" in source && source.isTTY === true;
|
|
6444
6490
|
}
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6491
|
+
const STDIN_STALLED = "STDIN_STALLED";
|
|
6492
|
+
/** How long a stdin that is not a terminal may send nothing, from the start of the read and from each chunk. */
|
|
6493
|
+
const STDIN_IDLE_MS = 3e4;
|
|
6494
|
+
function stdinStalled(callerLine) {
|
|
6495
|
+
return new CliError({
|
|
6496
|
+
code: STDIN_STALLED,
|
|
6497
|
+
message: `stdin sent nothing for ${STDIN_IDLE_MS} ms`,
|
|
6498
|
+
hint: `stdin never ended: close the pipe or heredoc that feeds \`${callerLine}\`, pass the text as an argument instead, or write a slow producer's output to a file and redirect it in`
|
|
6499
|
+
});
|
|
6451
6500
|
}
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
}
|
|
6478
|
-
const
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
const forceFlag = force === void 0 ? FORCE_FLAG : {
|
|
6499
|
-
...FORCE_FLAG,
|
|
6500
|
-
description: force
|
|
6501
|
-
};
|
|
6502
|
-
return [
|
|
6503
|
-
...EFFECT_FLAGS[effect],
|
|
6504
|
-
...outputs || force !== void 0 ? [forceFlag] : [],
|
|
6505
|
-
...cost ? [YES_FLAG] : [],
|
|
6506
|
-
...list?.standardFlags ?? []
|
|
6507
|
-
];
|
|
6508
|
-
}
|
|
6509
|
-
/** Throws when the Command declares an arg or option under a Standard flag's name. */
|
|
6510
|
-
function checkNoStandardFlags(command, label) {
|
|
6511
|
-
const lines = [...argFields(command.args).map(({ key }) => ({
|
|
6512
|
-
kind: "arg",
|
|
6513
|
-
key
|
|
6514
|
-
})), ...optionFields(command.options).map(({ key }) => ({
|
|
6515
|
-
kind: "option",
|
|
6516
|
-
key
|
|
6517
|
-
}))].flatMap(({ kind, key }) => {
|
|
6518
|
-
const flag = RESERVED.get(kebab(key));
|
|
6519
|
-
return flag === void 0 ? [] : [` ${kind} '${key}' is the Standard flag ${flag}`];
|
|
6501
|
+
/**
|
|
6502
|
+
* The whole of `source` as UTF-8; rejects with the signal's reason once it aborts, and with
|
|
6503
|
+
* `idle.stalled()` once a stream that is not a terminal sends nothing for `STDIN_IDLE_MS`.
|
|
6504
|
+
*/
|
|
6505
|
+
function readStdin(source, signal, idle) {
|
|
6506
|
+
if (typeof source === "string") return signal.aborted ? Promise.reject(signal.reason) : Promise.resolve(source);
|
|
6507
|
+
return new Promise((resolve, reject) => {
|
|
6508
|
+
const chunks = [];
|
|
6509
|
+
let idleWindow = new AbortController();
|
|
6510
|
+
const watchIdle = () => {
|
|
6511
|
+
idleWindow.abort();
|
|
6512
|
+
idleWindow = new AbortController();
|
|
6513
|
+
if (isTerminal(source)) return;
|
|
6514
|
+
after(idle.clock, STDIN_IDLE_MS, idleWindow.signal, () => {
|
|
6515
|
+
stop();
|
|
6516
|
+
reject(idle.stalled());
|
|
6517
|
+
});
|
|
6518
|
+
};
|
|
6519
|
+
const onData = (chunk) => {
|
|
6520
|
+
chunks.push(chunk instanceof Uint8Array ? chunk : Buffer.from(String(chunk)));
|
|
6521
|
+
watchIdle();
|
|
6522
|
+
};
|
|
6523
|
+
const onEnd = () => {
|
|
6524
|
+
stop();
|
|
6525
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
6526
|
+
};
|
|
6527
|
+
const onError = (error) => {
|
|
6528
|
+
stop();
|
|
6529
|
+
reject(error);
|
|
6530
|
+
};
|
|
6531
|
+
const onAbort = () => {
|
|
6532
|
+
stop();
|
|
6533
|
+
reject(signal.reason);
|
|
6534
|
+
};
|
|
6535
|
+
const stop = () => {
|
|
6536
|
+
source.off("data", onData).off("end", onEnd).off("error", onError).pause();
|
|
6537
|
+
signal.removeEventListener("abort", onAbort);
|
|
6538
|
+
idleWindow.abort();
|
|
6539
|
+
};
|
|
6540
|
+
if (signal.aborted) {
|
|
6541
|
+
reject(signal.reason);
|
|
6542
|
+
return;
|
|
6543
|
+
}
|
|
6544
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
6545
|
+
watchIdle();
|
|
6546
|
+
source.on("data", onData).once("end", onEnd).once("error", onError);
|
|
6520
6547
|
});
|
|
6521
|
-
if (lines.length > 0) throw new Error([`Command '${label}' declares Standard flags the Runtime adds; rename them:`, ...lines].join("\n"));
|
|
6522
|
-
}
|
|
6523
|
-
/** Throws when `force` is declared on a Command with no Plan, or says nothing. */
|
|
6524
|
-
function checkForce(command, label) {
|
|
6525
|
-
const force = "force" in command ? command.force : void 0;
|
|
6526
|
-
if (force === void 0) return;
|
|
6527
|
-
if (!hasPlan(command)) throw new Error(`Command '${label}' declares force, which only a write or destructive Command may`);
|
|
6528
|
-
if (force.trim() === "") throw new Error(`Command '${label}' declares an empty force; say what ${FORCE_FLAG.flag} replaces`);
|
|
6529
6548
|
}
|
|
6530
6549
|
//#endregion
|
|
6531
|
-
//#region ../agent-cli/dist/
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
checkNoStandardFlags(command, label);
|
|
6535
|
-
checkForce(command, label);
|
|
6536
|
-
checkArrayArgLast(label, command.args);
|
|
6537
|
-
checkStdinFields(label, command.args, command.options);
|
|
6538
|
-
checkNoBooleanArrayOption(label, command.options);
|
|
6539
|
-
checkErrors(command, label, table);
|
|
6540
|
-
}
|
|
6541
|
-
function checkErrors(command, label, table) {
|
|
6542
|
-
const unknown = (command.errors ?? []).filter((code) => !Object.hasOwn(table, code));
|
|
6543
|
-
if (unknown.length > 0) throw new Error(`${label} declares ${unknown.join(", ")} in errors, a code the Error table lacks`);
|
|
6544
|
-
}
|
|
6545
|
-
function defineCommandFor() {
|
|
6546
|
-
return declared;
|
|
6550
|
+
//#region ../agent-cli/dist/command-line/input.js
|
|
6551
|
+
function isWrapper(schema) {
|
|
6552
|
+
return schema instanceof ZodOptional || schema instanceof ZodNullable || schema instanceof ZodDefault || schema instanceof ZodPrefault;
|
|
6547
6553
|
}
|
|
6548
|
-
function
|
|
6549
|
-
return
|
|
6554
|
+
function kebab(key) {
|
|
6555
|
+
return key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
6550
6556
|
}
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
const PROJECT_INVALID = "PROJECT_INVALID";
|
|
6555
|
-
function invalid(path, problem, rerun, options) {
|
|
6556
|
-
return new CliError({
|
|
6557
|
-
code: PROJECT_INVALID,
|
|
6558
|
-
message: `the Project file ${path} is invalid: ${problem}`,
|
|
6559
|
-
hint: `fix ${path}, then run the Next step`,
|
|
6560
|
-
next: [rerun]
|
|
6561
|
-
}, options);
|
|
6557
|
+
function typeOf(schema) {
|
|
6558
|
+
if (schema instanceof ZodNumber) return "number";
|
|
6559
|
+
return schema instanceof ZodBoolean ? "boolean" : "string";
|
|
6562
6560
|
}
|
|
6563
|
-
function
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6561
|
+
function describe(schema) {
|
|
6562
|
+
let current = schema;
|
|
6563
|
+
let description = current.description;
|
|
6564
|
+
let required = true;
|
|
6565
|
+
let defaultValue;
|
|
6566
|
+
let stdin = isStdinSchema(current);
|
|
6567
|
+
while (isWrapper(current)) {
|
|
6568
|
+
if (current instanceof ZodDefault || current instanceof ZodPrefault) defaultValue = current.def.defaultValue;
|
|
6569
|
+
required = false;
|
|
6570
|
+
current = current.unwrap();
|
|
6571
|
+
description ??= current.description;
|
|
6572
|
+
stdin ||= isStdinSchema(current);
|
|
6572
6573
|
}
|
|
6574
|
+
const variadic = current instanceof ZodArray;
|
|
6575
|
+
if (current instanceof ZodArray) current = current.element;
|
|
6576
|
+
return {
|
|
6577
|
+
type: typeOf(current),
|
|
6578
|
+
required,
|
|
6579
|
+
defaultValue,
|
|
6580
|
+
description,
|
|
6581
|
+
choices: current instanceof ZodEnum ? current.options.map(String) : void 0,
|
|
6582
|
+
variadic,
|
|
6583
|
+
stdin
|
|
6584
|
+
};
|
|
6573
6585
|
}
|
|
6574
|
-
function
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
} catch (error) {
|
|
6579
|
-
throw invalid(path, messageOf(error), rerun, { cause: error });
|
|
6580
|
-
}
|
|
6581
|
-
const result = schema.safeParse(json);
|
|
6582
|
-
if (!result.success) throw invalid(path, prettifyError(result.error), rerun);
|
|
6583
|
-
return result.data;
|
|
6586
|
+
function argLabel(key, { variadic, required, stdin }) {
|
|
6587
|
+
if (stdin) return required ? `<${key}|->` : `[${key}|-]`;
|
|
6588
|
+
if (!variadic) return `<${key}>`;
|
|
6589
|
+
return required ? `<${key}...>` : `[${key}...]`;
|
|
6584
6590
|
}
|
|
6585
|
-
function
|
|
6586
|
-
const
|
|
6587
|
-
const
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
+
function argField(key, schema) {
|
|
6592
|
+
const described = describe(schema);
|
|
6593
|
+
const label = argLabel(key, described);
|
|
6594
|
+
return {
|
|
6595
|
+
key,
|
|
6596
|
+
name: key,
|
|
6597
|
+
schema,
|
|
6598
|
+
...described,
|
|
6599
|
+
label,
|
|
6600
|
+
usage: label
|
|
6591
6601
|
};
|
|
6592
|
-
const parent = dirname(folder);
|
|
6593
|
-
return parent === folder ? void 0 : projectFrom(projectFile, parent, scope);
|
|
6594
6602
|
}
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6603
|
+
function optionField(key, schema) {
|
|
6604
|
+
const described = describe(schema);
|
|
6605
|
+
const name = kebab(key);
|
|
6606
|
+
const isBoolean = described.type === "boolean";
|
|
6607
|
+
const label = isBoolean && described.defaultValue === true ? `--no-${name}` : `--${name}`;
|
|
6608
|
+
const value = `<${described.choices?.join("|") ?? "value"}>${described.variadic ? "..." : ""}`;
|
|
6609
|
+
const usage = isBoolean ? label : `${label} ${value}`;
|
|
6610
|
+
return {
|
|
6611
|
+
key,
|
|
6612
|
+
name,
|
|
6613
|
+
schema,
|
|
6614
|
+
...described,
|
|
6615
|
+
label,
|
|
6616
|
+
usage
|
|
6617
|
+
};
|
|
6598
6618
|
}
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
function isCommand(node) {
|
|
6602
|
-
return typeof node.effect === "string";
|
|
6619
|
+
function fieldsOf(schema, toField) {
|
|
6620
|
+
return schema === void 0 ? [] : Object.entries(schema.shape).map(([key, value]) => toField(key, value));
|
|
6603
6621
|
}
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
/**
|
|
6607
|
-
* What the CLI declares itself (`own`) and every Command under `commands` declares, each name once,
|
|
6608
|
-
* for a `doctor` row: a Command requires only its own, but `doctor` checks them all.
|
|
6609
|
-
*/
|
|
6610
|
-
function declaredIn(own, commands, needsOf) {
|
|
6611
|
-
const walk = (node) => isCommand(node) ? [...needsOf(node) ?? []] : Object.values(node).flatMap(walk);
|
|
6612
|
-
return [...new Map([...own, ...walk(commands)].map((need) => [need.name, need])).values()];
|
|
6622
|
+
function argFields(schema) {
|
|
6623
|
+
return fieldsOf(schema, argField);
|
|
6613
6624
|
}
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
const SECRET_MISSING = "SECRET_MISSING";
|
|
6617
|
-
/** `$XDG_CONFIG_HOME/<cli>/.env`, with `~/.config` when the variable is unset or empty. */
|
|
6618
|
-
function envFileOf(cliName, env) {
|
|
6619
|
-
const config = env.XDG_CONFIG_HOME || join(env.HOME || homedir(), ".config");
|
|
6620
|
-
return join(config, cliName, ".env");
|
|
6625
|
+
function keysOf(fields) {
|
|
6626
|
+
return fields.map(({ key }) => `'${key}'`).join(", ");
|
|
6621
6627
|
}
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6628
|
+
/** Throws when an arg that takes every remaining operand is not the last arg. */
|
|
6629
|
+
function checkArrayArgLast(name, args) {
|
|
6630
|
+
const misplaced = argFields(args).slice(0, -1).filter((field) => field.variadic);
|
|
6631
|
+
if (misplaced.length > 0) throw new Error(`Command '${name}' declares array arg ${keysOf(misplaced)} before its last arg; only the last arg may be an array`);
|
|
6632
|
+
}
|
|
6633
|
+
/** Throws unless at most one arg, and no option, is marked with `orStdin`. */
|
|
6634
|
+
function checkStdinFields(name, args, options) {
|
|
6635
|
+
const stdinArgs = argFields(args).filter((field) => field.stdin);
|
|
6636
|
+
if (stdinArgs.length > 1) throw new Error(`Command '${name}' marks args ${keysOf(stdinArgs)} with orStdin; only one arg may read stdin`);
|
|
6637
|
+
const stdinOptions = optionFields(options).filter((field) => field.stdin);
|
|
6638
|
+
if (stdinOptions.length > 0) {
|
|
6639
|
+
const noun = stdinOptions.length === 1 ? "option" : "options";
|
|
6640
|
+
throw new Error(`Command '${name}' marks ${noun} ${keysOf(stdinOptions)} with orStdin; only an arg may read stdin`);
|
|
6632
6641
|
}
|
|
6633
|
-
return parseEnv(text);
|
|
6634
6642
|
}
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
const fromEnv = env[name];
|
|
6638
|
-
if (fromEnv) return {
|
|
6639
|
-
value: fromEnv,
|
|
6640
|
-
origin: { kind: "env" }
|
|
6641
|
-
};
|
|
6642
|
-
const fromFile = readDotenv(envFile, fileError)[name];
|
|
6643
|
-
return fromFile ? {
|
|
6644
|
-
value: fromFile,
|
|
6645
|
-
origin: {
|
|
6646
|
-
kind: "file",
|
|
6647
|
-
path: envFile
|
|
6648
|
-
}
|
|
6649
|
-
} : void 0;
|
|
6643
|
+
function optionFields(schema) {
|
|
6644
|
+
return fieldsOf(schema, optionField);
|
|
6650
6645
|
}
|
|
6651
|
-
|
|
6652
|
-
|
|
6646
|
+
/** Throws when an option is an array of booleans, which a switch cannot express. */
|
|
6647
|
+
function checkNoBooleanArrayOption(name, options) {
|
|
6648
|
+
const booleans = optionFields(options).filter((field) => field.variadic && field.type === "boolean");
|
|
6649
|
+
if (booleans.length > 0) throw new Error(`Command '${name}' declares boolean array option ${keysOf(booleans)}; a switch cannot repeat, use a boolean`);
|
|
6653
6650
|
}
|
|
6654
|
-
|
|
6655
|
-
|
|
6651
|
+
//#endregion
|
|
6652
|
+
//#region ../agent-cli/dist/command-line/usage.js
|
|
6653
|
+
const INVALID_INPUT = "INVALID_INPUT";
|
|
6654
|
+
const UNKNOWN_OPTION = "UNKNOWN_OPTION";
|
|
6655
|
+
const UNKNOWN_COMMAND = "UNKNOWN_COMMAND";
|
|
6656
|
+
function unknownError({ code, problem, commandLine, corrections, listed }) {
|
|
6657
|
+
return new UsageError({
|
|
6658
|
+
code,
|
|
6659
|
+
message: `${problem} for \`${commandLine}\``,
|
|
6660
|
+
hint: corrections.length === 0 ? `run \`${commandLine} --help\` to see the ${listed}` : `did you mean ${corrections.map((correction) => `\`${correction}\``).join(" or ")}?`
|
|
6661
|
+
});
|
|
6656
6662
|
}
|
|
6657
|
-
function
|
|
6658
|
-
return
|
|
6663
|
+
function unknownOptionError(commandLine, invocation, flag, suggestions) {
|
|
6664
|
+
return unknownError({
|
|
6665
|
+
code: UNKNOWN_OPTION,
|
|
6666
|
+
problem: `unknown option '${flag}'`,
|
|
6667
|
+
commandLine,
|
|
6668
|
+
corrections: suggestions.map((name) => commandLineRenaming(invocation, /* @__PURE__ */ new Map([[flag, name]]))),
|
|
6669
|
+
listed: "options"
|
|
6670
|
+
});
|
|
6659
6671
|
}
|
|
6660
|
-
/**
|
|
6661
|
-
function
|
|
6662
|
-
const
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6672
|
+
/** Text opening with `-` was read as an option, where the Command could have read it from stdin. */
|
|
6673
|
+
function stdinTextError(commandLine, invocation, word) {
|
|
6674
|
+
const withStdin = commandLineOf(invocation, {
|
|
6675
|
+
remove: [word],
|
|
6676
|
+
add: ["-"]
|
|
6677
|
+
});
|
|
6678
|
+
return new UsageError({
|
|
6679
|
+
code: UNKNOWN_OPTION,
|
|
6680
|
+
message: `unknown option '${word}' for \`${commandLine}\``,
|
|
6681
|
+
hint: `to pass text opening with \`-\`, pass \`-\` and send the text on stdin through a heredoc: \`${withStdin} <<'EOF'\`, then the text, then \`EOF\``
|
|
6670
6682
|
});
|
|
6671
6683
|
}
|
|
6672
|
-
function
|
|
6673
|
-
return
|
|
6684
|
+
function unknownCommandError(commandLine, name, suggestions) {
|
|
6685
|
+
return unknownError({
|
|
6686
|
+
code: UNKNOWN_COMMAND,
|
|
6687
|
+
problem: `unknown command '${name}'`,
|
|
6688
|
+
commandLine,
|
|
6689
|
+
corrections: suggestions.map((suggestion) => `${commandLine} ${suggestion}`),
|
|
6690
|
+
listed: "commands"
|
|
6691
|
+
});
|
|
6674
6692
|
}
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
run: ({ env, envFile, fileError }) => {
|
|
6682
|
-
const read = declared.map((declaration) => ({
|
|
6683
|
-
declaration,
|
|
6684
|
-
secret: secretOf({
|
|
6685
|
-
env,
|
|
6686
|
-
envFile,
|
|
6687
|
-
fileError
|
|
6688
|
-
}, declaration.name)
|
|
6689
|
-
}));
|
|
6690
|
-
const missing = read.filter(({ secret }) => secret === void 0);
|
|
6691
|
-
if (missing.length > 0) return {
|
|
6692
|
-
state: "problem",
|
|
6693
|
-
message: `${missing.map(({ declaration }) => `${declaration.name} is not set ${getItFrom(declaration)}`).join(", ")}; add ${missing.length === 1 ? "it" : "them"} to ${envFile} or the environment`
|
|
6694
|
-
};
|
|
6695
|
-
return {
|
|
6696
|
-
state: "ok",
|
|
6697
|
-
message: read.flatMap(({ declaration, secret }) => secret === void 0 ? [] : [`${declaration.name} from ${originOf(secret)}`]).join(", ")
|
|
6698
|
-
};
|
|
6699
|
-
}
|
|
6700
|
-
}];
|
|
6693
|
+
function invalidInputError(commandLine, problem) {
|
|
6694
|
+
return new UsageError({
|
|
6695
|
+
code: INVALID_INPUT,
|
|
6696
|
+
message: problem,
|
|
6697
|
+
hint: `run \`${commandLine} --help\` to see the expected input`
|
|
6698
|
+
});
|
|
6701
6699
|
}
|
|
6702
6700
|
//#endregion
|
|
6703
|
-
//#region ../agent-cli/dist/
|
|
6704
|
-
const
|
|
6705
|
-
const
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6701
|
+
//#region ../agent-cli/dist/list.js
|
|
6702
|
+
const UNKNOWN_FIELD = "UNKNOWN_FIELD";
|
|
6703
|
+
const DEFAULT_PAGE_LIMIT = 20;
|
|
6704
|
+
const FIELDS_FLAG = "--fields";
|
|
6705
|
+
const SEPARATOR = ",";
|
|
6706
|
+
const COLUMN_SEPARATOR = " ";
|
|
6707
|
+
const UNSAFE_IN_COLUMN = /[\t\n\r]/;
|
|
6708
|
+
const PAGE_INPUT = object({
|
|
6709
|
+
limit: number().int().positive().default(DEFAULT_PAGE_LIMIT),
|
|
6710
|
+
cursor: string().min(1).optional()
|
|
6711
|
+
});
|
|
6712
|
+
var Page = class {
|
|
6713
|
+
items;
|
|
6714
|
+
nextCursor;
|
|
6715
|
+
constructor(items, nextCursor) {
|
|
6716
|
+
this.items = items;
|
|
6717
|
+
this.nextCursor = nextCursor;
|
|
6719
6718
|
}
|
|
6719
|
+
};
|
|
6720
|
+
const PAGE_FLAG_DESCRIPTIONS = {
|
|
6721
|
+
limit: `how many rows to show, a positive whole number (default: ${DEFAULT_PAGE_LIMIT})`,
|
|
6722
|
+
cursor: "where to start, as given by the previous page's next"
|
|
6723
|
+
};
|
|
6724
|
+
function pageFlag(key) {
|
|
6725
|
+
const field = optionField(key, PAGE_INPUT.shape[key]);
|
|
6726
|
+
return {
|
|
6727
|
+
flag: field.label,
|
|
6728
|
+
usage: field.usage,
|
|
6729
|
+
description: PAGE_FLAG_DESCRIPTIONS[key],
|
|
6730
|
+
takes: "optional value",
|
|
6731
|
+
schema: field.schema
|
|
6732
|
+
};
|
|
6720
6733
|
}
|
|
6721
|
-
|
|
6722
|
-
function fileNamesOf(name, env) {
|
|
6723
|
-
const extensions = extensionsOf(env);
|
|
6724
|
-
const own = extname(name).toUpperCase();
|
|
6725
|
-
return own !== "" && extensions.some((extension) => extension.toUpperCase() === own) ? [name] : extensions.map((extension) => `${name}${extension}`);
|
|
6726
|
-
}
|
|
6727
|
-
/** Where `name` is found on `PATH` from `env`, or undefined. */
|
|
6728
|
-
async function findTool(name, env) {
|
|
6729
|
-
const candidates = pathOf(env).split(delimiter).filter((folder) => folder !== "").flatMap((folder) => fileNamesOf(name, env).map((file) => join(folder, file)));
|
|
6730
|
-
for (const file of candidates) if (await isExecutable(file)) return file;
|
|
6731
|
-
}
|
|
6732
|
-
/** Fails with `TOOL_MISSING`, naming every missing tool at once, unless all are on `PATH`. */
|
|
6733
|
-
async function checkTools(tools, env, rerun) {
|
|
6734
|
-
const found = await Promise.all(tools.map((tool) => findTool(tool.name, env)));
|
|
6735
|
-
const missing = tools.filter((_tool, index) => found[index] === void 0);
|
|
6736
|
-
if (missing.length === 0) return;
|
|
6737
|
-
const names = missing.map(({ name }) => name).join(", ");
|
|
6738
|
-
throw runtimeError({
|
|
6739
|
-
code: TOOL_MISSING,
|
|
6740
|
-
message: `${missing.length === 1 ? "a tool it runs is" : "tools it runs are"} not on PATH: ${names}`,
|
|
6741
|
-
hint: "run the Next steps in order: each install, then the command again",
|
|
6742
|
-
next: [...missing.map(({ install }) => wholeLine(install)), rerun]
|
|
6743
|
-
});
|
|
6744
|
-
}
|
|
6745
|
-
/** One `doctor` row per distinct tool the CLI and its Commands declare, with its install command as the fix. */
|
|
6746
|
-
function toolRows(tools, commands) {
|
|
6747
|
-
return declaredIn(tools, commands, (command) => command.tools).map(({ name, install }) => ({
|
|
6748
|
-
name,
|
|
6749
|
-
run: async (ctx) => {
|
|
6750
|
-
const file = await findTool(name, ctx.env);
|
|
6751
|
-
return file === void 0 ? {
|
|
6752
|
-
state: "problem",
|
|
6753
|
-
message: `${name} is not on PATH`,
|
|
6754
|
-
fix: install
|
|
6755
|
-
} : {
|
|
6756
|
-
state: "ok",
|
|
6757
|
-
message: `found at ${file}`
|
|
6758
|
-
};
|
|
6759
|
-
}
|
|
6760
|
-
}));
|
|
6761
|
-
}
|
|
6762
|
-
//#endregion
|
|
6763
|
-
//#region ../agent-cli/dist/version.js
|
|
6764
|
-
/** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
|
|
6765
|
-
function buildStamp() {
|
|
6734
|
+
function fieldsFlag(rowFields) {
|
|
6766
6735
|
return {
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6736
|
+
flag: FIELDS_FLAG,
|
|
6737
|
+
usage: `${FIELDS_FLAG} <a,b>`,
|
|
6738
|
+
description: `print only these comma-separated fields of each row (any of: ${rowFields.join(", ")})`,
|
|
6739
|
+
takes: "optional value",
|
|
6740
|
+
schema: string()
|
|
6770
6741
|
};
|
|
6771
6742
|
}
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6743
|
+
const PAGE_FIELDS = optionFields(PAGE_INPUT);
|
|
6744
|
+
const PAGE_FLAGS = {
|
|
6745
|
+
limit: pageFlag("limit"),
|
|
6746
|
+
cursor: pageFlag("cursor")
|
|
6747
|
+
};
|
|
6748
|
+
/** The Standard flags every List Command gets, as the caller types them. */
|
|
6749
|
+
const LIST_FLAGS = [...Object.values(PAGE_FLAGS).map(({ flag }) => flag), FIELDS_FLAG];
|
|
6750
|
+
function listSpec(rows) {
|
|
6751
|
+
const rowFields = Object.keys(rows.shape);
|
|
6752
|
+
return {
|
|
6753
|
+
rowFields,
|
|
6754
|
+
pageFields: PAGE_FIELDS,
|
|
6755
|
+
standardFlags: [...Object.values(PAGE_FLAGS), fieldsFlag(rowFields)],
|
|
6756
|
+
output: object({
|
|
6757
|
+
items: array(rows),
|
|
6758
|
+
next: string().optional()
|
|
6759
|
+
})
|
|
6760
|
+
};
|
|
6776
6761
|
}
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
/** The last `count` lines of a child's output, trimmed; empty when it wrote nothing. */
|
|
6780
|
-
function lastLines(output, count) {
|
|
6781
|
-
return output.trim().split("\n").slice(-count).join("\n");
|
|
6762
|
+
function quoted(names) {
|
|
6763
|
+
return names.map((name) => `'${name}'`).join(", ");
|
|
6782
6764
|
}
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
const
|
|
6786
|
-
|
|
6787
|
-
const
|
|
6788
|
-
const
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6765
|
+
/** The fields the caller asked for, in their order, or undefined when they gave no `--fields`. */
|
|
6766
|
+
function selectedFields(values, rowFields, commandLine) {
|
|
6767
|
+
const raw = values.get(FIELDS_FLAG);
|
|
6768
|
+
if (raw === void 0) return;
|
|
6769
|
+
const hint = `pass ${FIELDS_FLAG} with any of: ${rowFields.join(", ")}`;
|
|
6770
|
+
const names = typeof raw === "string" ? raw.split(SEPARATOR).filter((name) => name !== "") : [];
|
|
6771
|
+
if (names.length === 0) throw new UsageError({
|
|
6772
|
+
code: INVALID_INPUT,
|
|
6773
|
+
message: `${FIELDS_FLAG}: missing value for \`${commandLine}\``,
|
|
6774
|
+
hint
|
|
6775
|
+
});
|
|
6776
|
+
const unknown = names.filter((name) => !rowFields.includes(name));
|
|
6777
|
+
if (unknown.length > 0) throw new UsageError({
|
|
6778
|
+
code: UNKNOWN_FIELD,
|
|
6779
|
+
message: `unknown field ${quoted(unknown)} for \`${commandLine}\``,
|
|
6780
|
+
hint
|
|
6781
|
+
});
|
|
6782
|
+
return names;
|
|
6799
6783
|
}
|
|
6800
|
-
function
|
|
6801
|
-
|
|
6802
|
-
return shim ? escapedForCmd(escaped) : escaped;
|
|
6784
|
+
function fieldOf(row, field) {
|
|
6785
|
+
return typeof row === "object" && row !== null ? row[field] : void 0;
|
|
6803
6786
|
}
|
|
6804
|
-
function
|
|
6805
|
-
|
|
6806
|
-
return [escapedForCmd(command), ...args.map((arg) => batchArgument(arg, shim))].join(" ");
|
|
6787
|
+
function projected(rows, fields) {
|
|
6788
|
+
return rows.map((row) => Object.fromEntries(fields.map((field) => [field, fieldOf(row, field)])));
|
|
6807
6789
|
}
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
command,
|
|
6812
|
-
args,
|
|
6813
|
-
shell: false
|
|
6814
|
-
};
|
|
6815
|
-
return {
|
|
6816
|
-
command: batchCommandLine(command, file, args),
|
|
6817
|
-
args: [],
|
|
6818
|
-
shell: true
|
|
6819
|
-
};
|
|
6790
|
+
function cell(value) {
|
|
6791
|
+
if (value === void 0 || value === null) return "";
|
|
6792
|
+
return typeof value === "string" && !UNSAFE_IN_COLUMN.test(value) ? value : JSON.stringify(value);
|
|
6820
6793
|
}
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
return Promise.resolve();
|
|
6825
|
-
}
|
|
6826
|
-
const pid = String(child.pid);
|
|
6827
|
-
return new Promise((resolve) => {
|
|
6828
|
-
const taskkill = spawn("taskkill", [
|
|
6829
|
-
"/pid",
|
|
6830
|
-
pid,
|
|
6831
|
-
"/t",
|
|
6832
|
-
"/f"
|
|
6833
|
-
], {
|
|
6834
|
-
stdio: "ignore",
|
|
6835
|
-
windowsHide: true
|
|
6836
|
-
});
|
|
6837
|
-
taskkill.once("error", () => {
|
|
6838
|
-
child.kill();
|
|
6839
|
-
resolve();
|
|
6840
|
-
});
|
|
6841
|
-
taskkill.once("exit", () => resolve());
|
|
6842
|
-
});
|
|
6794
|
+
/** Selected rows as text: a header line of the field names, then one tab-separated line per row. */
|
|
6795
|
+
function renderRows(rows, fields) {
|
|
6796
|
+
return `${[fields, ...rows.map((row) => fields.map((field) => cell(row[field])))].map((line) => line.join(COLUMN_SEPARATOR)).join("\n")}\n`;
|
|
6843
6797
|
}
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
return {
|
|
6848
|
-
push(text) {
|
|
6849
|
-
if (onLine === void 0) return;
|
|
6850
|
-
const lines = (rest + text).split(LINE_END);
|
|
6851
|
-
rest = lines.pop() ?? "";
|
|
6852
|
-
for (const line of lines) onLine(line);
|
|
6853
|
-
},
|
|
6854
|
-
end() {
|
|
6855
|
-
if (rest !== "") onLine?.(rest.replace(TRAILING_CR, ""));
|
|
6856
|
-
rest = "";
|
|
6857
|
-
}
|
|
6858
|
-
};
|
|
6798
|
+
function cursorWords(cursor) {
|
|
6799
|
+
const { flag } = PAGE_FLAGS.cursor;
|
|
6800
|
+
return cursor.startsWith("-") ? [`${flag}=${cursor}`] : [flag, cursor];
|
|
6859
6801
|
}
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6802
|
+
function printedPage(page, invocation) {
|
|
6803
|
+
return page.nextCursor === void 0 ? { items: page.items } : {
|
|
6804
|
+
items: page.items,
|
|
6805
|
+
next: lineOf({
|
|
6806
|
+
kind: "rerun",
|
|
6807
|
+
words: changedArgv(invocation, {
|
|
6808
|
+
remove: [PAGE_FLAGS.cursor.flag],
|
|
6809
|
+
add: cursorWords(page.nextCursor)
|
|
6810
|
+
}),
|
|
6811
|
+
stdinArg: invocation.stdinArg
|
|
6812
|
+
}, invocation.runAs)
|
|
6870
6813
|
};
|
|
6871
6814
|
}
|
|
6872
|
-
/**
|
|
6873
|
-
function
|
|
6874
|
-
return
|
|
6875
|
-
const done = () => {
|
|
6876
|
-
stdin.off("drain", done);
|
|
6877
|
-
stdin.off("close", done);
|
|
6878
|
-
resolve();
|
|
6879
|
-
};
|
|
6880
|
-
stdin.on("drain", done);
|
|
6881
|
-
stdin.on("close", done);
|
|
6882
|
-
});
|
|
6815
|
+
/** The lines that end a rendered page, so the caller always knows whether and how to get more. */
|
|
6816
|
+
function pageFooter({ items, next }, style) {
|
|
6817
|
+
return next === void 0 ? `${items.length} shown; no more pages\n` : `${items.length} shown; run the Next step for the next page\n${style.cyan("run:")} ${next}\n`;
|
|
6883
6818
|
}
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
stdin.on("error", () => void 0);
|
|
6890
|
-
const chunks = isWhole(input) ? [input] : input;
|
|
6891
|
-
for await (const chunk of chunks) {
|
|
6892
|
-
if (signal.aborted || stdin.destroyed) return;
|
|
6893
|
-
if (!stdin.write(chunk)) await drained(stdin);
|
|
6894
|
-
}
|
|
6895
|
-
stdin.end();
|
|
6819
|
+
//#endregion
|
|
6820
|
+
//#region ../agent-cli/dist/planned.js
|
|
6821
|
+
/** Whether `command` changes something through a Plan: a write or destructive Command. */
|
|
6822
|
+
function hasPlan(command) {
|
|
6823
|
+
return command.effect === "write" || command.effect === "destructive";
|
|
6896
6824
|
}
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
env,
|
|
6909
|
-
shell: launch.shell,
|
|
6910
|
-
windowsHide: true,
|
|
6911
|
-
stdio: [
|
|
6912
|
-
stdin === void 0 ? "ignore" : "pipe",
|
|
6913
|
-
"pipe",
|
|
6914
|
-
"pipe"
|
|
6915
|
-
],
|
|
6916
|
-
detached: detached && process.platform !== "win32"
|
|
6917
|
-
});
|
|
6918
|
-
let stopped = Promise.resolve();
|
|
6919
|
-
const kill = () => {
|
|
6920
|
-
stopped = stop(child);
|
|
6921
|
-
};
|
|
6922
|
-
signal.addEventListener("abort", kill, { once: true });
|
|
6923
|
-
let callerFailure;
|
|
6924
|
-
const fail = (error) => {
|
|
6925
|
-
if (callerFailure !== void 0) return;
|
|
6926
|
-
callerFailure = { error };
|
|
6927
|
-
kill();
|
|
6928
|
-
};
|
|
6929
|
-
let stdout = "";
|
|
6930
|
-
let stderr = "";
|
|
6931
|
-
const lines = guarded(onLine, () => callerFailure !== void 0, fail);
|
|
6932
|
-
const stdoutLines = lineSplitter(lines);
|
|
6933
|
-
const stderrLines = lineSplitter(lines);
|
|
6934
|
-
child.stdout?.setEncoding("utf8").on("data", (chunk) => {
|
|
6935
|
-
stdout += chunk;
|
|
6936
|
-
stdoutLines.push(chunk);
|
|
6937
|
-
});
|
|
6938
|
-
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
6939
|
-
stderr += chunk;
|
|
6940
|
-
stderrLines.push(chunk);
|
|
6941
|
-
});
|
|
6942
|
-
if (stdin !== void 0 && child.stdin !== null) feed(child.stdin, stdin, signal).catch(fail);
|
|
6943
|
-
child.on("error", (error) => {
|
|
6944
|
-
signal.removeEventListener("abort", kill);
|
|
6945
|
-
reject(error);
|
|
6946
|
-
});
|
|
6947
|
-
child.on("close", (code, killedBy) => {
|
|
6948
|
-
signal.removeEventListener("abort", kill);
|
|
6949
|
-
if (signal.aborted) {
|
|
6950
|
-
stopped.then(() => reject(signal.reason));
|
|
6951
|
-
return;
|
|
6952
|
-
}
|
|
6953
|
-
stdoutLines.end();
|
|
6954
|
-
stderrLines.end();
|
|
6955
|
-
if (callerFailure !== void 0) {
|
|
6956
|
-
const { error } = callerFailure;
|
|
6957
|
-
stopped.then(() => reject(error));
|
|
6958
|
-
return;
|
|
6959
|
-
}
|
|
6960
|
-
resolve({
|
|
6961
|
-
stdout,
|
|
6962
|
-
stderr,
|
|
6963
|
-
exitCode: code ?? SIGNAL_EXIT_BASE + (killedBy === null ? 0 : constants$1.signals[killedBy])
|
|
6964
|
-
});
|
|
6965
|
-
});
|
|
6966
|
-
});
|
|
6825
|
+
//#endregion
|
|
6826
|
+
//#region ../agent-cli/dist/command-line/flags.js
|
|
6827
|
+
const HELP_FLAG = "--help";
|
|
6828
|
+
const VERSION_FLAG = "--version";
|
|
6829
|
+
const ROUTING_FLAGS = [
|
|
6830
|
+
HELP_FLAG,
|
|
6831
|
+
"-h",
|
|
6832
|
+
VERSION_FLAG
|
|
6833
|
+
];
|
|
6834
|
+
function nameOf$1(flag) {
|
|
6835
|
+
return flag.replace(/^--?/, "");
|
|
6967
6836
|
}
|
|
6968
|
-
|
|
6969
|
-
return
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6837
|
+
function standardFlag(flag, { placeholder, ...rest }) {
|
|
6838
|
+
return {
|
|
6839
|
+
flag,
|
|
6840
|
+
usage: placeholder === void 0 ? flag : `${flag} ${placeholder}`,
|
|
6841
|
+
...rest
|
|
6973
6842
|
};
|
|
6974
6843
|
}
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6844
|
+
const JSON_FLAG = standardFlag("--json", {
|
|
6845
|
+
description: "print the result as JSON",
|
|
6846
|
+
takes: "nothing",
|
|
6847
|
+
schema: boolean()
|
|
6848
|
+
});
|
|
6849
|
+
const DRY_RUN_FLAG = standardFlag("--dry-run", {
|
|
6850
|
+
description: "print the Plan without changing anything",
|
|
6851
|
+
takes: "nothing",
|
|
6852
|
+
schema: boolean()
|
|
6853
|
+
});
|
|
6854
|
+
const CONFIRM_FLAG = standardFlag("--confirm", {
|
|
6855
|
+
placeholder: "<target>",
|
|
6856
|
+
description: "name the target to confirm the change; an error hint gives the exact command",
|
|
6857
|
+
takes: "value",
|
|
6858
|
+
schema: string()
|
|
6859
|
+
});
|
|
6860
|
+
const YES_FLAG = standardFlag("--yes", {
|
|
6861
|
+
description: "approve the cost the run states; --dry-run shows it",
|
|
6862
|
+
takes: "nothing",
|
|
6863
|
+
schema: boolean()
|
|
6864
|
+
});
|
|
6865
|
+
const FORCE_FLAG = standardFlag("--force", {
|
|
6866
|
+
description: "replace a file the run writes that already exists; --dry-run lists every file it writes",
|
|
6867
|
+
takes: "nothing",
|
|
6868
|
+
schema: boolean()
|
|
6869
|
+
});
|
|
6870
|
+
const EFFECT_FLAGS = {
|
|
6871
|
+
read: [JSON_FLAG],
|
|
6872
|
+
act: [JSON_FLAG],
|
|
6873
|
+
write: [JSON_FLAG, DRY_RUN_FLAG],
|
|
6874
|
+
destructive: [
|
|
6875
|
+
JSON_FLAG,
|
|
6876
|
+
DRY_RUN_FLAG,
|
|
6877
|
+
CONFIRM_FLAG
|
|
6878
|
+
],
|
|
6879
|
+
serve: [JSON_FLAG]
|
|
6999
6880
|
};
|
|
7000
|
-
const
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
6881
|
+
const RESERVED = new Map([
|
|
6882
|
+
...ROUTING_FLAGS,
|
|
6883
|
+
...EFFECT_FLAGS.destructive.map(({ flag }) => flag),
|
|
6884
|
+
YES_FLAG.flag,
|
|
6885
|
+
FORCE_FLAG.flag,
|
|
6886
|
+
...LIST_FLAGS
|
|
6887
|
+
].map((flag) => [nameOf$1(flag), flag]));
|
|
6888
|
+
/** The Standard flags of a Command with this Effect and declarations, and of a List Command. */
|
|
6889
|
+
function standardFlagsFor(effect, { outputs, force, cost }, list) {
|
|
6890
|
+
const forceFlag = force === void 0 ? FORCE_FLAG : {
|
|
6891
|
+
...FORCE_FLAG,
|
|
6892
|
+
description: force
|
|
6893
|
+
};
|
|
6894
|
+
return [
|
|
6895
|
+
...EFFECT_FLAGS[effect],
|
|
6896
|
+
...outputs || force !== void 0 ? [forceFlag] : [],
|
|
6897
|
+
...cost ? [YES_FLAG] : [],
|
|
6898
|
+
...list?.standardFlags ?? []
|
|
6899
|
+
];
|
|
6900
|
+
}
|
|
6901
|
+
/** Throws when the Command declares an arg or option under a Standard flag's name. */
|
|
6902
|
+
function checkNoStandardFlags(command, label) {
|
|
6903
|
+
const lines = [...argFields(command.args).map(({ key }) => ({
|
|
6904
|
+
kind: "arg",
|
|
6905
|
+
key
|
|
6906
|
+
})), ...optionFields(command.options).map(({ key }) => ({
|
|
6907
|
+
kind: "option",
|
|
6908
|
+
key
|
|
6909
|
+
}))].flatMap(({ kind, key }) => {
|
|
6910
|
+
const flag = RESERVED.get(kebab(key));
|
|
6911
|
+
return flag === void 0 ? [] : [` ${kind} '${key}' is the Standard flag ${flag}`];
|
|
7007
6912
|
});
|
|
6913
|
+
if (lines.length > 0) throw new Error([`Command '${label}' declares Standard flags the Runtime adds; rename them:`, ...lines].join("\n"));
|
|
6914
|
+
}
|
|
6915
|
+
/** Throws when `force` is declared on a Command with no Plan, or says nothing. */
|
|
6916
|
+
function checkForce(command, label) {
|
|
6917
|
+
const force = "force" in command ? command.force : void 0;
|
|
6918
|
+
if (force === void 0) return;
|
|
6919
|
+
if (!hasPlan(command)) throw new Error(`Command '${label}' declares force, which only a write or destructive Command may`);
|
|
6920
|
+
if (force.trim() === "") throw new Error(`Command '${label}' declares an empty force; say what ${FORCE_FLAG.flag} replaces`);
|
|
6921
|
+
}
|
|
6922
|
+
//#endregion
|
|
6923
|
+
//#region ../agent-cli/dist/define-command.js
|
|
6924
|
+
/** Throws when the Command's declaration breaks a rule its type cannot express; `label` names it. */
|
|
6925
|
+
function checkCommand(command, label, table) {
|
|
6926
|
+
checkNoStandardFlags(command, label);
|
|
6927
|
+
checkForce(command, label);
|
|
6928
|
+
checkArrayArgLast(label, command.args);
|
|
6929
|
+
checkStdinFields(label, command.args, command.options);
|
|
6930
|
+
checkNoBooleanArrayOption(label, command.options);
|
|
6931
|
+
checkErrors(command, label, table);
|
|
6932
|
+
}
|
|
6933
|
+
function checkErrors(command, label, table) {
|
|
6934
|
+
const unknown = (command.errors ?? []).filter((code) => !Object.hasOwn(table, code));
|
|
6935
|
+
if (unknown.length > 0) throw new Error(`${label} declares ${unknown.join(", ")} in errors, a code the Error table lacks`);
|
|
6936
|
+
}
|
|
6937
|
+
function defineCommandFor() {
|
|
6938
|
+
return declared;
|
|
6939
|
+
}
|
|
6940
|
+
function declared(command) {
|
|
6941
|
+
return command;
|
|
7008
6942
|
}
|
|
7009
|
-
|
|
6943
|
+
const defineCommand$1 = defineCommandFor();
|
|
6944
|
+
//#endregion
|
|
6945
|
+
//#region ../agent-cli/dist/context/project.js
|
|
6946
|
+
const PROJECT_INVALID = "PROJECT_INVALID";
|
|
6947
|
+
function invalid(path, problem, rerun, options) {
|
|
7010
6948
|
return new CliError({
|
|
7011
|
-
code:
|
|
7012
|
-
message:
|
|
7013
|
-
hint: `
|
|
6949
|
+
code: PROJECT_INVALID,
|
|
6950
|
+
message: `the Project file ${path} is invalid: ${problem}`,
|
|
6951
|
+
hint: `fix ${path}, then run the Next step`,
|
|
7014
6952
|
next: [rerun]
|
|
7015
|
-
},
|
|
7016
|
-
}
|
|
7017
|
-
function startError(command, error, { fileError, rerun }) {
|
|
7018
|
-
if (isMissingFile(error)) return missingError(command, rerun, error);
|
|
7019
|
-
return fileError(error, {
|
|
7020
|
-
path: command,
|
|
7021
|
-
access: "read"
|
|
7022
|
-
});
|
|
6953
|
+
}, options);
|
|
7023
6954
|
}
|
|
7024
|
-
|
|
6955
|
+
function readText(path, fileError) {
|
|
7025
6956
|
try {
|
|
7026
|
-
|
|
6957
|
+
return readFileSync(path, "utf8");
|
|
7027
6958
|
} catch (error) {
|
|
7028
|
-
|
|
7029
|
-
throw error
|
|
6959
|
+
if (isMissingFile(error)) return void 0;
|
|
6960
|
+
throw fileError(error, {
|
|
6961
|
+
path,
|
|
6962
|
+
access: "read"
|
|
6963
|
+
});
|
|
7030
6964
|
}
|
|
7031
6965
|
}
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
6966
|
+
function parsed(path, text, schema, rerun) {
|
|
6967
|
+
let json;
|
|
6968
|
+
try {
|
|
6969
|
+
json = JSON.parse(text);
|
|
6970
|
+
} catch (error) {
|
|
6971
|
+
throw invalid(path, messageOf(error), rerun, { cause: error });
|
|
6972
|
+
}
|
|
6973
|
+
const result = schema.safeParse(json);
|
|
6974
|
+
if (!result.success) throw invalid(path, prettifyError(result.error), rerun);
|
|
6975
|
+
return result.data;
|
|
7035
6976
|
}
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
} catch (error) {
|
|
7043
|
-
onError(error);
|
|
7044
|
-
throw error;
|
|
7045
|
-
}
|
|
6977
|
+
function projectFrom(projectFile, folder, scope) {
|
|
6978
|
+
const path = join(folder, projectFile.file);
|
|
6979
|
+
const text = readText(path, scope.fileError);
|
|
6980
|
+
if (text !== void 0) return {
|
|
6981
|
+
root: folder,
|
|
6982
|
+
settings: parsed(path, text, projectFile.schema, scope.rerun)
|
|
7046
6983
|
};
|
|
6984
|
+
const parent = dirname(folder);
|
|
6985
|
+
return parent === folder ? void 0 : projectFrom(projectFile, parent, scope);
|
|
7047
6986
|
}
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
}
|
|
7066
|
-
|
|
7067
|
-
|
|
6987
|
+
/** The nearest Project file at or above `cwd`, parsed by its schema; undefined when there is none. */
|
|
6988
|
+
function projectOf(projectFile, cwd, scope) {
|
|
6989
|
+
return projectFrom(projectFile, resolve(cwd), scope);
|
|
6990
|
+
}
|
|
6991
|
+
//#endregion
|
|
6992
|
+
//#region ../agent-cli/dist/context/secrets.js
|
|
6993
|
+
const SECRET_MISSING = "SECRET_MISSING";
|
|
6994
|
+
/** `$XDG_CONFIG_HOME/<cli>/.env`, with `~/.config` when the variable is unset or empty. */
|
|
6995
|
+
function envFileOf(cliName, env) {
|
|
6996
|
+
const config = env.XDG_CONFIG_HOME || join(env.HOME || homedir(), ".config");
|
|
6997
|
+
return join(config, cliName, ".env");
|
|
6998
|
+
}
|
|
6999
|
+
function readDotenv(path, fileError) {
|
|
7000
|
+
let text;
|
|
7001
|
+
try {
|
|
7002
|
+
text = readFileSync(path, "utf8");
|
|
7003
|
+
} catch (error) {
|
|
7004
|
+
if (isMissingFile(error)) return {};
|
|
7005
|
+
throw fileError(error, {
|
|
7006
|
+
path,
|
|
7007
|
+
access: "read"
|
|
7008
|
+
});
|
|
7009
|
+
}
|
|
7010
|
+
return parseEnv(text);
|
|
7011
|
+
}
|
|
7012
|
+
/** Looks `name` up in the env, then in `envFile`; an empty value counts as unset. */
|
|
7013
|
+
function secretOf({ envFile, env, fileError }, name) {
|
|
7014
|
+
const fromEnv = env[name];
|
|
7015
|
+
if (fromEnv) return {
|
|
7016
|
+
value: fromEnv,
|
|
7017
|
+
origin: { kind: "env" }
|
|
7068
7018
|
};
|
|
7019
|
+
const fromFile = readDotenv(envFile, fileError)[name];
|
|
7020
|
+
return fromFile ? {
|
|
7021
|
+
value: fromFile,
|
|
7022
|
+
origin: {
|
|
7023
|
+
kind: "file",
|
|
7024
|
+
path: envFile
|
|
7025
|
+
}
|
|
7026
|
+
} : void 0;
|
|
7069
7027
|
}
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7028
|
+
function missingIn(scope, secrets) {
|
|
7029
|
+
return secrets.filter(({ name }) => secretOf(scope, name) === void 0);
|
|
7030
|
+
}
|
|
7031
|
+
function getItFrom({ get }) {
|
|
7032
|
+
return `(get it from ${get})`;
|
|
7033
|
+
}
|
|
7034
|
+
function whereToGet(secrets) {
|
|
7035
|
+
return secrets.map((secret) => `${secret.name} ${getItFrom(secret)}`).join(", ");
|
|
7036
|
+
}
|
|
7037
|
+
/** Fails with `SECRET_MISSING`, naming every missing Secret at once, unless all are set. */
|
|
7038
|
+
function checkSecrets(secrets, scope, rerun) {
|
|
7039
|
+
const missing = missingIn(scope, secrets);
|
|
7040
|
+
if (missing.length === 0) return;
|
|
7041
|
+
const names = missing.map(({ name }) => name).join(", ");
|
|
7042
|
+
throw new CliError({
|
|
7043
|
+
code: SECRET_MISSING,
|
|
7044
|
+
message: `${missing.length === 1 ? "a Secret it needs is" : "Secrets it needs are"} not set: ${names}`,
|
|
7045
|
+
hint: `set ${whereToGet(missing)} in the environment or in ${scope.envFile}, then run the Next step`,
|
|
7046
|
+
next: [rerun]
|
|
7047
|
+
});
|
|
7048
|
+
}
|
|
7049
|
+
function originOf({ origin }) {
|
|
7050
|
+
return origin.kind === "env" ? "the environment" : origin.path;
|
|
7051
|
+
}
|
|
7052
|
+
/** The `doctor` row over every distinct Secret the CLI and its Commands declare; none without any. */
|
|
7053
|
+
function secretRows(secrets, commands) {
|
|
7054
|
+
const declared = declaredIn(secrets, commands, (command) => command.secrets);
|
|
7055
|
+
if (declared.length === 0) return [];
|
|
7056
|
+
return [{
|
|
7057
|
+
name: "secrets",
|
|
7058
|
+
run: ({ env, envFile, fileError }) => {
|
|
7059
|
+
const read = declared.map((declaration) => ({
|
|
7060
|
+
declaration,
|
|
7061
|
+
secret: secretOf({
|
|
7062
|
+
env,
|
|
7063
|
+
envFile,
|
|
7064
|
+
fileError
|
|
7065
|
+
}, declaration.name)
|
|
7066
|
+
}));
|
|
7067
|
+
const missing = read.filter(({ secret }) => secret === void 0);
|
|
7068
|
+
if (missing.length > 0) return {
|
|
7069
|
+
state: "problem",
|
|
7070
|
+
message: `${missing.map(({ declaration }) => `${declaration.name} is not set ${getItFrom(declaration)}`).join(", ")}; add ${missing.length === 1 ? "it" : "them"} to ${envFile} or the environment`
|
|
7071
|
+
};
|
|
7072
|
+
return {
|
|
7073
|
+
state: "ok",
|
|
7074
|
+
message: read.flatMap(({ declaration, secret }) => secret === void 0 ? [] : [`${declaration.name} from ${originOf(secret)}`]).join(", ")
|
|
7075
|
+
};
|
|
7081
7076
|
}
|
|
7077
|
+
}];
|
|
7078
|
+
}
|
|
7079
|
+
//#endregion
|
|
7080
|
+
//#region ../agent-cli/dist/version.js
|
|
7081
|
+
/** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
|
|
7082
|
+
function buildStamp() {
|
|
7083
|
+
return {
|
|
7084
|
+
"sha": "75fd8d7",
|
|
7085
|
+
"dirty": false,
|
|
7086
|
+
"builtAt": "2026-10-29T03:29:07.000Z"
|
|
7082
7087
|
};
|
|
7083
7088
|
}
|
|
7089
|
+
/** The first line of `--version`: the version, and the commit and time of a stamped build. */
|
|
7090
|
+
function versionLine({ version }) {
|
|
7091
|
+
const build = buildStamp();
|
|
7092
|
+
return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
|
|
7093
|
+
}
|
|
7084
7094
|
//#endregion
|
|
7085
7095
|
//#region ../agent-cli/dist/built-ins/semver.js
|
|
7086
7096
|
const VERSION = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
@@ -7475,7 +7485,7 @@ function packagesOf(install) {
|
|
|
7475
7485
|
function isUnchecked(standing) {
|
|
7476
7486
|
return standing.kind === "source" || standing.kind === "linked";
|
|
7477
7487
|
}
|
|
7478
|
-
function skippedFor(cli, install, standing) {
|
|
7488
|
+
function skippedFor$1(cli, install, standing) {
|
|
7479
7489
|
return {
|
|
7480
7490
|
state: "skipped",
|
|
7481
7491
|
message: standing.kind === "source" ? `not checked: ${cli.name} runs from source, not from an install` : `not checked: ${install.package} links to ${standing.copy.realDir}, so the workspace decides its version`
|
|
@@ -7500,7 +7510,7 @@ function installFinding(cli, runAs, install, { cwd, root, standing }) {
|
|
|
7500
7510
|
const lines = root === void 0 ? void 0 : LINES[root.manager];
|
|
7501
7511
|
switch (standing.kind) {
|
|
7502
7512
|
case "source":
|
|
7503
|
-
case "linked": return skippedFor(cli, install, standing);
|
|
7513
|
+
case "linked": return skippedFor$1(cli, install, standing);
|
|
7504
7514
|
case "missing": return problem(`no package ${searchedFrom(cwd, root)} depends on ${install.package}`, lines?.add(packagesOf(install)));
|
|
7505
7515
|
case "listed": return problem(`${standing.manifest} lists ${install.package}, but no copy of it is installed at or above ${cwd}`, lines?.install);
|
|
7506
7516
|
case "unreadable": return problem(`${standing.manifest} is not a package.json with a version: ${standing.reason}`, lines?.install);
|
|
@@ -7546,7 +7556,7 @@ function checkOf$1(cli, install, runAs, { bin, node }) {
|
|
|
7546
7556
|
install: async (ctx) => installFinding(cli, runAs, install, await surveyed(ctx.cwd)),
|
|
7547
7557
|
release: async (ctx) => {
|
|
7548
7558
|
const { cwd, root, standing } = await surveyed(ctx.cwd);
|
|
7549
|
-
if (isUnchecked(standing)) return { skipped: skippedFor(cli, install, standing) };
|
|
7559
|
+
if (isUnchecked(standing)) return { skipped: skippedFor$1(cli, install, standing) };
|
|
7550
7560
|
return {
|
|
7551
7561
|
npm: npmContext(cli, registryOf(install, root?.dir), ctx, node, root?.dir ?? cwd),
|
|
7552
7562
|
advise: (latest) => adviceFor(install, root, latest)
|
|
@@ -8759,12 +8769,21 @@ function nodeRow(cli) {
|
|
|
8759
8769
|
};
|
|
8760
8770
|
}
|
|
8761
8771
|
/** A row's thrown error as its problem: a `CliError` with its hint and code, anything else a bug. */
|
|
8762
|
-
function problemOfError(name, error) {
|
|
8763
|
-
if (error instanceof CliError)
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
8772
|
+
function problemOfError(name, error, runAs) {
|
|
8773
|
+
if (error instanceof CliError) {
|
|
8774
|
+
const message = `${error.message}; ${error.hint}`;
|
|
8775
|
+
const fix = error.next.find((step) => !isRerun(step));
|
|
8776
|
+
return fix === void 0 ? {
|
|
8777
|
+
state: "problem",
|
|
8778
|
+
message,
|
|
8779
|
+
code: error.code
|
|
8780
|
+
} : {
|
|
8781
|
+
state: "problem",
|
|
8782
|
+
message,
|
|
8783
|
+
fix: lineOf(fix, runAs),
|
|
8784
|
+
code: error.code
|
|
8785
|
+
};
|
|
8786
|
+
}
|
|
8768
8787
|
return {
|
|
8769
8788
|
state: "problem",
|
|
8770
8789
|
message: `the ${name} row hit a bug: ${messageOf(error)}`
|
|
@@ -8790,11 +8809,11 @@ function readProject(projectFile, ctx) {
|
|
|
8790
8809
|
};
|
|
8791
8810
|
}
|
|
8792
8811
|
}
|
|
8793
|
-
function projectRow({ file }, read) {
|
|
8812
|
+
function projectRow({ file }, { read, runAs }) {
|
|
8794
8813
|
return {
|
|
8795
8814
|
name: "project",
|
|
8796
8815
|
run: (ctx) => {
|
|
8797
|
-
if (!read.ok) return problemOfError("project", read.error);
|
|
8816
|
+
if (!read.ok) return problemOfError("project", read.error, runAs);
|
|
8798
8817
|
return ok(read.project === void 0 ? `none found: no ${file} at or above ${ctx.cwd}` : join(read.project.root, file));
|
|
8799
8818
|
}
|
|
8800
8819
|
};
|
|
@@ -8834,20 +8853,24 @@ function registryRow(cli, check) {
|
|
|
8834
8853
|
};
|
|
8835
8854
|
}
|
|
8836
8855
|
/** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
|
|
8837
|
-
function doctorChecks(cli, launch,
|
|
8856
|
+
function doctorChecks(cli, launch, scope) {
|
|
8838
8857
|
const install = installOf(cli);
|
|
8839
8858
|
const check = install.check(launch);
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8859
|
+
const tools = toolRows(cli.tools ?? [], cli.commands);
|
|
8860
|
+
return {
|
|
8861
|
+
checks: [
|
|
8862
|
+
buildRow(cli),
|
|
8863
|
+
nodeRow(cli),
|
|
8864
|
+
...cli.project === void 0 ? [] : [projectRow(cli.project, scope)],
|
|
8865
|
+
...cli.doctor ?? [],
|
|
8866
|
+
...tools,
|
|
8867
|
+
...secretRows(cli.secrets ?? [], cli.commands),
|
|
8868
|
+
installRow(check),
|
|
8869
|
+
registryRow(cli, check),
|
|
8870
|
+
...skillOf$1(cli, install).rows
|
|
8871
|
+
],
|
|
8872
|
+
tools: new Set(tools.map(({ name }) => name))
|
|
8873
|
+
};
|
|
8851
8874
|
}
|
|
8852
8875
|
const DEFAULT_DEADLINE_MS = 1e4;
|
|
8853
8876
|
const MS_PER_SECOND = 1e3;
|
|
@@ -8867,14 +8890,14 @@ const reportSchema = object({
|
|
|
8867
8890
|
})),
|
|
8868
8891
|
next: array(string())
|
|
8869
8892
|
});
|
|
8870
|
-
function problemOf(check, error, { signal, deadline, ms }) {
|
|
8893
|
+
function problemOf(check, error, { signal, deadline, ms, runAs }) {
|
|
8871
8894
|
if (!signal.aborted && deadline.aborted && error === deadline.reason) return {
|
|
8872
8895
|
state: "problem",
|
|
8873
8896
|
message: `${check.name} did not answer within ${ms / MS_PER_SECOND} s`
|
|
8874
8897
|
};
|
|
8875
|
-
return problemOfError(check.name, error);
|
|
8898
|
+
return problemOfError(check.name, error, runAs);
|
|
8876
8899
|
}
|
|
8877
|
-
function rowContext(ctx, signal, read) {
|
|
8900
|
+
function rowContext(ctx, signal, { read }) {
|
|
8878
8901
|
return Object.create(ctx, {
|
|
8879
8902
|
signal: { value: signal },
|
|
8880
8903
|
project: { get: () => {
|
|
@@ -8883,21 +8906,36 @@ function rowContext(ctx, signal, read) {
|
|
|
8883
8906
|
} }
|
|
8884
8907
|
});
|
|
8885
8908
|
}
|
|
8886
|
-
|
|
8909
|
+
function skippedFor(error, { read, tools }) {
|
|
8910
|
+
if (!read.ok && error === read.error) return {
|
|
8911
|
+
state: "skipped",
|
|
8912
|
+
message: "the Project file is unusable; the project row says why"
|
|
8913
|
+
};
|
|
8914
|
+
const tool = missingToolOf(error);
|
|
8915
|
+
if (tool !== void 0 && tools.has(tool)) return {
|
|
8916
|
+
state: "skipped",
|
|
8917
|
+
message: `${tool} is not on PATH; the ${tool} row says why`
|
|
8918
|
+
};
|
|
8919
|
+
}
|
|
8920
|
+
async function rowOf(check, ctx, { read, runAs, tools }) {
|
|
8887
8921
|
const ms = check.deadlineMs ?? DEFAULT_DEADLINE_MS;
|
|
8888
8922
|
const signal = ctx.deadline(ms);
|
|
8889
8923
|
let finding;
|
|
8890
8924
|
try {
|
|
8891
|
-
finding = await Promise.race([Promise.resolve().then(() => check.run(rowContext(ctx, signal,
|
|
8925
|
+
finding = await Promise.race([Promise.resolve().then(() => check.run(rowContext(ctx, signal, {
|
|
8926
|
+
read,
|
|
8927
|
+
runAs
|
|
8928
|
+
}))), untilAborted(signal)]);
|
|
8892
8929
|
} catch (error) {
|
|
8893
8930
|
if (ctx.signal.aborted) return void 0;
|
|
8894
|
-
finding =
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
}
|
|
8931
|
+
finding = skippedFor(error, {
|
|
8932
|
+
read,
|
|
8933
|
+
tools
|
|
8934
|
+
}) ?? problemOf(check, error, {
|
|
8898
8935
|
signal: ctx.signal,
|
|
8899
8936
|
deadline: signal,
|
|
8900
|
-
ms
|
|
8937
|
+
ms,
|
|
8938
|
+
runAs
|
|
8901
8939
|
});
|
|
8902
8940
|
}
|
|
8903
8941
|
return {
|
|
@@ -8908,8 +8946,10 @@ async function rowOf(check, ctx, read) {
|
|
|
8908
8946
|
advisory: check.advisory === true
|
|
8909
8947
|
};
|
|
8910
8948
|
}
|
|
8911
|
-
function nextOf(
|
|
8912
|
-
|
|
8949
|
+
function nextOf(checked, doctorLine) {
|
|
8950
|
+
const fixes = checked.flatMap(({ row }) => row.fix === void 0 ? [] : [row.fix]);
|
|
8951
|
+
const unfixed = checked.some(({ row, advisory }) => advisory && row.state === "problem" && row.fix === void 0);
|
|
8952
|
+
return [...new Set(unfixed ? [...fixes, doctorLine] : fixes)];
|
|
8913
8953
|
}
|
|
8914
8954
|
function render({ rows, next }, style) {
|
|
8915
8955
|
const width = Math.max(MIN_LABEL_WIDTH, ...rows.map(({ check }) => check.length)) + 2;
|
|
@@ -8927,8 +8967,8 @@ function failure(cliName, rerun, problems) {
|
|
|
8927
8967
|
};
|
|
8928
8968
|
return runtimeError(fix === void 0 ? {
|
|
8929
8969
|
...head,
|
|
8930
|
-
hint: "the row says
|
|
8931
|
-
next: []
|
|
8970
|
+
hint: "fix what the row says, then run the Next step",
|
|
8971
|
+
next: [rerun]
|
|
8932
8972
|
} : {
|
|
8933
8973
|
...head,
|
|
8934
8974
|
hint: "run the Next steps in order",
|
|
@@ -8951,12 +8991,18 @@ function doctorCommand(cli, launch) {
|
|
|
8951
8991
|
effect: "read",
|
|
8952
8992
|
output: reportSchema,
|
|
8953
8993
|
run: async (_input, ctx) => {
|
|
8954
|
-
const
|
|
8955
|
-
|
|
8956
|
-
|
|
8994
|
+
const rowsScope = {
|
|
8995
|
+
read: readProject(cli.project, ctx),
|
|
8996
|
+
runAs: runAsOf(cli)
|
|
8997
|
+
};
|
|
8998
|
+
const { checks, tools } = doctorChecks(cli, launch, rowsScope);
|
|
8999
|
+
const checked = (await Promise.all(checks.map((check) => rowOf(check, ctx, {
|
|
9000
|
+
...rowsScope,
|
|
9001
|
+
tools
|
|
9002
|
+
})))).filter((row) => row !== void 0);
|
|
8957
9003
|
const report = {
|
|
8958
|
-
rows,
|
|
8959
|
-
next: nextOf(
|
|
9004
|
+
rows: checked.map(({ row }) => row),
|
|
9005
|
+
next: nextOf(checked, lineOf(ctx.rerun(), rowsScope.runAs))
|
|
8960
9006
|
};
|
|
8961
9007
|
if (ctx.signal.aborted) {
|
|
8962
9008
|
ctx.deliver(report);
|
|
@@ -9226,18 +9272,26 @@ function installFrom(name, manifest, install) {
|
|
|
9226
9272
|
mode: "dependency",
|
|
9227
9273
|
package: manifest.name
|
|
9228
9274
|
};
|
|
9229
|
-
if (install
|
|
9275
|
+
if (install === "global") return {
|
|
9276
|
+
mode: "global",
|
|
9277
|
+
package: manifest.name,
|
|
9278
|
+
registry: publishedTo(name, manifest)
|
|
9279
|
+
};
|
|
9280
|
+
if (install.mode === "global") return {
|
|
9281
|
+
mode: "global",
|
|
9282
|
+
package: manifest.name,
|
|
9283
|
+
registry: install.registry
|
|
9284
|
+
};
|
|
9285
|
+
return {
|
|
9230
9286
|
mode: "dependency",
|
|
9231
9287
|
package: manifest.name,
|
|
9232
9288
|
lockstep: install.lockstep
|
|
9233
9289
|
};
|
|
9290
|
+
}
|
|
9291
|
+
function publishedTo(name, manifest) {
|
|
9234
9292
|
const registry = manifest.publishConfig?.registry;
|
|
9235
9293
|
if (registry === void 0) throw new Error(`${name} declares a Global install, which needs publishConfig.registry in its package.json`);
|
|
9236
|
-
return
|
|
9237
|
-
mode: "global",
|
|
9238
|
-
package: manifest.name,
|
|
9239
|
-
registry
|
|
9240
|
-
};
|
|
9294
|
+
return registry;
|
|
9241
9295
|
}
|
|
9242
9296
|
function resolved(cli) {
|
|
9243
9297
|
const { manifest, ...declared } = cli;
|
|
@@ -11290,7 +11344,7 @@ async function runProcess(cli) {
|
|
|
11290
11344
|
//#region package.json
|
|
11291
11345
|
var package_default = {
|
|
11292
11346
|
name: "@duyquangnvx/create-agent-cli",
|
|
11293
|
-
version: "0.
|
|
11347
|
+
version: "0.21.0",
|
|
11294
11348
|
description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
11295
11349
|
type: "module",
|
|
11296
11350
|
engines: { "node": ">=22.12.0" },
|