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