@duyquangnvx/create-agent-cli 0.26.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 +34 -0
- package/dist/bin.js +1503 -1373
- 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,177 +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 missingIn(scope, secrets) {
|
|
7037
|
-
return secrets.filter(({ name }) => secretOf(scope, name) === void 0);
|
|
7038
|
-
}
|
|
7039
|
-
function getItFrom({ get }) {
|
|
7040
|
-
return `(get it from ${get})`;
|
|
7041
|
-
}
|
|
7042
|
-
function whereToGet(secrets) {
|
|
7043
|
-
return secrets.map((secret) => `${secret.name} ${getItFrom(secret)}`).join(", ");
|
|
7044
|
-
}
|
|
7045
|
-
/** Fails with `SECRET_MISSING`, naming every missing Secret at once, unless all are set. */
|
|
7046
|
-
function checkSecrets(secrets, scope, rerun) {
|
|
7047
|
-
const missing = missingIn(scope, secrets);
|
|
7048
|
-
if (missing.length === 0) return;
|
|
7049
|
-
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;
|
|
7050
6957
|
throw new CliError({
|
|
7051
|
-
code:
|
|
7052
|
-
message:
|
|
7053
|
-
hint:
|
|
7054
|
-
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] })]
|
|
7055
6962
|
});
|
|
7056
6963
|
}
|
|
7057
|
-
function originOf({ origin }) {
|
|
7058
|
-
return origin.kind === "env" ? "the environment" : origin.path;
|
|
7059
|
-
}
|
|
7060
|
-
/** The `doctor` row over every distinct Secret the CLI and its Commands declare; none without any. */
|
|
7061
|
-
function secretRows(secrets, commands) {
|
|
7062
|
-
const declared = declaredIn(secrets, commands, (command) => command.secrets);
|
|
7063
|
-
if (declared.length === 0) return [];
|
|
7064
|
-
return [{
|
|
7065
|
-
name: "secrets",
|
|
7066
|
-
run: ({ env, envFile, fileError }) => {
|
|
7067
|
-
const read = declared.map((declaration) => ({
|
|
7068
|
-
declaration,
|
|
7069
|
-
secret: secretOf({
|
|
7070
|
-
env,
|
|
7071
|
-
envFile,
|
|
7072
|
-
fileError
|
|
7073
|
-
}, declaration.name)
|
|
7074
|
-
}));
|
|
7075
|
-
const missing = read.filter(({ secret }) => secret === void 0);
|
|
7076
|
-
if (missing.length > 0) return {
|
|
7077
|
-
state: "problem",
|
|
7078
|
-
message: `${missing.map(({ declaration }) => `${declaration.name} is not set ${getItFrom(declaration)}`).join(", ")}; add ${missing.length === 1 ? "it" : "them"} to ${envFile} or the environment`
|
|
7079
|
-
};
|
|
7080
|
-
return {
|
|
7081
|
-
state: "ok",
|
|
7082
|
-
message: read.flatMap(({ declaration, secret }) => secret === void 0 ? [] : [`${declaration.name} from ${originOf(secret)}`]).join(", ")
|
|
7083
|
-
};
|
|
7084
|
-
}
|
|
7085
|
-
}];
|
|
7086
|
-
}
|
|
7087
|
-
//#endregion
|
|
7088
|
-
//#region ../agent-cli/dist/version.js
|
|
7089
|
-
/** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
|
|
7090
|
-
function buildStamp() {
|
|
7091
|
-
return {
|
|
7092
|
-
"sha": "ed7073b",
|
|
7093
|
-
"dirty": false,
|
|
7094
|
-
"builtAt": "2026-11-06T05:43:19.000Z"
|
|
7095
|
-
};
|
|
7096
|
-
}
|
|
7097
|
-
/** The first line of `--version`: the version, and the commit and time of a stamped build. */
|
|
7098
|
-
function versionLine({ version }) {
|
|
7099
|
-
const build = buildStamp();
|
|
7100
|
-
return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
|
|
7101
|
-
}
|
|
7102
6964
|
//#endregion
|
|
7103
6965
|
//#region ../agent-cli/dist/context/agent.js
|
|
7104
6966
|
const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
|
|
@@ -7134,49 +6996,788 @@ function isAgent(env) {
|
|
|
7134
6996
|
return MARKERS.some((marker) => marker(env));
|
|
7135
6997
|
}
|
|
7136
6998
|
//#endregion
|
|
7137
|
-
//#region ../agent-cli/dist/
|
|
7138
|
-
const
|
|
7139
|
-
const
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
function
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
if (
|
|
7149
|
-
return
|
|
7150
|
-
}
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
if (a === void 0) return 1;
|
|
7154
|
-
if (b === void 0) return -1;
|
|
7155
|
-
const left = a.split(".");
|
|
7156
|
-
const right = b.split(".");
|
|
7157
|
-
for (let i = 0; i < Math.min(left.length, right.length); i += 1) {
|
|
7158
|
-
const difference = compareIdentifiers(left[i] ?? "", right[i] ?? "");
|
|
7159
|
-
if (difference !== 0) return difference;
|
|
7160
|
-
}
|
|
7161
|
-
return left.length - right.length;
|
|
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
|
+
};
|
|
7162
7015
|
}
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
for (const part of [
|
|
7169
|
-
1,
|
|
7170
|
-
2,
|
|
7171
|
-
3
|
|
7172
|
-
]) {
|
|
7173
|
-
const difference = Number(left[part]) - Number(right[part]);
|
|
7174
|
-
if (difference !== 0) return difference;
|
|
7175
|
-
}
|
|
7176
|
-
return comparePrerelease(left[4], right[4]);
|
|
7016
|
+
function fileErrorOf(scope) {
|
|
7017
|
+
return (error, at) => classifyErrno(error, {
|
|
7018
|
+
...at,
|
|
7019
|
+
...scope
|
|
7020
|
+
});
|
|
7177
7021
|
}
|
|
7178
|
-
|
|
7179
|
-
|
|
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. */
|
|
7765
|
+
function compareVersions(a, b) {
|
|
7766
|
+
const left = VERSION.exec(a);
|
|
7767
|
+
const right = VERSION.exec(b);
|
|
7768
|
+
if (left === null || right === null) throw new Error(`cannot compare ${a} with ${b}: both must be X.Y.Z versions`);
|
|
7769
|
+
for (const part of [
|
|
7770
|
+
1,
|
|
7771
|
+
2,
|
|
7772
|
+
3
|
|
7773
|
+
]) {
|
|
7774
|
+
const difference = Number(left[part]) - Number(right[part]);
|
|
7775
|
+
if (difference !== 0) return difference;
|
|
7776
|
+
}
|
|
7777
|
+
return comparePrerelease(left[4], right[4]);
|
|
7778
|
+
}
|
|
7779
|
+
/** Where `version` stands against `latest`. */
|
|
7780
|
+
function standingOf$1(version, latest) {
|
|
7180
7781
|
const difference = compareVersions(version, latest);
|
|
7181
7782
|
if (difference < 0) return "behind";
|
|
7182
7783
|
return difference > 0 ? "ahead" : "current";
|
|
@@ -7196,12 +7797,15 @@ function meetsMinimum(version, minimum) {
|
|
|
7196
7797
|
}
|
|
7197
7798
|
//#endregion
|
|
7198
7799
|
//#region ../agent-cli/dist/built-ins/npm.js
|
|
7199
|
-
|
|
7200
|
-
const
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
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) {
|
|
7205
7809
|
return {
|
|
7206
7810
|
cliName: cli.name,
|
|
7207
7811
|
rerun: ctx.rerun(),
|
|
@@ -7209,7 +7813,9 @@ function npmContext(cli, registry, ctx, node, cwd) {
|
|
|
7209
7813
|
cwd,
|
|
7210
7814
|
env: ctx.env,
|
|
7211
7815
|
signal: ctx.signal,
|
|
7212
|
-
node
|
|
7816
|
+
node,
|
|
7817
|
+
platform,
|
|
7818
|
+
files: ctx.files
|
|
7213
7819
|
};
|
|
7214
7820
|
}
|
|
7215
7821
|
const STDERR_TAIL_LINES = 10;
|
|
@@ -7251,9 +7857,10 @@ function setupCommandLine({ package: name, flags }) {
|
|
|
7251
7857
|
"setup"
|
|
7252
7858
|
].join(" ");
|
|
7253
7859
|
}
|
|
7254
|
-
function npmOf(ctx
|
|
7860
|
+
async function npmOf(ctx) {
|
|
7861
|
+
const windows = ctx.platform === "win32";
|
|
7255
7862
|
const beside = ctx.node === void 0 ? void 0 : join(dirname(ctx.node), windows ? "npm.cmd" : "npm");
|
|
7256
|
-
if (beside === void 0 || !
|
|
7863
|
+
if (beside === void 0 || !await ctx.files.exists(beside)) return {
|
|
7257
7864
|
path: "npm",
|
|
7258
7865
|
env: ctx.env
|
|
7259
7866
|
};
|
|
@@ -7270,8 +7877,7 @@ function npmOf(ctx, windows) {
|
|
|
7270
7877
|
}
|
|
7271
7878
|
};
|
|
7272
7879
|
}
|
|
7273
|
-
function spawnNpm(args, ctx, signal) {
|
|
7274
|
-
const npm = npmOf(ctx, process.platform === "win32");
|
|
7880
|
+
function spawnNpm(npm, args, ctx, signal) {
|
|
7275
7881
|
return spawnChild(npm.path, args, {
|
|
7276
7882
|
cwd: ctx.cwd,
|
|
7277
7883
|
env: npm.env,
|
|
@@ -7289,7 +7895,7 @@ function authHint(code, ctx) {
|
|
|
7289
7895
|
if (code !== "E403" || !isAgent(ctx.env)) return login;
|
|
7290
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}`;
|
|
7291
7897
|
}
|
|
7292
|
-
function failureOf(call, answer, ctx) {
|
|
7898
|
+
function failureOf$1(call, answer, ctx) {
|
|
7293
7899
|
const { label: registry, package: name, unreachableHint } = ctx.registry;
|
|
7294
7900
|
const code = NPM_CODE.exec(answer.stderr)?.[1];
|
|
7295
7901
|
const output = tail$1(answer.stderr);
|
|
@@ -7334,7 +7940,7 @@ function noLatest(ctx, message) {
|
|
|
7334
7940
|
async function runNpm(call, args, ctx, signal = ctx.signal) {
|
|
7335
7941
|
const cache = await mkdtemp(join(tmpdir(), `${ctx.cliName}-npm-`));
|
|
7336
7942
|
try {
|
|
7337
|
-
const answer = await spawnNpm([
|
|
7943
|
+
const answer = await spawnNpm(await npmOf(ctx), [
|
|
7338
7944
|
...args,
|
|
7339
7945
|
`--cache=${join(cache, "cache")}`,
|
|
7340
7946
|
"--logs-max=0"
|
|
@@ -7346,7 +7952,7 @@ async function runNpm(call, args, ctx, signal = ctx.signal) {
|
|
|
7346
7952
|
hint: "install Node with npm, or put npm on PATH"
|
|
7347
7953
|
}, { cause: error });
|
|
7348
7954
|
});
|
|
7349
|
-
if (answer.exitCode !== 0) throw failureOf(call, answer, ctx);
|
|
7955
|
+
if (answer.exitCode !== 0) throw failureOf$1(call, answer, ctx);
|
|
7350
7956
|
return answer.stdout;
|
|
7351
7957
|
} finally {
|
|
7352
7958
|
await rm(cache, {
|
|
@@ -7396,6 +8002,30 @@ async function latestVersion(ctx) {
|
|
|
7396
8002
|
return version;
|
|
7397
8003
|
}
|
|
7398
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
|
|
7399
8029
|
//#region ../agent-cli/dist/built-ins/project-root.js
|
|
7400
8030
|
const MANAGERS = [
|
|
7401
8031
|
"pnpm",
|
|
@@ -7420,22 +8050,14 @@ function ancestorsOf(dir) {
|
|
|
7420
8050
|
const parent = dirname(dir);
|
|
7421
8051
|
return parent === dir ? [dir] : [dir, ...ancestorsOf(parent)];
|
|
7422
8052
|
}
|
|
7423
|
-
async function exists(path) {
|
|
7424
|
-
return await orMissing(stat(path)) !== void 0;
|
|
7425
|
-
}
|
|
7426
8053
|
/** Whether `path` lies strictly inside `dir`. */
|
|
7427
8054
|
function isInside(dir, path) {
|
|
7428
8055
|
const inner = relative(dir, path);
|
|
7429
8056
|
return inner !== "" && !inner.startsWith("..") && !isAbsolute(inner);
|
|
7430
8057
|
}
|
|
7431
|
-
async function manifestIn(dir) {
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
text = await readFile(join(dir, "package.json"), "utf8");
|
|
7435
|
-
} catch (error) {
|
|
7436
|
-
if (isMissingFile(error)) return void 0;
|
|
7437
|
-
throw error;
|
|
7438
|
-
}
|
|
8058
|
+
async function manifestIn(files, dir) {
|
|
8059
|
+
const text = await readIfAny(files, join(dir, "package.json"));
|
|
8060
|
+
if (text === void 0) return void 0;
|
|
7439
8061
|
let manifest;
|
|
7440
8062
|
try {
|
|
7441
8063
|
manifest = JSON.parse(text);
|
|
@@ -7444,19 +8066,19 @@ async function manifestIn(dir) {
|
|
|
7444
8066
|
}
|
|
7445
8067
|
return manifestSchema$1.safeParse(manifest).data;
|
|
7446
8068
|
}
|
|
7447
|
-
async function declaredManager(dir) {
|
|
7448
|
-
const name = (await manifestIn(dir))?.packageManager?.split("@")[0];
|
|
8069
|
+
async function declaredManager(files, dir) {
|
|
8070
|
+
const name = (await manifestIn(files, dir))?.packageManager?.split("@")[0];
|
|
7449
8071
|
return MANAGERS.find((manager) => manager === name);
|
|
7450
8072
|
}
|
|
7451
|
-
async function lockfileRootOf(cwd) {
|
|
7452
|
-
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 {
|
|
7453
8075
|
dir,
|
|
7454
|
-
manager: await declaredManager(dir) ?? kind
|
|
8076
|
+
manager: await declaredManager(files, dir) ?? kind
|
|
7455
8077
|
};
|
|
7456
8078
|
}
|
|
7457
8079
|
/** The package manager's project root: the folder of the nearest lockfile, or `cwd` without one. */
|
|
7458
|
-
async function projectRootOf(cwd) {
|
|
7459
|
-
return (await lockfileRootOf(cwd))?.dir ?? cwd;
|
|
8080
|
+
async function projectRootOf(files, cwd) {
|
|
8081
|
+
return (await lockfileRootOf(files, cwd))?.dir ?? cwd;
|
|
7460
8082
|
}
|
|
7461
8083
|
//#endregion
|
|
7462
8084
|
//#region ../agent-cli/dist/built-ins/install/run-as.js
|
|
@@ -7479,29 +8101,30 @@ const LINES = {
|
|
|
7479
8101
|
}
|
|
7480
8102
|
};
|
|
7481
8103
|
const packageSchema = object({ version: string() });
|
|
7482
|
-
async function manifestListing(name, cwd, root) {
|
|
8104
|
+
async function manifestListing(files, name, cwd, root) {
|
|
7483
8105
|
const dirs = ancestorsOf(cwd);
|
|
7484
8106
|
const top = root === void 0 ? dirs.length : dirs.indexOf(root.dir) + 1;
|
|
7485
8107
|
for (const dir of dirs.slice(0, top)) {
|
|
7486
|
-
const manifest = await manifestIn(dir);
|
|
8108
|
+
const manifest = await manifestIn(files, dir);
|
|
7487
8109
|
if (manifest?.dependencies?.[name] !== void 0 || manifest?.devDependencies?.[name] !== void 0) return join(dir, "package.json");
|
|
7488
8110
|
}
|
|
7489
8111
|
}
|
|
7490
|
-
|
|
8112
|
+
function versionIn(text) {
|
|
7491
8113
|
try {
|
|
7492
|
-
return packageSchema.parse(JSON.parse(
|
|
8114
|
+
return packageSchema.parse(JSON.parse(text)).version;
|
|
7493
8115
|
} catch (error) {
|
|
7494
8116
|
if (error instanceof SyntaxError || error instanceof ZodError) return { reason: messageOf(error) };
|
|
7495
8117
|
throw error;
|
|
7496
8118
|
}
|
|
7497
8119
|
}
|
|
7498
|
-
async function standingOf(install, bin, cwd, root) {
|
|
8120
|
+
async function standingOf(files, install, bin, cwd, root) {
|
|
7499
8121
|
if (bin === void 0) return { kind: "source" };
|
|
7500
8122
|
for (const projectDir of ancestorsOf(cwd)) {
|
|
7501
8123
|
const folder = join(projectDir, "node_modules", install.package);
|
|
7502
8124
|
const manifest = join(folder, "package.json");
|
|
7503
|
-
|
|
7504
|
-
|
|
8125
|
+
const text = await readIfAny(files, manifest);
|
|
8126
|
+
if (text === void 0) continue;
|
|
8127
|
+
const version = versionIn(text);
|
|
7505
8128
|
if (typeof version !== "string") return {
|
|
7506
8129
|
kind: "unreadable",
|
|
7507
8130
|
manifest,
|
|
@@ -7509,14 +8132,14 @@ async function standingOf(install, bin, cwd, root) {
|
|
|
7509
8132
|
};
|
|
7510
8133
|
const copy = {
|
|
7511
8134
|
projectDir,
|
|
7512
|
-
realDir: await
|
|
8135
|
+
realDir: await files.realPath(folder) ?? folder,
|
|
7513
8136
|
version
|
|
7514
8137
|
};
|
|
7515
8138
|
if (!copy.realDir.split(sep).includes("node_modules")) return {
|
|
7516
8139
|
kind: "linked",
|
|
7517
8140
|
copy
|
|
7518
8141
|
};
|
|
7519
|
-
const runningFrom = await
|
|
8142
|
+
const runningFrom = await files.realPath(bin) ?? bin;
|
|
7520
8143
|
return isInside(copy.realDir, runningFrom) ? {
|
|
7521
8144
|
kind: "installed",
|
|
7522
8145
|
copy
|
|
@@ -7526,18 +8149,18 @@ async function standingOf(install, bin, cwd, root) {
|
|
|
7526
8149
|
runningFrom
|
|
7527
8150
|
};
|
|
7528
8151
|
}
|
|
7529
|
-
const manifest = await manifestListing(install.package, cwd, root);
|
|
8152
|
+
const manifest = await manifestListing(files, install.package, cwd, root);
|
|
7530
8153
|
return manifest === void 0 ? { kind: "missing" } : {
|
|
7531
8154
|
kind: "listed",
|
|
7532
8155
|
manifest
|
|
7533
8156
|
};
|
|
7534
8157
|
}
|
|
7535
|
-
async function surveyDependency(install, bin, cwd) {
|
|
7536
|
-
const root = await lockfileRootOf(cwd);
|
|
8158
|
+
async function surveyDependency(install, bin, { files, cwd }) {
|
|
8159
|
+
const root = await lockfileRootOf(files, cwd);
|
|
7537
8160
|
return {
|
|
7538
8161
|
cwd,
|
|
7539
8162
|
root,
|
|
7540
|
-
standing: await standingOf(install, bin, cwd, root)
|
|
8163
|
+
standing: await standingOf(files, install, bin, cwd, root)
|
|
7541
8164
|
};
|
|
7542
8165
|
}
|
|
7543
8166
|
function packagesOf(install) {
|
|
@@ -7584,269 +8207,69 @@ function installFinding(cli, runAs, install, { cwd, root, standing }) {
|
|
|
7584
8207
|
const where = root === void 0 ? copy.projectDir : `${root.dir} with ${root.manager}`;
|
|
7585
8208
|
return {
|
|
7586
8209
|
state: "ok",
|
|
7587
|
-
message: `${install.package} ${copy.version}, installed in ${where}`
|
|
7588
|
-
};
|
|
7589
|
-
}
|
|
7590
|
-
}
|
|
7591
|
-
}
|
|
7592
|
-
function registryOf({ package: name }, root) {
|
|
7593
|
-
const npmrc = root === void 0 ? "the project's .npmrc" : join(root, ".npmrc");
|
|
7594
|
-
const scope = scopeOf(name);
|
|
7595
|
-
return {
|
|
7596
|
-
package: name,
|
|
7597
|
-
label: `the registry npm uses for ${name}`,
|
|
7598
|
-
flags: [],
|
|
7599
|
-
unreachableHint: scope === void 0 ? "check the network or VPN" : `check the network or VPN and the ${scope}:registry that ${npmrc} sets`,
|
|
7600
|
-
loginHint: `log in to it (\`npm login${scope === void 0 ? "" : ` --scope=${scope}`}\`) or set its token in ${npmrc}`,
|
|
7601
|
-
missingHint: scope === void 0 ? `check that ${name} is published on the registry ${npmrc} names` : `check that ${name} is published, and that ${npmrc} maps ${scope} to its registry: ${scope}:registry=<url>`
|
|
7602
|
-
};
|
|
7603
|
-
}
|
|
7604
|
-
function adviceFor(install, root, latest) {
|
|
7605
|
-
const packages = packagesOf(install);
|
|
7606
|
-
const lines = root === void 0 ? void 0 : LINES[root.manager];
|
|
7607
|
-
if (lines !== void 0) return { fix: lines.upgrade(packages) };
|
|
7608
|
-
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"}` };
|
|
7609
|
-
}
|
|
7610
|
-
function checkOf$1(cli, install, runAs, { bin, node }) {
|
|
7611
|
-
let survey;
|
|
7612
|
-
const surveyed = (cwd) => {
|
|
7613
|
-
survey ??= surveyDependency(install, bin, cwd);
|
|
7614
|
-
return survey;
|
|
7615
|
-
};
|
|
7616
|
-
return {
|
|
7617
|
-
install: async (ctx) => installFinding(cli, runAs, install, await surveyed(ctx.cwd)),
|
|
7618
|
-
release: async (ctx) => {
|
|
7619
|
-
const { cwd, root, standing } = await surveyed(ctx.cwd);
|
|
7620
|
-
if (isUnchecked(standing)) return { skipped: skippedFor$1(cli, install, standing) };
|
|
7621
|
-
return {
|
|
7622
|
-
npm: npmContext(cli, registryOf(install, root?.dir), ctx, node, root?.dir ?? cwd),
|
|
7623
|
-
advise: (latest) => adviceFor(install, root, latest)
|
|
7624
|
-
};
|
|
7625
|
-
}
|
|
7626
|
-
};
|
|
7627
|
-
}
|
|
7628
|
-
/** The Install of a CLI each project pins as a dependency, run through npx (ADR 0011). */
|
|
7629
|
-
function dependencyInstall(cli, install) {
|
|
7630
|
-
const runAs = runAsOf(cli);
|
|
7631
|
-
return {
|
|
7632
|
-
runAs,
|
|
7633
|
-
builtIns: () => ({}),
|
|
7634
|
-
exitClasses: [],
|
|
7635
|
-
check: (launch) => checkOf$1(cli, install, runAs, launch),
|
|
7636
|
-
notice: async () => {}
|
|
7637
|
-
};
|
|
7638
|
-
}
|
|
7639
|
-
//#endregion
|
|
7640
|
-
//#region ../agent-cli/dist/context/errno.js
|
|
7641
|
-
const PERMISSION_DENIED = "PERMISSION_DENIED";
|
|
7642
|
-
const DISK_FULL = "DISK_FULL";
|
|
7643
|
-
const NOT_FOUND = "NOT_FOUND";
|
|
7644
|
-
const ALREADY_EXISTS = "ALREADY_EXISTS";
|
|
7645
|
-
const NOT_A_FILE = "NOT_A_FILE";
|
|
7646
|
-
function permissionAdvice({ path, access, rerun, env, platform }) {
|
|
7647
|
-
if (platform === "win32") return {
|
|
7648
|
-
hint: `a program may hold ${path} open: close it, then run the Next step`,
|
|
7649
|
-
next: [rerun]
|
|
7650
|
-
};
|
|
7651
|
-
if (!isAgent(env)) return { hint: `check the permissions of ${path} and its folder` };
|
|
7652
|
-
return {
|
|
7653
|
-
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`}`,
|
|
7654
|
-
next: [rerun]
|
|
7655
|
-
};
|
|
7656
|
-
}
|
|
7657
|
-
function fileErrorOf(scope) {
|
|
7658
|
-
return (error, at) => classifyErrno(error, {
|
|
7659
|
-
...at,
|
|
7660
|
-
...scope
|
|
7661
|
-
});
|
|
7662
|
-
}
|
|
7663
|
-
function classifyErrno(error, context) {
|
|
7664
|
-
const { path } = context;
|
|
7665
|
-
switch (errnoOf(error)) {
|
|
7666
|
-
case "EACCES":
|
|
7667
|
-
case "EPERM":
|
|
7668
|
-
case "EROFS":
|
|
7669
|
-
case "EBUSY": return new CliError({
|
|
7670
|
-
code: PERMISSION_DENIED,
|
|
7671
|
-
message: `permission denied: ${path}`,
|
|
7672
|
-
...permissionAdvice(context)
|
|
7673
|
-
}, { cause: error });
|
|
7674
|
-
case "ENOSPC":
|
|
7675
|
-
case "EDQUOT": return new CliError({
|
|
7676
|
-
code: DISK_FULL,
|
|
7677
|
-
message: `no space left on the disk holding ${path}`,
|
|
7678
|
-
hint: "free space on that disk, then run the Next step",
|
|
7679
|
-
next: [context.rerun]
|
|
7680
|
-
}, { cause: error });
|
|
7681
|
-
case "ENOENT":
|
|
7682
|
-
case "ENOTDIR":
|
|
7683
|
-
case "ENAMETOOLONG": return new CliError({
|
|
7684
|
-
code: NOT_FOUND,
|
|
7685
|
-
message: `not found: ${path}`,
|
|
7686
|
-
hint: `check that ${path} and its folder exist`
|
|
7687
|
-
}, { cause: error });
|
|
7688
|
-
case "EEXIST": return new CliError({
|
|
7689
|
-
code: ALREADY_EXISTS,
|
|
7690
|
-
message: `already exists: ${path}`,
|
|
7691
|
-
hint: `pass another path, or remove ${path}`
|
|
7692
|
-
}, { cause: error });
|
|
7693
|
-
case "EISDIR": return new CliError({
|
|
7694
|
-
code: NOT_A_FILE,
|
|
7695
|
-
message: `not a file: ${path} is a folder`,
|
|
7696
|
-
hint: `pass the path of a file, not of the folder ${path}`
|
|
7697
|
-
}, { cause: error });
|
|
7698
|
-
default: return error;
|
|
7699
|
-
}
|
|
7700
|
-
}
|
|
7701
|
-
//#endregion
|
|
7702
|
-
//#region ../agent-cli/dist/context/interrupts.js
|
|
7703
|
-
const INTERRUPTED = "INTERRUPTED";
|
|
7704
|
-
const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
|
|
7705
|
-
/** How long an interrupted Command may take to settle before the run ends without it. */
|
|
7706
|
-
const GRACE_MS = 3e3;
|
|
7707
|
-
const GRACE_OVER = Symbol("grace over");
|
|
7708
|
-
function interruptedError(rerun) {
|
|
7709
|
-
return new CliError({
|
|
7710
|
-
code: INTERRUPTED,
|
|
7711
|
-
message: "interrupted before it finished",
|
|
7712
|
-
hint: "to finish it, run the Next step, unless it was stopped on purpose; if a tool timeout stopped it, give it longer",
|
|
7713
|
-
next: [rerun]
|
|
7714
|
-
});
|
|
7715
|
-
}
|
|
7716
|
-
const BOUND_WORDS = {
|
|
7717
|
-
deadline: "deadline",
|
|
7718
|
-
ignoreStop: "timeout"
|
|
7719
|
-
};
|
|
7720
|
-
function waitHint(bound) {
|
|
7721
|
-
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";
|
|
7722
|
-
}
|
|
7723
|
-
function deadlineError(commandLine, ms, advice, bound = "deadline") {
|
|
7724
|
-
return new CliError({
|
|
7725
|
-
code: DEADLINE_EXCEEDED,
|
|
7726
|
-
message: `${commandLine} ran past its ${ms} ms ${BOUND_WORDS[bound]}`,
|
|
7727
|
-
...advice
|
|
7728
|
-
});
|
|
7729
|
-
}
|
|
7730
|
-
/** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
|
|
7731
|
-
var RunControl = class {
|
|
7732
|
-
signal;
|
|
7733
|
-
#controller = new AbortController();
|
|
7734
|
-
#commandLine;
|
|
7735
|
-
#rerun;
|
|
7736
|
-
#outer;
|
|
7737
|
-
#onOuterAbort = () => this.#controller.abort(interruptedError(this.#rerun));
|
|
7738
|
-
/** Each running must-finish step, tracked until it settles, with its name. */
|
|
7739
|
-
#steps = /* @__PURE__ */ new Map();
|
|
7740
|
-
#progress;
|
|
7741
|
-
#clock;
|
|
7742
|
-
#disposed = new AbortController();
|
|
7743
|
-
/** The reasons this run's deadlines aborted with, to tell them in a Command's error. */
|
|
7744
|
-
#abortReasons = /* @__PURE__ */ new WeakSet();
|
|
7745
|
-
#graceCancel = new AbortController();
|
|
7746
|
-
#endGrace = () => {};
|
|
7747
|
-
#graceOver = new Promise((resolve) => {
|
|
7748
|
-
this.#endGrace = resolve;
|
|
7749
|
-
});
|
|
7750
|
-
constructor({ commandLine, rerun, outer, clock, progress, runDeadline }) {
|
|
7751
|
-
this.#commandLine = commandLine;
|
|
7752
|
-
this.#rerun = rerun;
|
|
7753
|
-
this.#outer = outer;
|
|
7754
|
-
this.#clock = clock;
|
|
7755
|
-
this.#progress = progress;
|
|
7756
|
-
this.signal = this.#controller.signal;
|
|
7757
|
-
this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
|
|
7758
|
-
if (outer?.aborted === true) this.#onOuterAbort();
|
|
7759
|
-
else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
|
|
7760
|
-
if (runDeadline !== void 0) {
|
|
7761
|
-
const { ms, advice } = runDeadline;
|
|
7762
|
-
after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, advice())));
|
|
7763
|
-
}
|
|
7764
|
-
}
|
|
7765
|
-
deadline(ms, options = {}) {
|
|
7766
|
-
return options.ignoreStop === true ? this.#abortsAfter(ms, "ignoreStop") : AbortSignal.any([this.signal, this.#abortsAfter(ms, "deadline")]);
|
|
7767
|
-
}
|
|
7768
|
-
#abortsAfter(ms, bound) {
|
|
7769
|
-
const controller = new AbortController();
|
|
7770
|
-
after(this.#clock, ms, this.#disposed.signal, () => {
|
|
7771
|
-
const advice = {
|
|
7772
|
-
hint: waitHint(bound),
|
|
7773
|
-
next: [this.#rerun]
|
|
7774
|
-
};
|
|
7775
|
-
const reason = deadlineError(this.#commandLine, ms, advice, bound);
|
|
7776
|
-
this.#abortReasons.add(reason);
|
|
7777
|
-
controller.abort(reason);
|
|
7778
|
-
});
|
|
7779
|
-
return controller.signal;
|
|
7780
|
-
}
|
|
7781
|
-
async runToEnd(name, work) {
|
|
7782
|
-
this.#graceCancel.abort();
|
|
7783
|
-
if (this.signal.aborted) this.#finishing(name);
|
|
7784
|
-
const step = work();
|
|
7785
|
-
const settled = step.then(() => {}, () => {});
|
|
7786
|
-
this.#steps.set(settled, name);
|
|
7787
|
-
try {
|
|
7788
|
-
return await step;
|
|
7789
|
-
} finally {
|
|
7790
|
-
this.#steps.delete(settled);
|
|
7791
|
-
this.#startGrace();
|
|
7792
|
-
}
|
|
7793
|
-
}
|
|
7794
|
-
/** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
|
|
7795
|
-
async settle(work) {
|
|
7796
|
-
try {
|
|
7797
|
-
const value = await Promise.race([work, this.#graceOver]);
|
|
7798
|
-
if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
|
|
7799
|
-
return value;
|
|
7800
|
-
} finally {
|
|
7801
|
-
await this.#stepsDone();
|
|
7802
|
-
this.close();
|
|
7803
|
-
}
|
|
7804
|
-
}
|
|
7805
|
-
/**
|
|
7806
|
-
* The error a run ends with: an interrupt or the run deadline outranks what the Command threw,
|
|
7807
|
-
* unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
|
|
7808
|
-
* Otherwise each leaf that is not a `CliError` and whose cause chain holds a deadline's abort is
|
|
7809
|
-
* reported as that abort, so a declared error keeps its own code.
|
|
7810
|
-
*/
|
|
7811
|
-
errorAfter(error) {
|
|
7812
|
-
if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
|
|
7813
|
-
const leaves = errorsOf(error).map((leaf) => this.#abortIn(leaf) ?? leaf);
|
|
7814
|
-
return leaves.length === 1 ? leaves[0] : new AggregateError(leaves);
|
|
7815
|
-
}
|
|
7816
|
-
#abortIn(error) {
|
|
7817
|
-
if (error instanceof CliError) return void 0;
|
|
7818
|
-
return causesOf(error).find((each) => each instanceof CliError && this.#abortReasons.has(each));
|
|
7819
|
-
}
|
|
7820
|
-
#onStop() {
|
|
7821
|
-
for (const name of this.#steps.values()) this.#finishing(name);
|
|
7822
|
-
this.#startGrace();
|
|
7823
|
-
}
|
|
7824
|
-
#finishing(name) {
|
|
7825
|
-
this.#progress(`finishing ${name}`);
|
|
7826
|
-
}
|
|
7827
|
-
#startGrace() {
|
|
7828
|
-
if (this.signal.aborted && this.#steps.size === 0) {
|
|
7829
|
-
this.#graceCancel.abort();
|
|
7830
|
-
this.#graceCancel = new AbortController();
|
|
7831
|
-
after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
|
|
8210
|
+
message: `${install.package} ${copy.version}, installed in ${where}`
|
|
8211
|
+
};
|
|
7832
8212
|
}
|
|
7833
8213
|
}
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
}
|
|
8214
|
+
}
|
|
8215
|
+
function registryOf({ package: name }, root) {
|
|
8216
|
+
const npmrc = root === void 0 ? "the project's .npmrc" : join(root, ".npmrc");
|
|
8217
|
+
const scope = scopeOf(name);
|
|
8218
|
+
return {
|
|
8219
|
+
package: name,
|
|
8220
|
+
label: `the registry npm uses for ${name}`,
|
|
8221
|
+
flags: [],
|
|
8222
|
+
unreachableHint: scope === void 0 ? "check the network or VPN" : `check the network or VPN and the ${scope}:registry that ${npmrc} sets`,
|
|
8223
|
+
loginHint: `log in to it (\`npm login${scope === void 0 ? "" : ` --scope=${scope}`}\`) or set its token in ${npmrc}`,
|
|
8224
|
+
missingHint: scope === void 0 ? `check that ${name} is published on the registry ${npmrc} names` : `check that ${name} is published, and that ${npmrc} maps ${scope} to its registry: ${scope}:registry=<url>`
|
|
8225
|
+
};
|
|
8226
|
+
}
|
|
8227
|
+
function adviceFor(install, root, latest) {
|
|
8228
|
+
const packages = packagesOf(install);
|
|
8229
|
+
const lines = root === void 0 ? void 0 : LINES[root.manager];
|
|
8230
|
+
if (lines !== void 0) return { fix: lines.upgrade(packages) };
|
|
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"}` };
|
|
8232
|
+
}
|
|
8233
|
+
function checkOf$1(cli, install, runAs, launch) {
|
|
8234
|
+
let survey;
|
|
8235
|
+
const surveyed = (ctx) => {
|
|
8236
|
+
survey ??= surveyDependency(install, launch.bin, ctx);
|
|
8237
|
+
return survey;
|
|
8238
|
+
};
|
|
8239
|
+
return {
|
|
8240
|
+
install: async (ctx) => installFinding(cli, runAs, install, await surveyed(ctx)),
|
|
8241
|
+
release: async (ctx) => {
|
|
8242
|
+
const { cwd, root, standing } = await surveyed(ctx);
|
|
8243
|
+
if (isUnchecked(standing)) return { skipped: skippedFor$1(cli, install, standing) };
|
|
8244
|
+
return {
|
|
8245
|
+
npm: npmContext(cli, registryOf(install, root?.dir), ctx, launch, root?.dir ?? cwd),
|
|
8246
|
+
advise: (latest) => adviceFor(install, root, latest)
|
|
8247
|
+
};
|
|
8248
|
+
}
|
|
8249
|
+
};
|
|
8250
|
+
}
|
|
8251
|
+
/** The Install of a CLI each project pins as a dependency, run through npx (ADR 0011). */
|
|
8252
|
+
function dependencyInstall(cli, install) {
|
|
8253
|
+
const runAs = runAsOf(cli);
|
|
8254
|
+
return {
|
|
8255
|
+
runAs,
|
|
8256
|
+
builtIns: () => ({}),
|
|
8257
|
+
check: (launch) => checkOf$1(cli, install, runAs, launch),
|
|
8258
|
+
notice: async () => {}
|
|
8259
|
+
};
|
|
8260
|
+
}
|
|
7844
8261
|
//#endregion
|
|
7845
8262
|
//#region ../agent-cli/dist/built-ins/install/global-npm.js
|
|
7846
|
-
const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
|
|
7847
8263
|
/** Where npm puts global packages under its global prefix. */
|
|
7848
|
-
function globalModulesDir(prefix) {
|
|
7849
|
-
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));
|
|
7850
8273
|
}
|
|
7851
8274
|
function notGlobal(ctx, message) {
|
|
7852
8275
|
return runtimeError({
|
|
@@ -7863,11 +8286,11 @@ function notGlobal(ctx, message) {
|
|
|
7863
8286
|
async function globalInstallOf(bin, ctx) {
|
|
7864
8287
|
if (bin === void 0) throw notGlobal(ctx, `${ctx.cliName} runs from source, not from an npm install`);
|
|
7865
8288
|
const prefix = await globalPrefix(ctx);
|
|
7866
|
-
const packageDir = join(globalModulesDir(prefix), ctx.registry.package);
|
|
7867
|
-
const installed = await
|
|
7868
|
-
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;
|
|
7869
8292
|
if (installed === void 0 || !isInside(installed, here)) throw notGlobal(ctx, `${ctx.cliName} runs from ${here}, not from npm's global install at ${packageDir}`);
|
|
7870
|
-
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`);
|
|
7871
8294
|
return {
|
|
7872
8295
|
prefix,
|
|
7873
8296
|
packageDir
|
|
@@ -7896,25 +8319,32 @@ function globalRegistry(install) {
|
|
|
7896
8319
|
};
|
|
7897
8320
|
}
|
|
7898
8321
|
/** npm for a Global install, run in the working directory. */
|
|
7899
|
-
function globalNpm(cli, install, ctx,
|
|
7900
|
-
return npmContext(cli, globalRegistry(install), ctx,
|
|
8322
|
+
function globalNpm(cli, install, ctx, launch) {
|
|
8323
|
+
return npmContext(cli, globalRegistry(install), ctx, launch, ctx.cwd);
|
|
7901
8324
|
}
|
|
7902
8325
|
//#endregion
|
|
7903
8326
|
//#region ../agent-cli/dist/built-ins/skill/sync.js
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
return (await
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
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))));
|
|
7918
8348
|
return {
|
|
7919
8349
|
source,
|
|
7920
8350
|
copy,
|
|
@@ -7924,7 +8354,7 @@ async function syncOf(source, copy) {
|
|
|
7924
8354
|
};
|
|
7925
8355
|
}
|
|
7926
8356
|
async function applySync(sync, ctx) {
|
|
7927
|
-
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)));
|
|
7928
8358
|
for (const file of sync.remove) await ctx.files.remove(join(sync.copy, file));
|
|
7929
8359
|
}
|
|
7930
8360
|
//#endregion
|
|
@@ -7945,9 +8375,9 @@ function homeOf(env) {
|
|
|
7945
8375
|
return env.HOME ?? env.USERPROFILE ?? homedir();
|
|
7946
8376
|
}
|
|
7947
8377
|
/** The home folders of the Skill copy; the skills installer may have made one a link to the other. */
|
|
7948
|
-
async function homeCopiesOf(name, env) {
|
|
8378
|
+
async function homeCopiesOf(files, name, env) {
|
|
7949
8379
|
const paths = SKILL_DIRS.map((dir) => join(homeOf(env), ...dir, name));
|
|
7950
|
-
const reals = await Promise.all(paths.map(
|
|
8380
|
+
const reals = await Promise.all(paths.map((path) => files.realPath(path)));
|
|
7951
8381
|
return paths.filter((_path, index) => {
|
|
7952
8382
|
const real = reals[index];
|
|
7953
8383
|
return real === void 0 || reals.indexOf(real) === index;
|
|
@@ -7965,9 +8395,9 @@ const homeSyncSchema = object({
|
|
|
7965
8395
|
async function syncHome(cli, packageDir, ctx) {
|
|
7966
8396
|
if (cli.skill === void 0) return null;
|
|
7967
8397
|
const source = join(packageDir, cli.skill.folder);
|
|
7968
|
-
if ((await filesIn(source)).length === 0) return null;
|
|
7969
|
-
const copies = await homeCopiesOf(cli.name, ctx.env);
|
|
7970
|
-
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)));
|
|
7971
8401
|
for (const sync of syncs) await applySync(sync, ctx);
|
|
7972
8402
|
return {
|
|
7973
8403
|
copies,
|
|
@@ -7984,19 +8414,25 @@ function renderHomeSync(name, sync) {
|
|
|
7984
8414
|
const at = sync.copies.join(", ");
|
|
7985
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}`];
|
|
7986
8416
|
}
|
|
7987
|
-
async function homeCopyAt(path) {
|
|
8417
|
+
async function homeCopyAt(files, path) {
|
|
8418
|
+
let text;
|
|
7988
8419
|
try {
|
|
7989
|
-
|
|
7990
|
-
path,
|
|
7991
|
-
state: "readable",
|
|
7992
|
-
stated: skillVersionIn(await readFile(join(path, "SKILL.md"), "utf8"))
|
|
7993
|
-
};
|
|
8420
|
+
text = await readIfAny(files, join(path, "SKILL.md"));
|
|
7994
8421
|
} catch (error) {
|
|
8422
|
+
if (!(error instanceof CliError)) throw error;
|
|
7995
8423
|
return {
|
|
7996
8424
|
path,
|
|
7997
|
-
state:
|
|
8425
|
+
state: "unreadable"
|
|
7998
8426
|
};
|
|
7999
8427
|
}
|
|
8428
|
+
return text === void 0 ? {
|
|
8429
|
+
path,
|
|
8430
|
+
state: "missing"
|
|
8431
|
+
} : {
|
|
8432
|
+
path,
|
|
8433
|
+
state: "readable",
|
|
8434
|
+
stated: skillVersionIn(text)
|
|
8435
|
+
};
|
|
8000
8436
|
}
|
|
8001
8437
|
function describeCopy(copy) {
|
|
8002
8438
|
if (copy.state === "missing") return `no copy at ${copy.path}`;
|
|
@@ -8004,8 +8440,8 @@ function describeCopy(copy) {
|
|
|
8004
8440
|
return copy.stated === void 0 ? copy.path : `${copy.path} (${statedVersionNote(copy.stated)})`;
|
|
8005
8441
|
}
|
|
8006
8442
|
async function findingOf$1(cli, folder, install, ctx) {
|
|
8007
|
-
const paths = await homeCopiesOf(cli.name, ctx.env);
|
|
8008
|
-
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"))]);
|
|
8009
8445
|
const message = copies.map(describeCopy).join("; ");
|
|
8010
8446
|
const shipsStated = shipped !== void 0 && skillVersionIn(shipped) !== void 0;
|
|
8011
8447
|
return copies.every((copy) => copy.state === "readable" && (copy.stated === void 0 || shipsStated)) ? {
|
|
@@ -8083,14 +8519,9 @@ function upgradeOf({ version, lines }) {
|
|
|
8083
8519
|
* What each release after `from` up to `to` asks of the operator, oldest first, from the
|
|
8084
8520
|
* CHANGELOG.md in `packageDir`; none when the package ships no CHANGELOG.
|
|
8085
8521
|
*/
|
|
8086
|
-
async function upgradesOf(packageDir, from, to) {
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
changelog = await readFile(join(packageDir, "CHANGELOG.md"), "utf8");
|
|
8090
|
-
} catch (error) {
|
|
8091
|
-
if (isMissingFile(error)) return [];
|
|
8092
|
-
throw error;
|
|
8093
|
-
}
|
|
8522
|
+
async function upgradesOf(files, packageDir, from, to) {
|
|
8523
|
+
const changelog = await readIfAny(files, join(packageDir, "CHANGELOG.md"));
|
|
8524
|
+
if (changelog === void 0) return [];
|
|
8094
8525
|
return releasesIn(changelog).filter(({ version }) => compareVersions(version, from) > 0 && compareVersions(version, to) <= 0).sort((a, b) => compareVersions(a.version, b.version)).flatMap(upgradeOf);
|
|
8095
8526
|
}
|
|
8096
8527
|
function renderUpgrade(upgrade) {
|
|
@@ -8098,7 +8529,6 @@ function renderUpgrade(upgrade) {
|
|
|
8098
8529
|
}
|
|
8099
8530
|
//#endregion
|
|
8100
8531
|
//#region ../agent-cli/dist/built-ins/setup.js
|
|
8101
|
-
const NOT_ON_PATH = "NOT_ON_PATH";
|
|
8102
8532
|
const setupSchema = object({
|
|
8103
8533
|
version: string(),
|
|
8104
8534
|
prefix: string(),
|
|
@@ -8108,8 +8538,8 @@ const setupSchema = object({
|
|
|
8108
8538
|
docs: string().nullable()
|
|
8109
8539
|
});
|
|
8110
8540
|
const installedManifest = object({ version: string() });
|
|
8111
|
-
async function versionAt(packageDir) {
|
|
8112
|
-
const text = await
|
|
8541
|
+
async function versionAt(files, packageDir) {
|
|
8542
|
+
const text = await readIfAny(files, join(packageDir, "package.json"));
|
|
8113
8543
|
if (text === void 0) return null;
|
|
8114
8544
|
try {
|
|
8115
8545
|
const parsed = installedManifest.safeParse(JSON.parse(text));
|
|
@@ -8119,26 +8549,29 @@ async function versionAt(packageDir) {
|
|
|
8119
8549
|
throw error;
|
|
8120
8550
|
}
|
|
8121
8551
|
}
|
|
8122
|
-
function npmOfSetup(cli, install, ctx,
|
|
8552
|
+
function npmOfSetup(cli, install, ctx, launch) {
|
|
8123
8553
|
return {
|
|
8124
|
-
...globalNpm(cli, install, ctx,
|
|
8554
|
+
...globalNpm(cli, install, ctx, launch),
|
|
8125
8555
|
rerun: wholeLine(setupLineOf(install))
|
|
8126
8556
|
};
|
|
8127
8557
|
}
|
|
8128
8558
|
function needsInstall({ from, latest }) {
|
|
8129
8559
|
return from === null || standingOf$1(from, latest) === "behind";
|
|
8130
8560
|
}
|
|
8131
|
-
function binDirOf(prefix) {
|
|
8132
|
-
return
|
|
8561
|
+
function binDirOf(prefix, platform) {
|
|
8562
|
+
return platform === "win32" ? prefix : join(prefix, "bin");
|
|
8133
8563
|
}
|
|
8134
|
-
async function canonical(path) {
|
|
8135
|
-
|
|
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);
|
|
8136
8569
|
}
|
|
8137
|
-
async function assertOnPath(cli, prefix, node, ctx) {
|
|
8138
|
-
const binDir = binDirOf(prefix);
|
|
8139
|
-
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);
|
|
8140
8573
|
const entries = pathOf(ctx.env).split(delimiter).filter(Boolean);
|
|
8141
|
-
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;
|
|
8142
8575
|
throw runtimeError({
|
|
8143
8576
|
code: NOT_ON_PATH,
|
|
8144
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`,
|
|
@@ -8170,7 +8603,7 @@ function renderSetup(cli, install, setup) {
|
|
|
8170
8603
|
].join("\n");
|
|
8171
8604
|
}
|
|
8172
8605
|
/** The built-in `setup`, which runs from any copy, such as npx's, so it never asks to be the global one. */
|
|
8173
|
-
function setupCommand(cli, install,
|
|
8606
|
+
function setupCommand(cli, install, launch) {
|
|
8174
8607
|
return defineCommand$1({
|
|
8175
8608
|
description: "install this CLI and its skill on this machine",
|
|
8176
8609
|
examples: [{
|
|
@@ -8181,12 +8614,17 @@ function setupCommand(cli, install, { node }) {
|
|
|
8181
8614
|
note: "show what it would change"
|
|
8182
8615
|
}],
|
|
8183
8616
|
effect: "write",
|
|
8617
|
+
errors: [
|
|
8618
|
+
NOT_GLOBAL_INSTALL,
|
|
8619
|
+
NOT_ON_PATH,
|
|
8620
|
+
...NPM_ERRORS
|
|
8621
|
+
],
|
|
8184
8622
|
output: setupSchema,
|
|
8185
8623
|
plan: async (_input, ctx) => {
|
|
8186
|
-
const npm = npmOfSetup(cli, install, ctx,
|
|
8624
|
+
const npm = npmOfSetup(cli, install, ctx, launch);
|
|
8187
8625
|
const prefix = await globalPrefix(npm);
|
|
8188
|
-
const packageDir = join(globalModulesDir(prefix), install.package);
|
|
8189
|
-
if (
|
|
8626
|
+
const packageDir = join(globalModulesDir(prefix, launch.platform), install.package);
|
|
8627
|
+
if (await isLink(ctx.files, packageDir)) throw runtimeError({
|
|
8190
8628
|
code: NOT_GLOBAL_INSTALL,
|
|
8191
8629
|
message: `${packageDir} is a link (npm link), which an install would replace`,
|
|
8192
8630
|
hint: "remove the link with npm unlink, then run the Next step",
|
|
@@ -8194,11 +8632,11 @@ function setupCommand(cli, install, { node }) {
|
|
|
8194
8632
|
});
|
|
8195
8633
|
return {
|
|
8196
8634
|
package: install.package,
|
|
8197
|
-
from: await versionAt(packageDir),
|
|
8635
|
+
from: await versionAt(ctx.files, packageDir),
|
|
8198
8636
|
latest: await latestVersion(npm),
|
|
8199
8637
|
prefix,
|
|
8200
8638
|
packageDir,
|
|
8201
|
-
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)
|
|
8202
8640
|
};
|
|
8203
8641
|
},
|
|
8204
8642
|
apply: async (plan, ctx) => {
|
|
@@ -8207,7 +8645,7 @@ function setupCommand(cli, install, { node }) {
|
|
|
8207
8645
|
const skill = await ctx.runToEnd(`installing ${plan.package}@${latest}`, async () => {
|
|
8208
8646
|
if (installs) {
|
|
8209
8647
|
ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
|
|
8210
|
-
await installGlobally(latest, npmOfSetup(cli, install, ctx,
|
|
8648
|
+
await installGlobally(latest, npmOfSetup(cli, install, ctx, launch));
|
|
8211
8649
|
}
|
|
8212
8650
|
return syncHome(cli, packageDir, ctx);
|
|
8213
8651
|
}).catch((error) => {
|
|
@@ -8218,7 +8656,7 @@ function setupCommand(cli, install, { node }) {
|
|
|
8218
8656
|
prefix,
|
|
8219
8657
|
installed: installs,
|
|
8220
8658
|
skill,
|
|
8221
|
-
upgrades: installs && from !== null ? await upgradesOf(packageDir, from, latest) : [],
|
|
8659
|
+
upgrades: installs && from !== null ? await upgradesOf(ctx.files, packageDir, from, latest) : [],
|
|
8222
8660
|
docs: cli.docs === void 0 ? null : join(packageDir, cli.docs)
|
|
8223
8661
|
};
|
|
8224
8662
|
ctx.deliver(setup);
|
|
@@ -8227,7 +8665,7 @@ function setupCommand(cli, install, { node }) {
|
|
|
8227
8665
|
message: `interrupted, but ${plan.package}@${setup.version} and its skill are already in place`,
|
|
8228
8666
|
hint: "nothing to run again"
|
|
8229
8667
|
});
|
|
8230
|
-
await assertOnPath(cli, prefix,
|
|
8668
|
+
await assertOnPath(cli, prefix, launch, ctx);
|
|
8231
8669
|
return setup;
|
|
8232
8670
|
},
|
|
8233
8671
|
render: (setup) => renderSetup(cli, install, setup),
|
|
@@ -8266,7 +8704,7 @@ function interruptedAfterInstall(plan) {
|
|
|
8266
8704
|
});
|
|
8267
8705
|
}
|
|
8268
8706
|
/** The built-in `update`, for a CLI installed globally with npm. */
|
|
8269
|
-
function updateCommand(cli, install,
|
|
8707
|
+
function updateCommand(cli, install, launch) {
|
|
8270
8708
|
return defineCommand$1({
|
|
8271
8709
|
description: "install the latest version of this CLI from its registry",
|
|
8272
8710
|
examples: [{
|
|
@@ -8277,17 +8715,18 @@ function updateCommand(cli, install, { bin, node }) {
|
|
|
8277
8715
|
note: "show the version it would install"
|
|
8278
8716
|
}],
|
|
8279
8717
|
effect: "write",
|
|
8718
|
+
errors: [NOT_GLOBAL_INSTALL, ...NPM_ERRORS],
|
|
8280
8719
|
output: updateSchema,
|
|
8281
8720
|
plan: async (_input, ctx) => {
|
|
8282
|
-
const npm = globalNpm(cli, install, ctx,
|
|
8283
|
-
const copy = await globalInstallOf(bin, npm);
|
|
8721
|
+
const npm = globalNpm(cli, install, ctx, launch);
|
|
8722
|
+
const copy = await globalInstallOf(launch.bin, npm);
|
|
8284
8723
|
return {
|
|
8285
8724
|
package: install.package,
|
|
8286
8725
|
from: cli.version,
|
|
8287
8726
|
latest: await latestVersion(npm),
|
|
8288
8727
|
prefix: copy.prefix,
|
|
8289
8728
|
packageDir: copy.packageDir,
|
|
8290
|
-
copies: cli.skill === void 0 ? [] : await homeCopiesOf(cli.name, ctx.env)
|
|
8729
|
+
copies: cli.skill === void 0 ? [] : await homeCopiesOf(ctx.files, cli.name, ctx.env)
|
|
8291
8730
|
};
|
|
8292
8731
|
},
|
|
8293
8732
|
apply: async (plan, ctx) => {
|
|
@@ -8298,205 +8737,32 @@ function updateCommand(cli, install, { bin, node }) {
|
|
|
8298
8737
|
installed: null,
|
|
8299
8738
|
skill: await syncHome(cli, plan.packageDir, ctx),
|
|
8300
8739
|
upgrades: []
|
|
8301
|
-
};
|
|
8302
|
-
ctx.signal.throwIfAborted();
|
|
8303
|
-
ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
|
|
8304
|
-
const skill = await ctx.runToEnd(`installing ${plan.package}@${latest}`, async () => {
|
|
8305
|
-
await installGlobally(latest, globalNpm(cli, install, ctx,
|
|
8306
|
-
return syncHome(cli, plan.packageDir, ctx);
|
|
8307
|
-
});
|
|
8308
|
-
const update = {
|
|
8309
|
-
from,
|
|
8310
|
-
latest,
|
|
8311
|
-
installed: {
|
|
8312
|
-
version: latest,
|
|
8313
|
-
prefix
|
|
8314
|
-
},
|
|
8315
|
-
skill,
|
|
8316
|
-
upgrades: await upgradesOf(plan.packageDir, from, latest)
|
|
8317
|
-
};
|
|
8318
|
-
if (ctx.signal.aborted) {
|
|
8319
|
-
ctx.deliver(update);
|
|
8320
|
-
throw interruptedAfterInstall(plan);
|
|
8321
|
-
}
|
|
8322
|
-
return update;
|
|
8323
|
-
},
|
|
8324
|
-
render: (update) => renderUpdate(cli, install, update),
|
|
8325
|
-
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")
|
|
8326
|
-
});
|
|
8327
|
-
}
|
|
8328
|
-
//#endregion
|
|
8329
|
-
//#region ../agent-cli/dist/context/files.js
|
|
8330
|
-
const TEMP_FILE = /^\..+\.agent-cli-(\d+)\.tmp$/;
|
|
8331
|
-
/** Where the Runtime writes `path` before moving it into place; a dead writer's is swept. */
|
|
8332
|
-
function tempPathOf(path) {
|
|
8333
|
-
return join(dirname(path), `.${basename(path)}.agent-cli-${process.pid}.tmp`);
|
|
8334
|
-
}
|
|
8335
|
-
function isAlive(pid) {
|
|
8336
|
-
try {
|
|
8337
|
-
process.kill(pid, 0);
|
|
8338
|
-
return true;
|
|
8339
|
-
} catch (error) {
|
|
8340
|
-
return errnoOf(error) === "EPERM";
|
|
8341
|
-
}
|
|
8342
|
-
}
|
|
8343
|
-
async function sweep(folder) {
|
|
8344
|
-
const dead = (await readdir(folder)).filter((name) => {
|
|
8345
|
-
const pid = TEMP_FILE.exec(name)?.[1];
|
|
8346
|
-
return pid !== void 0 && !isAlive(Number(pid));
|
|
8347
|
-
});
|
|
8348
|
-
await Promise.all(dead.map((name) => rm(join(folder, name), { force: true })));
|
|
8349
|
-
}
|
|
8350
|
-
/** The file a symlink at `path` points to, so a replacing write keeps the link. */
|
|
8351
|
-
async function targetOf$1(path) {
|
|
8352
|
-
try {
|
|
8353
|
-
return await realpath(path);
|
|
8354
|
-
} catch (error) {
|
|
8355
|
-
if (isMissingFile(error)) return path;
|
|
8356
|
-
throw error;
|
|
8357
|
-
}
|
|
8358
|
-
}
|
|
8359
|
-
async function writeWithMode(path, data, mode) {
|
|
8360
|
-
if (mode === void 0) {
|
|
8361
|
-
await writeFile(path, data);
|
|
8362
|
-
return;
|
|
8363
|
-
}
|
|
8364
|
-
await writeFile(path, data, { mode });
|
|
8365
|
-
await chmod(path, mode);
|
|
8366
|
-
}
|
|
8367
|
-
/** What `link` answers on a file system without hard links, such as exFAT. */
|
|
8368
|
-
const NO_HARD_LINKS = /* @__PURE__ */ new Set([
|
|
8369
|
-
"EPERM",
|
|
8370
|
-
"ENOTSUP",
|
|
8371
|
-
"EXDEV"
|
|
8372
|
-
]);
|
|
8373
|
-
/** A link fails atomically when `path` is taken, so no reader ever sees half a file there. */
|
|
8374
|
-
async function linkNew(temp, path, data, mode) {
|
|
8375
|
-
try {
|
|
8376
|
-
await link(temp, path);
|
|
8377
|
-
} catch (error) {
|
|
8378
|
-
if (!NO_HARD_LINKS.has(errnoOf(error))) throw error;
|
|
8379
|
-
await createNew(path, data, mode);
|
|
8380
|
-
}
|
|
8381
|
-
}
|
|
8382
|
-
/** A file this call created and could not finish is removed, so a rerun does not find half of it. */
|
|
8383
|
-
async function createNew(path, data, mode) {
|
|
8384
|
-
const handle = await open(path, "wx", mode);
|
|
8385
|
-
try {
|
|
8386
|
-
await handle.writeFile(data);
|
|
8387
|
-
if (mode !== void 0) await handle.chmod(mode);
|
|
8388
|
-
} catch (error) {
|
|
8389
|
-
await handle.close();
|
|
8390
|
-
await rm(path, { force: true });
|
|
8391
|
-
throw error;
|
|
8392
|
-
}
|
|
8393
|
-
await handle.close();
|
|
8394
|
-
}
|
|
8395
|
-
const RENAME_ATTEMPTS = 10;
|
|
8396
|
-
const RENAME_BACKOFF_MS = 10;
|
|
8397
|
-
const HELD_BY_ANOTHER_PROGRAM = /* @__PURE__ */ new Set(["EPERM", "EBUSY"]);
|
|
8398
|
-
async function renameRetrying(from, to) {
|
|
8399
|
-
for (let attempt = 1;; attempt++) try {
|
|
8400
|
-
await rename(from, to);
|
|
8401
|
-
return;
|
|
8402
|
-
} catch (error) {
|
|
8403
|
-
if (!(process.platform === "win32" && HELD_BY_ANOTHER_PROGRAM.has(errnoOf(error))) || attempt === RENAME_ATTEMPTS) throw error;
|
|
8404
|
-
await setTimeout$1(attempt * RENAME_BACKOFF_MS);
|
|
8405
|
-
}
|
|
8406
|
-
}
|
|
8407
|
-
/** Real files for one run: the first write to each folder sweeps the temp files of dead writers. */
|
|
8408
|
-
function realFiles() {
|
|
8409
|
-
const swept = /* @__PURE__ */ new Set();
|
|
8410
|
-
return {
|
|
8411
|
-
exists: async (path) => {
|
|
8412
|
-
try {
|
|
8413
|
-
await access(path);
|
|
8414
|
-
return true;
|
|
8415
|
-
} catch (error) {
|
|
8416
|
-
if (isNothingThere(error)) return false;
|
|
8417
|
-
throw error;
|
|
8418
|
-
}
|
|
8419
|
-
},
|
|
8420
|
-
write: async (path, data, options = {}) => {
|
|
8421
|
-
await mkdir(dirname(path), { recursive: true });
|
|
8422
|
-
const target = options.exclusive === true ? path : await targetOf$1(path);
|
|
8423
|
-
const folder = dirname(target);
|
|
8424
|
-
if (!swept.has(folder)) {
|
|
8425
|
-
await sweep(folder);
|
|
8426
|
-
swept.add(folder);
|
|
8427
|
-
}
|
|
8428
|
-
const temp = tempPathOf(target);
|
|
8429
|
-
try {
|
|
8430
|
-
await writeWithMode(temp, data, options.mode);
|
|
8431
|
-
if (options.exclusive === true) {
|
|
8432
|
-
await linkNew(temp, target, data, options.mode);
|
|
8433
|
-
await rm(temp);
|
|
8434
|
-
} else await renameRetrying(temp, target);
|
|
8435
|
-
} catch (error) {
|
|
8436
|
-
await rm(temp, { force: true });
|
|
8437
|
-
throw error;
|
|
8438
|
-
}
|
|
8439
|
-
},
|
|
8440
|
-
read: (path) => readFile(path),
|
|
8441
|
-
list: (folder) => readdir(folder),
|
|
8442
|
-
remove: (path) => rm(path, {
|
|
8443
|
-
recursive: true,
|
|
8444
|
-
force: true
|
|
8445
|
-
}),
|
|
8446
|
-
mkdir: async (path, options) => {
|
|
8447
|
-
const mode = options?.mode;
|
|
8448
|
-
if (options?.exclusive !== true) {
|
|
8449
|
-
await mkdir(path, {
|
|
8450
|
-
recursive: true,
|
|
8451
|
-
mode
|
|
8452
|
-
});
|
|
8453
|
-
return;
|
|
8454
|
-
}
|
|
8455
|
-
await mkdir(dirname(path), {
|
|
8456
|
-
recursive: true,
|
|
8457
|
-
mode
|
|
8458
|
-
});
|
|
8459
|
-
await mkdir(path, { mode });
|
|
8460
|
-
},
|
|
8461
|
-
stat: async (path) => {
|
|
8462
|
-
try {
|
|
8463
|
-
const { size, dev, ino } = await stat(path, { bigint: true });
|
|
8464
|
-
return {
|
|
8465
|
-
size: Number(size),
|
|
8466
|
-
id: `${dev}:${ino}`
|
|
8467
|
-
};
|
|
8468
|
-
} catch (error) {
|
|
8469
|
-
if (isNothingThere(error)) return void 0;
|
|
8470
|
-
throw error;
|
|
8471
|
-
}
|
|
8472
|
-
}
|
|
8473
|
-
};
|
|
8474
|
-
}
|
|
8475
|
-
/**
|
|
8476
|
-
* `files` resolving paths against `cwd`, its failures classified by errno; `list` drops the
|
|
8477
|
-
* Runtime's temp files and sorts.
|
|
8478
|
-
*/
|
|
8479
|
-
function classifiedFiles(files, { cwd, fileError }) {
|
|
8480
|
-
async function at(access, path, work) {
|
|
8481
|
-
const target = resolve(cwd, path);
|
|
8482
|
-
try {
|
|
8483
|
-
return await work(target);
|
|
8484
|
-
} catch (error) {
|
|
8485
|
-
throw fileError(error, {
|
|
8486
|
-
path: target,
|
|
8487
|
-
access
|
|
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);
|
|
8488
8746
|
});
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
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
|
+
});
|
|
8500
8766
|
}
|
|
8501
8767
|
//#endregion
|
|
8502
8768
|
//#region ../agent-cli/dist/built-ins/install/release-check.js
|
|
@@ -8561,25 +8827,19 @@ function parsedCheck(text) {
|
|
|
8561
8827
|
return;
|
|
8562
8828
|
}
|
|
8563
8829
|
}
|
|
8564
|
-
async function lastCheck(file) {
|
|
8565
|
-
const text = await
|
|
8830
|
+
async function lastCheck(files, file) {
|
|
8831
|
+
const text = await readIfAny(files, file);
|
|
8566
8832
|
return text === void 0 ? void 0 : parsedCheck(text);
|
|
8567
8833
|
}
|
|
8568
|
-
async function
|
|
8569
|
-
await mkdir(dirname(file), { recursive: true });
|
|
8570
|
-
const temp = tempPathOf(file);
|
|
8571
|
-
await writeFile(temp, JSON.stringify(check));
|
|
8572
|
-
await rename(temp, file);
|
|
8573
|
-
}
|
|
8574
|
-
async function startCheck(cli, install, file, last, { node }, ctx) {
|
|
8834
|
+
async function startCheck(cli, install, file, last, launch, ctx) {
|
|
8575
8835
|
const checkedAt = ctx.clock.now();
|
|
8576
8836
|
const previous = last?.latest ?? "";
|
|
8577
|
-
await
|
|
8837
|
+
await ctx.files.write(file, JSON.stringify({
|
|
8578
8838
|
checkedAt,
|
|
8579
8839
|
latest: last?.latest
|
|
8580
|
-
});
|
|
8581
|
-
const npm = globalNpm(cli, install, ctx,
|
|
8582
|
-
const { path, env } = npmOf(npm
|
|
8840
|
+
}));
|
|
8841
|
+
const npm = globalNpm(cli, install, ctx, launch);
|
|
8842
|
+
const { path, env } = await npmOf(npm);
|
|
8583
8843
|
await ctx.spawnDetached(process.execPath, [
|
|
8584
8844
|
"-e",
|
|
8585
8845
|
CHECK_SCRIPT,
|
|
@@ -8612,7 +8872,7 @@ async function releaseCheck(cli, install, runAs, launch, ctx) {
|
|
|
8612
8872
|
}
|
|
8613
8873
|
async function checkAndTell(cli, install, runAs, launch, ctx) {
|
|
8614
8874
|
const file = cacheFileOf(cli.name, ctx.env);
|
|
8615
|
-
const last = await lastCheck(file);
|
|
8875
|
+
const last = await lastCheck(ctx.files, file);
|
|
8616
8876
|
if (last?.error !== void 0) debugLine(cli, ctx, last.error);
|
|
8617
8877
|
if (last === void 0 || ctx.clock.now() - last.checkedAt >= CHECK_INTERVAL_MS) await startCheck(cli, install, file, last, launch, ctx);
|
|
8618
8878
|
const latest = last?.latest;
|
|
@@ -8621,14 +8881,14 @@ async function checkAndTell(cli, install, runAs, launch, ctx) {
|
|
|
8621
8881
|
}
|
|
8622
8882
|
//#endregion
|
|
8623
8883
|
//#region ../agent-cli/dist/built-ins/install/global.js
|
|
8624
|
-
function checkOf(cli, install,
|
|
8884
|
+
function checkOf(cli, install, launch) {
|
|
8625
8885
|
return {
|
|
8626
8886
|
install: async (ctx) => {
|
|
8627
|
-
const npm = globalNpm(cli, install, ctx,
|
|
8887
|
+
const npm = globalNpm(cli, install, ctx, launch);
|
|
8628
8888
|
try {
|
|
8629
8889
|
return {
|
|
8630
8890
|
state: "ok",
|
|
8631
|
-
message: `a global npm install under ${(await globalInstallOf(bin, npm)).prefix}`
|
|
8891
|
+
message: `a global npm install under ${(await globalInstallOf(launch.bin, npm)).prefix}`
|
|
8632
8892
|
};
|
|
8633
8893
|
} catch (error) {
|
|
8634
8894
|
if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
|
|
@@ -8642,7 +8902,7 @@ function checkOf(cli, install, { bin, node }) {
|
|
|
8642
8902
|
release: async (ctx) => {
|
|
8643
8903
|
const fix = `${cli.name} update`;
|
|
8644
8904
|
return {
|
|
8645
|
-
npm: globalNpm(cli, install, ctx,
|
|
8905
|
+
npm: globalNpm(cli, install, ctx, launch),
|
|
8646
8906
|
advise: () => ({ fix })
|
|
8647
8907
|
};
|
|
8648
8908
|
}
|
|
@@ -8656,7 +8916,6 @@ function globalInstall(cli, install) {
|
|
|
8656
8916
|
setup: setupCommand(cli, install, launch),
|
|
8657
8917
|
update: updateCommand(cli, install, launch)
|
|
8658
8918
|
}),
|
|
8659
|
-
exitClasses: ["source"],
|
|
8660
8919
|
check: (launch) => checkOf(cli, install, launch),
|
|
8661
8920
|
notice: (launch, ctx) => releaseCheck(cli, install, adapter.runAs, launch, ctx)
|
|
8662
8921
|
};
|
|
@@ -8673,7 +8932,6 @@ function noInstall(cli) {
|
|
|
8673
8932
|
return {
|
|
8674
8933
|
runAs: runAsOf(cli),
|
|
8675
8934
|
builtIns: () => ({}),
|
|
8676
|
-
exitClasses: [],
|
|
8677
8935
|
check: () => ({
|
|
8678
8936
|
install: skipped,
|
|
8679
8937
|
release: async () => ({ skipped: await skipped() })
|
|
@@ -8704,9 +8962,9 @@ function withSection({ name, text: section }, text) {
|
|
|
8704
8962
|
if (text === "") return `${whole}\n`;
|
|
8705
8963
|
return `${text}${text.endsWith("\n") ? "" : "\n"}\n${whole}\n`;
|
|
8706
8964
|
}
|
|
8707
|
-
async function agentsMdAt(agents, root) {
|
|
8965
|
+
async function agentsMdAt(files, agents, root) {
|
|
8708
8966
|
const path = join(root, "AGENTS.md");
|
|
8709
|
-
const existing = await
|
|
8967
|
+
const existing = await readIfAny(files, path);
|
|
8710
8968
|
if (existing === void 0) return {
|
|
8711
8969
|
path,
|
|
8712
8970
|
agents,
|
|
@@ -8723,9 +8981,9 @@ async function agentsMdAt(agents, root) {
|
|
|
8723
8981
|
change: existing.includes(markersOf(agents.name).begin) ? "rewrite" : "add"
|
|
8724
8982
|
};
|
|
8725
8983
|
}
|
|
8726
|
-
async function handEditsAt(root) {
|
|
8984
|
+
async function handEditsAt(files, root) {
|
|
8727
8985
|
const claudeMd = join(root, "CLAUDE.md");
|
|
8728
|
-
const text = await
|
|
8986
|
+
const text = await readIfAny(files, claudeMd);
|
|
8729
8987
|
if (text === void 0 || text.split(/\r?\n/).some((line) => line.trim() === "@AGENTS.md")) return [];
|
|
8730
8988
|
return [`add a line \`@AGENTS.md\` to ${claudeMd}, so Claude Code reads AGENTS.md`];
|
|
8731
8989
|
}
|
|
@@ -8735,8 +8993,8 @@ function agentsRow(agents, install) {
|
|
|
8735
8993
|
name: "agents",
|
|
8736
8994
|
advisory: true,
|
|
8737
8995
|
run: async (ctx) => {
|
|
8738
|
-
const root = await projectRootOf(ctx.cwd);
|
|
8739
|
-
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);
|
|
8740
8998
|
const fix = `${install.runAs} skill install`;
|
|
8741
8999
|
if (change === "rewrite") return {
|
|
8742
9000
|
state: "problem",
|
|
@@ -8749,7 +9007,7 @@ function agentsRow(agents, install) {
|
|
|
8749
9007
|
fix
|
|
8750
9008
|
};
|
|
8751
9009
|
const holds = `${path} holds the ${name} Agents section`;
|
|
8752
|
-
const [edit] = await handEditsAt(root);
|
|
9010
|
+
const [edit] = await handEditsAt(ctx.files, root);
|
|
8753
9011
|
return edit === void 0 ? {
|
|
8754
9012
|
state: "ok",
|
|
8755
9013
|
message: holds
|
|
@@ -8763,8 +9021,8 @@ function agentsRow(agents, install) {
|
|
|
8763
9021
|
//#endregion
|
|
8764
9022
|
//#region ../agent-cli/dist/built-ins/skill/install-command.js
|
|
8765
9023
|
/** The project root and the Skill copy under it, as `skill install` writes it. */
|
|
8766
|
-
async function skillCopyOf(cli, cwd) {
|
|
8767
|
-
const root = await projectRootOf(cwd);
|
|
9024
|
+
async function skillCopyOf(files, cli, cwd) {
|
|
9025
|
+
const root = await projectRootOf(files, cwd);
|
|
8768
9026
|
return {
|
|
8769
9027
|
root,
|
|
8770
9028
|
copy: join(root, ".claude", "skills", cli.name)
|
|
@@ -8780,12 +9038,14 @@ const installSchema = object({
|
|
|
8780
9038
|
}).nullable(),
|
|
8781
9039
|
handEdits: array(string())
|
|
8782
9040
|
});
|
|
8783
|
-
async function planOf(cli, folder, agents, cwd, packageRoot) {
|
|
8784
|
-
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);
|
|
8785
9045
|
return {
|
|
8786
|
-
...await syncOf(
|
|
8787
|
-
agentsMd: agents === void 0 ? null : await agentsMdAt(agents, root),
|
|
8788
|
-
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)
|
|
8789
9049
|
};
|
|
8790
9050
|
}
|
|
8791
9051
|
const SECTION_VERBS = {
|
|
@@ -8828,12 +9088,12 @@ function skillInstallCommand(cli, folder, agents) {
|
|
|
8828
9088
|
}],
|
|
8829
9089
|
effect: "write",
|
|
8830
9090
|
output: installSchema,
|
|
8831
|
-
plan: (_input, ctx) => planOf(cli, folder, agents, ctx
|
|
9091
|
+
plan: (_input, ctx) => planOf(cli, folder, agents, ctx),
|
|
8832
9092
|
apply: async (plan, ctx) => {
|
|
8833
9093
|
await applySync(plan, ctx);
|
|
8834
9094
|
const { agentsMd } = plan;
|
|
8835
9095
|
if (agentsMd !== null && agentsMd.change !== null) {
|
|
8836
|
-
const existing = await
|
|
9096
|
+
const existing = await readIfAny(ctx.files, agentsMd.path) ?? "";
|
|
8837
9097
|
await ctx.files.write(agentsMd.path, withSection(agentsMd.agents, existing));
|
|
8838
9098
|
}
|
|
8839
9099
|
return {
|
|
@@ -8855,13 +9115,13 @@ function skillInstallCommand(cli, folder, agents) {
|
|
|
8855
9115
|
//#region ../agent-cli/dist/built-ins/skill/project.js
|
|
8856
9116
|
async function personalCopyOf(name, copy, ctx) {
|
|
8857
9117
|
const home = join(homeOf(ctx.env), ".claude", "skills", name);
|
|
8858
|
-
const real = await
|
|
8859
|
-
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;
|
|
8860
9120
|
}
|
|
8861
9121
|
/** The project root's copy against the package's, and any under home. */
|
|
8862
9122
|
async function findingOf(cli, folder, install, ctx) {
|
|
8863
|
-
const { copy } = await skillCopyOf(cli, ctx.cwd);
|
|
8864
|
-
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"))]);
|
|
8865
9125
|
const stated = installed === void 0 ? void 0 : skillVersionIn(installed);
|
|
8866
9126
|
const left = stated !== void 0 && skillVersionIn(shipped) === void 0;
|
|
8867
9127
|
const found = left ? `${copy} (${statedVersionNote(stated)})` : copy;
|
|
@@ -9039,7 +9299,7 @@ function registryRow(cli, check) {
|
|
|
9039
9299
|
function doctorChecks(cli, launch, scope) {
|
|
9040
9300
|
const install = installOf(cli);
|
|
9041
9301
|
const check = install.check(launch);
|
|
9042
|
-
const tools = toolRows(cli.tools ?? [], cli.commands);
|
|
9302
|
+
const tools = toolRows(cli.tools ?? [], cli.commands, launch.platform);
|
|
9043
9303
|
return {
|
|
9044
9304
|
checks: [
|
|
9045
9305
|
buildRow(cli),
|
|
@@ -9055,6 +9315,8 @@ function doctorChecks(cli, launch, scope) {
|
|
|
9055
9315
|
tools: new Set(tools.map(({ name }) => name))
|
|
9056
9316
|
};
|
|
9057
9317
|
}
|
|
9318
|
+
//#endregion
|
|
9319
|
+
//#region ../agent-cli/dist/built-ins/doctor.js
|
|
9058
9320
|
const DEFAULT_DEADLINE_MS = 1e4;
|
|
9059
9321
|
const MS_PER_SECOND = 1e3;
|
|
9060
9322
|
const MIN_LABEL_WIDTH = 8;
|
|
@@ -9125,12 +9387,12 @@ async function rowOf(check, ctx, { read, runAs, tools }) {
|
|
|
9125
9387
|
check: check.name,
|
|
9126
9388
|
...finding
|
|
9127
9389
|
},
|
|
9128
|
-
|
|
9390
|
+
check
|
|
9129
9391
|
};
|
|
9130
9392
|
}
|
|
9131
9393
|
function nextOf(checked, doctorLine) {
|
|
9132
9394
|
const fixes = checked.flatMap(({ row }) => row.fix === void 0 ? [] : [row.fix]);
|
|
9133
|
-
const unfixed = checked.some(({ row,
|
|
9395
|
+
const unfixed = checked.some(({ row, check }) => check.advisory === true && row.state === "problem" && row.fix === void 0);
|
|
9134
9396
|
return [...new Set(unfixed ? [...fixes, doctorLine] : fixes)];
|
|
9135
9397
|
}
|
|
9136
9398
|
function render({ rows, next }, style) {
|
|
@@ -9159,9 +9421,14 @@ function failure(cliName, rerun, problems) {
|
|
|
9159
9421
|
});
|
|
9160
9422
|
return new AggregateError(errors);
|
|
9161
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
|
+
}
|
|
9162
9429
|
/** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
|
|
9163
9430
|
function doctorCommand(cli, launch) {
|
|
9164
|
-
|
|
9431
|
+
const doctor = defineCommand$1({
|
|
9165
9432
|
description: "check the setup and say what fixes each problem",
|
|
9166
9433
|
examples: [{
|
|
9167
9434
|
command: `${cli.name} doctor`,
|
|
@@ -9190,15 +9457,20 @@ function doctorCommand(cli, launch) {
|
|
|
9190
9457
|
ctx.deliver(report);
|
|
9191
9458
|
ctx.signal.throwIfAborted();
|
|
9192
9459
|
}
|
|
9193
|
-
const problems = checked.filter(({ row,
|
|
9460
|
+
const problems = checked.filter(({ row, check }) => row.state === "problem" && check.advisory !== true);
|
|
9194
9461
|
if (problems.length > 0) {
|
|
9195
9462
|
ctx.deliver(report);
|
|
9196
|
-
throw failure(cli.name, ctx.rerun(), problems);
|
|
9463
|
+
throw checkedRows(failure(cli.name, ctx.rerun(), problems.map(({ row }) => row)), problems, errorTableOf(cli));
|
|
9197
9464
|
}
|
|
9198
9465
|
return report;
|
|
9199
9466
|
},
|
|
9200
9467
|
render
|
|
9201
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
|
+
};
|
|
9202
9474
|
}
|
|
9203
9475
|
//#endregion
|
|
9204
9476
|
//#region ../agent-cli/dist/built-ins/commands.js
|
|
@@ -9298,19 +9570,6 @@ function specOf(commandLine, node, place) {
|
|
|
9298
9570
|
return isCommand(node) ? commandSpec(commandLine, node, place) : group(commandLine, specsOf(commandLine, node, place));
|
|
9299
9571
|
}
|
|
9300
9572
|
//#endregion
|
|
9301
|
-
//#region ../agent-cli/dist/error-table.js
|
|
9302
|
-
/** The Error table a CLI declares, empty when it declares none. */
|
|
9303
|
-
function errorTableOf({ errors }) {
|
|
9304
|
-
return errors ?? {};
|
|
9305
|
-
}
|
|
9306
|
-
/** The `CliError` for a code of `Table`, as `cliError` and `ctx.fail` build it. */
|
|
9307
|
-
function cliErrorOf(code, init, options) {
|
|
9308
|
-
return new CliError({
|
|
9309
|
-
...init,
|
|
9310
|
-
code
|
|
9311
|
-
}, options);
|
|
9312
|
-
}
|
|
9313
|
-
//#endregion
|
|
9314
9573
|
//#region ../agent-cli/dist/exit-classes.js
|
|
9315
9574
|
const SIGNAL_EXIT_CODES = {
|
|
9316
9575
|
SIGINT: 130,
|
|
@@ -9323,95 +9582,38 @@ const EXIT_CODES = {
|
|
|
9323
9582
|
wait: 75,
|
|
9324
9583
|
bug: 70
|
|
9325
9584
|
};
|
|
9326
|
-
const RUNTIME_EXIT_CLASSES = {
|
|
9327
|
-
INVALID_INPUT: "caller",
|
|
9328
|
-
UNKNOWN_OPTION: "caller",
|
|
9329
|
-
UNKNOWN_COMMAND: "caller",
|
|
9330
|
-
UNKNOWN_FIELD: "caller",
|
|
9331
|
-
CONFIRMATION_REQUIRED: "caller",
|
|
9332
|
-
CONFIRMATION_MISMATCH: "caller",
|
|
9333
|
-
NOT_CONFIRMED: "operator",
|
|
9334
|
-
NEEDS_OPERATOR: "operator",
|
|
9335
|
-
OUTPUT_EXISTS: "caller",
|
|
9336
|
-
STDIN_STALLED: "caller",
|
|
9337
|
-
INTERRUPTED: "interrupted",
|
|
9338
|
-
DEADLINE_EXCEEDED: "wait",
|
|
9339
|
-
INTERNAL: "bug",
|
|
9340
|
-
PERMISSION_DENIED: "operator",
|
|
9341
|
-
DISK_FULL: "operator",
|
|
9342
|
-
NOT_FOUND: "caller",
|
|
9343
|
-
ALREADY_EXISTS: "caller",
|
|
9344
|
-
NOT_A_FILE: "caller",
|
|
9345
|
-
TOOL_FAILED: "operator",
|
|
9346
|
-
TOOL_MISSING: "operator",
|
|
9347
|
-
SECRET_MISSING: "operator",
|
|
9348
|
-
PROJECT_INVALID: "operator",
|
|
9349
|
-
DOCTOR_PROBLEMS: "operator",
|
|
9350
|
-
NOT_GLOBAL_INSTALL: "operator",
|
|
9351
|
-
NOT_ON_PATH: "operator",
|
|
9352
|
-
REGISTRY_UNREACHABLE: "operator",
|
|
9353
|
-
REGISTRY_AUTH: "operator",
|
|
9354
|
-
INSTALL_FAILED: "operator",
|
|
9355
|
-
NPM_FAILED: "operator",
|
|
9356
|
-
NO_LATEST: "source"
|
|
9357
|
-
};
|
|
9358
|
-
function isRuntimeCode(code) {
|
|
9359
|
-
return Object.hasOwn(RUNTIME_EXIT_CLASSES, code);
|
|
9360
|
-
}
|
|
9361
9585
|
/** The codes of `errors` that the Runtime already declares, which a CLI cannot redeclare. */
|
|
9362
9586
|
function runtimeCodesIn(errors) {
|
|
9363
9587
|
return Object.keys(errors).filter(isRuntimeCode);
|
|
9364
9588
|
}
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
if (isRuntimeCode(error.code)) return RUNTIME_EXIT_CLASSES[error.code];
|
|
9369
|
-
return errors[error.code]?.class ?? "bug";
|
|
9370
|
-
}
|
|
9371
|
-
function isBug(error, errors) {
|
|
9372
|
-
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;
|
|
9373
9592
|
}
|
|
9374
|
-
function
|
|
9375
|
-
return
|
|
9593
|
+
function declaredCodesOf(node) {
|
|
9594
|
+
return node.kind === "command" ? node.command.errors ?? [] : Object.values(node.commands).flatMap(declaredCodesOf);
|
|
9376
9595
|
}
|
|
9377
|
-
|
|
9378
|
-
"
|
|
9379
|
-
"operator",
|
|
9380
|
-
"source",
|
|
9381
|
-
"wait",
|
|
9382
|
-
"interrupted"
|
|
9383
|
-
];
|
|
9384
|
-
function exitClassOf(error, errors) {
|
|
9385
|
-
const classes = errorsOf(error).map((each) => exitClassOfOne(each, errors));
|
|
9386
|
-
if (classes.includes("bug")) return "bug";
|
|
9387
|
-
return ACT_FIRST.find((exitClass) => classes.includes(exitClass)) ?? "bug";
|
|
9596
|
+
function isDeclarable(exitClass) {
|
|
9597
|
+
return exitClass !== void 0 && exitClass !== "bug" && exitClass !== "interrupted";
|
|
9388
9598
|
}
|
|
9389
9599
|
/**
|
|
9390
|
-
* The classes other than bug and interrupt that a run of `cli` can end in: those of the Runtime
|
|
9391
|
-
* 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.
|
|
9392
9603
|
*/
|
|
9393
|
-
function reachableExitClasses(cli) {
|
|
9394
|
-
const
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
...
|
|
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)
|
|
9399
9610
|
];
|
|
9400
|
-
|
|
9401
|
-
return /* @__PURE__ */ new Set([...runtime, ...own]);
|
|
9611
|
+
return new Set(classes.filter(isDeclarable));
|
|
9402
9612
|
}
|
|
9403
9613
|
/** The signal named by an interrupt's abort reason; any other reason counts as Ctrl-C. */
|
|
9404
9614
|
function stopSignalOf(reason) {
|
|
9405
9615
|
return reason === "SIGTERM" ? "SIGTERM" : "SIGINT";
|
|
9406
9616
|
}
|
|
9407
|
-
/**
|
|
9408
|
-
* The exit code of a run that failed with `error`, given the CLI's error table; `stop` is the
|
|
9409
|
-
* signal that interrupted it, if any.
|
|
9410
|
-
*/
|
|
9411
|
-
function exitCodeOf(error, errors, stop) {
|
|
9412
|
-
const exitClass = exitClassOf(error, errors);
|
|
9413
|
-
return exitClass === "interrupted" ? SIGNAL_EXIT_CODES[stopSignalOf(stop?.reason)] : EXIT_CODES[exitClass];
|
|
9414
|
-
}
|
|
9415
9617
|
//#endregion
|
|
9416
9618
|
//#region ../agent-cli/dist/agent-cli.js
|
|
9417
9619
|
function checkOwnCodes(errors) {
|
|
@@ -9443,9 +9645,11 @@ function checkCli(cli) {
|
|
|
9443
9645
|
checkSkill(cli);
|
|
9444
9646
|
const { commands } = cli;
|
|
9445
9647
|
checkTree(commands, [], table);
|
|
9648
|
+
for (const row of cli.doctor ?? []) checkErrors(row.errors ?? [], `the ${row.name} row of doctor`, table);
|
|
9446
9649
|
const taken = Object.keys(builtInCommands(cli, {
|
|
9447
9650
|
bin: void 0,
|
|
9448
|
-
node: void 0
|
|
9651
|
+
node: void 0,
|
|
9652
|
+
platform: process.platform
|
|
9449
9653
|
})).filter((name) => commands[name] !== void 0);
|
|
9450
9654
|
if (taken.length > 0) throw new Error(`${cli.name} defines ${taken.join(", ")}, a built-in Command it cannot replace`);
|
|
9451
9655
|
return cli;
|
|
@@ -9833,37 +10037,6 @@ function route(root, invocation) {
|
|
|
9833
10037
|
return walk(root, argv.filter((word, index) => !(index < options.length && word === JSON_FLAG.flag)), json, invocation);
|
|
9834
10038
|
}
|
|
9835
10039
|
//#endregion
|
|
9836
|
-
//#region ../agent-cli/dist/command-line/route-line.js
|
|
9837
|
-
/** Routes `argv`, the line's words after the Run-as, to a Command of `root`. */
|
|
9838
|
-
function routeLine(root, runAs, argv) {
|
|
9839
|
-
if (!argv.ok) return {
|
|
9840
|
-
ok: false,
|
|
9841
|
-
reason: `it does not tokenize: ${argv.reason}`
|
|
9842
|
-
};
|
|
9843
|
-
const invocation = {
|
|
9844
|
-
runAs,
|
|
9845
|
-
argv: argv.words,
|
|
9846
|
-
flags: []
|
|
9847
|
-
};
|
|
9848
|
-
const routed = route(root, invocation);
|
|
9849
|
-
switch (routed.kind) {
|
|
9850
|
-
case "usage error": return {
|
|
9851
|
-
ok: false,
|
|
9852
|
-
reason: routed.error.message
|
|
9853
|
-
};
|
|
9854
|
-
case "help":
|
|
9855
|
-
case "version": return {
|
|
9856
|
-
ok: false,
|
|
9857
|
-
reason: "it names no Command"
|
|
9858
|
-
};
|
|
9859
|
-
case "command": return {
|
|
9860
|
-
ok: true,
|
|
9861
|
-
routed,
|
|
9862
|
-
invocation
|
|
9863
|
-
};
|
|
9864
|
-
}
|
|
9865
|
-
}
|
|
9866
|
-
//#endregion
|
|
9867
10040
|
//#region ../agent-cli/dist/command-line/shell-words.js
|
|
9868
10041
|
const BLANK = /\s/;
|
|
9869
10042
|
const SHELL_SYNTAX = /* @__PURE__ */ new Set([
|
|
@@ -9962,39 +10135,126 @@ function shellWords(line) {
|
|
|
9962
10135
|
};
|
|
9963
10136
|
}
|
|
9964
10137
|
//#endregion
|
|
9965
|
-
//#region ../agent-cli/dist/
|
|
9966
|
-
|
|
9967
|
-
|
|
9968
|
-
|
|
9969
|
-
|
|
9970
|
-
|
|
9971
|
-
|
|
9972
|
-
|
|
9973
|
-
|
|
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: []
|
|
9974
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
|
+
}
|
|
9975
10168
|
}
|
|
10169
|
+
//#endregion
|
|
10170
|
+
//#region ../agent-cli/dist/check-next-steps.js
|
|
9976
10171
|
/**
|
|
9977
10172
|
* Why `step` would not run a Command of `root`, or `undefined` when it would. A line given whole is
|
|
9978
10173
|
* not checked, since it may run another program.
|
|
9979
10174
|
*/
|
|
9980
|
-
function nextStepProblem(root, runAs, step) {
|
|
10175
|
+
async function nextStepProblem(root, runAs, step) {
|
|
9981
10176
|
const words = wordsOf(step);
|
|
9982
10177
|
if (words === void 0) return void 0;
|
|
9983
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
|
|
9987
|
-
|
|
9988
|
-
}
|
|
9989
|
-
/**
|
|
9990
|
-
function
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
|
|
9994
|
-
|
|
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
|
+
}
|
|
9995
10197
|
return problems.length === 0 ? error : new Error(`a Next step would not run: ${problems.join("; ")}`, { cause: error });
|
|
9996
10198
|
}
|
|
9997
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
|
|
9998
10258
|
//#region ../agent-cli/dist/help.js
|
|
9999
10259
|
function table(rows) {
|
|
10000
10260
|
const width = Math.max(...rows.map(([left]) => left.length));
|
|
@@ -10032,10 +10292,10 @@ const EXIT_MEANINGS = {
|
|
|
10032
10292
|
source: "someone at the source acts",
|
|
10033
10293
|
wait: "not done yet: do what the hint says, then run it again"
|
|
10034
10294
|
};
|
|
10035
|
-
function exitCodeRows(cli) {
|
|
10295
|
+
function exitCodeRows(cli, root) {
|
|
10036
10296
|
return [
|
|
10037
10297
|
["0", "done"],
|
|
10038
|
-
...[...[...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]),
|
|
10039
10299
|
[Object.values(SIGNAL_EXIT_CODES).join(", "), "interrupted"]
|
|
10040
10300
|
];
|
|
10041
10301
|
}
|
|
@@ -10053,7 +10313,7 @@ function examplesSection(examples, style) {
|
|
|
10053
10313
|
}
|
|
10054
10314
|
/** The CLI's own help: a listing or its single Command's help, ending with exit codes and examples. */
|
|
10055
10315
|
function cliHelp(cli, root, style) {
|
|
10056
|
-
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));
|
|
10057
10317
|
}
|
|
10058
10318
|
function groupHelp(group, style) {
|
|
10059
10319
|
return lines(...listingLines(group.commandLine, group, style));
|
|
@@ -10085,6 +10345,9 @@ function argRow(field) {
|
|
|
10085
10345
|
function optionRow(field) {
|
|
10086
10346
|
return fieldRow(field, defaultsOf(field));
|
|
10087
10347
|
}
|
|
10348
|
+
function secretRow(declaration) {
|
|
10349
|
+
return [isNeeded(declaration) ? declaration.name : `${declaration.name} (optional)`, `get it from ${declaration.get}`];
|
|
10350
|
+
}
|
|
10088
10351
|
function flagRow({ usage, description }) {
|
|
10089
10352
|
return [usage, description];
|
|
10090
10353
|
}
|
|
@@ -10095,6 +10358,7 @@ function commandLines(spec, style, { examples }) {
|
|
|
10095
10358
|
...examples ? examplesSection(spec.examples, style) : [],
|
|
10096
10359
|
...section("Arguments", spec.args.map(argRow), style),
|
|
10097
10360
|
...section("Options", spec.options.map(optionRow), style),
|
|
10361
|
+
...section("Secrets", (spec.command.secrets ?? []).map(secretRow), style),
|
|
10098
10362
|
...spec.help === void 0 ? [] : ["", spec.help],
|
|
10099
10363
|
"",
|
|
10100
10364
|
`${style.bold("Effect:")} ${spec.effect} (${EFFECT_NOTES[spec.effect]})`,
|
|
@@ -10127,7 +10391,7 @@ function commandData(spec, table) {
|
|
|
10127
10391
|
secrets: spec.command.secrets ?? [],
|
|
10128
10392
|
errors: (spec.command.errors ?? []).map((code) => ({
|
|
10129
10393
|
code,
|
|
10130
|
-
class: table
|
|
10394
|
+
class: classOfCode(code, table)
|
|
10131
10395
|
})),
|
|
10132
10396
|
args: inputSchema(spec.command.args ?? object({})),
|
|
10133
10397
|
options: optionsSchema(spec.options),
|
|
@@ -10160,7 +10424,10 @@ function errorsData(errors) {
|
|
|
10160
10424
|
function publishedTable(table, root) {
|
|
10161
10425
|
if (root.kind !== "command") return table;
|
|
10162
10426
|
const declared = new Set(root.command.errors ?? []);
|
|
10163
|
-
return Object.fromEntries(Object.entries(
|
|
10427
|
+
return Object.fromEntries(Object.entries({
|
|
10428
|
+
...table,
|
|
10429
|
+
...declaredRuntimeErrors
|
|
10430
|
+
}).filter(([code]) => declared.has(code)));
|
|
10164
10431
|
}
|
|
10165
10432
|
function cliHelpJson(cli, root) {
|
|
10166
10433
|
const { name, version, description } = cli;
|
|
@@ -10210,15 +10477,20 @@ function noticeLine(text, json) {
|
|
|
10210
10477
|
function stderrLine(key, text, json) {
|
|
10211
10478
|
return json ? `${JSON.stringify({ [key]: text })}\n` : `${text}\n`;
|
|
10212
10479
|
}
|
|
10213
|
-
function errorText(
|
|
10214
|
-
const text = formatRuntimeError(
|
|
10480
|
+
function errorText({ leaves }, format) {
|
|
10481
|
+
const text = formatRuntimeError(leaves, {
|
|
10215
10482
|
...format,
|
|
10216
|
-
runAs: format.invocation.runAs
|
|
10217
|
-
isBug: (each) => isBug(each, format.errors),
|
|
10218
|
-
isWait: (each) => isWait(each, format.errors)
|
|
10483
|
+
runAs: format.invocation.runAs
|
|
10219
10484
|
});
|
|
10220
10485
|
const { usageHelp, json } = format;
|
|
10221
|
-
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
|
+
};
|
|
10222
10494
|
}
|
|
10223
10495
|
function stdoutOf({ value, render }, format, spill) {
|
|
10224
10496
|
const text = printed(value, render, format);
|
|
@@ -10237,13 +10509,8 @@ const STOPPED_RESULT = {
|
|
|
10237
10509
|
stderr: "",
|
|
10238
10510
|
exitCode: SUCCESS_EXIT_CODE
|
|
10239
10511
|
};
|
|
10240
|
-
function errorResult(error, format) {
|
|
10241
|
-
|
|
10242
|
-
return {
|
|
10243
|
-
stdout: "",
|
|
10244
|
-
stderr: errorText(checked, format),
|
|
10245
|
-
exitCode: exitCodeOf(checked, format.errors, format.stop)
|
|
10246
|
-
};
|
|
10512
|
+
async function errorResult(error, format) {
|
|
10513
|
+
return failedResult(await failureOf(error, format), format);
|
|
10247
10514
|
}
|
|
10248
10515
|
async function commandResult(settled, format, spill) {
|
|
10249
10516
|
if (settled.ok) try {
|
|
@@ -10257,185 +10524,84 @@ async function commandResult(settled, format, spill) {
|
|
|
10257
10524
|
}
|
|
10258
10525
|
const { error, delivered } = settled;
|
|
10259
10526
|
if (delivered === void 0) return errorResult(error, format);
|
|
10527
|
+
const failure = await failureOf(error, format);
|
|
10260
10528
|
let outcome;
|
|
10261
10529
|
try {
|
|
10262
10530
|
outcome = delivered();
|
|
10263
10531
|
} catch (rejected) {
|
|
10264
|
-
return
|
|
10532
|
+
return failedResult(withLeaves(failure, rejected, format), format);
|
|
10265
10533
|
}
|
|
10266
|
-
const result =
|
|
10534
|
+
const result = failedResult(failure, format);
|
|
10267
10535
|
try {
|
|
10268
10536
|
return {
|
|
10269
10537
|
...result,
|
|
10270
10538
|
stdout: await stdoutOf(outcome, format, spill)
|
|
10271
10539
|
};
|
|
10272
10540
|
} catch (spillError) {
|
|
10273
|
-
const both = new AggregateError([...errorsOf(error), spillError]);
|
|
10274
10541
|
return {
|
|
10275
10542
|
...result,
|
|
10276
|
-
stderr: errorText(
|
|
10543
|
+
stderr: errorText(withLeaves(failure, spillError, format), format)
|
|
10277
10544
|
};
|
|
10278
10545
|
}
|
|
10279
|
-
}
|
|
10280
|
-
function textHelp(node, root, cli, style) {
|
|
10281
|
-
if (node === root) return cliHelp(cli, node, style);
|
|
10282
|
-
return node.kind === "command" ? commandHelp(node, style) : groupHelp(node, style);
|
|
10283
|
-
}
|
|
10284
|
-
function helpOf(node, root, cli, style, json) {
|
|
10285
|
-
if (!json) return textHelp(node, root, cli, style);
|
|
10286
|
-
if (node === root) return cliHelpJson(cli, node);
|
|
10287
|
-
const table = errorTableOf(cli);
|
|
10288
|
-
return node.kind === "command" ? commandHelpJson(node, table) : groupHelpJson(node, table);
|
|
10289
|
-
}
|
|
10290
|
-
function helpResult(node, root, cli, { style, json }) {
|
|
10291
|
-
return {
|
|
10292
|
-
stdout: helpOf(node, root, cli, style, json),
|
|
10293
|
-
stderr: "",
|
|
10294
|
-
exitCode: SUCCESS_EXIT_CODE
|
|
10295
|
-
};
|
|
10296
|
-
}
|
|
10297
|
-
function versionResult(cli, packageRoot) {
|
|
10298
|
-
return {
|
|
10299
|
-
stdout: `${[
|
|
10300
|
-
versionLine(cli),
|
|
10301
|
-
...buildStamp() === void 0 ? [] : [`changelog ${join(packageRoot, "CHANGELOG.md")}`],
|
|
10302
|
-
...cli.docs === void 0 ? [] : [`docs ${join(packageRoot, cli.docs)}`]
|
|
10303
|
-
].join("\n")}\n`,
|
|
10304
|
-
stderr: "",
|
|
10305
|
-
exitCode: SUCCESS_EXIT_CODE
|
|
10306
|
-
};
|
|
10307
|
-
}
|
|
10308
|
-
//#endregion
|
|
10309
|
-
//#region ../agent-cli/dist/package-root.js
|
|
10310
|
-
/**
|
|
10311
|
-
* The nearest folder holding a `package.json`, searched upward from the bin or, run without one,
|
|
10312
|
-
* from `cwd`; the search's start when none holds one. The bin is resolved through links first,
|
|
10313
|
-
* since npm's global bin is a link to it.
|
|
10314
|
-
*/
|
|
10315
|
-
function packageRootOf(bin, cwd) {
|
|
10316
|
-
const start = bin === void 0 ? resolve(cwd) : dirname(realpathSync(bin));
|
|
10317
|
-
for (let dir = start;; dir = dirname(dir)) {
|
|
10318
|
-
if (existsSync(join(dir, "package.json"))) return dir;
|
|
10319
|
-
if (dirname(dir) === dir) return start;
|
|
10320
|
-
}
|
|
10321
|
-
}
|
|
10322
|
-
//#endregion
|
|
10323
|
-
//#region ../agent-cli/dist/root-spec.js
|
|
10324
|
-
/** The CLI's Commands as routing and help see them, the Built-in Commands included. */
|
|
10325
|
-
function rootSpec(cli, launch) {
|
|
10326
|
-
const names = {
|
|
10327
|
-
name: cli.name,
|
|
10328
|
-
runAs: runAsOf(cli)
|
|
10329
|
-
};
|
|
10330
|
-
return cli.commands === void 0 ? {
|
|
10331
|
-
...commandSpec(names.runAs, cli.command, {
|
|
10332
|
-
path: [],
|
|
10333
|
-
builtIn: false,
|
|
10334
|
-
names
|
|
10335
|
-
}),
|
|
10336
|
-
description: cli.description
|
|
10337
|
-
} : groupSpec(names, cli.commands, builtInCommands(cli, launch));
|
|
10338
|
-
}
|
|
10339
|
-
//#endregion
|
|
10340
|
-
//#region ../agent-cli/dist/command-errors.js
|
|
10341
|
-
/**
|
|
10342
|
-
* `error`, or a bug naming each Error table code in it that the Command does not declare in its
|
|
10343
|
-
* `errors`. Runtime codes are never in the table, and a built-in's failures, `doctor` rows'
|
|
10344
|
-
* included, belong to no declaration, so both pass.
|
|
10345
|
-
*/
|
|
10346
|
-
function checkedCodes(error, { command, path, builtIn }, cli) {
|
|
10347
|
-
if (builtIn) return error;
|
|
10348
|
-
const table = errorTableOf(cli);
|
|
10349
|
-
const declared = new Set(command.errors ?? []);
|
|
10350
|
-
const undeclared = errorsOf(error).flatMap((leaf) => leaf instanceof CliError && Object.hasOwn(table, leaf.code) && !declared.has(leaf.code) ? [leaf.code] : []);
|
|
10351
|
-
if (undeclared.length === 0) return error;
|
|
10352
|
-
const label = commandLabel(path, cli.name).join(" ");
|
|
10353
|
-
const codes = [...new Set(undeclared)].join(", ");
|
|
10354
|
-
return new Error(`\`${label}\` threw ${codes}, which its errors do not declare`, { cause: error });
|
|
10355
|
-
}
|
|
10356
|
-
//#endregion
|
|
10357
|
-
//#region ../agent-cli/dist/context/operator.js
|
|
10358
|
-
const NEEDS_OPERATOR = "NEEDS_OPERATOR";
|
|
10359
|
-
/** Fails with `NEEDS_OPERATOR` when a Coding agent runs an Operator Command. */
|
|
10360
|
-
function checkOperator({ operator, commandLine }, env, rerun) {
|
|
10361
|
-
if (!operator || !isAgent(env)) return;
|
|
10362
|
-
throw new CliError({
|
|
10363
|
-
code: NEEDS_OPERATOR,
|
|
10364
|
-
message: `\`${commandLine}\` must be run by a person at a terminal, not a Coding agent`,
|
|
10365
|
-
hint: "ask the operator to type ! then the Next step",
|
|
10366
|
-
next: [rerun]
|
|
10367
|
-
});
|
|
10368
|
-
}
|
|
10369
|
-
//#endregion
|
|
10370
|
-
//#region ../agent-cli/dist/context/outputs.js
|
|
10371
|
-
const OUTPUT_EXISTS = "OUTPUT_EXISTS";
|
|
10372
|
-
/** Refuses a run that would overwrite existing outputs unless the caller passed `--force`. */
|
|
10373
|
-
async function checkOutputs({ rerun, commandLine, force, files }, outputs) {
|
|
10374
|
-
if (outputs === void 0 || force) return;
|
|
10375
|
-
const found = await Promise.all(outputs.map((path) => files.exists(path)));
|
|
10376
|
-
const existing = outputs.filter((_path, index) => found[index]);
|
|
10377
|
-
if (existing.length === 0) return;
|
|
10378
|
-
const them = existing.length === 1 ? "it" : "them";
|
|
10379
|
-
throw new CliError({
|
|
10380
|
-
code: OUTPUT_EXISTS,
|
|
10381
|
-
message: `\`${commandLine}\` would overwrite ${existing.join(", ")}`,
|
|
10382
|
-
hint: `once the user agrees to replace ${them}, run the Next step`,
|
|
10383
|
-
next: [rerun({ add: [FORCE_FLAG.flag] })]
|
|
10384
|
-
});
|
|
10385
|
-
}
|
|
10386
|
-
/** Guards the writes to declared outputs of `files`, whose paths resolve against `cwd`. */
|
|
10387
|
-
function outputsGuard(files, cwd) {
|
|
10388
|
-
const unwritten = /* @__PURE__ */ new Set();
|
|
10389
|
-
let forceStep = [];
|
|
10390
|
-
const exclusiveWrite = async (path, data, options) => {
|
|
10391
|
-
const target = resolve(cwd, path);
|
|
10392
|
-
unwritten.delete(target);
|
|
10393
|
-
try {
|
|
10394
|
-
await files.write(path, data, {
|
|
10395
|
-
...options,
|
|
10396
|
-
exclusive: true
|
|
10397
|
-
});
|
|
10398
|
-
} catch (error) {
|
|
10399
|
-
unwritten.add(target);
|
|
10400
|
-
if (!(error instanceof CliError) || error.code !== "ALREADY_EXISTS") throw error;
|
|
10401
|
-
throw new CliError({
|
|
10402
|
-
code: ALREADY_EXISTS,
|
|
10403
|
-
message: error.message,
|
|
10404
|
-
hint: "once the user agrees to replace it, run the Next step",
|
|
10405
|
-
next: [forceStep]
|
|
10406
|
-
}, { cause: error });
|
|
10407
|
-
}
|
|
10546
|
+
}
|
|
10547
|
+
function textHelp(node, root, cli, style) {
|
|
10548
|
+
if (node === root) return cliHelp(cli, node, style);
|
|
10549
|
+
return node.kind === "command" ? commandHelp(node, style) : groupHelp(node, style);
|
|
10550
|
+
}
|
|
10551
|
+
function helpOf(node, root, cli, style, json) {
|
|
10552
|
+
if (!json) return textHelp(node, root, cli, style);
|
|
10553
|
+
if (node === root) return cliHelpJson(cli, node);
|
|
10554
|
+
const table = errorTableOf(cli);
|
|
10555
|
+
return node.kind === "command" ? commandHelpJson(node, table) : groupHelpJson(node, table);
|
|
10556
|
+
}
|
|
10557
|
+
function helpResult(node, root, cli, { style, json }) {
|
|
10558
|
+
return {
|
|
10559
|
+
stdout: helpOf(node, root, cli, style, json),
|
|
10560
|
+
stderr: "",
|
|
10561
|
+
exitCode: SUCCESS_EXIT_CODE
|
|
10408
10562
|
};
|
|
10563
|
+
}
|
|
10564
|
+
function versionResult(cli, packageRoot) {
|
|
10409
10565
|
return {
|
|
10410
|
-
|
|
10411
|
-
|
|
10412
|
-
|
|
10413
|
-
|
|
10414
|
-
|
|
10415
|
-
|
|
10416
|
-
|
|
10417
|
-
}
|
|
10566
|
+
stdout: `${[
|
|
10567
|
+
versionLine(cli),
|
|
10568
|
+
...buildStamp() === void 0 ? [] : [`changelog ${join(packageRoot, "CHANGELOG.md")}`],
|
|
10569
|
+
...cli.docs === void 0 ? [] : [`docs ${join(packageRoot, cli.docs)}`]
|
|
10570
|
+
].join("\n")}\n`,
|
|
10571
|
+
stderr: "",
|
|
10572
|
+
exitCode: SUCCESS_EXIT_CODE
|
|
10418
10573
|
};
|
|
10419
10574
|
}
|
|
10575
|
+
//#endregion
|
|
10576
|
+
//#region ../agent-cli/dist/package-root.js
|
|
10420
10577
|
/**
|
|
10421
|
-
*
|
|
10422
|
-
*
|
|
10578
|
+
* The nearest folder holding a `package.json`, searched upward from the bin or, run without one,
|
|
10579
|
+
* from `cwd`; the search's start when none holds one. The bin is resolved through links first,
|
|
10580
|
+
* since npm's global bin is a link to it.
|
|
10423
10581
|
*/
|
|
10424
|
-
|
|
10425
|
-
const
|
|
10426
|
-
|
|
10427
|
-
|
|
10428
|
-
|
|
10429
|
-
|
|
10430
|
-
|
|
10431
|
-
|
|
10432
|
-
|
|
10433
|
-
|
|
10434
|
-
|
|
10435
|
-
|
|
10436
|
-
|
|
10437
|
-
|
|
10438
|
-
}
|
|
10582
|
+
function packageRootOf(bin, cwd) {
|
|
10583
|
+
const start = bin === void 0 ? resolve(cwd) : dirname(realpathSync(bin));
|
|
10584
|
+
for (let dir = start;; dir = dirname(dir)) {
|
|
10585
|
+
if (existsSync(join(dir, "package.json"))) return dir;
|
|
10586
|
+
if (dirname(dir) === dir) return start;
|
|
10587
|
+
}
|
|
10588
|
+
}
|
|
10589
|
+
//#endregion
|
|
10590
|
+
//#region ../agent-cli/dist/root-spec.js
|
|
10591
|
+
/** The CLI's Commands as routing and help see them, the Built-in Commands included. */
|
|
10592
|
+
function rootSpec(cli, launch) {
|
|
10593
|
+
const names = {
|
|
10594
|
+
name: cli.name,
|
|
10595
|
+
runAs: runAsOf(cli)
|
|
10596
|
+
};
|
|
10597
|
+
return cli.commands === void 0 ? {
|
|
10598
|
+
...commandSpec(names.runAs, cli.command, {
|
|
10599
|
+
path: [],
|
|
10600
|
+
builtIn: false,
|
|
10601
|
+
names
|
|
10602
|
+
}),
|
|
10603
|
+
description: cli.description
|
|
10604
|
+
} : groupSpec(names, cli.commands, builtInCommands(cli, launch));
|
|
10439
10605
|
}
|
|
10440
10606
|
//#endregion
|
|
10441
10607
|
//#region ../agent-cli/dist/context/spill.js
|
|
@@ -11018,43 +11184,6 @@ function resolveOptions(options) {
|
|
|
11018
11184
|
};
|
|
11019
11185
|
}
|
|
11020
11186
|
//#endregion
|
|
11021
|
-
//#region ../agent-cli/dist/context/confirm.js
|
|
11022
|
-
const CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED";
|
|
11023
|
-
const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
|
|
11024
|
-
function checkConfirmation({ rerun, commandLine, given }, expected) {
|
|
11025
|
-
if (given === expected) return;
|
|
11026
|
-
const advice = {
|
|
11027
|
-
hint: "once the user confirms the target, run the Next step",
|
|
11028
|
-
next: [rerun({
|
|
11029
|
-
remove: [CONFIRM_FLAG.flag],
|
|
11030
|
-
add: [CONFIRM_FLAG.flag, expected]
|
|
11031
|
-
})]
|
|
11032
|
-
};
|
|
11033
|
-
if (given === void 0) throw new UsageError({
|
|
11034
|
-
code: CONFIRMATION_REQUIRED,
|
|
11035
|
-
message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
|
|
11036
|
-
...advice
|
|
11037
|
-
});
|
|
11038
|
-
throw new UsageError({
|
|
11039
|
-
code: CONFIRMATION_MISMATCH,
|
|
11040
|
-
message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
|
|
11041
|
-
...advice
|
|
11042
|
-
});
|
|
11043
|
-
}
|
|
11044
|
-
//#endregion
|
|
11045
|
-
//#region ../agent-cli/dist/context/cost.js
|
|
11046
|
-
const NOT_CONFIRMED = "NOT_CONFIRMED";
|
|
11047
|
-
/** Refuses a run whose Plan states a cost the caller has not approved with `--yes`. */
|
|
11048
|
-
function checkCost({ rerun, commandLine, yes }, cost) {
|
|
11049
|
-
if (cost === void 0 || yes) return;
|
|
11050
|
-
throw new CliError({
|
|
11051
|
-
code: NOT_CONFIRMED,
|
|
11052
|
-
message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
|
|
11053
|
-
hint: "once the user approves the cost, run the Next step",
|
|
11054
|
-
next: [rerun({ add: [YES_FLAG.flag] })]
|
|
11055
|
-
});
|
|
11056
|
-
}
|
|
11057
|
-
//#endregion
|
|
11058
11187
|
//#region ../agent-cli/dist/execute.js
|
|
11059
11188
|
const PLAN_INDENT = " ";
|
|
11060
11189
|
function outcomeOf(value, render) {
|
|
@@ -11102,8 +11231,8 @@ function serveContextOf(ctx, end) {
|
|
|
11102
11231
|
const serveCtx = Object.create(ctx);
|
|
11103
11232
|
return Object.assign(serveCtx, { end });
|
|
11104
11233
|
}
|
|
11105
|
-
async function execute(command, input, ctx, {
|
|
11106
|
-
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());
|
|
11107
11236
|
const { env, envFile, fileError } = ctx;
|
|
11108
11237
|
checkSecrets(command.secrets ?? [], {
|
|
11109
11238
|
env,
|
|
@@ -11114,7 +11243,7 @@ async function execute(command, input, ctx, { dryRun, confirm, yes, page, comman
|
|
|
11114
11243
|
const { render } = command;
|
|
11115
11244
|
return outcomeOf(await command.run({
|
|
11116
11245
|
...input,
|
|
11117
|
-
...page
|
|
11246
|
+
...flags.page
|
|
11118
11247
|
}, ctx), (data, style) => render === void 0 ? encode(data.items) : render(data, style));
|
|
11119
11248
|
}
|
|
11120
11249
|
if (command.effect === "read" || command.effect === "act") return dataOutcome(command, await command.run(input, ctx), commandLine);
|
|
@@ -11129,32 +11258,28 @@ async function execute(command, input, ctx, { dryRun, confirm, yes, page, comman
|
|
|
11129
11258
|
return outcome;
|
|
11130
11259
|
}
|
|
11131
11260
|
const plan = await command.plan(input, ctx);
|
|
11132
|
-
if (dryRun) return dryRunOutcome(command, plan, commandLine);
|
|
11261
|
+
if (ctx.dryRun) return dryRunOutcome(command, plan, commandLine);
|
|
11133
11262
|
const outputs = command.outputs?.(plan);
|
|
11134
|
-
|
|
11135
|
-
|
|
11263
|
+
const { rerun, force } = ctx;
|
|
11264
|
+
if (outputs !== void 0) await runOutputs.refuseOverwrite(outputs, {
|
|
11265
|
+
rerun,
|
|
11136
11266
|
commandLine,
|
|
11137
|
-
force
|
|
11138
|
-
|
|
11139
|
-
}, outputs);
|
|
11267
|
+
force
|
|
11268
|
+
});
|
|
11140
11269
|
checkCost({
|
|
11141
|
-
rerun
|
|
11270
|
+
rerun,
|
|
11142
11271
|
commandLine,
|
|
11143
|
-
yes
|
|
11272
|
+
yes: flags.yes
|
|
11144
11273
|
}, command.cost?.(plan));
|
|
11145
11274
|
if (command.effect === "destructive") checkConfirmation({
|
|
11146
|
-
rerun
|
|
11275
|
+
rerun,
|
|
11147
11276
|
commandLine,
|
|
11148
|
-
given: confirm
|
|
11277
|
+
given: flags.confirm
|
|
11149
11278
|
}, command.confirm(plan));
|
|
11150
|
-
if (outputs !== void 0) {
|
|
11151
|
-
|
|
11152
|
-
|
|
11153
|
-
|
|
11154
|
-
fileError: ctx.fileError
|
|
11155
|
-
}, outputs);
|
|
11156
|
-
if (!ctx.force) guardOutputs(outputs, ctx.rerun({ add: [FORCE_FLAG.flag] }));
|
|
11157
|
-
}
|
|
11279
|
+
if (outputs !== void 0) await runOutputs.claim(outputs, {
|
|
11280
|
+
rerun,
|
|
11281
|
+
force
|
|
11282
|
+
});
|
|
11158
11283
|
return dataOutcome(command, await command.apply(plan, ctx), commandLine);
|
|
11159
11284
|
}
|
|
11160
11285
|
const manifestSchema = object({ bugs: union([string(), object({ url: string() })]).transform((value) => typeof value === "string" ? value : value.url).optional().catch(void 0) });
|
|
@@ -11170,16 +11295,7 @@ function manifestAt(packageRoot) {
|
|
|
11170
11295
|
return parsed.success ? parsed.data : NO_MANIFEST;
|
|
11171
11296
|
}
|
|
11172
11297
|
//#endregion
|
|
11173
|
-
//#region ../agent-cli/dist/run-
|
|
11174
|
-
function stdinNeverEndedHint(callerLine) {
|
|
11175
|
-
return `stdin never ended: pipe the text into \`${callerLine}\`, or redirect a file into it`;
|
|
11176
|
-
}
|
|
11177
|
-
function runDeadlineAdvice(resumed, pipedLine) {
|
|
11178
|
-
return resumed === void 0 ? { hint: stdinNeverEndedHint(pipedLine) } : {
|
|
11179
|
-
hint: "run the Next step to continue the work",
|
|
11180
|
-
next: [resumed]
|
|
11181
|
-
};
|
|
11182
|
-
}
|
|
11298
|
+
//#region ../agent-cli/dist/run-scope.js
|
|
11183
11299
|
function selfOf(name, bin) {
|
|
11184
11300
|
return bin === void 0 ? [name] : [
|
|
11185
11301
|
process.execPath,
|
|
@@ -11198,57 +11314,93 @@ function portsOf(cli, replacement) {
|
|
|
11198
11314
|
if (replacement !== void 0) return () => replacement;
|
|
11199
11315
|
return cli.ports ?? (() => ({}));
|
|
11200
11316
|
}
|
|
11201
|
-
function
|
|
11202
|
-
const {
|
|
11203
|
-
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));
|
|
11204
11342
|
const envFile = envFileOf(cli.name, env);
|
|
11343
|
+
const spawning = {
|
|
11344
|
+
fileError,
|
|
11345
|
+
rerun: rerun()
|
|
11346
|
+
};
|
|
11205
11347
|
return {
|
|
11206
|
-
|
|
11207
|
-
|
|
11208
|
-
packageRoot,
|
|
11209
|
-
progress,
|
|
11210
|
-
secret: (name) => secretOf({
|
|
11211
|
-
envFile,
|
|
11212
|
-
env,
|
|
11213
|
-
fileError
|
|
11214
|
-
}, name),
|
|
11215
|
-
envFile,
|
|
11348
|
+
invocation,
|
|
11349
|
+
rerun,
|
|
11216
11350
|
fileError,
|
|
11217
|
-
version: cli.version,
|
|
11218
|
-
build: buildStamp(),
|
|
11219
|
-
self: selfOf(cli.name, options.bin),
|
|
11220
|
-
signal,
|
|
11221
11351
|
clock,
|
|
11222
|
-
|
|
11223
|
-
|
|
11224
|
-
|
|
11225
|
-
|
|
11226
|
-
|
|
11227
|
-
|
|
11228
|
-
|
|
11229
|
-
rerun
|
|
11230
|
-
}),
|
|
11231
|
-
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) => ({
|
|
11232
11359
|
cwd,
|
|
11233
11360
|
env,
|
|
11234
|
-
|
|
11235
|
-
|
|
11361
|
+
platform,
|
|
11362
|
+
packageRoot,
|
|
11363
|
+
progress,
|
|
11364
|
+
secret: (name) => secretOf({
|
|
11365
|
+
envFile,
|
|
11366
|
+
env,
|
|
11367
|
+
fileError
|
|
11368
|
+
}, name),
|
|
11369
|
+
envFile,
|
|
11236
11370
|
fileError,
|
|
11237
|
-
|
|
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)
|
|
11238
11387
|
})
|
|
11239
11388
|
};
|
|
11240
11389
|
}
|
|
11241
|
-
function contextOf(
|
|
11242
|
-
const
|
|
11243
|
-
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]));
|
|
11394
|
+
function secret(name) {
|
|
11395
|
+
const declaration = declared.get(name);
|
|
11396
|
+
if (declaration === void 0) throw new Error(`ctx.secret("${name}") names a Secret the Command does not declare`);
|
|
11397
|
+
const found = portsBase.secret(name);
|
|
11398
|
+
if (found === void 0 && isNeeded(declaration)) throw new Error(`ctx.secret("${name}") found no value`);
|
|
11399
|
+
return found;
|
|
11400
|
+
}
|
|
11244
11401
|
return {
|
|
11245
11402
|
...portsBase,
|
|
11246
|
-
secret
|
|
11247
|
-
if (!declared.has(name)) throw new Error(`ctx.secret("${name}") names a Secret the Command does not declare`);
|
|
11248
|
-
const secret = portsBase.secret(name);
|
|
11249
|
-
if (secret === void 0) throw new Error(`ctx.secret("${name}") found no value`);
|
|
11250
|
-
return secret;
|
|
11251
|
-
},
|
|
11403
|
+
secret,
|
|
11252
11404
|
fail: cliErrorOf,
|
|
11253
11405
|
deadline: (ms, options) => control.deadline(ms, options),
|
|
11254
11406
|
runToEnd: (name, work) => control.runToEnd(name, work),
|
|
@@ -11256,47 +11408,57 @@ function contextOf(portsBase, control, parts) {
|
|
|
11256
11408
|
return ports();
|
|
11257
11409
|
},
|
|
11258
11410
|
project,
|
|
11259
|
-
force,
|
|
11260
|
-
dryRun,
|
|
11261
|
-
rerun,
|
|
11411
|
+
force: flags.force,
|
|
11412
|
+
dryRun: flags.dryRun,
|
|
11413
|
+
rerun: scope.rerun,
|
|
11262
11414
|
poll: (check, { intervalMs, signal: stop }) => poll(portsBase.clock, check, intervalMs, stop === void 0 ? control.signal : AbortSignal.any([control.signal, stop])),
|
|
11263
11415
|
deliver,
|
|
11264
|
-
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]
|
|
11265
11447
|
};
|
|
11266
11448
|
}
|
|
11267
|
-
/**
|
|
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
|
+
*/
|
|
11268
11455
|
async function runCommand(cli, routed, options, write, baseFormat) {
|
|
11269
11456
|
const { command, commandLine, builtIn } = routed.spec;
|
|
11270
11457
|
const { cwd, env } = options;
|
|
11271
|
-
const clock = options.clock ?? realClock;
|
|
11272
|
-
const files = options.files ?? realFiles();
|
|
11273
11458
|
const stdin = options.stdin ?? "";
|
|
11274
|
-
const
|
|
11275
|
-
const
|
|
11276
|
-
const packageRoot = packageRootOf(options.bin, cwd);
|
|
11277
|
-
const progress = (message) => write.stderr(progressLine(message, routed.json));
|
|
11278
|
-
const invocation = {
|
|
11279
|
-
...baseFormat.invocation,
|
|
11280
|
-
flags: knownFlags(routed.spec),
|
|
11281
|
-
stdinArg: stdinArgOf(routed)
|
|
11282
|
-
};
|
|
11283
|
-
const rerunOf = (change) => ({
|
|
11284
|
-
kind: "rerun",
|
|
11285
|
-
words: changedArgv(invocation, change),
|
|
11286
|
-
stdinArg: invocation.stdinArg
|
|
11287
|
-
});
|
|
11459
|
+
const scope = runScopeOf(cli, routed, options, write, baseFormat.invocation);
|
|
11460
|
+
const { invocation, rerun: rerunOf, fileError, clock, outputs, progress, platform } = scope;
|
|
11288
11461
|
const rerun = rerunOf();
|
|
11289
|
-
const fileError = fileErrorOf({
|
|
11290
|
-
rerun,
|
|
11291
|
-
env,
|
|
11292
|
-
platform
|
|
11293
|
-
});
|
|
11294
|
-
const format = {
|
|
11295
|
-
...baseFormat,
|
|
11296
|
-
bugs: () => manifestAt(packageRoot).bugs,
|
|
11297
|
-
json: routed.json,
|
|
11298
|
-
invocation
|
|
11299
|
-
};
|
|
11300
11462
|
const pipedLine = commandLineOf({
|
|
11301
11463
|
...invocation,
|
|
11302
11464
|
stdinArg: void 0
|
|
@@ -11313,10 +11475,18 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11313
11475
|
advice: () => runDeadlineAdvice(resumed, pipedLine)
|
|
11314
11476
|
}
|
|
11315
11477
|
});
|
|
11316
|
-
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) => {
|
|
11317
11487
|
control.close();
|
|
11318
11488
|
return {
|
|
11319
|
-
result: errorResult(error, format),
|
|
11489
|
+
result: await errorResult(error, format),
|
|
11320
11490
|
finishSideTask
|
|
11321
11491
|
};
|
|
11322
11492
|
};
|
|
@@ -11335,24 +11505,9 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11335
11505
|
}
|
|
11336
11506
|
const { input, flags } = parsed;
|
|
11337
11507
|
resumed = command.deadline?.resume(input, rerunOf);
|
|
11338
|
-
const outputs = outputsGuard(classifiedFiles(files, {
|
|
11339
|
-
cwd,
|
|
11340
|
-
fileError
|
|
11341
|
-
}), cwd);
|
|
11342
|
-
const scope = {
|
|
11343
|
-
cli,
|
|
11344
|
-
options,
|
|
11345
|
-
packageRoot,
|
|
11346
|
-
progress,
|
|
11347
|
-
fileError,
|
|
11348
|
-
rerun,
|
|
11349
|
-
clock,
|
|
11350
|
-
files: outputs.files
|
|
11351
|
-
};
|
|
11352
|
-
const portsBase = portsBaseOf(scope, control.signal);
|
|
11353
11508
|
const finishSideTask = startSideTask(builtIn ? void 0 : cli.sideTask?.bind(cli), (sideSignal) => {
|
|
11354
|
-
const sideBase =
|
|
11355
|
-
const sidePorts = once(() => ports(sideBase));
|
|
11509
|
+
const sideBase = scope.portsBase(sideSignal);
|
|
11510
|
+
const sidePorts = once(() => scope.ports(sideBase));
|
|
11356
11511
|
return {
|
|
11357
11512
|
...sideBase,
|
|
11358
11513
|
command: routed.spec.path,
|
|
@@ -11377,14 +11532,10 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11377
11532
|
return failed(error, finishSideTask);
|
|
11378
11533
|
}
|
|
11379
11534
|
let delivered;
|
|
11380
|
-
const ctx = contextOf(
|
|
11381
|
-
|
|
11535
|
+
const ctx = contextOf(scope, control, {
|
|
11536
|
+
command,
|
|
11537
|
+
flags,
|
|
11382
11538
|
project,
|
|
11383
|
-
force: flags.force,
|
|
11384
|
-
dryRun: flags.dryRun,
|
|
11385
|
-
secrets: command.secrets,
|
|
11386
|
-
rerun: rerunOf,
|
|
11387
|
-
notice: (text) => write.stderr(noticeLine(text, routed.json)),
|
|
11388
11539
|
deliver: (data) => {
|
|
11389
11540
|
delivered = () => dataOutcome(command, data, commandLine);
|
|
11390
11541
|
}
|
|
@@ -11403,19 +11554,16 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11403
11554
|
const outcome = await control.settle(noticesOf(cli, {
|
|
11404
11555
|
builtIn,
|
|
11405
11556
|
bin: options.bin,
|
|
11406
|
-
node: options.node
|
|
11557
|
+
node: options.node,
|
|
11558
|
+
platform
|
|
11407
11559
|
}, ctx).then(() => execute(command, input, ctx, {
|
|
11408
|
-
|
|
11409
|
-
confirm: flags.confirm,
|
|
11410
|
-
yes: flags.yes,
|
|
11411
|
-
page: flags.page,
|
|
11560
|
+
flags,
|
|
11412
11561
|
commandLine,
|
|
11413
11562
|
started: async (outcome) => {
|
|
11414
11563
|
write.stdout(await startedOutput(outcome, format, spill));
|
|
11415
11564
|
served = true;
|
|
11416
11565
|
},
|
|
11417
|
-
|
|
11418
|
-
guardOutputs: outputs.guard
|
|
11566
|
+
outputs
|
|
11419
11567
|
})));
|
|
11420
11568
|
if (served) return {
|
|
11421
11569
|
result: STOPPED_RESULT,
|
|
@@ -11432,7 +11580,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11432
11580
|
};
|
|
11433
11581
|
settled = {
|
|
11434
11582
|
ok: false,
|
|
11435
|
-
error
|
|
11583
|
+
error,
|
|
11436
11584
|
delivered
|
|
11437
11585
|
};
|
|
11438
11586
|
}
|
|
@@ -11444,25 +11592,6 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11444
11592
|
finishSideTask
|
|
11445
11593
|
};
|
|
11446
11594
|
}
|
|
11447
|
-
/** `build` runs on the first call; later calls give its value or throw its error again. */
|
|
11448
|
-
function once(build) {
|
|
11449
|
-
let outcome;
|
|
11450
|
-
return () => {
|
|
11451
|
-
if (outcome === void 0) try {
|
|
11452
|
-
outcome = {
|
|
11453
|
-
ok: true,
|
|
11454
|
-
value: build()
|
|
11455
|
-
};
|
|
11456
|
-
} catch (error) {
|
|
11457
|
-
outcome = {
|
|
11458
|
-
ok: false,
|
|
11459
|
-
error
|
|
11460
|
-
};
|
|
11461
|
-
}
|
|
11462
|
-
if (outcome.ok) return outcome.value;
|
|
11463
|
-
throw outcome.error;
|
|
11464
|
-
};
|
|
11465
|
-
}
|
|
11466
11595
|
async function noticesOf(cli, launch, ctx) {
|
|
11467
11596
|
await installOf(cli).notice(launch, ctx);
|
|
11468
11597
|
}
|
|
@@ -11521,7 +11650,8 @@ async function routeAndRun(cli, argv, options, write, stopNotice) {
|
|
|
11521
11650
|
const runAs = runAsOf(cli);
|
|
11522
11651
|
const root = rootSpec(cli, {
|
|
11523
11652
|
bin: options.bin,
|
|
11524
|
-
node: options.node
|
|
11653
|
+
node: options.node,
|
|
11654
|
+
platform: options.platform ?? process.platform
|
|
11525
11655
|
});
|
|
11526
11656
|
const format = {
|
|
11527
11657
|
cliName: cli.name,
|
|
@@ -11535,7 +11665,7 @@ async function routeAndRun(cli, argv, options, write, stopNotice) {
|
|
|
11535
11665
|
json: false,
|
|
11536
11666
|
errors: errorTableOf(cli),
|
|
11537
11667
|
stop: options.signal,
|
|
11538
|
-
...options.checkNextSteps === true ? {
|
|
11668
|
+
...options.checkNextSteps === true ? { nextStepCheck: nextStepCheck(root, runAs) } : {}
|
|
11539
11669
|
};
|
|
11540
11670
|
const routed = route(root, format.invocation);
|
|
11541
11671
|
const misused = (node) => isAgent(options.env) ? {
|
|
@@ -11614,7 +11744,7 @@ async function runProcess(cli) {
|
|
|
11614
11744
|
//#region package.json
|
|
11615
11745
|
var package_default = {
|
|
11616
11746
|
name: "@duyquangnvx/create-agent-cli",
|
|
11617
|
-
version: "0.
|
|
11747
|
+
version: "0.28.0",
|
|
11618
11748
|
description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
11619
11749
|
type: "module",
|
|
11620
11750
|
engines: { "node": ">=22.12.0" },
|