@duyquangnvx/create-agent-cli 0.30.1 → 0.32.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 +36 -0
- package/dist/bin.js +1443 -332
- package/package.json +2 -2
- package/template/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -6,6 +6,7 @@ import { parseArgs, parseEnv } from "node:util";
|
|
|
6
6
|
import { constants as constants$1, existsSync, readFileSync, realpathSync } from "node:fs";
|
|
7
7
|
import { access, chmod, link, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
8
8
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
10
11
|
//#region ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/util.js
|
|
11
12
|
function getEnumValues(entries) {
|
|
@@ -418,7 +419,7 @@ function hide(inst, key, value) {
|
|
|
418
419
|
}
|
|
419
420
|
/** Adds members a table derives from the instance: each builds on first read and shadows as own data, and assignment shadows the same way, as when these were own properties. */
|
|
420
421
|
/* @__NO_SIDE_EFFECTS__ */
|
|
421
|
-
function derived(computes, table) {
|
|
422
|
+
function derived$1(computes, table) {
|
|
422
423
|
for (const key in computes) {
|
|
423
424
|
const compute = computes[key];
|
|
424
425
|
Object.defineProperty(table, key, {
|
|
@@ -1075,7 +1076,7 @@ function timeSource(args) {
|
|
|
1075
1076
|
function time(args) {
|
|
1076
1077
|
return new RegExp(`^${timeSource(args)}$`);
|
|
1077
1078
|
}
|
|
1078
|
-
function datetime(args) {
|
|
1079
|
+
function datetime$1(args) {
|
|
1079
1080
|
const opts = ["Z"];
|
|
1080
1081
|
if (args.offset) opts.push(`([+-](?:[01]\\d|2[0-3]):[0-5]\\d)`);
|
|
1081
1082
|
const qualified = `${timeSource({
|
|
@@ -1087,7 +1088,7 @@ function datetime(args) {
|
|
|
1087
1088
|
}
|
|
1088
1089
|
const anyString = /^[\s\S]{0,}$/;
|
|
1089
1090
|
const integer = /^-?\d+$/;
|
|
1090
|
-
const number$
|
|
1091
|
+
const number$2 = /^-?\d+(?:\.\d+)?$/;
|
|
1091
1092
|
const boolean$1 = /^(?:true|false)$/i;
|
|
1092
1093
|
const lowercase = /^[^A-Z]*$/;
|
|
1093
1094
|
const uppercase = /^[^a-z]*$/;
|
|
@@ -1725,7 +1726,7 @@ const $ZodKSUID = /*@__PURE__*/ $constructor("$ZodKSUID", (inst, def) => {
|
|
|
1725
1726
|
$ZodStringFormat.init(inst, def);
|
|
1726
1727
|
});
|
|
1727
1728
|
const $ZodISODateTime = /*@__PURE__*/ $constructor("$ZodISODateTime", (inst, def) => {
|
|
1728
|
-
def.pattern ?? (def.pattern = datetime(def));
|
|
1729
|
+
def.pattern ?? (def.pattern = datetime$1(def));
|
|
1729
1730
|
$ZodStringFormat.init(inst, def);
|
|
1730
1731
|
});
|
|
1731
1732
|
const $ZodISODate = /*@__PURE__*/ $constructor("$ZodISODate", (inst, def) => {
|
|
@@ -1870,7 +1871,7 @@ const $ZodJWT = /*@__PURE__*/ $constructor("$ZodJWT", (inst, def) => {
|
|
|
1870
1871
|
});
|
|
1871
1872
|
const $ZodNumber = /*@__PURE__*/ $constructor("$ZodNumber", (inst, def) => {
|
|
1872
1873
|
$ZodType.init(inst, def);
|
|
1873
|
-
inst._zod.pattern = number$
|
|
1874
|
+
inst._zod.pattern = number$2;
|
|
1874
1875
|
inst._zod.parse = (payload, _ctx) => {
|
|
1875
1876
|
if (def.coerce) try {
|
|
1876
1877
|
payload.value = Number(payload.value);
|
|
@@ -2507,7 +2508,7 @@ const $ZodRecord = /*@__PURE__*/ $constructor("$ZodRecord", (inst, def) => {
|
|
|
2507
2508
|
issues: []
|
|
2508
2509
|
}, ctx);
|
|
2509
2510
|
if (keyResult instanceof Promise) throw new Error("Async schemas not supported in object keys currently");
|
|
2510
|
-
if (typeof key === "string" && number$
|
|
2511
|
+
if (typeof key === "string" && number$2.test(key) && keyResult.issues.length) {
|
|
2511
2512
|
const retryResult = def.keyType._zod.run({
|
|
2512
2513
|
value: Number(key),
|
|
2513
2514
|
issues: []
|
|
@@ -3497,6 +3498,15 @@ function _number(Class, params) {
|
|
|
3497
3498
|
}));
|
|
3498
3499
|
}
|
|
3499
3500
|
// @__NO_SIDE_EFFECTS__
|
|
3501
|
+
function _coercedNumber(Class, params) {
|
|
3502
|
+
return new Class(snapshotChecks({
|
|
3503
|
+
type: "number",
|
|
3504
|
+
coerce: true,
|
|
3505
|
+
checks: [],
|
|
3506
|
+
...normalizeParams(params)
|
|
3507
|
+
}));
|
|
3508
|
+
}
|
|
3509
|
+
// @__NO_SIDE_EFFECTS__
|
|
3500
3510
|
function _int(Class, params) {
|
|
3501
3511
|
return new Class({
|
|
3502
3512
|
type: "number",
|
|
@@ -4572,7 +4582,7 @@ function stringifyKeyNames(bySchema, json, visited) {
|
|
|
4572
4582
|
else if (typeof rest.const === "number") rest.const = String(rest.const);
|
|
4573
4583
|
if (!numericType) return rest;
|
|
4574
4584
|
rest.type = "string";
|
|
4575
|
-
if (!values) rest.pattern = (types.includes("number") ? number$
|
|
4585
|
+
if (!values) rest.pattern = (types.includes("number") ? number$2 : integer).source;
|
|
4576
4586
|
return rest;
|
|
4577
4587
|
}
|
|
4578
4588
|
/** Every record of one conversion, so the carriers are found in a single pass rather than once per record. */
|
|
@@ -5053,7 +5063,7 @@ const _ZodString = /*@__PURE__*/ $constructor("_ZodString", (inst, def) => {
|
|
|
5053
5063
|
$ZodString.init(inst, def);
|
|
5054
5064
|
ZodType.init(inst, def);
|
|
5055
5065
|
inst._zod.processJSONSchema = (ctx, json, params) => stringProcessor(inst, ctx, json, params);
|
|
5056
|
-
}, /*@__PURE__*/ derived({
|
|
5066
|
+
}, /*@__PURE__*/ derived$1({
|
|
5057
5067
|
format: (inst) => aggregateChecks(inst).format ?? null,
|
|
5058
5068
|
minLength: (inst) => aggregateChecks(inst).minimum ?? null,
|
|
5059
5069
|
maxLength: (inst) => aggregateChecks(inst).maximum ?? null
|
|
@@ -5299,7 +5309,7 @@ const ZodNumber = /*@__PURE__*/ $constructor("ZodNumber", (inst, def) => {
|
|
|
5299
5309
|
ZodType.init(inst, def);
|
|
5300
5310
|
inst._zod.processJSONSchema = (ctx, json, params) => numberProcessor(inst, ctx, json, params);
|
|
5301
5311
|
inst.isFinite = true;
|
|
5302
|
-
}, /*@__PURE__*/ derived({
|
|
5312
|
+
}, /*@__PURE__*/ derived$1({
|
|
5303
5313
|
minValue: (inst) => {
|
|
5304
5314
|
const { minimum, exclusiveMinimum } = aggregateChecks(inst);
|
|
5305
5315
|
return Math.max(minimum ?? Number.NEGATIVE_INFINITY, exclusiveMinimum ?? Number.NEGATIVE_INFINITY);
|
|
@@ -5360,7 +5370,7 @@ const ZodNumber = /*@__PURE__*/ $constructor("ZodNumber", (inst, def) => {
|
|
|
5360
5370
|
return this;
|
|
5361
5371
|
}
|
|
5362
5372
|
}));
|
|
5363
|
-
function number(params) {
|
|
5373
|
+
function number$1(params) {
|
|
5364
5374
|
return /* @__PURE__ */ _number(ZodNumber, params);
|
|
5365
5375
|
}
|
|
5366
5376
|
const ZodNumberFormat = /*@__PURE__*/ $constructor("ZodNumberFormat", (inst, def) => {
|
|
@@ -5728,6 +5738,16 @@ function superRefine(fn, params) {
|
|
|
5728
5738
|
return /* @__PURE__ */ _superRefine(fn, params);
|
|
5729
5739
|
}
|
|
5730
5740
|
//#endregion
|
|
5741
|
+
//#region ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/classic/iso.js
|
|
5742
|
+
function datetime(params) {
|
|
5743
|
+
return /* @__PURE__ */ _isoDateTime(ZodISODateTime, params);
|
|
5744
|
+
}
|
|
5745
|
+
//#endregion
|
|
5746
|
+
//#region ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/classic/coerce.js
|
|
5747
|
+
function number(params) {
|
|
5748
|
+
return /* @__PURE__ */ _coercedNumber(ZodNumber, params);
|
|
5749
|
+
}
|
|
5750
|
+
//#endregion
|
|
5731
5751
|
//#region ../agent-cli/dist/abort.js
|
|
5732
5752
|
/** Never resolves; rejects with the signal's reason once it aborts. */
|
|
5733
5753
|
function untilAborted(signal) {
|
|
@@ -5741,6 +5761,7 @@ function untilAborted(signal) {
|
|
|
5741
5761
|
}
|
|
5742
5762
|
//#endregion
|
|
5743
5763
|
//#region ../agent-cli/dist/clock.js
|
|
5764
|
+
const MS_PER_S = 1e3;
|
|
5744
5765
|
const realClock = {
|
|
5745
5766
|
now: () => Date.now(),
|
|
5746
5767
|
sleep: (ms, signal) => new Promise((resolve, reject) => {
|
|
@@ -6098,12 +6119,152 @@ function lastLines(output, count) {
|
|
|
6098
6119
|
return output.trim().split("\n").slice(-count).join("\n");
|
|
6099
6120
|
}
|
|
6100
6121
|
//#endregion
|
|
6122
|
+
//#region ../agent-cli/dist/context/fetch.js
|
|
6123
|
+
const UNREACHABLE = "UNREACHABLE";
|
|
6124
|
+
const UNAVAILABLE = "UNAVAILABLE";
|
|
6125
|
+
const UNANSWERED = "UNANSWERED";
|
|
6126
|
+
/** How long a request may take, body included, when the Command gives no `timeoutMs`. */
|
|
6127
|
+
const FETCH_TIMEOUT_MS = 3e4;
|
|
6128
|
+
const HTTP_REQUEST_TIMEOUT = 408;
|
|
6129
|
+
const HTTP_TOO_MANY_REQUESTS = 429;
|
|
6130
|
+
const HTTP_SERVER_ERROR = 500;
|
|
6131
|
+
const HTTP_SERVICE_UNAVAILABLE = 503;
|
|
6132
|
+
const NOT_SENT_CODES = /* @__PURE__ */ new Set([
|
|
6133
|
+
"ECONNREFUSED",
|
|
6134
|
+
"ENOTFOUND",
|
|
6135
|
+
"EAI_AGAIN",
|
|
6136
|
+
"EHOSTUNREACH",
|
|
6137
|
+
"ENETUNREACH",
|
|
6138
|
+
"UND_ERR_CONNECT_TIMEOUT"
|
|
6139
|
+
]);
|
|
6140
|
+
const ONCE_METHODS = /* @__PURE__ */ new Set(["POST", "PATCH"]);
|
|
6141
|
+
function headersOf(input, init) {
|
|
6142
|
+
return new Headers(init.headers ?? (input instanceof Request ? input.headers : void 0));
|
|
6143
|
+
}
|
|
6144
|
+
function methodOf(input, init) {
|
|
6145
|
+
return (init.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
6146
|
+
}
|
|
6147
|
+
/** The `check` of a request that must not be resent, or `undefined` for one that may be. */
|
|
6148
|
+
function onceOf(method, once) {
|
|
6149
|
+
if (once === false) return void 0;
|
|
6150
|
+
return once ?? (ONCE_METHODS.has(method) ? {} : void 0);
|
|
6151
|
+
}
|
|
6152
|
+
function originOf$1(input) {
|
|
6153
|
+
return new URL(input instanceof Request ? input.url : input).origin;
|
|
6154
|
+
}
|
|
6155
|
+
/** 5xx, 408 and 429: the server cannot answer now, whatever the request. */
|
|
6156
|
+
function isUnavailable(status) {
|
|
6157
|
+
return status === HTTP_REQUEST_TIMEOUT || status === HTTP_TOO_MANY_REQUESTS || status >= HTTP_SERVER_ERROR;
|
|
6158
|
+
}
|
|
6159
|
+
/** 408 and every 5xx but 503: the server or a gateway failed on a request it may have applied. */
|
|
6160
|
+
function isUnanswered(status) {
|
|
6161
|
+
return status === HTTP_REQUEST_TIMEOUT || status >= HTTP_SERVER_ERROR && status !== HTTP_SERVICE_UNAVAILABLE;
|
|
6162
|
+
}
|
|
6163
|
+
function retryAtOf(header, clock) {
|
|
6164
|
+
const value = header?.trim();
|
|
6165
|
+
if (value === void 0 || value === "") return void 0;
|
|
6166
|
+
const at = /^\d+$/.test(value) ? clock.now() + Number(value) * MS_PER_S : Date.parse(value);
|
|
6167
|
+
return Number.isNaN(at) ? void 0 : new Date(at).toISOString();
|
|
6168
|
+
}
|
|
6169
|
+
function reasonOf(error) {
|
|
6170
|
+
return error instanceof Error && error.cause instanceof Error ? error.cause : error;
|
|
6171
|
+
}
|
|
6172
|
+
function wasNotSent(error) {
|
|
6173
|
+
const reason = reasonOf(error);
|
|
6174
|
+
return reason instanceof Error && "code" in reason && NOT_SENT_CODES.has(reason.code);
|
|
6175
|
+
}
|
|
6176
|
+
function unreachable(origin, error, rerun) {
|
|
6177
|
+
return new CliError({
|
|
6178
|
+
code: UNREACHABLE,
|
|
6179
|
+
message: `could not reach ${origin}: ${messageOf(reasonOf(error))}`,
|
|
6180
|
+
hint: `check the network and that ${origin} is up, then run the Next step`,
|
|
6181
|
+
details: { origin },
|
|
6182
|
+
next: [rerun]
|
|
6183
|
+
}, { cause: error });
|
|
6184
|
+
}
|
|
6185
|
+
function unavailable(origin, response, scope) {
|
|
6186
|
+
const { status } = response;
|
|
6187
|
+
const retryAt = retryAtOf(response.headers.get("retry-after"), scope.clock);
|
|
6188
|
+
const when = retryAt === void 0 ? "in a minute" : "at retryAt";
|
|
6189
|
+
return new CliError({
|
|
6190
|
+
code: UNAVAILABLE,
|
|
6191
|
+
message: `${origin} answered ${status}: it cannot take requests now`,
|
|
6192
|
+
hint: `${origin} is down or busy, not your request: run the Next step ${when}`,
|
|
6193
|
+
details: {
|
|
6194
|
+
origin,
|
|
6195
|
+
status
|
|
6196
|
+
},
|
|
6197
|
+
retryAt,
|
|
6198
|
+
next: [scope.rerun]
|
|
6199
|
+
});
|
|
6200
|
+
}
|
|
6201
|
+
function unanswered({ origin, method, once }, status, cause) {
|
|
6202
|
+
const check = once?.check;
|
|
6203
|
+
const what = `a ${method} request it may have applied`;
|
|
6204
|
+
return new CliError({
|
|
6205
|
+
code: UNANSWERED,
|
|
6206
|
+
message: status === void 0 ? `${origin} gave no answer to ${what}` : `${origin} answered ${status} to ${what}`,
|
|
6207
|
+
hint: check === void 0 ? `${origin} may have applied it: ask whoever runs ${origin} whether it took before sending it again` : `${origin} may have applied it: run the Next step to see whether it took before sending it again`,
|
|
6208
|
+
details: {
|
|
6209
|
+
origin,
|
|
6210
|
+
status
|
|
6211
|
+
},
|
|
6212
|
+
next: check === void 0 ? [] : [check]
|
|
6213
|
+
}, { cause });
|
|
6214
|
+
}
|
|
6215
|
+
/** What a request that got no answer fails with; `bound` is the Runtime's part of `signal`. */
|
|
6216
|
+
function noAnswer(error, sent, { bound, signal }, rerun) {
|
|
6217
|
+
if (sent.once !== void 0 && bound.aborted) return unanswered(sent, void 0, error);
|
|
6218
|
+
if (signal.aborted) return error;
|
|
6219
|
+
if (sent.once !== void 0 && !wasNotSent(error)) return unanswered(sent, void 0, error);
|
|
6220
|
+
return unreachable(sent.origin, error, rerun);
|
|
6221
|
+
}
|
|
6222
|
+
/** What an answer the Command must not get fails with, or `undefined` for one it gets. */
|
|
6223
|
+
function refusal$1(response, sent, scope) {
|
|
6224
|
+
if (sent.once !== void 0 && isUnanswered(response.status)) return unanswered(sent, response.status);
|
|
6225
|
+
return isUnavailable(response.status) ? unavailable(sent.origin, response, scope) : void 0;
|
|
6226
|
+
}
|
|
6227
|
+
function contextFetch(scope) {
|
|
6228
|
+
return async (input, { timeoutMs = FETCH_TIMEOUT_MS, once, ignoreStop, ...init } = {}) => {
|
|
6229
|
+
const method = methodOf(input, init);
|
|
6230
|
+
const sent = {
|
|
6231
|
+
origin: originOf$1(input),
|
|
6232
|
+
method,
|
|
6233
|
+
once: onceOf(method, once)
|
|
6234
|
+
};
|
|
6235
|
+
const headers = headersOf(input, init);
|
|
6236
|
+
if (!headers.has("user-agent")) headers.set("user-agent", scope.userAgent);
|
|
6237
|
+
const bound = scope.deadline(timeoutMs, ignoreStop === true ? { ignoreStop } : {});
|
|
6238
|
+
const signal = init.signal == null ? bound : AbortSignal.any([bound, init.signal]);
|
|
6239
|
+
signal.throwIfAborted();
|
|
6240
|
+
let response;
|
|
6241
|
+
try {
|
|
6242
|
+
response = await scope.fetch(input, {
|
|
6243
|
+
...init,
|
|
6244
|
+
headers,
|
|
6245
|
+
signal
|
|
6246
|
+
});
|
|
6247
|
+
} catch (error) {
|
|
6248
|
+
throw noAnswer(error, sent, {
|
|
6249
|
+
bound,
|
|
6250
|
+
signal
|
|
6251
|
+
}, scope.rerun);
|
|
6252
|
+
}
|
|
6253
|
+
const failure = refusal$1(response, sent, scope);
|
|
6254
|
+
if (failure === void 0) return response;
|
|
6255
|
+
await response.body?.cancel();
|
|
6256
|
+
throw failure;
|
|
6257
|
+
};
|
|
6258
|
+
}
|
|
6259
|
+
//#endregion
|
|
6101
6260
|
//#region ../agent-cli/dist/context/interrupts.js
|
|
6102
6261
|
const INTERRUPTED = "INTERRUPTED";
|
|
6103
6262
|
const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
|
|
6104
6263
|
/** How long an interrupted Command may take to settle before the run ends without it. */
|
|
6105
6264
|
const GRACE_MS = 3e3;
|
|
6106
6265
|
const GRACE_OVER = Symbol("grace over");
|
|
6266
|
+
/** The codes a run ends with although it was stopped, since they say more than the stop does. */
|
|
6267
|
+
const OUTRANKS_STOP = /* @__PURE__ */ new Set([INTERRUPTED, UNANSWERED]);
|
|
6107
6268
|
function interruptedError(rerun) {
|
|
6108
6269
|
return new CliError({
|
|
6109
6270
|
code: INTERRUPTED,
|
|
@@ -6203,12 +6364,13 @@ var RunControl = class {
|
|
|
6203
6364
|
}
|
|
6204
6365
|
/**
|
|
6205
6366
|
* The error a run ends with: an interrupt or the run deadline outranks what the Command threw,
|
|
6206
|
-
* unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped
|
|
6367
|
+
* unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped, or
|
|
6368
|
+
* `ctx.fetch` threw `UNANSWERED`, since a rerun would send its request twice.
|
|
6207
6369
|
* Otherwise each leaf that is not a `CliError` and whose cause chain holds a deadline's abort is
|
|
6208
6370
|
* reported as that abort, so a declared error keeps its own code.
|
|
6209
6371
|
*/
|
|
6210
6372
|
errorAfter(error) {
|
|
6211
|
-
if (this.signal.aborted) return error instanceof CliError && error.code
|
|
6373
|
+
if (this.signal.aborted) return error instanceof CliError && OUTRANKS_STOP.has(error.code) ? error : this.signal.reason;
|
|
6212
6374
|
const leaves = errorsOf(error).map((leaf) => this.#abortIn(leaf) ?? leaf);
|
|
6213
6375
|
return leaves.length === 1 ? leaves[0] : new AggregateError(leaves);
|
|
6214
6376
|
}
|
|
@@ -6868,7 +7030,7 @@ const SEPARATOR = ",";
|
|
|
6868
7030
|
const COLUMN_SEPARATOR = " ";
|
|
6869
7031
|
const UNSAFE_IN_COLUMN = /[\t\n\r]/;
|
|
6870
7032
|
const PAGE_INPUT = object({
|
|
6871
|
-
limit: number().int().positive().default(DEFAULT_PAGE_LIMIT),
|
|
7033
|
+
limit: number$1().int().positive().default(DEFAULT_PAGE_LIMIT),
|
|
6872
7034
|
cursor: string().min(1).optional()
|
|
6873
7035
|
});
|
|
6874
7036
|
var Page = class {
|
|
@@ -7029,6 +7191,17 @@ const FORCE_FLAG = standardFlag("--force", {
|
|
|
7029
7191
|
takes: "nothing",
|
|
7030
7192
|
schema: boolean()
|
|
7031
7193
|
});
|
|
7194
|
+
const WAIT_SECONDS = number().int().nonnegative();
|
|
7195
|
+
const WAIT_FLAG = standardFlag("--wait", {
|
|
7196
|
+
placeholder: "<seconds>",
|
|
7197
|
+
description: "seconds to wait for a pending Approval to be approved",
|
|
7198
|
+
takes: "value",
|
|
7199
|
+
schema: WAIT_SECONDS
|
|
7200
|
+
});
|
|
7201
|
+
/** Whether `--wait` is a Standard flag of the Command: it is of one that declares a Login or Grant. */
|
|
7202
|
+
function takesWait(command) {
|
|
7203
|
+
return (command.logins ?? []).length > 0 || (command.grants ?? []).length > 0;
|
|
7204
|
+
}
|
|
7032
7205
|
const EFFECT_FLAGS = {
|
|
7033
7206
|
read: [JSON_FLAG],
|
|
7034
7207
|
act: [JSON_FLAG],
|
|
@@ -7048,7 +7221,7 @@ const RESERVED = new Map([
|
|
|
7048
7221
|
...LIST_FLAGS
|
|
7049
7222
|
].map((flag) => [nameOf$1(flag), flag]));
|
|
7050
7223
|
/** The Standard flags of a Command with this Effect and declarations, and of a List Command. */
|
|
7051
|
-
function standardFlagsFor(effect, { outputs, force, cost }, list) {
|
|
7224
|
+
function standardFlagsFor(effect, { outputs, force, cost, wait }, list) {
|
|
7052
7225
|
const forceFlag = force === void 0 ? FORCE_FLAG : {
|
|
7053
7226
|
...FORCE_FLAG,
|
|
7054
7227
|
description: force
|
|
@@ -7057,19 +7230,22 @@ function standardFlagsFor(effect, { outputs, force, cost }, list) {
|
|
|
7057
7230
|
...EFFECT_FLAGS[effect],
|
|
7058
7231
|
...outputs || force !== void 0 ? [forceFlag] : [],
|
|
7059
7232
|
...cost ? [YES_FLAG] : [],
|
|
7233
|
+
...wait ? [WAIT_FLAG] : [],
|
|
7060
7234
|
...list?.standardFlags ?? []
|
|
7061
7235
|
];
|
|
7062
7236
|
}
|
|
7063
7237
|
/** Throws when the Command declares an arg or option under a Standard flag's name. */
|
|
7064
7238
|
function checkNoStandardFlags(command, label) {
|
|
7065
|
-
const
|
|
7239
|
+
const declared = [...argFields(command.args).map(({ key }) => ({
|
|
7066
7240
|
kind: "arg",
|
|
7067
7241
|
key
|
|
7068
7242
|
})), ...optionFields(command.options).map(({ key }) => ({
|
|
7069
7243
|
kind: "option",
|
|
7070
7244
|
key
|
|
7071
|
-
}))]
|
|
7072
|
-
|
|
7245
|
+
}))];
|
|
7246
|
+
const wait = takesWait(command);
|
|
7247
|
+
const lines = declared.flatMap(({ kind, key }) => {
|
|
7248
|
+
const flag = RESERVED.get(kebab(key)) ?? (wait && kebab(key) === nameOf$1(WAIT_FLAG.flag) ? WAIT_FLAG.flag : void 0);
|
|
7073
7249
|
return flag === void 0 ? [] : [` ${kind} '${key}' is the Standard flag ${flag}`];
|
|
7074
7250
|
});
|
|
7075
7251
|
if (lines.length > 0) throw new Error([`Command '${label}' declares Standard flags the Runtime adds; rename them:`, ...lines].join("\n"));
|
|
@@ -7508,7 +7684,7 @@ function readText(path, fileError) {
|
|
|
7508
7684
|
});
|
|
7509
7685
|
}
|
|
7510
7686
|
}
|
|
7511
|
-
function parsed(path, text, schema, rerun) {
|
|
7687
|
+
function parsed$1(path, text, schema, rerun) {
|
|
7512
7688
|
let json;
|
|
7513
7689
|
try {
|
|
7514
7690
|
json = JSON.parse(text);
|
|
@@ -7524,7 +7700,7 @@ function projectFrom(projectFile, folder, scope) {
|
|
|
7524
7700
|
const text = readText(path, scope.fileError);
|
|
7525
7701
|
if (text !== void 0) return {
|
|
7526
7702
|
root: folder,
|
|
7527
|
-
settings: parsed(path, text, projectFile.schema, scope.rerun)
|
|
7703
|
+
settings: parsed$1(path, text, projectFile.schema, scope.rerun)
|
|
7528
7704
|
};
|
|
7529
7705
|
const parent = dirname(folder);
|
|
7530
7706
|
return parent === folder ? void 0 : projectFrom(projectFile, parent, scope);
|
|
@@ -7633,6 +7809,95 @@ function secretRows(secrets, commands) {
|
|
|
7633
7809
|
}];
|
|
7634
7810
|
}
|
|
7635
7811
|
//#endregion
|
|
7812
|
+
//#region ../agent-cli/dist/login/errors.js
|
|
7813
|
+
const LOGIN_REQUIRED = "LOGIN_REQUIRED";
|
|
7814
|
+
const APPROVAL_PENDING = "APPROVAL_PENDING";
|
|
7815
|
+
const APPROVAL_DENIED = "APPROVAL_DENIED";
|
|
7816
|
+
const APPROVAL_EXPIRED = "APPROVAL_EXPIRED";
|
|
7817
|
+
const LOGIN_REJECTED = "LOGIN_REJECTED";
|
|
7818
|
+
function loginStep(name) {
|
|
7819
|
+
return ["login", name];
|
|
7820
|
+
}
|
|
7821
|
+
/** Fails a Command whose Logins are found neither as a Secret nor stored. */
|
|
7822
|
+
function loginRequired(names, rerun) {
|
|
7823
|
+
const listed = names.join(", ");
|
|
7824
|
+
return new CliError({
|
|
7825
|
+
code: LOGIN_REQUIRED,
|
|
7826
|
+
message: `not signed in to ${listed}`,
|
|
7827
|
+
hint: "run the Next steps in order: the person who runs this CLI approves a code in a browser, then the Command runs again",
|
|
7828
|
+
details: { logins: names },
|
|
7829
|
+
next: [...names.map(loginStep), rerun]
|
|
7830
|
+
});
|
|
7831
|
+
}
|
|
7832
|
+
function loginAsking(name) {
|
|
7833
|
+
return {
|
|
7834
|
+
subject: `signing in to ${name}`,
|
|
7835
|
+
denied: () => approvalDenied(name),
|
|
7836
|
+
expired: () => approvalExpired(name)
|
|
7837
|
+
};
|
|
7838
|
+
}
|
|
7839
|
+
/** A Grant's Approval, whose expiry the rerun of the Command heals with a new code. */
|
|
7840
|
+
function grantAsking(name, scope, rerun) {
|
|
7841
|
+
const subject = `the grant of ${scope} from ${name}`;
|
|
7842
|
+
return {
|
|
7843
|
+
subject,
|
|
7844
|
+
denied: () => new CliError({
|
|
7845
|
+
code: APPROVAL_DENIED,
|
|
7846
|
+
message: `the person refused ${subject}`,
|
|
7847
|
+
hint: "ask the person whether to go ahead, and run the Command again only if they agree"
|
|
7848
|
+
}),
|
|
7849
|
+
expired: () => new CliError({
|
|
7850
|
+
code: APPROVAL_EXPIRED,
|
|
7851
|
+
message: `the code for ${subject} expired before it was approved`,
|
|
7852
|
+
hint: "run the Next step for a new code, and pass its link and code to the person",
|
|
7853
|
+
next: [rerun]
|
|
7854
|
+
})
|
|
7855
|
+
};
|
|
7856
|
+
}
|
|
7857
|
+
/** The wait ran out before the person approved: the same Approval stays pending for the rerun. */
|
|
7858
|
+
function approvalPending({ subject }, approval, rerun) {
|
|
7859
|
+
const { verificationUri, userCode, verificationUriComplete } = approval;
|
|
7860
|
+
return new CliError({
|
|
7861
|
+
code: APPROVAL_PENDING,
|
|
7862
|
+
message: `${subject} waits for the code ${userCode} to be approved at ${verificationUriComplete ?? verificationUri}`,
|
|
7863
|
+
hint: "the person sees only your reply: write the link and the code in it, then run the Next step, which waits for the approval",
|
|
7864
|
+
details: {
|
|
7865
|
+
verificationUri,
|
|
7866
|
+
userCode,
|
|
7867
|
+
...verificationUriComplete === void 0 ? {} : { verificationUriComplete }
|
|
7868
|
+
},
|
|
7869
|
+
next: [rerun]
|
|
7870
|
+
});
|
|
7871
|
+
}
|
|
7872
|
+
function approvalDenied(name) {
|
|
7873
|
+
return new CliError({
|
|
7874
|
+
code: APPROVAL_DENIED,
|
|
7875
|
+
message: `the person refused to sign in to ${name}`,
|
|
7876
|
+
hint: "ask the person whether to sign in, and run the Next step only if they agree",
|
|
7877
|
+
next: [loginStep(name)]
|
|
7878
|
+
});
|
|
7879
|
+
}
|
|
7880
|
+
/** A Command's `LOGIN_REJECTED` of a token from a Secret, which only the operator can replace. */
|
|
7881
|
+
function secretRejected(name, secret, rejected) {
|
|
7882
|
+
return new CliError({
|
|
7883
|
+
code: LOGIN_REJECTED,
|
|
7884
|
+
message: rejected.message,
|
|
7885
|
+
hint: `the server refused the token in ${secret.name}: ask the person who runs this CLI to set ${secret.name} to a valid token from ${secret.get}, or to unset it so the CLI signs in through a browser`,
|
|
7886
|
+
details: {
|
|
7887
|
+
login: name,
|
|
7888
|
+
secret: secret.name
|
|
7889
|
+
}
|
|
7890
|
+
}, { cause: rejected });
|
|
7891
|
+
}
|
|
7892
|
+
function approvalExpired(name) {
|
|
7893
|
+
return new CliError({
|
|
7894
|
+
code: APPROVAL_EXPIRED,
|
|
7895
|
+
message: `the code to sign in to ${name} expired before it was approved`,
|
|
7896
|
+
hint: "run the Next step for a new code, and pass its link and code to the person",
|
|
7897
|
+
next: [loginStep(name)]
|
|
7898
|
+
});
|
|
7899
|
+
}
|
|
7900
|
+
//#endregion
|
|
7636
7901
|
//#region ../agent-cli/dist/runtime-codes.js
|
|
7637
7902
|
const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
|
|
7638
7903
|
const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
|
|
@@ -7642,6 +7907,7 @@ const REGISTRY_AUTH = "REGISTRY_AUTH";
|
|
|
7642
7907
|
const INSTALL_FAILED = "INSTALL_FAILED";
|
|
7643
7908
|
const NPM_FAILED = "NPM_FAILED";
|
|
7644
7909
|
const NO_LATEST = "NO_LATEST";
|
|
7910
|
+
const RELEASE_TOO_OLD = "RELEASE_TOO_OLD";
|
|
7645
7911
|
/** The Runtime codes any Command can meet, which no Command declares. */
|
|
7646
7912
|
const anyCommandErrors = {
|
|
7647
7913
|
[INVALID_INPUT]: { class: "caller" },
|
|
@@ -7665,7 +7931,16 @@ const anyCommandErrors = {
|
|
|
7665
7931
|
[TOOL_FAILED]: { class: "operator" },
|
|
7666
7932
|
[TOOL_MISSING]: { class: "operator" },
|
|
7667
7933
|
[SECRET_MISSING]: { class: "operator" },
|
|
7668
|
-
[PROJECT_INVALID]: { class: "operator" }
|
|
7934
|
+
[PROJECT_INVALID]: { class: "operator" },
|
|
7935
|
+
[UNREACHABLE]: { class: "wait" },
|
|
7936
|
+
[UNAVAILABLE]: { class: "wait" },
|
|
7937
|
+
[UNANSWERED]: { class: "source" },
|
|
7938
|
+
[RELEASE_TOO_OLD]: { class: "caller" },
|
|
7939
|
+
[LOGIN_REQUIRED]: { class: "operator" },
|
|
7940
|
+
[APPROVAL_PENDING]: { class: "wait" },
|
|
7941
|
+
[APPROVAL_DENIED]: { class: "operator" },
|
|
7942
|
+
[APPROVAL_EXPIRED]: { class: "operator" },
|
|
7943
|
+
[LOGIN_REJECTED]: { class: "operator" }
|
|
7669
7944
|
};
|
|
7670
7945
|
/**
|
|
7671
7946
|
* The Runtime codes only some Commands reach: each Command that throws one, Built-in or the CLI's
|
|
@@ -7700,7 +7975,8 @@ function mustDeclare(code, table) {
|
|
|
7700
7975
|
//#endregion
|
|
7701
7976
|
//#region ../agent-cli/dist/define-command.js
|
|
7702
7977
|
/** Throws when the Command's declaration breaks a rule its type cannot express; `label` names it. */
|
|
7703
|
-
function checkCommand(command, label, table) {
|
|
7978
|
+
function checkCommand(command, label, table, logins) {
|
|
7979
|
+
checkLogins([...command.logins ?? [], ...command.grants ?? []], label, logins);
|
|
7704
7980
|
checkNoStandardFlags(command, label);
|
|
7705
7981
|
checkForce(command, label);
|
|
7706
7982
|
checkArrayArgLast(label, command.args);
|
|
@@ -7708,6 +7984,10 @@ function checkCommand(command, label, table) {
|
|
|
7708
7984
|
checkNoBooleanArrayOption(label, command.options);
|
|
7709
7985
|
checkErrors(command.errors ?? [], label, table);
|
|
7710
7986
|
}
|
|
7987
|
+
function checkLogins(names, label, logins) {
|
|
7988
|
+
const unknown = names.filter((name) => !Object.hasOwn(logins, name));
|
|
7989
|
+
if (unknown.length > 0) throw new Error(`${label} declares the Login ${unknown.join(", ")}, which the CLI does not declare in logins`);
|
|
7990
|
+
}
|
|
7711
7991
|
/** Throws on a code in `errors` that is neither in `table` nor a Runtime code some Commands declare. */
|
|
7712
7992
|
function checkErrors(errors, label, table) {
|
|
7713
7993
|
const unknown = errors.filter((code) => !mustDeclare(code, table));
|
|
@@ -7744,13 +8024,227 @@ function cliErrorOf(code, init, options) {
|
|
|
7744
8024
|
}, options);
|
|
7745
8025
|
}
|
|
7746
8026
|
//#endregion
|
|
8027
|
+
//#region ../agent-cli/dist/login/declared.js
|
|
8028
|
+
/** The declaration of the Login `name` for a run in `env`. */
|
|
8029
|
+
function declaredLogin(logins, name, env) {
|
|
8030
|
+
const login = logins[name];
|
|
8031
|
+
if (login === void 0) throw new Error(`${name} is not a Login the CLI declares`);
|
|
8032
|
+
return typeof login === "function" ? login(env) : login;
|
|
8033
|
+
}
|
|
8034
|
+
//#endregion
|
|
8035
|
+
//#region ../agent-cli/dist/login/store.js
|
|
8036
|
+
const storedLoginSchema = object({
|
|
8037
|
+
accessToken: string(),
|
|
8038
|
+
refreshToken: string().optional(),
|
|
8039
|
+
expiresAt: datetime().optional()
|
|
8040
|
+
});
|
|
8041
|
+
const approvalSchema = object({
|
|
8042
|
+
deviceCode: string(),
|
|
8043
|
+
userCode: string(),
|
|
8044
|
+
verificationUri: string(),
|
|
8045
|
+
verificationUriComplete: string().optional(),
|
|
8046
|
+
expiresAt: datetime(),
|
|
8047
|
+
/** Seconds between polls of the token endpoint. */
|
|
8048
|
+
interval: number$1()
|
|
8049
|
+
});
|
|
8050
|
+
const KEY_LENGTH = 16;
|
|
8051
|
+
function keyOf(...parts) {
|
|
8052
|
+
return createHash("sha256").update(parts.join("\n")).digest("hex").slice(0, KEY_LENGTH);
|
|
8053
|
+
}
|
|
8054
|
+
function loginFilesOf(userFile, login) {
|
|
8055
|
+
const key = keyOf(login.tokenEndpoint, login.clientId);
|
|
8056
|
+
return {
|
|
8057
|
+
stored: userFile(`login-${key}`, storedLoginSchema),
|
|
8058
|
+
approval: userFile(`approval-${key}`, approvalSchema)
|
|
8059
|
+
};
|
|
8060
|
+
}
|
|
8061
|
+
/** The Approval pending for a Grant of `scope`, kept apart from the Login's and every other scope's. */
|
|
8062
|
+
function grantApprovalOf(userFile, { tokenEndpoint, clientId }, scope) {
|
|
8063
|
+
return userFile(`grant-${keyOf(tokenEndpoint, clientId, scope)}`, approvalSchema);
|
|
8064
|
+
}
|
|
8065
|
+
/** The file's value, or `undefined` once its `expiresAt` has passed, removing the file then. */
|
|
8066
|
+
async function readUnexpired(file, clock) {
|
|
8067
|
+
const value = await file.read();
|
|
8068
|
+
if (value?.expiresAt === void 0 || Date.parse(value.expiresAt) > clock.now()) return value;
|
|
8069
|
+
await file.remove();
|
|
8070
|
+
}
|
|
8071
|
+
/**
|
|
8072
|
+
* The stored Login, unless its access token expired with no refresh token to renew it, removing
|
|
8073
|
+
* the file then.
|
|
8074
|
+
*/
|
|
8075
|
+
async function readStored(file, clock) {
|
|
8076
|
+
const stored = await file.read();
|
|
8077
|
+
return stored?.refreshToken === void 0 ? await readUnexpired(file, clock) : stored;
|
|
8078
|
+
}
|
|
8079
|
+
//#endregion
|
|
8080
|
+
//#region ../agent-cli/dist/login/token-endpoint.js
|
|
8081
|
+
const tokenSchema = object({
|
|
8082
|
+
access_token: string(),
|
|
8083
|
+
refresh_token: string().optional(),
|
|
8084
|
+
expires_in: number$1().optional()
|
|
8085
|
+
});
|
|
8086
|
+
const errorSchema = object({ error: string() });
|
|
8087
|
+
function isoAfter(clock, seconds) {
|
|
8088
|
+
return new Date(clock.now() + seconds * MS_PER_S).toISOString();
|
|
8089
|
+
}
|
|
8090
|
+
/** POSTs `form` to an OAuth endpoint and parses its JSON answer, whatever its status. */
|
|
8091
|
+
async function postForm(fetch, url, form, { ignoreStop } = {}) {
|
|
8092
|
+
const response = await fetch(url, {
|
|
8093
|
+
method: "POST",
|
|
8094
|
+
once: false,
|
|
8095
|
+
...ignoreStop === void 0 ? {} : { ignoreStop },
|
|
8096
|
+
headers: { accept: "application/json" },
|
|
8097
|
+
body: new URLSearchParams(form)
|
|
8098
|
+
});
|
|
8099
|
+
const text = await response.text();
|
|
8100
|
+
try {
|
|
8101
|
+
return JSON.parse(text);
|
|
8102
|
+
} catch (error) {
|
|
8103
|
+
throw new Error(`${url} answered ${response.status} with a body that is not JSON`, { cause: error });
|
|
8104
|
+
}
|
|
8105
|
+
}
|
|
8106
|
+
function parsedAnswer(schema, body, url) {
|
|
8107
|
+
const parsed = schema.safeParse(body);
|
|
8108
|
+
if (!parsed.success) throw new Error(`${url} answered ${JSON.stringify(body)}, not an OAuth answer`);
|
|
8109
|
+
return parsed.data;
|
|
8110
|
+
}
|
|
8111
|
+
function storedLoginOf({ access_token, refresh_token, expires_in }, clock) {
|
|
8112
|
+
return {
|
|
8113
|
+
accessToken: access_token,
|
|
8114
|
+
...refresh_token === void 0 ? {} : { refreshToken: refresh_token },
|
|
8115
|
+
...expires_in === void 0 ? {} : { expiresAt: isoAfter(clock, expires_in) }
|
|
8116
|
+
};
|
|
8117
|
+
}
|
|
8118
|
+
/** Whether the stored Login has a refresh token and its access token expires within the margin. */
|
|
8119
|
+
function nearExpiry({ refreshToken, expiresAt }, clock) {
|
|
8120
|
+
return refreshToken !== void 0 && expiresAt !== void 0 && Date.parse(expiresAt) - clock.now() < 3e5;
|
|
8121
|
+
}
|
|
8122
|
+
/**
|
|
8123
|
+
* Refreshes the stored Login per RFC 6749 §6 under `ctx.runToEnd`, so a single-use refresh token is
|
|
8124
|
+
* never spent without its successor stored. `invalid_grant` removes the stored Login and gives `undefined`.
|
|
8125
|
+
*/
|
|
8126
|
+
async function refreshed(name, login, file, refreshToken, ctx) {
|
|
8127
|
+
ctx.signal.throwIfAborted();
|
|
8128
|
+
const form = {
|
|
8129
|
+
grant_type: "refresh_token",
|
|
8130
|
+
refresh_token: refreshToken,
|
|
8131
|
+
client_id: login.clientId
|
|
8132
|
+
};
|
|
8133
|
+
return await ctx.runToEnd(`refreshing the Login ${name}`, async () => {
|
|
8134
|
+
const body = await postForm(ctx.fetch, login.tokenEndpoint, form, { ignoreStop: true });
|
|
8135
|
+
const granted = tokenSchema.safeParse(body);
|
|
8136
|
+
if (granted.success) {
|
|
8137
|
+
const stored = {
|
|
8138
|
+
refreshToken,
|
|
8139
|
+
...storedLoginOf(granted.data, ctx.clock)
|
|
8140
|
+
};
|
|
8141
|
+
await file.write(stored);
|
|
8142
|
+
return stored;
|
|
8143
|
+
}
|
|
8144
|
+
const { error } = parsedAnswer(errorSchema, body, login.tokenEndpoint);
|
|
8145
|
+
if (error !== "invalid_grant") throw new Error(`${login.tokenEndpoint} refused the refresh of ${name} with ${error}`);
|
|
8146
|
+
await file.remove();
|
|
8147
|
+
});
|
|
8148
|
+
}
|
|
8149
|
+
//#endregion
|
|
8150
|
+
//#region ../agent-cli/dist/login/token.js
|
|
8151
|
+
function setSecretOf(login, secret) {
|
|
8152
|
+
const name = login.secret?.name;
|
|
8153
|
+
const found = name === void 0 ? void 0 : secret(name);
|
|
8154
|
+
return name === void 0 || found === void 0 ? void 0 : {
|
|
8155
|
+
name,
|
|
8156
|
+
secret: found
|
|
8157
|
+
};
|
|
8158
|
+
}
|
|
8159
|
+
function secretTokenOf(login, scope) {
|
|
8160
|
+
const set = setSecretOf(login, scope.secret);
|
|
8161
|
+
return set && {
|
|
8162
|
+
value: set.secret.value,
|
|
8163
|
+
source: {
|
|
8164
|
+
kind: "secret",
|
|
8165
|
+
name: set.name
|
|
8166
|
+
}
|
|
8167
|
+
};
|
|
8168
|
+
}
|
|
8169
|
+
function storedToken({ accessToken }) {
|
|
8170
|
+
return {
|
|
8171
|
+
value: accessToken,
|
|
8172
|
+
source: { kind: "stored" }
|
|
8173
|
+
};
|
|
8174
|
+
}
|
|
8175
|
+
function unexpired({ expiresAt }, clock) {
|
|
8176
|
+
return expiresAt === void 0 || Date.parse(expiresAt) > clock.now();
|
|
8177
|
+
}
|
|
8178
|
+
/**
|
|
8179
|
+
* The Login's token without sending anything: its Secret's value when set, else the stored Login's
|
|
8180
|
+
* while its access token has not expired.
|
|
8181
|
+
*/
|
|
8182
|
+
async function currentTokenOf(login, scope) {
|
|
8183
|
+
const fromSecret = secretTokenOf(login, scope);
|
|
8184
|
+
if (fromSecret !== void 0) return fromSecret;
|
|
8185
|
+
const stored = await readStored(loginFilesOf(scope.userFile, login).stored, scope.clock);
|
|
8186
|
+
return stored && unexpired(stored, scope.clock) ? storedToken(stored) : void 0;
|
|
8187
|
+
}
|
|
8188
|
+
/** The Login's token: its Secret's value when set, else the stored Login's, refreshed near expiry. */
|
|
8189
|
+
async function freshTokenOf(name, login, ctx) {
|
|
8190
|
+
const fromSecret = secretTokenOf(login, tokenScopeOf(ctx));
|
|
8191
|
+
if (fromSecret !== void 0) return fromSecret;
|
|
8192
|
+
const file = loginFilesOf(ctx.userFile, login).stored;
|
|
8193
|
+
const stored = await readStored(file, ctx.clock);
|
|
8194
|
+
if (stored?.refreshToken === void 0 || !nearExpiry(stored, ctx.clock)) return stored && storedToken(stored);
|
|
8195
|
+
const renewed = await refreshed(name, login, file, stored.refreshToken, ctx);
|
|
8196
|
+
return renewed && storedToken(renewed);
|
|
8197
|
+
}
|
|
8198
|
+
function tokenScopeOf(ctx) {
|
|
8199
|
+
const { env, envFile, fileError } = ctx;
|
|
8200
|
+
return {
|
|
8201
|
+
secret: (name) => secretOf({
|
|
8202
|
+
env,
|
|
8203
|
+
envFile,
|
|
8204
|
+
fileError
|
|
8205
|
+
}, name),
|
|
8206
|
+
userFile: ctx.userFile,
|
|
8207
|
+
clock: ctx.clock
|
|
8208
|
+
};
|
|
8209
|
+
}
|
|
8210
|
+
//#endregion
|
|
8211
|
+
//#region ../agent-cli/dist/login/doctor-rows.js
|
|
8212
|
+
function loginRow(name, logins, runAs) {
|
|
8213
|
+
return {
|
|
8214
|
+
name: `login ${name}`,
|
|
8215
|
+
advisory: true,
|
|
8216
|
+
run: async (ctx) => {
|
|
8217
|
+
const login = declaredLogin(logins, name, ctx.env);
|
|
8218
|
+
const set = setSecretOf(login, tokenScopeOf(ctx).secret);
|
|
8219
|
+
if (set !== void 0) return {
|
|
8220
|
+
state: "ok",
|
|
8221
|
+
message: `the token in ${set.name}, from ${originOf(set.secret)}`
|
|
8222
|
+
};
|
|
8223
|
+
if (await readStored(loginFilesOf(ctx.userFile, login).stored, ctx.clock) !== void 0) return {
|
|
8224
|
+
state: "ok",
|
|
8225
|
+
message: "signed in: a stored Login"
|
|
8226
|
+
};
|
|
8227
|
+
const secretName = login.secret?.name;
|
|
8228
|
+
return {
|
|
8229
|
+
state: "problem",
|
|
8230
|
+
message: `not signed in${secretName === void 0 ? "" : `, and ${secretName} is not set`}; a Command that needs ${name} fails until the person approves a code`,
|
|
8231
|
+
fix: lineOf(loginStep(name), runAs)
|
|
8232
|
+
};
|
|
8233
|
+
}
|
|
8234
|
+
};
|
|
8235
|
+
}
|
|
8236
|
+
/** One `doctor` row per Login: where its token comes from, and `login` as the fix with none. */
|
|
8237
|
+
function loginRows(logins, runAs) {
|
|
8238
|
+
return Object.keys(logins).map((name) => loginRow(name, logins, runAs));
|
|
8239
|
+
}
|
|
8240
|
+
//#endregion
|
|
7747
8241
|
//#region ../agent-cli/dist/version.js
|
|
7748
8242
|
/** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
|
|
7749
8243
|
function buildStamp() {
|
|
7750
8244
|
return {
|
|
7751
|
-
"sha": "
|
|
8245
|
+
"sha": "8907e9d",
|
|
7752
8246
|
"dirty": false,
|
|
7753
|
-
"builtAt": "2026-11-
|
|
8247
|
+
"builtAt": "2026-11-10T09:08:31.000Z"
|
|
7754
8248
|
};
|
|
7755
8249
|
}
|
|
7756
8250
|
/** The first line of `--version`: the version, and the commit and time of a stamped build. */
|
|
@@ -8105,90 +8599,325 @@ async function projectRootOf(files, cwd) {
|
|
|
8105
8599
|
return (await lockfileRootOf(files, cwd))?.dir ?? cwd;
|
|
8106
8600
|
}
|
|
8107
8601
|
//#endregion
|
|
8108
|
-
//#region ../agent-cli/dist/built-ins/install/
|
|
8109
|
-
/**
|
|
8110
|
-
function
|
|
8111
|
-
return
|
|
8112
|
-
}
|
|
8113
|
-
//#endregion
|
|
8114
|
-
//#region ../agent-cli/dist/built-ins/install/dependency.js
|
|
8115
|
-
const LINES = {
|
|
8116
|
-
pnpm: {
|
|
8117
|
-
upgrade: (packages) => `pnpm up --latest ${packages.join(" ")}`,
|
|
8118
|
-
add: (packages) => `pnpm add -D ${packages.join(" ")}`,
|
|
8119
|
-
install: "pnpm install"
|
|
8120
|
-
},
|
|
8121
|
-
npm: {
|
|
8122
|
-
upgrade: (packages) => `npm install ${packages.map((name) => `${name}@latest`).join(" ")}`,
|
|
8123
|
-
add: (packages) => `npm install -D ${packages.join(" ")}`,
|
|
8124
|
-
install: "npm install"
|
|
8125
|
-
}
|
|
8126
|
-
};
|
|
8127
|
-
const packageSchema = object({ version: string() });
|
|
8128
|
-
async function manifestListing(files, name, cwd, root) {
|
|
8129
|
-
const dirs = ancestorsOf(cwd);
|
|
8130
|
-
const top = root === void 0 ? dirs.length : dirs.indexOf(root.dir) + 1;
|
|
8131
|
-
for (const dir of dirs.slice(0, top)) {
|
|
8132
|
-
const manifest = await manifestIn(files, dir);
|
|
8133
|
-
if (manifest?.dependencies?.[name] !== void 0 || manifest?.devDependencies?.[name] !== void 0) return join(dir, "package.json");
|
|
8134
|
-
}
|
|
8602
|
+
//#region ../agent-cli/dist/built-ins/install/global-npm.js
|
|
8603
|
+
/** Where npm puts global packages under its global prefix. */
|
|
8604
|
+
function globalModulesDir(prefix, platform) {
|
|
8605
|
+
return platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
|
|
8135
8606
|
}
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
}
|
|
8607
|
+
/** Whether `path` itself is a symlink, which a link in a folder above it does not make it. */
|
|
8608
|
+
async function isLink(files, path) {
|
|
8609
|
+
const real = await files.realPath(path);
|
|
8610
|
+
if (real === void 0) return false;
|
|
8611
|
+
const folder = await files.realPath(dirname(path));
|
|
8612
|
+
return folder === void 0 || real !== join(folder, basename(path));
|
|
8143
8613
|
}
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
|
|
8151
|
-
const version = versionIn(text);
|
|
8152
|
-
if (typeof version !== "string") return {
|
|
8153
|
-
kind: "unreadable",
|
|
8154
|
-
manifest,
|
|
8155
|
-
...version
|
|
8156
|
-
};
|
|
8157
|
-
const copy = {
|
|
8158
|
-
projectDir,
|
|
8159
|
-
realDir: await files.realPath(folder) ?? folder,
|
|
8160
|
-
version
|
|
8161
|
-
};
|
|
8162
|
-
if (!copy.realDir.split(sep).includes("node_modules")) return {
|
|
8163
|
-
kind: "linked",
|
|
8164
|
-
copy
|
|
8165
|
-
};
|
|
8166
|
-
const runningFrom = await files.realPath(bin) ?? bin;
|
|
8167
|
-
return isInside(copy.realDir, runningFrom) ? {
|
|
8168
|
-
kind: "installed",
|
|
8169
|
-
copy
|
|
8170
|
-
} : {
|
|
8171
|
-
kind: "other",
|
|
8172
|
-
copy,
|
|
8173
|
-
runningFrom
|
|
8174
|
-
};
|
|
8175
|
-
}
|
|
8176
|
-
const manifest = await manifestListing(files, install.package, cwd, root);
|
|
8177
|
-
return manifest === void 0 ? { kind: "missing" } : {
|
|
8178
|
-
kind: "listed",
|
|
8179
|
-
manifest
|
|
8180
|
-
};
|
|
8614
|
+
function notGlobal(ctx, message) {
|
|
8615
|
+
return runtimeError({
|
|
8616
|
+
code: NOT_GLOBAL_INSTALL,
|
|
8617
|
+
message,
|
|
8618
|
+
hint: "install it by running the Next step, so its update Command can replace it",
|
|
8619
|
+
next: [wholeLine(setupCommandLine(ctx.registry))]
|
|
8620
|
+
});
|
|
8181
8621
|
}
|
|
8182
|
-
|
|
8183
|
-
|
|
8622
|
+
/**
|
|
8623
|
+
* The global npm install this copy runs from, found by asking npm for its global prefix.
|
|
8624
|
+
* `bin` is the script that started the CLI; throws `NOT_GLOBAL_INSTALL` when it is not inside that install.
|
|
8625
|
+
*/
|
|
8626
|
+
async function globalInstallOf(bin, ctx) {
|
|
8627
|
+
if (bin === void 0) throw notGlobal(ctx, `${ctx.cliName} runs from source, not from an npm install`);
|
|
8628
|
+
const prefix = await globalPrefix(ctx);
|
|
8629
|
+
const packageDir = join(globalModulesDir(prefix, ctx.platform), ctx.registry.package);
|
|
8630
|
+
const installed = await ctx.files.realPath(packageDir);
|
|
8631
|
+
const here = await ctx.files.realPath(bin) ?? bin;
|
|
8632
|
+
if (installed === void 0 || !isInside(installed, here)) throw notGlobal(ctx, `${ctx.cliName} runs from ${here}, not from npm's global install at ${packageDir}`);
|
|
8633
|
+
if (await isLink(ctx.files, packageDir)) throw notGlobal(ctx, `${ctx.cliName} runs from a link to ${installed} (npm link), not from an npm install`);
|
|
8184
8634
|
return {
|
|
8185
|
-
|
|
8186
|
-
|
|
8187
|
-
standing: await standingOf(files, install, bin, cwd, root)
|
|
8635
|
+
prefix,
|
|
8636
|
+
packageDir
|
|
8188
8637
|
};
|
|
8189
8638
|
}
|
|
8190
|
-
|
|
8191
|
-
|
|
8639
|
+
const NPMJS = "https://registry.npmjs.org/";
|
|
8640
|
+
/** The flag that points npm at the CLI's registry; none for npmjs, npm's default. */
|
|
8641
|
+
function registryFlags({ package: name, registry }) {
|
|
8642
|
+
if (registry === NPMJS) return [];
|
|
8643
|
+
const scope = scopeOf(name);
|
|
8644
|
+
return [scope === void 0 ? `--registry=${registry}` : `--${scope}:registry=${registry}`];
|
|
8645
|
+
}
|
|
8646
|
+
/** The line that sets a Global install up, run before the CLI is on the machine. */
|
|
8647
|
+
function setupLineOf(install) {
|
|
8648
|
+
return setupCommandLine(globalRegistry(install));
|
|
8649
|
+
}
|
|
8650
|
+
function globalRegistry(install) {
|
|
8651
|
+
const { package: name, registry } = install;
|
|
8652
|
+
return {
|
|
8653
|
+
package: name,
|
|
8654
|
+
label: registry,
|
|
8655
|
+
flags: registryFlags(install),
|
|
8656
|
+
unreachableHint: "check the network or VPN",
|
|
8657
|
+
loginHint: `log in to ${registry} (\`npm login --registry=${registry}\`) or set its token in ~/.npmrc`,
|
|
8658
|
+
missingHint: `check that ${name} is published on ${registry}`
|
|
8659
|
+
};
|
|
8660
|
+
}
|
|
8661
|
+
/** npm for a Global install, run in the working directory. */
|
|
8662
|
+
function globalNpm(cli, install, ctx, launch) {
|
|
8663
|
+
return npmContext(cli, globalRegistry(install), ctx, launch, ctx.cwd);
|
|
8664
|
+
}
|
|
8665
|
+
//#endregion
|
|
8666
|
+
//#region ../agent-cli/dist/built-ins/install/release-check.js
|
|
8667
|
+
const CHECK_INTERVAL_MS = 864e5;
|
|
8668
|
+
const checkSchema = object({
|
|
8669
|
+
checkedAt: number$1(),
|
|
8670
|
+
latest: string().optional(),
|
|
8671
|
+
/** The highest release the CLI's server named, which started a check sooner. */
|
|
8672
|
+
named: string().optional(),
|
|
8673
|
+
error: string().optional()
|
|
8674
|
+
});
|
|
8675
|
+
const CHECK_SCRIPT = `
|
|
8676
|
+
const { spawnSync } = require("node:child_process");
|
|
8677
|
+
const { mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } = require("node:fs");
|
|
8678
|
+
const { tmpdir } = require("node:os");
|
|
8679
|
+
const { join } = require("node:path");
|
|
8680
|
+
const [file, checkedAt, previous, npm, ...args] = process.argv.slice(1);
|
|
8681
|
+
const windows = process.platform === "win32";
|
|
8682
|
+
const cache = mkdtempSync(join(tmpdir(), "release-check-"));
|
|
8683
|
+
const namedNow = () => {
|
|
8684
|
+
try {
|
|
8685
|
+
return JSON.parse(readFileSync(file, "utf8")).named;
|
|
8686
|
+
} catch {
|
|
8687
|
+
return undefined;
|
|
8688
|
+
}
|
|
8689
|
+
};
|
|
8690
|
+
const write = (check) => {
|
|
8691
|
+
const temp = file + "." + process.pid + ".tmp";
|
|
8692
|
+
const named = namedNow();
|
|
8693
|
+
writeFileSync(temp, JSON.stringify({ checkedAt: Number(checkedAt), named, ...check }));
|
|
8694
|
+
renameSync(temp, file);
|
|
8695
|
+
};
|
|
8696
|
+
const latestOf = (stdout) => {
|
|
8697
|
+
try {
|
|
8698
|
+
const parsed = JSON.parse(stdout);
|
|
8699
|
+
const latest = Array.isArray(parsed) ? parsed.at(-1) : parsed;
|
|
8700
|
+
return typeof latest === "string" && /^\\d+\\.\\d+\\.\\d+/.test(latest) ? latest : undefined;
|
|
8701
|
+
} catch {
|
|
8702
|
+
return undefined;
|
|
8703
|
+
}
|
|
8704
|
+
};
|
|
8705
|
+
// cmd.exe resolves %~dp0 against the working directory for a quoted batch name found on PATH, so
|
|
8706
|
+
// only a full path, which may hold a space, is quoted.
|
|
8707
|
+
const command = windows && /[\\\\/]/.test(npm) ? '"' + npm + '"' : npm;
|
|
8708
|
+
try {
|
|
8709
|
+
const answer = spawnSync(command, args, {
|
|
8710
|
+
encoding: "utf8",
|
|
8711
|
+
shell: windows,
|
|
8712
|
+
windowsHide: true,
|
|
8713
|
+
env: { ...process.env, npm_config_cache: join(cache, "cache"), npm_config_logs_max: "0" },
|
|
8714
|
+
});
|
|
8715
|
+
const latest = answer.status === 0 ? latestOf(answer.stdout) : undefined;
|
|
8716
|
+
if (latest !== undefined) {
|
|
8717
|
+
write({ latest });
|
|
8718
|
+
} else {
|
|
8719
|
+
const said = (answer.stderr || "").trim().split(/\\r?\\n/).slice(0, 5).join("; ");
|
|
8720
|
+
const error = answer.error ? answer.error.message : said || "npm answered " + answer.stdout.trim();
|
|
8721
|
+
write({ latest: previous || undefined, error });
|
|
8722
|
+
}
|
|
8723
|
+
} finally {
|
|
8724
|
+
rmSync(cache, { recursive: true, force: true });
|
|
8725
|
+
}
|
|
8726
|
+
`;
|
|
8727
|
+
function isOff(cliName, env) {
|
|
8728
|
+
return Boolean(env[cliVariable(cliName, "NO_RELEASE_CHECK")] || env.CI);
|
|
8729
|
+
}
|
|
8730
|
+
function cacheFileOf(cliName, env) {
|
|
8731
|
+
const cache = env.XDG_CACHE_HOME || join(env.HOME || env.USERPROFILE || homedir(), ".cache");
|
|
8732
|
+
return join(cache, cliName, "release-check.json");
|
|
8733
|
+
}
|
|
8734
|
+
function parsedCheck(text) {
|
|
8735
|
+
try {
|
|
8736
|
+
return checkSchema.safeParse(JSON.parse(text)).data;
|
|
8737
|
+
} catch {
|
|
8738
|
+
return;
|
|
8739
|
+
}
|
|
8740
|
+
}
|
|
8741
|
+
async function lastCheck(files, file) {
|
|
8742
|
+
const text = await readIfAny(files, file);
|
|
8743
|
+
return text === void 0 ? void 0 : parsedCheck(text);
|
|
8744
|
+
}
|
|
8745
|
+
async function startCheck(cli, install, file, last, named, launch, ctx) {
|
|
8746
|
+
const checkedAt = ctx.clock.now();
|
|
8747
|
+
const previous = last?.latest ?? "";
|
|
8748
|
+
await ctx.files.write(file, JSON.stringify({
|
|
8749
|
+
checkedAt,
|
|
8750
|
+
latest: last?.latest,
|
|
8751
|
+
named
|
|
8752
|
+
}));
|
|
8753
|
+
const npm = globalNpm(cli, install, ctx, launch);
|
|
8754
|
+
const { path, env } = await npmOf(npm);
|
|
8755
|
+
await ctx.spawnDetached(process.execPath, [
|
|
8756
|
+
"-e",
|
|
8757
|
+
CHECK_SCRIPT,
|
|
8758
|
+
file,
|
|
8759
|
+
String(checkedAt),
|
|
8760
|
+
previous,
|
|
8761
|
+
path,
|
|
8762
|
+
...latestViewArgs(npm.registry)
|
|
8763
|
+
], { env });
|
|
8764
|
+
}
|
|
8765
|
+
function debugLine(cli, ctx, text) {
|
|
8766
|
+
if (isDebug({
|
|
8767
|
+
env: ctx.env,
|
|
8768
|
+
cliName: cli.name
|
|
8769
|
+
})) ctx.progress(`release check failed: ${text}`);
|
|
8770
|
+
}
|
|
8771
|
+
const NO_RELEASE_CHECK = {
|
|
8772
|
+
notice: async () => {},
|
|
8773
|
+
latestRelease: async () => {}
|
|
8774
|
+
};
|
|
8775
|
+
function isHigher(version, than) {
|
|
8776
|
+
return than === void 0 || !isVersion(than) || compareVersions(version, than) > 0;
|
|
8777
|
+
}
|
|
8778
|
+
/**
|
|
8779
|
+
* Tells the caller of a newer release that an earlier Release check found, and starts the next
|
|
8780
|
+
* check once the last is a day old, or sooner when the CLI's server names a release higher than
|
|
8781
|
+
* any it named before (ADR 0019).
|
|
8782
|
+
*/
|
|
8783
|
+
function releaseCheck(cli, install, runAs, launch) {
|
|
8784
|
+
const guarded = async (ctx, work) => {
|
|
8785
|
+
if (launch.builtIn || launch.bin === void 0 || cli.commands === void 0) return;
|
|
8786
|
+
if (isOff(cli.name, ctx.env)) return;
|
|
8787
|
+
try {
|
|
8788
|
+
await work();
|
|
8789
|
+
} catch (error) {
|
|
8790
|
+
if (errnoOf(error) === void 0 && !(error instanceof CliError)) throw error;
|
|
8791
|
+
debugLine(cli, ctx, messageOf(error));
|
|
8792
|
+
}
|
|
8793
|
+
};
|
|
8794
|
+
let startedInRun = false;
|
|
8795
|
+
let naming = Promise.resolve();
|
|
8796
|
+
const named = async (version, ctx) => {
|
|
8797
|
+
if (!isVersion(version) || !isHigher(version, cli.version)) return;
|
|
8798
|
+
const file = cacheFileOf(cli.name, ctx.env);
|
|
8799
|
+
const last = await lastCheck(ctx.files, file);
|
|
8800
|
+
if (!isHigher(version, last?.named)) return;
|
|
8801
|
+
if (startedInRun) {
|
|
8802
|
+
await ctx.files.write(file, JSON.stringify({
|
|
8803
|
+
...last,
|
|
8804
|
+
named: version
|
|
8805
|
+
}));
|
|
8806
|
+
return;
|
|
8807
|
+
}
|
|
8808
|
+
await startCheck(cli, install, file, last, version, launch, ctx);
|
|
8809
|
+
startedInRun = true;
|
|
8810
|
+
};
|
|
8811
|
+
return {
|
|
8812
|
+
notice: (ctx) => guarded(ctx, async () => {
|
|
8813
|
+
startedInRun = await checkAndTell(cli, install, runAs, launch, ctx);
|
|
8814
|
+
}),
|
|
8815
|
+
latestRelease: (version, ctx) => {
|
|
8816
|
+
naming = naming.then(() => guarded(ctx, () => named(version, ctx)));
|
|
8817
|
+
return naming;
|
|
8818
|
+
}
|
|
8819
|
+
};
|
|
8820
|
+
}
|
|
8821
|
+
/** Whether it started a check. */
|
|
8822
|
+
async function checkAndTell(cli, install, runAs, launch, ctx) {
|
|
8823
|
+
const file = cacheFileOf(cli.name, ctx.env);
|
|
8824
|
+
const last = await lastCheck(ctx.files, file);
|
|
8825
|
+
if (last?.error !== void 0) debugLine(cli, ctx, last.error);
|
|
8826
|
+
const due = last === void 0 || ctx.clock.now() - last.checkedAt >= CHECK_INTERVAL_MS;
|
|
8827
|
+
if (due) await startCheck(cli, install, file, last, last?.named, launch, ctx);
|
|
8828
|
+
tell(cli, runAs, last, ctx);
|
|
8829
|
+
return due;
|
|
8830
|
+
}
|
|
8831
|
+
function tell(cli, runAs, last, ctx) {
|
|
8832
|
+
const latest = last?.latest;
|
|
8833
|
+
if (latest === void 0 || !isVersion(latest) || compareVersions(latest, cli.version) <= 0) return;
|
|
8834
|
+
ctx.notice(`${cli.name} ${latest} is out, this is ${cli.version}; run \`${runAs} update\``);
|
|
8835
|
+
}
|
|
8836
|
+
//#endregion
|
|
8837
|
+
//#region ../agent-cli/dist/built-ins/install/run-as.js
|
|
8838
|
+
/** The Run-as: the bare name, or `npx -q --no-install <bin>` for a Dependency install (ADR 0011). */
|
|
8839
|
+
function runAsOf({ name, install }) {
|
|
8840
|
+
return install?.mode === "dependency" ? `npx -q --no-install ${name}` : name;
|
|
8841
|
+
}
|
|
8842
|
+
//#endregion
|
|
8843
|
+
//#region ../agent-cli/dist/built-ins/install/dependency.js
|
|
8844
|
+
const LINES = {
|
|
8845
|
+
pnpm: {
|
|
8846
|
+
upgrade: (packages) => `pnpm up --latest ${packages.join(" ")}`,
|
|
8847
|
+
add: (packages) => `pnpm add -D ${packages.join(" ")}`,
|
|
8848
|
+
install: "pnpm install"
|
|
8849
|
+
},
|
|
8850
|
+
npm: {
|
|
8851
|
+
upgrade: (packages) => `npm install ${packages.map((name) => `${name}@latest`).join(" ")}`,
|
|
8852
|
+
add: (packages) => `npm install -D ${packages.join(" ")}`,
|
|
8853
|
+
install: "npm install"
|
|
8854
|
+
}
|
|
8855
|
+
};
|
|
8856
|
+
const packageSchema = object({ version: string() });
|
|
8857
|
+
async function manifestListing(files, name, cwd, root) {
|
|
8858
|
+
const dirs = ancestorsOf(cwd);
|
|
8859
|
+
const top = root === void 0 ? dirs.length : dirs.indexOf(root.dir) + 1;
|
|
8860
|
+
for (const dir of dirs.slice(0, top)) {
|
|
8861
|
+
const manifest = await manifestIn(files, dir);
|
|
8862
|
+
if (manifest?.dependencies?.[name] !== void 0 || manifest?.devDependencies?.[name] !== void 0) return join(dir, "package.json");
|
|
8863
|
+
}
|
|
8864
|
+
}
|
|
8865
|
+
function versionIn(text) {
|
|
8866
|
+
try {
|
|
8867
|
+
return packageSchema.parse(JSON.parse(text)).version;
|
|
8868
|
+
} catch (error) {
|
|
8869
|
+
if (error instanceof SyntaxError || error instanceof ZodError) return { reason: messageOf(error) };
|
|
8870
|
+
throw error;
|
|
8871
|
+
}
|
|
8872
|
+
}
|
|
8873
|
+
async function standingOf(files, install, bin, cwd, root) {
|
|
8874
|
+
if (bin === void 0) return { kind: "source" };
|
|
8875
|
+
for (const projectDir of ancestorsOf(cwd)) {
|
|
8876
|
+
const folder = join(projectDir, "node_modules", install.package);
|
|
8877
|
+
const manifest = join(folder, "package.json");
|
|
8878
|
+
const text = await readIfAny(files, manifest);
|
|
8879
|
+
if (text === void 0) continue;
|
|
8880
|
+
const version = versionIn(text);
|
|
8881
|
+
if (typeof version !== "string") return {
|
|
8882
|
+
kind: "unreadable",
|
|
8883
|
+
manifest,
|
|
8884
|
+
...version
|
|
8885
|
+
};
|
|
8886
|
+
const copy = {
|
|
8887
|
+
projectDir,
|
|
8888
|
+
realDir: await files.realPath(folder) ?? folder,
|
|
8889
|
+
version
|
|
8890
|
+
};
|
|
8891
|
+
if (!copy.realDir.split(sep).includes("node_modules")) return {
|
|
8892
|
+
kind: "linked",
|
|
8893
|
+
copy
|
|
8894
|
+
};
|
|
8895
|
+
const runningFrom = await files.realPath(bin) ?? bin;
|
|
8896
|
+
return isInside(copy.realDir, runningFrom) ? {
|
|
8897
|
+
kind: "installed",
|
|
8898
|
+
copy
|
|
8899
|
+
} : {
|
|
8900
|
+
kind: "other",
|
|
8901
|
+
copy,
|
|
8902
|
+
runningFrom
|
|
8903
|
+
};
|
|
8904
|
+
}
|
|
8905
|
+
const manifest = await manifestListing(files, install.package, cwd, root);
|
|
8906
|
+
return manifest === void 0 ? { kind: "missing" } : {
|
|
8907
|
+
kind: "listed",
|
|
8908
|
+
manifest
|
|
8909
|
+
};
|
|
8910
|
+
}
|
|
8911
|
+
async function surveyDependency(install, bin, { files, cwd }) {
|
|
8912
|
+
const root = await lockfileRootOf(files, cwd);
|
|
8913
|
+
return {
|
|
8914
|
+
cwd,
|
|
8915
|
+
root,
|
|
8916
|
+
standing: await standingOf(files, install, bin, cwd, root)
|
|
8917
|
+
};
|
|
8918
|
+
}
|
|
8919
|
+
function packagesOf(install) {
|
|
8920
|
+
return [install.package, ...install.lockstep ?? []];
|
|
8192
8921
|
}
|
|
8193
8922
|
function isUnchecked(standing) {
|
|
8194
8923
|
return standing.kind === "source" || standing.kind === "linked";
|
|
@@ -8264,88 +8993,29 @@ function checkOf$1(cli, install, runAs, launch) {
|
|
|
8264
8993
|
install: async (ctx) => installFinding(cli, runAs, install, await surveyed(ctx)),
|
|
8265
8994
|
release: async (ctx) => {
|
|
8266
8995
|
const { cwd, root, standing } = await surveyed(ctx);
|
|
8267
|
-
if (isUnchecked(standing)) return { skipped: skippedFor$1(cli, install, standing) };
|
|
8268
|
-
return {
|
|
8269
|
-
npm: npmContext(cli, registryOf(install, root?.dir), ctx, launch, root?.dir ?? cwd),
|
|
8270
|
-
advise: (latest) => adviceFor(install, root, latest)
|
|
8271
|
-
};
|
|
8272
|
-
}
|
|
8273
|
-
};
|
|
8274
|
-
}
|
|
8275
|
-
/** The Install of a CLI each project pins as a dependency, run through npx (ADR 0011). */
|
|
8276
|
-
function dependencyInstall(cli, install) {
|
|
8277
|
-
const runAs = runAsOf(cli);
|
|
8278
|
-
return {
|
|
8279
|
-
runAs,
|
|
8280
|
-
builtIns: () => ({}),
|
|
8281
|
-
check: (launch) => checkOf$1(cli, install, runAs, launch),
|
|
8282
|
-
notice: async () => {}
|
|
8283
|
-
};
|
|
8284
|
-
}
|
|
8285
|
-
//#endregion
|
|
8286
|
-
//#region ../agent-cli/dist/built-ins/install/global-npm.js
|
|
8287
|
-
/** Where npm puts global packages under its global prefix. */
|
|
8288
|
-
function globalModulesDir(prefix, platform) {
|
|
8289
|
-
return platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
|
|
8290
|
-
}
|
|
8291
|
-
/** Whether `path` itself is a symlink, which a link in a folder above it does not make it. */
|
|
8292
|
-
async function isLink(files, path) {
|
|
8293
|
-
const real = await files.realPath(path);
|
|
8294
|
-
if (real === void 0) return false;
|
|
8295
|
-
const folder = await files.realPath(dirname(path));
|
|
8296
|
-
return folder === void 0 || real !== join(folder, basename(path));
|
|
8297
|
-
}
|
|
8298
|
-
function notGlobal(ctx, message) {
|
|
8299
|
-
return runtimeError({
|
|
8300
|
-
code: NOT_GLOBAL_INSTALL,
|
|
8301
|
-
message,
|
|
8302
|
-
hint: "install it by running the Next step, so its update Command can replace it",
|
|
8303
|
-
next: [wholeLine(setupCommandLine(ctx.registry))]
|
|
8304
|
-
});
|
|
8305
|
-
}
|
|
8306
|
-
/**
|
|
8307
|
-
* The global npm install this copy runs from, found by asking npm for its global prefix.
|
|
8308
|
-
* `bin` is the script that started the CLI; throws `NOT_GLOBAL_INSTALL` when it is not inside that install.
|
|
8309
|
-
*/
|
|
8310
|
-
async function globalInstallOf(bin, ctx) {
|
|
8311
|
-
if (bin === void 0) throw notGlobal(ctx, `${ctx.cliName} runs from source, not from an npm install`);
|
|
8312
|
-
const prefix = await globalPrefix(ctx);
|
|
8313
|
-
const packageDir = join(globalModulesDir(prefix, ctx.platform), ctx.registry.package);
|
|
8314
|
-
const installed = await ctx.files.realPath(packageDir);
|
|
8315
|
-
const here = await ctx.files.realPath(bin) ?? bin;
|
|
8316
|
-
if (installed === void 0 || !isInside(installed, here)) throw notGlobal(ctx, `${ctx.cliName} runs from ${here}, not from npm's global install at ${packageDir}`);
|
|
8317
|
-
if (await isLink(ctx.files, packageDir)) throw notGlobal(ctx, `${ctx.cliName} runs from a link to ${installed} (npm link), not from an npm install`);
|
|
8318
|
-
return {
|
|
8319
|
-
prefix,
|
|
8320
|
-
packageDir
|
|
8321
|
-
};
|
|
8322
|
-
}
|
|
8323
|
-
const NPMJS = "https://registry.npmjs.org/";
|
|
8324
|
-
/** The flag that points npm at the CLI's registry; none for npmjs, npm's default. */
|
|
8325
|
-
function registryFlags({ package: name, registry }) {
|
|
8326
|
-
if (registry === NPMJS) return [];
|
|
8327
|
-
const scope = scopeOf(name);
|
|
8328
|
-
return [scope === void 0 ? `--registry=${registry}` : `--${scope}:registry=${registry}`];
|
|
8329
|
-
}
|
|
8330
|
-
/** The line that sets a Global install up, run before the CLI is on the machine. */
|
|
8331
|
-
function setupLineOf(install) {
|
|
8332
|
-
return setupCommandLine(globalRegistry(install));
|
|
8996
|
+
if (isUnchecked(standing)) return { skipped: skippedFor$1(cli, install, standing) };
|
|
8997
|
+
return {
|
|
8998
|
+
npm: npmContext(cli, registryOf(install, root?.dir), ctx, launch, root?.dir ?? cwd),
|
|
8999
|
+
advise: (latest) => adviceFor(install, root, latest)
|
|
9000
|
+
};
|
|
9001
|
+
}
|
|
9002
|
+
};
|
|
8333
9003
|
}
|
|
8334
|
-
|
|
8335
|
-
|
|
9004
|
+
/** The Install of a CLI each project pins as a dependency, run through npx (ADR 0011). */
|
|
9005
|
+
function dependencyInstall(cli, install) {
|
|
9006
|
+
const runAs = runAsOf(cli);
|
|
8336
9007
|
return {
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
9008
|
+
runAs,
|
|
9009
|
+
builtIns: () => ({}),
|
|
9010
|
+
check: (launch) => checkOf$1(cli, install, runAs, launch),
|
|
9011
|
+
releaseCheck: () => NO_RELEASE_CHECK,
|
|
9012
|
+
updateLine: async ({ files, cwd }) => {
|
|
9013
|
+
const root = await lockfileRootOf(files, cwd);
|
|
9014
|
+
const lines = root === void 0 ? void 0 : LINES[root.manager];
|
|
9015
|
+
return lines === void 0 ? void 0 : wholeLine(lines.upgrade(packagesOf(install)));
|
|
9016
|
+
}
|
|
8343
9017
|
};
|
|
8344
9018
|
}
|
|
8345
|
-
/** npm for a Global install, run in the working directory. */
|
|
8346
|
-
function globalNpm(cli, install, ctx, launch) {
|
|
8347
|
-
return npmContext(cli, globalRegistry(install), ctx, launch, ctx.cwd);
|
|
8348
|
-
}
|
|
8349
9019
|
//#endregion
|
|
8350
9020
|
//#region ../agent-cli/dist/built-ins/skill/sync.js
|
|
8351
9021
|
async function walk$1(files, dir, inner) {
|
|
@@ -8826,121 +9496,6 @@ function updateCommand(cli, install, launch) {
|
|
|
8826
9496
|
});
|
|
8827
9497
|
}
|
|
8828
9498
|
//#endregion
|
|
8829
|
-
//#region ../agent-cli/dist/built-ins/install/release-check.js
|
|
8830
|
-
const CHECK_INTERVAL_MS = 864e5;
|
|
8831
|
-
const checkSchema = object({
|
|
8832
|
-
checkedAt: number(),
|
|
8833
|
-
latest: string().optional(),
|
|
8834
|
-
error: string().optional()
|
|
8835
|
-
});
|
|
8836
|
-
const CHECK_SCRIPT = `
|
|
8837
|
-
const { spawnSync } = require("node:child_process");
|
|
8838
|
-
const { mkdtempSync, renameSync, rmSync, writeFileSync } = require("node:fs");
|
|
8839
|
-
const { tmpdir } = require("node:os");
|
|
8840
|
-
const { join } = require("node:path");
|
|
8841
|
-
const [file, checkedAt, previous, npm, ...args] = process.argv.slice(1);
|
|
8842
|
-
const windows = process.platform === "win32";
|
|
8843
|
-
const cache = mkdtempSync(join(tmpdir(), "release-check-"));
|
|
8844
|
-
const write = (check) => {
|
|
8845
|
-
const temp = file + "." + process.pid + ".tmp";
|
|
8846
|
-
writeFileSync(temp, JSON.stringify({ checkedAt: Number(checkedAt), ...check }));
|
|
8847
|
-
renameSync(temp, file);
|
|
8848
|
-
};
|
|
8849
|
-
const latestOf = (stdout) => {
|
|
8850
|
-
try {
|
|
8851
|
-
const parsed = JSON.parse(stdout);
|
|
8852
|
-
const latest = Array.isArray(parsed) ? parsed.at(-1) : parsed;
|
|
8853
|
-
return typeof latest === "string" && /^\\d+\\.\\d+\\.\\d+/.test(latest) ? latest : undefined;
|
|
8854
|
-
} catch {
|
|
8855
|
-
return undefined;
|
|
8856
|
-
}
|
|
8857
|
-
};
|
|
8858
|
-
try {
|
|
8859
|
-
const answer = spawnSync(windows ? '"' + npm + '"' : npm, args, {
|
|
8860
|
-
encoding: "utf8",
|
|
8861
|
-
shell: windows,
|
|
8862
|
-
windowsHide: true,
|
|
8863
|
-
env: { ...process.env, npm_config_cache: join(cache, "cache"), npm_config_logs_max: "0" },
|
|
8864
|
-
});
|
|
8865
|
-
const latest = answer.status === 0 ? latestOf(answer.stdout) : undefined;
|
|
8866
|
-
if (latest !== undefined) {
|
|
8867
|
-
write({ latest });
|
|
8868
|
-
} else {
|
|
8869
|
-
const said = (answer.stderr || "").trim().split(/\\r?\\n/).slice(0, 5).join("; ");
|
|
8870
|
-
const error = answer.error ? answer.error.message : said || "npm answered " + answer.stdout.trim();
|
|
8871
|
-
write({ latest: previous || undefined, error });
|
|
8872
|
-
}
|
|
8873
|
-
} finally {
|
|
8874
|
-
rmSync(cache, { recursive: true, force: true });
|
|
8875
|
-
}
|
|
8876
|
-
`;
|
|
8877
|
-
function isOff(cliName, env) {
|
|
8878
|
-
return Boolean(env[cliVariable(cliName, "NO_RELEASE_CHECK")] || env.CI);
|
|
8879
|
-
}
|
|
8880
|
-
function cacheFileOf(cliName, env) {
|
|
8881
|
-
const cache = env.XDG_CACHE_HOME || join(env.HOME || env.USERPROFILE || homedir(), ".cache");
|
|
8882
|
-
return join(cache, cliName, "release-check.json");
|
|
8883
|
-
}
|
|
8884
|
-
function parsedCheck(text) {
|
|
8885
|
-
try {
|
|
8886
|
-
return checkSchema.safeParse(JSON.parse(text)).data;
|
|
8887
|
-
} catch {
|
|
8888
|
-
return;
|
|
8889
|
-
}
|
|
8890
|
-
}
|
|
8891
|
-
async function lastCheck(files, file) {
|
|
8892
|
-
const text = await readIfAny(files, file);
|
|
8893
|
-
return text === void 0 ? void 0 : parsedCheck(text);
|
|
8894
|
-
}
|
|
8895
|
-
async function startCheck(cli, install, file, last, launch, ctx) {
|
|
8896
|
-
const checkedAt = ctx.clock.now();
|
|
8897
|
-
const previous = last?.latest ?? "";
|
|
8898
|
-
await ctx.files.write(file, JSON.stringify({
|
|
8899
|
-
checkedAt,
|
|
8900
|
-
latest: last?.latest
|
|
8901
|
-
}));
|
|
8902
|
-
const npm = globalNpm(cli, install, ctx, launch);
|
|
8903
|
-
const { path, env } = await npmOf(npm);
|
|
8904
|
-
await ctx.spawnDetached(process.execPath, [
|
|
8905
|
-
"-e",
|
|
8906
|
-
CHECK_SCRIPT,
|
|
8907
|
-
file,
|
|
8908
|
-
String(checkedAt),
|
|
8909
|
-
previous,
|
|
8910
|
-
path,
|
|
8911
|
-
...latestViewArgs(npm.registry)
|
|
8912
|
-
], { env });
|
|
8913
|
-
}
|
|
8914
|
-
function debugLine(cli, ctx, text) {
|
|
8915
|
-
if (isDebug({
|
|
8916
|
-
env: ctx.env,
|
|
8917
|
-
cliName: cli.name
|
|
8918
|
-
})) ctx.progress(`release check failed: ${text}`);
|
|
8919
|
-
}
|
|
8920
|
-
/**
|
|
8921
|
-
* Tells the caller of a newer release that an earlier Release check found, and starts the next
|
|
8922
|
-
* check once the last is a day old.
|
|
8923
|
-
*/
|
|
8924
|
-
async function releaseCheck(cli, install, runAs, launch, ctx) {
|
|
8925
|
-
if (launch.builtIn || launch.bin === void 0 || cli.commands === void 0) return;
|
|
8926
|
-
if (isOff(cli.name, ctx.env)) return;
|
|
8927
|
-
try {
|
|
8928
|
-
await checkAndTell(cli, install, runAs, launch, ctx);
|
|
8929
|
-
} catch (error) {
|
|
8930
|
-
if (errnoOf(error) === void 0 && !(error instanceof CliError)) throw error;
|
|
8931
|
-
debugLine(cli, ctx, messageOf(error));
|
|
8932
|
-
}
|
|
8933
|
-
}
|
|
8934
|
-
async function checkAndTell(cli, install, runAs, launch, ctx) {
|
|
8935
|
-
const file = cacheFileOf(cli.name, ctx.env);
|
|
8936
|
-
const last = await lastCheck(ctx.files, file);
|
|
8937
|
-
if (last?.error !== void 0) debugLine(cli, ctx, last.error);
|
|
8938
|
-
if (last === void 0 || ctx.clock.now() - last.checkedAt >= CHECK_INTERVAL_MS) await startCheck(cli, install, file, last, launch, ctx);
|
|
8939
|
-
const latest = last?.latest;
|
|
8940
|
-
if (latest === void 0 || !isVersion(latest) || compareVersions(latest, cli.version) <= 0) return;
|
|
8941
|
-
ctx.notice(`${cli.name} ${latest} is out, this is ${cli.version}; run \`${runAs} update\``);
|
|
8942
|
-
}
|
|
8943
|
-
//#endregion
|
|
8944
9499
|
//#region ../agent-cli/dist/built-ins/install/global.js
|
|
8945
9500
|
function checkOf(cli, install, launch) {
|
|
8946
9501
|
return {
|
|
@@ -8978,13 +9533,13 @@ function globalInstall(cli, install) {
|
|
|
8978
9533
|
update: updateCommand(cli, install, launch)
|
|
8979
9534
|
}),
|
|
8980
9535
|
check: (launch) => checkOf(cli, install, launch),
|
|
8981
|
-
|
|
9536
|
+
releaseCheck: (launch) => releaseCheck(cli, install, adapter.runAs, launch),
|
|
9537
|
+
updateLine: async () => ["update"]
|
|
8982
9538
|
};
|
|
8983
9539
|
return adapter;
|
|
8984
9540
|
}
|
|
8985
9541
|
//#endregion
|
|
8986
9542
|
//#region ../agent-cli/dist/built-ins/install/install.js
|
|
8987
|
-
const noNotice = async () => {};
|
|
8988
9543
|
function noInstall(cli) {
|
|
8989
9544
|
const skipped = async () => ({
|
|
8990
9545
|
state: "skipped",
|
|
@@ -8997,7 +9552,8 @@ function noInstall(cli) {
|
|
|
8997
9552
|
install: skipped,
|
|
8998
9553
|
release: async () => ({ skipped: await skipped() })
|
|
8999
9554
|
}),
|
|
9000
|
-
|
|
9555
|
+
releaseCheck: () => NO_RELEASE_CHECK,
|
|
9556
|
+
updateLine: async () => void 0
|
|
9001
9557
|
};
|
|
9002
9558
|
}
|
|
9003
9559
|
function installOf(cli) {
|
|
@@ -9114,7 +9670,7 @@ const SECTION_VERBS = {
|
|
|
9114
9670
|
add: "add the Agents section to",
|
|
9115
9671
|
rewrite: "rewrite the Agents section in"
|
|
9116
9672
|
};
|
|
9117
|
-
function renderPlan(cli, plan) {
|
|
9673
|
+
function renderPlan$1(cli, plan) {
|
|
9118
9674
|
const files = [
|
|
9119
9675
|
...plan.write.map((file) => ` write ${file}`),
|
|
9120
9676
|
...plan.replace.map((file) => ` replace ${file}`),
|
|
@@ -9168,7 +9724,7 @@ function skillInstallCommand(cli, folder, agents) {
|
|
|
9168
9724
|
};
|
|
9169
9725
|
},
|
|
9170
9726
|
render: (data) => renderInstalled(cli, data),
|
|
9171
|
-
renderPlan: (plan) => renderPlan(cli, plan)
|
|
9727
|
+
renderPlan: (plan) => renderPlan$1(cli, plan)
|
|
9172
9728
|
});
|
|
9173
9729
|
}
|
|
9174
9730
|
//#endregion
|
|
@@ -9368,6 +9924,7 @@ function doctorChecks(cli, launch, scope) {
|
|
|
9368
9924
|
...cli.doctor ?? [],
|
|
9369
9925
|
...tools,
|
|
9370
9926
|
...secretRows(cli.secrets ?? [], cli.commands),
|
|
9927
|
+
...loginRows(cli.logins ?? {}, scope.runAs),
|
|
9371
9928
|
installRow(check),
|
|
9372
9929
|
registryRow(cli, check),
|
|
9373
9930
|
...skillOf(cli, install).rows
|
|
@@ -9378,7 +9935,6 @@ function doctorChecks(cli, launch, scope) {
|
|
|
9378
9935
|
//#endregion
|
|
9379
9936
|
//#region ../agent-cli/dist/built-ins/doctor.js
|
|
9380
9937
|
const DEFAULT_DEADLINE_MS = 1e4;
|
|
9381
|
-
const MS_PER_SECOND = 1e3;
|
|
9382
9938
|
const MIN_LABEL_WIDTH = 8;
|
|
9383
9939
|
const reportSchema = object({
|
|
9384
9940
|
rows: array(object({
|
|
@@ -9397,7 +9953,7 @@ const reportSchema = object({
|
|
|
9397
9953
|
function problemOf(check, error, { signal, deadline, ms, runAs }) {
|
|
9398
9954
|
if (!signal.aborted && deadline.aborted && error === deadline.reason) return {
|
|
9399
9955
|
state: "problem",
|
|
9400
|
-
message: `${check.name} did not answer within ${ms /
|
|
9956
|
+
message: `${check.name} did not answer within ${ms / MS_PER_S} s`
|
|
9401
9957
|
};
|
|
9402
9958
|
return problemOfError(check.name, error, runAs);
|
|
9403
9959
|
}
|
|
@@ -9532,17 +10088,342 @@ function doctorCommand(cli, launch) {
|
|
|
9532
10088
|
};
|
|
9533
10089
|
}
|
|
9534
10090
|
//#endregion
|
|
10091
|
+
//#region ../agent-cli/dist/login/device-flow.js
|
|
10092
|
+
const DEVICE_CODE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
10093
|
+
/** RFC 8628 §3.2: the interval when the server gives none. */
|
|
10094
|
+
const DEFAULT_INTERVAL_S = 5;
|
|
10095
|
+
/** RFC 8628 §3.5: what each `slow_down` adds to the interval. */
|
|
10096
|
+
const SLOW_DOWN_S = 5;
|
|
10097
|
+
const deviceSchema = object({
|
|
10098
|
+
device_code: string(),
|
|
10099
|
+
user_code: string(),
|
|
10100
|
+
verification_uri: string(),
|
|
10101
|
+
verification_uri_complete: string().optional(),
|
|
10102
|
+
expires_in: number$1(),
|
|
10103
|
+
interval: number$1().optional()
|
|
10104
|
+
});
|
|
10105
|
+
async function startApproval(login, { fetch, clock }) {
|
|
10106
|
+
const form = {
|
|
10107
|
+
client_id: login.clientId,
|
|
10108
|
+
...login.scope === void 0 ? {} : { scope: login.scope }
|
|
10109
|
+
};
|
|
10110
|
+
const device = parsedAnswer(deviceSchema, await postForm(fetch, login.deviceEndpoint, form), login.deviceEndpoint);
|
|
10111
|
+
return {
|
|
10112
|
+
deviceCode: device.device_code,
|
|
10113
|
+
userCode: device.user_code,
|
|
10114
|
+
verificationUri: device.verification_uri,
|
|
10115
|
+
...device.verification_uri_complete === void 0 ? {} : { verificationUriComplete: device.verification_uri_complete },
|
|
10116
|
+
expiresAt: isoAfter(clock, device.expires_in),
|
|
10117
|
+
interval: device.interval ?? DEFAULT_INTERVAL_S
|
|
10118
|
+
};
|
|
10119
|
+
}
|
|
10120
|
+
async function pollApproval(asking, login, { approval, takenUp }, scope, signal) {
|
|
10121
|
+
const form = {
|
|
10122
|
+
grant_type: DEVICE_CODE_GRANT,
|
|
10123
|
+
device_code: approval.deviceCode,
|
|
10124
|
+
client_id: login.clientId
|
|
10125
|
+
};
|
|
10126
|
+
let intervalS = approval.interval;
|
|
10127
|
+
for (let first = true;; first = false) {
|
|
10128
|
+
if (!(first && takenUp)) await scope.clock.sleep(intervalS * MS_PER_S, signal);
|
|
10129
|
+
const body = await postForm(scope.fetch, login.tokenEndpoint, form);
|
|
10130
|
+
const granted = tokenSchema.safeParse(body);
|
|
10131
|
+
if (granted.success) return storedLoginOf(granted.data, scope.clock);
|
|
10132
|
+
const { error } = parsedAnswer(errorSchema, body, login.tokenEndpoint);
|
|
10133
|
+
if (error === "slow_down") intervalS += SLOW_DOWN_S;
|
|
10134
|
+
else if (error !== "authorization_pending") throw refusal(asking, login, error);
|
|
10135
|
+
}
|
|
10136
|
+
}
|
|
10137
|
+
/** The error a token endpoint's answer other than pending or slow down ends the polling with. */
|
|
10138
|
+
function refusal(asking, login, error) {
|
|
10139
|
+
if (error === "access_denied") return asking.denied();
|
|
10140
|
+
if (error === "expired_token" || error === "invalid_grant") return asking.expired();
|
|
10141
|
+
return /* @__PURE__ */ new Error(`${login.tokenEndpoint} refused the device code with ${error}`);
|
|
10142
|
+
}
|
|
10143
|
+
function endsApproval(error) {
|
|
10144
|
+
return error instanceof CliError && (error.code === "APPROVAL_DENIED" || error.code === "APPROVAL_EXPIRED");
|
|
10145
|
+
}
|
|
10146
|
+
/** Takes up the pending Approval, or asks for one and keeps it as a User file, and prints its code. */
|
|
10147
|
+
async function askedApproval(login, file, scope) {
|
|
10148
|
+
const pending = await readUnexpired(file, scope.clock);
|
|
10149
|
+
const approval = pending ?? await startApproval(login, scope);
|
|
10150
|
+
if (pending === void 0) await file.write(approval);
|
|
10151
|
+
scope.progress(`open ${approval.verificationUriComplete ?? approval.verificationUri} and approve code ${approval.userCode}`);
|
|
10152
|
+
return {
|
|
10153
|
+
approval,
|
|
10154
|
+
takenUp: pending !== void 0
|
|
10155
|
+
};
|
|
10156
|
+
}
|
|
10157
|
+
/** The `APPROVAL_PENDING` of the pending Approval, or of a new one, without polling it. */
|
|
10158
|
+
async function pendingApproval(name, login, files, scope, rerun) {
|
|
10159
|
+
const { approval } = await askedApproval(login, files.approval, scope);
|
|
10160
|
+
return approvalPending(loginAsking(name), approval, rerun);
|
|
10161
|
+
}
|
|
10162
|
+
/**
|
|
10163
|
+
* Polls the Approval pending in `file`, or a new one, until the person approves, removing it once
|
|
10164
|
+
* refused or expired. An approved one stays until what it granted is kept, so a cut-off run loses nothing.
|
|
10165
|
+
*/
|
|
10166
|
+
async function approved(asking, login, file, scope, { waitS, rerun }) {
|
|
10167
|
+
const asked = await askedApproval(login, file, scope);
|
|
10168
|
+
const wait = scope.deadline(waitS * MS_PER_S);
|
|
10169
|
+
let granted;
|
|
10170
|
+
try {
|
|
10171
|
+
granted = await pollApproval(asking, login, asked, scope, wait);
|
|
10172
|
+
} catch (error) {
|
|
10173
|
+
if (wait.aborted && !scope.signal.aborted) throw approvalPending(asking, asked.approval, rerun);
|
|
10174
|
+
if (endsApproval(error)) await file.remove();
|
|
10175
|
+
throw error;
|
|
10176
|
+
}
|
|
10177
|
+
return granted;
|
|
10178
|
+
}
|
|
10179
|
+
/** Polls the pending Approval, or a new one, until the person approves, and stores the Login. */
|
|
10180
|
+
async function signIn(name, login, files, scope, wait) {
|
|
10181
|
+
const stored = await approved(loginAsking(name), login, files.approval, scope, wait);
|
|
10182
|
+
await files.stored.write(stored);
|
|
10183
|
+
await files.approval.remove();
|
|
10184
|
+
return stored;
|
|
10185
|
+
}
|
|
10186
|
+
/** Polls the Approval of the Grant's scope, or a new one, until the person approves: its token. */
|
|
10187
|
+
async function grant(asking, login, file, scope, wait) {
|
|
10188
|
+
const { accessToken } = await approved(asking, login, file, scope, wait);
|
|
10189
|
+
await file.remove();
|
|
10190
|
+
return accessToken;
|
|
10191
|
+
}
|
|
10192
|
+
//#endregion
|
|
10193
|
+
//#region ../agent-cli/dist/built-ins/login-name.js
|
|
10194
|
+
/** The Login a `login` or `logout` run names, or the only one; with several, the name is needed. */
|
|
10195
|
+
function chosenName(run, given, names) {
|
|
10196
|
+
const [only, ...others] = names;
|
|
10197
|
+
if (given !== void 0) return given;
|
|
10198
|
+
if (others.length === 0) return only;
|
|
10199
|
+
throw new CliError({
|
|
10200
|
+
code: INVALID_INPUT,
|
|
10201
|
+
message: `\`${run.cliName} ${run.command}\` needs the name of a Login: ${names.join(", ")}`,
|
|
10202
|
+
hint: "run the Next step for the Login the task needs",
|
|
10203
|
+
next: names.map((name) => [run.command, name])
|
|
10204
|
+
});
|
|
10205
|
+
}
|
|
10206
|
+
/** The optional `name` arg of `login` and `logout`; `purpose` reads as "the Login to <purpose>". */
|
|
10207
|
+
function nameArgs(names, purpose) {
|
|
10208
|
+
return object({ name: _enum(names).optional().describe(`the Login to ${purpose}, of ${names.join(", ")}; needed only with several`) });
|
|
10209
|
+
}
|
|
10210
|
+
/** The name an example passes: none with one Login, the first with several. */
|
|
10211
|
+
function exampleName(names) {
|
|
10212
|
+
return names.length === 1 ? [] : [names[0]];
|
|
10213
|
+
}
|
|
10214
|
+
//#endregion
|
|
10215
|
+
//#region ../agent-cli/dist/built-ins/logout.js
|
|
10216
|
+
const logoutSchema = object({
|
|
10217
|
+
login: string(),
|
|
10218
|
+
stored: _enum([
|
|
10219
|
+
"revoked",
|
|
10220
|
+
"removed",
|
|
10221
|
+
"none"
|
|
10222
|
+
]),
|
|
10223
|
+
/** The Secret still set for this Login, which logout never touches. */
|
|
10224
|
+
secret: string().optional()
|
|
10225
|
+
});
|
|
10226
|
+
function renderLogout({ login, stored, secret }) {
|
|
10227
|
+
const head = stored === "none" ? `not signed in to ${login}: nothing to do` : `signed out of ${login}${stored === "revoked" ? ", and the server revoked its token" : ""}`;
|
|
10228
|
+
return secret === void 0 ? head : `${head}; ${login} still uses the token in ${secret}`;
|
|
10229
|
+
}
|
|
10230
|
+
function renderPlan({ login, stored, revokeEndpoint, secret }) {
|
|
10231
|
+
if (!stored) return renderLogout({
|
|
10232
|
+
login,
|
|
10233
|
+
stored: "none",
|
|
10234
|
+
...secret === void 0 ? {} : { secret }
|
|
10235
|
+
});
|
|
10236
|
+
return `would sign out of ${login}${revokeEndpoint === void 0 ? "" : `, revoking its token at ${revokeEndpoint}`}`;
|
|
10237
|
+
}
|
|
10238
|
+
/**
|
|
10239
|
+
* RFC 7009 §2.1: revoking the refresh token ends the grant, so the server should drop its access
|
|
10240
|
+
* tokens too; a Login without one revokes its access token.
|
|
10241
|
+
*/
|
|
10242
|
+
async function revoke(endpoint, { clientId }, { accessToken, refreshToken }, ctx) {
|
|
10243
|
+
const [token, hint] = refreshToken === void 0 ? [accessToken, "access_token"] : [refreshToken, "refresh_token"];
|
|
10244
|
+
const response = await ctx.fetch(endpoint, {
|
|
10245
|
+
method: "POST",
|
|
10246
|
+
once: false,
|
|
10247
|
+
body: new URLSearchParams({
|
|
10248
|
+
token,
|
|
10249
|
+
token_type_hint: hint,
|
|
10250
|
+
client_id: clientId
|
|
10251
|
+
})
|
|
10252
|
+
});
|
|
10253
|
+
if (!response.ok) throw new Error(`${endpoint} answered ${response.status} to the revocation of a token`);
|
|
10254
|
+
}
|
|
10255
|
+
/** The Built-in `logout`: revokes and removes the stored Login, never touching its Secret. */
|
|
10256
|
+
function logoutCommand(cli, logins, names) {
|
|
10257
|
+
const named = exampleName(names);
|
|
10258
|
+
return defineCommand$1.write({
|
|
10259
|
+
description: "sign out: revoke the stored Login and remove it",
|
|
10260
|
+
help: "Revokes the stored Login at the server when it has a revoke endpoint, then removes it for this user. A Secret set for the Login stays set, so the Commands keep using it. It does nothing when no Login is stored.",
|
|
10261
|
+
examples: [{
|
|
10262
|
+
command: [
|
|
10263
|
+
cli.name,
|
|
10264
|
+
"logout",
|
|
10265
|
+
...named
|
|
10266
|
+
].join(" "),
|
|
10267
|
+
note: "sign out"
|
|
10268
|
+
}],
|
|
10269
|
+
args: nameArgs(names, "sign out of"),
|
|
10270
|
+
output: logoutSchema,
|
|
10271
|
+
plan: async ({ name: given }, ctx) => {
|
|
10272
|
+
const name = chosenName({
|
|
10273
|
+
cliName: cli.name,
|
|
10274
|
+
command: "logout"
|
|
10275
|
+
}, given, names);
|
|
10276
|
+
const login = declaredLogin(logins, name, ctx.env);
|
|
10277
|
+
return {
|
|
10278
|
+
login: name,
|
|
10279
|
+
stored: await loginFilesOf(ctx.userFile, login).stored.read() !== void 0,
|
|
10280
|
+
revokeEndpoint: login.revokeEndpoint,
|
|
10281
|
+
secret: setSecretOf(login, tokenScopeOf(ctx).secret)?.name
|
|
10282
|
+
};
|
|
10283
|
+
},
|
|
10284
|
+
apply: async ({ login: name, secret }, ctx) => {
|
|
10285
|
+
const login = declaredLogin(logins, name, ctx.env);
|
|
10286
|
+
const file = loginFilesOf(ctx.userFile, login).stored;
|
|
10287
|
+
const stored = await file.read();
|
|
10288
|
+
const kept = secret === void 0 ? {} : { secret };
|
|
10289
|
+
if (stored === void 0) return {
|
|
10290
|
+
login: name,
|
|
10291
|
+
stored: "none",
|
|
10292
|
+
...kept
|
|
10293
|
+
};
|
|
10294
|
+
if (login.revokeEndpoint !== void 0) await revoke(login.revokeEndpoint, login, stored, ctx);
|
|
10295
|
+
await file.remove();
|
|
10296
|
+
return {
|
|
10297
|
+
login: name,
|
|
10298
|
+
stored: login.revokeEndpoint === void 0 ? "removed" : "revoked",
|
|
10299
|
+
...kept
|
|
10300
|
+
};
|
|
10301
|
+
},
|
|
10302
|
+
render: renderLogout,
|
|
10303
|
+
renderPlan
|
|
10304
|
+
});
|
|
10305
|
+
}
|
|
10306
|
+
//#endregion
|
|
10307
|
+
//#region ../agent-cli/dist/built-ins/login.js
|
|
10308
|
+
const loginSchema = object({
|
|
10309
|
+
login: string(),
|
|
10310
|
+
source: _enum([
|
|
10311
|
+
"approved",
|
|
10312
|
+
"stored",
|
|
10313
|
+
"secret"
|
|
10314
|
+
]),
|
|
10315
|
+
secret: string().optional()
|
|
10316
|
+
});
|
|
10317
|
+
function renderLogin({ login, source, secret }) {
|
|
10318
|
+
if (source === "approved") return `signed in to ${login}`;
|
|
10319
|
+
if (source === "stored") return `already signed in to ${login}: nothing to do`;
|
|
10320
|
+
return `${login} uses the token in ${secret}: nothing to do`;
|
|
10321
|
+
}
|
|
10322
|
+
function foundData(login, { source }) {
|
|
10323
|
+
return source.kind === "secret" ? {
|
|
10324
|
+
login,
|
|
10325
|
+
source: "secret",
|
|
10326
|
+
secret: source.name
|
|
10327
|
+
} : {
|
|
10328
|
+
login,
|
|
10329
|
+
source: "stored"
|
|
10330
|
+
};
|
|
10331
|
+
}
|
|
10332
|
+
function loginCommand(cli, logins, names) {
|
|
10333
|
+
const named = exampleName(names);
|
|
10334
|
+
return defineCommand$1.write({
|
|
10335
|
+
description: "sign in through a browser, so the Commands that need a Login can run",
|
|
10336
|
+
help: "Prints a link and a code for the person who runs this CLI to approve, then waits for the approval and keeps the Login for this user. A rerun takes up the same code. It does nothing when its Secret is set or the stored Login's access token has not expired, and refreshes one that has.",
|
|
10337
|
+
examples: [{
|
|
10338
|
+
command: [
|
|
10339
|
+
cli.name,
|
|
10340
|
+
"login",
|
|
10341
|
+
...named
|
|
10342
|
+
].join(" "),
|
|
10343
|
+
note: `sign in, waiting at most 100 s for the code to be approved`
|
|
10344
|
+
}, {
|
|
10345
|
+
command: [
|
|
10346
|
+
cli.name,
|
|
10347
|
+
"login",
|
|
10348
|
+
...named,
|
|
10349
|
+
WAIT_FLAG.flag,
|
|
10350
|
+
"30"
|
|
10351
|
+
].join(" "),
|
|
10352
|
+
note: "wait at most 30 s for the approval"
|
|
10353
|
+
}],
|
|
10354
|
+
args: nameArgs(names, "sign in to"),
|
|
10355
|
+
options: object({ wait: number$1().int().nonnegative().optional().describe(`${WAIT_FLAG.description}; 100 when absent`) }),
|
|
10356
|
+
output: loginSchema,
|
|
10357
|
+
plan: async ({ name: given, wait }, ctx) => {
|
|
10358
|
+
const name = chosenName({
|
|
10359
|
+
cliName: cli.name,
|
|
10360
|
+
command: "login"
|
|
10361
|
+
}, given, names);
|
|
10362
|
+
const login = declaredLogin(logins, name, ctx.env);
|
|
10363
|
+
const token = await currentTokenOf(login, tokenScopeOf(ctx));
|
|
10364
|
+
const stored = await readStored(loginFilesOf(ctx.userFile, login).stored, ctx.clock);
|
|
10365
|
+
return {
|
|
10366
|
+
login: name,
|
|
10367
|
+
wait,
|
|
10368
|
+
found: token && foundData(name, token),
|
|
10369
|
+
renew: stored?.refreshToken !== void 0
|
|
10370
|
+
};
|
|
10371
|
+
},
|
|
10372
|
+
apply: async ({ login: name, wait, found }, ctx) => {
|
|
10373
|
+
if (found !== void 0) return found;
|
|
10374
|
+
const login = declaredLogin(logins, name, ctx.env);
|
|
10375
|
+
const files = loginFilesOf(ctx.userFile, login);
|
|
10376
|
+
const stored = await readStored(files.stored, ctx.clock);
|
|
10377
|
+
if (stored?.refreshToken !== void 0 && await refreshed(name, login, files.stored, stored.refreshToken, ctx) !== void 0) return {
|
|
10378
|
+
login: name,
|
|
10379
|
+
source: "stored"
|
|
10380
|
+
};
|
|
10381
|
+
await signIn(name, login, files, ctx, {
|
|
10382
|
+
waitS: wait ?? 100,
|
|
10383
|
+
rerun: [
|
|
10384
|
+
"login",
|
|
10385
|
+
name,
|
|
10386
|
+
WAIT_FLAG.flag,
|
|
10387
|
+
String(100)
|
|
10388
|
+
]
|
|
10389
|
+
});
|
|
10390
|
+
return {
|
|
10391
|
+
login: name,
|
|
10392
|
+
source: "approved"
|
|
10393
|
+
};
|
|
10394
|
+
},
|
|
10395
|
+
render: renderLogin,
|
|
10396
|
+
renderPlan: ({ login, found, renew }) => {
|
|
10397
|
+
if (found !== void 0) return renderLogin(found);
|
|
10398
|
+
return renew ? `would refresh the Login ${login}, or sign in through a browser if the server refuses` : `would sign in to ${login} through a browser`;
|
|
10399
|
+
}
|
|
10400
|
+
});
|
|
10401
|
+
}
|
|
10402
|
+
/** The Built-in `login` and `logout`, for a CLI that declares a Login. */
|
|
10403
|
+
function loginBuiltIns(cli) {
|
|
10404
|
+
const logins = cli.logins ?? {};
|
|
10405
|
+
const [first, ...rest] = Object.keys(logins);
|
|
10406
|
+
if (first === void 0) return {};
|
|
10407
|
+
const names = [first, ...rest];
|
|
10408
|
+
return {
|
|
10409
|
+
login: loginCommand(cli, logins, names),
|
|
10410
|
+
logout: logoutCommand(cli, logins, names)
|
|
10411
|
+
};
|
|
10412
|
+
}
|
|
10413
|
+
//#endregion
|
|
9535
10414
|
//#region ../agent-cli/dist/built-ins/commands.js
|
|
9536
10415
|
/**
|
|
9537
10416
|
* The Commands the Runtime adds to a multi-Command CLI: `setup` and `update` only for a Global
|
|
9538
|
-
* install,
|
|
10417
|
+
* install, `skill install` only for a skill whose copy goes under the project, and `login` only
|
|
10418
|
+
* for a CLI that declares a Login.
|
|
9539
10419
|
*/
|
|
9540
10420
|
function builtInCommands(cli, launch) {
|
|
9541
10421
|
const install = installOf(cli);
|
|
9542
10422
|
return {
|
|
9543
10423
|
doctor: doctorCommand(cli, launch),
|
|
9544
10424
|
...install.builtIns(launch),
|
|
9545
|
-
...skillOf(cli, install).builtIns
|
|
10425
|
+
...skillOf(cli, install).builtIns,
|
|
10426
|
+
...loginBuiltIns(cli)
|
|
9546
10427
|
};
|
|
9547
10428
|
}
|
|
9548
10429
|
//#endregion
|
|
@@ -9556,15 +10437,18 @@ function commandLabel(path, cliName) {
|
|
|
9556
10437
|
return path.length === 0 ? [cliName] : path;
|
|
9557
10438
|
}
|
|
9558
10439
|
function declaredOf(command) {
|
|
10440
|
+
const wait = takesWait(command);
|
|
9559
10441
|
if (!hasPlan(command)) return {
|
|
9560
10442
|
outputs: false,
|
|
9561
10443
|
force: void 0,
|
|
9562
|
-
cost: false
|
|
10444
|
+
cost: false,
|
|
10445
|
+
wait
|
|
9563
10446
|
};
|
|
9564
10447
|
return {
|
|
9565
10448
|
outputs: command.outputs !== void 0,
|
|
9566
10449
|
force: command.force,
|
|
9567
|
-
cost: command.cost !== void 0
|
|
10450
|
+
cost: command.cost !== void 0,
|
|
10451
|
+
wait
|
|
9568
10452
|
};
|
|
9569
10453
|
}
|
|
9570
10454
|
function withRunAs(example, { name, runAs }) {
|
|
@@ -9679,11 +10563,11 @@ function checkOwnCodes(errors) {
|
|
|
9679
10563
|
const redeclared = runtimeCodesIn(errors);
|
|
9680
10564
|
if (redeclared.length > 0) throw new Error(`The Error table declares ${redeclared.join(", ")}, an error code the Runtime already declares`);
|
|
9681
10565
|
}
|
|
9682
|
-
function checkTree(commands, path, table) {
|
|
10566
|
+
function checkTree(commands, path, table, logins) {
|
|
9683
10567
|
for (const [key, node] of Object.entries(commands)) {
|
|
9684
10568
|
const at = [...path, key];
|
|
9685
|
-
if (isCommand(node)) checkCommand(node, at.join(" "), table);
|
|
9686
|
-
else checkTree(node, at, table);
|
|
10569
|
+
if (isCommand(node)) checkCommand(node, at.join(" "), table, logins);
|
|
10570
|
+
else checkTree(node, at, table, logins);
|
|
9687
10571
|
}
|
|
9688
10572
|
}
|
|
9689
10573
|
function checkSkill({ name, skill, install }) {
|
|
@@ -9702,13 +10586,13 @@ function checkDeclared(cli) {
|
|
|
9702
10586
|
const table = errorTableOf(cli);
|
|
9703
10587
|
checkDocs(cli);
|
|
9704
10588
|
if (cli.commands === void 0) {
|
|
9705
|
-
checkCommand(cli.command, commandLabel([], cli.name).join(" "), table);
|
|
10589
|
+
checkCommand(cli.command, commandLabel([], cli.name).join(" "), table, {});
|
|
9706
10590
|
if (cli.skill !== void 0) throw new Error(`${cli.name} declares a skill, which only a CLI with several Commands can install`);
|
|
9707
10591
|
return cli;
|
|
9708
10592
|
}
|
|
9709
10593
|
checkSkill(cli);
|
|
9710
10594
|
const { commands } = cli;
|
|
9711
|
-
checkTree(commands, [], table);
|
|
10595
|
+
checkTree(commands, [], table, cli.logins ?? {});
|
|
9712
10596
|
for (const row of cli.doctor ?? []) checkErrors(row.errors ?? [], `the ${row.name} row of doctor`, table);
|
|
9713
10597
|
const taken = Object.keys(builtInCommands(cli, {
|
|
9714
10598
|
bin: void 0,
|
|
@@ -10018,6 +10902,12 @@ async function parseInput(spec, words, invocation, readStdin) {
|
|
|
10018
10902
|
if (only !== void 0 && unknown.length === 1 && otherLines.length === 0) throw unknownOptionError(spec.commandLine, invocation, only.flag, only.suggestion === void 0 ? [] : [only.suggestion]);
|
|
10019
10903
|
throw invalidInput(spec.commandLine, invocation, unknown, otherLines);
|
|
10020
10904
|
}
|
|
10905
|
+
function waitOf(given, commandLine) {
|
|
10906
|
+
if (given === void 0) return void 0;
|
|
10907
|
+
const parsed = WAIT_SECONDS.safeParse(given === true ? void 0 : given);
|
|
10908
|
+
if (!parsed.success) throw invalidInputError(commandLine, `option '${WAIT_FLAG.usage}' takes a whole number of seconds, not ${given === true ? "nothing" : JSON.stringify(given)}`);
|
|
10909
|
+
return parsed.data;
|
|
10910
|
+
}
|
|
10021
10911
|
/**
|
|
10022
10912
|
* The validated input and Standard flag values of the routed Command, or one usage error; the arg
|
|
10023
10913
|
* given `-` takes its text from `readStdin`.
|
|
@@ -10033,6 +10923,7 @@ async function parseCommand({ spec, words, json }, invocation, readStdin) {
|
|
|
10033
10923
|
confirm: typeof confirm === "string" ? confirm : void 0,
|
|
10034
10924
|
yes: values.get(YES_FLAG.flag) === true,
|
|
10035
10925
|
force: values.get(FORCE_FLAG.flag) === true,
|
|
10926
|
+
wait: waitOf(values.get(WAIT_FLAG.flag), spec.commandLine),
|
|
10036
10927
|
page,
|
|
10037
10928
|
fields: spec.list === void 0 ? void 0 : selectedFields(values, spec.list.rowFields, spec.commandLine)
|
|
10038
10929
|
}
|
|
@@ -10710,6 +11601,28 @@ function rootSpec(cli, launch) {
|
|
|
10710
11601
|
} : groupSpec(names, cli.commands, builtInCommands(cli, launch));
|
|
10711
11602
|
}
|
|
10712
11603
|
//#endregion
|
|
11604
|
+
//#region ../agent-cli/dist/built-ins/install/release-too-old.js
|
|
11605
|
+
function isTooOld(error) {
|
|
11606
|
+
return error instanceof CliError && error.code === "RELEASE_TOO_OLD";
|
|
11607
|
+
}
|
|
11608
|
+
function mapLeaves(error, map) {
|
|
11609
|
+
if (error instanceof AggregateError && error.errors.length > 0) return new AggregateError(error.errors.map((inner) => mapLeaves(inner, map)), error.message, { cause: error.cause });
|
|
11610
|
+
return map(error);
|
|
11611
|
+
}
|
|
11612
|
+
/** `error` with the Install's update line leading the Next steps of each `RELEASE_TOO_OLD` in it. */
|
|
11613
|
+
async function withUpdateLine(error, install, ctx) {
|
|
11614
|
+
if (!errorsOf(error).some(isTooOld)) return error;
|
|
11615
|
+
const line = await install.updateLine(ctx);
|
|
11616
|
+
if (line === void 0) return error;
|
|
11617
|
+
return mapLeaves(error, (leaf) => isTooOld(leaf) ? runtimeError({
|
|
11618
|
+
code: leaf.code,
|
|
11619
|
+
message: leaf.message,
|
|
11620
|
+
hint: leaf.hint,
|
|
11621
|
+
details: leaf.details,
|
|
11622
|
+
next: [line, ...leaf.next]
|
|
11623
|
+
}, { cause: leaf.cause }) : leaf);
|
|
11624
|
+
}
|
|
11625
|
+
//#endregion
|
|
10713
11626
|
//#region ../agent-cli/dist/context/spill.js
|
|
10714
11627
|
const STDOUT_BUDGET_CHARS = 3e4;
|
|
10715
11628
|
const SPILL_RETENTION_MS = 6048e5;
|
|
@@ -11290,6 +12203,114 @@ function resolveOptions(options) {
|
|
|
11290
12203
|
};
|
|
11291
12204
|
}
|
|
11292
12205
|
//#endregion
|
|
12206
|
+
//#region ../agent-cli/dist/context/derived.js
|
|
12207
|
+
/**
|
|
12208
|
+
* `base` with `extra`'s properties over it, built on `base` as its prototype, so a lazy getter of
|
|
12209
|
+
* `base` such as `ctx.ports` is not read, as a spread would.
|
|
12210
|
+
*/
|
|
12211
|
+
function derived(base, extra) {
|
|
12212
|
+
const child = Object.create(base);
|
|
12213
|
+
return Object.assign(child, extra);
|
|
12214
|
+
}
|
|
12215
|
+
//#endregion
|
|
12216
|
+
//#region ../agent-cli/dist/login/with-logins.js
|
|
12217
|
+
/** The rerun of an `APPROVAL_PENDING`, which waits for the approval. */
|
|
12218
|
+
function approvalRerun(ctx) {
|
|
12219
|
+
return ctx.rerun({
|
|
12220
|
+
remove: [WAIT_FLAG.flag],
|
|
12221
|
+
add: [WAIT_FLAG.flag, String(100)]
|
|
12222
|
+
});
|
|
12223
|
+
}
|
|
12224
|
+
async function tokensOf(names, logins, ctx, waitS) {
|
|
12225
|
+
const found = await Promise.all(names.map(async (name) => {
|
|
12226
|
+
const login = declaredLogin(logins, name, ctx.env);
|
|
12227
|
+
return {
|
|
12228
|
+
name,
|
|
12229
|
+
token: ctx.dryRun ? await currentTokenOf(login, tokenScopeOf(ctx)) : await freshTokenOf(name, login, ctx)
|
|
12230
|
+
};
|
|
12231
|
+
}));
|
|
12232
|
+
const missing = found.flatMap(({ name, token }) => token === void 0 ? [name] : []);
|
|
12233
|
+
if (missing.length > 0 && ctx.dryRun) throw loginRequired(missing, ctx.rerun());
|
|
12234
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
12235
|
+
for (const { name, token } of found) {
|
|
12236
|
+
if (token !== void 0) {
|
|
12237
|
+
tokens.set(name, token);
|
|
12238
|
+
continue;
|
|
12239
|
+
}
|
|
12240
|
+
const login = declaredLogin(logins, name, ctx.env);
|
|
12241
|
+
const stored = await signIn(name, login, loginFilesOf(ctx.userFile, login), ctx, {
|
|
12242
|
+
waitS,
|
|
12243
|
+
rerun: approvalRerun(ctx)
|
|
12244
|
+
});
|
|
12245
|
+
tokens.set(name, storedToken(stored));
|
|
12246
|
+
}
|
|
12247
|
+
ctx.signal.throwIfAborted();
|
|
12248
|
+
return tokens;
|
|
12249
|
+
}
|
|
12250
|
+
/** The Login a `LOGIN_REJECTED` names in `details.login`, or the Command's only one. */
|
|
12251
|
+
function rejectedName(rejected, tokens) {
|
|
12252
|
+
const { details } = rejected;
|
|
12253
|
+
if (typeof details === "object" && details !== null && "login" in details) return typeof details.login === "string" ? details.login : void 0;
|
|
12254
|
+
const [only, ...rest] = tokens.keys();
|
|
12255
|
+
return rest.length === 0 ? only : void 0;
|
|
12256
|
+
}
|
|
12257
|
+
/**
|
|
12258
|
+
* What a run whose Command threw `error` fails with: a `LOGIN_REJECTED` of a stored Login drops
|
|
12259
|
+
* it for a new Approval, one of a Secret's token fails as the operator's to fix.
|
|
12260
|
+
*/
|
|
12261
|
+
async function afterRejection(error, tokens, logins, ctx) {
|
|
12262
|
+
if (!(error instanceof CliError && error.code === "LOGIN_REJECTED")) return error;
|
|
12263
|
+
const name = rejectedName(error, tokens);
|
|
12264
|
+
const token = name === void 0 ? void 0 : tokens.get(name);
|
|
12265
|
+
if (name === void 0 || token === void 0) return new Error(`the Command threw ${LOGIN_REJECTED} naming no Login it declares in details.login`, { cause: error });
|
|
12266
|
+
const login = declaredLogin(logins, name, ctx.env);
|
|
12267
|
+
if (token.source.kind === "secret" && login.secret !== void 0) return secretRejected(name, login.secret, error);
|
|
12268
|
+
const files = loginFilesOf(ctx.userFile, login);
|
|
12269
|
+
await files.stored.remove();
|
|
12270
|
+
if (ctx.dryRun) return loginRequired([name], ctx.rerun());
|
|
12271
|
+
return await pendingApproval(name, login, files, ctx, approvalRerun(ctx));
|
|
12272
|
+
}
|
|
12273
|
+
/** `ctx.grant`: polls the Approval of `scope` for at most `waitS` seconds, and keeps no token. */
|
|
12274
|
+
function granting(ctx, grants, logins, waitS) {
|
|
12275
|
+
return async (name, { scope }) => {
|
|
12276
|
+
if (!grants.includes(name)) undeclaredGrant(name);
|
|
12277
|
+
if (ctx.dryRun) throw new Error(`ctx.grant("${name}") in a dry run, which asks for nothing: ask in apply`);
|
|
12278
|
+
const login = {
|
|
12279
|
+
...declaredLogin(logins, name, ctx.env),
|
|
12280
|
+
scope
|
|
12281
|
+
};
|
|
12282
|
+
return await grant(grantAsking(name, scope, ctx.rerun({ remove: [WAIT_FLAG.flag] })), login, grantApprovalOf(ctx.userFile, login, scope), ctx, {
|
|
12283
|
+
waitS,
|
|
12284
|
+
rerun: approvalRerun(ctx)
|
|
12285
|
+
});
|
|
12286
|
+
};
|
|
12287
|
+
}
|
|
12288
|
+
/**
|
|
12289
|
+
* Runs `work` with a `ctx` whose `login` gives each named Login's token, signing in first, for at
|
|
12290
|
+
* most `waitS` seconds, where one is missing, and whose `grant` asks for a Grant. A
|
|
12291
|
+
* `LOGIN_REJECTED` from `work` drops the stored Login.
|
|
12292
|
+
*/
|
|
12293
|
+
async function withLogins(ctx, { logins: names, grants }, logins, waitS, work) {
|
|
12294
|
+
const tokens = names.length === 0 ? /* @__PURE__ */ new Map() : await tokensOf(names, logins, ctx, waitS ?? 0);
|
|
12295
|
+
const loggedIn = derived(ctx, {
|
|
12296
|
+
login: (name) => tokens.get(name)?.value ?? undeclaredLogin(name),
|
|
12297
|
+
grant: granting(ctx, grants, logins, waitS ?? 0)
|
|
12298
|
+
});
|
|
12299
|
+
try {
|
|
12300
|
+
return await work(loggedIn);
|
|
12301
|
+
} catch (error) {
|
|
12302
|
+
throw await afterRejection(error, tokens, logins, ctx);
|
|
12303
|
+
}
|
|
12304
|
+
}
|
|
12305
|
+
/** `ctx.grant` of a name the Command does not declare in `grants`, which only a cast reaches. */
|
|
12306
|
+
function undeclaredGrant(name) {
|
|
12307
|
+
throw new Error(`ctx.grant("${name}") names a Login the Command does not declare in grants`);
|
|
12308
|
+
}
|
|
12309
|
+
/** `ctx.login` of a name the Command does not declare, which only a cast reaches. */
|
|
12310
|
+
function undeclaredLogin(name) {
|
|
12311
|
+
throw new Error(`ctx.login("${name}") names a Login the Command does not declare`);
|
|
12312
|
+
}
|
|
12313
|
+
//#endregion
|
|
11293
12314
|
//#region ../agent-cli/dist/execute.js
|
|
11294
12315
|
const PLAN_INDENT = " ";
|
|
11295
12316
|
function outcomeOf(value, render) {
|
|
@@ -11334,17 +12355,22 @@ function dryRunOutcome(command, plan, commandLine) {
|
|
|
11334
12355
|
};
|
|
11335
12356
|
}
|
|
11336
12357
|
function serveContextOf(ctx, end) {
|
|
11337
|
-
|
|
11338
|
-
return Object.assign(serveCtx, { end });
|
|
12358
|
+
return derived(ctx, { end });
|
|
11339
12359
|
}
|
|
11340
|
-
async function execute(command, input,
|
|
11341
|
-
await checkTools(command.tools ?? [],
|
|
11342
|
-
const { env, envFile, fileError } =
|
|
12360
|
+
async function execute(command, input, unlogged, run) {
|
|
12361
|
+
await checkTools(command.tools ?? [], unlogged, unlogged.rerun());
|
|
12362
|
+
const { env, envFile, fileError } = unlogged;
|
|
11343
12363
|
checkSecrets(command.secrets ?? [], {
|
|
11344
12364
|
env,
|
|
11345
12365
|
envFile,
|
|
11346
12366
|
fileError
|
|
11347
|
-
},
|
|
12367
|
+
}, unlogged.rerun());
|
|
12368
|
+
return await withLogins(unlogged, {
|
|
12369
|
+
logins: command.logins ?? [],
|
|
12370
|
+
grants: command.grants ?? []
|
|
12371
|
+
}, run.logins, run.flags.wait, (ctx) => executeLoggedIn(command, input, ctx, run));
|
|
12372
|
+
}
|
|
12373
|
+
async function executeLoggedIn(command, input, ctx, { flags, commandLine, started, outputs: runOutputs }) {
|
|
11348
12374
|
if (command.rows !== void 0) {
|
|
11349
12375
|
const { render } = command;
|
|
11350
12376
|
return outcomeOf(await command.run({
|
|
@@ -11401,6 +12427,66 @@ function manifestAt(packageRoot) {
|
|
|
11401
12427
|
return parsed.success ? parsed.data : NO_MANIFEST;
|
|
11402
12428
|
}
|
|
11403
12429
|
//#endregion
|
|
12430
|
+
//#region ../agent-cli/dist/context/user-file.js
|
|
12431
|
+
const FOLDER_MODE = 448;
|
|
12432
|
+
const FILE_MODE = 384;
|
|
12433
|
+
/** `$XDG_STATE_HOME/<cli>`, with `~/.local/state` when the variable is unset or empty. */
|
|
12434
|
+
function stateFolderOf(cliName, env) {
|
|
12435
|
+
const state = env.XDG_STATE_HOME || join(env.HOME || homedir(), ".local", "state");
|
|
12436
|
+
return join(state, cliName);
|
|
12437
|
+
}
|
|
12438
|
+
function parsed(bytes, schema) {
|
|
12439
|
+
let json;
|
|
12440
|
+
try {
|
|
12441
|
+
json = JSON.parse(new TextDecoder().decode(bytes));
|
|
12442
|
+
} catch {
|
|
12443
|
+
return;
|
|
12444
|
+
}
|
|
12445
|
+
const result = schema.safeParse(json);
|
|
12446
|
+
return result.success ? result.data : void 0;
|
|
12447
|
+
}
|
|
12448
|
+
function userFileOf({ folder, files, fileError }) {
|
|
12449
|
+
return (name, schema) => {
|
|
12450
|
+
const path = join(folder, `${name}.json`);
|
|
12451
|
+
return {
|
|
12452
|
+
read: async () => {
|
|
12453
|
+
let bytes;
|
|
12454
|
+
try {
|
|
12455
|
+
bytes = await files.read(path);
|
|
12456
|
+
} catch (error) {
|
|
12457
|
+
if (isMissingFile(error)) return void 0;
|
|
12458
|
+
throw fileError(error, {
|
|
12459
|
+
path,
|
|
12460
|
+
access: "read"
|
|
12461
|
+
});
|
|
12462
|
+
}
|
|
12463
|
+
return parsed(bytes, schema);
|
|
12464
|
+
},
|
|
12465
|
+
write: async (value) => {
|
|
12466
|
+
try {
|
|
12467
|
+
await files.mkdir(dirname(path), { mode: FOLDER_MODE });
|
|
12468
|
+
await files.write(path, `${JSON.stringify(value)}\n`, { mode: FILE_MODE });
|
|
12469
|
+
} catch (error) {
|
|
12470
|
+
throw fileError(error, {
|
|
12471
|
+
path,
|
|
12472
|
+
access: "write"
|
|
12473
|
+
});
|
|
12474
|
+
}
|
|
12475
|
+
},
|
|
12476
|
+
remove: async () => {
|
|
12477
|
+
try {
|
|
12478
|
+
await files.remove(path);
|
|
12479
|
+
} catch (error) {
|
|
12480
|
+
throw fileError(error, {
|
|
12481
|
+
path,
|
|
12482
|
+
access: "write"
|
|
12483
|
+
});
|
|
12484
|
+
}
|
|
12485
|
+
}
|
|
12486
|
+
};
|
|
12487
|
+
};
|
|
12488
|
+
}
|
|
12489
|
+
//#endregion
|
|
11404
12490
|
//#region ../agent-cli/dist/run-scope.js
|
|
11405
12491
|
function selfOf(name, bin) {
|
|
11406
12492
|
return bin === void 0 ? [name] : [
|
|
@@ -11439,12 +12525,19 @@ function runScopeOf(cli, routed, settings, write, baseInvocation) {
|
|
|
11439
12525
|
env,
|
|
11440
12526
|
platform
|
|
11441
12527
|
});
|
|
11442
|
-
const
|
|
12528
|
+
const files = settings.files ?? realFiles();
|
|
12529
|
+
const outputs = outputsOf(files, {
|
|
11443
12530
|
cwd,
|
|
11444
12531
|
fileError
|
|
11445
12532
|
});
|
|
11446
12533
|
const packageRoot = packageRootOf(settings.bin, cwd);
|
|
11447
12534
|
const progress = (message) => write.stderr(progressLine(message, routed.json));
|
|
12535
|
+
const noticed = /* @__PURE__ */ new Set();
|
|
12536
|
+
const notice = (text) => {
|
|
12537
|
+
if (noticed.has(text)) return;
|
|
12538
|
+
noticed.add(text);
|
|
12539
|
+
write.stderr(noticeLine(text, routed.json));
|
|
12540
|
+
};
|
|
11448
12541
|
const envFile = envFileOf(cli.name, env);
|
|
11449
12542
|
const spawning = {
|
|
11450
12543
|
fileError,
|
|
@@ -11458,8 +12551,15 @@ function runScopeOf(cli, routed, settings, write, baseInvocation) {
|
|
|
11458
12551
|
platform,
|
|
11459
12552
|
outputs,
|
|
11460
12553
|
progress,
|
|
11461
|
-
notice
|
|
12554
|
+
notice,
|
|
11462
12555
|
packageRoot,
|
|
12556
|
+
fetch: settings.fetch ?? ((input, init) => globalThis.fetch(input, init)),
|
|
12557
|
+
userAgent: `${cli.name}/${cli.version}`,
|
|
12558
|
+
userFile: userFileOf({
|
|
12559
|
+
folder: stateFolderOf(cli.name, env),
|
|
12560
|
+
files,
|
|
12561
|
+
fileError
|
|
12562
|
+
}),
|
|
11463
12563
|
ports: portsOf(cli, settings.ports),
|
|
11464
12564
|
portsBase: (signal) => ({
|
|
11465
12565
|
cwd,
|
|
@@ -11493,7 +12593,7 @@ function runScopeOf(cli, routed, settings, write, baseInvocation) {
|
|
|
11493
12593
|
})
|
|
11494
12594
|
};
|
|
11495
12595
|
}
|
|
11496
|
-
function contextOf(scope, control, { command, flags, project, deliver }) {
|
|
12596
|
+
function contextOf(scope, control, { command, flags, project, deliver, latestRelease }) {
|
|
11497
12597
|
const portsBase = scope.portsBase(control.signal);
|
|
11498
12598
|
const ports = once(() => scope.ports(portsBase));
|
|
11499
12599
|
const declared = new Map((command.secrets ?? []).map((declaration) => [declaration.name, declaration]));
|
|
@@ -11507,8 +12607,18 @@ function contextOf(scope, control, { command, flags, project, deliver }) {
|
|
|
11507
12607
|
return {
|
|
11508
12608
|
...portsBase,
|
|
11509
12609
|
secret,
|
|
12610
|
+
login: undeclaredLogin,
|
|
12611
|
+
grant: async (name) => undeclaredGrant(name),
|
|
12612
|
+
userFile: scope.userFile,
|
|
11510
12613
|
fail: cliErrorOf,
|
|
11511
12614
|
deadline: (ms, options) => control.deadline(ms, options),
|
|
12615
|
+
fetch: contextFetch({
|
|
12616
|
+
fetch: scope.fetch,
|
|
12617
|
+
userAgent: scope.userAgent,
|
|
12618
|
+
deadline: (ms, options) => control.deadline(ms, options),
|
|
12619
|
+
clock: scope.clock,
|
|
12620
|
+
rerun: scope.rerun()
|
|
12621
|
+
}),
|
|
11512
12622
|
runToEnd: (name, work) => control.runToEnd(name, work),
|
|
11513
12623
|
get ports() {
|
|
11514
12624
|
return ports();
|
|
@@ -11519,7 +12629,8 @@ function contextOf(scope, control, { command, flags, project, deliver }) {
|
|
|
11519
12629
|
rerun: scope.rerun,
|
|
11520
12630
|
poll: (check, { intervalMs, signal: stop }) => poll(portsBase.clock, check, intervalMs, stop === void 0 ? control.signal : AbortSignal.any([control.signal, stop])),
|
|
11521
12631
|
deliver,
|
|
11522
|
-
notice: scope.notice
|
|
12632
|
+
notice: scope.notice,
|
|
12633
|
+
latestRelease
|
|
11523
12634
|
};
|
|
11524
12635
|
}
|
|
11525
12636
|
/** `build` runs on the first call; later calls give its value or throw its error again. */
|
|
@@ -11644,10 +12755,17 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11644
12755
|
return ran(await failed(error), sideTask);
|
|
11645
12756
|
}
|
|
11646
12757
|
let delivered;
|
|
12758
|
+
const release = installOf(cli).releaseCheck({
|
|
12759
|
+
builtIn,
|
|
12760
|
+
bin: options.bin,
|
|
12761
|
+
node: options.node,
|
|
12762
|
+
platform
|
|
12763
|
+
});
|
|
11647
12764
|
const ctx = contextOf(scope, control, {
|
|
11648
12765
|
command,
|
|
11649
12766
|
flags,
|
|
11650
12767
|
project,
|
|
12768
|
+
latestRelease: (version) => release.latestRelease(version, ctx),
|
|
11651
12769
|
deliver: (data) => {
|
|
11652
12770
|
delivered = () => dataOutcome(command, data, commandLine);
|
|
11653
12771
|
}
|
|
@@ -11663,19 +12781,15 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11663
12781
|
let served = false;
|
|
11664
12782
|
let settled;
|
|
11665
12783
|
try {
|
|
11666
|
-
const outcome = await control.settle(
|
|
11667
|
-
builtIn,
|
|
11668
|
-
bin: options.bin,
|
|
11669
|
-
node: options.node,
|
|
11670
|
-
platform
|
|
11671
|
-
}, ctx).then(() => execute(command, input, ctx, {
|
|
12784
|
+
const outcome = await control.settle(release.notice(ctx).then(() => execute(command, input, ctx, {
|
|
11672
12785
|
flags,
|
|
11673
12786
|
commandLine,
|
|
11674
12787
|
started: async (outcome) => {
|
|
11675
12788
|
write.stdout(await startedOutput(outcome, format, spill));
|
|
11676
12789
|
served = true;
|
|
11677
12790
|
},
|
|
11678
|
-
outputs
|
|
12791
|
+
outputs,
|
|
12792
|
+
logins: cli.logins ?? {}
|
|
11679
12793
|
})));
|
|
11680
12794
|
if (served) return ran(STOPPED, sideTask);
|
|
11681
12795
|
settled = {
|
|
@@ -11686,7 +12800,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11686
12800
|
if (served && control.signal.aborted) return ran(STOPPED, sideTask);
|
|
11687
12801
|
settled = {
|
|
11688
12802
|
ok: false,
|
|
11689
|
-
error,
|
|
12803
|
+
error: await withUpdateLine(error, installOf(cli), ctx),
|
|
11690
12804
|
delivered
|
|
11691
12805
|
};
|
|
11692
12806
|
}
|
|
@@ -11695,9 +12809,6 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11695
12809
|
fields: flags.fields
|
|
11696
12810
|
}, spill), sideTask);
|
|
11697
12811
|
}
|
|
11698
|
-
async function noticesOf(cli, launch, ctx) {
|
|
11699
|
-
await installOf(cli).notice(launch, ctx);
|
|
11700
|
-
}
|
|
11701
12812
|
//#endregion
|
|
11702
12813
|
//#region ../agent-cli/dist/style.js
|
|
11703
12814
|
function colourEnabled(env, stdoutIsTTY) {
|
|
@@ -11856,7 +12967,7 @@ async function runProcess(cli) {
|
|
|
11856
12967
|
//#region package.json
|
|
11857
12968
|
var package_default = {
|
|
11858
12969
|
name: "@duyquangnvx/create-agent-cli",
|
|
11859
|
-
version: "0.
|
|
12970
|
+
version: "0.32.0",
|
|
11860
12971
|
description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
11861
12972
|
type: "module",
|
|
11862
12973
|
engines: { "node": ">=22.12.0" },
|