@duyquangnvx/create-agent-cli 0.2.1 → 0.3.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 +49 -0
- package/README.md +1 -1
- package/dist/bin.js +1635 -679
- package/package.json +2 -2
- package/template/AGENTS.md +5 -3
- package/template/skills/{{binName}}/SKILL.md +20 -8
- package/template/src/cli.ts +55 -2
- package/template/src/errors.ts +5 -0
- package/template/src/ports.ts +23 -0
- package/template/test/cli.test.ts +86 -6
- package/template/tsdown.config.ts +1 -0
package/dist/bin.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { basename, delimiter, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { access, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
|
-
import { homedir, tmpdir } from "node:os";
|
|
6
|
-
import {
|
|
5
|
+
import { constants, homedir, tmpdir } from "node:os";
|
|
6
|
+
import { constants as constants$1, existsSync, readFileSync, realpathSync } from "node:fs";
|
|
7
|
+
import { parseArgs, parseEnv } from "node:util";
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
8
9
|
//#region ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/util.js
|
|
9
10
|
function getEnumValues(entries) {
|
|
@@ -761,6 +762,59 @@ function formatError(error, mapper = (issue) => issue.message) {
|
|
|
761
762
|
processError(error);
|
|
762
763
|
return fieldErrors;
|
|
763
764
|
}
|
|
765
|
+
/** Format a ZodError as a human-readable string in the following form.
|
|
766
|
+
*
|
|
767
|
+
* From
|
|
768
|
+
*
|
|
769
|
+
* ```ts
|
|
770
|
+
* ZodError {
|
|
771
|
+
* issues: [
|
|
772
|
+
* {
|
|
773
|
+
* expected: 'string',
|
|
774
|
+
* code: 'invalid_type',
|
|
775
|
+
* path: [ 'username' ],
|
|
776
|
+
* message: 'Invalid input: expected string'
|
|
777
|
+
* },
|
|
778
|
+
* {
|
|
779
|
+
* expected: 'number',
|
|
780
|
+
* code: 'invalid_type',
|
|
781
|
+
* path: [ 'favoriteNumbers', 1 ],
|
|
782
|
+
* message: 'Invalid input: expected number'
|
|
783
|
+
* }
|
|
784
|
+
* ];
|
|
785
|
+
* }
|
|
786
|
+
* ```
|
|
787
|
+
*
|
|
788
|
+
* to
|
|
789
|
+
*
|
|
790
|
+
* ```
|
|
791
|
+
* username
|
|
792
|
+
* ✖ Expected number, received string at "username
|
|
793
|
+
* favoriteNumbers[0]
|
|
794
|
+
* ✖ Invalid input: expected number
|
|
795
|
+
* ```
|
|
796
|
+
*/
|
|
797
|
+
function toDotPath(_path) {
|
|
798
|
+
const segs = [];
|
|
799
|
+
const path = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
800
|
+
for (const seg of path) if (typeof seg === "number") segs.push(`[${seg}]`);
|
|
801
|
+
else if (typeof seg === "symbol") segs.push(`[${JSON.stringify(String(seg))}]`);
|
|
802
|
+
else if (/[^\w$]/.test(seg)) segs.push(`[${JSON.stringify(seg)}]`);
|
|
803
|
+
else {
|
|
804
|
+
if (segs.length) segs.push(".");
|
|
805
|
+
segs.push(seg);
|
|
806
|
+
}
|
|
807
|
+
return segs.join("");
|
|
808
|
+
}
|
|
809
|
+
function prettifyError(error) {
|
|
810
|
+
const lines = [];
|
|
811
|
+
const issues = [...error.issues].sort((a, b) => (a.path ?? []).length - (b.path ?? []).length);
|
|
812
|
+
for (const issue of issues) {
|
|
813
|
+
lines.push(`✖ ${issue.message}`);
|
|
814
|
+
if (issue.path?.length) lines.push(` → at ${toDotPath(issue.path)}`);
|
|
815
|
+
}
|
|
816
|
+
return lines.join("\n");
|
|
817
|
+
}
|
|
764
818
|
//#endregion
|
|
765
819
|
//#region ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/parse.js
|
|
766
820
|
function finalizeParams(callee, params) {
|
|
@@ -5515,6 +5569,18 @@ function superRefine(fn, params) {
|
|
|
5515
5569
|
return /* @__PURE__ */ _superRefine(fn, params);
|
|
5516
5570
|
}
|
|
5517
5571
|
//#endregion
|
|
5572
|
+
//#region ../agent-cli/dist/abort.js
|
|
5573
|
+
/** Never resolves; rejects with the signal's reason once it aborts. */
|
|
5574
|
+
function untilAborted(signal) {
|
|
5575
|
+
return new Promise((_resolve, reject) => {
|
|
5576
|
+
if (signal.aborted) {
|
|
5577
|
+
reject(signal.reason);
|
|
5578
|
+
return;
|
|
5579
|
+
}
|
|
5580
|
+
signal.addEventListener("abort", () => reject(signal.reason), { once: true });
|
|
5581
|
+
});
|
|
5582
|
+
}
|
|
5583
|
+
//#endregion
|
|
5518
5584
|
//#region ../agent-cli/dist/input.js
|
|
5519
5585
|
function isWrapper(schema) {
|
|
5520
5586
|
return schema instanceof ZodOptional || schema instanceof ZodNullable || schema instanceof ZodDefault || schema instanceof ZodPrefault;
|
|
@@ -5571,42 +5637,44 @@ function optionField(key, schema) {
|
|
|
5571
5637
|
usage
|
|
5572
5638
|
};
|
|
5573
5639
|
}
|
|
5574
|
-
function fieldsOf(schema, toField) {
|
|
5640
|
+
function fieldsOf$1(schema, toField) {
|
|
5575
5641
|
return schema === void 0 ? [] : Object.entries(schema.shape).map(([key, value]) => toField(key, value));
|
|
5576
5642
|
}
|
|
5577
5643
|
function argFields(schema) {
|
|
5578
|
-
return fieldsOf(schema, argField);
|
|
5644
|
+
return fieldsOf$1(schema, argField);
|
|
5579
5645
|
}
|
|
5580
5646
|
function optionFields(schema) {
|
|
5581
|
-
return fieldsOf(schema, optionField);
|
|
5647
|
+
return fieldsOf$1(schema, optionField);
|
|
5582
5648
|
}
|
|
5583
5649
|
//#endregion
|
|
5584
5650
|
//#region ../agent-cli/dist/errors.js
|
|
5585
5651
|
var CliError = class extends Error {
|
|
5586
5652
|
code;
|
|
5587
5653
|
hint;
|
|
5588
|
-
|
|
5589
|
-
|
|
5654
|
+
details;
|
|
5655
|
+
retryAt;
|
|
5656
|
+
constructor({ code, message, hint, details, retryAt }, options) {
|
|
5590
5657
|
super(message, options);
|
|
5591
5658
|
this.name = "CliError";
|
|
5592
5659
|
this.code = code;
|
|
5593
5660
|
this.hint = hint;
|
|
5594
|
-
this.
|
|
5661
|
+
this.details = details;
|
|
5662
|
+
this.retryAt = retryAt;
|
|
5595
5663
|
}
|
|
5596
5664
|
};
|
|
5597
|
-
/** The caller called the Agent CLI wrong;
|
|
5665
|
+
/** The caller called the Agent CLI wrong; the same call can't succeed. */
|
|
5598
5666
|
var UsageError = class extends CliError {
|
|
5599
|
-
constructor(
|
|
5600
|
-
super(
|
|
5601
|
-
...details,
|
|
5602
|
-
retryable: false
|
|
5603
|
-
});
|
|
5667
|
+
constructor(fields) {
|
|
5668
|
+
super(fields);
|
|
5604
5669
|
this.name = "UsageError";
|
|
5605
5670
|
}
|
|
5606
5671
|
};
|
|
5607
5672
|
const INTERNAL_CODE = "INTERNAL";
|
|
5673
|
+
function errnoOf(error) {
|
|
5674
|
+
return error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : void 0;
|
|
5675
|
+
}
|
|
5608
5676
|
function isMissingFile(error) {
|
|
5609
|
-
return error
|
|
5677
|
+
return errnoOf(error) === "ENOENT";
|
|
5610
5678
|
}
|
|
5611
5679
|
function debugVariable(cliName) {
|
|
5612
5680
|
return `${cliName.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_DEBUG`;
|
|
@@ -5617,47 +5685,61 @@ function messageOf(error) {
|
|
|
5617
5685
|
function isDebug({ env, cliName }) {
|
|
5618
5686
|
return Boolean(env[debugVariable(cliName)]);
|
|
5619
5687
|
}
|
|
5620
|
-
function
|
|
5688
|
+
function bugHint(format) {
|
|
5689
|
+
return isDebug(format) ? "the stack trace shows where it failed" : `rerun with ${debugVariable(format.cliName)}=1 to see the stack trace`;
|
|
5690
|
+
}
|
|
5691
|
+
function fieldsOf(error, format) {
|
|
5621
5692
|
if (error instanceof CliError) {
|
|
5622
|
-
const { code, message, hint,
|
|
5693
|
+
const { code, message, hint, details, retryAt } = error;
|
|
5623
5694
|
return {
|
|
5624
5695
|
code,
|
|
5625
5696
|
message,
|
|
5626
|
-
hint,
|
|
5627
|
-
|
|
5697
|
+
hint: format.isBug(error) ? bugHint(format) : hint,
|
|
5698
|
+
details,
|
|
5699
|
+
retryAt
|
|
5628
5700
|
};
|
|
5629
5701
|
}
|
|
5630
5702
|
return {
|
|
5631
5703
|
code: INTERNAL_CODE,
|
|
5632
5704
|
message: messageOf(error),
|
|
5633
|
-
hint:
|
|
5634
|
-
retryable: false
|
|
5705
|
+
hint: bugHint(format)
|
|
5635
5706
|
};
|
|
5636
5707
|
}
|
|
5637
|
-
function
|
|
5638
|
-
return
|
|
5708
|
+
function causesOf(error) {
|
|
5709
|
+
return error instanceof Error && error.cause !== void 0 ? [error.cause, ...causesOf(error.cause)] : [];
|
|
5639
5710
|
}
|
|
5640
|
-
function
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
})
|
|
5711
|
+
function traceOf(error) {
|
|
5712
|
+
return error instanceof Error ? error.stack ?? error.message : String(error);
|
|
5713
|
+
}
|
|
5714
|
+
/** Under debug: a bug's stack trace, then every error's cause chain. */
|
|
5715
|
+
function debugTrace(error, format) {
|
|
5716
|
+
if (!isDebug(format)) return void 0;
|
|
5717
|
+
const lines = [...format.isBug(error) ? [traceOf(error)] : [], ...causesOf(error).map((cause) => `caused by: ${traceOf(cause)}`)];
|
|
5718
|
+
return lines.length === 0 ? void 0 : lines.join("\n");
|
|
5719
|
+
}
|
|
5720
|
+
/** The errors a run failed with: each one an `AggregateError` holds, or the one it threw. */
|
|
5721
|
+
function errorsOf(error) {
|
|
5722
|
+
return error instanceof AggregateError && error.errors.length > 0 ? error.errors : [error];
|
|
5723
|
+
}
|
|
5724
|
+
function errorLines(error, format) {
|
|
5725
|
+
const fields = fieldsOf(error, format);
|
|
5726
|
+
const stack = debugTrace(error, format);
|
|
5647
5727
|
const { style } = format;
|
|
5648
|
-
const label = style.red(error
|
|
5728
|
+
const label = style.red(format.isBug(error) ? "internal error:" : "error:");
|
|
5649
5729
|
const trace = stack === void 0 ? "" : `${stack}\n`;
|
|
5650
|
-
return `${label} ${
|
|
5730
|
+
return `${label} ${fields.message}\n${trace}${style.cyan("hint:")} ${fields.hint}\n`;
|
|
5651
5731
|
}
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5732
|
+
function formatRuntimeError(error, format) {
|
|
5733
|
+
const errors = errorsOf(error);
|
|
5734
|
+
if (format.json) {
|
|
5735
|
+
const printed = errors.map((each) => ({
|
|
5736
|
+
...fieldsOf(each, format),
|
|
5737
|
+
stack: debugTrace(each, format)
|
|
5738
|
+
}));
|
|
5739
|
+
return `${JSON.stringify({ errors: printed })}\n`;
|
|
5659
5740
|
}
|
|
5660
|
-
|
|
5741
|
+
return errors.map((each) => errorLines(each, format)).join("");
|
|
5742
|
+
}
|
|
5661
5743
|
const SHELL_SAFE = /^[\w@%+=:,./-]+$/;
|
|
5662
5744
|
function quoted$1(token) {
|
|
5663
5745
|
return SHELL_SAFE.test(token) ? token : `'${token.replaceAll("'", `'\\''`)}'`;
|
|
@@ -5665,6 +5747,10 @@ function quoted$1(token) {
|
|
|
5665
5747
|
function pasteable(cliName, tokens) {
|
|
5666
5748
|
return [cliName, ...tokens].map(quoted$1).join(" ");
|
|
5667
5749
|
}
|
|
5750
|
+
/** The caller's command line as typed, ready to paste. */
|
|
5751
|
+
function commandLineOf({ cliName, argv }) {
|
|
5752
|
+
return pasteable(cliName, argv);
|
|
5753
|
+
}
|
|
5668
5754
|
function split(argv) {
|
|
5669
5755
|
const end = argv.indexOf("--");
|
|
5670
5756
|
return end === -1 ? {
|
|
@@ -5678,13 +5764,16 @@ function split(argv) {
|
|
|
5678
5764
|
function without(flag, tokens) {
|
|
5679
5765
|
return tokens.filter((token, index) => token !== flag && !token.startsWith(`${flag}=`) && tokens[index - 1] !== flag);
|
|
5680
5766
|
}
|
|
5681
|
-
/**
|
|
5767
|
+
/**
|
|
5768
|
+
* The caller's command line, ready to paste, with `flag value` (or `flag` alone, for a flag that
|
|
5769
|
+
* takes nothing) replacing any value they gave.
|
|
5770
|
+
*/
|
|
5682
5771
|
function commandLineWith({ cliName, argv }, flag, value) {
|
|
5683
5772
|
const { options, operands } = split(argv);
|
|
5773
|
+
const added = value === void 0 ? [flag] : [flag, value];
|
|
5684
5774
|
return pasteable(cliName, [
|
|
5685
5775
|
...without(flag, options),
|
|
5686
|
-
|
|
5687
|
-
value,
|
|
5776
|
+
...added,
|
|
5688
5777
|
...operands
|
|
5689
5778
|
]);
|
|
5690
5779
|
}
|
|
@@ -5700,7 +5789,45 @@ function commandLineRenaming({ cliName, argv }, renames) {
|
|
|
5700
5789
|
return pasteable(cliName, [...options.map((token) => renamed(token, renames)), ...operands]);
|
|
5701
5790
|
}
|
|
5702
5791
|
//#endregion
|
|
5792
|
+
//#region ../agent-cli/dist/usage.js
|
|
5793
|
+
const INVALID_INPUT = "INVALID_INPUT";
|
|
5794
|
+
const UNKNOWN_OPTION = "UNKNOWN_OPTION";
|
|
5795
|
+
const UNKNOWN_COMMAND = "UNKNOWN_COMMAND";
|
|
5796
|
+
function unknownError({ code, problem, commandLine, corrections, listed }) {
|
|
5797
|
+
return new UsageError({
|
|
5798
|
+
code,
|
|
5799
|
+
message: `${problem} for \`${commandLine}\``,
|
|
5800
|
+
hint: corrections.length === 0 ? `run \`${commandLine} --help\` to see the ${listed}` : `did you mean ${corrections.map((correction) => `\`${correction}\``).join(" or ")}?`
|
|
5801
|
+
});
|
|
5802
|
+
}
|
|
5803
|
+
function unknownOptionError(commandLine, invocation, flag, suggestions) {
|
|
5804
|
+
return unknownError({
|
|
5805
|
+
code: UNKNOWN_OPTION,
|
|
5806
|
+
problem: `unknown option '${flag}'`,
|
|
5807
|
+
commandLine,
|
|
5808
|
+
corrections: suggestions.map((name) => commandLineRenaming(invocation, /* @__PURE__ */ new Map([[flag, name]]))),
|
|
5809
|
+
listed: "options"
|
|
5810
|
+
});
|
|
5811
|
+
}
|
|
5812
|
+
function unknownCommandError(commandLine, name, suggestions) {
|
|
5813
|
+
return unknownError({
|
|
5814
|
+
code: UNKNOWN_COMMAND,
|
|
5815
|
+
problem: `unknown command '${name}'`,
|
|
5816
|
+
commandLine,
|
|
5817
|
+
corrections: suggestions.map((suggestion) => `${commandLine} ${suggestion}`),
|
|
5818
|
+
listed: "commands"
|
|
5819
|
+
});
|
|
5820
|
+
}
|
|
5821
|
+
function invalidInputError(commandLine, problem) {
|
|
5822
|
+
return new UsageError({
|
|
5823
|
+
code: INVALID_INPUT,
|
|
5824
|
+
message: problem,
|
|
5825
|
+
hint: `run \`${commandLine} --help\` to see the expected input`
|
|
5826
|
+
});
|
|
5827
|
+
}
|
|
5828
|
+
//#endregion
|
|
5703
5829
|
//#region ../agent-cli/dist/list.js
|
|
5830
|
+
const UNKNOWN_FIELD = "UNKNOWN_FIELD";
|
|
5704
5831
|
const DEFAULT_PAGE_LIMIT = 20;
|
|
5705
5832
|
const FIELDS_FLAG = "--fields";
|
|
5706
5833
|
const SEPARATOR = ",";
|
|
@@ -5770,13 +5897,13 @@ function selectedFields(values, rowFields, commandLine) {
|
|
|
5770
5897
|
const hint = `pass ${FIELDS_FLAG} with any of: ${rowFields.join(", ")}`;
|
|
5771
5898
|
const names = typeof raw === "string" ? raw.split(SEPARATOR).filter((name) => name !== "") : [];
|
|
5772
5899
|
if (names.length === 0) throw new UsageError({
|
|
5773
|
-
code:
|
|
5900
|
+
code: INVALID_INPUT,
|
|
5774
5901
|
message: `${FIELDS_FLAG}: missing value for \`${commandLine}\``,
|
|
5775
5902
|
hint
|
|
5776
5903
|
});
|
|
5777
5904
|
const unknown = names.filter((name) => !rowFields.includes(name));
|
|
5778
5905
|
if (unknown.length > 0) throw new UsageError({
|
|
5779
|
-
code:
|
|
5906
|
+
code: UNKNOWN_FIELD,
|
|
5780
5907
|
message: `unknown field ${quoted(unknown)} for \`${commandLine}\``,
|
|
5781
5908
|
hint
|
|
5782
5909
|
});
|
|
@@ -5842,6 +5969,16 @@ const CONFIRM_FLAG = standardFlag("--confirm", {
|
|
|
5842
5969
|
takes: "value",
|
|
5843
5970
|
schema: string()
|
|
5844
5971
|
});
|
|
5972
|
+
const YES_FLAG = standardFlag("--yes", {
|
|
5973
|
+
description: "approve the cost the run states; --dry-run shows it",
|
|
5974
|
+
takes: "nothing",
|
|
5975
|
+
schema: boolean()
|
|
5976
|
+
});
|
|
5977
|
+
const FORCE_FLAG = standardFlag("--force", {
|
|
5978
|
+
description: "replace the files the run writes when they exist; --dry-run lists them",
|
|
5979
|
+
takes: "nothing",
|
|
5980
|
+
schema: boolean()
|
|
5981
|
+
});
|
|
5845
5982
|
const EFFECT_FLAGS = {
|
|
5846
5983
|
read: [JSON_FLAG],
|
|
5847
5984
|
write: [JSON_FLAG, DRY_RUN_FLAG],
|
|
@@ -5849,16 +5986,24 @@ const EFFECT_FLAGS = {
|
|
|
5849
5986
|
JSON_FLAG,
|
|
5850
5987
|
DRY_RUN_FLAG,
|
|
5851
5988
|
CONFIRM_FLAG
|
|
5852
|
-
]
|
|
5989
|
+
],
|
|
5990
|
+
serve: [JSON_FLAG]
|
|
5853
5991
|
};
|
|
5854
5992
|
const RESERVED = new Map([
|
|
5855
5993
|
...ROUTING_FLAGS,
|
|
5856
5994
|
...EFFECT_FLAGS.destructive.map(({ flag }) => flag),
|
|
5995
|
+
YES_FLAG.flag,
|
|
5996
|
+
FORCE_FLAG.flag,
|
|
5857
5997
|
...LIST_FLAGS
|
|
5858
5998
|
].map((flag) => [nameOf(flag), flag]));
|
|
5859
|
-
/** The Standard flags of a Command with this Effect, and of a List Command. */
|
|
5860
|
-
function standardFlagsFor(effect, list) {
|
|
5861
|
-
return [
|
|
5999
|
+
/** The Standard flags of a Command with this Effect and declarations, and of a List Command. */
|
|
6000
|
+
function standardFlagsFor(effect, { outputs, cost }, list) {
|
|
6001
|
+
return [
|
|
6002
|
+
...EFFECT_FLAGS[effect],
|
|
6003
|
+
...outputs ? [FORCE_FLAG] : [],
|
|
6004
|
+
...cost ? [YES_FLAG] : [],
|
|
6005
|
+
...list?.standardFlags ?? []
|
|
6006
|
+
];
|
|
5862
6007
|
}
|
|
5863
6008
|
/** Throws when the Command declares an arg or option under a Standard flag's name. */
|
|
5864
6009
|
function checkNoStandardFlags(command) {
|
|
@@ -5876,10 +6021,14 @@ function checkNoStandardFlags(command) {
|
|
|
5876
6021
|
}
|
|
5877
6022
|
//#endregion
|
|
5878
6023
|
//#region ../agent-cli/dist/define.js
|
|
5879
|
-
function
|
|
6024
|
+
function checkedCommand(command) {
|
|
5880
6025
|
checkNoStandardFlags(command);
|
|
5881
6026
|
return command;
|
|
5882
6027
|
}
|
|
6028
|
+
function defineCommandFor() {
|
|
6029
|
+
return checkedCommand;
|
|
6030
|
+
}
|
|
6031
|
+
const defineCommand$1 = defineCommandFor();
|
|
5883
6032
|
function isCommand(node) {
|
|
5884
6033
|
return typeof node.effect === "string";
|
|
5885
6034
|
}
|
|
@@ -5945,7 +6094,18 @@ function meetsMinimum(version, minimum) {
|
|
|
5945
6094
|
return true;
|
|
5946
6095
|
}
|
|
5947
6096
|
//#endregion
|
|
6097
|
+
//#region ../agent-cli/dist/tail.js
|
|
6098
|
+
/** The last `count` lines of a child's output, trimmed; empty when it wrote nothing. */
|
|
6099
|
+
function lastLines(output, count) {
|
|
6100
|
+
return output.trim().split("\n").slice(-count).join("\n");
|
|
6101
|
+
}
|
|
6102
|
+
//#endregion
|
|
5948
6103
|
//#region ../agent-cli/dist/npm.js
|
|
6104
|
+
const REGISTRY_UNREACHABLE = "REGISTRY_UNREACHABLE";
|
|
6105
|
+
const REGISTRY_AUTH = "REGISTRY_AUTH";
|
|
6106
|
+
const INSTALL_FAILED = "INSTALL_FAILED";
|
|
6107
|
+
const NPM_FAILED = "NPM_FAILED";
|
|
6108
|
+
const NO_LATEST = "NO_LATEST";
|
|
5949
6109
|
function npmContext(cli, install, ctx) {
|
|
5950
6110
|
return {
|
|
5951
6111
|
cliName: cli.name,
|
|
@@ -5955,6 +6115,7 @@ function npmContext(cli, install, ctx) {
|
|
|
5955
6115
|
};
|
|
5956
6116
|
}
|
|
5957
6117
|
const NPMJS = "https://registry.npmjs.org/";
|
|
6118
|
+
const STDERR_TAIL_LINES$1 = 10;
|
|
5958
6119
|
const RM_RETRIES = 3;
|
|
5959
6120
|
const NETWORK_CODES = /* @__PURE__ */ new Set([
|
|
5960
6121
|
"ENOTFOUND",
|
|
@@ -5986,7 +6147,7 @@ function installCommandLine(install, version = "latest") {
|
|
|
5986
6147
|
}
|
|
5987
6148
|
function spawnNpm(args, ctx, signal) {
|
|
5988
6149
|
const windows = process.platform === "win32";
|
|
5989
|
-
const command = windows ? ["npm", ...args
|
|
6150
|
+
const command = windows ? ["npm", ...args.map((arg) => `"${arg.replaceAll("\"", "\"\"")}"`)].join(" ") : "npm";
|
|
5990
6151
|
return new Promise((resolve, reject) => {
|
|
5991
6152
|
const child = spawn(command, windows ? [] : args, {
|
|
5992
6153
|
env: ctx.env,
|
|
@@ -6017,45 +6178,40 @@ function spawnNpm(args, ctx, signal) {
|
|
|
6017
6178
|
});
|
|
6018
6179
|
}
|
|
6019
6180
|
function tail$1(stderr) {
|
|
6020
|
-
const lines = stderr
|
|
6021
|
-
return lines
|
|
6181
|
+
const lines = lastLines(stderr, STDERR_TAIL_LINES$1);
|
|
6182
|
+
return lines === "" ? "" : `\n${lines}`;
|
|
6022
6183
|
}
|
|
6023
6184
|
function failureOf(call, answer, ctx) {
|
|
6024
6185
|
const { registry, package: name } = ctx.install;
|
|
6025
6186
|
const code = NPM_CODE.exec(answer.stderr)?.[1];
|
|
6026
6187
|
const output = tail$1(answer.stderr);
|
|
6027
6188
|
if (code !== void 0 && NETWORK_CODES.has(code)) return new CliError({
|
|
6028
|
-
code:
|
|
6189
|
+
code: REGISTRY_UNREACHABLE,
|
|
6029
6190
|
message: `npm could not reach ${registry} (${code})${output}`,
|
|
6030
|
-
hint: "check the network or VPN, then run it again"
|
|
6031
|
-
retryable: true
|
|
6191
|
+
hint: "check the network or VPN, then run it again"
|
|
6032
6192
|
});
|
|
6033
6193
|
if (code !== void 0 && AUTH_CODES.has(code)) return new CliError({
|
|
6034
|
-
code:
|
|
6194
|
+
code: REGISTRY_AUTH,
|
|
6035
6195
|
message: `${registry} refused npm's credentials (${code})${output}`,
|
|
6036
|
-
hint: `log in to ${registry} (npm login --registry=${registry}) or set its token in ~/.npmrc, then run it again
|
|
6037
|
-
retryable: false
|
|
6196
|
+
hint: `log in to ${registry} (npm login --registry=${registry}) or set its token in ~/.npmrc, then run it again`
|
|
6038
6197
|
});
|
|
6039
6198
|
if (call === "view" && code === "E404") return noLatest(ctx, `npm found no ${name}@latest on ${registry}: no version of it is published there`);
|
|
6040
6199
|
if (call === "install") return new CliError({
|
|
6041
|
-
code:
|
|
6200
|
+
code: INSTALL_FAILED,
|
|
6042
6201
|
message: `npm could not install ${name}${code === void 0 ? "" : ` (${code})`}${output}`,
|
|
6043
|
-
hint: `fix what npm reports, then run it again, or install it yourself: ${installCommandLine(ctx.install)}
|
|
6044
|
-
retryable: false
|
|
6202
|
+
hint: `fix what npm reports, then run it again, or install it yourself: ${installCommandLine(ctx.install)}`
|
|
6045
6203
|
});
|
|
6046
6204
|
return new CliError({
|
|
6047
|
-
code:
|
|
6205
|
+
code: NPM_FAILED,
|
|
6048
6206
|
message: `npm ${call} failed with exit code ${answer.exitCode}${output}`,
|
|
6049
|
-
hint: "fix what npm reports, then run it again"
|
|
6050
|
-
retryable: false
|
|
6207
|
+
hint: "fix what npm reports, then run it again"
|
|
6051
6208
|
});
|
|
6052
6209
|
}
|
|
6053
6210
|
function noLatest(ctx, message) {
|
|
6054
6211
|
return new CliError({
|
|
6055
|
-
code:
|
|
6212
|
+
code: NO_LATEST,
|
|
6056
6213
|
message,
|
|
6057
|
-
hint: `check that ${ctx.install.package} is published on ${ctx.install.registry}
|
|
6058
|
-
retryable: false
|
|
6214
|
+
hint: `check that ${ctx.install.package} is published on ${ctx.install.registry}`
|
|
6059
6215
|
});
|
|
6060
6216
|
}
|
|
6061
6217
|
/**
|
|
@@ -6072,10 +6228,9 @@ async function runNpm(call, args, ctx, signal = ctx.signal) {
|
|
|
6072
6228
|
], ctx, signal).catch((error) => {
|
|
6073
6229
|
if (signal?.aborted === true) throw signal.reason;
|
|
6074
6230
|
throw new CliError({
|
|
6075
|
-
code:
|
|
6231
|
+
code: NPM_FAILED,
|
|
6076
6232
|
message: `npm could not be started: ${messageOf(error)}`,
|
|
6077
|
-
hint: "install Node with npm, or put npm on PATH"
|
|
6078
|
-
retryable: false
|
|
6233
|
+
hint: "install Node with npm, or put npm on PATH"
|
|
6079
6234
|
}, { cause: error });
|
|
6080
6235
|
});
|
|
6081
6236
|
if (answer.exitCode !== 0) throw failureOf(call, answer, ctx);
|
|
@@ -6126,16 +6281,16 @@ async function latestVersion(ctx) {
|
|
|
6126
6281
|
}
|
|
6127
6282
|
//#endregion
|
|
6128
6283
|
//#region ../agent-cli/dist/global-install.js
|
|
6284
|
+
const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
|
|
6129
6285
|
/** Where npm puts global packages under its global prefix. */
|
|
6130
6286
|
function globalModulesDir(prefix) {
|
|
6131
6287
|
return process.platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
|
|
6132
6288
|
}
|
|
6133
6289
|
function notGlobal(ctx, message) {
|
|
6134
6290
|
return new CliError({
|
|
6135
|
-
code:
|
|
6291
|
+
code: NOT_GLOBAL_INSTALL,
|
|
6136
6292
|
message,
|
|
6137
|
-
hint: `install it with npm, so \`${ctx.cliName} update\` can replace it: ${installCommandLine(ctx.install)}
|
|
6138
|
-
retryable: false
|
|
6293
|
+
hint: `install it with npm, so \`${ctx.cliName} update\` can replace it: ${installCommandLine(ctx.install)}`
|
|
6139
6294
|
});
|
|
6140
6295
|
}
|
|
6141
6296
|
async function realpathOrUndefined(path) {
|
|
@@ -6166,131 +6321,941 @@ async function globalInstallOf(bin, ctx) {
|
|
|
6166
6321
|
};
|
|
6167
6322
|
}
|
|
6168
6323
|
//#endregion
|
|
6169
|
-
//#region ../agent-cli/dist/
|
|
6170
|
-
|
|
6171
|
-
const
|
|
6172
|
-
function
|
|
6173
|
-
|
|
6324
|
+
//#region ../agent-cli/dist/confirm.js
|
|
6325
|
+
const CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED";
|
|
6326
|
+
const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
|
|
6327
|
+
function checkConfirmation({ invocation, commandLine, given }, expected) {
|
|
6328
|
+
if (given === expected) return;
|
|
6329
|
+
const hint = `to confirm, run \`${commandLineWith(invocation, CONFIRM_FLAG.flag, expected)}\``;
|
|
6330
|
+
if (given === void 0) throw new UsageError({
|
|
6331
|
+
code: CONFIRMATION_REQUIRED,
|
|
6332
|
+
message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
|
|
6333
|
+
hint
|
|
6334
|
+
});
|
|
6335
|
+
throw new UsageError({
|
|
6336
|
+
code: CONFIRMATION_MISMATCH,
|
|
6337
|
+
message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
|
|
6338
|
+
hint
|
|
6339
|
+
});
|
|
6174
6340
|
}
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6341
|
+
//#endregion
|
|
6342
|
+
//#region ../agent-cli/dist/cost.js
|
|
6343
|
+
const NOT_CONFIRMED = "NOT_CONFIRMED";
|
|
6344
|
+
/** Refuses a run whose Plan states a cost the caller has not approved with `--yes`. */
|
|
6345
|
+
function checkCost({ invocation, commandLine, yes }, cost) {
|
|
6346
|
+
if (cost === void 0 || yes) return;
|
|
6347
|
+
throw new CliError({
|
|
6348
|
+
code: NOT_CONFIRMED,
|
|
6349
|
+
message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
|
|
6350
|
+
hint: `to approve the cost, run \`${commandLineWith(invocation, YES_FLAG.flag)}\``
|
|
6351
|
+
});
|
|
6181
6352
|
}
|
|
6182
|
-
|
|
6183
|
-
|
|
6353
|
+
//#endregion
|
|
6354
|
+
//#region ../agent-cli/dist/agent.js
|
|
6355
|
+
const AGENT_VARIABLES = [
|
|
6356
|
+
"CLAUDECODE",
|
|
6357
|
+
"CODEX_CI",
|
|
6358
|
+
"CODEX_SANDBOX",
|
|
6359
|
+
"GEMINI_CLI",
|
|
6360
|
+
"CURSOR_AGENT",
|
|
6361
|
+
"CI"
|
|
6362
|
+
];
|
|
6363
|
+
/** Whether a Coding agent, not a person, is running the Agent CLI. */
|
|
6364
|
+
function isAgent(env) {
|
|
6365
|
+
return AGENT_VARIABLES.some((name) => env[name] !== void 0);
|
|
6184
6366
|
}
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6367
|
+
//#endregion
|
|
6368
|
+
//#region ../agent-cli/dist/errno.js
|
|
6369
|
+
const PERMISSION_DENIED = "PERMISSION_DENIED";
|
|
6370
|
+
const DISK_FULL = "DISK_FULL";
|
|
6371
|
+
const NOT_FOUND = "NOT_FOUND";
|
|
6372
|
+
function permissionHint({ path, callerLine, env }) {
|
|
6373
|
+
return isAgent(env) ? `your sandbox may block it: ask your user to run \`! ${callerLine}\`, or to allow ${path} in the sandbox's write list` : `check the permissions of ${path} and its folder`;
|
|
6374
|
+
}
|
|
6375
|
+
/** The error a file system failure on `path` stands for; any other error comes back unchanged. */
|
|
6376
|
+
function classifyErrno(error, context) {
|
|
6377
|
+
const { path } = context;
|
|
6378
|
+
switch (errnoOf(error)) {
|
|
6379
|
+
case "EACCES":
|
|
6380
|
+
case "EPERM": return new CliError({
|
|
6381
|
+
code: PERMISSION_DENIED,
|
|
6382
|
+
message: `permission denied: ${path}`,
|
|
6383
|
+
hint: permissionHint(context)
|
|
6384
|
+
}, { cause: error });
|
|
6385
|
+
case "ENOSPC": return new CliError({
|
|
6386
|
+
code: DISK_FULL,
|
|
6387
|
+
message: `no space left on the disk holding ${path}`,
|
|
6388
|
+
hint: "free space on that disk, then run the same command again"
|
|
6389
|
+
}, { cause: error });
|
|
6390
|
+
case "ENOENT": return new CliError({
|
|
6391
|
+
code: NOT_FOUND,
|
|
6392
|
+
message: `not found: ${path}`,
|
|
6393
|
+
hint: `check that ${path} and its folder exist`
|
|
6394
|
+
}, { cause: error });
|
|
6395
|
+
default: return error;
|
|
6196
6396
|
}
|
|
6197
|
-
const version = skillVersionIn(skill);
|
|
6198
|
-
if (version === void 0 || !isVersion(version) || !isVersion(cliVersion)) return {
|
|
6199
|
-
path,
|
|
6200
|
-
version,
|
|
6201
|
-
state: "unversioned"
|
|
6202
|
-
};
|
|
6203
|
-
return {
|
|
6204
|
-
path,
|
|
6205
|
-
version,
|
|
6206
|
-
state: standingOf(version, cliVersion)
|
|
6207
|
-
};
|
|
6208
6397
|
}
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
if (copy !== void 0 && real !== void 0 && !seen.has(real)) {
|
|
6218
|
-
seen.add(real);
|
|
6219
|
-
copies.push(copy);
|
|
6398
|
+
//#endregion
|
|
6399
|
+
//#region ../agent-cli/dist/clock.js
|
|
6400
|
+
const realClock = {
|
|
6401
|
+
now: () => Date.now(),
|
|
6402
|
+
sleep: (ms, signal) => new Promise((resolve, reject) => {
|
|
6403
|
+
if (signal?.aborted === true) {
|
|
6404
|
+
reject(signal.reason);
|
|
6405
|
+
return;
|
|
6220
6406
|
}
|
|
6407
|
+
const onAbort = () => {
|
|
6408
|
+
clearTimeout(timer);
|
|
6409
|
+
reject(signal?.reason);
|
|
6410
|
+
};
|
|
6411
|
+
const timer = setTimeout(() => {
|
|
6412
|
+
signal?.removeEventListener("abort", onAbort);
|
|
6413
|
+
resolve();
|
|
6414
|
+
}, ms);
|
|
6415
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
6416
|
+
})
|
|
6417
|
+
};
|
|
6418
|
+
/** Runs `then` once `ms` have passed on `clock`; aborting `cancel` drops it. */
|
|
6419
|
+
function after(clock, ms, cancel, then) {
|
|
6420
|
+
clock.sleep(ms, cancel).then(then, (error) => {
|
|
6421
|
+
if (!cancel.aborted) throw error;
|
|
6422
|
+
});
|
|
6423
|
+
}
|
|
6424
|
+
async function poll(clock, check, intervalMs, signal) {
|
|
6425
|
+
for (;;) {
|
|
6426
|
+
signal.throwIfAborted();
|
|
6427
|
+
const value = await check();
|
|
6428
|
+
if (value !== void 0) return value;
|
|
6429
|
+
await clock.sleep(intervalMs, signal);
|
|
6221
6430
|
}
|
|
6222
|
-
return copies;
|
|
6223
6431
|
}
|
|
6224
6432
|
//#endregion
|
|
6225
|
-
//#region ../agent-cli/dist/
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
const range = cli.engines?.node;
|
|
6238
|
-
if (range === void 0) return ok(`Node ${found}; ${cli.name} declares no engines.node to check it against`);
|
|
6239
|
-
const minimum = engineMinimum(range);
|
|
6240
|
-
if (minimum === void 0) return {
|
|
6241
|
-
state: "problem",
|
|
6242
|
-
message: `${cli.name} declares engines.node "${range}", which doctor cannot read: only >=X.Y.Z is supported`
|
|
6243
|
-
};
|
|
6244
|
-
if (meetsMinimum(found, minimum)) return ok(`Node ${found} meets ${range}`);
|
|
6245
|
-
const major = minimum[0];
|
|
6246
|
-
return {
|
|
6247
|
-
state: "problem",
|
|
6248
|
-
message: `${cli.name} runs on Node ${found}, older than the ${range} it needs; install Node ${minimum.join(".")} or newer and make it the default`,
|
|
6249
|
-
fix: `nvm install ${major} && nvm alias default ${major}`
|
|
6250
|
-
};
|
|
6251
|
-
}
|
|
6252
|
-
};
|
|
6433
|
+
//#region ../agent-cli/dist/interrupts.js
|
|
6434
|
+
const INTERRUPTED = "INTERRUPTED";
|
|
6435
|
+
const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
|
|
6436
|
+
/** How long an interrupted Command may take to settle before the run ends without it. */
|
|
6437
|
+
const GRACE_MS = 3e3;
|
|
6438
|
+
const GRACE_OVER = Symbol("grace over");
|
|
6439
|
+
function interruptedError() {
|
|
6440
|
+
return new CliError({
|
|
6441
|
+
code: INTERRUPTED,
|
|
6442
|
+
message: "interrupted before it finished",
|
|
6443
|
+
hint: "run the command again"
|
|
6444
|
+
});
|
|
6253
6445
|
}
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
return ok(`a global npm install under ${(await globalInstallOf(bin, npmContext(cli, install, ctx))).prefix}`);
|
|
6262
|
-
} catch (error) {
|
|
6263
|
-
if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
|
|
6264
|
-
state: "problem",
|
|
6265
|
-
message: error.message,
|
|
6266
|
-
fix: installCommandLine(install)
|
|
6267
|
-
};
|
|
6268
|
-
throw error;
|
|
6269
|
-
}
|
|
6270
|
-
}
|
|
6271
|
-
};
|
|
6446
|
+
const WAIT_HINT = "run it again later, when the service it waits on may answer faster";
|
|
6447
|
+
function deadlineError(commandLine, ms, hint) {
|
|
6448
|
+
return new CliError({
|
|
6449
|
+
code: DEADLINE_EXCEEDED,
|
|
6450
|
+
message: `${commandLine} ran past its ${ms} ms deadline`,
|
|
6451
|
+
hint
|
|
6452
|
+
});
|
|
6272
6453
|
}
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6454
|
+
/** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
|
|
6455
|
+
var RunControl = class {
|
|
6456
|
+
signal;
|
|
6457
|
+
#controller = new AbortController();
|
|
6458
|
+
#commandLine;
|
|
6459
|
+
#outer;
|
|
6460
|
+
#onOuterAbort = () => this.#controller.abort(interruptedError());
|
|
6461
|
+
/** Each running must-finish step, tracked until it settles, with its name. */
|
|
6462
|
+
#steps = /* @__PURE__ */ new Map();
|
|
6463
|
+
#progress;
|
|
6464
|
+
#clock;
|
|
6465
|
+
#disposed = new AbortController();
|
|
6466
|
+
#deadlineHit;
|
|
6467
|
+
#graceCancel = new AbortController();
|
|
6468
|
+
#endGrace = () => {};
|
|
6469
|
+
#graceOver = new Promise((resolve) => {
|
|
6470
|
+
this.#endGrace = resolve;
|
|
6471
|
+
});
|
|
6472
|
+
constructor({ commandLine, outer, clock, progress, runDeadline }) {
|
|
6473
|
+
this.#commandLine = commandLine;
|
|
6474
|
+
this.#outer = outer;
|
|
6475
|
+
this.#clock = clock;
|
|
6476
|
+
this.#progress = progress;
|
|
6477
|
+
this.signal = this.#controller.signal;
|
|
6478
|
+
this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
|
|
6479
|
+
if (outer?.aborted === true) this.#onOuterAbort();
|
|
6480
|
+
else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
|
|
6481
|
+
if (runDeadline !== void 0) {
|
|
6482
|
+
const { ms, hint } = runDeadline;
|
|
6483
|
+
after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, hint)));
|
|
6288
6484
|
}
|
|
6289
|
-
}
|
|
6290
|
-
|
|
6291
|
-
const
|
|
6292
|
-
|
|
6293
|
-
|
|
6485
|
+
}
|
|
6486
|
+
deadline(ms) {
|
|
6487
|
+
const controller = new AbortController();
|
|
6488
|
+
after(this.#clock, ms, this.#disposed.signal, () => {
|
|
6489
|
+
this.#deadlineHit ??= deadlineError(this.#commandLine, ms, WAIT_HINT);
|
|
6490
|
+
controller.abort(this.#deadlineHit);
|
|
6491
|
+
});
|
|
6492
|
+
return AbortSignal.any([this.signal, controller.signal]);
|
|
6493
|
+
}
|
|
6494
|
+
async runToEnd(name, work) {
|
|
6495
|
+
this.#graceCancel.abort();
|
|
6496
|
+
if (this.signal.aborted) this.#finishing(name);
|
|
6497
|
+
const step = work();
|
|
6498
|
+
const settled = step.then(() => {}, () => {});
|
|
6499
|
+
this.#steps.set(settled, name);
|
|
6500
|
+
try {
|
|
6501
|
+
return await step;
|
|
6502
|
+
} finally {
|
|
6503
|
+
this.#steps.delete(settled);
|
|
6504
|
+
this.#startGrace();
|
|
6505
|
+
}
|
|
6506
|
+
}
|
|
6507
|
+
/** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
|
|
6508
|
+
async settle(work) {
|
|
6509
|
+
try {
|
|
6510
|
+
const value = await Promise.race([work, this.#graceOver]);
|
|
6511
|
+
if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
|
|
6512
|
+
return value;
|
|
6513
|
+
} finally {
|
|
6514
|
+
await this.#stepsDone();
|
|
6515
|
+
this.#dispose();
|
|
6516
|
+
}
|
|
6517
|
+
}
|
|
6518
|
+
/**
|
|
6519
|
+
* The error a run ends with: an interrupt or a missed deadline outranks what the Command threw,
|
|
6520
|
+
* unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
|
|
6521
|
+
*/
|
|
6522
|
+
errorAfter(error) {
|
|
6523
|
+
if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
|
|
6524
|
+
if (this.#deadlineHit !== void 0 && !(error instanceof CliError)) return this.#deadlineHit;
|
|
6525
|
+
return error;
|
|
6526
|
+
}
|
|
6527
|
+
#onStop() {
|
|
6528
|
+
for (const name of this.#steps.values()) this.#finishing(name);
|
|
6529
|
+
this.#startGrace();
|
|
6530
|
+
}
|
|
6531
|
+
#finishing(name) {
|
|
6532
|
+
this.#progress(`finishing ${name}`);
|
|
6533
|
+
}
|
|
6534
|
+
#startGrace() {
|
|
6535
|
+
if (this.signal.aborted && this.#steps.size === 0) {
|
|
6536
|
+
this.#graceCancel.abort();
|
|
6537
|
+
this.#graceCancel = new AbortController();
|
|
6538
|
+
after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
|
|
6539
|
+
}
|
|
6540
|
+
}
|
|
6541
|
+
async #stepsDone() {
|
|
6542
|
+
while (this.#steps.size > 0) await Promise.all(this.#steps.keys());
|
|
6543
|
+
}
|
|
6544
|
+
#dispose() {
|
|
6545
|
+
this.#graceCancel.abort();
|
|
6546
|
+
this.#disposed.abort();
|
|
6547
|
+
this.#outer?.removeEventListener("abort", this.#onOuterAbort);
|
|
6548
|
+
}
|
|
6549
|
+
};
|
|
6550
|
+
//#endregion
|
|
6551
|
+
//#region ../agent-cli/dist/outputs.js
|
|
6552
|
+
const OUTPUT_EXISTS = "OUTPUT_EXISTS";
|
|
6553
|
+
/** Refuses a run that would overwrite existing outputs unless the caller passed `--force`. */
|
|
6554
|
+
async function checkOutputs({ invocation, commandLine, force, files }, outputs) {
|
|
6555
|
+
if (outputs === void 0 || force) return;
|
|
6556
|
+
const found = await Promise.all(outputs.map((path) => files.exists(path)));
|
|
6557
|
+
const existing = outputs.filter((_path, index) => found[index]);
|
|
6558
|
+
if (existing.length === 0) return;
|
|
6559
|
+
const them = existing.length === 1 ? "it" : "them";
|
|
6560
|
+
throw new CliError({
|
|
6561
|
+
code: OUTPUT_EXISTS,
|
|
6562
|
+
message: `\`${commandLine}\` would overwrite ${existing.join(", ")}`,
|
|
6563
|
+
hint: `to replace ${them}, run \`${commandLineWith(invocation, FORCE_FLAG.flag)}\``
|
|
6564
|
+
});
|
|
6565
|
+
}
|
|
6566
|
+
//#endregion
|
|
6567
|
+
//#region ../agent-cli/dist/project.js
|
|
6568
|
+
const PROJECT_INVALID = "PROJECT_INVALID";
|
|
6569
|
+
function invalid(path, problem, options) {
|
|
6570
|
+
return new CliError({
|
|
6571
|
+
code: PROJECT_INVALID,
|
|
6572
|
+
message: `the Project file ${path} is invalid: ${problem}`,
|
|
6573
|
+
hint: `fix ${path}, then run the command again`
|
|
6574
|
+
}, options);
|
|
6575
|
+
}
|
|
6576
|
+
function readText(path) {
|
|
6577
|
+
try {
|
|
6578
|
+
return readFileSync(path, "utf8");
|
|
6579
|
+
} catch (error) {
|
|
6580
|
+
if (isMissingFile(error)) return void 0;
|
|
6581
|
+
throw error;
|
|
6582
|
+
}
|
|
6583
|
+
}
|
|
6584
|
+
function parsed(path, text, schema) {
|
|
6585
|
+
let json;
|
|
6586
|
+
try {
|
|
6587
|
+
json = JSON.parse(text);
|
|
6588
|
+
} catch (error) {
|
|
6589
|
+
throw invalid(path, messageOf(error), { cause: error });
|
|
6590
|
+
}
|
|
6591
|
+
const result = schema.safeParse(json);
|
|
6592
|
+
if (!result.success) throw invalid(path, prettifyError(result.error));
|
|
6593
|
+
return result.data;
|
|
6594
|
+
}
|
|
6595
|
+
function projectFrom({ file, schema }, folder) {
|
|
6596
|
+
const path = join(folder, file);
|
|
6597
|
+
const text = readText(path);
|
|
6598
|
+
if (text !== void 0) return {
|
|
6599
|
+
root: folder,
|
|
6600
|
+
settings: parsed(path, text, schema)
|
|
6601
|
+
};
|
|
6602
|
+
const parent = dirname(folder);
|
|
6603
|
+
return parent === folder ? void 0 : projectFrom({
|
|
6604
|
+
file,
|
|
6605
|
+
schema
|
|
6606
|
+
}, parent);
|
|
6607
|
+
}
|
|
6608
|
+
/** The nearest Project file at or above `cwd`, parsed by its schema; undefined when there is none. */
|
|
6609
|
+
function projectOf(projectFile, cwd) {
|
|
6610
|
+
return projectFrom(projectFile, resolve(cwd));
|
|
6611
|
+
}
|
|
6612
|
+
//#endregion
|
|
6613
|
+
//#region ../agent-cli/dist/tools.js
|
|
6614
|
+
const TOOL_MISSING = "TOOL_MISSING";
|
|
6615
|
+
const WINDOWS_EXTENSIONS = ".COM;.EXE;.BAT;.CMD";
|
|
6616
|
+
const NOT_EXECUTABLE = /* @__PURE__ */ new Set([
|
|
6617
|
+
"ENOENT",
|
|
6618
|
+
"ENOTDIR",
|
|
6619
|
+
"EACCES"
|
|
6620
|
+
]);
|
|
6621
|
+
function pathOf(env) {
|
|
6622
|
+
return env[Object.keys(env).find((name) => name.toUpperCase() === "PATH") ?? "PATH"] ?? "";
|
|
6623
|
+
}
|
|
6624
|
+
function extensionsOf(env) {
|
|
6625
|
+
return process.platform === "win32" ? (env.PATHEXT ?? WINDOWS_EXTENSIONS).split(";") : [""];
|
|
6626
|
+
}
|
|
6627
|
+
async function isExecutable(file) {
|
|
6628
|
+
try {
|
|
6629
|
+
await access(file, constants$1.X_OK);
|
|
6630
|
+
return true;
|
|
6631
|
+
} catch (error) {
|
|
6632
|
+
if (NOT_EXECUTABLE.has(errnoOf(error) ?? "")) return false;
|
|
6633
|
+
throw error;
|
|
6634
|
+
}
|
|
6635
|
+
}
|
|
6636
|
+
/** Where `name` is found on `PATH` from `env`, or undefined. */
|
|
6637
|
+
async function findTool(name, env) {
|
|
6638
|
+
const candidates = pathOf(env).split(delimiter).filter((folder) => folder !== "").flatMap((folder) => extensionsOf(env).map((extension) => join(folder, `${name}${extension}`)));
|
|
6639
|
+
for (const file of candidates) if (await isExecutable(file)) return file;
|
|
6640
|
+
}
|
|
6641
|
+
/** Fails with `TOOL_MISSING`, naming every missing tool at once, unless all are on `PATH`. */
|
|
6642
|
+
async function checkTools(tools, env) {
|
|
6643
|
+
const found = await Promise.all(tools.map((tool) => findTool(tool.name, env)));
|
|
6644
|
+
const missing = tools.filter((_tool, index) => found[index] === void 0);
|
|
6645
|
+
if (missing.length === 0) return;
|
|
6646
|
+
const names = missing.map(({ name }) => name).join(", ");
|
|
6647
|
+
throw new CliError({
|
|
6648
|
+
code: TOOL_MISSING,
|
|
6649
|
+
message: `${missing.length === 1 ? "a tool it runs is" : "tools it runs are"} not on PATH: ${names}`,
|
|
6650
|
+
hint: `install ${missing.length === 1 ? "it" : "them"}, then run the same command again: ${missing.map(({ install }) => install).join("; ")}`
|
|
6651
|
+
});
|
|
6652
|
+
}
|
|
6653
|
+
function toolsIn(node) {
|
|
6654
|
+
return isCommand(node) ? [...node.tools ?? []] : Object.values(node).flatMap(toolsIn);
|
|
6655
|
+
}
|
|
6656
|
+
/** One `doctor` row per distinct tool the Commands declare, with its install command as the fix. */
|
|
6657
|
+
function toolRows(commands) {
|
|
6658
|
+
return [...new Map(toolsIn(commands).map((tool) => [tool.name, tool])).values()].map(({ name, install }) => ({
|
|
6659
|
+
name,
|
|
6660
|
+
run: async (ctx) => {
|
|
6661
|
+
const file = await findTool(name, ctx.env);
|
|
6662
|
+
return file === void 0 ? {
|
|
6663
|
+
state: "problem",
|
|
6664
|
+
message: `${name} is not on PATH`,
|
|
6665
|
+
fix: install
|
|
6666
|
+
} : {
|
|
6667
|
+
state: "ok",
|
|
6668
|
+
message: `found at ${file}`
|
|
6669
|
+
};
|
|
6670
|
+
}
|
|
6671
|
+
}));
|
|
6672
|
+
}
|
|
6673
|
+
//#endregion
|
|
6674
|
+
//#region ../agent-cli/dist/spawn.js
|
|
6675
|
+
const TOOL_FAILED = "TOOL_FAILED";
|
|
6676
|
+
const STDERR_TAIL_LINES = 20;
|
|
6677
|
+
const SIGNAL_EXIT_BASE = 128;
|
|
6678
|
+
const realSpawn = (command, args, { cwd, env, signal }) => new Promise((resolve, reject) => {
|
|
6679
|
+
signal.throwIfAborted();
|
|
6680
|
+
const child = spawn(command, args, {
|
|
6681
|
+
cwd,
|
|
6682
|
+
env,
|
|
6683
|
+
stdio: [
|
|
6684
|
+
"ignore",
|
|
6685
|
+
"pipe",
|
|
6686
|
+
"pipe"
|
|
6687
|
+
]
|
|
6688
|
+
});
|
|
6689
|
+
const stdout = [];
|
|
6690
|
+
const stderr = [];
|
|
6691
|
+
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
6692
|
+
child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
6693
|
+
const kill = () => {
|
|
6694
|
+
child.kill();
|
|
6695
|
+
};
|
|
6696
|
+
signal.addEventListener("abort", kill, { once: true });
|
|
6697
|
+
child.on("error", (error) => {
|
|
6698
|
+
signal.removeEventListener("abort", kill);
|
|
6699
|
+
reject(error);
|
|
6700
|
+
});
|
|
6701
|
+
child.on("close", (code, killedBy) => {
|
|
6702
|
+
signal.removeEventListener("abort", kill);
|
|
6703
|
+
if (signal.aborted) {
|
|
6704
|
+
reject(signal.reason);
|
|
6705
|
+
return;
|
|
6706
|
+
}
|
|
6707
|
+
resolve({
|
|
6708
|
+
stdout: Buffer.concat(stdout).toString(),
|
|
6709
|
+
stderr: Buffer.concat(stderr).toString(),
|
|
6710
|
+
exitCode: code ?? SIGNAL_EXIT_BASE + (killedBy === null ? 0 : constants.signals[killedBy])
|
|
6711
|
+
});
|
|
6712
|
+
});
|
|
6713
|
+
});
|
|
6714
|
+
function failedError(command, { exitCode, stderr }) {
|
|
6715
|
+
const tail = lastLines(stderr, STDERR_TAIL_LINES);
|
|
6716
|
+
return new CliError({
|
|
6717
|
+
code: TOOL_FAILED,
|
|
6718
|
+
message: tail === "" ? `${command} exited with ${exitCode} and wrote nothing to stderr` : `${command} exited with ${exitCode}; its stderr ends:\n${tail}`,
|
|
6719
|
+
hint: `the output of ${command} above says why it failed`
|
|
6720
|
+
});
|
|
6721
|
+
}
|
|
6722
|
+
function missingError(command, cause) {
|
|
6723
|
+
return new CliError({
|
|
6724
|
+
code: TOOL_MISSING,
|
|
6725
|
+
message: `${command} could not be started: it is not on PATH`,
|
|
6726
|
+
hint: `install ${command}, then run the same command again`
|
|
6727
|
+
}, { cause });
|
|
6728
|
+
}
|
|
6729
|
+
/** `ctx.spawn`: `spawn` bound to the run, its start failures classified by errno. */
|
|
6730
|
+
function contextSpawn(run, defaults, callerLine) {
|
|
6731
|
+
return async (command, args = [], options = {}) => {
|
|
6732
|
+
let result;
|
|
6733
|
+
try {
|
|
6734
|
+
result = await run(command, args, {
|
|
6735
|
+
cwd: options.cwd ?? defaults.cwd,
|
|
6736
|
+
env: options.env ?? defaults.env,
|
|
6737
|
+
signal: defaults.signal
|
|
6738
|
+
});
|
|
6739
|
+
} catch (error) {
|
|
6740
|
+
if (isMissingFile(error)) throw missingError(command, error);
|
|
6741
|
+
throw classifyErrno(error, {
|
|
6742
|
+
path: command,
|
|
6743
|
+
callerLine,
|
|
6744
|
+
env: defaults.env
|
|
6745
|
+
});
|
|
6746
|
+
}
|
|
6747
|
+
if (options.check === true && result.exitCode !== 0) throw failedError(command, result);
|
|
6748
|
+
return result;
|
|
6749
|
+
};
|
|
6750
|
+
}
|
|
6751
|
+
//#endregion
|
|
6752
|
+
//#region ../agent-cli/dist/exit-classes.js
|
|
6753
|
+
const SIGNAL_EXIT_CODES = {
|
|
6754
|
+
SIGINT: 130,
|
|
6755
|
+
SIGTERM: 143
|
|
6756
|
+
};
|
|
6757
|
+
const EXIT_CODES = {
|
|
6758
|
+
caller: 1,
|
|
6759
|
+
operator: 2,
|
|
6760
|
+
source: 3,
|
|
6761
|
+
wait: 75,
|
|
6762
|
+
bug: 70
|
|
6763
|
+
};
|
|
6764
|
+
/** The codes the Runtime and its built-in Commands raise, each with the class that must act on it. */
|
|
6765
|
+
function runtimeCodes() {
|
|
6766
|
+
return {
|
|
6767
|
+
[INVALID_INPUT]: "caller",
|
|
6768
|
+
[UNKNOWN_OPTION]: "caller",
|
|
6769
|
+
[UNKNOWN_COMMAND]: "caller",
|
|
6770
|
+
[UNKNOWN_FIELD]: "caller",
|
|
6771
|
+
[CONFIRMATION_REQUIRED]: "caller",
|
|
6772
|
+
[CONFIRMATION_MISMATCH]: "caller",
|
|
6773
|
+
[NOT_CONFIRMED]: "operator",
|
|
6774
|
+
[OUTPUT_EXISTS]: "caller",
|
|
6775
|
+
[INTERRUPTED]: "interrupted",
|
|
6776
|
+
[DEADLINE_EXCEEDED]: "wait",
|
|
6777
|
+
[INTERNAL_CODE]: "bug",
|
|
6778
|
+
[PERMISSION_DENIED]: "operator",
|
|
6779
|
+
[DISK_FULL]: "operator",
|
|
6780
|
+
[NOT_FOUND]: "caller",
|
|
6781
|
+
[TOOL_FAILED]: "operator",
|
|
6782
|
+
[TOOL_MISSING]: "operator",
|
|
6783
|
+
[PROJECT_INVALID]: "operator",
|
|
6784
|
+
[DOCTOR_PROBLEMS]: "operator",
|
|
6785
|
+
[NOT_GLOBAL_INSTALL]: "operator",
|
|
6786
|
+
[REGISTRY_UNREACHABLE]: "operator",
|
|
6787
|
+
[REGISTRY_AUTH]: "operator",
|
|
6788
|
+
[INSTALL_FAILED]: "operator",
|
|
6789
|
+
[NPM_FAILED]: "operator",
|
|
6790
|
+
[NO_LATEST]: "source"
|
|
6791
|
+
};
|
|
6792
|
+
}
|
|
6793
|
+
/** The codes of `errors` that the Runtime already declares, which a CLI cannot redeclare. */
|
|
6794
|
+
function runtimeCodesIn(errors) {
|
|
6795
|
+
return Object.keys(errors).filter((code) => Object.hasOwn(runtimeCodes(), code));
|
|
6796
|
+
}
|
|
6797
|
+
function exitClassOfOne(error, errors) {
|
|
6798
|
+
if (!(error instanceof CliError)) return "bug";
|
|
6799
|
+
if (error instanceof UsageError) return "caller";
|
|
6800
|
+
return runtimeCodes()[error.code] ?? errors[error.code]?.class ?? "bug";
|
|
6801
|
+
}
|
|
6802
|
+
function isBug(error, errors) {
|
|
6803
|
+
return exitClassOfOne(error, errors) === "bug";
|
|
6804
|
+
}
|
|
6805
|
+
const ACT_FIRST = [
|
|
6806
|
+
"caller",
|
|
6807
|
+
"operator",
|
|
6808
|
+
"source",
|
|
6809
|
+
"wait",
|
|
6810
|
+
"interrupted"
|
|
6811
|
+
];
|
|
6812
|
+
function exitClassOf(error, errors) {
|
|
6813
|
+
const classes = errorsOf(error).map((each) => exitClassOfOne(each, errors));
|
|
6814
|
+
if (classes.includes("bug")) return "bug";
|
|
6815
|
+
return ACT_FIRST.find((exitClass) => classes.includes(exitClass)) ?? "bug";
|
|
6816
|
+
}
|
|
6817
|
+
/**
|
|
6818
|
+
* The classes other than bug and interrupt that a run of `cli` can end in: those of the Runtime's
|
|
6819
|
+
* codes it can reach, and those of its own table.
|
|
6820
|
+
*/
|
|
6821
|
+
function reachableExitClasses(cli) {
|
|
6822
|
+
const runtime = [
|
|
6823
|
+
"caller",
|
|
6824
|
+
"operator",
|
|
6825
|
+
"wait",
|
|
6826
|
+
...cli.commands !== void 0 && cli.install !== void 0 ? ["source"] : []
|
|
6827
|
+
];
|
|
6828
|
+
const own = Object.values(cli.errors ?? {}).map((declaration) => declaration.class);
|
|
6829
|
+
return /* @__PURE__ */ new Set([...runtime, ...own]);
|
|
6830
|
+
}
|
|
6831
|
+
/** The signal named by an interrupt's abort reason; any other reason counts as Ctrl-C. */
|
|
6832
|
+
function stopSignalOf(reason) {
|
|
6833
|
+
return reason === "SIGTERM" ? "SIGTERM" : "SIGINT";
|
|
6834
|
+
}
|
|
6835
|
+
/**
|
|
6836
|
+
* The exit code of a run that failed with `error`, given the CLI's error table; `stop` is the
|
|
6837
|
+
* signal that interrupted it, if any.
|
|
6838
|
+
*/
|
|
6839
|
+
function exitCodeOf(error, errors, stop) {
|
|
6840
|
+
const exitClass = exitClassOf(error, errors);
|
|
6841
|
+
return exitClass === "interrupted" ? SIGNAL_EXIT_CODES[stopSignalOf(stop?.reason)] : EXIT_CODES[exitClass];
|
|
6842
|
+
}
|
|
6843
|
+
//#endregion
|
|
6844
|
+
//#region ../agent-cli/dist/help.js
|
|
6845
|
+
function table(rows) {
|
|
6846
|
+
const width = Math.max(...rows.map(([left]) => left.length));
|
|
6847
|
+
return rows.map(([left, right]) => right === "" ? ` ${left}` : ` ${left.padEnd(width)} ${right}`);
|
|
6848
|
+
}
|
|
6849
|
+
function section(title, rows, style) {
|
|
6850
|
+
return rows.length === 0 ? [] : [
|
|
6851
|
+
"",
|
|
6852
|
+
style.bold(`${title}:`),
|
|
6853
|
+
...table(rows)
|
|
6854
|
+
];
|
|
6855
|
+
}
|
|
6856
|
+
function summaryOf(node) {
|
|
6857
|
+
return node.kind === "command" ? node.description : `group: ${Object.keys(node.commands).join(", ")}`;
|
|
6858
|
+
}
|
|
6859
|
+
function commandRows({ commands }) {
|
|
6860
|
+
return Object.entries(commands).map(([key, node]) => [key, summaryOf(node)]);
|
|
6861
|
+
}
|
|
6862
|
+
function lines(...parts) {
|
|
6863
|
+
return `${parts.join("\n")}\n`;
|
|
6864
|
+
}
|
|
6865
|
+
function listingLines(heading, group, style) {
|
|
6866
|
+
return [
|
|
6867
|
+
style.bold(heading),
|
|
6868
|
+
...section("Commands", commandRows(group), style),
|
|
6869
|
+
"",
|
|
6870
|
+
`Run \`${group.commandLine} <command> --help\` for its examples and input.`
|
|
6871
|
+
];
|
|
6872
|
+
}
|
|
6873
|
+
const EXIT_MEANINGS = {
|
|
6874
|
+
caller: "the caller fixes its command",
|
|
6875
|
+
operator: "the operator acts on their machine",
|
|
6876
|
+
source: "someone at the source acts",
|
|
6877
|
+
wait: "nobody acts: wait, then run it again"
|
|
6878
|
+
};
|
|
6879
|
+
function exitCodeRows(cli) {
|
|
6880
|
+
return [
|
|
6881
|
+
["0", "done"],
|
|
6882
|
+
...[...[...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]),
|
|
6883
|
+
[Object.values(SIGNAL_EXIT_CODES).join(", "), "interrupted"]
|
|
6884
|
+
];
|
|
6885
|
+
}
|
|
6886
|
+
function firstExample(node) {
|
|
6887
|
+
if (node.kind === "command") return node.examples.slice(0, 1);
|
|
6888
|
+
return Object.values(node.commands).flatMap(firstExample).slice(0, 1);
|
|
6889
|
+
}
|
|
6890
|
+
function rootExamples(root) {
|
|
6891
|
+
if (root.kind === "command") return [...root.examples];
|
|
6892
|
+
const { doctor, ...others } = root.commands;
|
|
6893
|
+
return [...Object.values(others), ...doctor === void 0 ? [] : [doctor]].flatMap(firstExample);
|
|
6894
|
+
}
|
|
6895
|
+
function examplesSection(examples, style) {
|
|
6896
|
+
return section("Examples", examples.map(({ command, note }) => [command, note]), style);
|
|
6897
|
+
}
|
|
6898
|
+
/** The CLI's own help: a listing or its single Command's help, ending with exit codes and examples. */
|
|
6899
|
+
function cliHelp(cli, root, style) {
|
|
6900
|
+
return lines(...root.kind === "command" ? commandLines(root, style, { examples: false }) : listingLines(`${root.commandLine} ${cli.version}`, root, style), ...section("Exit codes", exitCodeRows(cli), style), ...examplesSection(rootExamples(root), style));
|
|
6901
|
+
}
|
|
6902
|
+
function groupHelp(group, style) {
|
|
6903
|
+
return lines(...listingLines(group.commandLine, group, style));
|
|
6904
|
+
}
|
|
6905
|
+
const EFFECT_NOTES = {
|
|
6906
|
+
read: "changes nothing",
|
|
6907
|
+
write: "changes something; show the Plan with --dry-run",
|
|
6908
|
+
destructive: "changes something hard to undo; show the Plan with --dry-run, run with --confirm",
|
|
6909
|
+
serve: "changes nothing; prints its data at once, then runs until Ctrl-C, which exits 0"
|
|
6910
|
+
};
|
|
6911
|
+
function choicesOf(field) {
|
|
6912
|
+
return field.choices === void 0 ? [] : [`one of: ${field.choices.join(", ")}`];
|
|
6913
|
+
}
|
|
6914
|
+
function defaultsOf(field) {
|
|
6915
|
+
if (field.defaultValue === void 0) return [];
|
|
6916
|
+
if (field.type !== "boolean") return [`default: ${JSON.stringify(field.defaultValue)}`];
|
|
6917
|
+
return field.defaultValue === true ? ["on by default; this flag turns it off"] : [];
|
|
6918
|
+
}
|
|
6919
|
+
function fieldRow(field, notes) {
|
|
6920
|
+
const all = [...field.required ? ["required"] : [], ...notes];
|
|
6921
|
+
const suffix = all.length === 0 ? "" : `(${all.join("; ")})`;
|
|
6922
|
+
return [field.usage, [field.description ?? "", suffix].filter((part) => part !== "").join(" ")];
|
|
6923
|
+
}
|
|
6924
|
+
function argRow(field) {
|
|
6925
|
+
return fieldRow(field, [...choicesOf(field), ...defaultsOf(field)]);
|
|
6926
|
+
}
|
|
6927
|
+
function optionRow(field) {
|
|
6928
|
+
return fieldRow(field, defaultsOf(field));
|
|
6929
|
+
}
|
|
6930
|
+
function flagRow({ usage, description }) {
|
|
6931
|
+
return [usage, description];
|
|
6932
|
+
}
|
|
6933
|
+
function commandLines(spec, style, { examples }) {
|
|
6934
|
+
return [
|
|
6935
|
+
style.bold(spec.commandLine),
|
|
6936
|
+
spec.description,
|
|
6937
|
+
...examples ? examplesSection(spec.examples, style) : [],
|
|
6938
|
+
...section("Arguments", spec.args.map(argRow), style),
|
|
6939
|
+
...section("Options", spec.options.map(optionRow), style),
|
|
6940
|
+
"",
|
|
6941
|
+
`${style.bold("Effect:")} ${spec.effect} (${EFFECT_NOTES[spec.effect]})`,
|
|
6942
|
+
...section("Standard flags", spec.standardFlags.map(flagRow), style)
|
|
6943
|
+
];
|
|
6944
|
+
}
|
|
6945
|
+
function commandHelp(spec, style) {
|
|
6946
|
+
return lines(...commandLines(spec, style, { examples: true }));
|
|
6947
|
+
}
|
|
6948
|
+
//#endregion
|
|
6949
|
+
//#region ../agent-cli/dist/help-json.js
|
|
6950
|
+
function inputSchema(schema) {
|
|
6951
|
+
return toJSONSchema(schema, {
|
|
6952
|
+
io: "input",
|
|
6953
|
+
unrepresentable: "any"
|
|
6954
|
+
});
|
|
6955
|
+
}
|
|
6956
|
+
function optionsSchema(options) {
|
|
6957
|
+
return inputSchema(object(Object.fromEntries(options.map(({ name, schema }) => [name, schema]))));
|
|
6958
|
+
}
|
|
6959
|
+
function commandData(spec) {
|
|
6960
|
+
return {
|
|
6961
|
+
name: spec.commandLine,
|
|
6962
|
+
description: spec.description,
|
|
6963
|
+
effect: spec.effect,
|
|
6964
|
+
examples: spec.examples,
|
|
6965
|
+
args: inputSchema(spec.command.args ?? object({})),
|
|
6966
|
+
options: optionsSchema(spec.options),
|
|
6967
|
+
standardFlags: spec.standardFlags.map(({ usage, description, schema }) => ({
|
|
6968
|
+
usage,
|
|
6969
|
+
description,
|
|
6970
|
+
schema: inputSchema(schema)
|
|
6971
|
+
})),
|
|
6972
|
+
output: spec.output === void 0 ? null : toJSONSchema(spec.output, { unrepresentable: "any" })
|
|
6973
|
+
};
|
|
6974
|
+
}
|
|
6975
|
+
function nodeData(node) {
|
|
6976
|
+
return node.kind === "command" ? commandData(node) : groupData(node);
|
|
6977
|
+
}
|
|
6978
|
+
function groupData(group) {
|
|
6979
|
+
return {
|
|
6980
|
+
name: group.commandLine,
|
|
6981
|
+
commands: Object.values(group.commands).map(nodeData)
|
|
6982
|
+
};
|
|
6983
|
+
}
|
|
6984
|
+
function printed$1(data) {
|
|
6985
|
+
return `${JSON.stringify(data)}\n`;
|
|
6986
|
+
}
|
|
6987
|
+
function errorsData(errors) {
|
|
6988
|
+
return Object.fromEntries(Object.entries(errors).map(([code, declaration]) => [code, {
|
|
6989
|
+
class: declaration.class,
|
|
6990
|
+
details: declaration.details === void 0 ? null : toJSONSchema(declaration.details, { unrepresentable: "any" })
|
|
6991
|
+
}]));
|
|
6992
|
+
}
|
|
6993
|
+
function cliHelpJson({ version, errors }, root) {
|
|
6994
|
+
return printed$1({
|
|
6995
|
+
name: root.commandLine,
|
|
6996
|
+
version,
|
|
6997
|
+
...nodeData(root),
|
|
6998
|
+
errors: errorsData(errors ?? {})
|
|
6999
|
+
});
|
|
7000
|
+
}
|
|
7001
|
+
function groupHelpJson(group) {
|
|
7002
|
+
return printed$1(groupData(group));
|
|
7003
|
+
}
|
|
7004
|
+
function commandHelpJson(spec) {
|
|
7005
|
+
return printed$1(commandData(spec));
|
|
7006
|
+
}
|
|
7007
|
+
//#endregion
|
|
7008
|
+
//#region ../agent-cli/dist/output.js
|
|
7009
|
+
const SUCCESS_EXIT_CODE = 0;
|
|
7010
|
+
function withTrailingNewline(text) {
|
|
7011
|
+
return text === "" || text.endsWith("\n") ? text : `${text}\n`;
|
|
7012
|
+
}
|
|
7013
|
+
function printed(value, render, { json, invocation, style, fields }) {
|
|
7014
|
+
if (!(value instanceof Page)) return json ? `${JSON.stringify(value)}\n` : withTrailingNewline(render(style));
|
|
7015
|
+
const page = printedPage(value, invocation);
|
|
7016
|
+
if (fields === void 0) return json ? `${JSON.stringify(page)}\n` : `${withTrailingNewline(render(style))}${pageFooter(page)}`;
|
|
7017
|
+
const rows = projected(page.items, fields);
|
|
7018
|
+
return json ? `${JSON.stringify({
|
|
7019
|
+
...page,
|
|
7020
|
+
items: rows
|
|
7021
|
+
})}\n` : `${renderRows(rows, fields)}${pageFooter(page)}`;
|
|
7022
|
+
}
|
|
7023
|
+
function errorText(error, format) {
|
|
7024
|
+
const text = formatRuntimeError(error, {
|
|
7025
|
+
...format,
|
|
7026
|
+
isBug: (each) => isBug(each, format.errors)
|
|
7027
|
+
});
|
|
7028
|
+
const { usageHelp, json } = format;
|
|
7029
|
+
return usageHelp !== void 0 && !json && error instanceof UsageError ? `${text}\n${usageHelp}` : text;
|
|
7030
|
+
}
|
|
7031
|
+
function stdoutOf({ value, render }, format, spill) {
|
|
7032
|
+
const text = printed(value, render, format);
|
|
7033
|
+
return value instanceof Page ? text : spill(text, format.json);
|
|
7034
|
+
}
|
|
7035
|
+
/** What a serve Command prints the moment it starts. */
|
|
7036
|
+
function startedOutput(outcome, format, spill) {
|
|
7037
|
+
return stdoutOf(outcome, {
|
|
7038
|
+
...format,
|
|
7039
|
+
fields: void 0
|
|
7040
|
+
}, spill);
|
|
7041
|
+
}
|
|
7042
|
+
/** A serve Command stopped after it started: stopping it is how it is meant to end. */
|
|
7043
|
+
const STOPPED_RESULT = {
|
|
7044
|
+
stdout: "",
|
|
7045
|
+
stderr: "",
|
|
7046
|
+
exitCode: SUCCESS_EXIT_CODE
|
|
7047
|
+
};
|
|
7048
|
+
function errorResult(error, format) {
|
|
7049
|
+
return {
|
|
7050
|
+
stdout: "",
|
|
7051
|
+
stderr: errorText(error, format),
|
|
7052
|
+
exitCode: exitCodeOf(error, format.errors, format.stop)
|
|
7053
|
+
};
|
|
7054
|
+
}
|
|
7055
|
+
async function commandResult(settled, format, spill) {
|
|
7056
|
+
if (settled.ok) try {
|
|
7057
|
+
return {
|
|
7058
|
+
stdout: await stdoutOf(settled.outcome, format, spill),
|
|
7059
|
+
stderr: "",
|
|
7060
|
+
exitCode: SUCCESS_EXIT_CODE
|
|
7061
|
+
};
|
|
7062
|
+
} catch (spillError) {
|
|
7063
|
+
return errorResult(spillError, format);
|
|
7064
|
+
}
|
|
7065
|
+
const { error, delivered } = settled;
|
|
7066
|
+
const result = errorResult(error, format);
|
|
7067
|
+
if (delivered === void 0) return result;
|
|
7068
|
+
try {
|
|
7069
|
+
return {
|
|
7070
|
+
...result,
|
|
7071
|
+
stdout: await stdoutOf(delivered, format, spill)
|
|
7072
|
+
};
|
|
7073
|
+
} catch (spillError) {
|
|
7074
|
+
const both = new AggregateError([...errorsOf(error), spillError]);
|
|
7075
|
+
return {
|
|
7076
|
+
...result,
|
|
7077
|
+
stderr: errorText(both, format)
|
|
7078
|
+
};
|
|
7079
|
+
}
|
|
7080
|
+
}
|
|
7081
|
+
function textHelp(node, root, cli, style) {
|
|
7082
|
+
if (node === root) return cliHelp(cli, node, style);
|
|
7083
|
+
return node.kind === "command" ? commandHelp(node, style) : groupHelp(node, style);
|
|
7084
|
+
}
|
|
7085
|
+
function helpOf(node, root, cli, style, json) {
|
|
7086
|
+
if (!json) return textHelp(node, root, cli, style);
|
|
7087
|
+
if (node === root) return cliHelpJson(cli, node);
|
|
7088
|
+
return node.kind === "command" ? commandHelpJson(node) : groupHelpJson(node);
|
|
7089
|
+
}
|
|
7090
|
+
function helpResult(node, root, cli, { style, json }) {
|
|
7091
|
+
return {
|
|
7092
|
+
stdout: helpOf(node, root, cli, style, json),
|
|
7093
|
+
stderr: "",
|
|
7094
|
+
exitCode: SUCCESS_EXIT_CODE
|
|
7095
|
+
};
|
|
7096
|
+
}
|
|
7097
|
+
/** The first line of `--version`: the version, and the commit and time of a stamped build. */
|
|
7098
|
+
function versionLine({ version, build }) {
|
|
7099
|
+
return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
|
|
7100
|
+
}
|
|
7101
|
+
function versionResult(cli, packageRoot) {
|
|
7102
|
+
return {
|
|
7103
|
+
stdout: `${(cli.build === void 0 ? [versionLine(cli)] : [versionLine(cli), `changelog ${join(packageRoot, "CHANGELOG.md")}`]).join("\n")}\n`,
|
|
7104
|
+
stderr: "",
|
|
7105
|
+
exitCode: SUCCESS_EXIT_CODE
|
|
7106
|
+
};
|
|
7107
|
+
}
|
|
7108
|
+
//#endregion
|
|
7109
|
+
//#region ../agent-cli/dist/skills.js
|
|
7110
|
+
/** Where the skills CLI (`npx skills add`) puts a skill, under a project and under home. */
|
|
7111
|
+
const SKILL_DIRS = [[".claude", "skills"], [".agents", "skills"]];
|
|
7112
|
+
function skillUpdateCommand(cliName) {
|
|
7113
|
+
return `npx -y skills update ${cliName}`;
|
|
7114
|
+
}
|
|
7115
|
+
const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
7116
|
+
const METADATA_VERSION = /^metadata:[ \t]*\r?\n(?:[ \t]+.*\r?\n)*?[ \t]+version:[ \t]*["']?([^"'\s]+)["']?[ \t]*$/m;
|
|
7117
|
+
/** The version a SKILL.md's frontmatter `metadata.version` states. */
|
|
7118
|
+
function skillVersionIn(skill) {
|
|
7119
|
+
const frontmatter = FRONTMATTER.exec(skill)?.[1];
|
|
7120
|
+
return frontmatter === void 0 ? void 0 : METADATA_VERSION.exec(`${frontmatter}\n`)?.[1];
|
|
7121
|
+
}
|
|
7122
|
+
function homeOf(env) {
|
|
7123
|
+
return env.HOME ?? env.USERPROFILE ?? homedir();
|
|
7124
|
+
}
|
|
7125
|
+
async function copyAt(path, cliVersion) {
|
|
7126
|
+
let skill;
|
|
7127
|
+
try {
|
|
7128
|
+
skill = await readFile(join(path, "SKILL.md"), "utf8");
|
|
7129
|
+
} catch (error) {
|
|
7130
|
+
if (isMissingFile(error)) return;
|
|
7131
|
+
return {
|
|
7132
|
+
path,
|
|
7133
|
+
version: void 0,
|
|
7134
|
+
state: "unreadable"
|
|
7135
|
+
};
|
|
7136
|
+
}
|
|
7137
|
+
const version = skillVersionIn(skill);
|
|
7138
|
+
if (version === void 0 || !isVersion(version) || !isVersion(cliVersion)) return {
|
|
7139
|
+
path,
|
|
7140
|
+
version,
|
|
7141
|
+
state: "unversioned"
|
|
7142
|
+
};
|
|
7143
|
+
return {
|
|
7144
|
+
path,
|
|
7145
|
+
version,
|
|
7146
|
+
state: standingOf(version, cliVersion)
|
|
7147
|
+
};
|
|
7148
|
+
}
|
|
7149
|
+
/** Every installed copy of the skill named after the CLI, in the project first, then under home. */
|
|
7150
|
+
async function skillCopies(skillName, cliVersion, cwd, env) {
|
|
7151
|
+
const paths = [cwd, homeOf(env)].flatMap((root) => SKILL_DIRS.map((dir) => join(root, ...dir, skillName)));
|
|
7152
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7153
|
+
const copies = [];
|
|
7154
|
+
for (const path of paths) {
|
|
7155
|
+
const copy = await copyAt(path, cliVersion);
|
|
7156
|
+
const real = copy === void 0 ? void 0 : await realpath(path);
|
|
7157
|
+
if (copy !== void 0 && real !== void 0 && !seen.has(real)) {
|
|
7158
|
+
seen.add(real);
|
|
7159
|
+
copies.push(copy);
|
|
7160
|
+
}
|
|
7161
|
+
}
|
|
7162
|
+
return copies;
|
|
7163
|
+
}
|
|
7164
|
+
//#endregion
|
|
7165
|
+
//#region ../agent-cli/dist/doctor-rows.js
|
|
7166
|
+
function ok(message) {
|
|
7167
|
+
return {
|
|
7168
|
+
state: "ok",
|
|
7169
|
+
message
|
|
7170
|
+
};
|
|
7171
|
+
}
|
|
7172
|
+
function buildRow(cli) {
|
|
7173
|
+
return {
|
|
7174
|
+
name: "build",
|
|
7175
|
+
run: () => ok(versionLine(cli))
|
|
7176
|
+
};
|
|
7177
|
+
}
|
|
7178
|
+
function nodeRow(cli) {
|
|
7179
|
+
return {
|
|
7180
|
+
name: "node",
|
|
7181
|
+
run: () => {
|
|
7182
|
+
const found = process.versions.node;
|
|
7183
|
+
const range = cli.engines?.node;
|
|
7184
|
+
if (range === void 0) return ok(`Node ${found}; ${cli.name} declares no engines.node to check it against`);
|
|
7185
|
+
const minimum = engineMinimum(range);
|
|
7186
|
+
if (minimum === void 0) return {
|
|
7187
|
+
state: "problem",
|
|
7188
|
+
message: `${cli.name} declares engines.node "${range}", which doctor cannot read: only >=X.Y.Z is supported`
|
|
7189
|
+
};
|
|
7190
|
+
if (meetsMinimum(found, minimum)) return ok(`Node ${found} meets ${range}`);
|
|
7191
|
+
const major = minimum[0];
|
|
7192
|
+
return {
|
|
7193
|
+
state: "problem",
|
|
7194
|
+
message: `${cli.name} runs on Node ${found}, older than the ${range} it needs; install Node ${minimum.join(".")} or newer and make it the default`,
|
|
7195
|
+
fix: `nvm install ${major} && nvm alias default ${major}`
|
|
7196
|
+
};
|
|
7197
|
+
}
|
|
7198
|
+
};
|
|
7199
|
+
}
|
|
7200
|
+
function projectRow(projectFile) {
|
|
7201
|
+
const { file } = projectFile;
|
|
7202
|
+
return {
|
|
7203
|
+
name: "project",
|
|
7204
|
+
run: (ctx) => {
|
|
7205
|
+
let project;
|
|
7206
|
+
try {
|
|
7207
|
+
project = projectOf(projectFile, ctx.cwd);
|
|
7208
|
+
} catch (error) {
|
|
7209
|
+
if (error instanceof CliError && error.code === "PROJECT_INVALID") return {
|
|
7210
|
+
state: "problem",
|
|
7211
|
+
message: error.message
|
|
7212
|
+
};
|
|
7213
|
+
throw error;
|
|
7214
|
+
}
|
|
7215
|
+
return ok(project === void 0 ? `none found: no ${file} at or above ${ctx.cwd}` : join(project.root, file));
|
|
7216
|
+
}
|
|
7217
|
+
};
|
|
7218
|
+
}
|
|
7219
|
+
function installRow(cli, bin) {
|
|
7220
|
+
return {
|
|
7221
|
+
name: "install",
|
|
7222
|
+
run: async (ctx) => {
|
|
7223
|
+
const { install } = cli;
|
|
7224
|
+
if (install === void 0) return ok(`${cli.name} declares no install, so it has no \`update\``);
|
|
7225
|
+
try {
|
|
7226
|
+
return ok(`a global npm install under ${(await globalInstallOf(bin, npmContext(cli, install, ctx))).prefix}`);
|
|
7227
|
+
} catch (error) {
|
|
7228
|
+
if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
|
|
7229
|
+
state: "problem",
|
|
7230
|
+
message: error.message,
|
|
7231
|
+
fix: installCommandLine(install)
|
|
7232
|
+
};
|
|
7233
|
+
throw error;
|
|
7234
|
+
}
|
|
7235
|
+
}
|
|
7236
|
+
};
|
|
7237
|
+
}
|
|
7238
|
+
function registryRow(cli) {
|
|
7239
|
+
return {
|
|
7240
|
+
name: "registry",
|
|
7241
|
+
run: async (ctx) => {
|
|
7242
|
+
const { install } = cli;
|
|
7243
|
+
if (install === void 0) return ok(`${cli.name} declares no registry to compare this build with`);
|
|
7244
|
+
const latest = await latestVersion(npmContext(cli, install, ctx));
|
|
7245
|
+
const standing = standingOf(cli.version, latest);
|
|
7246
|
+
const head = `${install.registry} has ${latest} as latest: this build`;
|
|
7247
|
+
if (standing === "behind") return {
|
|
7248
|
+
state: "problem",
|
|
7249
|
+
message: `${head} (${cli.version}) is older`,
|
|
7250
|
+
fix: `${cli.name} update`
|
|
7251
|
+
};
|
|
7252
|
+
return ok(`${head} is ${standing === "current" ? "current" : `newer (${cli.version})`}`);
|
|
7253
|
+
}
|
|
7254
|
+
};
|
|
7255
|
+
}
|
|
7256
|
+
const SKILL_STATE_NOTES = {
|
|
7257
|
+
current: "the same as this build",
|
|
7258
|
+
behind: "older than this build",
|
|
6294
7259
|
ahead: "newer than this build",
|
|
6295
7260
|
unversioned: "states no metadata.version",
|
|
6296
7261
|
unreadable: "could not be read"
|
|
@@ -6318,10 +7283,14 @@ function skillRow(cli) {
|
|
|
6318
7283
|
}
|
|
6319
7284
|
};
|
|
6320
7285
|
}
|
|
6321
|
-
/**
|
|
6322
|
-
function
|
|
7286
|
+
/** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
|
|
7287
|
+
function doctorChecks(cli, bin) {
|
|
6323
7288
|
return [
|
|
7289
|
+
buildRow(cli),
|
|
6324
7290
|
nodeRow(cli),
|
|
7291
|
+
...cli.project === void 0 ? [] : [projectRow(cli.project)],
|
|
7292
|
+
...cli.doctor ?? [],
|
|
7293
|
+
...toolRows(cli.commands),
|
|
6325
7294
|
installRow(cli, bin),
|
|
6326
7295
|
registryRow(cli),
|
|
6327
7296
|
skillRow(cli)
|
|
@@ -6329,7 +7298,8 @@ function fixedRows(cli, bin) {
|
|
|
6329
7298
|
}
|
|
6330
7299
|
//#endregion
|
|
6331
7300
|
//#region ../agent-cli/dist/doctor.js
|
|
6332
|
-
const
|
|
7301
|
+
const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
|
|
7302
|
+
const DEFAULT_DEADLINE_MS = 1e4;
|
|
6333
7303
|
const MS_PER_SECOND = 1e3;
|
|
6334
7304
|
const MIN_LABEL_WIDTH = 8;
|
|
6335
7305
|
const reportSchema = object({
|
|
@@ -6341,19 +7311,10 @@ const reportSchema = object({
|
|
|
6341
7311
|
})),
|
|
6342
7312
|
next: array(string())
|
|
6343
7313
|
});
|
|
6344
|
-
function
|
|
6345
|
-
return new Promise((_resolve, reject) => {
|
|
6346
|
-
if (signal.aborted) {
|
|
6347
|
-
reject(signal.reason);
|
|
6348
|
-
return;
|
|
6349
|
-
}
|
|
6350
|
-
signal.addEventListener("abort", () => reject(signal.reason), { once: true });
|
|
6351
|
-
});
|
|
6352
|
-
}
|
|
6353
|
-
function problemOf(check, error, { signal, deadline }) {
|
|
7314
|
+
function problemOf(check, error, { signal, deadline, ms }) {
|
|
6354
7315
|
if (!signal.aborted && deadline.aborted && error === deadline.reason) return {
|
|
6355
7316
|
state: "problem",
|
|
6356
|
-
message: `${check.name} did not answer within ${
|
|
7317
|
+
message: `${check.name} did not answer within ${ms / MS_PER_SECOND} s`
|
|
6357
7318
|
};
|
|
6358
7319
|
if (error instanceof CliError) return {
|
|
6359
7320
|
state: "problem",
|
|
@@ -6365,7 +7326,8 @@ function problemOf(check, error, { signal, deadline }) {
|
|
|
6365
7326
|
};
|
|
6366
7327
|
}
|
|
6367
7328
|
async function rowOf(check, ctx) {
|
|
6368
|
-
const
|
|
7329
|
+
const ms = check.deadlineMs ?? DEFAULT_DEADLINE_MS;
|
|
7330
|
+
const signal = ctx.deadline(ms);
|
|
6369
7331
|
let finding;
|
|
6370
7332
|
try {
|
|
6371
7333
|
finding = await Promise.race([Promise.resolve().then(() => check.run({
|
|
@@ -6375,12 +7337,16 @@ async function rowOf(check, ctx) {
|
|
|
6375
7337
|
} catch (error) {
|
|
6376
7338
|
finding = problemOf(check, error, {
|
|
6377
7339
|
signal: ctx.signal,
|
|
6378
|
-
deadline: signal
|
|
7340
|
+
deadline: signal,
|
|
7341
|
+
ms
|
|
6379
7342
|
});
|
|
6380
7343
|
}
|
|
6381
7344
|
return {
|
|
6382
|
-
|
|
6383
|
-
|
|
7345
|
+
row: {
|
|
7346
|
+
check: check.name,
|
|
7347
|
+
...finding
|
|
7348
|
+
},
|
|
7349
|
+
advisory: check.advisory === true
|
|
6384
7350
|
};
|
|
6385
7351
|
}
|
|
6386
7352
|
function nextOf(rows) {
|
|
@@ -6394,18 +7360,16 @@ function render({ rows, next }, style) {
|
|
|
6394
7360
|
return `${check.padEnd(width)}${label}${" ".repeat(stateWidth - state.length)}${message}`;
|
|
6395
7361
|
}), ...next.map((fix) => `${"next".padEnd(width)}${fix}`)].join("\n");
|
|
6396
7362
|
}
|
|
6397
|
-
function failure(cliName, report) {
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
code: "DOCTOR_PROBLEMS",
|
|
7363
|
+
function failure(cliName, problems, report) {
|
|
7364
|
+
return new CliError({
|
|
7365
|
+
code: DOCTOR_PROBLEMS,
|
|
6401
7366
|
message: `${cliName} doctor found ${problems.length === 1 ? "a problem" : `${problems.length} problems`}: ${problems.join(", ")}`,
|
|
6402
|
-
hint: report.next.length === 0 ? "each problem row says what is wrong" : "run the commands under next, then run doctor again"
|
|
6403
|
-
|
|
6404
|
-
}, report);
|
|
7367
|
+
hint: report.next.length === 0 ? "each problem row says what is wrong" : "run the commands under next, then run doctor again"
|
|
7368
|
+
});
|
|
6405
7369
|
}
|
|
6406
|
-
/** The built-in `doctor`: the fixed rows
|
|
7370
|
+
/** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
|
|
6407
7371
|
function doctorCommand(cli, bin) {
|
|
6408
|
-
return defineCommand({
|
|
7372
|
+
return defineCommand$1({
|
|
6409
7373
|
name: "doctor",
|
|
6410
7374
|
description: "check the setup and say what fixes each problem",
|
|
6411
7375
|
examples: [{
|
|
@@ -6418,120 +7382,24 @@ function doctorCommand(cli, bin) {
|
|
|
6418
7382
|
effect: "read",
|
|
6419
7383
|
output: reportSchema,
|
|
6420
7384
|
run: async (_input, ctx) => {
|
|
6421
|
-
const
|
|
6422
|
-
|
|
7385
|
+
const checked = await Promise.all(doctorChecks(cli, bin).map((check) => rowOf(check, ctx)));
|
|
7386
|
+
ctx.signal.throwIfAborted();
|
|
7387
|
+
const rows = checked.map(({ row }) => row);
|
|
6423
7388
|
const report = {
|
|
6424
7389
|
rows,
|
|
6425
7390
|
next: nextOf(rows)
|
|
6426
7391
|
};
|
|
6427
|
-
|
|
7392
|
+
const problems = checked.filter(({ row, advisory }) => row.state === "problem" && !advisory).map(({ row }) => row.check);
|
|
7393
|
+
if (problems.length > 0) {
|
|
7394
|
+
ctx.deliver(report);
|
|
7395
|
+
throw failure(cli.name, problems, report);
|
|
7396
|
+
}
|
|
6428
7397
|
return report;
|
|
6429
7398
|
},
|
|
6430
7399
|
render
|
|
6431
7400
|
});
|
|
6432
7401
|
}
|
|
6433
7402
|
//#endregion
|
|
6434
|
-
//#region ../agent-cli/dist/interrupts.js
|
|
6435
|
-
/** How long an interrupted Command may take to settle before the run ends without it. */
|
|
6436
|
-
const GRACE_MS = 3e3;
|
|
6437
|
-
const GRACE_OVER = Symbol("grace over");
|
|
6438
|
-
const INTERRUPTED = "INTERRUPTED";
|
|
6439
|
-
function interruptedError() {
|
|
6440
|
-
return new CliError({
|
|
6441
|
-
code: INTERRUPTED,
|
|
6442
|
-
message: "interrupted before it finished",
|
|
6443
|
-
hint: "run the command again",
|
|
6444
|
-
retryable: true
|
|
6445
|
-
});
|
|
6446
|
-
}
|
|
6447
|
-
function deadlineError(commandLine, ms) {
|
|
6448
|
-
return new CliError({
|
|
6449
|
-
code: "DEADLINE_EXCEEDED",
|
|
6450
|
-
message: `${commandLine} ran past its ${ms} ms deadline`,
|
|
6451
|
-
hint: "run it again later, when the service it waits on may answer faster",
|
|
6452
|
-
retryable: true
|
|
6453
|
-
});
|
|
6454
|
-
}
|
|
6455
|
-
/** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
|
|
6456
|
-
var RunControl = class {
|
|
6457
|
-
signal;
|
|
6458
|
-
#controller = new AbortController();
|
|
6459
|
-
#commandLine;
|
|
6460
|
-
#outer;
|
|
6461
|
-
#onOuterAbort = () => this.#controller.abort(interruptedError());
|
|
6462
|
-
#steps = /* @__PURE__ */ new Set();
|
|
6463
|
-
#deadlineTimers = /* @__PURE__ */ new Set();
|
|
6464
|
-
#deadlineHit;
|
|
6465
|
-
#graceTimer;
|
|
6466
|
-
#endGrace = () => {};
|
|
6467
|
-
#graceOver = new Promise((resolve) => {
|
|
6468
|
-
this.#endGrace = resolve;
|
|
6469
|
-
});
|
|
6470
|
-
constructor(commandLine, outer) {
|
|
6471
|
-
this.#commandLine = commandLine;
|
|
6472
|
-
this.#outer = outer;
|
|
6473
|
-
this.signal = this.#controller.signal;
|
|
6474
|
-
this.signal.addEventListener("abort", () => this.#startGrace(), { once: true });
|
|
6475
|
-
if (outer?.aborted === true) this.#onOuterAbort();
|
|
6476
|
-
else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
|
|
6477
|
-
}
|
|
6478
|
-
deadline(ms) {
|
|
6479
|
-
const controller = new AbortController();
|
|
6480
|
-
const timer = setTimeout(() => {
|
|
6481
|
-
this.#deadlineHit ??= deadlineError(this.#commandLine, ms);
|
|
6482
|
-
controller.abort(this.#deadlineHit);
|
|
6483
|
-
}, ms);
|
|
6484
|
-
this.#deadlineTimers.add(timer);
|
|
6485
|
-
return AbortSignal.any([this.signal, controller.signal]);
|
|
6486
|
-
}
|
|
6487
|
-
async runToEnd(work) {
|
|
6488
|
-
clearTimeout(this.#graceTimer);
|
|
6489
|
-
const step = work();
|
|
6490
|
-
const settled = step.then(() => {}, () => {});
|
|
6491
|
-
this.#steps.add(settled);
|
|
6492
|
-
try {
|
|
6493
|
-
return await step;
|
|
6494
|
-
} finally {
|
|
6495
|
-
this.#steps.delete(settled);
|
|
6496
|
-
this.#startGrace();
|
|
6497
|
-
}
|
|
6498
|
-
}
|
|
6499
|
-
/** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
|
|
6500
|
-
async settle(work) {
|
|
6501
|
-
try {
|
|
6502
|
-
const value = await Promise.race([work, this.#graceOver]);
|
|
6503
|
-
if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
|
|
6504
|
-
return value;
|
|
6505
|
-
} finally {
|
|
6506
|
-
await this.#stepsDone();
|
|
6507
|
-
this.#dispose();
|
|
6508
|
-
}
|
|
6509
|
-
}
|
|
6510
|
-
/**
|
|
6511
|
-
* The error a run ends with: an interrupt or a missed deadline outranks what the Command threw,
|
|
6512
|
-
* unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
|
|
6513
|
-
*/
|
|
6514
|
-
errorAfter(error) {
|
|
6515
|
-
if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
|
|
6516
|
-
if (this.#deadlineHit !== void 0 && !(error instanceof CliError)) return this.#deadlineHit;
|
|
6517
|
-
return error;
|
|
6518
|
-
}
|
|
6519
|
-
#startGrace() {
|
|
6520
|
-
if (this.signal.aborted && this.#steps.size === 0) {
|
|
6521
|
-
clearTimeout(this.#graceTimer);
|
|
6522
|
-
this.#graceTimer = setTimeout(() => this.#endGrace(GRACE_OVER), GRACE_MS);
|
|
6523
|
-
}
|
|
6524
|
-
}
|
|
6525
|
-
async #stepsDone() {
|
|
6526
|
-
while (this.#steps.size > 0) await Promise.all(this.#steps);
|
|
6527
|
-
}
|
|
6528
|
-
#dispose() {
|
|
6529
|
-
clearTimeout(this.#graceTimer);
|
|
6530
|
-
for (const timer of this.#deadlineTimers) clearTimeout(timer);
|
|
6531
|
-
this.#outer?.removeEventListener("abort", this.#onOuterAbort);
|
|
6532
|
-
}
|
|
6533
|
-
};
|
|
6534
|
-
//#endregion
|
|
6535
7403
|
//#region ../agent-cli/dist/upgrade-notes.js
|
|
6536
7404
|
const RELEASE_HEADING = /^## v?(\S+)/;
|
|
6537
7405
|
function releasesIn(changelog) {
|
|
@@ -6595,17 +7463,16 @@ function renderUpdate(cli, install, update) {
|
|
|
6595
7463
|
...update.next.map((command) => `next: ${command}, so the ${cli.name} skill describes ${installed.version}`)
|
|
6596
7464
|
].join("\n");
|
|
6597
7465
|
}
|
|
6598
|
-
function interruptedAfterInstall(plan
|
|
6599
|
-
return new
|
|
7466
|
+
function interruptedAfterInstall(plan) {
|
|
7467
|
+
return new CliError({
|
|
6600
7468
|
code: INTERRUPTED,
|
|
6601
7469
|
message: `interrupted, but ${plan.package}@${plan.latest} had already been installed with the npm under ${plan.prefix}, replacing ${plan.from}`,
|
|
6602
|
-
hint: "nothing to run again: run the commands under next"
|
|
6603
|
-
|
|
6604
|
-
}, update);
|
|
7470
|
+
hint: "nothing to run again: run the commands under next"
|
|
7471
|
+
});
|
|
6605
7472
|
}
|
|
6606
7473
|
/** The built-in `update`, for a CLI installed globally with npm. */
|
|
6607
7474
|
function updateCommand(cli, install, bin) {
|
|
6608
|
-
return defineCommand({
|
|
7475
|
+
return defineCommand$1({
|
|
6609
7476
|
name: "update",
|
|
6610
7477
|
description: "install the latest version of this CLI from its registry",
|
|
6611
7478
|
examples: [{
|
|
@@ -6639,7 +7506,7 @@ function updateCommand(cli, install, bin) {
|
|
|
6639
7506
|
};
|
|
6640
7507
|
ctx.signal.throwIfAborted();
|
|
6641
7508
|
ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
|
|
6642
|
-
await ctx.runToEnd(() => installGlobally(latest, npmContext(cli, install, ctx)));
|
|
7509
|
+
await ctx.runToEnd(`installing ${plan.package}@${latest}`, () => installGlobally(latest, npmContext(cli, install, ctx)));
|
|
6643
7510
|
const update = {
|
|
6644
7511
|
from,
|
|
6645
7512
|
latest,
|
|
@@ -6650,7 +7517,10 @@ function updateCommand(cli, install, bin) {
|
|
|
6650
7517
|
upgrades: await upgradeNotes(plan.packageDir, from, latest),
|
|
6651
7518
|
next: [skillUpdateCommand(cli.name)]
|
|
6652
7519
|
};
|
|
6653
|
-
if (ctx.signal.aborted)
|
|
7520
|
+
if (ctx.signal.aborted) {
|
|
7521
|
+
ctx.deliver(update);
|
|
7522
|
+
throw interruptedAfterInstall(plan);
|
|
7523
|
+
}
|
|
6654
7524
|
return update;
|
|
6655
7525
|
},
|
|
6656
7526
|
render: (update) => renderUpdate(cli, install, update),
|
|
@@ -6666,263 +7536,208 @@ function builtInCommands(cli, bin) {
|
|
|
6666
7536
|
...cli.install === void 0 ? {} : { update: updateCommand(cli, cli.install, bin) }
|
|
6667
7537
|
};
|
|
6668
7538
|
}
|
|
6669
|
-
|
|
7539
|
+
//#endregion
|
|
7540
|
+
//#region ../agent-cli/dist/agent-cli.js
|
|
7541
|
+
function checkOwnCodes(cli) {
|
|
7542
|
+
const redeclared = runtimeCodesIn(cli.errors ?? {});
|
|
7543
|
+
if (redeclared.length > 0) throw new Error(`${cli.name} declares ${redeclared.join(", ")}, an error code the Runtime already declares`);
|
|
7544
|
+
}
|
|
7545
|
+
function defineCli$1(cli) {
|
|
7546
|
+
checkOwnCodes(cli);
|
|
6670
7547
|
if (cli.commands === void 0) return cli;
|
|
6671
7548
|
const { commands } = cli;
|
|
6672
7549
|
const taken = Object.keys(builtInCommands(cli, void 0)).filter((name) => commands[name] !== void 0);
|
|
6673
7550
|
if (taken.length > 0) throw new Error(`${cli.name} defines ${taken.join(", ")}, a built-in Command it cannot replace`);
|
|
6674
|
-
return cli;
|
|
6675
|
-
}
|
|
6676
|
-
//#endregion
|
|
6677
|
-
//#region ../agent-cli/dist/command-spec.js
|
|
6678
|
-
function commandSpec(commandLine, command) {
|
|
6679
|
-
const list = command.rows === void 0 ? void 0 : listSpec(command.rows);
|
|
6680
|
-
return {
|
|
6681
|
-
kind: "command",
|
|
6682
|
-
commandLine,
|
|
6683
|
-
command,
|
|
6684
|
-
description: command.description,
|
|
6685
|
-
effect: command.effect,
|
|
6686
|
-
examples: command.examples,
|
|
6687
|
-
args: argFields(command.args),
|
|
6688
|
-
options: optionFields(command.options),
|
|
6689
|
-
standardFlags: standardFlagsFor(command.effect, list),
|
|
6690
|
-
list,
|
|
6691
|
-
output: list?.output ?? command.output
|
|
6692
|
-
};
|
|
6693
|
-
}
|
|
6694
|
-
function groupSpec(commandLine, commands) {
|
|
6695
|
-
return {
|
|
6696
|
-
kind: "group",
|
|
6697
|
-
commandLine,
|
|
6698
|
-
commands: Object.fromEntries(Object.entries(commands).map(([key, node]) => [key, specOf(`${commandLine} ${key}`, node)]))
|
|
6699
|
-
};
|
|
6700
|
-
}
|
|
6701
|
-
function specOf(commandLine, node) {
|
|
6702
|
-
return isCommand(node) ? commandSpec(commandLine, node) : groupSpec(commandLine, node);
|
|
6703
|
-
}
|
|
6704
|
-
//#endregion
|
|
6705
|
-
//#region ../agent-cli/dist/confirm.js
|
|
6706
|
-
function checkConfirmation({ invocation, commandLine, given }, expected) {
|
|
6707
|
-
if (given === expected) return;
|
|
6708
|
-
const hint = `to confirm, run \`${commandLineWith(invocation, CONFIRM_FLAG.flag, expected)}\``;
|
|
6709
|
-
if (given === void 0) throw new UsageError({
|
|
6710
|
-
code: "CONFIRMATION_REQUIRED",
|
|
6711
|
-
message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
|
|
6712
|
-
hint
|
|
6713
|
-
});
|
|
6714
|
-
throw new UsageError({
|
|
6715
|
-
code: "CONFIRMATION_MISMATCH",
|
|
6716
|
-
message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
|
|
6717
|
-
hint
|
|
6718
|
-
});
|
|
6719
|
-
}
|
|
6720
|
-
//#endregion
|
|
6721
|
-
//#region ../agent-cli/dist/execute.js
|
|
6722
|
-
function outcomeOf(value, render) {
|
|
6723
|
-
return {
|
|
6724
|
-
value,
|
|
6725
|
-
render: (style) => render(value, style)
|
|
6726
|
-
};
|
|
6727
|
-
}
|
|
6728
|
-
/** How the Command prints data it fails with, which is the data its `run` or `apply` returns. */
|
|
6729
|
-
function renderOf(command) {
|
|
6730
|
-
return (data, style) => command.render(data, style);
|
|
6731
|
-
}
|
|
6732
|
-
async function execute(command, input, ctx, { dryRun, confirm, page, invocation, commandLine }) {
|
|
6733
|
-
if (command.rows !== void 0) return outcomeOf(await command.run({
|
|
6734
|
-
...input,
|
|
6735
|
-
...page
|
|
6736
|
-
}, ctx), (data, style) => command.render(data, style));
|
|
6737
|
-
if (command.effect === "read") return outcomeOf(await command.run(input, ctx), (data, style) => command.render(data, style));
|
|
6738
|
-
const plan = await command.plan(input, ctx);
|
|
6739
|
-
if (dryRun) return outcomeOf(plan, (value, style) => command.renderPlan(value, style));
|
|
6740
|
-
if (command.effect === "destructive") checkConfirmation({
|
|
6741
|
-
invocation,
|
|
6742
|
-
commandLine,
|
|
6743
|
-
given: confirm
|
|
6744
|
-
}, command.confirm(plan));
|
|
6745
|
-
return outcomeOf(await command.apply(plan, ctx), (data, style) => command.render(data, style));
|
|
6746
|
-
}
|
|
6747
|
-
//#endregion
|
|
6748
|
-
//#region ../agent-cli/dist/help.js
|
|
6749
|
-
function table(rows) {
|
|
6750
|
-
const width = Math.max(...rows.map(([left]) => left.length));
|
|
6751
|
-
return rows.map(([left, right]) => right === "" ? ` ${left}` : ` ${left.padEnd(width)} ${right}`);
|
|
6752
|
-
}
|
|
6753
|
-
function section(title, rows, style) {
|
|
6754
|
-
return rows.length === 0 ? [] : [
|
|
6755
|
-
"",
|
|
6756
|
-
style.bold(`${title}:`),
|
|
6757
|
-
...table(rows)
|
|
6758
|
-
];
|
|
6759
|
-
}
|
|
6760
|
-
function summaryOf(node) {
|
|
6761
|
-
return node.kind === "command" ? node.description : `group: ${Object.keys(node.commands).join(", ")}`;
|
|
6762
|
-
}
|
|
6763
|
-
function commandRows({ commands }) {
|
|
6764
|
-
return Object.entries(commands).map(([key, node]) => [key, summaryOf(node)]);
|
|
6765
|
-
}
|
|
6766
|
-
function lines(...parts) {
|
|
6767
|
-
return `${parts.join("\n")}\n`;
|
|
6768
|
-
}
|
|
6769
|
-
function listing(heading, group, style) {
|
|
6770
|
-
return lines(style.bold(heading), ...section("Commands", commandRows(group), style), "", `Run \`${group.commandLine} <command> --help\` for its examples and input.`);
|
|
6771
|
-
}
|
|
6772
|
-
function cliHelp(version, root, style) {
|
|
6773
|
-
return listing(`${root.commandLine} ${version}`, root, style);
|
|
6774
|
-
}
|
|
6775
|
-
function groupHelp(group, style) {
|
|
6776
|
-
return listing(group.commandLine, group, style);
|
|
6777
|
-
}
|
|
6778
|
-
const EFFECT_NOTES = {
|
|
6779
|
-
read: "changes nothing",
|
|
6780
|
-
write: "changes something; show the Plan with --dry-run",
|
|
6781
|
-
destructive: "changes something hard to undo; show the Plan with --dry-run, run with --confirm"
|
|
6782
|
-
};
|
|
6783
|
-
function choicesOf(field) {
|
|
6784
|
-
return field.choices === void 0 ? [] : [`one of: ${field.choices.join(", ")}`];
|
|
6785
|
-
}
|
|
6786
|
-
function defaultsOf(field) {
|
|
6787
|
-
if (field.defaultValue === void 0) return [];
|
|
6788
|
-
if (field.type !== "boolean") return [`default: ${JSON.stringify(field.defaultValue)}`];
|
|
6789
|
-
return field.defaultValue === true ? ["on by default; this flag turns it off"] : [];
|
|
6790
|
-
}
|
|
6791
|
-
function fieldRow(field, notes) {
|
|
6792
|
-
const all = [...field.required ? ["required"] : [], ...notes];
|
|
6793
|
-
const suffix = all.length === 0 ? "" : `(${all.join("; ")})`;
|
|
6794
|
-
return [field.usage, [field.description ?? "", suffix].filter((part) => part !== "").join(" ")];
|
|
6795
|
-
}
|
|
6796
|
-
function argRow(field) {
|
|
6797
|
-
return fieldRow(field, [...choicesOf(field), ...defaultsOf(field)]);
|
|
6798
|
-
}
|
|
6799
|
-
function optionRow(field) {
|
|
6800
|
-
return fieldRow(field, defaultsOf(field));
|
|
6801
|
-
}
|
|
6802
|
-
function flagRow({ usage, description }) {
|
|
6803
|
-
return [usage, description];
|
|
6804
|
-
}
|
|
6805
|
-
function commandHelp(spec, style) {
|
|
6806
|
-
return lines(style.bold(spec.commandLine), spec.description, ...section("Examples", spec.examples.map(({ command, note }) => [command, note]), style), ...section("Arguments", spec.args.map(argRow), style), ...section("Options", spec.options.map(optionRow), style), "", `${style.bold("Effect:")} ${spec.effect} (${EFFECT_NOTES[spec.effect]})`, ...section("Standard flags", spec.standardFlags.map(flagRow), style));
|
|
6807
|
-
}
|
|
6808
|
-
//#endregion
|
|
6809
|
-
//#region ../agent-cli/dist/help-json.js
|
|
6810
|
-
function inputSchema(schema) {
|
|
6811
|
-
return toJSONSchema(schema, {
|
|
6812
|
-
io: "input",
|
|
6813
|
-
unrepresentable: "any"
|
|
6814
|
-
});
|
|
6815
|
-
}
|
|
6816
|
-
function optionsSchema(options) {
|
|
6817
|
-
return inputSchema(object(Object.fromEntries(options.map(({ name, schema }) => [name, schema]))));
|
|
7551
|
+
return cli;
|
|
6818
7552
|
}
|
|
6819
|
-
|
|
7553
|
+
/**
|
|
7554
|
+
* Declares the CLI's ports and its Project file's settings once; the `defineCommand` and
|
|
7555
|
+
* `defineCli` it returns type `ctx.ports` and `ctx.project`.
|
|
7556
|
+
*/
|
|
7557
|
+
function agentCli() {
|
|
6820
7558
|
return {
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
effect: spec.effect,
|
|
6824
|
-
examples: spec.examples,
|
|
6825
|
-
args: inputSchema(spec.command.args ?? object({})),
|
|
6826
|
-
options: optionsSchema(spec.options),
|
|
6827
|
-
standardFlags: spec.standardFlags.map(({ usage, description, schema }) => ({
|
|
6828
|
-
usage,
|
|
6829
|
-
description,
|
|
6830
|
-
schema: inputSchema(schema)
|
|
6831
|
-
})),
|
|
6832
|
-
output: spec.output === void 0 ? null : toJSONSchema(spec.output, { unrepresentable: "any" })
|
|
7559
|
+
defineCommand: defineCommandFor(),
|
|
7560
|
+
defineCli: defineCli$1
|
|
6833
7561
|
};
|
|
6834
7562
|
}
|
|
6835
|
-
|
|
6836
|
-
|
|
7563
|
+
//#endregion
|
|
7564
|
+
//#region ../agent-cli/dist/error-table.js
|
|
7565
|
+
/** Keeps each code's literal class and `details` schema, so `cliError` can type its throw sites. */
|
|
7566
|
+
function defineErrors(table) {
|
|
7567
|
+
return table;
|
|
6837
7568
|
}
|
|
6838
|
-
|
|
7569
|
+
/** A `CliError` for a code of `table`, whose `details` must match the code's schema. */
|
|
7570
|
+
function cliError(_table, code, init, options) {
|
|
7571
|
+
return new CliError({
|
|
7572
|
+
...init,
|
|
7573
|
+
code
|
|
7574
|
+
}, options);
|
|
7575
|
+
}
|
|
7576
|
+
//#endregion
|
|
7577
|
+
//#region ../agent-cli/dist/command-spec.js
|
|
7578
|
+
function declaredOf(command) {
|
|
7579
|
+
const planned = command.effect === "write" || command.effect === "destructive";
|
|
6839
7580
|
return {
|
|
6840
|
-
|
|
6841
|
-
|
|
7581
|
+
outputs: planned && command.outputs !== void 0,
|
|
7582
|
+
cost: planned && command.cost !== void 0
|
|
6842
7583
|
};
|
|
6843
7584
|
}
|
|
6844
|
-
function
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
7585
|
+
function commandSpec(commandLine, command) {
|
|
7586
|
+
const list = command.rows === void 0 ? void 0 : listSpec(command.rows);
|
|
7587
|
+
return {
|
|
7588
|
+
kind: "command",
|
|
7589
|
+
commandLine,
|
|
7590
|
+
command,
|
|
7591
|
+
description: command.description,
|
|
7592
|
+
effect: command.effect,
|
|
7593
|
+
examples: command.examples,
|
|
7594
|
+
args: argFields(command.args),
|
|
7595
|
+
options: optionFields(command.options),
|
|
7596
|
+
standardFlags: standardFlagsFor(command.effect, declaredOf(command), list),
|
|
7597
|
+
list,
|
|
7598
|
+
output: list?.output ?? command.output
|
|
7599
|
+
};
|
|
6853
7600
|
}
|
|
6854
|
-
function
|
|
6855
|
-
return
|
|
7601
|
+
function groupSpec(commandLine, commands) {
|
|
7602
|
+
return {
|
|
7603
|
+
kind: "group",
|
|
7604
|
+
commandLine,
|
|
7605
|
+
commands: Object.fromEntries(Object.entries(commands).map(([key, node]) => [key, specOf(`${commandLine} ${key}`, node)]))
|
|
7606
|
+
};
|
|
6856
7607
|
}
|
|
6857
|
-
function
|
|
6858
|
-
return
|
|
7608
|
+
function specOf(commandLine, node) {
|
|
7609
|
+
return isCommand(node) ? commandSpec(commandLine, node) : groupSpec(commandLine, node);
|
|
6859
7610
|
}
|
|
6860
7611
|
//#endregion
|
|
6861
|
-
//#region ../agent-cli/dist/
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
7612
|
+
//#region ../agent-cli/dist/execute.js
|
|
7613
|
+
function outcomeOf(value, render) {
|
|
7614
|
+
return {
|
|
7615
|
+
value,
|
|
7616
|
+
render: (style) => render(value, style)
|
|
7617
|
+
};
|
|
6867
7618
|
}
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
if (fields === void 0) return json ? `${JSON.stringify(page)}\n` : `${withTrailingNewline(render(style))}${pageFooter(page)}`;
|
|
6872
|
-
const rows = projected(page.items, fields);
|
|
6873
|
-
return json ? `${JSON.stringify({
|
|
6874
|
-
...page,
|
|
6875
|
-
items: rows
|
|
6876
|
-
})}\n` : `${renderRows(rows, fields)}${pageFooter(page)}`;
|
|
7619
|
+
/** Delivered data as the Command prints it, which is the data its `run` or `apply` returns. */
|
|
7620
|
+
function deliveredOutcome(command, data) {
|
|
7621
|
+
return outcomeOf(data, command.render);
|
|
6877
7622
|
}
|
|
6878
|
-
function
|
|
7623
|
+
function dryRunOutcome(command, plan) {
|
|
7624
|
+
const renderPlan = (style) => command.renderPlan(plan, style);
|
|
7625
|
+
if (command.outputs === void 0 && command.cost === void 0) return {
|
|
7626
|
+
value: plan,
|
|
7627
|
+
render: renderPlan
|
|
7628
|
+
};
|
|
7629
|
+
const outputs = command.outputs?.(plan);
|
|
7630
|
+
const cost = command.cost?.(plan);
|
|
6879
7631
|
return {
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
7632
|
+
value: {
|
|
7633
|
+
plan,
|
|
7634
|
+
...outputs === void 0 ? {} : { outputs },
|
|
7635
|
+
...command.cost === void 0 ? {} : { cost: cost ?? null }
|
|
7636
|
+
},
|
|
7637
|
+
render: (style) => [
|
|
7638
|
+
renderPlan(style),
|
|
7639
|
+
...outputs === void 0 ? [] : ["outputs:", ...outputs.map((path) => ` ${path}`)],
|
|
7640
|
+
...cost === void 0 ? [] : [`cost: ${cost}`]
|
|
7641
|
+
].join("\n")
|
|
6883
7642
|
};
|
|
6884
7643
|
}
|
|
6885
|
-
function
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
7644
|
+
async function execute(command, input, ctx, { dryRun, confirm, yes, force, page, invocation, commandLine, started }) {
|
|
7645
|
+
await checkTools(command.tools ?? [], ctx.env);
|
|
7646
|
+
if (command.rows !== void 0) return outcomeOf(await command.run({
|
|
7647
|
+
...input,
|
|
7648
|
+
...page
|
|
7649
|
+
}, ctx), (data, style) => command.render(data, style));
|
|
7650
|
+
if (command.effect === "read") return outcomeOf(await command.run(input, ctx), (data, style) => command.render(data, style));
|
|
7651
|
+
if (command.effect === "serve") {
|
|
7652
|
+
await started(outcomeOf(await command.start(input, ctx), (value, style) => command.render(value, style)));
|
|
7653
|
+
return untilAborted(ctx.signal);
|
|
6893
7654
|
}
|
|
6894
|
-
const
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
};
|
|
7655
|
+
const plan = await command.plan(input, ctx);
|
|
7656
|
+
if (dryRun) return dryRunOutcome(command, plan);
|
|
7657
|
+
await checkOutputs({
|
|
7658
|
+
invocation,
|
|
7659
|
+
commandLine,
|
|
7660
|
+
force,
|
|
7661
|
+
files: ctx.files
|
|
7662
|
+
}, command.outputs?.(plan));
|
|
7663
|
+
checkCost({
|
|
7664
|
+
invocation,
|
|
7665
|
+
commandLine,
|
|
7666
|
+
yes
|
|
7667
|
+
}, command.cost?.(plan));
|
|
7668
|
+
if (command.effect === "destructive") checkConfirmation({
|
|
7669
|
+
invocation,
|
|
7670
|
+
commandLine,
|
|
7671
|
+
given: confirm
|
|
7672
|
+
}, command.confirm(plan));
|
|
7673
|
+
return outcomeOf(await command.apply(plan, ctx), (data, style) => command.render(data, style));
|
|
6902
7674
|
}
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
7675
|
+
//#endregion
|
|
7676
|
+
//#region ../agent-cli/dist/files.js
|
|
7677
|
+
const TEMP_FILE = /^\..+\.agent-cli-(\d+)\.tmp$/;
|
|
7678
|
+
function tempPathOf(path) {
|
|
7679
|
+
return join(dirname(path), `.${basename(path)}.agent-cli-${process.pid}.tmp`);
|
|
7680
|
+
}
|
|
7681
|
+
function isAlive(pid) {
|
|
7682
|
+
try {
|
|
7683
|
+
process.kill(pid, 0);
|
|
7684
|
+
return true;
|
|
7685
|
+
} catch (error) {
|
|
7686
|
+
return errnoOf(error) === "EPERM";
|
|
6907
7687
|
}
|
|
6908
|
-
if (node.kind === "command") return commandHelp(node, style);
|
|
6909
|
-
return node === root ? cliHelp(cli.version, node, style) : groupHelp(node, style);
|
|
6910
7688
|
}
|
|
6911
|
-
function
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
};
|
|
7689
|
+
async function sweep(folder) {
|
|
7690
|
+
const dead = (await readdir(folder)).filter((name) => {
|
|
7691
|
+
const pid = TEMP_FILE.exec(name)?.[1];
|
|
7692
|
+
return pid !== void 0 && !isAlive(Number(pid));
|
|
7693
|
+
});
|
|
7694
|
+
await Promise.all(dead.map((name) => rm(join(folder, name), { force: true })));
|
|
6917
7695
|
}
|
|
6918
|
-
|
|
7696
|
+
/** Real files for one run: the first write to each folder sweeps the temp files of dead writers. */
|
|
7697
|
+
function realFiles() {
|
|
7698
|
+
const swept = /* @__PURE__ */ new Set();
|
|
6919
7699
|
return {
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
7700
|
+
exists: async (path) => {
|
|
7701
|
+
try {
|
|
7702
|
+
await access(path);
|
|
7703
|
+
return true;
|
|
7704
|
+
} catch (error) {
|
|
7705
|
+
if (isMissingFile(error)) return false;
|
|
7706
|
+
throw error;
|
|
7707
|
+
}
|
|
7708
|
+
},
|
|
7709
|
+
write: async (path, data) => {
|
|
7710
|
+
const folder = dirname(path);
|
|
7711
|
+
if (!swept.has(folder)) {
|
|
7712
|
+
await sweep(folder);
|
|
7713
|
+
swept.add(folder);
|
|
7714
|
+
}
|
|
7715
|
+
const temp = tempPathOf(path);
|
|
7716
|
+
try {
|
|
7717
|
+
await writeFile(temp, data);
|
|
7718
|
+
await rename(temp, path);
|
|
7719
|
+
} catch (error) {
|
|
7720
|
+
await rm(temp, { force: true });
|
|
7721
|
+
throw error;
|
|
7722
|
+
}
|
|
7723
|
+
}
|
|
6923
7724
|
};
|
|
6924
7725
|
}
|
|
6925
7726
|
//#endregion
|
|
7727
|
+
//#region ../agent-cli/dist/package-root.js
|
|
7728
|
+
/**
|
|
7729
|
+
* The nearest folder holding a `package.json`, searched upward from the bin or, run without one,
|
|
7730
|
+
* from `cwd`; the search's start when none holds one. The bin is resolved through links first,
|
|
7731
|
+
* since npm's global bin is a link to it.
|
|
7732
|
+
*/
|
|
7733
|
+
function packageRootOf(bin, cwd) {
|
|
7734
|
+
const start = bin === void 0 ? resolve(cwd) : dirname(realpathSync(bin));
|
|
7735
|
+
for (let dir = start;; dir = dirname(dir)) {
|
|
7736
|
+
if (existsSync(join(dir, "package.json"))) return dir;
|
|
7737
|
+
if (dirname(dir) === dir) return start;
|
|
7738
|
+
}
|
|
7739
|
+
}
|
|
7740
|
+
//#endregion
|
|
6926
7741
|
//#region ../agent-cli/dist/suggest.js
|
|
6927
7742
|
const MAX_DISTANCE = 3;
|
|
6928
7743
|
const MIN_SIMILARITY = .4;
|
|
@@ -7047,40 +7862,6 @@ function tokenize(words, known) {
|
|
|
7047
7862
|
};
|
|
7048
7863
|
}
|
|
7049
7864
|
//#endregion
|
|
7050
|
-
//#region ../agent-cli/dist/usage.js
|
|
7051
|
-
function unknownError({ code, problem, commandLine, corrections, listed }) {
|
|
7052
|
-
return new UsageError({
|
|
7053
|
-
code,
|
|
7054
|
-
message: `${problem} for \`${commandLine}\``,
|
|
7055
|
-
hint: corrections.length === 0 ? `run \`${commandLine} --help\` to see the ${listed}` : `did you mean ${corrections.map((correction) => `\`${correction}\``).join(" or ")}?`
|
|
7056
|
-
});
|
|
7057
|
-
}
|
|
7058
|
-
function unknownOptionError(commandLine, invocation, flag, suggestions) {
|
|
7059
|
-
return unknownError({
|
|
7060
|
-
code: "UNKNOWN_OPTION",
|
|
7061
|
-
problem: `unknown option '${flag}'`,
|
|
7062
|
-
commandLine,
|
|
7063
|
-
corrections: suggestions.map((name) => commandLineRenaming(invocation, /* @__PURE__ */ new Map([[flag, name]]))),
|
|
7064
|
-
listed: "options"
|
|
7065
|
-
});
|
|
7066
|
-
}
|
|
7067
|
-
function unknownCommandError(commandLine, name, suggestions) {
|
|
7068
|
-
return unknownError({
|
|
7069
|
-
code: "UNKNOWN_COMMAND",
|
|
7070
|
-
problem: `unknown command '${name}'`,
|
|
7071
|
-
commandLine,
|
|
7072
|
-
corrections: suggestions.map((suggestion) => `${commandLine} ${suggestion}`),
|
|
7073
|
-
listed: "commands"
|
|
7074
|
-
});
|
|
7075
|
-
}
|
|
7076
|
-
function invalidInputError(commandLine, problem) {
|
|
7077
|
-
return new UsageError({
|
|
7078
|
-
code: "INVALID_INPUT",
|
|
7079
|
-
message: problem,
|
|
7080
|
-
hint: `run \`${commandLine} --help\` to see the expected input`
|
|
7081
|
-
});
|
|
7082
|
-
}
|
|
7083
|
-
//#endregion
|
|
7084
7865
|
//#region ../agent-cli/dist/parse-input.js
|
|
7085
7866
|
const NO_INPUT = object({});
|
|
7086
7867
|
function knownFlags(spec) {
|
|
@@ -7137,7 +7918,7 @@ function invalidInput(commandLine, invocation, unknown, otherLines) {
|
|
|
7137
7918
|
const renames = new Map(unknown.flatMap(({ flag, suggestion }) => suggestion === void 0 ? [] : [[flag, suggestion]]));
|
|
7138
7919
|
const correctable = otherLines.length === 0 && renames.size === unknown.length;
|
|
7139
7920
|
return new UsageError({
|
|
7140
|
-
code:
|
|
7921
|
+
code: INVALID_INPUT,
|
|
7141
7922
|
message: [
|
|
7142
7923
|
`invalid input for \`${commandLine}\``,
|
|
7143
7924
|
...unknown.map(unknownLine),
|
|
@@ -7190,6 +7971,8 @@ function parseCommand({ spec, words, json }, invocation) {
|
|
|
7190
7971
|
json,
|
|
7191
7972
|
dryRun: values.get(DRY_RUN_FLAG.flag) === true,
|
|
7192
7973
|
confirm: typeof confirm === "string" ? confirm : void 0,
|
|
7974
|
+
yes: values.get(YES_FLAG.flag) === true,
|
|
7975
|
+
force: values.get(FORCE_FLAG.flag) === true,
|
|
7193
7976
|
page,
|
|
7194
7977
|
fields: spec.list === void 0 ? void 0 : selectedFields(values, spec.list.rowFields, spec.commandLine)
|
|
7195
7978
|
}
|
|
@@ -7244,6 +8027,7 @@ function walk(node, words, json, invocation) {
|
|
|
7244
8027
|
return {
|
|
7245
8028
|
kind: "usage error",
|
|
7246
8029
|
error: unknownOptionError(node.commandLine, invocation, first, suggestions),
|
|
8030
|
+
node,
|
|
7247
8031
|
json
|
|
7248
8032
|
};
|
|
7249
8033
|
}
|
|
@@ -7254,6 +8038,7 @@ function unknownCommand(group, name, json) {
|
|
|
7254
8038
|
return {
|
|
7255
8039
|
kind: "usage error",
|
|
7256
8040
|
error: unknownCommandError(group.commandLine, name, suggestions),
|
|
8041
|
+
node: group,
|
|
7257
8042
|
json
|
|
7258
8043
|
};
|
|
7259
8044
|
}
|
|
@@ -7269,19 +8054,94 @@ function route(root, invocation) {
|
|
|
7269
8054
|
return walk(root, argv.filter((word, index) => !(index < options.length && word === JSON_FLAG.flag)), json, invocation);
|
|
7270
8055
|
}
|
|
7271
8056
|
//#endregion
|
|
8057
|
+
//#region ../agent-cli/dist/secrets.js
|
|
8058
|
+
function dotenvPath(cliName, env) {
|
|
8059
|
+
const config = env.XDG_CONFIG_HOME || join(env.HOME || homedir(), ".config");
|
|
8060
|
+
return join(config, cliName, ".env");
|
|
8061
|
+
}
|
|
8062
|
+
function readDotenv(path) {
|
|
8063
|
+
try {
|
|
8064
|
+
return parseEnv(readFileSync(path, "utf8"));
|
|
8065
|
+
} catch (error) {
|
|
8066
|
+
if (isMissingFile(error)) return {};
|
|
8067
|
+
throw error;
|
|
8068
|
+
}
|
|
8069
|
+
}
|
|
8070
|
+
/** Looks `name` up in the env, then in `$XDG_CONFIG_HOME/<cli>/.env`; an empty value counts as unset. */
|
|
8071
|
+
function secretOf(cliName, env, name) {
|
|
8072
|
+
const fromEnv = env[name];
|
|
8073
|
+
if (fromEnv) return {
|
|
8074
|
+
value: fromEnv,
|
|
8075
|
+
origin: { kind: "env" }
|
|
8076
|
+
};
|
|
8077
|
+
const path = dotenvPath(cliName, env);
|
|
8078
|
+
const fromFile = readDotenv(path)[name];
|
|
8079
|
+
return fromFile ? {
|
|
8080
|
+
value: fromFile,
|
|
8081
|
+
origin: {
|
|
8082
|
+
kind: "file",
|
|
8083
|
+
path
|
|
8084
|
+
}
|
|
8085
|
+
} : void 0;
|
|
8086
|
+
}
|
|
8087
|
+
//#endregion
|
|
8088
|
+
//#region ../agent-cli/dist/spill.js
|
|
8089
|
+
const STDOUT_BUDGET_CHARS = 3e4;
|
|
8090
|
+
const SPILL_RETENTION_MS = 6048e5;
|
|
8091
|
+
const PREVIEW_CHARS = 2e3;
|
|
8092
|
+
const SPILL_FILE = /^\d{4}-\d\d-\d\dT\d\d-\d\d-\d\d\.\d{3}Z-.+\.(json|txt)$/;
|
|
8093
|
+
function spillName(now, commandName, json) {
|
|
8094
|
+
return `${new Date(now).toISOString().replaceAll(":", "-")}-${commandName}.${json ? "json" : "txt"}`;
|
|
8095
|
+
}
|
|
8096
|
+
async function pruneOne(path, cutoff) {
|
|
8097
|
+
try {
|
|
8098
|
+
if ((await stat(path)).mtimeMs < cutoff) await rm(path, { force: true });
|
|
8099
|
+
} catch (error) {
|
|
8100
|
+
if (!isMissingFile(error)) throw error;
|
|
8101
|
+
}
|
|
8102
|
+
}
|
|
8103
|
+
async function pruneOld(folder, cutoff) {
|
|
8104
|
+
const names = (await readdir(folder)).filter((name) => SPILL_FILE.test(name));
|
|
8105
|
+
await Promise.all(names.map((name) => pruneOne(join(folder, name), cutoff)));
|
|
8106
|
+
}
|
|
8107
|
+
async function prepare(folder, cutoff, target) {
|
|
8108
|
+
try {
|
|
8109
|
+
await mkdir(folder, { recursive: true });
|
|
8110
|
+
await pruneOld(folder, cutoff);
|
|
8111
|
+
} catch (error) {
|
|
8112
|
+
throw classifyErrno(error, {
|
|
8113
|
+
path: folder,
|
|
8114
|
+
callerLine: target.callerLine,
|
|
8115
|
+
env: target.env
|
|
8116
|
+
});
|
|
8117
|
+
}
|
|
8118
|
+
}
|
|
8119
|
+
function tempRoot(env) {
|
|
8120
|
+
const { TMPDIR } = env;
|
|
8121
|
+
return TMPDIR === void 0 || TMPDIR === "" ? tmpdir() : TMPDIR;
|
|
8122
|
+
}
|
|
8123
|
+
function spillTo(target) {
|
|
8124
|
+
return async (printed, json) => {
|
|
8125
|
+
if (printed.length <= STDOUT_BUDGET_CHARS) return printed;
|
|
8126
|
+
const now = target.clock.now();
|
|
8127
|
+
const folder = join(tempRoot(target.env), target.cliName);
|
|
8128
|
+
await prepare(folder, now - SPILL_RETENTION_MS, target);
|
|
8129
|
+
const file = join(folder, spillName(now, target.commandName, json));
|
|
8130
|
+
await target.files.write(file, printed);
|
|
8131
|
+
const bytes = Buffer.byteLength(printed);
|
|
8132
|
+
const preview = printed.slice(0, PREVIEW_CHARS);
|
|
8133
|
+
return json ? `${JSON.stringify({
|
|
8134
|
+
file,
|
|
8135
|
+
bytes,
|
|
8136
|
+
preview
|
|
8137
|
+
})}\n` : `file: ${file}\nbytes: ${bytes}\npreview:\n${preview}\n`;
|
|
8138
|
+
};
|
|
8139
|
+
}
|
|
8140
|
+
//#endregion
|
|
7272
8141
|
//#region ../agent-cli/dist/style.js
|
|
7273
|
-
const AGENT_VARIABLES = [
|
|
7274
|
-
"CLAUDECODE",
|
|
7275
|
-
"CODEX_CI",
|
|
7276
|
-
"CODEX_SANDBOX",
|
|
7277
|
-
"GEMINI_CLI",
|
|
7278
|
-
"CURSOR_AGENT",
|
|
7279
|
-
"CI"
|
|
7280
|
-
];
|
|
7281
8142
|
function colourEnabled(env, stdoutIsTTY) {
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
return stdoutIsTTY && !noColor && !agent;
|
|
8143
|
+
if (env.NO_COLOR !== void 0 && env.NO_COLOR !== "") return false;
|
|
8144
|
+
return env.FORCE_COLOR !== void 0 && env.FORCE_COLOR !== "" && env.FORCE_COLOR !== "0" || stdoutIsTTY && !isAgent(env);
|
|
7285
8145
|
}
|
|
7286
8146
|
function sgr(open, close) {
|
|
7287
8147
|
return (text) => `\u001b[${open}m${text}\u001b[${close}m`;
|
|
@@ -7308,7 +8168,36 @@ function styleFor(env, stdoutIsTTY) {
|
|
|
7308
8168
|
}
|
|
7309
8169
|
//#endregion
|
|
7310
8170
|
//#region ../agent-cli/dist/run-cli.js
|
|
7311
|
-
|
|
8171
|
+
/** `files` resolving paths against `cwd`, its failures classified by errno. */
|
|
8172
|
+
function classifiedFiles(files, base, callerLine) {
|
|
8173
|
+
return {
|
|
8174
|
+
exists: async (path) => {
|
|
8175
|
+
const target = resolve(base.cwd, path);
|
|
8176
|
+
try {
|
|
8177
|
+
return await files.exists(target);
|
|
8178
|
+
} catch (error) {
|
|
8179
|
+
throw classifyErrno(error, {
|
|
8180
|
+
path: target,
|
|
8181
|
+
callerLine,
|
|
8182
|
+
env: base.env
|
|
8183
|
+
});
|
|
8184
|
+
}
|
|
8185
|
+
},
|
|
8186
|
+
write: async (path, data) => {
|
|
8187
|
+
const target = resolve(base.cwd, path);
|
|
8188
|
+
try {
|
|
8189
|
+
await files.write(target, data);
|
|
8190
|
+
} catch (error) {
|
|
8191
|
+
throw classifyErrno(error, {
|
|
8192
|
+
path: target,
|
|
8193
|
+
callerLine,
|
|
8194
|
+
env: base.env
|
|
8195
|
+
});
|
|
8196
|
+
}
|
|
8197
|
+
}
|
|
8198
|
+
};
|
|
8199
|
+
}
|
|
8200
|
+
async function runCommand(routed, { base, writeStdout, signal, format: baseFormat, ports, clock, files, spawn, projectFile }) {
|
|
7312
8201
|
const { command, commandLine } = routed.spec;
|
|
7313
8202
|
const format = {
|
|
7314
8203
|
...baseFormat,
|
|
@@ -7316,19 +8205,55 @@ async function runCommand(routed, { base, signal, format: baseFormat }) {
|
|
|
7316
8205
|
};
|
|
7317
8206
|
const { invocation } = format;
|
|
7318
8207
|
let parsed;
|
|
8208
|
+
let project;
|
|
7319
8209
|
try {
|
|
7320
8210
|
parsed = parseCommand(routed, invocation);
|
|
8211
|
+
project = projectFile === void 0 ? void 0 : projectOf(projectFile, base.cwd);
|
|
7321
8212
|
} catch (error) {
|
|
7322
8213
|
return errorResult(error, format);
|
|
7323
8214
|
}
|
|
7324
8215
|
const { input, flags } = parsed;
|
|
7325
|
-
const control = new RunControl(
|
|
8216
|
+
const control = new RunControl({
|
|
8217
|
+
commandLine,
|
|
8218
|
+
outer: signal,
|
|
8219
|
+
clock,
|
|
8220
|
+
progress: base.progress,
|
|
8221
|
+
runDeadline: command.deadline === void 0 ? void 0 : {
|
|
8222
|
+
ms: command.deadline.ms,
|
|
8223
|
+
hint: command.deadline.resume(input)
|
|
8224
|
+
}
|
|
8225
|
+
});
|
|
8226
|
+
let delivered;
|
|
8227
|
+
const callerLine = commandLineOf(invocation);
|
|
8228
|
+
const classified = classifiedFiles(files, base, callerLine);
|
|
8229
|
+
const spill = spillTo({
|
|
8230
|
+
cliName: format.cliName,
|
|
8231
|
+
commandName: command.name,
|
|
8232
|
+
env: base.env,
|
|
8233
|
+
clock,
|
|
8234
|
+
files: classified,
|
|
8235
|
+
callerLine
|
|
8236
|
+
});
|
|
7326
8237
|
const ctx = {
|
|
7327
8238
|
...base,
|
|
7328
8239
|
signal: control.signal,
|
|
7329
8240
|
deadline: (ms) => control.deadline(ms),
|
|
7330
|
-
runToEnd: (work) => control.runToEnd(work)
|
|
8241
|
+
runToEnd: (name, work) => control.runToEnd(name, work),
|
|
8242
|
+
ports: ports(),
|
|
8243
|
+
clock,
|
|
8244
|
+
files: classified,
|
|
8245
|
+
spawn: contextSpawn(spawn, {
|
|
8246
|
+
cwd: base.cwd,
|
|
8247
|
+
env: base.env,
|
|
8248
|
+
signal: control.signal
|
|
8249
|
+
}, callerLine),
|
|
8250
|
+
project,
|
|
8251
|
+
poll: (check, { intervalMs }) => poll(clock, check, intervalMs, control.signal),
|
|
8252
|
+
deliver: (data) => {
|
|
8253
|
+
delivered = deliveredOutcome(command, data);
|
|
8254
|
+
}
|
|
7331
8255
|
};
|
|
8256
|
+
let served = false;
|
|
7332
8257
|
let settled;
|
|
7333
8258
|
try {
|
|
7334
8259
|
settled = {
|
|
@@ -7336,24 +8261,35 @@ async function runCommand(routed, { base, signal, format: baseFormat }) {
|
|
|
7336
8261
|
outcome: await control.settle(execute(command, input, ctx, {
|
|
7337
8262
|
dryRun: flags.dryRun,
|
|
7338
8263
|
confirm: flags.confirm,
|
|
8264
|
+
yes: flags.yes,
|
|
8265
|
+
force: flags.force,
|
|
7339
8266
|
page: flags.page,
|
|
7340
8267
|
invocation,
|
|
7341
|
-
commandLine
|
|
8268
|
+
commandLine,
|
|
8269
|
+
started: async (outcome) => {
|
|
8270
|
+
writeStdout(await startedOutput(outcome, format, spill));
|
|
8271
|
+
served = true;
|
|
8272
|
+
}
|
|
7342
8273
|
}))
|
|
7343
8274
|
};
|
|
7344
8275
|
} catch (error) {
|
|
8276
|
+
if (served && control.signal.aborted) return STOPPED_RESULT;
|
|
7345
8277
|
settled = {
|
|
7346
8278
|
ok: false,
|
|
7347
8279
|
error: control.errorAfter(error),
|
|
7348
|
-
|
|
8280
|
+
delivered
|
|
7349
8281
|
};
|
|
7350
8282
|
}
|
|
7351
8283
|
return commandResult(settled, {
|
|
7352
8284
|
...format,
|
|
7353
8285
|
fields: flags.fields
|
|
7354
|
-
});
|
|
8286
|
+
}, spill);
|
|
8287
|
+
}
|
|
8288
|
+
function portsOf(cli, replacement) {
|
|
8289
|
+
if (replacement !== void 0) return () => replacement;
|
|
8290
|
+
return cli.ports ?? (() => ({}));
|
|
7355
8291
|
}
|
|
7356
|
-
async function runWithProgress(cli, argv, options,
|
|
8292
|
+
async function runWithProgress(cli, argv, options, write) {
|
|
7357
8293
|
const format = {
|
|
7358
8294
|
cliName: cli.name,
|
|
7359
8295
|
env: options.env,
|
|
@@ -7362,40 +8298,51 @@ async function runWithProgress(cli, argv, options, writeProgress) {
|
|
|
7362
8298
|
cliName: cli.name,
|
|
7363
8299
|
argv
|
|
7364
8300
|
},
|
|
7365
|
-
json: false
|
|
8301
|
+
json: false,
|
|
8302
|
+
errors: cli.errors ?? {},
|
|
8303
|
+
stop: options.signal
|
|
7366
8304
|
};
|
|
8305
|
+
const builtIns = cli.commands === void 0 ? {} : builtInCommands(cli, options.bin);
|
|
7367
8306
|
const root = cli.commands === void 0 ? commandSpec(cli.name, cli.command) : groupSpec(cli.name, {
|
|
7368
8307
|
...cli.commands,
|
|
7369
|
-
...
|
|
8308
|
+
...builtIns
|
|
7370
8309
|
});
|
|
7371
8310
|
const routed = route(root, format.invocation);
|
|
8311
|
+
const misused = (node) => isAgent(options.env) ? {
|
|
8312
|
+
...format,
|
|
8313
|
+
usageHelp: textHelp(node, root, cli, format.style)
|
|
8314
|
+
} : format;
|
|
7372
8315
|
switch (routed.kind) {
|
|
7373
|
-
case "version": return versionResult(cli);
|
|
8316
|
+
case "version": return versionResult(cli, packageRootOf(options.bin, options.cwd));
|
|
7374
8317
|
case "help": return helpResult(routed.node, root, cli, {
|
|
7375
8318
|
...format,
|
|
7376
8319
|
json: routed.json
|
|
7377
8320
|
});
|
|
7378
8321
|
case "usage error": return errorResult(routed.error, {
|
|
7379
|
-
...
|
|
8322
|
+
...misused(routed.node),
|
|
7380
8323
|
json: routed.json
|
|
7381
8324
|
});
|
|
7382
8325
|
case "command": return runCommand(routed, {
|
|
7383
8326
|
base: {
|
|
7384
8327
|
cwd: options.cwd,
|
|
7385
8328
|
env: options.env,
|
|
7386
|
-
|
|
8329
|
+
packageRoot: packageRootOf(options.bin, options.cwd),
|
|
8330
|
+
progress: (message) => write.stderr(`${message}\n`),
|
|
8331
|
+
secret: (name) => secretOf(cli.name, options.env, name)
|
|
7387
8332
|
},
|
|
8333
|
+
writeStdout: (text) => write.stdout(text),
|
|
7388
8334
|
signal: options.signal,
|
|
7389
|
-
format
|
|
8335
|
+
format: misused(routed.spec),
|
|
8336
|
+
ports: portsOf(cli, options.ports),
|
|
8337
|
+
clock: options.clock ?? realClock,
|
|
8338
|
+
files: options.files ?? realFiles(),
|
|
8339
|
+
spawn: options.spawn ?? realSpawn,
|
|
8340
|
+
projectFile: Object.values(builtIns).includes(routed.spec.command) ? void 0 : cli.project
|
|
7390
8341
|
});
|
|
7391
8342
|
}
|
|
7392
8343
|
}
|
|
7393
8344
|
//#endregion
|
|
7394
8345
|
//#region ../agent-cli/dist/run-process.js
|
|
7395
|
-
const SIGNAL_EXIT_CODES = {
|
|
7396
|
-
SIGINT: 130,
|
|
7397
|
-
SIGTERM: 143
|
|
7398
|
-
};
|
|
7399
8346
|
function written(stream, text) {
|
|
7400
8347
|
return new Promise((resolve) => stream.write(text, () => resolve()));
|
|
7401
8348
|
}
|
|
@@ -7404,7 +8351,7 @@ async function runProcess(cli) {
|
|
|
7404
8351
|
const onSignal = (signal) => {
|
|
7405
8352
|
if (stop.signal.aborted) process.exit(SIGNAL_EXIT_CODES[signal]);
|
|
7406
8353
|
process.stderr.write("\nstopping… (Ctrl-C again to quit now)\n");
|
|
7407
|
-
stop.abort();
|
|
8354
|
+
stop.abort(signal);
|
|
7408
8355
|
};
|
|
7409
8356
|
process.on("SIGINT", onSignal);
|
|
7410
8357
|
process.on("SIGTERM", onSignal);
|
|
@@ -7415,7 +8362,10 @@ async function runProcess(cli) {
|
|
|
7415
8362
|
stdoutIsTTY: process.stdout.isTTY === true,
|
|
7416
8363
|
signal: stop.signal,
|
|
7417
8364
|
bin: process.argv[1]
|
|
7418
|
-
},
|
|
8365
|
+
}, {
|
|
8366
|
+
stdout: (text) => process.stdout.write(text),
|
|
8367
|
+
stderr: (text) => process.stderr.write(text)
|
|
8368
|
+
});
|
|
7419
8369
|
await written(process.stdout, result.stdout);
|
|
7420
8370
|
await written(process.stderr, result.stderr);
|
|
7421
8371
|
process.exitCode = result.exitCode;
|
|
@@ -7428,11 +8378,13 @@ async function runProcess(cli) {
|
|
|
7428
8378
|
//#endregion
|
|
7429
8379
|
//#region src/build.ts
|
|
7430
8380
|
const build = {
|
|
7431
|
-
"sha": "
|
|
7432
|
-
"dirty": false
|
|
8381
|
+
"sha": "411548a",
|
|
8382
|
+
"dirty": false,
|
|
8383
|
+
"builtAt": "2026-10-19T08:42:00.085Z"
|
|
7433
8384
|
};
|
|
7434
8385
|
//#endregion
|
|
7435
8386
|
//#region src/steps.ts
|
|
8387
|
+
const stepErrors = defineErrors({ STEP_FAILED: { class: "operator" } });
|
|
7436
8388
|
const INSTALL = [{
|
|
7437
8389
|
command: "pnpm",
|
|
7438
8390
|
args: ["install"]
|
|
@@ -7501,11 +8453,9 @@ function tail(output) {
|
|
|
7501
8453
|
return output.trimEnd().split(/\r?\n/).slice(-20).join("\n");
|
|
7502
8454
|
}
|
|
7503
8455
|
function stepFailed(problem, left, cwd, cause) {
|
|
7504
|
-
return
|
|
7505
|
-
code: "STEP_FAILED",
|
|
8456
|
+
return cliError(stepErrors, "STEP_FAILED", {
|
|
7506
8457
|
message: problem,
|
|
7507
|
-
hint: `fix the problem, then run in ${cwd}: ${left.map(stepLine).join(" && ")}
|
|
7508
|
-
retryable: false
|
|
8458
|
+
hint: `fix the problem, then run in ${cwd}: ${left.map(stepLine).join(" && ")}`
|
|
7509
8459
|
}, { cause });
|
|
7510
8460
|
}
|
|
7511
8461
|
/** Runs each step in `cwd` in order, capturing its output so only the Runtime writes to stdout. */
|
|
@@ -7559,9 +8509,10 @@ async function writeTemplate(root, files, values) {
|
|
|
7559
8509
|
}
|
|
7560
8510
|
//#endregion
|
|
7561
8511
|
//#region src/version.ts
|
|
7562
|
-
const version = "0.
|
|
8512
|
+
const version = "0.3.0";
|
|
7563
8513
|
//#endregion
|
|
7564
8514
|
//#region src/create.ts
|
|
8515
|
+
const { defineCommand } = agentCli();
|
|
7565
8516
|
const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?([a-z0-9][a-z0-9._-]*)$/;
|
|
7566
8517
|
function errorCode(error) {
|
|
7567
8518
|
return error instanceof Error && "code" in error ? error.code : void 0;
|
|
@@ -7582,64 +8533,69 @@ async function ensureFree(path) {
|
|
|
7582
8533
|
hint: "run `create-agent-cli <name>` with a name whose directory is free, or empty it first"
|
|
7583
8534
|
});
|
|
7584
8535
|
}
|
|
8536
|
+
const create = defineCommand({
|
|
8537
|
+
name: "create",
|
|
8538
|
+
description: "Create a new Agent CLI project in ./<name>",
|
|
8539
|
+
examples: [{
|
|
8540
|
+
command: "create-agent-cli weather --description \"Weather forecasts\"",
|
|
8541
|
+
note: "create ./weather, install dependencies and make the first commit"
|
|
8542
|
+
}],
|
|
8543
|
+
effect: "write",
|
|
8544
|
+
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") }),
|
|
8545
|
+
options: object({
|
|
8546
|
+
description: string().default("An Agent CLI.").describe("one line saying what the CLI does"),
|
|
8547
|
+
install: boolean().default(true).describe("run pnpm install"),
|
|
8548
|
+
git: boolean().default(true).describe("create a git repository with an initial commit")
|
|
8549
|
+
}),
|
|
8550
|
+
plan: async ({ name, description, install, git }, ctx) => {
|
|
8551
|
+
const binName = PACKAGE_NAME.exec(name)?.[1] ?? name;
|
|
8552
|
+
const path = resolve(ctx.cwd, binName);
|
|
8553
|
+
await ensureFree(path);
|
|
8554
|
+
const values = {
|
|
8555
|
+
packageName: name,
|
|
8556
|
+
binName,
|
|
8557
|
+
description,
|
|
8558
|
+
debugVariable: debugVariable(binName),
|
|
8559
|
+
runtimeVersion: version
|
|
8560
|
+
};
|
|
8561
|
+
return {
|
|
8562
|
+
path,
|
|
8563
|
+
values,
|
|
8564
|
+
files: await templateFiles(values),
|
|
8565
|
+
steps: stepsFor({
|
|
8566
|
+
install,
|
|
8567
|
+
git
|
|
8568
|
+
}),
|
|
8569
|
+
install
|
|
8570
|
+
};
|
|
8571
|
+
},
|
|
8572
|
+
apply: async (plan, ctx) => {
|
|
8573
|
+
await mkdir(plan.path, { recursive: true });
|
|
8574
|
+
await writeTemplate(plan.path, plan.files, plan.values);
|
|
8575
|
+
await runSteps(plan.steps, plan.path, ctx);
|
|
8576
|
+
return {
|
|
8577
|
+
path: plan.path,
|
|
8578
|
+
next: `cd ${plan.values.binName} && pnpm ${plan.install ? "test" : "install"}`
|
|
8579
|
+
};
|
|
8580
|
+
},
|
|
8581
|
+
render: ({ path, next }) => `Created ${path}\nnext: ${next}`,
|
|
8582
|
+
renderPlan: (plan) => [
|
|
8583
|
+
`Would create ${plan.path}:`,
|
|
8584
|
+
...plan.files.map((file) => ` create ${file.target}`),
|
|
8585
|
+
...plan.steps.map((step) => ` run ${stepLine(step)}`)
|
|
8586
|
+
].join("\n")
|
|
8587
|
+
});
|
|
8588
|
+
//#endregion
|
|
8589
|
+
//#region src/cli.ts
|
|
8590
|
+
const { defineCli } = agentCli();
|
|
7585
8591
|
//#endregion
|
|
7586
8592
|
//#region src/bin.ts
|
|
7587
8593
|
await runProcess(defineCli({
|
|
7588
8594
|
name: "create-agent-cli",
|
|
7589
8595
|
version,
|
|
7590
8596
|
build,
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
description: "Create a new Agent CLI project in ./<name>",
|
|
7594
|
-
examples: [{
|
|
7595
|
-
command: "create-agent-cli weather --description \"Weather forecasts\"",
|
|
7596
|
-
note: "create ./weather, install dependencies and make the first commit"
|
|
7597
|
-
}],
|
|
7598
|
-
effect: "write",
|
|
7599
|
-
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") }),
|
|
7600
|
-
options: object({
|
|
7601
|
-
description: string().default("An Agent CLI.").describe("one line saying what the CLI does"),
|
|
7602
|
-
install: boolean().default(true).describe("run pnpm install"),
|
|
7603
|
-
git: boolean().default(true).describe("create a git repository with an initial commit")
|
|
7604
|
-
}),
|
|
7605
|
-
plan: async ({ name, description, install, git }, ctx) => {
|
|
7606
|
-
const binName = PACKAGE_NAME.exec(name)?.[1] ?? name;
|
|
7607
|
-
const path = resolve(ctx.cwd, binName);
|
|
7608
|
-
await ensureFree(path);
|
|
7609
|
-
const values = {
|
|
7610
|
-
packageName: name,
|
|
7611
|
-
binName,
|
|
7612
|
-
description,
|
|
7613
|
-
debugVariable: debugVariable(binName),
|
|
7614
|
-
runtimeVersion: version
|
|
7615
|
-
};
|
|
7616
|
-
return {
|
|
7617
|
-
path,
|
|
7618
|
-
values,
|
|
7619
|
-
files: await templateFiles(values),
|
|
7620
|
-
steps: stepsFor({
|
|
7621
|
-
install,
|
|
7622
|
-
git
|
|
7623
|
-
}),
|
|
7624
|
-
install
|
|
7625
|
-
};
|
|
7626
|
-
},
|
|
7627
|
-
apply: async (plan, ctx) => {
|
|
7628
|
-
await mkdir(plan.path, { recursive: true });
|
|
7629
|
-
await writeTemplate(plan.path, plan.files, plan.values);
|
|
7630
|
-
await runSteps(plan.steps, plan.path, ctx);
|
|
7631
|
-
return {
|
|
7632
|
-
path: plan.path,
|
|
7633
|
-
next: `cd ${plan.values.binName} && pnpm ${plan.install ? "test" : "install"}`
|
|
7634
|
-
};
|
|
7635
|
-
},
|
|
7636
|
-
render: ({ path, next }) => `Created ${path}\nnext: ${next}`,
|
|
7637
|
-
renderPlan: (plan) => [
|
|
7638
|
-
`Would create ${plan.path}:`,
|
|
7639
|
-
...plan.files.map((file) => ` create ${file.target}`),
|
|
7640
|
-
...plan.steps.map((step) => ` run ${stepLine(step)}`)
|
|
7641
|
-
].join("\n")
|
|
7642
|
-
})
|
|
8597
|
+
errors: stepErrors,
|
|
8598
|
+
command: create
|
|
7643
8599
|
}));
|
|
7644
8600
|
//#endregion
|
|
7645
8601
|
export {};
|