@duyquangnvx/create-agent-cli 0.27.0 → 0.28.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 +27 -0
- package/dist/bin.js +1463 -1350
- package/package.json +2 -2
- package/template/test/cli.test.ts +5 -1
package/dist/bin.js
CHANGED
|
@@ -4,7 +4,7 @@ import { spawn } from "node:child_process";
|
|
|
4
4
|
import { constants, homedir, tmpdir } from "node:os";
|
|
5
5
|
import { parseArgs, parseEnv } from "node:util";
|
|
6
6
|
import { constants as constants$1, existsSync, readFileSync, realpathSync } from "node:fs";
|
|
7
|
-
import { access, chmod, link,
|
|
7
|
+
import { access, chmod, link, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
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
|
|
@@ -5947,15 +5947,6 @@ function errnoOf(error) {
|
|
|
5947
5947
|
function isMissingFile(error) {
|
|
5948
5948
|
return errnoOf(error) === "ENOENT";
|
|
5949
5949
|
}
|
|
5950
|
-
/** What `pending` resolves with, or undefined when it fails because a file is missing. */
|
|
5951
|
-
async function orMissing(pending) {
|
|
5952
|
-
try {
|
|
5953
|
-
return await pending;
|
|
5954
|
-
} catch (error) {
|
|
5955
|
-
if (isMissingFile(error)) return void 0;
|
|
5956
|
-
throw error;
|
|
5957
|
-
}
|
|
5958
|
-
}
|
|
5959
5950
|
function isNothingThere(error) {
|
|
5960
5951
|
return isMissingFile(error) || errnoOf(error) === "ENOTDIR";
|
|
5961
5952
|
}
|
|
@@ -5981,14 +5972,14 @@ function bugHint(format) {
|
|
|
5981
5972
|
const rerun = `rerun with ${debugVariable(format.cliName)}=1 to see the stack trace`;
|
|
5982
5973
|
return bugs === void 0 ? rerun : `${rerun}, then report it at ${bugs}`;
|
|
5983
5974
|
}
|
|
5984
|
-
function fieldsOf$1(error, format) {
|
|
5975
|
+
function fieldsOf$1({ error, exitClass }, format) {
|
|
5985
5976
|
if (!(error instanceof CliError)) return {
|
|
5986
5977
|
code: INTERNAL,
|
|
5987
5978
|
message: messageOf(error),
|
|
5988
5979
|
hint: bugHint(format)
|
|
5989
5980
|
};
|
|
5990
5981
|
const { code, message, hint, details, retryAt, next } = error;
|
|
5991
|
-
if (
|
|
5982
|
+
if (exitClass === "bug") return {
|
|
5992
5983
|
code,
|
|
5993
5984
|
message,
|
|
5994
5985
|
hint: bugHint(format),
|
|
@@ -6012,9 +6003,9 @@ function traceOf(error) {
|
|
|
6012
6003
|
return error instanceof Error ? error.stack ?? error.message : String(error);
|
|
6013
6004
|
}
|
|
6014
6005
|
/** Under debug: a bug's stack trace, then every error's cause chain. */
|
|
6015
|
-
function debugTrace(error, format) {
|
|
6006
|
+
function debugTrace({ error, exitClass }, format) {
|
|
6016
6007
|
if (!isDebug(format)) return void 0;
|
|
6017
|
-
const lines = [...
|
|
6008
|
+
const lines = [...exitClass === "bug" ? [traceOf(error)] : [], ...causesOf(error).map((cause) => `caused by: ${traceOf(cause)}`)];
|
|
6018
6009
|
return lines.length === 0 ? void 0 : lines.join("\n");
|
|
6019
6010
|
}
|
|
6020
6011
|
/**
|
|
@@ -6033,29 +6024,29 @@ function printedDetail(error, format) {
|
|
|
6033
6024
|
const value = Reflect.get(details, key);
|
|
6034
6025
|
return typeof value === "string" ? value : void 0;
|
|
6035
6026
|
}
|
|
6036
|
-
function labelOf(
|
|
6037
|
-
if (
|
|
6038
|
-
return style.red(
|
|
6027
|
+
function labelOf({ exitClass }, style) {
|
|
6028
|
+
if (exitClass === "wait") return style.yellow("wait:");
|
|
6029
|
+
return style.red(exitClass === "bug" ? "internal error:" : "error:");
|
|
6039
6030
|
}
|
|
6040
|
-
function errorLines(
|
|
6041
|
-
const fields = fieldsOf$1(
|
|
6042
|
-
const blocks = [printedDetail(error, format), debugTrace(
|
|
6031
|
+
function errorLines(leaf, format) {
|
|
6032
|
+
const fields = fieldsOf$1(leaf, format);
|
|
6033
|
+
const blocks = [printedDetail(leaf.error, format), debugTrace(leaf, format)].filter((block) => block !== void 0);
|
|
6043
6034
|
const { style } = format;
|
|
6044
|
-
const label = labelOf(
|
|
6035
|
+
const label = labelOf(leaf, style);
|
|
6045
6036
|
const between = blocks.map((block) => `${block}\n`).join("");
|
|
6046
6037
|
const runs = (fields.next ?? []).map((line) => `${style.cyan("run:")} ${line}\n`).join("");
|
|
6047
6038
|
return `${label} ${fields.message}\n${between}${style.cyan("hint:")} ${fields.hint}\n${runs}`;
|
|
6048
6039
|
}
|
|
6049
|
-
|
|
6050
|
-
|
|
6040
|
+
/** The classified errors a run failed with, as stderr carries them. */
|
|
6041
|
+
function formatRuntimeError(leaves, format) {
|
|
6051
6042
|
if (format.json) {
|
|
6052
|
-
const printed =
|
|
6053
|
-
...fieldsOf$1(
|
|
6054
|
-
stack: debugTrace(
|
|
6043
|
+
const printed = leaves.map((leaf) => ({
|
|
6044
|
+
...fieldsOf$1(leaf, format),
|
|
6045
|
+
stack: debugTrace(leaf, format)
|
|
6055
6046
|
}));
|
|
6056
6047
|
return `${JSON.stringify({ errors: printed })}\n`;
|
|
6057
6048
|
}
|
|
6058
|
-
return
|
|
6049
|
+
return leaves.map((leaf) => errorLines(leaf, format)).join("");
|
|
6059
6050
|
}
|
|
6060
6051
|
//#endregion
|
|
6061
6052
|
//#region ../agent-cli/dist/tail.js
|
|
@@ -6085,8 +6076,8 @@ const WINDOWS_EXTENSIONS = ".COM;.EXE;.BAT;.CMD";
|
|
|
6085
6076
|
function pathOf(env) {
|
|
6086
6077
|
return env[Object.keys(env).find((name) => name.toUpperCase() === "PATH") ?? "PATH"] ?? "";
|
|
6087
6078
|
}
|
|
6088
|
-
function extensionsOf(env) {
|
|
6089
|
-
return
|
|
6079
|
+
function extensionsOf({ env, platform }) {
|
|
6080
|
+
return platform === "win32" ? (env.PATHEXT ?? WINDOWS_EXTENSIONS).split(";") : [""];
|
|
6090
6081
|
}
|
|
6091
6082
|
async function isExecutable(file) {
|
|
6092
6083
|
try {
|
|
@@ -6098,19 +6089,19 @@ async function isExecutable(file) {
|
|
|
6098
6089
|
}
|
|
6099
6090
|
}
|
|
6100
6091
|
/** The file names `name` may have on `PATH`: itself when it already ends in one of the extensions. */
|
|
6101
|
-
function fileNamesOf(name,
|
|
6102
|
-
const extensions = extensionsOf(
|
|
6092
|
+
function fileNamesOf(name, search) {
|
|
6093
|
+
const extensions = extensionsOf(search);
|
|
6103
6094
|
const own = extname(name).toUpperCase();
|
|
6104
6095
|
return own !== "" && extensions.some((extension) => extension.toUpperCase() === own) ? [name] : extensions.map((extension) => `${name}${extension}`);
|
|
6105
6096
|
}
|
|
6106
|
-
/** Where `name` is found on `PATH
|
|
6107
|
-
async function findTool(name,
|
|
6108
|
-
const candidates = pathOf(env).split(delimiter).filter((folder) => folder !== "").flatMap((folder) => fileNamesOf(name,
|
|
6097
|
+
/** Where `name` is found on `PATH`, or undefined. */
|
|
6098
|
+
async function findTool(name, search) {
|
|
6099
|
+
const candidates = pathOf(search.env).split(delimiter).filter((folder) => folder !== "").flatMap((folder) => fileNamesOf(name, search).map((file) => join(folder, file)));
|
|
6109
6100
|
for (const file of candidates) if (await isExecutable(file)) return file;
|
|
6110
6101
|
}
|
|
6111
6102
|
/** Fails with `TOOL_MISSING`, naming every missing tool at once, unless all are on `PATH`. */
|
|
6112
|
-
async function checkTools(tools,
|
|
6113
|
-
const found = await Promise.all(tools.map((tool) => findTool(tool.name,
|
|
6103
|
+
async function checkTools(tools, search, rerun) {
|
|
6104
|
+
const found = await Promise.all(tools.map((tool) => findTool(tool.name, search)));
|
|
6114
6105
|
const missing = tools.filter((_tool, index) => found[index] === void 0);
|
|
6115
6106
|
if (missing.length === 0) return;
|
|
6116
6107
|
const names = missing.map(({ name }) => name).join(", ");
|
|
@@ -6122,11 +6113,14 @@ async function checkTools(tools, env, rerun) {
|
|
|
6122
6113
|
});
|
|
6123
6114
|
}
|
|
6124
6115
|
/** One `doctor` row per distinct tool the CLI and its Commands declare, with its install command as the fix. */
|
|
6125
|
-
function toolRows(tools, commands) {
|
|
6116
|
+
function toolRows(tools, commands, platform) {
|
|
6126
6117
|
return declaredIn(tools, commands, (command) => command.tools).map(({ name, install }) => ({
|
|
6127
6118
|
name,
|
|
6128
6119
|
run: async (ctx) => {
|
|
6129
|
-
const file = await findTool(name,
|
|
6120
|
+
const file = await findTool(name, {
|
|
6121
|
+
env: ctx.env,
|
|
6122
|
+
platform
|
|
6123
|
+
});
|
|
6130
6124
|
return file === void 0 ? {
|
|
6131
6125
|
state: "problem",
|
|
6132
6126
|
message: `${name} is not on PATH`,
|
|
@@ -6164,7 +6158,10 @@ function batchCommandLine(command, file, args) {
|
|
|
6164
6158
|
return [escapedForCmd(command), ...args.map((arg) => batchArgument(arg, shim))].join(" ");
|
|
6165
6159
|
}
|
|
6166
6160
|
async function windowsLaunch(command, args, env) {
|
|
6167
|
-
const file = PATH_SEPARATOR.test(command) ? command : await findTool(command,
|
|
6161
|
+
const file = PATH_SEPARATOR.test(command) ? command : await findTool(command, {
|
|
6162
|
+
env,
|
|
6163
|
+
platform: process.platform
|
|
6164
|
+
});
|
|
6168
6165
|
if (file === void 0 || !BATCH_FILE.test(file)) return {
|
|
6169
6166
|
command,
|
|
6170
6167
|
args,
|
|
@@ -6928,188 +6925,42 @@ function checkForce(command, label) {
|
|
|
6928
6925
|
if (force.trim() === "") throw new Error(`Command '${label}' declares an empty force; say what ${FORCE_FLAG.flag} replaces`);
|
|
6929
6926
|
}
|
|
6930
6927
|
//#endregion
|
|
6931
|
-
//#region ../agent-cli/dist/
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
const unknown = (command.errors ?? []).filter((code) => !Object.hasOwn(table, code));
|
|
6943
|
-
if (unknown.length > 0) throw new Error(`${label} declares ${unknown.join(", ")} in errors, a code the Error table lacks`);
|
|
6944
|
-
}
|
|
6945
|
-
function defineCommandFor() {
|
|
6946
|
-
return declared;
|
|
6947
|
-
}
|
|
6948
|
-
function declared(command) {
|
|
6949
|
-
return command;
|
|
6950
|
-
}
|
|
6951
|
-
const defineCommand$1 = defineCommandFor();
|
|
6952
|
-
//#endregion
|
|
6953
|
-
//#region ../agent-cli/dist/context/project.js
|
|
6954
|
-
const PROJECT_INVALID = "PROJECT_INVALID";
|
|
6955
|
-
function invalid(path, problem, rerun, options) {
|
|
6956
|
-
return new CliError({
|
|
6957
|
-
code: PROJECT_INVALID,
|
|
6958
|
-
message: `the Project file ${path} is invalid: ${problem}`,
|
|
6959
|
-
hint: `fix ${path}, then run the Next step`,
|
|
6960
|
-
next: [rerun]
|
|
6961
|
-
}, options);
|
|
6962
|
-
}
|
|
6963
|
-
function readText(path, fileError) {
|
|
6964
|
-
try {
|
|
6965
|
-
return readFileSync(path, "utf8");
|
|
6966
|
-
} catch (error) {
|
|
6967
|
-
if (isMissingFile(error)) return void 0;
|
|
6968
|
-
throw fileError(error, {
|
|
6969
|
-
path,
|
|
6970
|
-
access: "read"
|
|
6971
|
-
});
|
|
6972
|
-
}
|
|
6973
|
-
}
|
|
6974
|
-
function parsed(path, text, schema, rerun) {
|
|
6975
|
-
let json;
|
|
6976
|
-
try {
|
|
6977
|
-
json = JSON.parse(text);
|
|
6978
|
-
} catch (error) {
|
|
6979
|
-
throw invalid(path, messageOf(error), rerun, { cause: error });
|
|
6980
|
-
}
|
|
6981
|
-
const result = schema.safeParse(json);
|
|
6982
|
-
if (!result.success) throw invalid(path, prettifyError(result.error), rerun);
|
|
6983
|
-
return result.data;
|
|
6984
|
-
}
|
|
6985
|
-
function projectFrom(projectFile, folder, scope) {
|
|
6986
|
-
const path = join(folder, projectFile.file);
|
|
6987
|
-
const text = readText(path, scope.fileError);
|
|
6988
|
-
if (text !== void 0) return {
|
|
6989
|
-
root: folder,
|
|
6990
|
-
settings: parsed(path, text, projectFile.schema, scope.rerun)
|
|
6928
|
+
//#region ../agent-cli/dist/context/confirm.js
|
|
6929
|
+
const CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED";
|
|
6930
|
+
const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
|
|
6931
|
+
function checkConfirmation({ rerun, commandLine, given }, expected) {
|
|
6932
|
+
if (given === expected) return;
|
|
6933
|
+
const advice = {
|
|
6934
|
+
hint: "once the user confirms the target, run the Next step",
|
|
6935
|
+
next: [rerun({
|
|
6936
|
+
remove: [CONFIRM_FLAG.flag],
|
|
6937
|
+
add: [CONFIRM_FLAG.flag, expected]
|
|
6938
|
+
})]
|
|
6991
6939
|
};
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
}
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6940
|
+
if (given === void 0) throw new UsageError({
|
|
6941
|
+
code: CONFIRMATION_REQUIRED,
|
|
6942
|
+
message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
|
|
6943
|
+
...advice
|
|
6944
|
+
});
|
|
6945
|
+
throw new UsageError({
|
|
6946
|
+
code: CONFIRMATION_MISMATCH,
|
|
6947
|
+
message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
|
|
6948
|
+
...advice
|
|
6949
|
+
});
|
|
6998
6950
|
}
|
|
6999
6951
|
//#endregion
|
|
7000
|
-
//#region ../agent-cli/dist/context/
|
|
7001
|
-
const
|
|
7002
|
-
/**
|
|
7003
|
-
function
|
|
7004
|
-
|
|
7005
|
-
return join(config, cliName, ".env");
|
|
7006
|
-
}
|
|
7007
|
-
function readDotenv(path, fileError) {
|
|
7008
|
-
let text;
|
|
7009
|
-
try {
|
|
7010
|
-
text = readFileSync(path, "utf8");
|
|
7011
|
-
} catch (error) {
|
|
7012
|
-
if (isMissingFile(error)) return {};
|
|
7013
|
-
throw fileError(error, {
|
|
7014
|
-
path,
|
|
7015
|
-
access: "read"
|
|
7016
|
-
});
|
|
7017
|
-
}
|
|
7018
|
-
return parseEnv(text);
|
|
7019
|
-
}
|
|
7020
|
-
/** Looks `name` up in the env, then in `envFile`; an empty value counts as unset. */
|
|
7021
|
-
function secretOf({ envFile, env, fileError }, name) {
|
|
7022
|
-
const fromEnv = env[name];
|
|
7023
|
-
if (fromEnv) return {
|
|
7024
|
-
value: fromEnv,
|
|
7025
|
-
origin: { kind: "env" }
|
|
7026
|
-
};
|
|
7027
|
-
const fromFile = readDotenv(envFile, fileError)[name];
|
|
7028
|
-
return fromFile ? {
|
|
7029
|
-
value: fromFile,
|
|
7030
|
-
origin: {
|
|
7031
|
-
kind: "file",
|
|
7032
|
-
path: envFile
|
|
7033
|
-
}
|
|
7034
|
-
} : void 0;
|
|
7035
|
-
}
|
|
7036
|
-
function isNeeded({ optional }) {
|
|
7037
|
-
return optional !== true;
|
|
7038
|
-
}
|
|
7039
|
-
function missingIn(scope, secrets) {
|
|
7040
|
-
return secrets.filter((secret) => isNeeded(secret) && secretOf(scope, secret.name) === void 0);
|
|
7041
|
-
}
|
|
7042
|
-
function getItFrom({ get }) {
|
|
7043
|
-
return `(get it from ${get})`;
|
|
7044
|
-
}
|
|
7045
|
-
function whereToGet(secrets) {
|
|
7046
|
-
return secrets.map((secret) => `${secret.name} ${getItFrom(secret)}`).join(", ");
|
|
7047
|
-
}
|
|
7048
|
-
/** Fails with `SECRET_MISSING`, naming every missing Secret at once, unless all are set. */
|
|
7049
|
-
function checkSecrets(secrets, scope, rerun) {
|
|
7050
|
-
const missing = missingIn(scope, secrets);
|
|
7051
|
-
if (missing.length === 0) return;
|
|
7052
|
-
const names = missing.map(({ name }) => name).join(", ");
|
|
6952
|
+
//#region ../agent-cli/dist/context/cost.js
|
|
6953
|
+
const NOT_CONFIRMED = "NOT_CONFIRMED";
|
|
6954
|
+
/** Refuses a run whose Plan states a cost the caller has not approved with `--yes`. */
|
|
6955
|
+
function checkCost({ rerun, commandLine, yes }, cost) {
|
|
6956
|
+
if (cost === void 0 || yes) return;
|
|
7053
6957
|
throw new CliError({
|
|
7054
|
-
code:
|
|
7055
|
-
message:
|
|
7056
|
-
hint:
|
|
7057
|
-
next: [rerun]
|
|
6958
|
+
code: NOT_CONFIRMED,
|
|
6959
|
+
message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
|
|
6960
|
+
hint: "once the user approves the cost, run the Next step",
|
|
6961
|
+
next: [rerun({ add: [YES_FLAG.flag] })]
|
|
7058
6962
|
});
|
|
7059
6963
|
}
|
|
7060
|
-
function originOf({ origin }) {
|
|
7061
|
-
return origin.kind === "env" ? "the environment" : origin.path;
|
|
7062
|
-
}
|
|
7063
|
-
/**
|
|
7064
|
-
* The `doctor` row over every distinct Secret the CLI and its Commands declare; none without any. A
|
|
7065
|
-
* name needed anywhere counts as needed, and an unset optional one goes unmentioned.
|
|
7066
|
-
*/
|
|
7067
|
-
function secretRows(secrets, commands) {
|
|
7068
|
-
const declared = declaredIn(secrets, commands, (command) => command.secrets);
|
|
7069
|
-
if (declared.length === 0) return [];
|
|
7070
|
-
const needed = new Map(declaredIn(secrets.filter(isNeeded), commands, (command) => command.secrets?.filter(isNeeded)).map((declaration) => [declaration.name, declaration]));
|
|
7071
|
-
return [{
|
|
7072
|
-
name: "secrets",
|
|
7073
|
-
run: ({ env, envFile, fileError }) => {
|
|
7074
|
-
const read = declared.map((declaration) => ({
|
|
7075
|
-
declaration,
|
|
7076
|
-
secret: secretOf({
|
|
7077
|
-
env,
|
|
7078
|
-
envFile,
|
|
7079
|
-
fileError
|
|
7080
|
-
}, declaration.name)
|
|
7081
|
-
}));
|
|
7082
|
-
const missing = read.flatMap(({ declaration, secret }) => {
|
|
7083
|
-
const neededAs = needed.get(declaration.name);
|
|
7084
|
-
return secret === void 0 && neededAs !== void 0 ? [neededAs] : [];
|
|
7085
|
-
});
|
|
7086
|
-
if (missing.length > 0) return {
|
|
7087
|
-
state: "problem",
|
|
7088
|
-
message: `${missing.map((declaration) => `${declaration.name} is not set ${getItFrom(declaration)}`).join(", ")}; add ${missing.length === 1 ? "it" : "them"} to ${envFile} or the environment`
|
|
7089
|
-
};
|
|
7090
|
-
const found = read.flatMap(({ declaration, secret }) => secret === void 0 ? [] : [`${declaration.name} from ${originOf(secret)}`]);
|
|
7091
|
-
return {
|
|
7092
|
-
state: "ok",
|
|
7093
|
-
message: found.length > 0 ? found.join(", ") : "no Secret is set, and none is needed"
|
|
7094
|
-
};
|
|
7095
|
-
}
|
|
7096
|
-
}];
|
|
7097
|
-
}
|
|
7098
|
-
//#endregion
|
|
7099
|
-
//#region ../agent-cli/dist/version.js
|
|
7100
|
-
/** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
|
|
7101
|
-
function buildStamp() {
|
|
7102
|
-
return {
|
|
7103
|
-
"sha": "8ba1668",
|
|
7104
|
-
"dirty": false,
|
|
7105
|
-
"builtAt": "2026-11-08T16:12:26.000Z"
|
|
7106
|
-
};
|
|
7107
|
-
}
|
|
7108
|
-
/** The first line of `--version`: the version, and the commit and time of a stamped build. */
|
|
7109
|
-
function versionLine({ version }) {
|
|
7110
|
-
const build = buildStamp();
|
|
7111
|
-
return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
|
|
7112
|
-
}
|
|
7113
6964
|
//#endregion
|
|
7114
6965
|
//#region ../agent-cli/dist/context/agent.js
|
|
7115
6966
|
const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
|
|
@@ -7145,33 +6996,772 @@ function isAgent(env) {
|
|
|
7145
6996
|
return MARKERS.some((marker) => marker(env));
|
|
7146
6997
|
}
|
|
7147
6998
|
//#endregion
|
|
7148
|
-
//#region ../agent-cli/dist/
|
|
7149
|
-
const
|
|
7150
|
-
const
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
function
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
if (
|
|
7160
|
-
return
|
|
6999
|
+
//#region ../agent-cli/dist/context/errno.js
|
|
7000
|
+
const PERMISSION_DENIED = "PERMISSION_DENIED";
|
|
7001
|
+
const DISK_FULL = "DISK_FULL";
|
|
7002
|
+
const NOT_FOUND = "NOT_FOUND";
|
|
7003
|
+
const ALREADY_EXISTS = "ALREADY_EXISTS";
|
|
7004
|
+
const NOT_A_FILE = "NOT_A_FILE";
|
|
7005
|
+
function permissionAdvice({ path, access, rerun, env, platform }) {
|
|
7006
|
+
if (platform === "win32") return {
|
|
7007
|
+
hint: `a program may hold ${path} open: close it, then run the Next step`,
|
|
7008
|
+
next: [rerun]
|
|
7009
|
+
};
|
|
7010
|
+
if (!isAgent(env)) return { hint: `check the permissions of ${path} and its folder` };
|
|
7011
|
+
return {
|
|
7012
|
+
hint: `your sandbox may block it: ask your user to type ! then the Next step, or ${access === "read" ? `to allow reading ${path} in the sandbox` : `to allow ${path} in the sandbox's write list`}`,
|
|
7013
|
+
next: [rerun]
|
|
7014
|
+
};
|
|
7161
7015
|
}
|
|
7162
|
-
function
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
const right = b.split(".");
|
|
7168
|
-
for (let i = 0; i < Math.min(left.length, right.length); i += 1) {
|
|
7169
|
-
const difference = compareIdentifiers(left[i] ?? "", right[i] ?? "");
|
|
7170
|
-
if (difference !== 0) return difference;
|
|
7171
|
-
}
|
|
7172
|
-
return left.length - right.length;
|
|
7016
|
+
function fileErrorOf(scope) {
|
|
7017
|
+
return (error, at) => classifyErrno(error, {
|
|
7018
|
+
...at,
|
|
7019
|
+
...scope
|
|
7020
|
+
});
|
|
7173
7021
|
}
|
|
7174
|
-
|
|
7022
|
+
function classifyErrno(error, context) {
|
|
7023
|
+
const { path } = context;
|
|
7024
|
+
switch (errnoOf(error)) {
|
|
7025
|
+
case "EACCES":
|
|
7026
|
+
case "EPERM":
|
|
7027
|
+
case "EROFS":
|
|
7028
|
+
case "EBUSY": return new CliError({
|
|
7029
|
+
code: PERMISSION_DENIED,
|
|
7030
|
+
message: `permission denied: ${path}`,
|
|
7031
|
+
...permissionAdvice(context)
|
|
7032
|
+
}, { cause: error });
|
|
7033
|
+
case "ENOSPC":
|
|
7034
|
+
case "EDQUOT": return new CliError({
|
|
7035
|
+
code: DISK_FULL,
|
|
7036
|
+
message: `no space left on the disk holding ${path}`,
|
|
7037
|
+
hint: "free space on that disk, then run the Next step",
|
|
7038
|
+
next: [context.rerun]
|
|
7039
|
+
}, { cause: error });
|
|
7040
|
+
case "ENOENT":
|
|
7041
|
+
case "ENOTDIR":
|
|
7042
|
+
case "ENAMETOOLONG": return new CliError({
|
|
7043
|
+
code: NOT_FOUND,
|
|
7044
|
+
message: `not found: ${path}`,
|
|
7045
|
+
hint: `check that ${path} and its folder exist`
|
|
7046
|
+
}, { cause: error });
|
|
7047
|
+
case "EEXIST": return new CliError({
|
|
7048
|
+
code: ALREADY_EXISTS,
|
|
7049
|
+
message: `already exists: ${path}`,
|
|
7050
|
+
hint: `pass another path, or remove ${path}`
|
|
7051
|
+
}, { cause: error });
|
|
7052
|
+
case "EISDIR": return new CliError({
|
|
7053
|
+
code: NOT_A_FILE,
|
|
7054
|
+
message: `not a file: ${path} is a folder`,
|
|
7055
|
+
hint: `pass the path of a file, not of the folder ${path}`
|
|
7056
|
+
}, { cause: error });
|
|
7057
|
+
default: return error;
|
|
7058
|
+
}
|
|
7059
|
+
}
|
|
7060
|
+
//#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
|
+
//#region ../agent-cli/dist/context/operator.js
|
|
7205
|
+
const NEEDS_OPERATOR = "NEEDS_OPERATOR";
|
|
7206
|
+
/** Fails with `NEEDS_OPERATOR` when a Coding agent runs an Operator Command. */
|
|
7207
|
+
function checkOperator({ operator, commandLine }, env, rerun) {
|
|
7208
|
+
if (!operator || !isAgent(env)) return;
|
|
7209
|
+
throw new CliError({
|
|
7210
|
+
code: NEEDS_OPERATOR,
|
|
7211
|
+
message: `\`${commandLine}\` must be run by a person at a terminal, not a Coding agent`,
|
|
7212
|
+
hint: "ask the operator to type ! then the Next step",
|
|
7213
|
+
next: [rerun]
|
|
7214
|
+
});
|
|
7215
|
+
}
|
|
7216
|
+
//#endregion
|
|
7217
|
+
//#region ../agent-cli/dist/context/files.js
|
|
7218
|
+
const TEMP_FILE = /^\..+\.agent-cli-(\d+)\.tmp$/;
|
|
7219
|
+
/** Where the Runtime writes `path` before moving it into place; a dead writer's is swept. */
|
|
7220
|
+
function tempPathOf(path) {
|
|
7221
|
+
return join(dirname(path), `.${basename(path)}.agent-cli-${process.pid}.tmp`);
|
|
7222
|
+
}
|
|
7223
|
+
function isAlive(pid) {
|
|
7224
|
+
try {
|
|
7225
|
+
process.kill(pid, 0);
|
|
7226
|
+
return true;
|
|
7227
|
+
} catch (error) {
|
|
7228
|
+
return errnoOf(error) === "EPERM";
|
|
7229
|
+
}
|
|
7230
|
+
}
|
|
7231
|
+
async function sweep(folder) {
|
|
7232
|
+
const dead = (await readdir(folder)).filter((name) => {
|
|
7233
|
+
const pid = TEMP_FILE.exec(name)?.[1];
|
|
7234
|
+
return pid !== void 0 && !isAlive(Number(pid));
|
|
7235
|
+
});
|
|
7236
|
+
await Promise.all(dead.map((name) => rm(join(folder, name), { force: true })));
|
|
7237
|
+
}
|
|
7238
|
+
/** The file a symlink at `path` points to, so a replacing write keeps the link. */
|
|
7239
|
+
async function targetOf$1(path) {
|
|
7240
|
+
try {
|
|
7241
|
+
return await realpath(path);
|
|
7242
|
+
} catch (error) {
|
|
7243
|
+
if (isMissingFile(error)) return path;
|
|
7244
|
+
throw error;
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7247
|
+
async function writeWithMode(path, data, mode) {
|
|
7248
|
+
if (mode === void 0) {
|
|
7249
|
+
await writeFile(path, data);
|
|
7250
|
+
return;
|
|
7251
|
+
}
|
|
7252
|
+
await writeFile(path, data, { mode });
|
|
7253
|
+
await chmod(path, mode);
|
|
7254
|
+
}
|
|
7255
|
+
/** What `link` answers on a file system without hard links, such as exFAT. */
|
|
7256
|
+
const NO_HARD_LINKS = /* @__PURE__ */ new Set([
|
|
7257
|
+
"EPERM",
|
|
7258
|
+
"ENOTSUP",
|
|
7259
|
+
"EXDEV"
|
|
7260
|
+
]);
|
|
7261
|
+
/** A link fails atomically when `path` is taken, so no reader ever sees half a file there. */
|
|
7262
|
+
async function linkNew(temp, path, data, mode) {
|
|
7263
|
+
try {
|
|
7264
|
+
await link(temp, path);
|
|
7265
|
+
} catch (error) {
|
|
7266
|
+
if (!NO_HARD_LINKS.has(errnoOf(error))) throw error;
|
|
7267
|
+
await createNew(path, data, mode);
|
|
7268
|
+
}
|
|
7269
|
+
}
|
|
7270
|
+
/** A file this call created and could not finish is removed, so a rerun does not find half of it. */
|
|
7271
|
+
async function createNew(path, data, mode) {
|
|
7272
|
+
const handle = await open(path, "wx", mode);
|
|
7273
|
+
try {
|
|
7274
|
+
await handle.writeFile(data);
|
|
7275
|
+
if (mode !== void 0) await handle.chmod(mode);
|
|
7276
|
+
} catch (error) {
|
|
7277
|
+
await handle.close();
|
|
7278
|
+
await rm(path, { force: true });
|
|
7279
|
+
throw error;
|
|
7280
|
+
}
|
|
7281
|
+
await handle.close();
|
|
7282
|
+
}
|
|
7283
|
+
const RENAME_ATTEMPTS = 10;
|
|
7284
|
+
const RENAME_BACKOFF_MS = 10;
|
|
7285
|
+
const HELD_BY_ANOTHER_PROGRAM = /* @__PURE__ */ new Set(["EPERM", "EBUSY"]);
|
|
7286
|
+
async function renameRetrying(from, to) {
|
|
7287
|
+
for (let attempt = 1;; attempt++) try {
|
|
7288
|
+
await rename(from, to);
|
|
7289
|
+
return;
|
|
7290
|
+
} catch (error) {
|
|
7291
|
+
if (!(process.platform === "win32" && HELD_BY_ANOTHER_PROGRAM.has(errnoOf(error))) || attempt === RENAME_ATTEMPTS) throw error;
|
|
7292
|
+
await setTimeout$1(attempt * RENAME_BACKOFF_MS);
|
|
7293
|
+
}
|
|
7294
|
+
}
|
|
7295
|
+
/** Real files for one run: the first write to each folder sweeps the temp files of dead writers. */
|
|
7296
|
+
function realFiles() {
|
|
7297
|
+
const swept = /* @__PURE__ */ new Set();
|
|
7298
|
+
return {
|
|
7299
|
+
exists: async (path) => {
|
|
7300
|
+
try {
|
|
7301
|
+
await access(path);
|
|
7302
|
+
return true;
|
|
7303
|
+
} catch (error) {
|
|
7304
|
+
if (isNothingThere(error)) return false;
|
|
7305
|
+
throw error;
|
|
7306
|
+
}
|
|
7307
|
+
},
|
|
7308
|
+
write: async (path, data, options = {}) => {
|
|
7309
|
+
await mkdir(dirname(path), { recursive: true });
|
|
7310
|
+
const target = options.exclusive === true ? path : await targetOf$1(path);
|
|
7311
|
+
const folder = dirname(target);
|
|
7312
|
+
if (!swept.has(folder)) {
|
|
7313
|
+
await sweep(folder);
|
|
7314
|
+
swept.add(folder);
|
|
7315
|
+
}
|
|
7316
|
+
const temp = tempPathOf(target);
|
|
7317
|
+
try {
|
|
7318
|
+
await writeWithMode(temp, data, options.mode);
|
|
7319
|
+
if (options.exclusive === true) {
|
|
7320
|
+
await linkNew(temp, target, data, options.mode);
|
|
7321
|
+
await rm(temp);
|
|
7322
|
+
} else await renameRetrying(temp, target);
|
|
7323
|
+
} catch (error) {
|
|
7324
|
+
await rm(temp, { force: true });
|
|
7325
|
+
throw error;
|
|
7326
|
+
}
|
|
7327
|
+
},
|
|
7328
|
+
read: (path) => readFile(path),
|
|
7329
|
+
list: (folder) => readdir(folder),
|
|
7330
|
+
remove: (path) => rm(path, {
|
|
7331
|
+
recursive: true,
|
|
7332
|
+
force: true
|
|
7333
|
+
}),
|
|
7334
|
+
mkdir: async (path, options) => {
|
|
7335
|
+
const mode = options?.mode;
|
|
7336
|
+
if (options?.exclusive !== true) {
|
|
7337
|
+
await mkdir(path, {
|
|
7338
|
+
recursive: true,
|
|
7339
|
+
mode
|
|
7340
|
+
});
|
|
7341
|
+
return;
|
|
7342
|
+
}
|
|
7343
|
+
await mkdir(dirname(path), {
|
|
7344
|
+
recursive: true,
|
|
7345
|
+
mode
|
|
7346
|
+
});
|
|
7347
|
+
await mkdir(path, { mode });
|
|
7348
|
+
},
|
|
7349
|
+
stat: async (path) => {
|
|
7350
|
+
try {
|
|
7351
|
+
const found = await stat(path, { bigint: true });
|
|
7352
|
+
return {
|
|
7353
|
+
size: Number(found.size),
|
|
7354
|
+
id: `${found.dev}:${found.ino}`,
|
|
7355
|
+
folder: found.isDirectory()
|
|
7356
|
+
};
|
|
7357
|
+
} catch (error) {
|
|
7358
|
+
if (isNothingThere(error)) return void 0;
|
|
7359
|
+
throw error;
|
|
7360
|
+
}
|
|
7361
|
+
},
|
|
7362
|
+
realPath: async (path) => {
|
|
7363
|
+
try {
|
|
7364
|
+
return await realpath(path);
|
|
7365
|
+
} catch (error) {
|
|
7366
|
+
if (isNothingThere(error)) return void 0;
|
|
7367
|
+
throw error;
|
|
7368
|
+
}
|
|
7369
|
+
}
|
|
7370
|
+
};
|
|
7371
|
+
}
|
|
7372
|
+
/**
|
|
7373
|
+
* `files` resolving paths against `cwd`, its failures classified by errno; `list` drops the
|
|
7374
|
+
* Runtime's temp files and sorts.
|
|
7375
|
+
*/
|
|
7376
|
+
function classifiedFiles(files, { cwd, fileError }) {
|
|
7377
|
+
async function at(access, path, work) {
|
|
7378
|
+
const target = resolve(cwd, path);
|
|
7379
|
+
try {
|
|
7380
|
+
return await work(target);
|
|
7381
|
+
} catch (error) {
|
|
7382
|
+
throw fileError(error, {
|
|
7383
|
+
path: target,
|
|
7384
|
+
access
|
|
7385
|
+
});
|
|
7386
|
+
}
|
|
7387
|
+
}
|
|
7388
|
+
return {
|
|
7389
|
+
exists: (path) => at("read", path, (target) => files.exists(target)),
|
|
7390
|
+
write: (path, data, options) => at("write", path, (target) => files.write(target, data, options)),
|
|
7391
|
+
read: (path) => at("read", path, (target) => files.read(target)),
|
|
7392
|
+
list: (folder) => at("read", folder, async (target) => (await files.list(target)).filter((name) => !TEMP_FILE.test(name)).sort()),
|
|
7393
|
+
remove: (path) => at("write", path, (target) => files.remove(target)),
|
|
7394
|
+
mkdir: (path, options) => at("write", path, (target) => files.mkdir(target, options)),
|
|
7395
|
+
stat: (path) => at("read", path, (target) => files.stat(target)),
|
|
7396
|
+
realPath: (path) => at("read", path, (target) => files.realPath(target))
|
|
7397
|
+
};
|
|
7398
|
+
}
|
|
7399
|
+
//#endregion
|
|
7400
|
+
//#region ../agent-cli/dist/context/outputs.js
|
|
7401
|
+
const OUTPUT_EXISTS = "OUTPUT_EXISTS";
|
|
7402
|
+
/** The outputs of a run on `files`, whose paths resolve against `cwd`. */
|
|
7403
|
+
function outputsOf(files, { cwd, fileError }) {
|
|
7404
|
+
const classified = classifiedFiles(files, {
|
|
7405
|
+
cwd,
|
|
7406
|
+
fileError
|
|
7407
|
+
});
|
|
7408
|
+
const unwritten = /* @__PURE__ */ new Set();
|
|
7409
|
+
let forceStep = [];
|
|
7410
|
+
const exclusiveWrite = async (path, data, options) => {
|
|
7411
|
+
const target = resolve(cwd, path);
|
|
7412
|
+
unwritten.delete(target);
|
|
7413
|
+
try {
|
|
7414
|
+
await classified.write(path, data, {
|
|
7415
|
+
...options,
|
|
7416
|
+
exclusive: true
|
|
7417
|
+
});
|
|
7418
|
+
} catch (error) {
|
|
7419
|
+
unwritten.add(target);
|
|
7420
|
+
if (!(error instanceof CliError) || error.code !== "ALREADY_EXISTS") throw error;
|
|
7421
|
+
throw new CliError({
|
|
7422
|
+
code: ALREADY_EXISTS,
|
|
7423
|
+
message: error.message,
|
|
7424
|
+
hint: "once the user agrees to replace it, run the Next step",
|
|
7425
|
+
next: [forceStep]
|
|
7426
|
+
}, { cause: error });
|
|
7427
|
+
}
|
|
7428
|
+
};
|
|
7429
|
+
const guarded = {
|
|
7430
|
+
...classified,
|
|
7431
|
+
write: (path, data, options) => unwritten.has(resolve(cwd, path)) ? exclusiveWrite(path, data, options) : classified.write(path, data, options)
|
|
7432
|
+
};
|
|
7433
|
+
return {
|
|
7434
|
+
files: guarded,
|
|
7435
|
+
refuseOverwrite: async (outputs, { rerun, commandLine, force }) => {
|
|
7436
|
+
if (force) return;
|
|
7437
|
+
const found = await Promise.all(outputs.map((path) => guarded.exists(path)));
|
|
7438
|
+
const existing = outputs.filter((_path, index) => found[index]);
|
|
7439
|
+
if (existing.length === 0) return;
|
|
7440
|
+
const them = existing.length === 1 ? "it" : "them";
|
|
7441
|
+
throw new CliError({
|
|
7442
|
+
code: OUTPUT_EXISTS,
|
|
7443
|
+
message: `\`${commandLine}\` would overwrite ${existing.join(", ")}`,
|
|
7444
|
+
hint: `once the user agrees to replace ${them}, run the Next step`,
|
|
7445
|
+
next: [rerun({ add: [FORCE_FLAG.flag] })]
|
|
7446
|
+
});
|
|
7447
|
+
},
|
|
7448
|
+
claim: async (outputs, { rerun, force }) => {
|
|
7449
|
+
await probe(outputs, files, {
|
|
7450
|
+
cwd,
|
|
7451
|
+
fileError
|
|
7452
|
+
});
|
|
7453
|
+
if (force) return;
|
|
7454
|
+
forceStep = rerun({ add: [FORCE_FLAG.flag] });
|
|
7455
|
+
for (const path of outputs) unwritten.add(resolve(cwd, path));
|
|
7456
|
+
}
|
|
7457
|
+
};
|
|
7458
|
+
}
|
|
7459
|
+
async function probe(outputs, files, { cwd, fileError }) {
|
|
7460
|
+
const folders = new Set(outputs.map((path) => dirname(resolve(cwd, path))));
|
|
7461
|
+
await Promise.all([...folders].map(async (folder) => {
|
|
7462
|
+
const probePath = tempPathOf(join(folder, "outputs-probe"));
|
|
7463
|
+
try {
|
|
7464
|
+
await files.mkdir(folder);
|
|
7465
|
+
await files.write(probePath, "");
|
|
7466
|
+
await files.remove(probePath);
|
|
7467
|
+
} catch (error) {
|
|
7468
|
+
throw fileError(error, {
|
|
7469
|
+
path: folder,
|
|
7470
|
+
access: "write"
|
|
7471
|
+
});
|
|
7472
|
+
}
|
|
7473
|
+
}));
|
|
7474
|
+
}
|
|
7475
|
+
//#endregion
|
|
7476
|
+
//#region ../agent-cli/dist/context/project.js
|
|
7477
|
+
const PROJECT_INVALID = "PROJECT_INVALID";
|
|
7478
|
+
function invalid(path, problem, rerun, options) {
|
|
7479
|
+
return new CliError({
|
|
7480
|
+
code: PROJECT_INVALID,
|
|
7481
|
+
message: `the Project file ${path} is invalid: ${problem}`,
|
|
7482
|
+
hint: `fix ${path}, then run the Next step`,
|
|
7483
|
+
next: [rerun]
|
|
7484
|
+
}, options);
|
|
7485
|
+
}
|
|
7486
|
+
function readText(path, fileError) {
|
|
7487
|
+
try {
|
|
7488
|
+
return readFileSync(path, "utf8");
|
|
7489
|
+
} catch (error) {
|
|
7490
|
+
if (isMissingFile(error)) return void 0;
|
|
7491
|
+
throw fileError(error, {
|
|
7492
|
+
path,
|
|
7493
|
+
access: "read"
|
|
7494
|
+
});
|
|
7495
|
+
}
|
|
7496
|
+
}
|
|
7497
|
+
function parsed(path, text, schema, rerun) {
|
|
7498
|
+
let json;
|
|
7499
|
+
try {
|
|
7500
|
+
json = JSON.parse(text);
|
|
7501
|
+
} catch (error) {
|
|
7502
|
+
throw invalid(path, messageOf(error), rerun, { cause: error });
|
|
7503
|
+
}
|
|
7504
|
+
const result = schema.safeParse(json);
|
|
7505
|
+
if (!result.success) throw invalid(path, prettifyError(result.error), rerun);
|
|
7506
|
+
return result.data;
|
|
7507
|
+
}
|
|
7508
|
+
function projectFrom(projectFile, folder, scope) {
|
|
7509
|
+
const path = join(folder, projectFile.file);
|
|
7510
|
+
const text = readText(path, scope.fileError);
|
|
7511
|
+
if (text !== void 0) return {
|
|
7512
|
+
root: folder,
|
|
7513
|
+
settings: parsed(path, text, projectFile.schema, scope.rerun)
|
|
7514
|
+
};
|
|
7515
|
+
const parent = dirname(folder);
|
|
7516
|
+
return parent === folder ? void 0 : projectFrom(projectFile, parent, scope);
|
|
7517
|
+
}
|
|
7518
|
+
/** The nearest Project file at or above `cwd`, parsed by its schema; undefined when there is none. */
|
|
7519
|
+
function projectOf(projectFile, cwd, scope) {
|
|
7520
|
+
return projectFrom(projectFile, resolve(cwd), scope);
|
|
7521
|
+
}
|
|
7522
|
+
//#endregion
|
|
7523
|
+
//#region ../agent-cli/dist/context/secrets.js
|
|
7524
|
+
const SECRET_MISSING = "SECRET_MISSING";
|
|
7525
|
+
/** `$XDG_CONFIG_HOME/<cli>/.env`, with `~/.config` when the variable is unset or empty. */
|
|
7526
|
+
function envFileOf(cliName, env) {
|
|
7527
|
+
const config = env.XDG_CONFIG_HOME || join(env.HOME || homedir(), ".config");
|
|
7528
|
+
return join(config, cliName, ".env");
|
|
7529
|
+
}
|
|
7530
|
+
function readDotenv(path, fileError) {
|
|
7531
|
+
let text;
|
|
7532
|
+
try {
|
|
7533
|
+
text = readFileSync(path, "utf8");
|
|
7534
|
+
} catch (error) {
|
|
7535
|
+
if (isMissingFile(error)) return {};
|
|
7536
|
+
throw fileError(error, {
|
|
7537
|
+
path,
|
|
7538
|
+
access: "read"
|
|
7539
|
+
});
|
|
7540
|
+
}
|
|
7541
|
+
return parseEnv(text);
|
|
7542
|
+
}
|
|
7543
|
+
/** Looks `name` up in the env, then in `envFile`; an empty value counts as unset. */
|
|
7544
|
+
function secretOf({ envFile, env, fileError }, name) {
|
|
7545
|
+
const fromEnv = env[name];
|
|
7546
|
+
if (fromEnv) return {
|
|
7547
|
+
value: fromEnv,
|
|
7548
|
+
origin: { kind: "env" }
|
|
7549
|
+
};
|
|
7550
|
+
const fromFile = readDotenv(envFile, fileError)[name];
|
|
7551
|
+
return fromFile ? {
|
|
7552
|
+
value: fromFile,
|
|
7553
|
+
origin: {
|
|
7554
|
+
kind: "file",
|
|
7555
|
+
path: envFile
|
|
7556
|
+
}
|
|
7557
|
+
} : void 0;
|
|
7558
|
+
}
|
|
7559
|
+
function isNeeded({ optional }) {
|
|
7560
|
+
return optional !== true;
|
|
7561
|
+
}
|
|
7562
|
+
function missingIn(scope, secrets) {
|
|
7563
|
+
return secrets.filter((secret) => isNeeded(secret) && secretOf(scope, secret.name) === void 0);
|
|
7564
|
+
}
|
|
7565
|
+
function getItFrom({ get }) {
|
|
7566
|
+
return `(get it from ${get})`;
|
|
7567
|
+
}
|
|
7568
|
+
function whereToGet(secrets) {
|
|
7569
|
+
return secrets.map((secret) => `${secret.name} ${getItFrom(secret)}`).join(", ");
|
|
7570
|
+
}
|
|
7571
|
+
/** Fails with `SECRET_MISSING`, naming every missing Secret at once, unless all are set. */
|
|
7572
|
+
function checkSecrets(secrets, scope, rerun) {
|
|
7573
|
+
const missing = missingIn(scope, secrets);
|
|
7574
|
+
if (missing.length === 0) return;
|
|
7575
|
+
const names = missing.map(({ name }) => name).join(", ");
|
|
7576
|
+
throw new CliError({
|
|
7577
|
+
code: SECRET_MISSING,
|
|
7578
|
+
message: `${missing.length === 1 ? "a Secret it needs is" : "Secrets it needs are"} not set: ${names}`,
|
|
7579
|
+
hint: `set ${whereToGet(missing)} in the environment or in ${scope.envFile}, then run the Next step`,
|
|
7580
|
+
next: [rerun]
|
|
7581
|
+
});
|
|
7582
|
+
}
|
|
7583
|
+
function originOf({ origin }) {
|
|
7584
|
+
return origin.kind === "env" ? "the environment" : origin.path;
|
|
7585
|
+
}
|
|
7586
|
+
/**
|
|
7587
|
+
* The `doctor` row over every distinct Secret the CLI and its Commands declare; none without any. A
|
|
7588
|
+
* name needed anywhere counts as needed, and an unset optional one goes unmentioned.
|
|
7589
|
+
*/
|
|
7590
|
+
function secretRows(secrets, commands) {
|
|
7591
|
+
const declared = declaredIn(secrets, commands, (command) => command.secrets);
|
|
7592
|
+
if (declared.length === 0) return [];
|
|
7593
|
+
const needed = new Map(declaredIn(secrets.filter(isNeeded), commands, (command) => command.secrets?.filter(isNeeded)).map((declaration) => [declaration.name, declaration]));
|
|
7594
|
+
return [{
|
|
7595
|
+
name: "secrets",
|
|
7596
|
+
run: ({ env, envFile, fileError }) => {
|
|
7597
|
+
const read = declared.map((declaration) => ({
|
|
7598
|
+
declaration,
|
|
7599
|
+
secret: secretOf({
|
|
7600
|
+
env,
|
|
7601
|
+
envFile,
|
|
7602
|
+
fileError
|
|
7603
|
+
}, declaration.name)
|
|
7604
|
+
}));
|
|
7605
|
+
const missing = read.flatMap(({ declaration, secret }) => {
|
|
7606
|
+
const neededAs = needed.get(declaration.name);
|
|
7607
|
+
return secret === void 0 && neededAs !== void 0 ? [neededAs] : [];
|
|
7608
|
+
});
|
|
7609
|
+
if (missing.length > 0) return {
|
|
7610
|
+
state: "problem",
|
|
7611
|
+
message: `${missing.map((declaration) => `${declaration.name} is not set ${getItFrom(declaration)}`).join(", ")}; add ${missing.length === 1 ? "it" : "them"} to ${envFile} or the environment`
|
|
7612
|
+
};
|
|
7613
|
+
const found = read.flatMap(({ declaration, secret }) => secret === void 0 ? [] : [`${declaration.name} from ${originOf(secret)}`]);
|
|
7614
|
+
return {
|
|
7615
|
+
state: "ok",
|
|
7616
|
+
message: found.length > 0 ? found.join(", ") : "no Secret is set, and none is needed"
|
|
7617
|
+
};
|
|
7618
|
+
}
|
|
7619
|
+
}];
|
|
7620
|
+
}
|
|
7621
|
+
//#endregion
|
|
7622
|
+
//#region ../agent-cli/dist/runtime-codes.js
|
|
7623
|
+
const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
|
|
7624
|
+
const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
|
|
7625
|
+
const NOT_ON_PATH = "NOT_ON_PATH";
|
|
7626
|
+
const REGISTRY_UNREACHABLE = "REGISTRY_UNREACHABLE";
|
|
7627
|
+
const REGISTRY_AUTH = "REGISTRY_AUTH";
|
|
7628
|
+
const INSTALL_FAILED = "INSTALL_FAILED";
|
|
7629
|
+
const NPM_FAILED = "NPM_FAILED";
|
|
7630
|
+
const NO_LATEST = "NO_LATEST";
|
|
7631
|
+
/** The Runtime codes any Command can meet, which no Command declares. */
|
|
7632
|
+
const anyCommandErrors = {
|
|
7633
|
+
[INVALID_INPUT]: { class: "caller" },
|
|
7634
|
+
[UNKNOWN_OPTION]: { class: "caller" },
|
|
7635
|
+
[UNKNOWN_COMMAND]: { class: "caller" },
|
|
7636
|
+
[UNKNOWN_FIELD]: { class: "caller" },
|
|
7637
|
+
[CONFIRMATION_REQUIRED]: { class: "caller" },
|
|
7638
|
+
[CONFIRMATION_MISMATCH]: { class: "caller" },
|
|
7639
|
+
[NOT_CONFIRMED]: { class: "operator" },
|
|
7640
|
+
[NEEDS_OPERATOR]: { class: "operator" },
|
|
7641
|
+
[OUTPUT_EXISTS]: { class: "caller" },
|
|
7642
|
+
[STDIN_STALLED]: { class: "caller" },
|
|
7643
|
+
[INTERRUPTED]: { class: "interrupted" },
|
|
7644
|
+
[DEADLINE_EXCEEDED]: { class: "wait" },
|
|
7645
|
+
[INTERNAL]: { class: "bug" },
|
|
7646
|
+
[PERMISSION_DENIED]: { class: "operator" },
|
|
7647
|
+
[DISK_FULL]: { class: "operator" },
|
|
7648
|
+
[NOT_FOUND]: { class: "caller" },
|
|
7649
|
+
[ALREADY_EXISTS]: { class: "caller" },
|
|
7650
|
+
[NOT_A_FILE]: { class: "caller" },
|
|
7651
|
+
[TOOL_FAILED]: { class: "operator" },
|
|
7652
|
+
[TOOL_MISSING]: { class: "operator" },
|
|
7653
|
+
[SECRET_MISSING]: { class: "operator" },
|
|
7654
|
+
[PROJECT_INVALID]: { class: "operator" }
|
|
7655
|
+
};
|
|
7656
|
+
/**
|
|
7657
|
+
* The Runtime codes only some Commands reach: each Command that throws one, Built-in or the CLI's
|
|
7658
|
+
* own, declares it in `errors`, as it does a code of the CLI's Error table.
|
|
7659
|
+
*/
|
|
7660
|
+
const declaredRuntimeErrors = {
|
|
7661
|
+
[DOCTOR_PROBLEMS]: { class: "operator" },
|
|
7662
|
+
[NOT_GLOBAL_INSTALL]: { class: "operator" },
|
|
7663
|
+
[NOT_ON_PATH]: { class: "operator" },
|
|
7664
|
+
[REGISTRY_UNREACHABLE]: { class: "operator" },
|
|
7665
|
+
[REGISTRY_AUTH]: { class: "operator" },
|
|
7666
|
+
[INSTALL_FAILED]: { class: "operator" },
|
|
7667
|
+
[NPM_FAILED]: { class: "operator" },
|
|
7668
|
+
[NO_LATEST]: { class: "source" }
|
|
7669
|
+
};
|
|
7670
|
+
/** The Runtime's own Error table: every code it fails a run with, and the class that must act. */
|
|
7671
|
+
const runtimeErrors = {
|
|
7672
|
+
...anyCommandErrors,
|
|
7673
|
+
...declaredRuntimeErrors
|
|
7674
|
+
};
|
|
7675
|
+
const anyCommandClasses = Object.values(anyCommandErrors).map((declaration) => declaration.class);
|
|
7676
|
+
function isRuntimeCode(code) {
|
|
7677
|
+
return Object.hasOwn(runtimeErrors, code);
|
|
7678
|
+
}
|
|
7679
|
+
function isDeclaredRuntimeCode(code) {
|
|
7680
|
+
return Object.hasOwn(declaredRuntimeErrors, code);
|
|
7681
|
+
}
|
|
7682
|
+
/** Whether a Command or `doctor` row throwing `code` must declare it: a code of `table`, or a Runtime one only some reach. */
|
|
7683
|
+
function mustDeclare(code, table) {
|
|
7684
|
+
return Object.hasOwn(table, code) || isDeclaredRuntimeCode(code);
|
|
7685
|
+
}
|
|
7686
|
+
//#endregion
|
|
7687
|
+
//#region ../agent-cli/dist/define-command.js
|
|
7688
|
+
/** Throws when the Command's declaration breaks a rule its type cannot express; `label` names it. */
|
|
7689
|
+
function checkCommand(command, label, table) {
|
|
7690
|
+
checkNoStandardFlags(command, label);
|
|
7691
|
+
checkForce(command, label);
|
|
7692
|
+
checkArrayArgLast(label, command.args);
|
|
7693
|
+
checkStdinFields(label, command.args, command.options);
|
|
7694
|
+
checkNoBooleanArrayOption(label, command.options);
|
|
7695
|
+
checkErrors(command.errors ?? [], label, table);
|
|
7696
|
+
}
|
|
7697
|
+
/** Throws on a code in `errors` that is neither in `table` nor a Runtime code some Commands declare. */
|
|
7698
|
+
function checkErrors(errors, label, table) {
|
|
7699
|
+
const unknown = errors.filter((code) => !mustDeclare(code, table));
|
|
7700
|
+
if (unknown.length > 0) throw new Error(`${label} declares ${unknown.join(", ")} in errors, a code the Error table lacks`);
|
|
7701
|
+
}
|
|
7702
|
+
function defineCommandFor() {
|
|
7703
|
+
return declared;
|
|
7704
|
+
}
|
|
7705
|
+
function declared(command) {
|
|
7706
|
+
return command;
|
|
7707
|
+
}
|
|
7708
|
+
const defineCommand$1 = defineCommandFor();
|
|
7709
|
+
//#endregion
|
|
7710
|
+
//#region ../agent-cli/dist/error-table.js
|
|
7711
|
+
/** The Error table a CLI declares, empty when it declares none. */
|
|
7712
|
+
function errorTableOf({ errors }) {
|
|
7713
|
+
return errors ?? {};
|
|
7714
|
+
}
|
|
7715
|
+
/** The `CliError` for a code of `Table`, as `cliError` and `ctx.fail` build it. */
|
|
7716
|
+
function cliErrorOf(code, init, options) {
|
|
7717
|
+
return new CliError({
|
|
7718
|
+
...init,
|
|
7719
|
+
code
|
|
7720
|
+
}, options);
|
|
7721
|
+
}
|
|
7722
|
+
//#endregion
|
|
7723
|
+
//#region ../agent-cli/dist/version.js
|
|
7724
|
+
/** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
|
|
7725
|
+
function buildStamp() {
|
|
7726
|
+
return {
|
|
7727
|
+
"sha": "b509cf2",
|
|
7728
|
+
"dirty": false,
|
|
7729
|
+
"builtAt": "2026-11-09T01:38:28.000Z"
|
|
7730
|
+
};
|
|
7731
|
+
}
|
|
7732
|
+
/** The first line of `--version`: the version, and the commit and time of a stamped build. */
|
|
7733
|
+
function versionLine({ version }) {
|
|
7734
|
+
const build = buildStamp();
|
|
7735
|
+
return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
|
|
7736
|
+
}
|
|
7737
|
+
//#endregion
|
|
7738
|
+
//#region ../agent-cli/dist/built-ins/semver.js
|
|
7739
|
+
const VERSION = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
7740
|
+
const ENGINE = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?$/;
|
|
7741
|
+
function isVersion(text) {
|
|
7742
|
+
return VERSION.test(text);
|
|
7743
|
+
}
|
|
7744
|
+
function compareIdentifiers(x, y) {
|
|
7745
|
+
const xNumeric = /^\d+$/.test(x);
|
|
7746
|
+
const yNumeric = /^\d+$/.test(y);
|
|
7747
|
+
if (xNumeric && yNumeric) return Number(x) - Number(y);
|
|
7748
|
+
if (xNumeric !== yNumeric) return xNumeric ? -1 : 1;
|
|
7749
|
+
if (x === y) return 0;
|
|
7750
|
+
return x < y ? -1 : 1;
|
|
7751
|
+
}
|
|
7752
|
+
function comparePrerelease(a, b) {
|
|
7753
|
+
if (a === b) return 0;
|
|
7754
|
+
if (a === void 0) return 1;
|
|
7755
|
+
if (b === void 0) return -1;
|
|
7756
|
+
const left = a.split(".");
|
|
7757
|
+
const right = b.split(".");
|
|
7758
|
+
for (let i = 0; i < Math.min(left.length, right.length); i += 1) {
|
|
7759
|
+
const difference = compareIdentifiers(left[i] ?? "", right[i] ?? "");
|
|
7760
|
+
if (difference !== 0) return difference;
|
|
7761
|
+
}
|
|
7762
|
+
return left.length - right.length;
|
|
7763
|
+
}
|
|
7764
|
+
/** Negative when `a` is older than `b`, positive when newer, 0 when the same release. */
|
|
7175
7765
|
function compareVersions(a, b) {
|
|
7176
7766
|
const left = VERSION.exec(a);
|
|
7177
7767
|
const right = VERSION.exec(b);
|
|
@@ -7207,12 +7797,15 @@ function meetsMinimum(version, minimum) {
|
|
|
7207
7797
|
}
|
|
7208
7798
|
//#endregion
|
|
7209
7799
|
//#region ../agent-cli/dist/built-ins/npm.js
|
|
7210
|
-
|
|
7211
|
-
const
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7800
|
+
/** What a Command running npm may fail with, for it to declare in `errors`. */
|
|
7801
|
+
const NPM_ERRORS = [
|
|
7802
|
+
REGISTRY_UNREACHABLE,
|
|
7803
|
+
REGISTRY_AUTH,
|
|
7804
|
+
INSTALL_FAILED,
|
|
7805
|
+
NPM_FAILED,
|
|
7806
|
+
NO_LATEST
|
|
7807
|
+
];
|
|
7808
|
+
function npmContext(cli, registry, ctx, { node, platform }, cwd) {
|
|
7216
7809
|
return {
|
|
7217
7810
|
cliName: cli.name,
|
|
7218
7811
|
rerun: ctx.rerun(),
|
|
@@ -7220,7 +7813,9 @@ function npmContext(cli, registry, ctx, node, cwd) {
|
|
|
7220
7813
|
cwd,
|
|
7221
7814
|
env: ctx.env,
|
|
7222
7815
|
signal: ctx.signal,
|
|
7223
|
-
node
|
|
7816
|
+
node,
|
|
7817
|
+
platform,
|
|
7818
|
+
files: ctx.files
|
|
7224
7819
|
};
|
|
7225
7820
|
}
|
|
7226
7821
|
const STDERR_TAIL_LINES = 10;
|
|
@@ -7262,9 +7857,10 @@ function setupCommandLine({ package: name, flags }) {
|
|
|
7262
7857
|
"setup"
|
|
7263
7858
|
].join(" ");
|
|
7264
7859
|
}
|
|
7265
|
-
function npmOf(ctx
|
|
7860
|
+
async function npmOf(ctx) {
|
|
7861
|
+
const windows = ctx.platform === "win32";
|
|
7266
7862
|
const beside = ctx.node === void 0 ? void 0 : join(dirname(ctx.node), windows ? "npm.cmd" : "npm");
|
|
7267
|
-
if (beside === void 0 || !
|
|
7863
|
+
if (beside === void 0 || !await ctx.files.exists(beside)) return {
|
|
7268
7864
|
path: "npm",
|
|
7269
7865
|
env: ctx.env
|
|
7270
7866
|
};
|
|
@@ -7281,8 +7877,7 @@ function npmOf(ctx, windows) {
|
|
|
7281
7877
|
}
|
|
7282
7878
|
};
|
|
7283
7879
|
}
|
|
7284
|
-
function spawnNpm(args, ctx, signal) {
|
|
7285
|
-
const npm = npmOf(ctx, process.platform === "win32");
|
|
7880
|
+
function spawnNpm(npm, args, ctx, signal) {
|
|
7286
7881
|
return spawnChild(npm.path, args, {
|
|
7287
7882
|
cwd: ctx.cwd,
|
|
7288
7883
|
env: npm.env,
|
|
@@ -7300,7 +7895,7 @@ function authHint(code, ctx) {
|
|
|
7300
7895
|
if (code !== "E403" || !isAgent(ctx.env)) return login;
|
|
7301
7896
|
return `a Coding agent's sandbox may refuse ${registry} with a 403: ask your user to type ! and ${RUN_AGAIN}, or to allow ${registry} in the sandbox's network list; if it fails outside the sandbox too, ${login}`;
|
|
7302
7897
|
}
|
|
7303
|
-
function failureOf(call, answer, ctx) {
|
|
7898
|
+
function failureOf$1(call, answer, ctx) {
|
|
7304
7899
|
const { label: registry, package: name, unreachableHint } = ctx.registry;
|
|
7305
7900
|
const code = NPM_CODE.exec(answer.stderr)?.[1];
|
|
7306
7901
|
const output = tail$1(answer.stderr);
|
|
@@ -7345,7 +7940,7 @@ function noLatest(ctx, message) {
|
|
|
7345
7940
|
async function runNpm(call, args, ctx, signal = ctx.signal) {
|
|
7346
7941
|
const cache = await mkdtemp(join(tmpdir(), `${ctx.cliName}-npm-`));
|
|
7347
7942
|
try {
|
|
7348
|
-
const answer = await spawnNpm([
|
|
7943
|
+
const answer = await spawnNpm(await npmOf(ctx), [
|
|
7349
7944
|
...args,
|
|
7350
7945
|
`--cache=${join(cache, "cache")}`,
|
|
7351
7946
|
"--logs-max=0"
|
|
@@ -7357,7 +7952,7 @@ async function runNpm(call, args, ctx, signal = ctx.signal) {
|
|
|
7357
7952
|
hint: "install Node with npm, or put npm on PATH"
|
|
7358
7953
|
}, { cause: error });
|
|
7359
7954
|
});
|
|
7360
|
-
if (answer.exitCode !== 0) throw failureOf(call, answer, ctx);
|
|
7955
|
+
if (answer.exitCode !== 0) throw failureOf$1(call, answer, ctx);
|
|
7361
7956
|
return answer.stdout;
|
|
7362
7957
|
} finally {
|
|
7363
7958
|
await rm(cache, {
|
|
@@ -7407,6 +8002,30 @@ async function latestVersion(ctx) {
|
|
|
7407
8002
|
return version;
|
|
7408
8003
|
}
|
|
7409
8004
|
//#endregion
|
|
8005
|
+
//#region ../agent-cli/dist/built-ins/read.js
|
|
8006
|
+
function isNotFound(error) {
|
|
8007
|
+
return error instanceof CliError && error.code === "NOT_FOUND";
|
|
8008
|
+
}
|
|
8009
|
+
const decoder = new TextDecoder();
|
|
8010
|
+
/** The text of the file at `path`, or `undefined` when nothing is there. */
|
|
8011
|
+
async function readIfAny(files, path) {
|
|
8012
|
+
try {
|
|
8013
|
+
return decoder.decode(await files.read(path));
|
|
8014
|
+
} catch (error) {
|
|
8015
|
+
if (isNotFound(error)) return void 0;
|
|
8016
|
+
throw error;
|
|
8017
|
+
}
|
|
8018
|
+
}
|
|
8019
|
+
/** The text of a file the CLI's own package ships, whose absence is the release's bug. */
|
|
8020
|
+
async function readRequired(files, path) {
|
|
8021
|
+
try {
|
|
8022
|
+
return decoder.decode(await files.read(path));
|
|
8023
|
+
} catch (error) {
|
|
8024
|
+
if (!isNotFound(error)) throw error;
|
|
8025
|
+
throw new Error(`the package is missing its own file ${path}`, { cause: error });
|
|
8026
|
+
}
|
|
8027
|
+
}
|
|
8028
|
+
//#endregion
|
|
7410
8029
|
//#region ../agent-cli/dist/built-ins/project-root.js
|
|
7411
8030
|
const MANAGERS = [
|
|
7412
8031
|
"pnpm",
|
|
@@ -7431,22 +8050,14 @@ function ancestorsOf(dir) {
|
|
|
7431
8050
|
const parent = dirname(dir);
|
|
7432
8051
|
return parent === dir ? [dir] : [dir, ...ancestorsOf(parent)];
|
|
7433
8052
|
}
|
|
7434
|
-
async function exists(path) {
|
|
7435
|
-
return await orMissing(stat(path)) !== void 0;
|
|
7436
|
-
}
|
|
7437
8053
|
/** Whether `path` lies strictly inside `dir`. */
|
|
7438
8054
|
function isInside(dir, path) {
|
|
7439
8055
|
const inner = relative(dir, path);
|
|
7440
8056
|
return inner !== "" && !inner.startsWith("..") && !isAbsolute(inner);
|
|
7441
8057
|
}
|
|
7442
|
-
async function manifestIn(dir) {
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
text = await readFile(join(dir, "package.json"), "utf8");
|
|
7446
|
-
} catch (error) {
|
|
7447
|
-
if (isMissingFile(error)) return void 0;
|
|
7448
|
-
throw error;
|
|
7449
|
-
}
|
|
8058
|
+
async function manifestIn(files, dir) {
|
|
8059
|
+
const text = await readIfAny(files, join(dir, "package.json"));
|
|
8060
|
+
if (text === void 0) return void 0;
|
|
7450
8061
|
let manifest;
|
|
7451
8062
|
try {
|
|
7452
8063
|
manifest = JSON.parse(text);
|
|
@@ -7455,19 +8066,19 @@ async function manifestIn(dir) {
|
|
|
7455
8066
|
}
|
|
7456
8067
|
return manifestSchema$1.safeParse(manifest).data;
|
|
7457
8068
|
}
|
|
7458
|
-
async function declaredManager(dir) {
|
|
7459
|
-
const name = (await manifestIn(dir))?.packageManager?.split("@")[0];
|
|
8069
|
+
async function declaredManager(files, dir) {
|
|
8070
|
+
const name = (await manifestIn(files, dir))?.packageManager?.split("@")[0];
|
|
7460
8071
|
return MANAGERS.find((manager) => manager === name);
|
|
7461
8072
|
}
|
|
7462
|
-
async function lockfileRootOf(cwd) {
|
|
7463
|
-
for (const dir of ancestorsOf(cwd)) for (const [file, kind] of LOCKFILES) if (await exists(join(dir, file))) return {
|
|
8073
|
+
async function lockfileRootOf(files, cwd) {
|
|
8074
|
+
for (const dir of ancestorsOf(cwd)) for (const [file, kind] of LOCKFILES) if (await files.exists(join(dir, file))) return {
|
|
7464
8075
|
dir,
|
|
7465
|
-
manager: await declaredManager(dir) ?? kind
|
|
8076
|
+
manager: await declaredManager(files, dir) ?? kind
|
|
7466
8077
|
};
|
|
7467
8078
|
}
|
|
7468
8079
|
/** The package manager's project root: the folder of the nearest lockfile, or `cwd` without one. */
|
|
7469
|
-
async function projectRootOf(cwd) {
|
|
7470
|
-
return (await lockfileRootOf(cwd))?.dir ?? cwd;
|
|
8080
|
+
async function projectRootOf(files, cwd) {
|
|
8081
|
+
return (await lockfileRootOf(files, cwd))?.dir ?? cwd;
|
|
7471
8082
|
}
|
|
7472
8083
|
//#endregion
|
|
7473
8084
|
//#region ../agent-cli/dist/built-ins/install/run-as.js
|
|
@@ -7490,29 +8101,30 @@ const LINES = {
|
|
|
7490
8101
|
}
|
|
7491
8102
|
};
|
|
7492
8103
|
const packageSchema = object({ version: string() });
|
|
7493
|
-
async function manifestListing(name, cwd, root) {
|
|
8104
|
+
async function manifestListing(files, name, cwd, root) {
|
|
7494
8105
|
const dirs = ancestorsOf(cwd);
|
|
7495
8106
|
const top = root === void 0 ? dirs.length : dirs.indexOf(root.dir) + 1;
|
|
7496
8107
|
for (const dir of dirs.slice(0, top)) {
|
|
7497
|
-
const manifest = await manifestIn(dir);
|
|
8108
|
+
const manifest = await manifestIn(files, dir);
|
|
7498
8109
|
if (manifest?.dependencies?.[name] !== void 0 || manifest?.devDependencies?.[name] !== void 0) return join(dir, "package.json");
|
|
7499
8110
|
}
|
|
7500
8111
|
}
|
|
7501
|
-
|
|
8112
|
+
function versionIn(text) {
|
|
7502
8113
|
try {
|
|
7503
|
-
return packageSchema.parse(JSON.parse(
|
|
8114
|
+
return packageSchema.parse(JSON.parse(text)).version;
|
|
7504
8115
|
} catch (error) {
|
|
7505
8116
|
if (error instanceof SyntaxError || error instanceof ZodError) return { reason: messageOf(error) };
|
|
7506
8117
|
throw error;
|
|
7507
8118
|
}
|
|
7508
8119
|
}
|
|
7509
|
-
async function standingOf(install, bin, cwd, root) {
|
|
8120
|
+
async function standingOf(files, install, bin, cwd, root) {
|
|
7510
8121
|
if (bin === void 0) return { kind: "source" };
|
|
7511
8122
|
for (const projectDir of ancestorsOf(cwd)) {
|
|
7512
8123
|
const folder = join(projectDir, "node_modules", install.package);
|
|
7513
8124
|
const manifest = join(folder, "package.json");
|
|
7514
|
-
|
|
7515
|
-
|
|
8125
|
+
const text = await readIfAny(files, manifest);
|
|
8126
|
+
if (text === void 0) continue;
|
|
8127
|
+
const version = versionIn(text);
|
|
7516
8128
|
if (typeof version !== "string") return {
|
|
7517
8129
|
kind: "unreadable",
|
|
7518
8130
|
manifest,
|
|
@@ -7520,14 +8132,14 @@ async function standingOf(install, bin, cwd, root) {
|
|
|
7520
8132
|
};
|
|
7521
8133
|
const copy = {
|
|
7522
8134
|
projectDir,
|
|
7523
|
-
realDir: await
|
|
8135
|
+
realDir: await files.realPath(folder) ?? folder,
|
|
7524
8136
|
version
|
|
7525
8137
|
};
|
|
7526
8138
|
if (!copy.realDir.split(sep).includes("node_modules")) return {
|
|
7527
8139
|
kind: "linked",
|
|
7528
8140
|
copy
|
|
7529
8141
|
};
|
|
7530
|
-
const runningFrom = await
|
|
8142
|
+
const runningFrom = await files.realPath(bin) ?? bin;
|
|
7531
8143
|
return isInside(copy.realDir, runningFrom) ? {
|
|
7532
8144
|
kind: "installed",
|
|
7533
8145
|
copy
|
|
@@ -7537,18 +8149,18 @@ async function standingOf(install, bin, cwd, root) {
|
|
|
7537
8149
|
runningFrom
|
|
7538
8150
|
};
|
|
7539
8151
|
}
|
|
7540
|
-
const manifest = await manifestListing(install.package, cwd, root);
|
|
8152
|
+
const manifest = await manifestListing(files, install.package, cwd, root);
|
|
7541
8153
|
return manifest === void 0 ? { kind: "missing" } : {
|
|
7542
8154
|
kind: "listed",
|
|
7543
8155
|
manifest
|
|
7544
8156
|
};
|
|
7545
8157
|
}
|
|
7546
|
-
async function surveyDependency(install, bin, cwd) {
|
|
7547
|
-
const root = await lockfileRootOf(cwd);
|
|
8158
|
+
async function surveyDependency(install, bin, { files, cwd }) {
|
|
8159
|
+
const root = await lockfileRootOf(files, cwd);
|
|
7548
8160
|
return {
|
|
7549
8161
|
cwd,
|
|
7550
8162
|
root,
|
|
7551
|
-
standing: await standingOf(install, bin, cwd, root)
|
|
8163
|
+
standing: await standingOf(files, install, bin, cwd, root)
|
|
7552
8164
|
};
|
|
7553
8165
|
}
|
|
7554
8166
|
function packagesOf(install) {
|
|
@@ -7618,19 +8230,19 @@ function adviceFor(install, root, latest) {
|
|
|
7618
8230
|
if (lines !== void 0) return { fix: lines.upgrade(packages) };
|
|
7619
8231
|
return { note: `move ${packages.length === 1 ? packages[0] : `${packages.slice(0, -1).join(", ")} and ${packages.at(-1)}`} to ${latest} with ${root?.manager ?? "the project's package manager"}` };
|
|
7620
8232
|
}
|
|
7621
|
-
function checkOf$1(cli, install, runAs,
|
|
8233
|
+
function checkOf$1(cli, install, runAs, launch) {
|
|
7622
8234
|
let survey;
|
|
7623
|
-
const surveyed = (
|
|
7624
|
-
survey ??= surveyDependency(install, bin,
|
|
8235
|
+
const surveyed = (ctx) => {
|
|
8236
|
+
survey ??= surveyDependency(install, launch.bin, ctx);
|
|
7625
8237
|
return survey;
|
|
7626
8238
|
};
|
|
7627
8239
|
return {
|
|
7628
|
-
install: async (ctx) => installFinding(cli, runAs, install, await surveyed(ctx
|
|
8240
|
+
install: async (ctx) => installFinding(cli, runAs, install, await surveyed(ctx)),
|
|
7629
8241
|
release: async (ctx) => {
|
|
7630
|
-
const { cwd, root, standing } = await surveyed(ctx
|
|
8242
|
+
const { cwd, root, standing } = await surveyed(ctx);
|
|
7631
8243
|
if (isUnchecked(standing)) return { skipped: skippedFor$1(cli, install, standing) };
|
|
7632
8244
|
return {
|
|
7633
|
-
npm: npmContext(cli, registryOf(install, root?.dir), ctx,
|
|
8245
|
+
npm: npmContext(cli, registryOf(install, root?.dir), ctx, launch, root?.dir ?? cwd),
|
|
7634
8246
|
advise: (latest) => adviceFor(install, root, latest)
|
|
7635
8247
|
};
|
|
7636
8248
|
}
|
|
@@ -7642,222 +8254,22 @@ function dependencyInstall(cli, install) {
|
|
|
7642
8254
|
return {
|
|
7643
8255
|
runAs,
|
|
7644
8256
|
builtIns: () => ({}),
|
|
7645
|
-
exitClasses: [],
|
|
7646
8257
|
check: (launch) => checkOf$1(cli, install, runAs, launch),
|
|
7647
8258
|
notice: async () => {}
|
|
7648
8259
|
};
|
|
7649
8260
|
}
|
|
7650
8261
|
//#endregion
|
|
7651
|
-
//#region ../agent-cli/dist/context/errno.js
|
|
7652
|
-
const PERMISSION_DENIED = "PERMISSION_DENIED";
|
|
7653
|
-
const DISK_FULL = "DISK_FULL";
|
|
7654
|
-
const NOT_FOUND = "NOT_FOUND";
|
|
7655
|
-
const ALREADY_EXISTS = "ALREADY_EXISTS";
|
|
7656
|
-
const NOT_A_FILE = "NOT_A_FILE";
|
|
7657
|
-
function permissionAdvice({ path, access, rerun, env, platform }) {
|
|
7658
|
-
if (platform === "win32") return {
|
|
7659
|
-
hint: `a program may hold ${path} open: close it, then run the Next step`,
|
|
7660
|
-
next: [rerun]
|
|
7661
|
-
};
|
|
7662
|
-
if (!isAgent(env)) return { hint: `check the permissions of ${path} and its folder` };
|
|
7663
|
-
return {
|
|
7664
|
-
hint: `your sandbox may block it: ask your user to type ! then the Next step, or ${access === "read" ? `to allow reading ${path} in the sandbox` : `to allow ${path} in the sandbox's write list`}`,
|
|
7665
|
-
next: [rerun]
|
|
7666
|
-
};
|
|
7667
|
-
}
|
|
7668
|
-
function fileErrorOf(scope) {
|
|
7669
|
-
return (error, at) => classifyErrno(error, {
|
|
7670
|
-
...at,
|
|
7671
|
-
...scope
|
|
7672
|
-
});
|
|
7673
|
-
}
|
|
7674
|
-
function classifyErrno(error, context) {
|
|
7675
|
-
const { path } = context;
|
|
7676
|
-
switch (errnoOf(error)) {
|
|
7677
|
-
case "EACCES":
|
|
7678
|
-
case "EPERM":
|
|
7679
|
-
case "EROFS":
|
|
7680
|
-
case "EBUSY": return new CliError({
|
|
7681
|
-
code: PERMISSION_DENIED,
|
|
7682
|
-
message: `permission denied: ${path}`,
|
|
7683
|
-
...permissionAdvice(context)
|
|
7684
|
-
}, { cause: error });
|
|
7685
|
-
case "ENOSPC":
|
|
7686
|
-
case "EDQUOT": return new CliError({
|
|
7687
|
-
code: DISK_FULL,
|
|
7688
|
-
message: `no space left on the disk holding ${path}`,
|
|
7689
|
-
hint: "free space on that disk, then run the Next step",
|
|
7690
|
-
next: [context.rerun]
|
|
7691
|
-
}, { cause: error });
|
|
7692
|
-
case "ENOENT":
|
|
7693
|
-
case "ENOTDIR":
|
|
7694
|
-
case "ENAMETOOLONG": return new CliError({
|
|
7695
|
-
code: NOT_FOUND,
|
|
7696
|
-
message: `not found: ${path}`,
|
|
7697
|
-
hint: `check that ${path} and its folder exist`
|
|
7698
|
-
}, { cause: error });
|
|
7699
|
-
case "EEXIST": return new CliError({
|
|
7700
|
-
code: ALREADY_EXISTS,
|
|
7701
|
-
message: `already exists: ${path}`,
|
|
7702
|
-
hint: `pass another path, or remove ${path}`
|
|
7703
|
-
}, { cause: error });
|
|
7704
|
-
case "EISDIR": return new CliError({
|
|
7705
|
-
code: NOT_A_FILE,
|
|
7706
|
-
message: `not a file: ${path} is a folder`,
|
|
7707
|
-
hint: `pass the path of a file, not of the folder ${path}`
|
|
7708
|
-
}, { cause: error });
|
|
7709
|
-
default: return error;
|
|
7710
|
-
}
|
|
7711
|
-
}
|
|
7712
|
-
//#endregion
|
|
7713
|
-
//#region ../agent-cli/dist/context/interrupts.js
|
|
7714
|
-
const INTERRUPTED = "INTERRUPTED";
|
|
7715
|
-
const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
|
|
7716
|
-
/** How long an interrupted Command may take to settle before the run ends without it. */
|
|
7717
|
-
const GRACE_MS = 3e3;
|
|
7718
|
-
const GRACE_OVER = Symbol("grace over");
|
|
7719
|
-
function interruptedError(rerun) {
|
|
7720
|
-
return new CliError({
|
|
7721
|
-
code: INTERRUPTED,
|
|
7722
|
-
message: "interrupted before it finished",
|
|
7723
|
-
hint: "to finish it, run the Next step, unless it was stopped on purpose; if a tool timeout stopped it, give it longer",
|
|
7724
|
-
next: [rerun]
|
|
7725
|
-
});
|
|
7726
|
-
}
|
|
7727
|
-
const BOUND_WORDS = {
|
|
7728
|
-
deadline: "deadline",
|
|
7729
|
-
ignoreStop: "timeout"
|
|
7730
|
-
};
|
|
7731
|
-
function waitHint(bound) {
|
|
7732
|
-
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";
|
|
7733
|
-
}
|
|
7734
|
-
function deadlineError(commandLine, ms, advice, bound = "deadline") {
|
|
7735
|
-
return new CliError({
|
|
7736
|
-
code: DEADLINE_EXCEEDED,
|
|
7737
|
-
message: `${commandLine} ran past its ${ms} ms ${BOUND_WORDS[bound]}`,
|
|
7738
|
-
...advice
|
|
7739
|
-
});
|
|
7740
|
-
}
|
|
7741
|
-
/** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
|
|
7742
|
-
var RunControl = class {
|
|
7743
|
-
signal;
|
|
7744
|
-
#controller = new AbortController();
|
|
7745
|
-
#commandLine;
|
|
7746
|
-
#rerun;
|
|
7747
|
-
#outer;
|
|
7748
|
-
#onOuterAbort = () => this.#controller.abort(interruptedError(this.#rerun));
|
|
7749
|
-
/** Each running must-finish step, tracked until it settles, with its name. */
|
|
7750
|
-
#steps = /* @__PURE__ */ new Map();
|
|
7751
|
-
#progress;
|
|
7752
|
-
#clock;
|
|
7753
|
-
#disposed = new AbortController();
|
|
7754
|
-
/** The reasons this run's deadlines aborted with, to tell them in a Command's error. */
|
|
7755
|
-
#abortReasons = /* @__PURE__ */ new WeakSet();
|
|
7756
|
-
#graceCancel = new AbortController();
|
|
7757
|
-
#endGrace = () => {};
|
|
7758
|
-
#graceOver = new Promise((resolve) => {
|
|
7759
|
-
this.#endGrace = resolve;
|
|
7760
|
-
});
|
|
7761
|
-
constructor({ commandLine, rerun, outer, clock, progress, runDeadline }) {
|
|
7762
|
-
this.#commandLine = commandLine;
|
|
7763
|
-
this.#rerun = rerun;
|
|
7764
|
-
this.#outer = outer;
|
|
7765
|
-
this.#clock = clock;
|
|
7766
|
-
this.#progress = progress;
|
|
7767
|
-
this.signal = this.#controller.signal;
|
|
7768
|
-
this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
|
|
7769
|
-
if (outer?.aborted === true) this.#onOuterAbort();
|
|
7770
|
-
else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
|
|
7771
|
-
if (runDeadline !== void 0) {
|
|
7772
|
-
const { ms, advice } = runDeadline;
|
|
7773
|
-
after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, advice())));
|
|
7774
|
-
}
|
|
7775
|
-
}
|
|
7776
|
-
deadline(ms, options = {}) {
|
|
7777
|
-
return options.ignoreStop === true ? this.#abortsAfter(ms, "ignoreStop") : AbortSignal.any([this.signal, this.#abortsAfter(ms, "deadline")]);
|
|
7778
|
-
}
|
|
7779
|
-
#abortsAfter(ms, bound) {
|
|
7780
|
-
const controller = new AbortController();
|
|
7781
|
-
after(this.#clock, ms, this.#disposed.signal, () => {
|
|
7782
|
-
const advice = {
|
|
7783
|
-
hint: waitHint(bound),
|
|
7784
|
-
next: [this.#rerun]
|
|
7785
|
-
};
|
|
7786
|
-
const reason = deadlineError(this.#commandLine, ms, advice, bound);
|
|
7787
|
-
this.#abortReasons.add(reason);
|
|
7788
|
-
controller.abort(reason);
|
|
7789
|
-
});
|
|
7790
|
-
return controller.signal;
|
|
7791
|
-
}
|
|
7792
|
-
async runToEnd(name, work) {
|
|
7793
|
-
this.#graceCancel.abort();
|
|
7794
|
-
if (this.signal.aborted) this.#finishing(name);
|
|
7795
|
-
const step = work();
|
|
7796
|
-
const settled = step.then(() => {}, () => {});
|
|
7797
|
-
this.#steps.set(settled, name);
|
|
7798
|
-
try {
|
|
7799
|
-
return await step;
|
|
7800
|
-
} finally {
|
|
7801
|
-
this.#steps.delete(settled);
|
|
7802
|
-
this.#startGrace();
|
|
7803
|
-
}
|
|
7804
|
-
}
|
|
7805
|
-
/** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
|
|
7806
|
-
async settle(work) {
|
|
7807
|
-
try {
|
|
7808
|
-
const value = await Promise.race([work, this.#graceOver]);
|
|
7809
|
-
if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
|
|
7810
|
-
return value;
|
|
7811
|
-
} finally {
|
|
7812
|
-
await this.#stepsDone();
|
|
7813
|
-
this.close();
|
|
7814
|
-
}
|
|
7815
|
-
}
|
|
7816
|
-
/**
|
|
7817
|
-
* The error a run ends with: an interrupt or the run deadline outranks what the Command threw,
|
|
7818
|
-
* unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
|
|
7819
|
-
* Otherwise each leaf that is not a `CliError` and whose cause chain holds a deadline's abort is
|
|
7820
|
-
* reported as that abort, so a declared error keeps its own code.
|
|
7821
|
-
*/
|
|
7822
|
-
errorAfter(error) {
|
|
7823
|
-
if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
|
|
7824
|
-
const leaves = errorsOf(error).map((leaf) => this.#abortIn(leaf) ?? leaf);
|
|
7825
|
-
return leaves.length === 1 ? leaves[0] : new AggregateError(leaves);
|
|
7826
|
-
}
|
|
7827
|
-
#abortIn(error) {
|
|
7828
|
-
if (error instanceof CliError) return void 0;
|
|
7829
|
-
return causesOf(error).find((each) => each instanceof CliError && this.#abortReasons.has(each));
|
|
7830
|
-
}
|
|
7831
|
-
#onStop() {
|
|
7832
|
-
for (const name of this.#steps.values()) this.#finishing(name);
|
|
7833
|
-
this.#startGrace();
|
|
7834
|
-
}
|
|
7835
|
-
#finishing(name) {
|
|
7836
|
-
this.#progress(`finishing ${name}`);
|
|
7837
|
-
}
|
|
7838
|
-
#startGrace() {
|
|
7839
|
-
if (this.signal.aborted && this.#steps.size === 0) {
|
|
7840
|
-
this.#graceCancel.abort();
|
|
7841
|
-
this.#graceCancel = new AbortController();
|
|
7842
|
-
after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
|
|
7843
|
-
}
|
|
7844
|
-
}
|
|
7845
|
-
async #stepsDone() {
|
|
7846
|
-
while (this.#steps.size > 0) await Promise.all(this.#steps.keys());
|
|
7847
|
-
}
|
|
7848
|
-
/** Stops the run's timers and its hold on the caller's signal, for a run that never settles. */
|
|
7849
|
-
close() {
|
|
7850
|
-
this.#graceCancel.abort();
|
|
7851
|
-
this.#disposed.abort();
|
|
7852
|
-
this.#outer?.removeEventListener("abort", this.#onOuterAbort);
|
|
7853
|
-
}
|
|
7854
|
-
};
|
|
7855
|
-
//#endregion
|
|
7856
8262
|
//#region ../agent-cli/dist/built-ins/install/global-npm.js
|
|
7857
|
-
const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
|
|
7858
8263
|
/** Where npm puts global packages under its global prefix. */
|
|
7859
|
-
function globalModulesDir(prefix) {
|
|
7860
|
-
return
|
|
8264
|
+
function globalModulesDir(prefix, platform) {
|
|
8265
|
+
return platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
|
|
8266
|
+
}
|
|
8267
|
+
/** Whether `path` itself is a symlink, which a link in a folder above it does not make it. */
|
|
8268
|
+
async function isLink(files, path) {
|
|
8269
|
+
const real = await files.realPath(path);
|
|
8270
|
+
if (real === void 0) return false;
|
|
8271
|
+
const folder = await files.realPath(dirname(path));
|
|
8272
|
+
return folder === void 0 || real !== join(folder, basename(path));
|
|
7861
8273
|
}
|
|
7862
8274
|
function notGlobal(ctx, message) {
|
|
7863
8275
|
return runtimeError({
|
|
@@ -7874,11 +8286,11 @@ function notGlobal(ctx, message) {
|
|
|
7874
8286
|
async function globalInstallOf(bin, ctx) {
|
|
7875
8287
|
if (bin === void 0) throw notGlobal(ctx, `${ctx.cliName} runs from source, not from an npm install`);
|
|
7876
8288
|
const prefix = await globalPrefix(ctx);
|
|
7877
|
-
const packageDir = join(globalModulesDir(prefix), ctx.registry.package);
|
|
7878
|
-
const installed = await
|
|
7879
|
-
const here = await
|
|
8289
|
+
const packageDir = join(globalModulesDir(prefix, ctx.platform), ctx.registry.package);
|
|
8290
|
+
const installed = await ctx.files.realPath(packageDir);
|
|
8291
|
+
const here = await ctx.files.realPath(bin) ?? bin;
|
|
7880
8292
|
if (installed === void 0 || !isInside(installed, here)) throw notGlobal(ctx, `${ctx.cliName} runs from ${here}, not from npm's global install at ${packageDir}`);
|
|
7881
|
-
if (
|
|
8293
|
+
if (await isLink(ctx.files, packageDir)) throw notGlobal(ctx, `${ctx.cliName} runs from a link to ${installed} (npm link), not from an npm install`);
|
|
7882
8294
|
return {
|
|
7883
8295
|
prefix,
|
|
7884
8296
|
packageDir
|
|
@@ -7907,25 +8319,32 @@ function globalRegistry(install) {
|
|
|
7907
8319
|
};
|
|
7908
8320
|
}
|
|
7909
8321
|
/** npm for a Global install, run in the working directory. */
|
|
7910
|
-
function globalNpm(cli, install, ctx,
|
|
7911
|
-
return npmContext(cli, globalRegistry(install), ctx,
|
|
8322
|
+
function globalNpm(cli, install, ctx, launch) {
|
|
8323
|
+
return npmContext(cli, globalRegistry(install), ctx, launch, ctx.cwd);
|
|
7912
8324
|
}
|
|
7913
8325
|
//#endregion
|
|
7914
8326
|
//#region ../agent-cli/dist/built-ins/skill/sync.js
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
return (await
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
8327
|
+
async function walk$1(files, dir, inner) {
|
|
8328
|
+
const names = await files.list(join(dir, inner));
|
|
8329
|
+
return (await Promise.all(names.map(async (name) => {
|
|
8330
|
+
const path = join(inner, name);
|
|
8331
|
+
const found = await files.stat(join(dir, path));
|
|
8332
|
+
if (found === void 0) return [];
|
|
8333
|
+
return found.folder ? walk$1(files, dir, path) : [path];
|
|
8334
|
+
}))).flat();
|
|
8335
|
+
}
|
|
8336
|
+
/** Every file under `dir`, relative to it and sorted; none when `dir` is no folder. */
|
|
8337
|
+
async function filesIn(files, dir) {
|
|
8338
|
+
if ((await files.stat(dir))?.folder !== true) return [];
|
|
8339
|
+
return (await walk$1(files, dir, "")).sort();
|
|
8340
|
+
}
|
|
8341
|
+
async function sameContent(files, left, right) {
|
|
8342
|
+
const [a, b] = await Promise.all([files.read(left), files.read(right)]);
|
|
8343
|
+
return Buffer.from(a).equals(b);
|
|
8344
|
+
}
|
|
8345
|
+
async function syncOf(files, source, copy) {
|
|
8346
|
+
const [shipped, present] = await Promise.all([filesIn(files, source), filesIn(files, copy)]);
|
|
8347
|
+
const differs = await Promise.all(shipped.map(async (file) => present.includes(file) && !await sameContent(files, join(source, file), join(copy, file))));
|
|
7929
8348
|
return {
|
|
7930
8349
|
source,
|
|
7931
8350
|
copy,
|
|
@@ -7935,7 +8354,7 @@ async function syncOf(source, copy) {
|
|
|
7935
8354
|
};
|
|
7936
8355
|
}
|
|
7937
8356
|
async function applySync(sync, ctx) {
|
|
7938
|
-
for (const file of [...sync.write, ...sync.replace]) await ctx.files.write(join(sync.copy, file), await
|
|
8357
|
+
for (const file of [...sync.write, ...sync.replace]) await ctx.files.write(join(sync.copy, file), await ctx.files.read(join(sync.source, file)));
|
|
7939
8358
|
for (const file of sync.remove) await ctx.files.remove(join(sync.copy, file));
|
|
7940
8359
|
}
|
|
7941
8360
|
//#endregion
|
|
@@ -7956,9 +8375,9 @@ function homeOf(env) {
|
|
|
7956
8375
|
return env.HOME ?? env.USERPROFILE ?? homedir();
|
|
7957
8376
|
}
|
|
7958
8377
|
/** The home folders of the Skill copy; the skills installer may have made one a link to the other. */
|
|
7959
|
-
async function homeCopiesOf(name, env) {
|
|
8378
|
+
async function homeCopiesOf(files, name, env) {
|
|
7960
8379
|
const paths = SKILL_DIRS.map((dir) => join(homeOf(env), ...dir, name));
|
|
7961
|
-
const reals = await Promise.all(paths.map(
|
|
8380
|
+
const reals = await Promise.all(paths.map((path) => files.realPath(path)));
|
|
7962
8381
|
return paths.filter((_path, index) => {
|
|
7963
8382
|
const real = reals[index];
|
|
7964
8383
|
return real === void 0 || reals.indexOf(real) === index;
|
|
@@ -7976,9 +8395,9 @@ const homeSyncSchema = object({
|
|
|
7976
8395
|
async function syncHome(cli, packageDir, ctx) {
|
|
7977
8396
|
if (cli.skill === void 0) return null;
|
|
7978
8397
|
const source = join(packageDir, cli.skill.folder);
|
|
7979
|
-
if ((await filesIn(source)).length === 0) return null;
|
|
7980
|
-
const copies = await homeCopiesOf(cli.name, ctx.env);
|
|
7981
|
-
const syncs = await Promise.all(copies.map((copy) => syncOf(source, copy)));
|
|
8398
|
+
if ((await filesIn(ctx.files, source)).length === 0) return null;
|
|
8399
|
+
const copies = await homeCopiesOf(ctx.files, cli.name, ctx.env);
|
|
8400
|
+
const syncs = await Promise.all(copies.map((copy) => syncOf(ctx.files, source, copy)));
|
|
7982
8401
|
for (const sync of syncs) await applySync(sync, ctx);
|
|
7983
8402
|
return {
|
|
7984
8403
|
copies,
|
|
@@ -7995,19 +8414,25 @@ function renderHomeSync(name, sync) {
|
|
|
7995
8414
|
const at = sync.copies.join(", ");
|
|
7996
8415
|
return sync.written.length + sync.removed.length === 0 ? [`the ${name} skill at ${at} already matches the package`] : [`copied the ${name} skill to ${at}: wrote ${sync.written.length} files, removed ${sync.removed.length}`];
|
|
7997
8416
|
}
|
|
7998
|
-
async function homeCopyAt(path) {
|
|
8417
|
+
async function homeCopyAt(files, path) {
|
|
8418
|
+
let text;
|
|
7999
8419
|
try {
|
|
8000
|
-
|
|
8001
|
-
path,
|
|
8002
|
-
state: "readable",
|
|
8003
|
-
stated: skillVersionIn(await readFile(join(path, "SKILL.md"), "utf8"))
|
|
8004
|
-
};
|
|
8420
|
+
text = await readIfAny(files, join(path, "SKILL.md"));
|
|
8005
8421
|
} catch (error) {
|
|
8422
|
+
if (!(error instanceof CliError)) throw error;
|
|
8006
8423
|
return {
|
|
8007
8424
|
path,
|
|
8008
|
-
state:
|
|
8425
|
+
state: "unreadable"
|
|
8009
8426
|
};
|
|
8010
8427
|
}
|
|
8428
|
+
return text === void 0 ? {
|
|
8429
|
+
path,
|
|
8430
|
+
state: "missing"
|
|
8431
|
+
} : {
|
|
8432
|
+
path,
|
|
8433
|
+
state: "readable",
|
|
8434
|
+
stated: skillVersionIn(text)
|
|
8435
|
+
};
|
|
8011
8436
|
}
|
|
8012
8437
|
function describeCopy(copy) {
|
|
8013
8438
|
if (copy.state === "missing") return `no copy at ${copy.path}`;
|
|
@@ -8015,8 +8440,8 @@ function describeCopy(copy) {
|
|
|
8015
8440
|
return copy.stated === void 0 ? copy.path : `${copy.path} (${statedVersionNote(copy.stated)})`;
|
|
8016
8441
|
}
|
|
8017
8442
|
async function findingOf$1(cli, folder, install, ctx) {
|
|
8018
|
-
const paths = await homeCopiesOf(cli.name, ctx.env);
|
|
8019
|
-
const [copies, shipped] = await Promise.all([Promise.all(paths.map(homeCopyAt)),
|
|
8443
|
+
const paths = await homeCopiesOf(ctx.files, cli.name, ctx.env);
|
|
8444
|
+
const [copies, shipped] = await Promise.all([Promise.all(paths.map((path) => homeCopyAt(ctx.files, path))), readIfAny(ctx.files, join(ctx.packageRoot, folder, "SKILL.md"))]);
|
|
8020
8445
|
const message = copies.map(describeCopy).join("; ");
|
|
8021
8446
|
const shipsStated = shipped !== void 0 && skillVersionIn(shipped) !== void 0;
|
|
8022
8447
|
return copies.every((copy) => copy.state === "readable" && (copy.stated === void 0 || shipsStated)) ? {
|
|
@@ -8094,14 +8519,9 @@ function upgradeOf({ version, lines }) {
|
|
|
8094
8519
|
* What each release after `from` up to `to` asks of the operator, oldest first, from the
|
|
8095
8520
|
* CHANGELOG.md in `packageDir`; none when the package ships no CHANGELOG.
|
|
8096
8521
|
*/
|
|
8097
|
-
async function upgradesOf(packageDir, from, to) {
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
changelog = await readFile(join(packageDir, "CHANGELOG.md"), "utf8");
|
|
8101
|
-
} catch (error) {
|
|
8102
|
-
if (isMissingFile(error)) return [];
|
|
8103
|
-
throw error;
|
|
8104
|
-
}
|
|
8522
|
+
async function upgradesOf(files, packageDir, from, to) {
|
|
8523
|
+
const changelog = await readIfAny(files, join(packageDir, "CHANGELOG.md"));
|
|
8524
|
+
if (changelog === void 0) return [];
|
|
8105
8525
|
return releasesIn(changelog).filter(({ version }) => compareVersions(version, from) > 0 && compareVersions(version, to) <= 0).sort((a, b) => compareVersions(a.version, b.version)).flatMap(upgradeOf);
|
|
8106
8526
|
}
|
|
8107
8527
|
function renderUpgrade(upgrade) {
|
|
@@ -8109,7 +8529,6 @@ function renderUpgrade(upgrade) {
|
|
|
8109
8529
|
}
|
|
8110
8530
|
//#endregion
|
|
8111
8531
|
//#region ../agent-cli/dist/built-ins/setup.js
|
|
8112
|
-
const NOT_ON_PATH = "NOT_ON_PATH";
|
|
8113
8532
|
const setupSchema = object({
|
|
8114
8533
|
version: string(),
|
|
8115
8534
|
prefix: string(),
|
|
@@ -8119,8 +8538,8 @@ const setupSchema = object({
|
|
|
8119
8538
|
docs: string().nullable()
|
|
8120
8539
|
});
|
|
8121
8540
|
const installedManifest = object({ version: string() });
|
|
8122
|
-
async function versionAt(packageDir) {
|
|
8123
|
-
const text = await
|
|
8541
|
+
async function versionAt(files, packageDir) {
|
|
8542
|
+
const text = await readIfAny(files, join(packageDir, "package.json"));
|
|
8124
8543
|
if (text === void 0) return null;
|
|
8125
8544
|
try {
|
|
8126
8545
|
const parsed = installedManifest.safeParse(JSON.parse(text));
|
|
@@ -8130,26 +8549,29 @@ async function versionAt(packageDir) {
|
|
|
8130
8549
|
throw error;
|
|
8131
8550
|
}
|
|
8132
8551
|
}
|
|
8133
|
-
function npmOfSetup(cli, install, ctx,
|
|
8552
|
+
function npmOfSetup(cli, install, ctx, launch) {
|
|
8134
8553
|
return {
|
|
8135
|
-
...globalNpm(cli, install, ctx,
|
|
8554
|
+
...globalNpm(cli, install, ctx, launch),
|
|
8136
8555
|
rerun: wholeLine(setupLineOf(install))
|
|
8137
8556
|
};
|
|
8138
8557
|
}
|
|
8139
8558
|
function needsInstall({ from, latest }) {
|
|
8140
8559
|
return from === null || standingOf$1(from, latest) === "behind";
|
|
8141
8560
|
}
|
|
8142
|
-
function binDirOf(prefix) {
|
|
8143
|
-
return
|
|
8561
|
+
function binDirOf(prefix, platform) {
|
|
8562
|
+
return platform === "win32" ? prefix : join(prefix, "bin");
|
|
8144
8563
|
}
|
|
8145
|
-
async function canonical(path) {
|
|
8146
|
-
|
|
8564
|
+
async function canonical(files, path) {
|
|
8565
|
+
const real = await files.realPath(path).catch((error) => {
|
|
8566
|
+
if (!(error instanceof CliError) && errnoOf(error) === void 0) throw error;
|
|
8567
|
+
});
|
|
8568
|
+
return normalize(real ?? path);
|
|
8147
8569
|
}
|
|
8148
|
-
async function assertOnPath(cli, prefix, node, ctx) {
|
|
8149
|
-
const binDir = binDirOf(prefix);
|
|
8150
|
-
const wanted = await canonical(binDir);
|
|
8570
|
+
async function assertOnPath(cli, prefix, { node, platform }, ctx) {
|
|
8571
|
+
const binDir = binDirOf(prefix, platform);
|
|
8572
|
+
const wanted = await canonical(ctx.files, binDir);
|
|
8151
8573
|
const entries = pathOf(ctx.env).split(delimiter).filter(Boolean);
|
|
8152
|
-
if ((await Promise.all(entries.map(canonical))).includes(wanted)) return;
|
|
8574
|
+
if ((await Promise.all(entries.map((entry) => canonical(ctx.files, entry)))).includes(wanted)) return;
|
|
8153
8575
|
throw runtimeError({
|
|
8154
8576
|
code: NOT_ON_PATH,
|
|
8155
8577
|
message: `${cli.name} is installed in ${binDir}, by the npm of ${node ?? "the Node on PATH"}, a folder not on this shell's PATH`,
|
|
@@ -8181,7 +8603,7 @@ function renderSetup(cli, install, setup) {
|
|
|
8181
8603
|
].join("\n");
|
|
8182
8604
|
}
|
|
8183
8605
|
/** The built-in `setup`, which runs from any copy, such as npx's, so it never asks to be the global one. */
|
|
8184
|
-
function setupCommand(cli, install,
|
|
8606
|
+
function setupCommand(cli, install, launch) {
|
|
8185
8607
|
return defineCommand$1({
|
|
8186
8608
|
description: "install this CLI and its skill on this machine",
|
|
8187
8609
|
examples: [{
|
|
@@ -8192,12 +8614,17 @@ function setupCommand(cli, install, { node }) {
|
|
|
8192
8614
|
note: "show what it would change"
|
|
8193
8615
|
}],
|
|
8194
8616
|
effect: "write",
|
|
8617
|
+
errors: [
|
|
8618
|
+
NOT_GLOBAL_INSTALL,
|
|
8619
|
+
NOT_ON_PATH,
|
|
8620
|
+
...NPM_ERRORS
|
|
8621
|
+
],
|
|
8195
8622
|
output: setupSchema,
|
|
8196
8623
|
plan: async (_input, ctx) => {
|
|
8197
|
-
const npm = npmOfSetup(cli, install, ctx,
|
|
8624
|
+
const npm = npmOfSetup(cli, install, ctx, launch);
|
|
8198
8625
|
const prefix = await globalPrefix(npm);
|
|
8199
|
-
const packageDir = join(globalModulesDir(prefix), install.package);
|
|
8200
|
-
if (
|
|
8626
|
+
const packageDir = join(globalModulesDir(prefix, launch.platform), install.package);
|
|
8627
|
+
if (await isLink(ctx.files, packageDir)) throw runtimeError({
|
|
8201
8628
|
code: NOT_GLOBAL_INSTALL,
|
|
8202
8629
|
message: `${packageDir} is a link (npm link), which an install would replace`,
|
|
8203
8630
|
hint: "remove the link with npm unlink, then run the Next step",
|
|
@@ -8205,309 +8632,137 @@ function setupCommand(cli, install, { node }) {
|
|
|
8205
8632
|
});
|
|
8206
8633
|
return {
|
|
8207
8634
|
package: install.package,
|
|
8208
|
-
from: await versionAt(packageDir),
|
|
8635
|
+
from: await versionAt(ctx.files, packageDir),
|
|
8209
8636
|
latest: await latestVersion(npm),
|
|
8210
8637
|
prefix,
|
|
8211
8638
|
packageDir,
|
|
8212
|
-
copies: cli.skill === void 0 ? [] : await homeCopiesOf(cli.name, ctx.env)
|
|
8639
|
+
copies: cli.skill === void 0 ? [] : await homeCopiesOf(ctx.files, cli.name, ctx.env)
|
|
8213
8640
|
};
|
|
8214
8641
|
},
|
|
8215
8642
|
apply: async (plan, ctx) => {
|
|
8216
8643
|
const { from, latest, prefix, packageDir } = plan;
|
|
8217
8644
|
const installs = needsInstall(plan);
|
|
8218
8645
|
const skill = await ctx.runToEnd(`installing ${plan.package}@${latest}`, async () => {
|
|
8219
|
-
if (installs) {
|
|
8220
|
-
ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
|
|
8221
|
-
await installGlobally(latest, npmOfSetup(cli, install, ctx,
|
|
8222
|
-
}
|
|
8223
|
-
return syncHome(cli, packageDir, ctx);
|
|
8224
|
-
}).catch((error) => {
|
|
8225
|
-
throw withSetupLine(error, install);
|
|
8226
|
-
});
|
|
8227
|
-
const setup = {
|
|
8228
|
-
version: installs || from === null ? latest : from,
|
|
8229
|
-
prefix,
|
|
8230
|
-
installed: installs,
|
|
8231
|
-
skill,
|
|
8232
|
-
upgrades: installs && from !== null ? await upgradesOf(packageDir, from, latest) : [],
|
|
8233
|
-
docs: cli.docs === void 0 ? null : join(packageDir, cli.docs)
|
|
8234
|
-
};
|
|
8235
|
-
ctx.deliver(setup);
|
|
8236
|
-
if (ctx.signal.aborted) throw new CliError({
|
|
8237
|
-
code: INTERRUPTED,
|
|
8238
|
-
message: `interrupted, but ${plan.package}@${setup.version} and its skill are already in place`,
|
|
8239
|
-
hint: "nothing to run again"
|
|
8240
|
-
});
|
|
8241
|
-
await assertOnPath(cli, prefix, node, ctx);
|
|
8242
|
-
return setup;
|
|
8243
|
-
},
|
|
8244
|
-
render: (setup) => renderSetup(cli, install, setup),
|
|
8245
|
-
renderPlan: (plan) => [needsInstall(plan) ? `would install ${plan.package}@${plan.latest} with the npm under ${plan.prefix}` : `${plan.package}@${plan.from} is already installed under ${plan.prefix}`, ...renderPlannedSync(cli.name, plan.copies)].join("\n")
|
|
8246
|
-
});
|
|
8247
|
-
}
|
|
8248
|
-
//#endregion
|
|
8249
|
-
//#region ../agent-cli/dist/built-ins/update.js
|
|
8250
|
-
const updateSchema = object({
|
|
8251
|
-
from: string(),
|
|
8252
|
-
latest: string(),
|
|
8253
|
-
installed: object({
|
|
8254
|
-
version: string(),
|
|
8255
|
-
prefix: string()
|
|
8256
|
-
}).nullable(),
|
|
8257
|
-
skill: homeSyncSchema.nullable(),
|
|
8258
|
-
upgrades: array(upgradeSchema)
|
|
8259
|
-
});
|
|
8260
|
-
function nothingToInstall({ from, latest }) {
|
|
8261
|
-
return standingOf$1(from, latest) === "current" ? `already on ${from}, the latest release: nothing to install` : `${from} is newer than the latest release ${latest}: nothing to install`;
|
|
8262
|
-
}
|
|
8263
|
-
function renderUpdate(cli, install, update) {
|
|
8264
|
-
const { installed } = update;
|
|
8265
|
-
const skill = renderHomeSync(cli.name, update.skill);
|
|
8266
|
-
return installed === null ? [nothingToInstall(update), ...skill].join("\n") : [
|
|
8267
|
-
`installed ${install.package}@${installed.version} with the npm under ${installed.prefix}, replacing ${update.from}`,
|
|
8268
|
-
...skill,
|
|
8269
|
-
...update.upgrades.map(renderUpgrade)
|
|
8270
|
-
].join("\n");
|
|
8271
|
-
}
|
|
8272
|
-
function interruptedAfterInstall(plan) {
|
|
8273
|
-
return new CliError({
|
|
8274
|
-
code: INTERRUPTED,
|
|
8275
|
-
message: `interrupted, but ${plan.package}@${plan.latest} had already been installed with the npm under ${plan.prefix}, replacing ${plan.from}`,
|
|
8276
|
-
hint: "nothing to run again"
|
|
8277
|
-
});
|
|
8278
|
-
}
|
|
8279
|
-
/** The built-in `update`, for a CLI installed globally with npm. */
|
|
8280
|
-
function updateCommand(cli, install, { bin, node }) {
|
|
8281
|
-
return defineCommand$1({
|
|
8282
|
-
description: "install the latest version of this CLI from its registry",
|
|
8283
|
-
examples: [{
|
|
8284
|
-
command: `${cli.name} update`,
|
|
8285
|
-
note: "install the latest version and show what changed"
|
|
8286
|
-
}, {
|
|
8287
|
-
command: `${cli.name} update --dry-run`,
|
|
8288
|
-
note: "show the version it would install"
|
|
8289
|
-
}],
|
|
8290
|
-
effect: "write",
|
|
8291
|
-
output: updateSchema,
|
|
8292
|
-
plan: async (_input, ctx) => {
|
|
8293
|
-
const npm = globalNpm(cli, install, ctx, node);
|
|
8294
|
-
const copy = await globalInstallOf(bin, npm);
|
|
8295
|
-
return {
|
|
8296
|
-
package: install.package,
|
|
8297
|
-
from: cli.version,
|
|
8298
|
-
latest: await latestVersion(npm),
|
|
8299
|
-
prefix: copy.prefix,
|
|
8300
|
-
packageDir: copy.packageDir,
|
|
8301
|
-
copies: cli.skill === void 0 ? [] : await homeCopiesOf(cli.name, ctx.env)
|
|
8302
|
-
};
|
|
8303
|
-
},
|
|
8304
|
-
apply: async (plan, ctx) => {
|
|
8305
|
-
const { from, latest, prefix } = plan;
|
|
8306
|
-
if (standingOf$1(from, latest) !== "behind") return {
|
|
8307
|
-
from,
|
|
8308
|
-
latest,
|
|
8309
|
-
installed: null,
|
|
8310
|
-
skill: await syncHome(cli, plan.packageDir, ctx),
|
|
8311
|
-
upgrades: []
|
|
8312
|
-
};
|
|
8313
|
-
ctx.signal.throwIfAborted();
|
|
8314
|
-
ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
|
|
8315
|
-
const skill = await ctx.runToEnd(`installing ${plan.package}@${latest}`, async () => {
|
|
8316
|
-
await installGlobally(latest, globalNpm(cli, install, ctx, node));
|
|
8317
|
-
return syncHome(cli, plan.packageDir, ctx);
|
|
8318
|
-
});
|
|
8319
|
-
const update = {
|
|
8320
|
-
from,
|
|
8321
|
-
latest,
|
|
8322
|
-
installed: {
|
|
8323
|
-
version: latest,
|
|
8324
|
-
prefix
|
|
8325
|
-
},
|
|
8326
|
-
skill,
|
|
8327
|
-
upgrades: await upgradesOf(plan.packageDir, from, latest)
|
|
8328
|
-
};
|
|
8329
|
-
if (ctx.signal.aborted) {
|
|
8330
|
-
ctx.deliver(update);
|
|
8331
|
-
throw interruptedAfterInstall(plan);
|
|
8332
|
-
}
|
|
8333
|
-
return update;
|
|
8334
|
-
},
|
|
8335
|
-
render: (update) => renderUpdate(cli, install, update),
|
|
8336
|
-
renderPlan: (plan) => [standingOf$1(plan.from, plan.latest) === "behind" ? `would install ${plan.package}@${plan.latest} with the npm under ${plan.prefix}, replacing ${plan.from}` : nothingToInstall(plan), ...renderPlannedSync(cli.name, plan.copies)].join("\n")
|
|
8337
|
-
});
|
|
8338
|
-
}
|
|
8339
|
-
//#endregion
|
|
8340
|
-
//#region ../agent-cli/dist/context/files.js
|
|
8341
|
-
const TEMP_FILE = /^\..+\.agent-cli-(\d+)\.tmp$/;
|
|
8342
|
-
/** Where the Runtime writes `path` before moving it into place; a dead writer's is swept. */
|
|
8343
|
-
function tempPathOf(path) {
|
|
8344
|
-
return join(dirname(path), `.${basename(path)}.agent-cli-${process.pid}.tmp`);
|
|
8345
|
-
}
|
|
8346
|
-
function isAlive(pid) {
|
|
8347
|
-
try {
|
|
8348
|
-
process.kill(pid, 0);
|
|
8349
|
-
return true;
|
|
8350
|
-
} catch (error) {
|
|
8351
|
-
return errnoOf(error) === "EPERM";
|
|
8352
|
-
}
|
|
8353
|
-
}
|
|
8354
|
-
async function sweep(folder) {
|
|
8355
|
-
const dead = (await readdir(folder)).filter((name) => {
|
|
8356
|
-
const pid = TEMP_FILE.exec(name)?.[1];
|
|
8357
|
-
return pid !== void 0 && !isAlive(Number(pid));
|
|
8358
|
-
});
|
|
8359
|
-
await Promise.all(dead.map((name) => rm(join(folder, name), { force: true })));
|
|
8360
|
-
}
|
|
8361
|
-
/** The file a symlink at `path` points to, so a replacing write keeps the link. */
|
|
8362
|
-
async function targetOf$1(path) {
|
|
8363
|
-
try {
|
|
8364
|
-
return await realpath(path);
|
|
8365
|
-
} catch (error) {
|
|
8366
|
-
if (isMissingFile(error)) return path;
|
|
8367
|
-
throw error;
|
|
8368
|
-
}
|
|
8369
|
-
}
|
|
8370
|
-
async function writeWithMode(path, data, mode) {
|
|
8371
|
-
if (mode === void 0) {
|
|
8372
|
-
await writeFile(path, data);
|
|
8373
|
-
return;
|
|
8374
|
-
}
|
|
8375
|
-
await writeFile(path, data, { mode });
|
|
8376
|
-
await chmod(path, mode);
|
|
8377
|
-
}
|
|
8378
|
-
/** What `link` answers on a file system without hard links, such as exFAT. */
|
|
8379
|
-
const NO_HARD_LINKS = /* @__PURE__ */ new Set([
|
|
8380
|
-
"EPERM",
|
|
8381
|
-
"ENOTSUP",
|
|
8382
|
-
"EXDEV"
|
|
8383
|
-
]);
|
|
8384
|
-
/** A link fails atomically when `path` is taken, so no reader ever sees half a file there. */
|
|
8385
|
-
async function linkNew(temp, path, data, mode) {
|
|
8386
|
-
try {
|
|
8387
|
-
await link(temp, path);
|
|
8388
|
-
} catch (error) {
|
|
8389
|
-
if (!NO_HARD_LINKS.has(errnoOf(error))) throw error;
|
|
8390
|
-
await createNew(path, data, mode);
|
|
8391
|
-
}
|
|
8392
|
-
}
|
|
8393
|
-
/** A file this call created and could not finish is removed, so a rerun does not find half of it. */
|
|
8394
|
-
async function createNew(path, data, mode) {
|
|
8395
|
-
const handle = await open(path, "wx", mode);
|
|
8396
|
-
try {
|
|
8397
|
-
await handle.writeFile(data);
|
|
8398
|
-
if (mode !== void 0) await handle.chmod(mode);
|
|
8399
|
-
} catch (error) {
|
|
8400
|
-
await handle.close();
|
|
8401
|
-
await rm(path, { force: true });
|
|
8402
|
-
throw error;
|
|
8403
|
-
}
|
|
8404
|
-
await handle.close();
|
|
8405
|
-
}
|
|
8406
|
-
const RENAME_ATTEMPTS = 10;
|
|
8407
|
-
const RENAME_BACKOFF_MS = 10;
|
|
8408
|
-
const HELD_BY_ANOTHER_PROGRAM = /* @__PURE__ */ new Set(["EPERM", "EBUSY"]);
|
|
8409
|
-
async function renameRetrying(from, to) {
|
|
8410
|
-
for (let attempt = 1;; attempt++) try {
|
|
8411
|
-
await rename(from, to);
|
|
8412
|
-
return;
|
|
8413
|
-
} catch (error) {
|
|
8414
|
-
if (!(process.platform === "win32" && HELD_BY_ANOTHER_PROGRAM.has(errnoOf(error))) || attempt === RENAME_ATTEMPTS) throw error;
|
|
8415
|
-
await setTimeout$1(attempt * RENAME_BACKOFF_MS);
|
|
8416
|
-
}
|
|
8417
|
-
}
|
|
8418
|
-
/** Real files for one run: the first write to each folder sweeps the temp files of dead writers. */
|
|
8419
|
-
function realFiles() {
|
|
8420
|
-
const swept = /* @__PURE__ */ new Set();
|
|
8421
|
-
return {
|
|
8422
|
-
exists: async (path) => {
|
|
8423
|
-
try {
|
|
8424
|
-
await access(path);
|
|
8425
|
-
return true;
|
|
8426
|
-
} catch (error) {
|
|
8427
|
-
if (isNothingThere(error)) return false;
|
|
8428
|
-
throw error;
|
|
8429
|
-
}
|
|
8430
|
-
},
|
|
8431
|
-
write: async (path, data, options = {}) => {
|
|
8432
|
-
await mkdir(dirname(path), { recursive: true });
|
|
8433
|
-
const target = options.exclusive === true ? path : await targetOf$1(path);
|
|
8434
|
-
const folder = dirname(target);
|
|
8435
|
-
if (!swept.has(folder)) {
|
|
8436
|
-
await sweep(folder);
|
|
8437
|
-
swept.add(folder);
|
|
8438
|
-
}
|
|
8439
|
-
const temp = tempPathOf(target);
|
|
8440
|
-
try {
|
|
8441
|
-
await writeWithMode(temp, data, options.mode);
|
|
8442
|
-
if (options.exclusive === true) {
|
|
8443
|
-
await linkNew(temp, target, data, options.mode);
|
|
8444
|
-
await rm(temp);
|
|
8445
|
-
} else await renameRetrying(temp, target);
|
|
8446
|
-
} catch (error) {
|
|
8447
|
-
await rm(temp, { force: true });
|
|
8448
|
-
throw error;
|
|
8449
|
-
}
|
|
8450
|
-
},
|
|
8451
|
-
read: (path) => readFile(path),
|
|
8452
|
-
list: (folder) => readdir(folder),
|
|
8453
|
-
remove: (path) => rm(path, {
|
|
8454
|
-
recursive: true,
|
|
8455
|
-
force: true
|
|
8456
|
-
}),
|
|
8457
|
-
mkdir: async (path, options) => {
|
|
8458
|
-
const mode = options?.mode;
|
|
8459
|
-
if (options?.exclusive !== true) {
|
|
8460
|
-
await mkdir(path, {
|
|
8461
|
-
recursive: true,
|
|
8462
|
-
mode
|
|
8463
|
-
});
|
|
8464
|
-
return;
|
|
8465
|
-
}
|
|
8466
|
-
await mkdir(dirname(path), {
|
|
8467
|
-
recursive: true,
|
|
8468
|
-
mode
|
|
8646
|
+
if (installs) {
|
|
8647
|
+
ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
|
|
8648
|
+
await installGlobally(latest, npmOfSetup(cli, install, ctx, launch));
|
|
8649
|
+
}
|
|
8650
|
+
return syncHome(cli, packageDir, ctx);
|
|
8651
|
+
}).catch((error) => {
|
|
8652
|
+
throw withSetupLine(error, install);
|
|
8469
8653
|
});
|
|
8470
|
-
|
|
8654
|
+
const setup = {
|
|
8655
|
+
version: installs || from === null ? latest : from,
|
|
8656
|
+
prefix,
|
|
8657
|
+
installed: installs,
|
|
8658
|
+
skill,
|
|
8659
|
+
upgrades: installs && from !== null ? await upgradesOf(ctx.files, packageDir, from, latest) : [],
|
|
8660
|
+
docs: cli.docs === void 0 ? null : join(packageDir, cli.docs)
|
|
8661
|
+
};
|
|
8662
|
+
ctx.deliver(setup);
|
|
8663
|
+
if (ctx.signal.aborted) throw new CliError({
|
|
8664
|
+
code: INTERRUPTED,
|
|
8665
|
+
message: `interrupted, but ${plan.package}@${setup.version} and its skill are already in place`,
|
|
8666
|
+
hint: "nothing to run again"
|
|
8667
|
+
});
|
|
8668
|
+
await assertOnPath(cli, prefix, launch, ctx);
|
|
8669
|
+
return setup;
|
|
8471
8670
|
},
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
return {
|
|
8476
|
-
size: Number(size),
|
|
8477
|
-
id: `${dev}:${ino}`
|
|
8478
|
-
};
|
|
8479
|
-
} catch (error) {
|
|
8480
|
-
if (isNothingThere(error)) return void 0;
|
|
8481
|
-
throw error;
|
|
8482
|
-
}
|
|
8483
|
-
}
|
|
8484
|
-
};
|
|
8671
|
+
render: (setup) => renderSetup(cli, install, setup),
|
|
8672
|
+
renderPlan: (plan) => [needsInstall(plan) ? `would install ${plan.package}@${plan.latest} with the npm under ${plan.prefix}` : `${plan.package}@${plan.from} is already installed under ${plan.prefix}`, ...renderPlannedSync(cli.name, plan.copies)].join("\n")
|
|
8673
|
+
});
|
|
8485
8674
|
}
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8675
|
+
//#endregion
|
|
8676
|
+
//#region ../agent-cli/dist/built-ins/update.js
|
|
8677
|
+
const updateSchema = object({
|
|
8678
|
+
from: string(),
|
|
8679
|
+
latest: string(),
|
|
8680
|
+
installed: object({
|
|
8681
|
+
version: string(),
|
|
8682
|
+
prefix: string()
|
|
8683
|
+
}).nullable(),
|
|
8684
|
+
skill: homeSyncSchema.nullable(),
|
|
8685
|
+
upgrades: array(upgradeSchema)
|
|
8686
|
+
});
|
|
8687
|
+
function nothingToInstall({ from, latest }) {
|
|
8688
|
+
return standingOf$1(from, latest) === "current" ? `already on ${from}, the latest release: nothing to install` : `${from} is newer than the latest release ${latest}: nothing to install`;
|
|
8689
|
+
}
|
|
8690
|
+
function renderUpdate(cli, install, update) {
|
|
8691
|
+
const { installed } = update;
|
|
8692
|
+
const skill = renderHomeSync(cli.name, update.skill);
|
|
8693
|
+
return installed === null ? [nothingToInstall(update), ...skill].join("\n") : [
|
|
8694
|
+
`installed ${install.package}@${installed.version} with the npm under ${installed.prefix}, replacing ${update.from}`,
|
|
8695
|
+
...skill,
|
|
8696
|
+
...update.upgrades.map(renderUpgrade)
|
|
8697
|
+
].join("\n");
|
|
8698
|
+
}
|
|
8699
|
+
function interruptedAfterInstall(plan) {
|
|
8700
|
+
return new CliError({
|
|
8701
|
+
code: INTERRUPTED,
|
|
8702
|
+
message: `interrupted, but ${plan.package}@${plan.latest} had already been installed with the npm under ${plan.prefix}, replacing ${plan.from}`,
|
|
8703
|
+
hint: "nothing to run again"
|
|
8704
|
+
});
|
|
8705
|
+
}
|
|
8706
|
+
/** The built-in `update`, for a CLI installed globally with npm. */
|
|
8707
|
+
function updateCommand(cli, install, launch) {
|
|
8708
|
+
return defineCommand$1({
|
|
8709
|
+
description: "install the latest version of this CLI from its registry",
|
|
8710
|
+
examples: [{
|
|
8711
|
+
command: `${cli.name} update`,
|
|
8712
|
+
note: "install the latest version and show what changed"
|
|
8713
|
+
}, {
|
|
8714
|
+
command: `${cli.name} update --dry-run`,
|
|
8715
|
+
note: "show the version it would install"
|
|
8716
|
+
}],
|
|
8717
|
+
effect: "write",
|
|
8718
|
+
errors: [NOT_GLOBAL_INSTALL, ...NPM_ERRORS],
|
|
8719
|
+
output: updateSchema,
|
|
8720
|
+
plan: async (_input, ctx) => {
|
|
8721
|
+
const npm = globalNpm(cli, install, ctx, launch);
|
|
8722
|
+
const copy = await globalInstallOf(launch.bin, npm);
|
|
8723
|
+
return {
|
|
8724
|
+
package: install.package,
|
|
8725
|
+
from: cli.version,
|
|
8726
|
+
latest: await latestVersion(npm),
|
|
8727
|
+
prefix: copy.prefix,
|
|
8728
|
+
packageDir: copy.packageDir,
|
|
8729
|
+
copies: cli.skill === void 0 ? [] : await homeCopiesOf(ctx.files, cli.name, ctx.env)
|
|
8730
|
+
};
|
|
8731
|
+
},
|
|
8732
|
+
apply: async (plan, ctx) => {
|
|
8733
|
+
const { from, latest, prefix } = plan;
|
|
8734
|
+
if (standingOf$1(from, latest) !== "behind") return {
|
|
8735
|
+
from,
|
|
8736
|
+
latest,
|
|
8737
|
+
installed: null,
|
|
8738
|
+
skill: await syncHome(cli, plan.packageDir, ctx),
|
|
8739
|
+
upgrades: []
|
|
8740
|
+
};
|
|
8741
|
+
ctx.signal.throwIfAborted();
|
|
8742
|
+
ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
|
|
8743
|
+
const skill = await ctx.runToEnd(`installing ${plan.package}@${latest}`, async () => {
|
|
8744
|
+
await installGlobally(latest, globalNpm(cli, install, ctx, launch));
|
|
8745
|
+
return syncHome(cli, plan.packageDir, ctx);
|
|
8499
8746
|
});
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8747
|
+
const update = {
|
|
8748
|
+
from,
|
|
8749
|
+
latest,
|
|
8750
|
+
installed: {
|
|
8751
|
+
version: latest,
|
|
8752
|
+
prefix
|
|
8753
|
+
},
|
|
8754
|
+
skill,
|
|
8755
|
+
upgrades: await upgradesOf(ctx.files, plan.packageDir, from, latest)
|
|
8756
|
+
};
|
|
8757
|
+
if (ctx.signal.aborted) {
|
|
8758
|
+
ctx.deliver(update);
|
|
8759
|
+
throw interruptedAfterInstall(plan);
|
|
8760
|
+
}
|
|
8761
|
+
return update;
|
|
8762
|
+
},
|
|
8763
|
+
render: (update) => renderUpdate(cli, install, update),
|
|
8764
|
+
renderPlan: (plan) => [standingOf$1(plan.from, plan.latest) === "behind" ? `would install ${plan.package}@${plan.latest} with the npm under ${plan.prefix}, replacing ${plan.from}` : nothingToInstall(plan), ...renderPlannedSync(cli.name, plan.copies)].join("\n")
|
|
8765
|
+
});
|
|
8511
8766
|
}
|
|
8512
8767
|
//#endregion
|
|
8513
8768
|
//#region ../agent-cli/dist/built-ins/install/release-check.js
|
|
@@ -8572,25 +8827,19 @@ function parsedCheck(text) {
|
|
|
8572
8827
|
return;
|
|
8573
8828
|
}
|
|
8574
8829
|
}
|
|
8575
|
-
async function lastCheck(file) {
|
|
8576
|
-
const text = await
|
|
8830
|
+
async function lastCheck(files, file) {
|
|
8831
|
+
const text = await readIfAny(files, file);
|
|
8577
8832
|
return text === void 0 ? void 0 : parsedCheck(text);
|
|
8578
8833
|
}
|
|
8579
|
-
async function
|
|
8580
|
-
await mkdir(dirname(file), { recursive: true });
|
|
8581
|
-
const temp = tempPathOf(file);
|
|
8582
|
-
await writeFile(temp, JSON.stringify(check));
|
|
8583
|
-
await rename(temp, file);
|
|
8584
|
-
}
|
|
8585
|
-
async function startCheck(cli, install, file, last, { node }, ctx) {
|
|
8834
|
+
async function startCheck(cli, install, file, last, launch, ctx) {
|
|
8586
8835
|
const checkedAt = ctx.clock.now();
|
|
8587
8836
|
const previous = last?.latest ?? "";
|
|
8588
|
-
await
|
|
8837
|
+
await ctx.files.write(file, JSON.stringify({
|
|
8589
8838
|
checkedAt,
|
|
8590
8839
|
latest: last?.latest
|
|
8591
|
-
});
|
|
8592
|
-
const npm = globalNpm(cli, install, ctx,
|
|
8593
|
-
const { path, env } = npmOf(npm
|
|
8840
|
+
}));
|
|
8841
|
+
const npm = globalNpm(cli, install, ctx, launch);
|
|
8842
|
+
const { path, env } = await npmOf(npm);
|
|
8594
8843
|
await ctx.spawnDetached(process.execPath, [
|
|
8595
8844
|
"-e",
|
|
8596
8845
|
CHECK_SCRIPT,
|
|
@@ -8623,7 +8872,7 @@ async function releaseCheck(cli, install, runAs, launch, ctx) {
|
|
|
8623
8872
|
}
|
|
8624
8873
|
async function checkAndTell(cli, install, runAs, launch, ctx) {
|
|
8625
8874
|
const file = cacheFileOf(cli.name, ctx.env);
|
|
8626
|
-
const last = await lastCheck(file);
|
|
8875
|
+
const last = await lastCheck(ctx.files, file);
|
|
8627
8876
|
if (last?.error !== void 0) debugLine(cli, ctx, last.error);
|
|
8628
8877
|
if (last === void 0 || ctx.clock.now() - last.checkedAt >= CHECK_INTERVAL_MS) await startCheck(cli, install, file, last, launch, ctx);
|
|
8629
8878
|
const latest = last?.latest;
|
|
@@ -8632,14 +8881,14 @@ async function checkAndTell(cli, install, runAs, launch, ctx) {
|
|
|
8632
8881
|
}
|
|
8633
8882
|
//#endregion
|
|
8634
8883
|
//#region ../agent-cli/dist/built-ins/install/global.js
|
|
8635
|
-
function checkOf(cli, install,
|
|
8884
|
+
function checkOf(cli, install, launch) {
|
|
8636
8885
|
return {
|
|
8637
8886
|
install: async (ctx) => {
|
|
8638
|
-
const npm = globalNpm(cli, install, ctx,
|
|
8887
|
+
const npm = globalNpm(cli, install, ctx, launch);
|
|
8639
8888
|
try {
|
|
8640
8889
|
return {
|
|
8641
8890
|
state: "ok",
|
|
8642
|
-
message: `a global npm install under ${(await globalInstallOf(bin, npm)).prefix}`
|
|
8891
|
+
message: `a global npm install under ${(await globalInstallOf(launch.bin, npm)).prefix}`
|
|
8643
8892
|
};
|
|
8644
8893
|
} catch (error) {
|
|
8645
8894
|
if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
|
|
@@ -8653,7 +8902,7 @@ function checkOf(cli, install, { bin, node }) {
|
|
|
8653
8902
|
release: async (ctx) => {
|
|
8654
8903
|
const fix = `${cli.name} update`;
|
|
8655
8904
|
return {
|
|
8656
|
-
npm: globalNpm(cli, install, ctx,
|
|
8905
|
+
npm: globalNpm(cli, install, ctx, launch),
|
|
8657
8906
|
advise: () => ({ fix })
|
|
8658
8907
|
};
|
|
8659
8908
|
}
|
|
@@ -8667,7 +8916,6 @@ function globalInstall(cli, install) {
|
|
|
8667
8916
|
setup: setupCommand(cli, install, launch),
|
|
8668
8917
|
update: updateCommand(cli, install, launch)
|
|
8669
8918
|
}),
|
|
8670
|
-
exitClasses: ["source"],
|
|
8671
8919
|
check: (launch) => checkOf(cli, install, launch),
|
|
8672
8920
|
notice: (launch, ctx) => releaseCheck(cli, install, adapter.runAs, launch, ctx)
|
|
8673
8921
|
};
|
|
@@ -8684,7 +8932,6 @@ function noInstall(cli) {
|
|
|
8684
8932
|
return {
|
|
8685
8933
|
runAs: runAsOf(cli),
|
|
8686
8934
|
builtIns: () => ({}),
|
|
8687
|
-
exitClasses: [],
|
|
8688
8935
|
check: () => ({
|
|
8689
8936
|
install: skipped,
|
|
8690
8937
|
release: async () => ({ skipped: await skipped() })
|
|
@@ -8715,9 +8962,9 @@ function withSection({ name, text: section }, text) {
|
|
|
8715
8962
|
if (text === "") return `${whole}\n`;
|
|
8716
8963
|
return `${text}${text.endsWith("\n") ? "" : "\n"}\n${whole}\n`;
|
|
8717
8964
|
}
|
|
8718
|
-
async function agentsMdAt(agents, root) {
|
|
8965
|
+
async function agentsMdAt(files, agents, root) {
|
|
8719
8966
|
const path = join(root, "AGENTS.md");
|
|
8720
|
-
const existing = await
|
|
8967
|
+
const existing = await readIfAny(files, path);
|
|
8721
8968
|
if (existing === void 0) return {
|
|
8722
8969
|
path,
|
|
8723
8970
|
agents,
|
|
@@ -8734,9 +8981,9 @@ async function agentsMdAt(agents, root) {
|
|
|
8734
8981
|
change: existing.includes(markersOf(agents.name).begin) ? "rewrite" : "add"
|
|
8735
8982
|
};
|
|
8736
8983
|
}
|
|
8737
|
-
async function handEditsAt(root) {
|
|
8984
|
+
async function handEditsAt(files, root) {
|
|
8738
8985
|
const claudeMd = join(root, "CLAUDE.md");
|
|
8739
|
-
const text = await
|
|
8986
|
+
const text = await readIfAny(files, claudeMd);
|
|
8740
8987
|
if (text === void 0 || text.split(/\r?\n/).some((line) => line.trim() === "@AGENTS.md")) return [];
|
|
8741
8988
|
return [`add a line \`@AGENTS.md\` to ${claudeMd}, so Claude Code reads AGENTS.md`];
|
|
8742
8989
|
}
|
|
@@ -8746,8 +8993,8 @@ function agentsRow(agents, install) {
|
|
|
8746
8993
|
name: "agents",
|
|
8747
8994
|
advisory: true,
|
|
8748
8995
|
run: async (ctx) => {
|
|
8749
|
-
const root = await projectRootOf(ctx.cwd);
|
|
8750
|
-
const { path, change } = await agentsMdAt(agents, root);
|
|
8996
|
+
const root = await projectRootOf(ctx.files, ctx.cwd);
|
|
8997
|
+
const { path, change } = await agentsMdAt(ctx.files, agents, root);
|
|
8751
8998
|
const fix = `${install.runAs} skill install`;
|
|
8752
8999
|
if (change === "rewrite") return {
|
|
8753
9000
|
state: "problem",
|
|
@@ -8760,7 +9007,7 @@ function agentsRow(agents, install) {
|
|
|
8760
9007
|
fix
|
|
8761
9008
|
};
|
|
8762
9009
|
const holds = `${path} holds the ${name} Agents section`;
|
|
8763
|
-
const [edit] = await handEditsAt(root);
|
|
9010
|
+
const [edit] = await handEditsAt(ctx.files, root);
|
|
8764
9011
|
return edit === void 0 ? {
|
|
8765
9012
|
state: "ok",
|
|
8766
9013
|
message: holds
|
|
@@ -8774,8 +9021,8 @@ function agentsRow(agents, install) {
|
|
|
8774
9021
|
//#endregion
|
|
8775
9022
|
//#region ../agent-cli/dist/built-ins/skill/install-command.js
|
|
8776
9023
|
/** The project root and the Skill copy under it, as `skill install` writes it. */
|
|
8777
|
-
async function skillCopyOf(cli, cwd) {
|
|
8778
|
-
const root = await projectRootOf(cwd);
|
|
9024
|
+
async function skillCopyOf(files, cli, cwd) {
|
|
9025
|
+
const root = await projectRootOf(files, cwd);
|
|
8779
9026
|
return {
|
|
8780
9027
|
root,
|
|
8781
9028
|
copy: join(root, ".claude", "skills", cli.name)
|
|
@@ -8791,12 +9038,14 @@ const installSchema = object({
|
|
|
8791
9038
|
}).nullable(),
|
|
8792
9039
|
handEdits: array(string())
|
|
8793
9040
|
});
|
|
8794
|
-
async function planOf(cli, folder, agents, cwd, packageRoot) {
|
|
8795
|
-
const
|
|
9041
|
+
async function planOf(cli, folder, agents, { files, cwd, packageRoot }) {
|
|
9042
|
+
const source = join(packageRoot, folder);
|
|
9043
|
+
await readRequired(files, join(source, "SKILL.md"));
|
|
9044
|
+
const { root, copy } = await skillCopyOf(files, cli, cwd);
|
|
8796
9045
|
return {
|
|
8797
|
-
...await syncOf(
|
|
8798
|
-
agentsMd: agents === void 0 ? null : await agentsMdAt(agents, root),
|
|
8799
|
-
handEdits: agents === void 0 ? [] : await handEditsAt(root)
|
|
9046
|
+
...await syncOf(files, source, copy),
|
|
9047
|
+
agentsMd: agents === void 0 ? null : await agentsMdAt(files, agents, root),
|
|
9048
|
+
handEdits: agents === void 0 ? [] : await handEditsAt(files, root)
|
|
8800
9049
|
};
|
|
8801
9050
|
}
|
|
8802
9051
|
const SECTION_VERBS = {
|
|
@@ -8839,12 +9088,12 @@ function skillInstallCommand(cli, folder, agents) {
|
|
|
8839
9088
|
}],
|
|
8840
9089
|
effect: "write",
|
|
8841
9090
|
output: installSchema,
|
|
8842
|
-
plan: (_input, ctx) => planOf(cli, folder, agents, ctx
|
|
9091
|
+
plan: (_input, ctx) => planOf(cli, folder, agents, ctx),
|
|
8843
9092
|
apply: async (plan, ctx) => {
|
|
8844
9093
|
await applySync(plan, ctx);
|
|
8845
9094
|
const { agentsMd } = plan;
|
|
8846
9095
|
if (agentsMd !== null && agentsMd.change !== null) {
|
|
8847
|
-
const existing = await
|
|
9096
|
+
const existing = await readIfAny(ctx.files, agentsMd.path) ?? "";
|
|
8848
9097
|
await ctx.files.write(agentsMd.path, withSection(agentsMd.agents, existing));
|
|
8849
9098
|
}
|
|
8850
9099
|
return {
|
|
@@ -8866,13 +9115,13 @@ function skillInstallCommand(cli, folder, agents) {
|
|
|
8866
9115
|
//#region ../agent-cli/dist/built-ins/skill/project.js
|
|
8867
9116
|
async function personalCopyOf(name, copy, ctx) {
|
|
8868
9117
|
const home = join(homeOf(ctx.env), ".claude", "skills", name);
|
|
8869
|
-
const real = await
|
|
8870
|
-
return real === void 0 || real === await
|
|
9118
|
+
const real = await ctx.files.realPath(home);
|
|
9119
|
+
return real === void 0 || real === await ctx.files.realPath(copy) ? void 0 : home;
|
|
8871
9120
|
}
|
|
8872
9121
|
/** The project root's copy against the package's, and any under home. */
|
|
8873
9122
|
async function findingOf(cli, folder, install, ctx) {
|
|
8874
|
-
const { copy } = await skillCopyOf(cli, ctx.cwd);
|
|
8875
|
-
const [installed, shipped] = await Promise.all([
|
|
9123
|
+
const { copy } = await skillCopyOf(ctx.files, cli, ctx.cwd);
|
|
9124
|
+
const [installed, shipped] = await Promise.all([readIfAny(ctx.files, join(copy, "SKILL.md")), readRequired(ctx.files, join(ctx.packageRoot, folder, "SKILL.md"))]);
|
|
8876
9125
|
const stated = installed === void 0 ? void 0 : skillVersionIn(installed);
|
|
8877
9126
|
const left = stated !== void 0 && skillVersionIn(shipped) === void 0;
|
|
8878
9127
|
const found = left ? `${copy} (${statedVersionNote(stated)})` : copy;
|
|
@@ -9050,7 +9299,7 @@ function registryRow(cli, check) {
|
|
|
9050
9299
|
function doctorChecks(cli, launch, scope) {
|
|
9051
9300
|
const install = installOf(cli);
|
|
9052
9301
|
const check = install.check(launch);
|
|
9053
|
-
const tools = toolRows(cli.tools ?? [], cli.commands);
|
|
9302
|
+
const tools = toolRows(cli.tools ?? [], cli.commands, launch.platform);
|
|
9054
9303
|
return {
|
|
9055
9304
|
checks: [
|
|
9056
9305
|
buildRow(cli),
|
|
@@ -9066,6 +9315,8 @@ function doctorChecks(cli, launch, scope) {
|
|
|
9066
9315
|
tools: new Set(tools.map(({ name }) => name))
|
|
9067
9316
|
};
|
|
9068
9317
|
}
|
|
9318
|
+
//#endregion
|
|
9319
|
+
//#region ../agent-cli/dist/built-ins/doctor.js
|
|
9069
9320
|
const DEFAULT_DEADLINE_MS = 1e4;
|
|
9070
9321
|
const MS_PER_SECOND = 1e3;
|
|
9071
9322
|
const MIN_LABEL_WIDTH = 8;
|
|
@@ -9136,12 +9387,12 @@ async function rowOf(check, ctx, { read, runAs, tools }) {
|
|
|
9136
9387
|
check: check.name,
|
|
9137
9388
|
...finding
|
|
9138
9389
|
},
|
|
9139
|
-
|
|
9390
|
+
check
|
|
9140
9391
|
};
|
|
9141
9392
|
}
|
|
9142
9393
|
function nextOf(checked, doctorLine) {
|
|
9143
9394
|
const fixes = checked.flatMap(({ row }) => row.fix === void 0 ? [] : [row.fix]);
|
|
9144
|
-
const unfixed = checked.some(({ row,
|
|
9395
|
+
const unfixed = checked.some(({ row, check }) => check.advisory === true && row.state === "problem" && row.fix === void 0);
|
|
9145
9396
|
return [...new Set(unfixed ? [...fixes, doctorLine] : fixes)];
|
|
9146
9397
|
}
|
|
9147
9398
|
function render({ rows, next }, style) {
|
|
@@ -9170,9 +9421,14 @@ function failure(cliName, rerun, problems) {
|
|
|
9170
9421
|
});
|
|
9171
9422
|
return new AggregateError(errors);
|
|
9172
9423
|
}
|
|
9424
|
+
function checkedRows(error, problems, table) {
|
|
9425
|
+
const undeclared = problems.flatMap(({ row, check }) => row.code !== void 0 && mustDeclare(row.code, table) && !check.errors?.includes(row.code) ? [`the ${check.name} row failed with ${row.code}`] : []);
|
|
9426
|
+
if (undeclared.length === 0) return error;
|
|
9427
|
+
return new Error(`doctor: ${undeclared.join("; ")}, which its errors do not declare`, { cause: error });
|
|
9428
|
+
}
|
|
9173
9429
|
/** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
|
|
9174
9430
|
function doctorCommand(cli, launch) {
|
|
9175
|
-
|
|
9431
|
+
const doctor = defineCommand$1({
|
|
9176
9432
|
description: "check the setup and say what fixes each problem",
|
|
9177
9433
|
examples: [{
|
|
9178
9434
|
command: `${cli.name} doctor`,
|
|
@@ -9201,15 +9457,20 @@ function doctorCommand(cli, launch) {
|
|
|
9201
9457
|
ctx.deliver(report);
|
|
9202
9458
|
ctx.signal.throwIfAborted();
|
|
9203
9459
|
}
|
|
9204
|
-
const problems = checked.filter(({ row,
|
|
9460
|
+
const problems = checked.filter(({ row, check }) => row.state === "problem" && check.advisory !== true);
|
|
9205
9461
|
if (problems.length > 0) {
|
|
9206
9462
|
ctx.deliver(report);
|
|
9207
|
-
throw failure(cli.name, ctx.rerun(), problems);
|
|
9463
|
+
throw checkedRows(failure(cli.name, ctx.rerun(), problems.map(({ row }) => row)), problems, errorTableOf(cli));
|
|
9208
9464
|
}
|
|
9209
9465
|
return report;
|
|
9210
9466
|
},
|
|
9211
9467
|
render
|
|
9212
9468
|
});
|
|
9469
|
+
const rowCodes = (cli.doctor ?? []).flatMap((row) => row.advisory === true ? [] : row.errors ?? []);
|
|
9470
|
+
return {
|
|
9471
|
+
...doctor,
|
|
9472
|
+
errors: [.../* @__PURE__ */ new Set([DOCTOR_PROBLEMS, ...rowCodes])]
|
|
9473
|
+
};
|
|
9213
9474
|
}
|
|
9214
9475
|
//#endregion
|
|
9215
9476
|
//#region ../agent-cli/dist/built-ins/commands.js
|
|
@@ -9309,19 +9570,6 @@ function specOf(commandLine, node, place) {
|
|
|
9309
9570
|
return isCommand(node) ? commandSpec(commandLine, node, place) : group(commandLine, specsOf(commandLine, node, place));
|
|
9310
9571
|
}
|
|
9311
9572
|
//#endregion
|
|
9312
|
-
//#region ../agent-cli/dist/error-table.js
|
|
9313
|
-
/** The Error table a CLI declares, empty when it declares none. */
|
|
9314
|
-
function errorTableOf({ errors }) {
|
|
9315
|
-
return errors ?? {};
|
|
9316
|
-
}
|
|
9317
|
-
/** The `CliError` for a code of `Table`, as `cliError` and `ctx.fail` build it. */
|
|
9318
|
-
function cliErrorOf(code, init, options) {
|
|
9319
|
-
return new CliError({
|
|
9320
|
-
...init,
|
|
9321
|
-
code
|
|
9322
|
-
}, options);
|
|
9323
|
-
}
|
|
9324
|
-
//#endregion
|
|
9325
9573
|
//#region ../agent-cli/dist/exit-classes.js
|
|
9326
9574
|
const SIGNAL_EXIT_CODES = {
|
|
9327
9575
|
SIGINT: 130,
|
|
@@ -9334,95 +9582,38 @@ const EXIT_CODES = {
|
|
|
9334
9582
|
wait: 75,
|
|
9335
9583
|
bug: 70
|
|
9336
9584
|
};
|
|
9337
|
-
const RUNTIME_EXIT_CLASSES = {
|
|
9338
|
-
INVALID_INPUT: "caller",
|
|
9339
|
-
UNKNOWN_OPTION: "caller",
|
|
9340
|
-
UNKNOWN_COMMAND: "caller",
|
|
9341
|
-
UNKNOWN_FIELD: "caller",
|
|
9342
|
-
CONFIRMATION_REQUIRED: "caller",
|
|
9343
|
-
CONFIRMATION_MISMATCH: "caller",
|
|
9344
|
-
NOT_CONFIRMED: "operator",
|
|
9345
|
-
NEEDS_OPERATOR: "operator",
|
|
9346
|
-
OUTPUT_EXISTS: "caller",
|
|
9347
|
-
STDIN_STALLED: "caller",
|
|
9348
|
-
INTERRUPTED: "interrupted",
|
|
9349
|
-
DEADLINE_EXCEEDED: "wait",
|
|
9350
|
-
INTERNAL: "bug",
|
|
9351
|
-
PERMISSION_DENIED: "operator",
|
|
9352
|
-
DISK_FULL: "operator",
|
|
9353
|
-
NOT_FOUND: "caller",
|
|
9354
|
-
ALREADY_EXISTS: "caller",
|
|
9355
|
-
NOT_A_FILE: "caller",
|
|
9356
|
-
TOOL_FAILED: "operator",
|
|
9357
|
-
TOOL_MISSING: "operator",
|
|
9358
|
-
SECRET_MISSING: "operator",
|
|
9359
|
-
PROJECT_INVALID: "operator",
|
|
9360
|
-
DOCTOR_PROBLEMS: "operator",
|
|
9361
|
-
NOT_GLOBAL_INSTALL: "operator",
|
|
9362
|
-
NOT_ON_PATH: "operator",
|
|
9363
|
-
REGISTRY_UNREACHABLE: "operator",
|
|
9364
|
-
REGISTRY_AUTH: "operator",
|
|
9365
|
-
INSTALL_FAILED: "operator",
|
|
9366
|
-
NPM_FAILED: "operator",
|
|
9367
|
-
NO_LATEST: "source"
|
|
9368
|
-
};
|
|
9369
|
-
function isRuntimeCode(code) {
|
|
9370
|
-
return Object.hasOwn(RUNTIME_EXIT_CLASSES, code);
|
|
9371
|
-
}
|
|
9372
9585
|
/** The codes of `errors` that the Runtime already declares, which a CLI cannot redeclare. */
|
|
9373
9586
|
function runtimeCodesIn(errors) {
|
|
9374
9587
|
return Object.keys(errors).filter(isRuntimeCode);
|
|
9375
9588
|
}
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
if (isRuntimeCode(error.code)) return RUNTIME_EXIT_CLASSES[error.code];
|
|
9380
|
-
return errors[error.code]?.class ?? "bug";
|
|
9381
|
-
}
|
|
9382
|
-
function isBug(error, errors) {
|
|
9383
|
-
return exitClassOfOne(error, errors) === "bug";
|
|
9589
|
+
/** The class of `code` in the Runtime's Error table or else in the CLI's `errors`. */
|
|
9590
|
+
function classOfCode(code, errors) {
|
|
9591
|
+
return isRuntimeCode(code) ? runtimeErrors[code].class : errors[code]?.class;
|
|
9384
9592
|
}
|
|
9385
|
-
function
|
|
9386
|
-
return
|
|
9593
|
+
function declaredCodesOf(node) {
|
|
9594
|
+
return node.kind === "command" ? node.command.errors ?? [] : Object.values(node.commands).flatMap(declaredCodesOf);
|
|
9387
9595
|
}
|
|
9388
|
-
|
|
9389
|
-
"
|
|
9390
|
-
"operator",
|
|
9391
|
-
"source",
|
|
9392
|
-
"wait",
|
|
9393
|
-
"interrupted"
|
|
9394
|
-
];
|
|
9395
|
-
function exitClassOf(error, errors) {
|
|
9396
|
-
const classes = errorsOf(error).map((each) => exitClassOfOne(each, errors));
|
|
9397
|
-
if (classes.includes("bug")) return "bug";
|
|
9398
|
-
return ACT_FIRST.find((exitClass) => classes.includes(exitClass)) ?? "bug";
|
|
9596
|
+
function isDeclarable(exitClass) {
|
|
9597
|
+
return exitClass !== void 0 && exitClass !== "bug" && exitClass !== "interrupted";
|
|
9399
9598
|
}
|
|
9400
9599
|
/**
|
|
9401
|
-
* The classes other than bug and interrupt that a run of `cli` can end in: those of the Runtime
|
|
9402
|
-
* codes
|
|
9600
|
+
* The classes other than bug and interrupt that a run of `cli` can end in: those of the Runtime
|
|
9601
|
+
* codes any Command can meet, those of the codes its Commands under `root` declare, Built-ins
|
|
9602
|
+
* included, and those of its own table.
|
|
9403
9603
|
*/
|
|
9404
|
-
function reachableExitClasses(cli) {
|
|
9405
|
-
const
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
...
|
|
9604
|
+
function reachableExitClasses(cli, root) {
|
|
9605
|
+
const table = errorTableOf(cli);
|
|
9606
|
+
const classes = [
|
|
9607
|
+
...anyCommandClasses,
|
|
9608
|
+
...declaredCodesOf(root).map((code) => classOfCode(code, table)),
|
|
9609
|
+
...Object.values(table).map((declaration) => declaration.class)
|
|
9410
9610
|
];
|
|
9411
|
-
|
|
9412
|
-
return /* @__PURE__ */ new Set([...runtime, ...own]);
|
|
9611
|
+
return new Set(classes.filter(isDeclarable));
|
|
9413
9612
|
}
|
|
9414
9613
|
/** The signal named by an interrupt's abort reason; any other reason counts as Ctrl-C. */
|
|
9415
9614
|
function stopSignalOf(reason) {
|
|
9416
9615
|
return reason === "SIGTERM" ? "SIGTERM" : "SIGINT";
|
|
9417
9616
|
}
|
|
9418
|
-
/**
|
|
9419
|
-
* The exit code of a run that failed with `error`, given the CLI's error table; `stop` is the
|
|
9420
|
-
* signal that interrupted it, if any.
|
|
9421
|
-
*/
|
|
9422
|
-
function exitCodeOf(error, errors, stop) {
|
|
9423
|
-
const exitClass = exitClassOf(error, errors);
|
|
9424
|
-
return exitClass === "interrupted" ? SIGNAL_EXIT_CODES[stopSignalOf(stop?.reason)] : EXIT_CODES[exitClass];
|
|
9425
|
-
}
|
|
9426
9617
|
//#endregion
|
|
9427
9618
|
//#region ../agent-cli/dist/agent-cli.js
|
|
9428
9619
|
function checkOwnCodes(errors) {
|
|
@@ -9454,9 +9645,11 @@ function checkCli(cli) {
|
|
|
9454
9645
|
checkSkill(cli);
|
|
9455
9646
|
const { commands } = cli;
|
|
9456
9647
|
checkTree(commands, [], table);
|
|
9648
|
+
for (const row of cli.doctor ?? []) checkErrors(row.errors ?? [], `the ${row.name} row of doctor`, table);
|
|
9457
9649
|
const taken = Object.keys(builtInCommands(cli, {
|
|
9458
9650
|
bin: void 0,
|
|
9459
|
-
node: void 0
|
|
9651
|
+
node: void 0,
|
|
9652
|
+
platform: process.platform
|
|
9460
9653
|
})).filter((name) => commands[name] !== void 0);
|
|
9461
9654
|
if (taken.length > 0) throw new Error(`${cli.name} defines ${taken.join(", ")}, a built-in Command it cannot replace`);
|
|
9462
9655
|
return cli;
|
|
@@ -9844,37 +10037,6 @@ function route(root, invocation) {
|
|
|
9844
10037
|
return walk(root, argv.filter((word, index) => !(index < options.length && word === JSON_FLAG.flag)), json, invocation);
|
|
9845
10038
|
}
|
|
9846
10039
|
//#endregion
|
|
9847
|
-
//#region ../agent-cli/dist/command-line/route-line.js
|
|
9848
|
-
/** Routes `argv`, the line's words after the Run-as, to a Command of `root`. */
|
|
9849
|
-
function routeLine(root, runAs, argv) {
|
|
9850
|
-
if (!argv.ok) return {
|
|
9851
|
-
ok: false,
|
|
9852
|
-
reason: `it does not tokenize: ${argv.reason}`
|
|
9853
|
-
};
|
|
9854
|
-
const invocation = {
|
|
9855
|
-
runAs,
|
|
9856
|
-
argv: argv.words,
|
|
9857
|
-
flags: []
|
|
9858
|
-
};
|
|
9859
|
-
const routed = route(root, invocation);
|
|
9860
|
-
switch (routed.kind) {
|
|
9861
|
-
case "usage error": return {
|
|
9862
|
-
ok: false,
|
|
9863
|
-
reason: routed.error.message
|
|
9864
|
-
};
|
|
9865
|
-
case "help":
|
|
9866
|
-
case "version": return {
|
|
9867
|
-
ok: false,
|
|
9868
|
-
reason: "it names no Command"
|
|
9869
|
-
};
|
|
9870
|
-
case "command": return {
|
|
9871
|
-
ok: true,
|
|
9872
|
-
routed,
|
|
9873
|
-
invocation
|
|
9874
|
-
};
|
|
9875
|
-
}
|
|
9876
|
-
}
|
|
9877
|
-
//#endregion
|
|
9878
10040
|
//#region ../agent-cli/dist/command-line/shell-words.js
|
|
9879
10041
|
const BLANK = /\s/;
|
|
9880
10042
|
const SHELL_SYNTAX = /* @__PURE__ */ new Set([
|
|
@@ -9973,39 +10135,126 @@ function shellWords(line) {
|
|
|
9973
10135
|
};
|
|
9974
10136
|
}
|
|
9975
10137
|
//#endregion
|
|
9976
|
-
//#region ../agent-cli/dist/
|
|
9977
|
-
|
|
9978
|
-
|
|
9979
|
-
|
|
9980
|
-
|
|
9981
|
-
|
|
9982
|
-
|
|
9983
|
-
|
|
9984
|
-
|
|
10138
|
+
//#region ../agent-cli/dist/command-line/line-problem.js
|
|
10139
|
+
const STDIN_TEXT = "text from stdin";
|
|
10140
|
+
/**
|
|
10141
|
+
* Why `line` would not run a Command of `root`: it does not tokenize, start with its lead, route to
|
|
10142
|
+
* a Command or pass its schemas; `undefined` when it would.
|
|
10143
|
+
*/
|
|
10144
|
+
async function lineProblem(root, { text, lead, runAs }) {
|
|
10145
|
+
const words = shellWords(text);
|
|
10146
|
+
if (!words.ok) return `it does not tokenize: ${words.reason}`;
|
|
10147
|
+
const leadWords = shellWords(lead);
|
|
10148
|
+
if (!leadWords.ok) return `its lead does not tokenize: ${leadWords.reason}`;
|
|
10149
|
+
if (leadWords.words.some((word, index) => words.words[index] !== word)) return `it does not start with \`${lead}\``;
|
|
10150
|
+
const invocation = {
|
|
10151
|
+
runAs,
|
|
10152
|
+
argv: words.words.slice(leadWords.words.length),
|
|
10153
|
+
flags: []
|
|
9985
10154
|
};
|
|
10155
|
+
const routed = route(root, invocation);
|
|
10156
|
+
switch (routed.kind) {
|
|
10157
|
+
case "usage error": return routed.error.message;
|
|
10158
|
+
case "help":
|
|
10159
|
+
case "version": return "it names no Command";
|
|
10160
|
+
case "command": try {
|
|
10161
|
+
await parseCommand(routed, invocation, async () => STDIN_TEXT);
|
|
10162
|
+
return;
|
|
10163
|
+
} catch (error) {
|
|
10164
|
+
if (error instanceof UsageError) return error.message;
|
|
10165
|
+
throw error;
|
|
10166
|
+
}
|
|
10167
|
+
}
|
|
9986
10168
|
}
|
|
10169
|
+
//#endregion
|
|
10170
|
+
//#region ../agent-cli/dist/check-next-steps.js
|
|
9987
10171
|
/**
|
|
9988
10172
|
* Why `step` would not run a Command of `root`, or `undefined` when it would. A line given whole is
|
|
9989
10173
|
* not checked, since it may run another program.
|
|
9990
10174
|
*/
|
|
9991
|
-
function nextStepProblem(root, runAs, step) {
|
|
10175
|
+
async function nextStepProblem(root, runAs, step) {
|
|
9992
10176
|
const words = wordsOf(step);
|
|
9993
10177
|
if (words === void 0) return void 0;
|
|
9994
|
-
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
}
|
|
10000
|
-
/**
|
|
10001
|
-
function
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10178
|
+
return lineProblem(root, {
|
|
10179
|
+
text: lineOf(words, runAs),
|
|
10180
|
+
lead: runAs,
|
|
10181
|
+
runAs
|
|
10182
|
+
});
|
|
10183
|
+
}
|
|
10184
|
+
/** The check that a failure's Next steps run Commands of `root`: a bug names each that would not. */
|
|
10185
|
+
function nextStepCheck(root, runAs) {
|
|
10186
|
+
return (error) => checkedNextSteps(error, root, runAs);
|
|
10187
|
+
}
|
|
10188
|
+
async function checkedNextSteps(error, root, runAs) {
|
|
10189
|
+
const problems = [];
|
|
10190
|
+
for (const leaf of errorsOf(error)) {
|
|
10191
|
+
if (!(leaf instanceof CliError)) continue;
|
|
10192
|
+
for (const step of leaf.next) {
|
|
10193
|
+
const problem = await nextStepProblem(root, runAs, step);
|
|
10194
|
+
if (problem !== void 0) problems.push(`\`${lineOf(step, runAs)}\`, a Next step of ${leaf.code}: ${problem}`);
|
|
10195
|
+
}
|
|
10196
|
+
}
|
|
10006
10197
|
return problems.length === 0 ? error : new Error(`a Next step would not run: ${problems.join("; ")}`, { cause: error });
|
|
10007
10198
|
}
|
|
10008
10199
|
//#endregion
|
|
10200
|
+
//#region ../agent-cli/dist/failure.js
|
|
10201
|
+
/**
|
|
10202
|
+
* What a run that threw `error` reports: the interrupt and deadline rewrite, then the undeclared
|
|
10203
|
+
* code check, then the Next step check, each leaf classified, and the exit code they give.
|
|
10204
|
+
*/
|
|
10205
|
+
async function failureOf(error, sources) {
|
|
10206
|
+
const { command, control, nextStepCheck } = sources;
|
|
10207
|
+
const rewritten = control === void 0 ? error : control.errorAfter(error);
|
|
10208
|
+
const declared = command === void 0 ? rewritten : checkedCodes(rewritten, command, sources);
|
|
10209
|
+
return failureFrom(classified(nextStepCheck === void 0 ? declared : await nextStepCheck(declared), sources.errors), sources.stop);
|
|
10210
|
+
}
|
|
10211
|
+
/** `failure` with `error`'s leaves after its own, which may change its exit code. */
|
|
10212
|
+
function withLeaves(failure, error, sources) {
|
|
10213
|
+
return failureFrom([...failure.leaves, ...classified(error, sources.errors)], sources.stop);
|
|
10214
|
+
}
|
|
10215
|
+
function classified(error, errors) {
|
|
10216
|
+
return errorsOf(error).map((leaf) => ({
|
|
10217
|
+
error: leaf,
|
|
10218
|
+
exitClass: exitClassOfOne(leaf, errors)
|
|
10219
|
+
}));
|
|
10220
|
+
}
|
|
10221
|
+
function failureFrom(leaves, stop) {
|
|
10222
|
+
const exitClass = exitClassOf(leaves);
|
|
10223
|
+
return {
|
|
10224
|
+
leaves,
|
|
10225
|
+
exitCode: exitClass === "interrupted" ? SIGNAL_EXIT_CODES[stopSignalOf(stop?.reason)] : EXIT_CODES[exitClass]
|
|
10226
|
+
};
|
|
10227
|
+
}
|
|
10228
|
+
function exitClassOfOne(error, errors) {
|
|
10229
|
+
if (!(error instanceof CliError)) return "bug";
|
|
10230
|
+
if (error instanceof UsageError) return "caller";
|
|
10231
|
+
return classOfCode(error.code, errors) ?? "bug";
|
|
10232
|
+
}
|
|
10233
|
+
const ACT_FIRST = [
|
|
10234
|
+
"caller",
|
|
10235
|
+
"operator",
|
|
10236
|
+
"source",
|
|
10237
|
+
"wait",
|
|
10238
|
+
"interrupted"
|
|
10239
|
+
];
|
|
10240
|
+
function exitClassOf(leaves) {
|
|
10241
|
+
const classes = leaves.map((leaf) => leaf.exitClass);
|
|
10242
|
+
if (classes.includes("bug")) return "bug";
|
|
10243
|
+
return ACT_FIRST.find((exitClass) => classes.includes(exitClass)) ?? "bug";
|
|
10244
|
+
}
|
|
10245
|
+
/**
|
|
10246
|
+
* `error`, or a bug naming each code in it that the Command must declare in its `errors` and does
|
|
10247
|
+
* not: one of the Error table, or a Runtime code only some Commands reach.
|
|
10248
|
+
*/
|
|
10249
|
+
function checkedCodes(error, { command, path }, { cliName, errors }) {
|
|
10250
|
+
const declared = new Set(command.errors ?? []);
|
|
10251
|
+
const undeclared = errorsOf(error).flatMap((leaf) => leaf instanceof CliError && mustDeclare(leaf.code, errors) && !declared.has(leaf.code) ? [leaf.code] : []);
|
|
10252
|
+
if (undeclared.length === 0) return error;
|
|
10253
|
+
const label = commandLabel(path, cliName).join(" ");
|
|
10254
|
+
const codes = [...new Set(undeclared)].join(", ");
|
|
10255
|
+
return new Error(`\`${label}\` threw ${codes}, which its errors do not declare`, { cause: error });
|
|
10256
|
+
}
|
|
10257
|
+
//#endregion
|
|
10009
10258
|
//#region ../agent-cli/dist/help.js
|
|
10010
10259
|
function table(rows) {
|
|
10011
10260
|
const width = Math.max(...rows.map(([left]) => left.length));
|
|
@@ -10043,10 +10292,10 @@ const EXIT_MEANINGS = {
|
|
|
10043
10292
|
source: "someone at the source acts",
|
|
10044
10293
|
wait: "not done yet: do what the hint says, then run it again"
|
|
10045
10294
|
};
|
|
10046
|
-
function exitCodeRows(cli) {
|
|
10295
|
+
function exitCodeRows(cli, root) {
|
|
10047
10296
|
return [
|
|
10048
10297
|
["0", "done"],
|
|
10049
|
-
...[...[...reachableExitClasses(cli)].map((exitClass) => [EXIT_CODES[exitClass], EXIT_MEANINGS[exitClass]]), [EXIT_CODES.bug, `a bug in ${cli.name}`]].sort(([a], [b]) => a - b).map(([code, meaning]) => [String(code), meaning]),
|
|
10298
|
+
...[...[...reachableExitClasses(cli, root)].map((exitClass) => [EXIT_CODES[exitClass], EXIT_MEANINGS[exitClass]]), [EXIT_CODES.bug, `a bug in ${cli.name}`]].sort(([a], [b]) => a - b).map(([code, meaning]) => [String(code), meaning]),
|
|
10050
10299
|
[Object.values(SIGNAL_EXIT_CODES).join(", "), "interrupted"]
|
|
10051
10300
|
];
|
|
10052
10301
|
}
|
|
@@ -10064,7 +10313,7 @@ function examplesSection(examples, style) {
|
|
|
10064
10313
|
}
|
|
10065
10314
|
/** The CLI's own help: a listing or its single Command's help, ending with exit codes and examples. */
|
|
10066
10315
|
function cliHelp(cli, root, style) {
|
|
10067
|
-
return lines(...root.kind === "command" ? commandLines(root, style, { examples: false }) : listingLines(`${cli.name} ${cli.version}`, root, style, cli.description), ...section("Exit codes", exitCodeRows(cli), style), ...examplesSection(rootExamples(root), style));
|
|
10316
|
+
return lines(...root.kind === "command" ? commandLines(root, style, { examples: false }) : listingLines(`${cli.name} ${cli.version}`, root, style, cli.description), ...section("Exit codes", exitCodeRows(cli, root), style), ...examplesSection(rootExamples(root), style));
|
|
10068
10317
|
}
|
|
10069
10318
|
function groupHelp(group, style) {
|
|
10070
10319
|
return lines(...listingLines(group.commandLine, group, style));
|
|
@@ -10142,7 +10391,7 @@ function commandData(spec, table) {
|
|
|
10142
10391
|
secrets: spec.command.secrets ?? [],
|
|
10143
10392
|
errors: (spec.command.errors ?? []).map((code) => ({
|
|
10144
10393
|
code,
|
|
10145
|
-
class: table
|
|
10394
|
+
class: classOfCode(code, table)
|
|
10146
10395
|
})),
|
|
10147
10396
|
args: inputSchema(spec.command.args ?? object({})),
|
|
10148
10397
|
options: optionsSchema(spec.options),
|
|
@@ -10175,7 +10424,10 @@ function errorsData(errors) {
|
|
|
10175
10424
|
function publishedTable(table, root) {
|
|
10176
10425
|
if (root.kind !== "command") return table;
|
|
10177
10426
|
const declared = new Set(root.command.errors ?? []);
|
|
10178
|
-
return Object.fromEntries(Object.entries(
|
|
10427
|
+
return Object.fromEntries(Object.entries({
|
|
10428
|
+
...table,
|
|
10429
|
+
...declaredRuntimeErrors
|
|
10430
|
+
}).filter(([code]) => declared.has(code)));
|
|
10179
10431
|
}
|
|
10180
10432
|
function cliHelpJson(cli, root) {
|
|
10181
10433
|
const { name, version, description } = cli;
|
|
@@ -10225,15 +10477,20 @@ function noticeLine(text, json) {
|
|
|
10225
10477
|
function stderrLine(key, text, json) {
|
|
10226
10478
|
return json ? `${JSON.stringify({ [key]: text })}\n` : `${text}\n`;
|
|
10227
10479
|
}
|
|
10228
|
-
function errorText(
|
|
10229
|
-
const text = formatRuntimeError(
|
|
10480
|
+
function errorText({ leaves }, format) {
|
|
10481
|
+
const text = formatRuntimeError(leaves, {
|
|
10230
10482
|
...format,
|
|
10231
|
-
runAs: format.invocation.runAs
|
|
10232
|
-
isBug: (each) => isBug(each, format.errors),
|
|
10233
|
-
isWait: (each) => isWait(each, format.errors)
|
|
10483
|
+
runAs: format.invocation.runAs
|
|
10234
10484
|
});
|
|
10235
10485
|
const { usageHelp, json } = format;
|
|
10236
|
-
return usageHelp !== void 0 && !json && error instanceof UsageError ? `${text}\n${usageHelp}` : text;
|
|
10486
|
+
return usageHelp !== void 0 && !json && leaves.some((leaf) => leaf.error instanceof UsageError) ? `${text}\n${usageHelp}` : text;
|
|
10487
|
+
}
|
|
10488
|
+
function failedResult(failure, format) {
|
|
10489
|
+
return {
|
|
10490
|
+
stdout: "",
|
|
10491
|
+
stderr: errorText(failure, format),
|
|
10492
|
+
exitCode: failure.exitCode
|
|
10493
|
+
};
|
|
10237
10494
|
}
|
|
10238
10495
|
function stdoutOf({ value, render }, format, spill) {
|
|
10239
10496
|
const text = printed(value, render, format);
|
|
@@ -10251,14 +10508,9 @@ const STOPPED_RESULT = {
|
|
|
10251
10508
|
stdout: "",
|
|
10252
10509
|
stderr: "",
|
|
10253
10510
|
exitCode: SUCCESS_EXIT_CODE
|
|
10254
|
-
};
|
|
10255
|
-
function errorResult(error, format) {
|
|
10256
|
-
|
|
10257
|
-
return {
|
|
10258
|
-
stdout: "",
|
|
10259
|
-
stderr: errorText(checked, format),
|
|
10260
|
-
exitCode: exitCodeOf(checked, format.errors, format.stop)
|
|
10261
|
-
};
|
|
10511
|
+
};
|
|
10512
|
+
async function errorResult(error, format) {
|
|
10513
|
+
return failedResult(await failureOf(error, format), format);
|
|
10262
10514
|
}
|
|
10263
10515
|
async function commandResult(settled, format, spill) {
|
|
10264
10516
|
if (settled.ok) try {
|
|
@@ -10272,23 +10524,23 @@ async function commandResult(settled, format, spill) {
|
|
|
10272
10524
|
}
|
|
10273
10525
|
const { error, delivered } = settled;
|
|
10274
10526
|
if (delivered === void 0) return errorResult(error, format);
|
|
10527
|
+
const failure = await failureOf(error, format);
|
|
10275
10528
|
let outcome;
|
|
10276
10529
|
try {
|
|
10277
10530
|
outcome = delivered();
|
|
10278
10531
|
} catch (rejected) {
|
|
10279
|
-
return
|
|
10532
|
+
return failedResult(withLeaves(failure, rejected, format), format);
|
|
10280
10533
|
}
|
|
10281
|
-
const result =
|
|
10534
|
+
const result = failedResult(failure, format);
|
|
10282
10535
|
try {
|
|
10283
10536
|
return {
|
|
10284
10537
|
...result,
|
|
10285
10538
|
stdout: await stdoutOf(outcome, format, spill)
|
|
10286
10539
|
};
|
|
10287
10540
|
} catch (spillError) {
|
|
10288
|
-
const both = new AggregateError([...errorsOf(error), spillError]);
|
|
10289
10541
|
return {
|
|
10290
10542
|
...result,
|
|
10291
|
-
stderr: errorText(
|
|
10543
|
+
stderr: errorText(withLeaves(failure, spillError, format), format)
|
|
10292
10544
|
};
|
|
10293
10545
|
}
|
|
10294
10546
|
}
|
|
@@ -10352,107 +10604,6 @@ function rootSpec(cli, launch) {
|
|
|
10352
10604
|
} : groupSpec(names, cli.commands, builtInCommands(cli, launch));
|
|
10353
10605
|
}
|
|
10354
10606
|
//#endregion
|
|
10355
|
-
//#region ../agent-cli/dist/command-errors.js
|
|
10356
|
-
/**
|
|
10357
|
-
* `error`, or a bug naming each Error table code in it that the Command does not declare in its
|
|
10358
|
-
* `errors`. Runtime codes are never in the table, and a built-in's failures, `doctor` rows'
|
|
10359
|
-
* included, belong to no declaration, so both pass.
|
|
10360
|
-
*/
|
|
10361
|
-
function checkedCodes(error, { command, path, builtIn }, cli) {
|
|
10362
|
-
if (builtIn) return error;
|
|
10363
|
-
const table = errorTableOf(cli);
|
|
10364
|
-
const declared = new Set(command.errors ?? []);
|
|
10365
|
-
const undeclared = errorsOf(error).flatMap((leaf) => leaf instanceof CliError && Object.hasOwn(table, leaf.code) && !declared.has(leaf.code) ? [leaf.code] : []);
|
|
10366
|
-
if (undeclared.length === 0) return error;
|
|
10367
|
-
const label = commandLabel(path, cli.name).join(" ");
|
|
10368
|
-
const codes = [...new Set(undeclared)].join(", ");
|
|
10369
|
-
return new Error(`\`${label}\` threw ${codes}, which its errors do not declare`, { cause: error });
|
|
10370
|
-
}
|
|
10371
|
-
//#endregion
|
|
10372
|
-
//#region ../agent-cli/dist/context/operator.js
|
|
10373
|
-
const NEEDS_OPERATOR = "NEEDS_OPERATOR";
|
|
10374
|
-
/** Fails with `NEEDS_OPERATOR` when a Coding agent runs an Operator Command. */
|
|
10375
|
-
function checkOperator({ operator, commandLine }, env, rerun) {
|
|
10376
|
-
if (!operator || !isAgent(env)) return;
|
|
10377
|
-
throw new CliError({
|
|
10378
|
-
code: NEEDS_OPERATOR,
|
|
10379
|
-
message: `\`${commandLine}\` must be run by a person at a terminal, not a Coding agent`,
|
|
10380
|
-
hint: "ask the operator to type ! then the Next step",
|
|
10381
|
-
next: [rerun]
|
|
10382
|
-
});
|
|
10383
|
-
}
|
|
10384
|
-
//#endregion
|
|
10385
|
-
//#region ../agent-cli/dist/context/outputs.js
|
|
10386
|
-
const OUTPUT_EXISTS = "OUTPUT_EXISTS";
|
|
10387
|
-
/** Refuses a run that would overwrite existing outputs unless the caller passed `--force`. */
|
|
10388
|
-
async function checkOutputs({ rerun, commandLine, force, files }, outputs) {
|
|
10389
|
-
if (outputs === void 0 || force) return;
|
|
10390
|
-
const found = await Promise.all(outputs.map((path) => files.exists(path)));
|
|
10391
|
-
const existing = outputs.filter((_path, index) => found[index]);
|
|
10392
|
-
if (existing.length === 0) return;
|
|
10393
|
-
const them = existing.length === 1 ? "it" : "them";
|
|
10394
|
-
throw new CliError({
|
|
10395
|
-
code: OUTPUT_EXISTS,
|
|
10396
|
-
message: `\`${commandLine}\` would overwrite ${existing.join(", ")}`,
|
|
10397
|
-
hint: `once the user agrees to replace ${them}, run the Next step`,
|
|
10398
|
-
next: [rerun({ add: [FORCE_FLAG.flag] })]
|
|
10399
|
-
});
|
|
10400
|
-
}
|
|
10401
|
-
/** Guards the writes to declared outputs of `files`, whose paths resolve against `cwd`. */
|
|
10402
|
-
function outputsGuard(files, cwd) {
|
|
10403
|
-
const unwritten = /* @__PURE__ */ new Set();
|
|
10404
|
-
let forceStep = [];
|
|
10405
|
-
const exclusiveWrite = async (path, data, options) => {
|
|
10406
|
-
const target = resolve(cwd, path);
|
|
10407
|
-
unwritten.delete(target);
|
|
10408
|
-
try {
|
|
10409
|
-
await files.write(path, data, {
|
|
10410
|
-
...options,
|
|
10411
|
-
exclusive: true
|
|
10412
|
-
});
|
|
10413
|
-
} catch (error) {
|
|
10414
|
-
unwritten.add(target);
|
|
10415
|
-
if (!(error instanceof CliError) || error.code !== "ALREADY_EXISTS") throw error;
|
|
10416
|
-
throw new CliError({
|
|
10417
|
-
code: ALREADY_EXISTS,
|
|
10418
|
-
message: error.message,
|
|
10419
|
-
hint: "once the user agrees to replace it, run the Next step",
|
|
10420
|
-
next: [forceStep]
|
|
10421
|
-
}, { cause: error });
|
|
10422
|
-
}
|
|
10423
|
-
};
|
|
10424
|
-
return {
|
|
10425
|
-
files: {
|
|
10426
|
-
...files,
|
|
10427
|
-
write: (path, data, options) => unwritten.has(resolve(cwd, path)) ? exclusiveWrite(path, data, options) : files.write(path, data, options)
|
|
10428
|
-
},
|
|
10429
|
-
guard: (outputs, step) => {
|
|
10430
|
-
forceStep = step;
|
|
10431
|
-
for (const path of outputs) unwritten.add(resolve(cwd, path));
|
|
10432
|
-
}
|
|
10433
|
-
};
|
|
10434
|
-
}
|
|
10435
|
-
/**
|
|
10436
|
-
* Creates each output folder and a temp file in it, so a sandbox denial fails before `apply`
|
|
10437
|
-
* spends anything. The temp file's name is the Runtime's, so a crashed run's leftover is swept.
|
|
10438
|
-
*/
|
|
10439
|
-
async function probeOutputs({ files, cwd, fileError }, outputs) {
|
|
10440
|
-
const folders = new Set(outputs.map((path) => dirname(resolve(cwd, path))));
|
|
10441
|
-
await Promise.all([...folders].map(async (folder) => {
|
|
10442
|
-
const probe = tempPathOf(join(folder, "outputs-probe"));
|
|
10443
|
-
try {
|
|
10444
|
-
await files.mkdir(folder);
|
|
10445
|
-
await files.write(probe, "");
|
|
10446
|
-
await files.remove(probe);
|
|
10447
|
-
} catch (error) {
|
|
10448
|
-
throw fileError(error, {
|
|
10449
|
-
path: folder,
|
|
10450
|
-
access: "write"
|
|
10451
|
-
});
|
|
10452
|
-
}
|
|
10453
|
-
}));
|
|
10454
|
-
}
|
|
10455
|
-
//#endregion
|
|
10456
10607
|
//#region ../agent-cli/dist/context/spill.js
|
|
10457
10608
|
const STDOUT_BUDGET_CHARS = 3e4;
|
|
10458
10609
|
const SPILL_RETENTION_MS = 6048e5;
|
|
@@ -11033,43 +11184,6 @@ function resolveOptions(options) {
|
|
|
11033
11184
|
};
|
|
11034
11185
|
}
|
|
11035
11186
|
//#endregion
|
|
11036
|
-
//#region ../agent-cli/dist/context/confirm.js
|
|
11037
|
-
const CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED";
|
|
11038
|
-
const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
|
|
11039
|
-
function checkConfirmation({ rerun, commandLine, given }, expected) {
|
|
11040
|
-
if (given === expected) return;
|
|
11041
|
-
const advice = {
|
|
11042
|
-
hint: "once the user confirms the target, run the Next step",
|
|
11043
|
-
next: [rerun({
|
|
11044
|
-
remove: [CONFIRM_FLAG.flag],
|
|
11045
|
-
add: [CONFIRM_FLAG.flag, expected]
|
|
11046
|
-
})]
|
|
11047
|
-
};
|
|
11048
|
-
if (given === void 0) throw new UsageError({
|
|
11049
|
-
code: CONFIRMATION_REQUIRED,
|
|
11050
|
-
message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
|
|
11051
|
-
...advice
|
|
11052
|
-
});
|
|
11053
|
-
throw new UsageError({
|
|
11054
|
-
code: CONFIRMATION_MISMATCH,
|
|
11055
|
-
message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
|
|
11056
|
-
...advice
|
|
11057
|
-
});
|
|
11058
|
-
}
|
|
11059
|
-
//#endregion
|
|
11060
|
-
//#region ../agent-cli/dist/context/cost.js
|
|
11061
|
-
const NOT_CONFIRMED = "NOT_CONFIRMED";
|
|
11062
|
-
/** Refuses a run whose Plan states a cost the caller has not approved with `--yes`. */
|
|
11063
|
-
function checkCost({ rerun, commandLine, yes }, cost) {
|
|
11064
|
-
if (cost === void 0 || yes) return;
|
|
11065
|
-
throw new CliError({
|
|
11066
|
-
code: NOT_CONFIRMED,
|
|
11067
|
-
message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
|
|
11068
|
-
hint: "once the user approves the cost, run the Next step",
|
|
11069
|
-
next: [rerun({ add: [YES_FLAG.flag] })]
|
|
11070
|
-
});
|
|
11071
|
-
}
|
|
11072
|
-
//#endregion
|
|
11073
11187
|
//#region ../agent-cli/dist/execute.js
|
|
11074
11188
|
const PLAN_INDENT = " ";
|
|
11075
11189
|
function outcomeOf(value, render) {
|
|
@@ -11117,8 +11231,8 @@ function serveContextOf(ctx, end) {
|
|
|
11117
11231
|
const serveCtx = Object.create(ctx);
|
|
11118
11232
|
return Object.assign(serveCtx, { end });
|
|
11119
11233
|
}
|
|
11120
|
-
async function execute(command, input, ctx, {
|
|
11121
|
-
await checkTools(command.tools ?? [], ctx
|
|
11234
|
+
async function execute(command, input, ctx, { flags, commandLine, started, outputs: runOutputs }) {
|
|
11235
|
+
await checkTools(command.tools ?? [], ctx, ctx.rerun());
|
|
11122
11236
|
const { env, envFile, fileError } = ctx;
|
|
11123
11237
|
checkSecrets(command.secrets ?? [], {
|
|
11124
11238
|
env,
|
|
@@ -11129,7 +11243,7 @@ async function execute(command, input, ctx, { dryRun, confirm, yes, page, comman
|
|
|
11129
11243
|
const { render } = command;
|
|
11130
11244
|
return outcomeOf(await command.run({
|
|
11131
11245
|
...input,
|
|
11132
|
-
...page
|
|
11246
|
+
...flags.page
|
|
11133
11247
|
}, ctx), (data, style) => render === void 0 ? encode(data.items) : render(data, style));
|
|
11134
11248
|
}
|
|
11135
11249
|
if (command.effect === "read" || command.effect === "act") return dataOutcome(command, await command.run(input, ctx), commandLine);
|
|
@@ -11144,32 +11258,28 @@ async function execute(command, input, ctx, { dryRun, confirm, yes, page, comman
|
|
|
11144
11258
|
return outcome;
|
|
11145
11259
|
}
|
|
11146
11260
|
const plan = await command.plan(input, ctx);
|
|
11147
|
-
if (dryRun) return dryRunOutcome(command, plan, commandLine);
|
|
11261
|
+
if (ctx.dryRun) return dryRunOutcome(command, plan, commandLine);
|
|
11148
11262
|
const outputs = command.outputs?.(plan);
|
|
11149
|
-
|
|
11150
|
-
|
|
11263
|
+
const { rerun, force } = ctx;
|
|
11264
|
+
if (outputs !== void 0) await runOutputs.refuseOverwrite(outputs, {
|
|
11265
|
+
rerun,
|
|
11151
11266
|
commandLine,
|
|
11152
|
-
force
|
|
11153
|
-
|
|
11154
|
-
}, outputs);
|
|
11267
|
+
force
|
|
11268
|
+
});
|
|
11155
11269
|
checkCost({
|
|
11156
|
-
rerun
|
|
11270
|
+
rerun,
|
|
11157
11271
|
commandLine,
|
|
11158
|
-
yes
|
|
11272
|
+
yes: flags.yes
|
|
11159
11273
|
}, command.cost?.(plan));
|
|
11160
11274
|
if (command.effect === "destructive") checkConfirmation({
|
|
11161
|
-
rerun
|
|
11275
|
+
rerun,
|
|
11162
11276
|
commandLine,
|
|
11163
|
-
given: confirm
|
|
11277
|
+
given: flags.confirm
|
|
11164
11278
|
}, command.confirm(plan));
|
|
11165
|
-
if (outputs !== void 0) {
|
|
11166
|
-
|
|
11167
|
-
|
|
11168
|
-
|
|
11169
|
-
fileError: ctx.fileError
|
|
11170
|
-
}, outputs);
|
|
11171
|
-
if (!ctx.force) guardOutputs(outputs, ctx.rerun({ add: [FORCE_FLAG.flag] }));
|
|
11172
|
-
}
|
|
11279
|
+
if (outputs !== void 0) await runOutputs.claim(outputs, {
|
|
11280
|
+
rerun,
|
|
11281
|
+
force
|
|
11282
|
+
});
|
|
11173
11283
|
return dataOutcome(command, await command.apply(plan, ctx), commandLine);
|
|
11174
11284
|
}
|
|
11175
11285
|
const manifestSchema = object({ bugs: union([string(), object({ url: string() })]).transform((value) => typeof value === "string" ? value : value.url).optional().catch(void 0) });
|
|
@@ -11185,16 +11295,7 @@ function manifestAt(packageRoot) {
|
|
|
11185
11295
|
return parsed.success ? parsed.data : NO_MANIFEST;
|
|
11186
11296
|
}
|
|
11187
11297
|
//#endregion
|
|
11188
|
-
//#region ../agent-cli/dist/run-
|
|
11189
|
-
function stdinNeverEndedHint(callerLine) {
|
|
11190
|
-
return `stdin never ended: pipe the text into \`${callerLine}\`, or redirect a file into it`;
|
|
11191
|
-
}
|
|
11192
|
-
function runDeadlineAdvice(resumed, pipedLine) {
|
|
11193
|
-
return resumed === void 0 ? { hint: stdinNeverEndedHint(pipedLine) } : {
|
|
11194
|
-
hint: "run the Next step to continue the work",
|
|
11195
|
-
next: [resumed]
|
|
11196
|
-
};
|
|
11197
|
-
}
|
|
11298
|
+
//#region ../agent-cli/dist/run-scope.js
|
|
11198
11299
|
function selfOf(name, bin) {
|
|
11199
11300
|
return bin === void 0 ? [name] : [
|
|
11200
11301
|
process.execPath,
|
|
@@ -11213,49 +11314,83 @@ function portsOf(cli, replacement) {
|
|
|
11213
11314
|
if (replacement !== void 0) return () => replacement;
|
|
11214
11315
|
return cli.ports ?? (() => ({}));
|
|
11215
11316
|
}
|
|
11216
|
-
function
|
|
11217
|
-
const {
|
|
11218
|
-
const
|
|
11317
|
+
function runScopeOf(cli, routed, settings, write, baseInvocation) {
|
|
11318
|
+
const { cwd, env } = settings;
|
|
11319
|
+
const clock = settings.clock ?? realClock;
|
|
11320
|
+
const platform = settings.platform ?? process.platform;
|
|
11321
|
+
const invocation = {
|
|
11322
|
+
...baseInvocation,
|
|
11323
|
+
flags: knownFlags(routed.spec),
|
|
11324
|
+
stdinArg: stdinArgOf(routed)
|
|
11325
|
+
};
|
|
11326
|
+
const rerun = (change) => ({
|
|
11327
|
+
kind: "rerun",
|
|
11328
|
+
words: changedArgv(invocation, change),
|
|
11329
|
+
stdinArg: invocation.stdinArg
|
|
11330
|
+
});
|
|
11331
|
+
const fileError = fileErrorOf({
|
|
11332
|
+
rerun: rerun(),
|
|
11333
|
+
env,
|
|
11334
|
+
platform
|
|
11335
|
+
});
|
|
11336
|
+
const outputs = outputsOf(settings.files ?? realFiles(), {
|
|
11337
|
+
cwd,
|
|
11338
|
+
fileError
|
|
11339
|
+
});
|
|
11340
|
+
const packageRoot = packageRootOf(settings.bin, cwd);
|
|
11341
|
+
const progress = (message) => write.stderr(progressLine(message, routed.json));
|
|
11219
11342
|
const envFile = envFileOf(cli.name, env);
|
|
11343
|
+
const spawning = {
|
|
11344
|
+
fileError,
|
|
11345
|
+
rerun: rerun()
|
|
11346
|
+
};
|
|
11220
11347
|
return {
|
|
11221
|
-
|
|
11222
|
-
|
|
11223
|
-
packageRoot,
|
|
11224
|
-
progress,
|
|
11225
|
-
secret: (name) => secretOf({
|
|
11226
|
-
envFile,
|
|
11227
|
-
env,
|
|
11228
|
-
fileError
|
|
11229
|
-
}, name),
|
|
11230
|
-
envFile,
|
|
11348
|
+
invocation,
|
|
11349
|
+
rerun,
|
|
11231
11350
|
fileError,
|
|
11232
|
-
version: cli.version,
|
|
11233
|
-
build: buildStamp(),
|
|
11234
|
-
self: selfOf(cli.name, options.bin),
|
|
11235
|
-
signal,
|
|
11236
11351
|
clock,
|
|
11237
|
-
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11242
|
-
|
|
11243
|
-
|
|
11244
|
-
rerun
|
|
11245
|
-
}),
|
|
11246
|
-
spawnDetached: contextSpawnDetached(spawnDetachedOf(options), {
|
|
11352
|
+
platform,
|
|
11353
|
+
outputs,
|
|
11354
|
+
progress,
|
|
11355
|
+
notice: (text) => write.stderr(noticeLine(text, routed.json)),
|
|
11356
|
+
packageRoot,
|
|
11357
|
+
ports: portsOf(cli, settings.ports),
|
|
11358
|
+
portsBase: (signal) => ({
|
|
11247
11359
|
cwd,
|
|
11248
11360
|
env,
|
|
11249
|
-
|
|
11250
|
-
|
|
11361
|
+
platform,
|
|
11362
|
+
packageRoot,
|
|
11363
|
+
progress,
|
|
11364
|
+
secret: (name) => secretOf({
|
|
11365
|
+
envFile,
|
|
11366
|
+
env,
|
|
11367
|
+
fileError
|
|
11368
|
+
}, name),
|
|
11369
|
+
envFile,
|
|
11251
11370
|
fileError,
|
|
11252
|
-
|
|
11371
|
+
version: cli.version,
|
|
11372
|
+
build: buildStamp(),
|
|
11373
|
+
self: selfOf(cli.name, settings.bin),
|
|
11374
|
+
signal,
|
|
11375
|
+
clock,
|
|
11376
|
+
files: outputs.files,
|
|
11377
|
+
spawn: contextSpawn(settings.spawn ?? realSpawn, {
|
|
11378
|
+
cwd,
|
|
11379
|
+
env,
|
|
11380
|
+
signal
|
|
11381
|
+
}, spawning),
|
|
11382
|
+
spawnDetached: contextSpawnDetached(spawnDetachedOf(settings), {
|
|
11383
|
+
cwd,
|
|
11384
|
+
env,
|
|
11385
|
+
signal
|
|
11386
|
+
}, spawning)
|
|
11253
11387
|
})
|
|
11254
11388
|
};
|
|
11255
11389
|
}
|
|
11256
|
-
function contextOf(
|
|
11257
|
-
const
|
|
11258
|
-
const
|
|
11390
|
+
function contextOf(scope, control, { command, flags, project, deliver }) {
|
|
11391
|
+
const portsBase = scope.portsBase(control.signal);
|
|
11392
|
+
const ports = once(() => scope.ports(portsBase));
|
|
11393
|
+
const declared = new Map((command.secrets ?? []).map((declaration) => [declaration.name, declaration]));
|
|
11259
11394
|
function secret(name) {
|
|
11260
11395
|
const declaration = declared.get(name);
|
|
11261
11396
|
if (declaration === void 0) throw new Error(`ctx.secret("${name}") names a Secret the Command does not declare`);
|
|
@@ -11273,47 +11408,57 @@ function contextOf(portsBase, control, parts) {
|
|
|
11273
11408
|
return ports();
|
|
11274
11409
|
},
|
|
11275
11410
|
project,
|
|
11276
|
-
force,
|
|
11277
|
-
dryRun,
|
|
11278
|
-
rerun,
|
|
11411
|
+
force: flags.force,
|
|
11412
|
+
dryRun: flags.dryRun,
|
|
11413
|
+
rerun: scope.rerun,
|
|
11279
11414
|
poll: (check, { intervalMs, signal: stop }) => poll(portsBase.clock, check, intervalMs, stop === void 0 ? control.signal : AbortSignal.any([control.signal, stop])),
|
|
11280
11415
|
deliver,
|
|
11281
|
-
notice
|
|
11416
|
+
notice: scope.notice
|
|
11417
|
+
};
|
|
11418
|
+
}
|
|
11419
|
+
/** `build` runs on the first call; later calls give its value or throw its error again. */
|
|
11420
|
+
function once(build) {
|
|
11421
|
+
let outcome;
|
|
11422
|
+
return () => {
|
|
11423
|
+
if (outcome === void 0) try {
|
|
11424
|
+
outcome = {
|
|
11425
|
+
ok: true,
|
|
11426
|
+
value: build()
|
|
11427
|
+
};
|
|
11428
|
+
} catch (error) {
|
|
11429
|
+
outcome = {
|
|
11430
|
+
ok: false,
|
|
11431
|
+
error
|
|
11432
|
+
};
|
|
11433
|
+
}
|
|
11434
|
+
if (outcome.ok) return outcome.value;
|
|
11435
|
+
throw outcome.error;
|
|
11436
|
+
};
|
|
11437
|
+
}
|
|
11438
|
+
//#endregion
|
|
11439
|
+
//#region ../agent-cli/dist/run-command.js
|
|
11440
|
+
function stdinNeverEndedHint(callerLine) {
|
|
11441
|
+
return `stdin never ended: pipe the text into \`${callerLine}\`, or redirect a file into it`;
|
|
11442
|
+
}
|
|
11443
|
+
function runDeadlineAdvice(resumed, pipedLine) {
|
|
11444
|
+
return resumed === void 0 ? { hint: stdinNeverEndedHint(pipedLine) } : {
|
|
11445
|
+
hint: "run the Next step to continue the work",
|
|
11446
|
+
next: [resumed]
|
|
11282
11447
|
};
|
|
11283
11448
|
}
|
|
11284
|
-
/**
|
|
11449
|
+
/**
|
|
11450
|
+
* Leaves the Side task running, so the caller writes the result before it waits for it.
|
|
11451
|
+
*
|
|
11452
|
+
* A gate that needs no `ctx` (operator, parse, Project file) runs here; one that does (tools,
|
|
11453
|
+
* Secrets, the Plan's) runs in `execute`, after the Notices, so a failing run still delivers them.
|
|
11454
|
+
*/
|
|
11285
11455
|
async function runCommand(cli, routed, options, write, baseFormat) {
|
|
11286
11456
|
const { command, commandLine, builtIn } = routed.spec;
|
|
11287
11457
|
const { cwd, env } = options;
|
|
11288
|
-
const clock = options.clock ?? realClock;
|
|
11289
|
-
const files = options.files ?? realFiles();
|
|
11290
11458
|
const stdin = options.stdin ?? "";
|
|
11291
|
-
const
|
|
11292
|
-
const
|
|
11293
|
-
const packageRoot = packageRootOf(options.bin, cwd);
|
|
11294
|
-
const progress = (message) => write.stderr(progressLine(message, routed.json));
|
|
11295
|
-
const invocation = {
|
|
11296
|
-
...baseFormat.invocation,
|
|
11297
|
-
flags: knownFlags(routed.spec),
|
|
11298
|
-
stdinArg: stdinArgOf(routed)
|
|
11299
|
-
};
|
|
11300
|
-
const rerunOf = (change) => ({
|
|
11301
|
-
kind: "rerun",
|
|
11302
|
-
words: changedArgv(invocation, change),
|
|
11303
|
-
stdinArg: invocation.stdinArg
|
|
11304
|
-
});
|
|
11459
|
+
const scope = runScopeOf(cli, routed, options, write, baseFormat.invocation);
|
|
11460
|
+
const { invocation, rerun: rerunOf, fileError, clock, outputs, progress, platform } = scope;
|
|
11305
11461
|
const rerun = rerunOf();
|
|
11306
|
-
const fileError = fileErrorOf({
|
|
11307
|
-
rerun,
|
|
11308
|
-
env,
|
|
11309
|
-
platform
|
|
11310
|
-
});
|
|
11311
|
-
const format = {
|
|
11312
|
-
...baseFormat,
|
|
11313
|
-
bugs: () => manifestAt(packageRoot).bugs,
|
|
11314
|
-
json: routed.json,
|
|
11315
|
-
invocation
|
|
11316
|
-
};
|
|
11317
11462
|
const pipedLine = commandLineOf({
|
|
11318
11463
|
...invocation,
|
|
11319
11464
|
stdinArg: void 0
|
|
@@ -11330,10 +11475,18 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11330
11475
|
advice: () => runDeadlineAdvice(resumed, pipedLine)
|
|
11331
11476
|
}
|
|
11332
11477
|
});
|
|
11333
|
-
const
|
|
11478
|
+
const format = {
|
|
11479
|
+
...baseFormat,
|
|
11480
|
+
command: routed.spec,
|
|
11481
|
+
control,
|
|
11482
|
+
bugs: () => manifestAt(scope.packageRoot).bugs,
|
|
11483
|
+
json: routed.json,
|
|
11484
|
+
invocation
|
|
11485
|
+
};
|
|
11486
|
+
const failed = async (error, finishSideTask) => {
|
|
11334
11487
|
control.close();
|
|
11335
11488
|
return {
|
|
11336
|
-
result: errorResult(error, format),
|
|
11489
|
+
result: await errorResult(error, format),
|
|
11337
11490
|
finishSideTask
|
|
11338
11491
|
};
|
|
11339
11492
|
};
|
|
@@ -11352,24 +11505,9 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11352
11505
|
}
|
|
11353
11506
|
const { input, flags } = parsed;
|
|
11354
11507
|
resumed = command.deadline?.resume(input, rerunOf);
|
|
11355
|
-
const outputs = outputsGuard(classifiedFiles(files, {
|
|
11356
|
-
cwd,
|
|
11357
|
-
fileError
|
|
11358
|
-
}), cwd);
|
|
11359
|
-
const scope = {
|
|
11360
|
-
cli,
|
|
11361
|
-
options,
|
|
11362
|
-
packageRoot,
|
|
11363
|
-
progress,
|
|
11364
|
-
fileError,
|
|
11365
|
-
rerun,
|
|
11366
|
-
clock,
|
|
11367
|
-
files: outputs.files
|
|
11368
|
-
};
|
|
11369
|
-
const portsBase = portsBaseOf(scope, control.signal);
|
|
11370
11508
|
const finishSideTask = startSideTask(builtIn ? void 0 : cli.sideTask?.bind(cli), (sideSignal) => {
|
|
11371
|
-
const sideBase =
|
|
11372
|
-
const sidePorts = once(() => ports(sideBase));
|
|
11509
|
+
const sideBase = scope.portsBase(sideSignal);
|
|
11510
|
+
const sidePorts = once(() => scope.ports(sideBase));
|
|
11373
11511
|
return {
|
|
11374
11512
|
...sideBase,
|
|
11375
11513
|
command: routed.spec.path,
|
|
@@ -11394,14 +11532,10 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11394
11532
|
return failed(error, finishSideTask);
|
|
11395
11533
|
}
|
|
11396
11534
|
let delivered;
|
|
11397
|
-
const ctx = contextOf(
|
|
11398
|
-
|
|
11535
|
+
const ctx = contextOf(scope, control, {
|
|
11536
|
+
command,
|
|
11537
|
+
flags,
|
|
11399
11538
|
project,
|
|
11400
|
-
force: flags.force,
|
|
11401
|
-
dryRun: flags.dryRun,
|
|
11402
|
-
secrets: command.secrets,
|
|
11403
|
-
rerun: rerunOf,
|
|
11404
|
-
notice: (text) => write.stderr(noticeLine(text, routed.json)),
|
|
11405
11539
|
deliver: (data) => {
|
|
11406
11540
|
delivered = () => dataOutcome(command, data, commandLine);
|
|
11407
11541
|
}
|
|
@@ -11420,19 +11554,16 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11420
11554
|
const outcome = await control.settle(noticesOf(cli, {
|
|
11421
11555
|
builtIn,
|
|
11422
11556
|
bin: options.bin,
|
|
11423
|
-
node: options.node
|
|
11557
|
+
node: options.node,
|
|
11558
|
+
platform
|
|
11424
11559
|
}, ctx).then(() => execute(command, input, ctx, {
|
|
11425
|
-
|
|
11426
|
-
confirm: flags.confirm,
|
|
11427
|
-
yes: flags.yes,
|
|
11428
|
-
page: flags.page,
|
|
11560
|
+
flags,
|
|
11429
11561
|
commandLine,
|
|
11430
11562
|
started: async (outcome) => {
|
|
11431
11563
|
write.stdout(await startedOutput(outcome, format, spill));
|
|
11432
11564
|
served = true;
|
|
11433
11565
|
},
|
|
11434
|
-
|
|
11435
|
-
guardOutputs: outputs.guard
|
|
11566
|
+
outputs
|
|
11436
11567
|
})));
|
|
11437
11568
|
if (served) return {
|
|
11438
11569
|
result: STOPPED_RESULT,
|
|
@@ -11449,7 +11580,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11449
11580
|
};
|
|
11450
11581
|
settled = {
|
|
11451
11582
|
ok: false,
|
|
11452
|
-
error
|
|
11583
|
+
error,
|
|
11453
11584
|
delivered
|
|
11454
11585
|
};
|
|
11455
11586
|
}
|
|
@@ -11461,25 +11592,6 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11461
11592
|
finishSideTask
|
|
11462
11593
|
};
|
|
11463
11594
|
}
|
|
11464
|
-
/** `build` runs on the first call; later calls give its value or throw its error again. */
|
|
11465
|
-
function once(build) {
|
|
11466
|
-
let outcome;
|
|
11467
|
-
return () => {
|
|
11468
|
-
if (outcome === void 0) try {
|
|
11469
|
-
outcome = {
|
|
11470
|
-
ok: true,
|
|
11471
|
-
value: build()
|
|
11472
|
-
};
|
|
11473
|
-
} catch (error) {
|
|
11474
|
-
outcome = {
|
|
11475
|
-
ok: false,
|
|
11476
|
-
error
|
|
11477
|
-
};
|
|
11478
|
-
}
|
|
11479
|
-
if (outcome.ok) return outcome.value;
|
|
11480
|
-
throw outcome.error;
|
|
11481
|
-
};
|
|
11482
|
-
}
|
|
11483
11595
|
async function noticesOf(cli, launch, ctx) {
|
|
11484
11596
|
await installOf(cli).notice(launch, ctx);
|
|
11485
11597
|
}
|
|
@@ -11538,7 +11650,8 @@ async function routeAndRun(cli, argv, options, write, stopNotice) {
|
|
|
11538
11650
|
const runAs = runAsOf(cli);
|
|
11539
11651
|
const root = rootSpec(cli, {
|
|
11540
11652
|
bin: options.bin,
|
|
11541
|
-
node: options.node
|
|
11653
|
+
node: options.node,
|
|
11654
|
+
platform: options.platform ?? process.platform
|
|
11542
11655
|
});
|
|
11543
11656
|
const format = {
|
|
11544
11657
|
cliName: cli.name,
|
|
@@ -11552,7 +11665,7 @@ async function routeAndRun(cli, argv, options, write, stopNotice) {
|
|
|
11552
11665
|
json: false,
|
|
11553
11666
|
errors: errorTableOf(cli),
|
|
11554
11667
|
stop: options.signal,
|
|
11555
|
-
...options.checkNextSteps === true ? {
|
|
11668
|
+
...options.checkNextSteps === true ? { nextStepCheck: nextStepCheck(root, runAs) } : {}
|
|
11556
11669
|
};
|
|
11557
11670
|
const routed = route(root, format.invocation);
|
|
11558
11671
|
const misused = (node) => isAgent(options.env) ? {
|
|
@@ -11631,7 +11744,7 @@ async function runProcess(cli) {
|
|
|
11631
11744
|
//#region package.json
|
|
11632
11745
|
var package_default = {
|
|
11633
11746
|
name: "@duyquangnvx/create-agent-cli",
|
|
11634
|
-
version: "0.
|
|
11747
|
+
version: "0.28.0",
|
|
11635
11748
|
description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
11636
11749
|
type: "module",
|
|
11637
11750
|
engines: { "node": ">=22.12.0" },
|