@duyquangnvx/create-agent-cli 0.30.0 → 0.31.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 +1483 -427
- 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) {
|
|
@@ -5740,6 +5760,41 @@ function untilAborted(signal) {
|
|
|
5740
5760
|
});
|
|
5741
5761
|
}
|
|
5742
5762
|
//#endregion
|
|
5763
|
+
//#region ../agent-cli/dist/clock.js
|
|
5764
|
+
const MS_PER_S = 1e3;
|
|
5765
|
+
const realClock = {
|
|
5766
|
+
now: () => Date.now(),
|
|
5767
|
+
sleep: (ms, signal) => new Promise((resolve, reject) => {
|
|
5768
|
+
if (signal?.aborted === true) {
|
|
5769
|
+
reject(signal.reason);
|
|
5770
|
+
return;
|
|
5771
|
+
}
|
|
5772
|
+
const onAbort = () => {
|
|
5773
|
+
clearTimeout(timer);
|
|
5774
|
+
reject(signal?.reason);
|
|
5775
|
+
};
|
|
5776
|
+
const timer = setTimeout(() => {
|
|
5777
|
+
signal?.removeEventListener("abort", onAbort);
|
|
5778
|
+
resolve();
|
|
5779
|
+
}, ms);
|
|
5780
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
5781
|
+
})
|
|
5782
|
+
};
|
|
5783
|
+
/** Runs `then` once `ms` have passed on `clock`; aborting `cancel` drops it. */
|
|
5784
|
+
function after(clock, ms, cancel, then) {
|
|
5785
|
+
clock.sleep(ms, cancel).then(then, (error) => {
|
|
5786
|
+
if (!cancel.aborted) throw error;
|
|
5787
|
+
});
|
|
5788
|
+
}
|
|
5789
|
+
async function poll(clock, check, intervalMs, signal) {
|
|
5790
|
+
for (;;) {
|
|
5791
|
+
signal.throwIfAborted();
|
|
5792
|
+
const value = await check();
|
|
5793
|
+
if (value !== void 0) return value;
|
|
5794
|
+
await clock.sleep(intervalMs, signal);
|
|
5795
|
+
}
|
|
5796
|
+
}
|
|
5797
|
+
//#endregion
|
|
5743
5798
|
//#region ../agent-cli/dist/command-line/tokenize.js
|
|
5744
5799
|
const NEGATIVE_NUMBER = /^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/;
|
|
5745
5800
|
function isOptionLike(word) {
|
|
@@ -6064,6 +6119,290 @@ function lastLines(output, count) {
|
|
|
6064
6119
|
return output.trim().split("\n").slice(-count).join("\n");
|
|
6065
6120
|
}
|
|
6066
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
|
|
6260
|
+
//#region ../agent-cli/dist/context/interrupts.js
|
|
6261
|
+
const INTERRUPTED = "INTERRUPTED";
|
|
6262
|
+
const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
|
|
6263
|
+
/** How long an interrupted Command may take to settle before the run ends without it. */
|
|
6264
|
+
const GRACE_MS = 3e3;
|
|
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]);
|
|
6268
|
+
function interruptedError(rerun) {
|
|
6269
|
+
return new CliError({
|
|
6270
|
+
code: INTERRUPTED,
|
|
6271
|
+
message: "interrupted before it finished",
|
|
6272
|
+
hint: "to finish it, run the Next step, unless it was stopped on purpose; if a tool timeout stopped it, give it longer",
|
|
6273
|
+
next: [rerun]
|
|
6274
|
+
});
|
|
6275
|
+
}
|
|
6276
|
+
const BOUND_WORDS = {
|
|
6277
|
+
deadline: "deadline",
|
|
6278
|
+
ignoreStop: "timeout"
|
|
6279
|
+
};
|
|
6280
|
+
function waitHint(bound) {
|
|
6281
|
+
return bound === "ignoreStop" ? "the call may have been billed: ask your user before running the Next step" : "run the Next step later, when the service it waits on may answer faster";
|
|
6282
|
+
}
|
|
6283
|
+
function deadlineError(commandLine, ms, advice, bound = "deadline") {
|
|
6284
|
+
return new CliError({
|
|
6285
|
+
code: DEADLINE_EXCEEDED,
|
|
6286
|
+
message: `${commandLine} ran past its ${ms} ms ${BOUND_WORDS[bound]}`,
|
|
6287
|
+
...advice
|
|
6288
|
+
});
|
|
6289
|
+
}
|
|
6290
|
+
/** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
|
|
6291
|
+
var RunControl = class {
|
|
6292
|
+
signal;
|
|
6293
|
+
#controller = new AbortController();
|
|
6294
|
+
#commandLine;
|
|
6295
|
+
#rerun;
|
|
6296
|
+
#outer;
|
|
6297
|
+
#onOuterAbort = () => this.#controller.abort(interruptedError(this.#rerun));
|
|
6298
|
+
/** Each running must-finish step, tracked until it settles, with its name. */
|
|
6299
|
+
#steps = /* @__PURE__ */ new Map();
|
|
6300
|
+
#progress;
|
|
6301
|
+
#clock;
|
|
6302
|
+
#disposed = new AbortController();
|
|
6303
|
+
/** The reasons this run's deadlines aborted with, to tell them in a Command's error. */
|
|
6304
|
+
#abortReasons = /* @__PURE__ */ new WeakSet();
|
|
6305
|
+
#graceCancel = new AbortController();
|
|
6306
|
+
#endGrace = () => {};
|
|
6307
|
+
#graceOver = new Promise((resolve) => {
|
|
6308
|
+
this.#endGrace = resolve;
|
|
6309
|
+
});
|
|
6310
|
+
constructor({ commandLine, rerun, outer, clock, progress, runDeadline }) {
|
|
6311
|
+
this.#commandLine = commandLine;
|
|
6312
|
+
this.#rerun = rerun;
|
|
6313
|
+
this.#outer = outer;
|
|
6314
|
+
this.#clock = clock;
|
|
6315
|
+
this.#progress = progress;
|
|
6316
|
+
this.signal = this.#controller.signal;
|
|
6317
|
+
this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
|
|
6318
|
+
if (outer?.aborted === true) this.#onOuterAbort();
|
|
6319
|
+
else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
|
|
6320
|
+
if (runDeadline !== void 0) {
|
|
6321
|
+
const { ms, advice } = runDeadline;
|
|
6322
|
+
after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, advice())));
|
|
6323
|
+
}
|
|
6324
|
+
}
|
|
6325
|
+
deadline(ms, options = {}) {
|
|
6326
|
+
return options.ignoreStop === true ? this.#abortsAfter(ms, "ignoreStop") : AbortSignal.any([this.signal, this.#abortsAfter(ms, "deadline")]);
|
|
6327
|
+
}
|
|
6328
|
+
#abortsAfter(ms, bound) {
|
|
6329
|
+
const controller = new AbortController();
|
|
6330
|
+
after(this.#clock, ms, this.#disposed.signal, () => {
|
|
6331
|
+
const advice = {
|
|
6332
|
+
hint: waitHint(bound),
|
|
6333
|
+
next: [this.#rerun]
|
|
6334
|
+
};
|
|
6335
|
+
const reason = deadlineError(this.#commandLine, ms, advice, bound);
|
|
6336
|
+
this.#abortReasons.add(reason);
|
|
6337
|
+
controller.abort(reason);
|
|
6338
|
+
});
|
|
6339
|
+
return controller.signal;
|
|
6340
|
+
}
|
|
6341
|
+
async runToEnd(name, work) {
|
|
6342
|
+
this.#graceCancel.abort();
|
|
6343
|
+
if (this.signal.aborted) this.#finishing(name);
|
|
6344
|
+
const step = work();
|
|
6345
|
+
const settled = step.then(() => {}, () => {});
|
|
6346
|
+
this.#steps.set(settled, name);
|
|
6347
|
+
try {
|
|
6348
|
+
return await step;
|
|
6349
|
+
} finally {
|
|
6350
|
+
this.#steps.delete(settled);
|
|
6351
|
+
this.#startGrace();
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6354
|
+
/** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
|
|
6355
|
+
async settle(work) {
|
|
6356
|
+
try {
|
|
6357
|
+
const value = await Promise.race([work, this.#graceOver]);
|
|
6358
|
+
if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
|
|
6359
|
+
return value;
|
|
6360
|
+
} finally {
|
|
6361
|
+
await this.#stepsDone();
|
|
6362
|
+
this.close();
|
|
6363
|
+
}
|
|
6364
|
+
}
|
|
6365
|
+
/**
|
|
6366
|
+
* The error a run ends with: an interrupt or the run deadline outranks what the Command threw,
|
|
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.
|
|
6369
|
+
* Otherwise each leaf that is not a `CliError` and whose cause chain holds a deadline's abort is
|
|
6370
|
+
* reported as that abort, so a declared error keeps its own code.
|
|
6371
|
+
*/
|
|
6372
|
+
errorAfter(error) {
|
|
6373
|
+
if (this.signal.aborted) return error instanceof CliError && OUTRANKS_STOP.has(error.code) ? error : this.signal.reason;
|
|
6374
|
+
const leaves = errorsOf(error).map((leaf) => this.#abortIn(leaf) ?? leaf);
|
|
6375
|
+
return leaves.length === 1 ? leaves[0] : new AggregateError(leaves);
|
|
6376
|
+
}
|
|
6377
|
+
#abortIn(error) {
|
|
6378
|
+
if (error instanceof CliError) return void 0;
|
|
6379
|
+
return causesOf(error).find((each) => each instanceof CliError && this.#abortReasons.has(each));
|
|
6380
|
+
}
|
|
6381
|
+
#onStop() {
|
|
6382
|
+
for (const name of this.#steps.values()) this.#finishing(name);
|
|
6383
|
+
this.#startGrace();
|
|
6384
|
+
}
|
|
6385
|
+
#finishing(name) {
|
|
6386
|
+
this.#progress(`finishing ${name}`);
|
|
6387
|
+
}
|
|
6388
|
+
#startGrace() {
|
|
6389
|
+
if (this.signal.aborted && this.#steps.size === 0) {
|
|
6390
|
+
this.#graceCancel.abort();
|
|
6391
|
+
this.#graceCancel = new AbortController();
|
|
6392
|
+
after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
|
|
6393
|
+
}
|
|
6394
|
+
}
|
|
6395
|
+
async #stepsDone() {
|
|
6396
|
+
while (this.#steps.size > 0) await Promise.all(this.#steps.keys());
|
|
6397
|
+
}
|
|
6398
|
+
/** Stops the run's timers and its hold on the caller's signal, for a run that never settles. */
|
|
6399
|
+
close() {
|
|
6400
|
+
this.#graceCancel.abort();
|
|
6401
|
+
this.#disposed.abort();
|
|
6402
|
+
this.#outer?.removeEventListener("abort", this.#onOuterAbort);
|
|
6403
|
+
}
|
|
6404
|
+
};
|
|
6405
|
+
//#endregion
|
|
6067
6406
|
//#region ../agent-cli/dist/define.js
|
|
6068
6407
|
function isCommand(node) {
|
|
6069
6408
|
return typeof node.effect === "string";
|
|
@@ -6146,6 +6485,7 @@ function toolRows(tools, commands, platform) {
|
|
|
6146
6485
|
const TOOL_FAILED = "TOOL_FAILED";
|
|
6147
6486
|
const STDERR_TAIL_LINES$1 = 20;
|
|
6148
6487
|
const SIGNAL_EXIT_BASE = 128;
|
|
6488
|
+
const KILL_AFTER_MS = 2e3;
|
|
6149
6489
|
const BATCH_FILE = /\.(?:cmd|bat)$/i;
|
|
6150
6490
|
const CMD_SHIM = /\.cmd$/i;
|
|
6151
6491
|
const CMD_META = /([()\][%!^"`<>&|;, *?])/g;
|
|
@@ -6182,9 +6522,10 @@ async function windowsLaunch(command, args, env) {
|
|
|
6182
6522
|
shell: true
|
|
6183
6523
|
};
|
|
6184
6524
|
}
|
|
6185
|
-
function stop(child) {
|
|
6525
|
+
function stop(child, exited) {
|
|
6186
6526
|
if (process.platform !== "win32" || child.pid === void 0) {
|
|
6187
6527
|
child.kill();
|
|
6528
|
+
after(realClock, KILL_AFTER_MS, exited, () => child.kill("SIGKILL"));
|
|
6188
6529
|
return Promise.resolve();
|
|
6189
6530
|
}
|
|
6190
6531
|
const pid = String(child.pid);
|
|
@@ -6280,8 +6621,9 @@ async function spawnChild(command, args, { cwd, env, signal, onLine, stdin, deta
|
|
|
6280
6621
|
detached: detached && process.platform !== "win32"
|
|
6281
6622
|
});
|
|
6282
6623
|
let stopped = Promise.resolve();
|
|
6624
|
+
const exited = new AbortController();
|
|
6283
6625
|
const kill = () => {
|
|
6284
|
-
stopped = stop(child);
|
|
6626
|
+
stopped = stop(child, exited.signal);
|
|
6285
6627
|
};
|
|
6286
6628
|
signal.addEventListener("abort", kill, { once: true });
|
|
6287
6629
|
let callerFailure;
|
|
@@ -6304,7 +6646,9 @@ async function spawnChild(command, args, { cwd, env, signal, onLine, stdin, deta
|
|
|
6304
6646
|
stderrLines.push(chunk);
|
|
6305
6647
|
});
|
|
6306
6648
|
if (stdin !== void 0 && child.stdin !== null) feed(child.stdin, stdin, signal).catch(fail);
|
|
6649
|
+
child.once("exit", () => exited.abort());
|
|
6307
6650
|
child.on("error", (error) => {
|
|
6651
|
+
exited.abort();
|
|
6308
6652
|
signal.removeEventListener("abort", kill);
|
|
6309
6653
|
reject(error);
|
|
6310
6654
|
});
|
|
@@ -6453,40 +6797,6 @@ function contextSpawnDetached(run, defaults, scope) {
|
|
|
6453
6797
|
};
|
|
6454
6798
|
}
|
|
6455
6799
|
//#endregion
|
|
6456
|
-
//#region ../agent-cli/dist/clock.js
|
|
6457
|
-
const realClock = {
|
|
6458
|
-
now: () => Date.now(),
|
|
6459
|
-
sleep: (ms, signal) => new Promise((resolve, reject) => {
|
|
6460
|
-
if (signal?.aborted === true) {
|
|
6461
|
-
reject(signal.reason);
|
|
6462
|
-
return;
|
|
6463
|
-
}
|
|
6464
|
-
const onAbort = () => {
|
|
6465
|
-
clearTimeout(timer);
|
|
6466
|
-
reject(signal?.reason);
|
|
6467
|
-
};
|
|
6468
|
-
const timer = setTimeout(() => {
|
|
6469
|
-
signal?.removeEventListener("abort", onAbort);
|
|
6470
|
-
resolve();
|
|
6471
|
-
}, ms);
|
|
6472
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
6473
|
-
})
|
|
6474
|
-
};
|
|
6475
|
-
/** Runs `then` once `ms` have passed on `clock`; aborting `cancel` drops it. */
|
|
6476
|
-
function after(clock, ms, cancel, then) {
|
|
6477
|
-
clock.sleep(ms, cancel).then(then, (error) => {
|
|
6478
|
-
if (!cancel.aborted) throw error;
|
|
6479
|
-
});
|
|
6480
|
-
}
|
|
6481
|
-
async function poll(clock, check, intervalMs, signal) {
|
|
6482
|
-
for (;;) {
|
|
6483
|
-
signal.throwIfAborted();
|
|
6484
|
-
const value = await check();
|
|
6485
|
-
if (value !== void 0) return value;
|
|
6486
|
-
await clock.sleep(intervalMs, signal);
|
|
6487
|
-
}
|
|
6488
|
-
}
|
|
6489
|
-
//#endregion
|
|
6490
6800
|
//#region ../agent-cli/dist/context/stdin.js
|
|
6491
6801
|
const STDIN_META = "stdin";
|
|
6492
6802
|
function isStdinSchema(schema) {
|
|
@@ -6720,7 +7030,7 @@ const SEPARATOR = ",";
|
|
|
6720
7030
|
const COLUMN_SEPARATOR = " ";
|
|
6721
7031
|
const UNSAFE_IN_COLUMN = /[\t\n\r]/;
|
|
6722
7032
|
const PAGE_INPUT = object({
|
|
6723
|
-
limit: number().int().positive().default(DEFAULT_PAGE_LIMIT),
|
|
7033
|
+
limit: number$1().int().positive().default(DEFAULT_PAGE_LIMIT),
|
|
6724
7034
|
cursor: string().min(1).optional()
|
|
6725
7035
|
});
|
|
6726
7036
|
var Page = class {
|
|
@@ -6881,6 +7191,17 @@ const FORCE_FLAG = standardFlag("--force", {
|
|
|
6881
7191
|
takes: "nothing",
|
|
6882
7192
|
schema: boolean()
|
|
6883
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. */
|
|
7202
|
+
function takesWait(command) {
|
|
7203
|
+
return (command.logins ?? []).length > 0;
|
|
7204
|
+
}
|
|
6884
7205
|
const EFFECT_FLAGS = {
|
|
6885
7206
|
read: [JSON_FLAG],
|
|
6886
7207
|
act: [JSON_FLAG],
|
|
@@ -6900,7 +7221,7 @@ const RESERVED = new Map([
|
|
|
6900
7221
|
...LIST_FLAGS
|
|
6901
7222
|
].map((flag) => [nameOf$1(flag), flag]));
|
|
6902
7223
|
/** The Standard flags of a Command with this Effect and declarations, and of a List Command. */
|
|
6903
|
-
function standardFlagsFor(effect, { outputs, force, cost }, list) {
|
|
7224
|
+
function standardFlagsFor(effect, { outputs, force, cost, wait }, list) {
|
|
6904
7225
|
const forceFlag = force === void 0 ? FORCE_FLAG : {
|
|
6905
7226
|
...FORCE_FLAG,
|
|
6906
7227
|
description: force
|
|
@@ -6909,19 +7230,22 @@ function standardFlagsFor(effect, { outputs, force, cost }, list) {
|
|
|
6909
7230
|
...EFFECT_FLAGS[effect],
|
|
6910
7231
|
...outputs || force !== void 0 ? [forceFlag] : [],
|
|
6911
7232
|
...cost ? [YES_FLAG] : [],
|
|
7233
|
+
...wait ? [WAIT_FLAG] : [],
|
|
6912
7234
|
...list?.standardFlags ?? []
|
|
6913
7235
|
];
|
|
6914
7236
|
}
|
|
6915
7237
|
/** Throws when the Command declares an arg or option under a Standard flag's name. */
|
|
6916
7238
|
function checkNoStandardFlags(command, label) {
|
|
6917
|
-
const
|
|
7239
|
+
const declared = [...argFields(command.args).map(({ key }) => ({
|
|
6918
7240
|
kind: "arg",
|
|
6919
7241
|
key
|
|
6920
7242
|
})), ...optionFields(command.options).map(({ key }) => ({
|
|
6921
7243
|
kind: "option",
|
|
6922
7244
|
key
|
|
6923
|
-
}))]
|
|
6924
|
-
|
|
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);
|
|
6925
7249
|
return flag === void 0 ? [] : [` ${kind} '${key}' is the Standard flag ${flag}`];
|
|
6926
7250
|
});
|
|
6927
7251
|
if (lines.length > 0) throw new Error([`Command '${label}' declares Standard flags the Runtime adds; rename them:`, ...lines].join("\n"));
|
|
@@ -7067,149 +7391,6 @@ function classifyErrno(error, context) {
|
|
|
7067
7391
|
}
|
|
7068
7392
|
}
|
|
7069
7393
|
//#endregion
|
|
7070
|
-
//#region ../agent-cli/dist/context/interrupts.js
|
|
7071
|
-
const INTERRUPTED = "INTERRUPTED";
|
|
7072
|
-
const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
|
|
7073
|
-
/** How long an interrupted Command may take to settle before the run ends without it. */
|
|
7074
|
-
const GRACE_MS = 3e3;
|
|
7075
|
-
const GRACE_OVER = Symbol("grace over");
|
|
7076
|
-
function interruptedError(rerun) {
|
|
7077
|
-
return new CliError({
|
|
7078
|
-
code: INTERRUPTED,
|
|
7079
|
-
message: "interrupted before it finished",
|
|
7080
|
-
hint: "to finish it, run the Next step, unless it was stopped on purpose; if a tool timeout stopped it, give it longer",
|
|
7081
|
-
next: [rerun]
|
|
7082
|
-
});
|
|
7083
|
-
}
|
|
7084
|
-
const BOUND_WORDS = {
|
|
7085
|
-
deadline: "deadline",
|
|
7086
|
-
ignoreStop: "timeout"
|
|
7087
|
-
};
|
|
7088
|
-
function waitHint(bound) {
|
|
7089
|
-
return bound === "ignoreStop" ? "the call may have been billed: ask your user before running the Next step" : "run the Next step later, when the service it waits on may answer faster";
|
|
7090
|
-
}
|
|
7091
|
-
function deadlineError(commandLine, ms, advice, bound = "deadline") {
|
|
7092
|
-
return new CliError({
|
|
7093
|
-
code: DEADLINE_EXCEEDED,
|
|
7094
|
-
message: `${commandLine} ran past its ${ms} ms ${BOUND_WORDS[bound]}`,
|
|
7095
|
-
...advice
|
|
7096
|
-
});
|
|
7097
|
-
}
|
|
7098
|
-
/** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
|
|
7099
|
-
var RunControl = class {
|
|
7100
|
-
signal;
|
|
7101
|
-
#controller = new AbortController();
|
|
7102
|
-
#commandLine;
|
|
7103
|
-
#rerun;
|
|
7104
|
-
#outer;
|
|
7105
|
-
#onOuterAbort = () => this.#controller.abort(interruptedError(this.#rerun));
|
|
7106
|
-
/** Each running must-finish step, tracked until it settles, with its name. */
|
|
7107
|
-
#steps = /* @__PURE__ */ new Map();
|
|
7108
|
-
#progress;
|
|
7109
|
-
#clock;
|
|
7110
|
-
#disposed = new AbortController();
|
|
7111
|
-
/** The reasons this run's deadlines aborted with, to tell them in a Command's error. */
|
|
7112
|
-
#abortReasons = /* @__PURE__ */ new WeakSet();
|
|
7113
|
-
#graceCancel = new AbortController();
|
|
7114
|
-
#endGrace = () => {};
|
|
7115
|
-
#graceOver = new Promise((resolve) => {
|
|
7116
|
-
this.#endGrace = resolve;
|
|
7117
|
-
});
|
|
7118
|
-
constructor({ commandLine, rerun, outer, clock, progress, runDeadline }) {
|
|
7119
|
-
this.#commandLine = commandLine;
|
|
7120
|
-
this.#rerun = rerun;
|
|
7121
|
-
this.#outer = outer;
|
|
7122
|
-
this.#clock = clock;
|
|
7123
|
-
this.#progress = progress;
|
|
7124
|
-
this.signal = this.#controller.signal;
|
|
7125
|
-
this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
|
|
7126
|
-
if (outer?.aborted === true) this.#onOuterAbort();
|
|
7127
|
-
else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
|
|
7128
|
-
if (runDeadline !== void 0) {
|
|
7129
|
-
const { ms, advice } = runDeadline;
|
|
7130
|
-
after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, advice())));
|
|
7131
|
-
}
|
|
7132
|
-
}
|
|
7133
|
-
deadline(ms, options = {}) {
|
|
7134
|
-
return options.ignoreStop === true ? this.#abortsAfter(ms, "ignoreStop") : AbortSignal.any([this.signal, this.#abortsAfter(ms, "deadline")]);
|
|
7135
|
-
}
|
|
7136
|
-
#abortsAfter(ms, bound) {
|
|
7137
|
-
const controller = new AbortController();
|
|
7138
|
-
after(this.#clock, ms, this.#disposed.signal, () => {
|
|
7139
|
-
const advice = {
|
|
7140
|
-
hint: waitHint(bound),
|
|
7141
|
-
next: [this.#rerun]
|
|
7142
|
-
};
|
|
7143
|
-
const reason = deadlineError(this.#commandLine, ms, advice, bound);
|
|
7144
|
-
this.#abortReasons.add(reason);
|
|
7145
|
-
controller.abort(reason);
|
|
7146
|
-
});
|
|
7147
|
-
return controller.signal;
|
|
7148
|
-
}
|
|
7149
|
-
async runToEnd(name, work) {
|
|
7150
|
-
this.#graceCancel.abort();
|
|
7151
|
-
if (this.signal.aborted) this.#finishing(name);
|
|
7152
|
-
const step = work();
|
|
7153
|
-
const settled = step.then(() => {}, () => {});
|
|
7154
|
-
this.#steps.set(settled, name);
|
|
7155
|
-
try {
|
|
7156
|
-
return await step;
|
|
7157
|
-
} finally {
|
|
7158
|
-
this.#steps.delete(settled);
|
|
7159
|
-
this.#startGrace();
|
|
7160
|
-
}
|
|
7161
|
-
}
|
|
7162
|
-
/** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
|
|
7163
|
-
async settle(work) {
|
|
7164
|
-
try {
|
|
7165
|
-
const value = await Promise.race([work, this.#graceOver]);
|
|
7166
|
-
if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
|
|
7167
|
-
return value;
|
|
7168
|
-
} finally {
|
|
7169
|
-
await this.#stepsDone();
|
|
7170
|
-
this.close();
|
|
7171
|
-
}
|
|
7172
|
-
}
|
|
7173
|
-
/**
|
|
7174
|
-
* The error a run ends with: an interrupt or the run deadline outranks what the Command threw,
|
|
7175
|
-
* unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
|
|
7176
|
-
* Otherwise each leaf that is not a `CliError` and whose cause chain holds a deadline's abort is
|
|
7177
|
-
* reported as that abort, so a declared error keeps its own code.
|
|
7178
|
-
*/
|
|
7179
|
-
errorAfter(error) {
|
|
7180
|
-
if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
|
|
7181
|
-
const leaves = errorsOf(error).map((leaf) => this.#abortIn(leaf) ?? leaf);
|
|
7182
|
-
return leaves.length === 1 ? leaves[0] : new AggregateError(leaves);
|
|
7183
|
-
}
|
|
7184
|
-
#abortIn(error) {
|
|
7185
|
-
if (error instanceof CliError) return void 0;
|
|
7186
|
-
return causesOf(error).find((each) => each instanceof CliError && this.#abortReasons.has(each));
|
|
7187
|
-
}
|
|
7188
|
-
#onStop() {
|
|
7189
|
-
for (const name of this.#steps.values()) this.#finishing(name);
|
|
7190
|
-
this.#startGrace();
|
|
7191
|
-
}
|
|
7192
|
-
#finishing(name) {
|
|
7193
|
-
this.#progress(`finishing ${name}`);
|
|
7194
|
-
}
|
|
7195
|
-
#startGrace() {
|
|
7196
|
-
if (this.signal.aborted && this.#steps.size === 0) {
|
|
7197
|
-
this.#graceCancel.abort();
|
|
7198
|
-
this.#graceCancel = new AbortController();
|
|
7199
|
-
after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
|
|
7200
|
-
}
|
|
7201
|
-
}
|
|
7202
|
-
async #stepsDone() {
|
|
7203
|
-
while (this.#steps.size > 0) await Promise.all(this.#steps.keys());
|
|
7204
|
-
}
|
|
7205
|
-
/** Stops the run's timers and its hold on the caller's signal, for a run that never settles. */
|
|
7206
|
-
close() {
|
|
7207
|
-
this.#graceCancel.abort();
|
|
7208
|
-
this.#disposed.abort();
|
|
7209
|
-
this.#outer?.removeEventListener("abort", this.#onOuterAbort);
|
|
7210
|
-
}
|
|
7211
|
-
};
|
|
7212
|
-
//#endregion
|
|
7213
7394
|
//#region ../agent-cli/dist/context/operator.js
|
|
7214
7395
|
const NEEDS_OPERATOR = "NEEDS_OPERATOR";
|
|
7215
7396
|
/** Fails with `NEEDS_OPERATOR` when a Coding agent runs an Operator Command. */
|
|
@@ -7503,7 +7684,7 @@ function readText(path, fileError) {
|
|
|
7503
7684
|
});
|
|
7504
7685
|
}
|
|
7505
7686
|
}
|
|
7506
|
-
function parsed(path, text, schema, rerun) {
|
|
7687
|
+
function parsed$1(path, text, schema, rerun) {
|
|
7507
7688
|
let json;
|
|
7508
7689
|
try {
|
|
7509
7690
|
json = JSON.parse(text);
|
|
@@ -7519,7 +7700,7 @@ function projectFrom(projectFile, folder, scope) {
|
|
|
7519
7700
|
const text = readText(path, scope.fileError);
|
|
7520
7701
|
if (text !== void 0) return {
|
|
7521
7702
|
root: folder,
|
|
7522
|
-
settings: parsed(path, text, projectFile.schema, scope.rerun)
|
|
7703
|
+
settings: parsed$1(path, text, projectFile.schema, scope.rerun)
|
|
7523
7704
|
};
|
|
7524
7705
|
const parent = dirname(folder);
|
|
7525
7706
|
return parent === folder ? void 0 : projectFrom(projectFile, parent, scope);
|
|
@@ -7628,6 +7809,70 @@ function secretRows(secrets, commands) {
|
|
|
7628
7809
|
}];
|
|
7629
7810
|
}
|
|
7630
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
|
+
/** The wait ran out before the person approved: the same Approval stays pending for the rerun. */
|
|
7833
|
+
function approvalPending(name, approval, rerun) {
|
|
7834
|
+
const { verificationUri, userCode, verificationUriComplete } = approval;
|
|
7835
|
+
return new CliError({
|
|
7836
|
+
code: APPROVAL_PENDING,
|
|
7837
|
+
message: `signing in to ${name} waits for the code ${userCode} to be approved at ${verificationUriComplete ?? verificationUri}`,
|
|
7838
|
+
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",
|
|
7839
|
+
details: {
|
|
7840
|
+
verificationUri,
|
|
7841
|
+
userCode,
|
|
7842
|
+
...verificationUriComplete === void 0 ? {} : { verificationUriComplete }
|
|
7843
|
+
},
|
|
7844
|
+
next: [rerun]
|
|
7845
|
+
});
|
|
7846
|
+
}
|
|
7847
|
+
function approvalDenied(name) {
|
|
7848
|
+
return new CliError({
|
|
7849
|
+
code: APPROVAL_DENIED,
|
|
7850
|
+
message: `the person refused to sign in to ${name}`,
|
|
7851
|
+
hint: "ask the person whether to sign in, and run the Next step only if they agree",
|
|
7852
|
+
next: [loginStep(name)]
|
|
7853
|
+
});
|
|
7854
|
+
}
|
|
7855
|
+
/** A Command's `LOGIN_REJECTED` of a token from a Secret, which only the operator can replace. */
|
|
7856
|
+
function secretRejected(name, secret, rejected) {
|
|
7857
|
+
return new CliError({
|
|
7858
|
+
code: LOGIN_REJECTED,
|
|
7859
|
+
message: rejected.message,
|
|
7860
|
+
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`,
|
|
7861
|
+
details: {
|
|
7862
|
+
login: name,
|
|
7863
|
+
secret: secret.name
|
|
7864
|
+
}
|
|
7865
|
+
}, { cause: rejected });
|
|
7866
|
+
}
|
|
7867
|
+
function approvalExpired(name) {
|
|
7868
|
+
return new CliError({
|
|
7869
|
+
code: APPROVAL_EXPIRED,
|
|
7870
|
+
message: `the code to sign in to ${name} expired before it was approved`,
|
|
7871
|
+
hint: "run the Next step for a new code, and pass its link and code to the person",
|
|
7872
|
+
next: [loginStep(name)]
|
|
7873
|
+
});
|
|
7874
|
+
}
|
|
7875
|
+
//#endregion
|
|
7631
7876
|
//#region ../agent-cli/dist/runtime-codes.js
|
|
7632
7877
|
const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
|
|
7633
7878
|
const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
|
|
@@ -7637,6 +7882,7 @@ const REGISTRY_AUTH = "REGISTRY_AUTH";
|
|
|
7637
7882
|
const INSTALL_FAILED = "INSTALL_FAILED";
|
|
7638
7883
|
const NPM_FAILED = "NPM_FAILED";
|
|
7639
7884
|
const NO_LATEST = "NO_LATEST";
|
|
7885
|
+
const RELEASE_TOO_OLD = "RELEASE_TOO_OLD";
|
|
7640
7886
|
/** The Runtime codes any Command can meet, which no Command declares. */
|
|
7641
7887
|
const anyCommandErrors = {
|
|
7642
7888
|
[INVALID_INPUT]: { class: "caller" },
|
|
@@ -7660,7 +7906,16 @@ const anyCommandErrors = {
|
|
|
7660
7906
|
[TOOL_FAILED]: { class: "operator" },
|
|
7661
7907
|
[TOOL_MISSING]: { class: "operator" },
|
|
7662
7908
|
[SECRET_MISSING]: { class: "operator" },
|
|
7663
|
-
[PROJECT_INVALID]: { class: "operator" }
|
|
7909
|
+
[PROJECT_INVALID]: { class: "operator" },
|
|
7910
|
+
[UNREACHABLE]: { class: "wait" },
|
|
7911
|
+
[UNAVAILABLE]: { class: "wait" },
|
|
7912
|
+
[UNANSWERED]: { class: "source" },
|
|
7913
|
+
[RELEASE_TOO_OLD]: { class: "caller" },
|
|
7914
|
+
[LOGIN_REQUIRED]: { class: "operator" },
|
|
7915
|
+
[APPROVAL_PENDING]: { class: "wait" },
|
|
7916
|
+
[APPROVAL_DENIED]: { class: "operator" },
|
|
7917
|
+
[APPROVAL_EXPIRED]: { class: "operator" },
|
|
7918
|
+
[LOGIN_REJECTED]: { class: "operator" }
|
|
7664
7919
|
};
|
|
7665
7920
|
/**
|
|
7666
7921
|
* The Runtime codes only some Commands reach: each Command that throws one, Built-in or the CLI's
|
|
@@ -7695,7 +7950,8 @@ function mustDeclare(code, table) {
|
|
|
7695
7950
|
//#endregion
|
|
7696
7951
|
//#region ../agent-cli/dist/define-command.js
|
|
7697
7952
|
/** Throws when the Command's declaration breaks a rule its type cannot express; `label` names it. */
|
|
7698
|
-
function checkCommand(command, label, table) {
|
|
7953
|
+
function checkCommand(command, label, table, logins) {
|
|
7954
|
+
checkLogins(command.logins ?? [], label, logins);
|
|
7699
7955
|
checkNoStandardFlags(command, label);
|
|
7700
7956
|
checkForce(command, label);
|
|
7701
7957
|
checkArrayArgLast(label, command.args);
|
|
@@ -7703,6 +7959,10 @@ function checkCommand(command, label, table) {
|
|
|
7703
7959
|
checkNoBooleanArrayOption(label, command.options);
|
|
7704
7960
|
checkErrors(command.errors ?? [], label, table);
|
|
7705
7961
|
}
|
|
7962
|
+
function checkLogins(names, label, logins) {
|
|
7963
|
+
const unknown = names.filter((name) => !Object.hasOwn(logins, name));
|
|
7964
|
+
if (unknown.length > 0) throw new Error(`${label} declares the Login ${unknown.join(", ")}, which the CLI does not declare in logins`);
|
|
7965
|
+
}
|
|
7706
7966
|
/** Throws on a code in `errors` that is neither in `table` nor a Runtime code some Commands declare. */
|
|
7707
7967
|
function checkErrors(errors, label, table) {
|
|
7708
7968
|
const unknown = errors.filter((code) => !mustDeclare(code, table));
|
|
@@ -7739,13 +7999,214 @@ function cliErrorOf(code, init, options) {
|
|
|
7739
7999
|
}, options);
|
|
7740
8000
|
}
|
|
7741
8001
|
//#endregion
|
|
8002
|
+
//#region ../agent-cli/dist/login/store.js
|
|
8003
|
+
const storedLoginSchema = object({
|
|
8004
|
+
accessToken: string(),
|
|
8005
|
+
refreshToken: string().optional(),
|
|
8006
|
+
expiresAt: datetime().optional()
|
|
8007
|
+
});
|
|
8008
|
+
const approvalSchema = object({
|
|
8009
|
+
deviceCode: string(),
|
|
8010
|
+
userCode: string(),
|
|
8011
|
+
verificationUri: string(),
|
|
8012
|
+
verificationUriComplete: string().optional(),
|
|
8013
|
+
expiresAt: datetime(),
|
|
8014
|
+
/** Seconds between polls of the token endpoint. */
|
|
8015
|
+
interval: number$1()
|
|
8016
|
+
});
|
|
8017
|
+
const KEY_LENGTH = 16;
|
|
8018
|
+
function keyOf({ tokenEndpoint, clientId }) {
|
|
8019
|
+
return createHash("sha256").update(`${tokenEndpoint}\n${clientId}`).digest("hex").slice(0, KEY_LENGTH);
|
|
8020
|
+
}
|
|
8021
|
+
function loginFilesOf(userFile, login) {
|
|
8022
|
+
const key = keyOf(login);
|
|
8023
|
+
return {
|
|
8024
|
+
stored: userFile(`login-${key}`, storedLoginSchema),
|
|
8025
|
+
approval: userFile(`approval-${key}`, approvalSchema)
|
|
8026
|
+
};
|
|
8027
|
+
}
|
|
8028
|
+
/** The file's value, or `undefined` once its `expiresAt` has passed, removing the file then. */
|
|
8029
|
+
async function readUnexpired(file, clock) {
|
|
8030
|
+
const value = await file.read();
|
|
8031
|
+
if (value?.expiresAt === void 0 || Date.parse(value.expiresAt) > clock.now()) return value;
|
|
8032
|
+
await file.remove();
|
|
8033
|
+
}
|
|
8034
|
+
/**
|
|
8035
|
+
* The stored Login, unless its access token expired with no refresh token to renew it, removing
|
|
8036
|
+
* the file then.
|
|
8037
|
+
*/
|
|
8038
|
+
async function readStored(file, clock) {
|
|
8039
|
+
const stored = await file.read();
|
|
8040
|
+
return stored?.refreshToken === void 0 ? await readUnexpired(file, clock) : stored;
|
|
8041
|
+
}
|
|
8042
|
+
//#endregion
|
|
8043
|
+
//#region ../agent-cli/dist/login/token-endpoint.js
|
|
8044
|
+
const tokenSchema = object({
|
|
8045
|
+
access_token: string(),
|
|
8046
|
+
refresh_token: string().optional(),
|
|
8047
|
+
expires_in: number$1().optional()
|
|
8048
|
+
});
|
|
8049
|
+
const errorSchema = object({ error: string() });
|
|
8050
|
+
function isoAfter(clock, seconds) {
|
|
8051
|
+
return new Date(clock.now() + seconds * MS_PER_S).toISOString();
|
|
8052
|
+
}
|
|
8053
|
+
/** POSTs `form` to an OAuth endpoint and parses its JSON answer, whatever its status. */
|
|
8054
|
+
async function postForm(fetch, url, form, { ignoreStop } = {}) {
|
|
8055
|
+
const response = await fetch(url, {
|
|
8056
|
+
method: "POST",
|
|
8057
|
+
once: false,
|
|
8058
|
+
...ignoreStop === void 0 ? {} : { ignoreStop },
|
|
8059
|
+
headers: { accept: "application/json" },
|
|
8060
|
+
body: new URLSearchParams(form)
|
|
8061
|
+
});
|
|
8062
|
+
const text = await response.text();
|
|
8063
|
+
try {
|
|
8064
|
+
return JSON.parse(text);
|
|
8065
|
+
} catch (error) {
|
|
8066
|
+
throw new Error(`${url} answered ${response.status} with a body that is not JSON`, { cause: error });
|
|
8067
|
+
}
|
|
8068
|
+
}
|
|
8069
|
+
function parsedAnswer(schema, body, url) {
|
|
8070
|
+
const parsed = schema.safeParse(body);
|
|
8071
|
+
if (!parsed.success) throw new Error(`${url} answered ${JSON.stringify(body)}, not an OAuth answer`);
|
|
8072
|
+
return parsed.data;
|
|
8073
|
+
}
|
|
8074
|
+
function storedLoginOf({ access_token, refresh_token, expires_in }, clock) {
|
|
8075
|
+
return {
|
|
8076
|
+
accessToken: access_token,
|
|
8077
|
+
...refresh_token === void 0 ? {} : { refreshToken: refresh_token },
|
|
8078
|
+
...expires_in === void 0 ? {} : { expiresAt: isoAfter(clock, expires_in) }
|
|
8079
|
+
};
|
|
8080
|
+
}
|
|
8081
|
+
/** Whether the stored Login has a refresh token and its access token expires within the margin. */
|
|
8082
|
+
function nearExpiry({ refreshToken, expiresAt }, clock) {
|
|
8083
|
+
return refreshToken !== void 0 && expiresAt !== void 0 && Date.parse(expiresAt) - clock.now() < 3e5;
|
|
8084
|
+
}
|
|
8085
|
+
/**
|
|
8086
|
+
* Refreshes the stored Login per RFC 6749 §6 under `ctx.runToEnd`, so a single-use refresh token is
|
|
8087
|
+
* never spent without its successor stored. `invalid_grant` removes the stored Login and gives `undefined`.
|
|
8088
|
+
*/
|
|
8089
|
+
async function refreshed(name, login, file, refreshToken, ctx) {
|
|
8090
|
+
ctx.signal.throwIfAborted();
|
|
8091
|
+
const form = {
|
|
8092
|
+
grant_type: "refresh_token",
|
|
8093
|
+
refresh_token: refreshToken,
|
|
8094
|
+
client_id: login.clientId
|
|
8095
|
+
};
|
|
8096
|
+
return await ctx.runToEnd(`refreshing the Login ${name}`, async () => {
|
|
8097
|
+
const body = await postForm(ctx.fetch, login.tokenEndpoint, form, { ignoreStop: true });
|
|
8098
|
+
const granted = tokenSchema.safeParse(body);
|
|
8099
|
+
if (granted.success) {
|
|
8100
|
+
const stored = {
|
|
8101
|
+
refreshToken,
|
|
8102
|
+
...storedLoginOf(granted.data, ctx.clock)
|
|
8103
|
+
};
|
|
8104
|
+
await file.write(stored);
|
|
8105
|
+
return stored;
|
|
8106
|
+
}
|
|
8107
|
+
const { error } = parsedAnswer(errorSchema, body, login.tokenEndpoint);
|
|
8108
|
+
if (error !== "invalid_grant") throw new Error(`${login.tokenEndpoint} refused the refresh of ${name} with ${error}`);
|
|
8109
|
+
await file.remove();
|
|
8110
|
+
});
|
|
8111
|
+
}
|
|
8112
|
+
//#endregion
|
|
8113
|
+
//#region ../agent-cli/dist/login/token.js
|
|
8114
|
+
function setSecretOf(login, secret) {
|
|
8115
|
+
const name = login.secret?.name;
|
|
8116
|
+
const found = name === void 0 ? void 0 : secret(name);
|
|
8117
|
+
return name === void 0 || found === void 0 ? void 0 : {
|
|
8118
|
+
name,
|
|
8119
|
+
secret: found
|
|
8120
|
+
};
|
|
8121
|
+
}
|
|
8122
|
+
function secretTokenOf(login, scope) {
|
|
8123
|
+
const set = setSecretOf(login, scope.secret);
|
|
8124
|
+
return set && {
|
|
8125
|
+
value: set.secret.value,
|
|
8126
|
+
source: {
|
|
8127
|
+
kind: "secret",
|
|
8128
|
+
name: set.name
|
|
8129
|
+
}
|
|
8130
|
+
};
|
|
8131
|
+
}
|
|
8132
|
+
function storedToken({ accessToken }) {
|
|
8133
|
+
return {
|
|
8134
|
+
value: accessToken,
|
|
8135
|
+
source: { kind: "stored" }
|
|
8136
|
+
};
|
|
8137
|
+
}
|
|
8138
|
+
function unexpired({ expiresAt }, clock) {
|
|
8139
|
+
return expiresAt === void 0 || Date.parse(expiresAt) > clock.now();
|
|
8140
|
+
}
|
|
8141
|
+
/**
|
|
8142
|
+
* The Login's token without sending anything: its Secret's value when set, else the stored Login's
|
|
8143
|
+
* while its access token has not expired.
|
|
8144
|
+
*/
|
|
8145
|
+
async function currentTokenOf(login, scope) {
|
|
8146
|
+
const fromSecret = secretTokenOf(login, scope);
|
|
8147
|
+
if (fromSecret !== void 0) return fromSecret;
|
|
8148
|
+
const stored = await readStored(loginFilesOf(scope.userFile, login).stored, scope.clock);
|
|
8149
|
+
return stored && unexpired(stored, scope.clock) ? storedToken(stored) : void 0;
|
|
8150
|
+
}
|
|
8151
|
+
/** The Login's token: its Secret's value when set, else the stored Login's, refreshed near expiry. */
|
|
8152
|
+
async function freshTokenOf(name, login, ctx) {
|
|
8153
|
+
const fromSecret = secretTokenOf(login, tokenScopeOf(ctx));
|
|
8154
|
+
if (fromSecret !== void 0) return fromSecret;
|
|
8155
|
+
const file = loginFilesOf(ctx.userFile, login).stored;
|
|
8156
|
+
const stored = await readStored(file, ctx.clock);
|
|
8157
|
+
if (stored?.refreshToken === void 0 || !nearExpiry(stored, ctx.clock)) return stored && storedToken(stored);
|
|
8158
|
+
const renewed = await refreshed(name, login, file, stored.refreshToken, ctx);
|
|
8159
|
+
return renewed && storedToken(renewed);
|
|
8160
|
+
}
|
|
8161
|
+
function tokenScopeOf(ctx) {
|
|
8162
|
+
const { env, envFile, fileError } = ctx;
|
|
8163
|
+
return {
|
|
8164
|
+
secret: (name) => secretOf({
|
|
8165
|
+
env,
|
|
8166
|
+
envFile,
|
|
8167
|
+
fileError
|
|
8168
|
+
}, name),
|
|
8169
|
+
userFile: ctx.userFile,
|
|
8170
|
+
clock: ctx.clock
|
|
8171
|
+
};
|
|
8172
|
+
}
|
|
8173
|
+
//#endregion
|
|
8174
|
+
//#region ../agent-cli/dist/login/doctor-rows.js
|
|
8175
|
+
function loginRow(name, login, runAs) {
|
|
8176
|
+
return {
|
|
8177
|
+
name: `login ${name}`,
|
|
8178
|
+
advisory: true,
|
|
8179
|
+
run: async (ctx) => {
|
|
8180
|
+
const set = setSecretOf(login, tokenScopeOf(ctx).secret);
|
|
8181
|
+
if (set !== void 0) return {
|
|
8182
|
+
state: "ok",
|
|
8183
|
+
message: `the token in ${set.name}, from ${originOf(set.secret)}`
|
|
8184
|
+
};
|
|
8185
|
+
if (await readStored(loginFilesOf(ctx.userFile, login).stored, ctx.clock) !== void 0) return {
|
|
8186
|
+
state: "ok",
|
|
8187
|
+
message: "signed in: a stored Login"
|
|
8188
|
+
};
|
|
8189
|
+
const secretName = login.secret?.name;
|
|
8190
|
+
return {
|
|
8191
|
+
state: "problem",
|
|
8192
|
+
message: `not signed in${secretName === void 0 ? "" : `, and ${secretName} is not set`}; a Command that needs ${name} fails until the person approves a code`,
|
|
8193
|
+
fix: lineOf(loginStep(name), runAs)
|
|
8194
|
+
};
|
|
8195
|
+
}
|
|
8196
|
+
};
|
|
8197
|
+
}
|
|
8198
|
+
/** One `doctor` row per Login: where its token comes from, and `login` as the fix with none. */
|
|
8199
|
+
function loginRows(logins, runAs) {
|
|
8200
|
+
return Object.entries(logins).map(([name, login]) => loginRow(name, login, runAs));
|
|
8201
|
+
}
|
|
8202
|
+
//#endregion
|
|
7742
8203
|
//#region ../agent-cli/dist/version.js
|
|
7743
8204
|
/** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
|
|
7744
8205
|
function buildStamp() {
|
|
7745
8206
|
return {
|
|
7746
|
-
"sha": "
|
|
8207
|
+
"sha": "7dd915f",
|
|
7747
8208
|
"dirty": false,
|
|
7748
|
-
"builtAt": "2026-11-
|
|
8209
|
+
"builtAt": "2026-11-10T03:40:31.000Z"
|
|
7749
8210
|
};
|
|
7750
8211
|
}
|
|
7751
8212
|
/** The first line of `--version`: the version, and the commit and time of a stamped build. */
|
|
@@ -8100,6 +8561,241 @@ async function projectRootOf(files, cwd) {
|
|
|
8100
8561
|
return (await lockfileRootOf(files, cwd))?.dir ?? cwd;
|
|
8101
8562
|
}
|
|
8102
8563
|
//#endregion
|
|
8564
|
+
//#region ../agent-cli/dist/built-ins/install/global-npm.js
|
|
8565
|
+
/** Where npm puts global packages under its global prefix. */
|
|
8566
|
+
function globalModulesDir(prefix, platform) {
|
|
8567
|
+
return platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
|
|
8568
|
+
}
|
|
8569
|
+
/** Whether `path` itself is a symlink, which a link in a folder above it does not make it. */
|
|
8570
|
+
async function isLink(files, path) {
|
|
8571
|
+
const real = await files.realPath(path);
|
|
8572
|
+
if (real === void 0) return false;
|
|
8573
|
+
const folder = await files.realPath(dirname(path));
|
|
8574
|
+
return folder === void 0 || real !== join(folder, basename(path));
|
|
8575
|
+
}
|
|
8576
|
+
function notGlobal(ctx, message) {
|
|
8577
|
+
return runtimeError({
|
|
8578
|
+
code: NOT_GLOBAL_INSTALL,
|
|
8579
|
+
message,
|
|
8580
|
+
hint: "install it by running the Next step, so its update Command can replace it",
|
|
8581
|
+
next: [wholeLine(setupCommandLine(ctx.registry))]
|
|
8582
|
+
});
|
|
8583
|
+
}
|
|
8584
|
+
/**
|
|
8585
|
+
* The global npm install this copy runs from, found by asking npm for its global prefix.
|
|
8586
|
+
* `bin` is the script that started the CLI; throws `NOT_GLOBAL_INSTALL` when it is not inside that install.
|
|
8587
|
+
*/
|
|
8588
|
+
async function globalInstallOf(bin, ctx) {
|
|
8589
|
+
if (bin === void 0) throw notGlobal(ctx, `${ctx.cliName} runs from source, not from an npm install`);
|
|
8590
|
+
const prefix = await globalPrefix(ctx);
|
|
8591
|
+
const packageDir = join(globalModulesDir(prefix, ctx.platform), ctx.registry.package);
|
|
8592
|
+
const installed = await ctx.files.realPath(packageDir);
|
|
8593
|
+
const here = await ctx.files.realPath(bin) ?? bin;
|
|
8594
|
+
if (installed === void 0 || !isInside(installed, here)) throw notGlobal(ctx, `${ctx.cliName} runs from ${here}, not from npm's global install at ${packageDir}`);
|
|
8595
|
+
if (await isLink(ctx.files, packageDir)) throw notGlobal(ctx, `${ctx.cliName} runs from a link to ${installed} (npm link), not from an npm install`);
|
|
8596
|
+
return {
|
|
8597
|
+
prefix,
|
|
8598
|
+
packageDir
|
|
8599
|
+
};
|
|
8600
|
+
}
|
|
8601
|
+
const NPMJS = "https://registry.npmjs.org/";
|
|
8602
|
+
/** The flag that points npm at the CLI's registry; none for npmjs, npm's default. */
|
|
8603
|
+
function registryFlags({ package: name, registry }) {
|
|
8604
|
+
if (registry === NPMJS) return [];
|
|
8605
|
+
const scope = scopeOf(name);
|
|
8606
|
+
return [scope === void 0 ? `--registry=${registry}` : `--${scope}:registry=${registry}`];
|
|
8607
|
+
}
|
|
8608
|
+
/** The line that sets a Global install up, run before the CLI is on the machine. */
|
|
8609
|
+
function setupLineOf(install) {
|
|
8610
|
+
return setupCommandLine(globalRegistry(install));
|
|
8611
|
+
}
|
|
8612
|
+
function globalRegistry(install) {
|
|
8613
|
+
const { package: name, registry } = install;
|
|
8614
|
+
return {
|
|
8615
|
+
package: name,
|
|
8616
|
+
label: registry,
|
|
8617
|
+
flags: registryFlags(install),
|
|
8618
|
+
unreachableHint: "check the network or VPN",
|
|
8619
|
+
loginHint: `log in to ${registry} (\`npm login --registry=${registry}\`) or set its token in ~/.npmrc`,
|
|
8620
|
+
missingHint: `check that ${name} is published on ${registry}`
|
|
8621
|
+
};
|
|
8622
|
+
}
|
|
8623
|
+
/** npm for a Global install, run in the working directory. */
|
|
8624
|
+
function globalNpm(cli, install, ctx, launch) {
|
|
8625
|
+
return npmContext(cli, globalRegistry(install), ctx, launch, ctx.cwd);
|
|
8626
|
+
}
|
|
8627
|
+
//#endregion
|
|
8628
|
+
//#region ../agent-cli/dist/built-ins/install/release-check.js
|
|
8629
|
+
const CHECK_INTERVAL_MS = 864e5;
|
|
8630
|
+
const checkSchema = object({
|
|
8631
|
+
checkedAt: number$1(),
|
|
8632
|
+
latest: string().optional(),
|
|
8633
|
+
/** The highest release the CLI's server named, which started a check sooner. */
|
|
8634
|
+
named: string().optional(),
|
|
8635
|
+
error: string().optional()
|
|
8636
|
+
});
|
|
8637
|
+
const CHECK_SCRIPT = `
|
|
8638
|
+
const { spawnSync } = require("node:child_process");
|
|
8639
|
+
const { mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } = require("node:fs");
|
|
8640
|
+
const { tmpdir } = require("node:os");
|
|
8641
|
+
const { join } = require("node:path");
|
|
8642
|
+
const [file, checkedAt, previous, npm, ...args] = process.argv.slice(1);
|
|
8643
|
+
const windows = process.platform === "win32";
|
|
8644
|
+
const cache = mkdtempSync(join(tmpdir(), "release-check-"));
|
|
8645
|
+
const namedNow = () => {
|
|
8646
|
+
try {
|
|
8647
|
+
return JSON.parse(readFileSync(file, "utf8")).named;
|
|
8648
|
+
} catch {
|
|
8649
|
+
return undefined;
|
|
8650
|
+
}
|
|
8651
|
+
};
|
|
8652
|
+
const write = (check) => {
|
|
8653
|
+
const temp = file + "." + process.pid + ".tmp";
|
|
8654
|
+
const named = namedNow();
|
|
8655
|
+
writeFileSync(temp, JSON.stringify({ checkedAt: Number(checkedAt), named, ...check }));
|
|
8656
|
+
renameSync(temp, file);
|
|
8657
|
+
};
|
|
8658
|
+
const latestOf = (stdout) => {
|
|
8659
|
+
try {
|
|
8660
|
+
const parsed = JSON.parse(stdout);
|
|
8661
|
+
const latest = Array.isArray(parsed) ? parsed.at(-1) : parsed;
|
|
8662
|
+
return typeof latest === "string" && /^\\d+\\.\\d+\\.\\d+/.test(latest) ? latest : undefined;
|
|
8663
|
+
} catch {
|
|
8664
|
+
return undefined;
|
|
8665
|
+
}
|
|
8666
|
+
};
|
|
8667
|
+
// cmd.exe resolves %~dp0 against the working directory for a quoted batch name found on PATH, so
|
|
8668
|
+
// only a full path, which may hold a space, is quoted.
|
|
8669
|
+
const command = windows && /[\\\\/]/.test(npm) ? '"' + npm + '"' : npm;
|
|
8670
|
+
try {
|
|
8671
|
+
const answer = spawnSync(command, args, {
|
|
8672
|
+
encoding: "utf8",
|
|
8673
|
+
shell: windows,
|
|
8674
|
+
windowsHide: true,
|
|
8675
|
+
env: { ...process.env, npm_config_cache: join(cache, "cache"), npm_config_logs_max: "0" },
|
|
8676
|
+
});
|
|
8677
|
+
const latest = answer.status === 0 ? latestOf(answer.stdout) : undefined;
|
|
8678
|
+
if (latest !== undefined) {
|
|
8679
|
+
write({ latest });
|
|
8680
|
+
} else {
|
|
8681
|
+
const said = (answer.stderr || "").trim().split(/\\r?\\n/).slice(0, 5).join("; ");
|
|
8682
|
+
const error = answer.error ? answer.error.message : said || "npm answered " + answer.stdout.trim();
|
|
8683
|
+
write({ latest: previous || undefined, error });
|
|
8684
|
+
}
|
|
8685
|
+
} finally {
|
|
8686
|
+
rmSync(cache, { recursive: true, force: true });
|
|
8687
|
+
}
|
|
8688
|
+
`;
|
|
8689
|
+
function isOff(cliName, env) {
|
|
8690
|
+
return Boolean(env[cliVariable(cliName, "NO_RELEASE_CHECK")] || env.CI);
|
|
8691
|
+
}
|
|
8692
|
+
function cacheFileOf(cliName, env) {
|
|
8693
|
+
const cache = env.XDG_CACHE_HOME || join(env.HOME || env.USERPROFILE || homedir(), ".cache");
|
|
8694
|
+
return join(cache, cliName, "release-check.json");
|
|
8695
|
+
}
|
|
8696
|
+
function parsedCheck(text) {
|
|
8697
|
+
try {
|
|
8698
|
+
return checkSchema.safeParse(JSON.parse(text)).data;
|
|
8699
|
+
} catch {
|
|
8700
|
+
return;
|
|
8701
|
+
}
|
|
8702
|
+
}
|
|
8703
|
+
async function lastCheck(files, file) {
|
|
8704
|
+
const text = await readIfAny(files, file);
|
|
8705
|
+
return text === void 0 ? void 0 : parsedCheck(text);
|
|
8706
|
+
}
|
|
8707
|
+
async function startCheck(cli, install, file, last, named, launch, ctx) {
|
|
8708
|
+
const checkedAt = ctx.clock.now();
|
|
8709
|
+
const previous = last?.latest ?? "";
|
|
8710
|
+
await ctx.files.write(file, JSON.stringify({
|
|
8711
|
+
checkedAt,
|
|
8712
|
+
latest: last?.latest,
|
|
8713
|
+
named
|
|
8714
|
+
}));
|
|
8715
|
+
const npm = globalNpm(cli, install, ctx, launch);
|
|
8716
|
+
const { path, env } = await npmOf(npm);
|
|
8717
|
+
await ctx.spawnDetached(process.execPath, [
|
|
8718
|
+
"-e",
|
|
8719
|
+
CHECK_SCRIPT,
|
|
8720
|
+
file,
|
|
8721
|
+
String(checkedAt),
|
|
8722
|
+
previous,
|
|
8723
|
+
path,
|
|
8724
|
+
...latestViewArgs(npm.registry)
|
|
8725
|
+
], { env });
|
|
8726
|
+
}
|
|
8727
|
+
function debugLine(cli, ctx, text) {
|
|
8728
|
+
if (isDebug({
|
|
8729
|
+
env: ctx.env,
|
|
8730
|
+
cliName: cli.name
|
|
8731
|
+
})) ctx.progress(`release check failed: ${text}`);
|
|
8732
|
+
}
|
|
8733
|
+
const NO_RELEASE_CHECK = {
|
|
8734
|
+
notice: async () => {},
|
|
8735
|
+
latestRelease: async () => {}
|
|
8736
|
+
};
|
|
8737
|
+
function isHigher(version, than) {
|
|
8738
|
+
return than === void 0 || !isVersion(than) || compareVersions(version, than) > 0;
|
|
8739
|
+
}
|
|
8740
|
+
/**
|
|
8741
|
+
* Tells the caller of a newer release that an earlier Release check found, and starts the next
|
|
8742
|
+
* check once the last is a day old, or sooner when the CLI's server names a release higher than
|
|
8743
|
+
* any it named before (ADR 0019).
|
|
8744
|
+
*/
|
|
8745
|
+
function releaseCheck(cli, install, runAs, launch) {
|
|
8746
|
+
const guarded = async (ctx, work) => {
|
|
8747
|
+
if (launch.builtIn || launch.bin === void 0 || cli.commands === void 0) return;
|
|
8748
|
+
if (isOff(cli.name, ctx.env)) return;
|
|
8749
|
+
try {
|
|
8750
|
+
await work();
|
|
8751
|
+
} catch (error) {
|
|
8752
|
+
if (errnoOf(error) === void 0 && !(error instanceof CliError)) throw error;
|
|
8753
|
+
debugLine(cli, ctx, messageOf(error));
|
|
8754
|
+
}
|
|
8755
|
+
};
|
|
8756
|
+
let startedInRun = false;
|
|
8757
|
+
let naming = Promise.resolve();
|
|
8758
|
+
const named = async (version, ctx) => {
|
|
8759
|
+
if (!isVersion(version) || !isHigher(version, cli.version)) return;
|
|
8760
|
+
const file = cacheFileOf(cli.name, ctx.env);
|
|
8761
|
+
const last = await lastCheck(ctx.files, file);
|
|
8762
|
+
if (!isHigher(version, last?.named)) return;
|
|
8763
|
+
if (startedInRun) {
|
|
8764
|
+
await ctx.files.write(file, JSON.stringify({
|
|
8765
|
+
...last,
|
|
8766
|
+
named: version
|
|
8767
|
+
}));
|
|
8768
|
+
return;
|
|
8769
|
+
}
|
|
8770
|
+
await startCheck(cli, install, file, last, version, launch, ctx);
|
|
8771
|
+
startedInRun = true;
|
|
8772
|
+
};
|
|
8773
|
+
return {
|
|
8774
|
+
notice: (ctx) => guarded(ctx, async () => {
|
|
8775
|
+
startedInRun = await checkAndTell(cli, install, runAs, launch, ctx);
|
|
8776
|
+
}),
|
|
8777
|
+
latestRelease: (version, ctx) => {
|
|
8778
|
+
naming = naming.then(() => guarded(ctx, () => named(version, ctx)));
|
|
8779
|
+
return naming;
|
|
8780
|
+
}
|
|
8781
|
+
};
|
|
8782
|
+
}
|
|
8783
|
+
/** Whether it started a check. */
|
|
8784
|
+
async function checkAndTell(cli, install, runAs, launch, ctx) {
|
|
8785
|
+
const file = cacheFileOf(cli.name, ctx.env);
|
|
8786
|
+
const last = await lastCheck(ctx.files, file);
|
|
8787
|
+
if (last?.error !== void 0) debugLine(cli, ctx, last.error);
|
|
8788
|
+
const due = last === void 0 || ctx.clock.now() - last.checkedAt >= CHECK_INTERVAL_MS;
|
|
8789
|
+
if (due) await startCheck(cli, install, file, last, last?.named, launch, ctx);
|
|
8790
|
+
tell(cli, runAs, last, ctx);
|
|
8791
|
+
return due;
|
|
8792
|
+
}
|
|
8793
|
+
function tell(cli, runAs, last, ctx) {
|
|
8794
|
+
const latest = last?.latest;
|
|
8795
|
+
if (latest === void 0 || !isVersion(latest) || compareVersions(latest, cli.version) <= 0) return;
|
|
8796
|
+
ctx.notice(`${cli.name} ${latest} is out, this is ${cli.version}; run \`${runAs} update\``);
|
|
8797
|
+
}
|
|
8798
|
+
//#endregion
|
|
8103
8799
|
//#region ../agent-cli/dist/built-ins/install/run-as.js
|
|
8104
8800
|
/** The Run-as: the bare name, or `npx -q --no-install <bin>` for a Dependency install (ADR 0011). */
|
|
8105
8801
|
function runAsOf({ name, install }) {
|
|
@@ -8264,83 +8960,24 @@ function checkOf$1(cli, install, runAs, launch) {
|
|
|
8264
8960
|
npm: npmContext(cli, registryOf(install, root?.dir), ctx, launch, root?.dir ?? cwd),
|
|
8265
8961
|
advise: (latest) => adviceFor(install, root, latest)
|
|
8266
8962
|
};
|
|
8267
|
-
}
|
|
8268
|
-
};
|
|
8269
|
-
}
|
|
8270
|
-
/** The Install of a CLI each project pins as a dependency, run through npx (ADR 0011). */
|
|
8271
|
-
function dependencyInstall(cli, install) {
|
|
8272
|
-
const runAs = runAsOf(cli);
|
|
8273
|
-
return {
|
|
8274
|
-
runAs,
|
|
8275
|
-
builtIns: () => ({}),
|
|
8276
|
-
check: (launch) => checkOf$1(cli, install, runAs, launch),
|
|
8277
|
-
notice: async () => {}
|
|
8278
|
-
};
|
|
8279
|
-
}
|
|
8280
|
-
//#endregion
|
|
8281
|
-
//#region ../agent-cli/dist/built-ins/install/global-npm.js
|
|
8282
|
-
/** Where npm puts global packages under its global prefix. */
|
|
8283
|
-
function globalModulesDir(prefix, platform) {
|
|
8284
|
-
return platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
|
|
8285
|
-
}
|
|
8286
|
-
/** Whether `path` itself is a symlink, which a link in a folder above it does not make it. */
|
|
8287
|
-
async function isLink(files, path) {
|
|
8288
|
-
const real = await files.realPath(path);
|
|
8289
|
-
if (real === void 0) return false;
|
|
8290
|
-
const folder = await files.realPath(dirname(path));
|
|
8291
|
-
return folder === void 0 || real !== join(folder, basename(path));
|
|
8292
|
-
}
|
|
8293
|
-
function notGlobal(ctx, message) {
|
|
8294
|
-
return runtimeError({
|
|
8295
|
-
code: NOT_GLOBAL_INSTALL,
|
|
8296
|
-
message,
|
|
8297
|
-
hint: "install it by running the Next step, so its update Command can replace it",
|
|
8298
|
-
next: [wholeLine(setupCommandLine(ctx.registry))]
|
|
8299
|
-
});
|
|
8300
|
-
}
|
|
8301
|
-
/**
|
|
8302
|
-
* The global npm install this copy runs from, found by asking npm for its global prefix.
|
|
8303
|
-
* `bin` is the script that started the CLI; throws `NOT_GLOBAL_INSTALL` when it is not inside that install.
|
|
8304
|
-
*/
|
|
8305
|
-
async function globalInstallOf(bin, ctx) {
|
|
8306
|
-
if (bin === void 0) throw notGlobal(ctx, `${ctx.cliName} runs from source, not from an npm install`);
|
|
8307
|
-
const prefix = await globalPrefix(ctx);
|
|
8308
|
-
const packageDir = join(globalModulesDir(prefix, ctx.platform), ctx.registry.package);
|
|
8309
|
-
const installed = await ctx.files.realPath(packageDir);
|
|
8310
|
-
const here = await ctx.files.realPath(bin) ?? bin;
|
|
8311
|
-
if (installed === void 0 || !isInside(installed, here)) throw notGlobal(ctx, `${ctx.cliName} runs from ${here}, not from npm's global install at ${packageDir}`);
|
|
8312
|
-
if (await isLink(ctx.files, packageDir)) throw notGlobal(ctx, `${ctx.cliName} runs from a link to ${installed} (npm link), not from an npm install`);
|
|
8313
|
-
return {
|
|
8314
|
-
prefix,
|
|
8315
|
-
packageDir
|
|
8963
|
+
}
|
|
8316
8964
|
};
|
|
8317
8965
|
}
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
if (registry === NPMJS) return [];
|
|
8322
|
-
const scope = scopeOf(name);
|
|
8323
|
-
return [scope === void 0 ? `--registry=${registry}` : `--${scope}:registry=${registry}`];
|
|
8324
|
-
}
|
|
8325
|
-
/** The line that sets a Global install up, run before the CLI is on the machine. */
|
|
8326
|
-
function setupLineOf(install) {
|
|
8327
|
-
return setupCommandLine(globalRegistry(install));
|
|
8328
|
-
}
|
|
8329
|
-
function globalRegistry(install) {
|
|
8330
|
-
const { package: name, registry } = install;
|
|
8966
|
+
/** The Install of a CLI each project pins as a dependency, run through npx (ADR 0011). */
|
|
8967
|
+
function dependencyInstall(cli, install) {
|
|
8968
|
+
const runAs = runAsOf(cli);
|
|
8331
8969
|
return {
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8970
|
+
runAs,
|
|
8971
|
+
builtIns: () => ({}),
|
|
8972
|
+
check: (launch) => checkOf$1(cli, install, runAs, launch),
|
|
8973
|
+
releaseCheck: () => NO_RELEASE_CHECK,
|
|
8974
|
+
updateLine: async ({ files, cwd }) => {
|
|
8975
|
+
const root = await lockfileRootOf(files, cwd);
|
|
8976
|
+
const lines = root === void 0 ? void 0 : LINES[root.manager];
|
|
8977
|
+
return lines === void 0 ? void 0 : wholeLine(lines.upgrade(packagesOf(install)));
|
|
8978
|
+
}
|
|
8338
8979
|
};
|
|
8339
8980
|
}
|
|
8340
|
-
/** npm for a Global install, run in the working directory. */
|
|
8341
|
-
function globalNpm(cli, install, ctx, launch) {
|
|
8342
|
-
return npmContext(cli, globalRegistry(install), ctx, launch, ctx.cwd);
|
|
8343
|
-
}
|
|
8344
8981
|
//#endregion
|
|
8345
8982
|
//#region ../agent-cli/dist/built-ins/skill/sync.js
|
|
8346
8983
|
async function walk$1(files, dir, inner) {
|
|
@@ -8821,121 +9458,6 @@ function updateCommand(cli, install, launch) {
|
|
|
8821
9458
|
});
|
|
8822
9459
|
}
|
|
8823
9460
|
//#endregion
|
|
8824
|
-
//#region ../agent-cli/dist/built-ins/install/release-check.js
|
|
8825
|
-
const CHECK_INTERVAL_MS = 864e5;
|
|
8826
|
-
const checkSchema = object({
|
|
8827
|
-
checkedAt: number(),
|
|
8828
|
-
latest: string().optional(),
|
|
8829
|
-
error: string().optional()
|
|
8830
|
-
});
|
|
8831
|
-
const CHECK_SCRIPT = `
|
|
8832
|
-
const { spawnSync } = require("node:child_process");
|
|
8833
|
-
const { mkdtempSync, renameSync, rmSync, writeFileSync } = require("node:fs");
|
|
8834
|
-
const { tmpdir } = require("node:os");
|
|
8835
|
-
const { join } = require("node:path");
|
|
8836
|
-
const [file, checkedAt, previous, npm, ...args] = process.argv.slice(1);
|
|
8837
|
-
const windows = process.platform === "win32";
|
|
8838
|
-
const cache = mkdtempSync(join(tmpdir(), "release-check-"));
|
|
8839
|
-
const write = (check) => {
|
|
8840
|
-
const temp = file + "." + process.pid + ".tmp";
|
|
8841
|
-
writeFileSync(temp, JSON.stringify({ checkedAt: Number(checkedAt), ...check }));
|
|
8842
|
-
renameSync(temp, file);
|
|
8843
|
-
};
|
|
8844
|
-
const latestOf = (stdout) => {
|
|
8845
|
-
try {
|
|
8846
|
-
const parsed = JSON.parse(stdout);
|
|
8847
|
-
const latest = Array.isArray(parsed) ? parsed.at(-1) : parsed;
|
|
8848
|
-
return typeof latest === "string" && /^\\d+\\.\\d+\\.\\d+/.test(latest) ? latest : undefined;
|
|
8849
|
-
} catch {
|
|
8850
|
-
return undefined;
|
|
8851
|
-
}
|
|
8852
|
-
};
|
|
8853
|
-
try {
|
|
8854
|
-
const answer = spawnSync(windows ? '"' + npm + '"' : npm, args, {
|
|
8855
|
-
encoding: "utf8",
|
|
8856
|
-
shell: windows,
|
|
8857
|
-
windowsHide: true,
|
|
8858
|
-
env: { ...process.env, npm_config_cache: join(cache, "cache"), npm_config_logs_max: "0" },
|
|
8859
|
-
});
|
|
8860
|
-
const latest = answer.status === 0 ? latestOf(answer.stdout) : undefined;
|
|
8861
|
-
if (latest !== undefined) {
|
|
8862
|
-
write({ latest });
|
|
8863
|
-
} else {
|
|
8864
|
-
const said = (answer.stderr || "").trim().split(/\\r?\\n/).slice(0, 5).join("; ");
|
|
8865
|
-
const error = answer.error ? answer.error.message : said || "npm answered " + answer.stdout.trim();
|
|
8866
|
-
write({ latest: previous || undefined, error });
|
|
8867
|
-
}
|
|
8868
|
-
} finally {
|
|
8869
|
-
rmSync(cache, { recursive: true, force: true });
|
|
8870
|
-
}
|
|
8871
|
-
`;
|
|
8872
|
-
function isOff(cliName, env) {
|
|
8873
|
-
return Boolean(env[cliVariable(cliName, "NO_RELEASE_CHECK")] || env.CI);
|
|
8874
|
-
}
|
|
8875
|
-
function cacheFileOf(cliName, env) {
|
|
8876
|
-
const cache = env.XDG_CACHE_HOME || join(env.HOME || env.USERPROFILE || homedir(), ".cache");
|
|
8877
|
-
return join(cache, cliName, "release-check.json");
|
|
8878
|
-
}
|
|
8879
|
-
function parsedCheck(text) {
|
|
8880
|
-
try {
|
|
8881
|
-
return checkSchema.safeParse(JSON.parse(text)).data;
|
|
8882
|
-
} catch {
|
|
8883
|
-
return;
|
|
8884
|
-
}
|
|
8885
|
-
}
|
|
8886
|
-
async function lastCheck(files, file) {
|
|
8887
|
-
const text = await readIfAny(files, file);
|
|
8888
|
-
return text === void 0 ? void 0 : parsedCheck(text);
|
|
8889
|
-
}
|
|
8890
|
-
async function startCheck(cli, install, file, last, launch, ctx) {
|
|
8891
|
-
const checkedAt = ctx.clock.now();
|
|
8892
|
-
const previous = last?.latest ?? "";
|
|
8893
|
-
await ctx.files.write(file, JSON.stringify({
|
|
8894
|
-
checkedAt,
|
|
8895
|
-
latest: last?.latest
|
|
8896
|
-
}));
|
|
8897
|
-
const npm = globalNpm(cli, install, ctx, launch);
|
|
8898
|
-
const { path, env } = await npmOf(npm);
|
|
8899
|
-
await ctx.spawnDetached(process.execPath, [
|
|
8900
|
-
"-e",
|
|
8901
|
-
CHECK_SCRIPT,
|
|
8902
|
-
file,
|
|
8903
|
-
String(checkedAt),
|
|
8904
|
-
previous,
|
|
8905
|
-
path,
|
|
8906
|
-
...latestViewArgs(npm.registry)
|
|
8907
|
-
], { env });
|
|
8908
|
-
}
|
|
8909
|
-
function debugLine(cli, ctx, text) {
|
|
8910
|
-
if (isDebug({
|
|
8911
|
-
env: ctx.env,
|
|
8912
|
-
cliName: cli.name
|
|
8913
|
-
})) ctx.progress(`release check failed: ${text}`);
|
|
8914
|
-
}
|
|
8915
|
-
/**
|
|
8916
|
-
* Tells the caller of a newer release that an earlier Release check found, and starts the next
|
|
8917
|
-
* check once the last is a day old.
|
|
8918
|
-
*/
|
|
8919
|
-
async function releaseCheck(cli, install, runAs, launch, ctx) {
|
|
8920
|
-
if (launch.builtIn || launch.bin === void 0 || cli.commands === void 0) return;
|
|
8921
|
-
if (isOff(cli.name, ctx.env)) return;
|
|
8922
|
-
try {
|
|
8923
|
-
await checkAndTell(cli, install, runAs, launch, ctx);
|
|
8924
|
-
} catch (error) {
|
|
8925
|
-
if (errnoOf(error) === void 0 && !(error instanceof CliError)) throw error;
|
|
8926
|
-
debugLine(cli, ctx, messageOf(error));
|
|
8927
|
-
}
|
|
8928
|
-
}
|
|
8929
|
-
async function checkAndTell(cli, install, runAs, launch, ctx) {
|
|
8930
|
-
const file = cacheFileOf(cli.name, ctx.env);
|
|
8931
|
-
const last = await lastCheck(ctx.files, file);
|
|
8932
|
-
if (last?.error !== void 0) debugLine(cli, ctx, last.error);
|
|
8933
|
-
if (last === void 0 || ctx.clock.now() - last.checkedAt >= CHECK_INTERVAL_MS) await startCheck(cli, install, file, last, launch, ctx);
|
|
8934
|
-
const latest = last?.latest;
|
|
8935
|
-
if (latest === void 0 || !isVersion(latest) || compareVersions(latest, cli.version) <= 0) return;
|
|
8936
|
-
ctx.notice(`${cli.name} ${latest} is out, this is ${cli.version}; run \`${runAs} update\``);
|
|
8937
|
-
}
|
|
8938
|
-
//#endregion
|
|
8939
9461
|
//#region ../agent-cli/dist/built-ins/install/global.js
|
|
8940
9462
|
function checkOf(cli, install, launch) {
|
|
8941
9463
|
return {
|
|
@@ -8973,13 +9495,13 @@ function globalInstall(cli, install) {
|
|
|
8973
9495
|
update: updateCommand(cli, install, launch)
|
|
8974
9496
|
}),
|
|
8975
9497
|
check: (launch) => checkOf(cli, install, launch),
|
|
8976
|
-
|
|
9498
|
+
releaseCheck: (launch) => releaseCheck(cli, install, adapter.runAs, launch),
|
|
9499
|
+
updateLine: async () => ["update"]
|
|
8977
9500
|
};
|
|
8978
9501
|
return adapter;
|
|
8979
9502
|
}
|
|
8980
9503
|
//#endregion
|
|
8981
9504
|
//#region ../agent-cli/dist/built-ins/install/install.js
|
|
8982
|
-
const noNotice = async () => {};
|
|
8983
9505
|
function noInstall(cli) {
|
|
8984
9506
|
const skipped = async () => ({
|
|
8985
9507
|
state: "skipped",
|
|
@@ -8992,7 +9514,8 @@ function noInstall(cli) {
|
|
|
8992
9514
|
install: skipped,
|
|
8993
9515
|
release: async () => ({ skipped: await skipped() })
|
|
8994
9516
|
}),
|
|
8995
|
-
|
|
9517
|
+
releaseCheck: () => NO_RELEASE_CHECK,
|
|
9518
|
+
updateLine: async () => void 0
|
|
8996
9519
|
};
|
|
8997
9520
|
}
|
|
8998
9521
|
function installOf(cli) {
|
|
@@ -9109,7 +9632,7 @@ const SECTION_VERBS = {
|
|
|
9109
9632
|
add: "add the Agents section to",
|
|
9110
9633
|
rewrite: "rewrite the Agents section in"
|
|
9111
9634
|
};
|
|
9112
|
-
function renderPlan(cli, plan) {
|
|
9635
|
+
function renderPlan$1(cli, plan) {
|
|
9113
9636
|
const files = [
|
|
9114
9637
|
...plan.write.map((file) => ` write ${file}`),
|
|
9115
9638
|
...plan.replace.map((file) => ` replace ${file}`),
|
|
@@ -9163,7 +9686,7 @@ function skillInstallCommand(cli, folder, agents) {
|
|
|
9163
9686
|
};
|
|
9164
9687
|
},
|
|
9165
9688
|
render: (data) => renderInstalled(cli, data),
|
|
9166
|
-
renderPlan: (plan) => renderPlan(cli, plan)
|
|
9689
|
+
renderPlan: (plan) => renderPlan$1(cli, plan)
|
|
9167
9690
|
});
|
|
9168
9691
|
}
|
|
9169
9692
|
//#endregion
|
|
@@ -9363,6 +9886,7 @@ function doctorChecks(cli, launch, scope) {
|
|
|
9363
9886
|
...cli.doctor ?? [],
|
|
9364
9887
|
...tools,
|
|
9365
9888
|
...secretRows(cli.secrets ?? [], cli.commands),
|
|
9889
|
+
...loginRows(cli.logins ?? {}, scope.runAs),
|
|
9366
9890
|
installRow(check),
|
|
9367
9891
|
registryRow(cli, check),
|
|
9368
9892
|
...skillOf(cli, install).rows
|
|
@@ -9373,7 +9897,6 @@ function doctorChecks(cli, launch, scope) {
|
|
|
9373
9897
|
//#endregion
|
|
9374
9898
|
//#region ../agent-cli/dist/built-ins/doctor.js
|
|
9375
9899
|
const DEFAULT_DEADLINE_MS = 1e4;
|
|
9376
|
-
const MS_PER_SECOND = 1e3;
|
|
9377
9900
|
const MIN_LABEL_WIDTH = 8;
|
|
9378
9901
|
const reportSchema = object({
|
|
9379
9902
|
rows: array(object({
|
|
@@ -9392,7 +9915,7 @@ const reportSchema = object({
|
|
|
9392
9915
|
function problemOf(check, error, { signal, deadline, ms, runAs }) {
|
|
9393
9916
|
if (!signal.aborted && deadline.aborted && error === deadline.reason) return {
|
|
9394
9917
|
state: "problem",
|
|
9395
|
-
message: `${check.name} did not answer within ${ms /
|
|
9918
|
+
message: `${check.name} did not answer within ${ms / MS_PER_S} s`
|
|
9396
9919
|
};
|
|
9397
9920
|
return problemOfError(check.name, error, runAs);
|
|
9398
9921
|
}
|
|
@@ -9527,17 +10050,338 @@ function doctorCommand(cli, launch) {
|
|
|
9527
10050
|
};
|
|
9528
10051
|
}
|
|
9529
10052
|
//#endregion
|
|
10053
|
+
//#region ../agent-cli/dist/login/declared.js
|
|
10054
|
+
function declaredLogin(logins, name) {
|
|
10055
|
+
const login = logins[name];
|
|
10056
|
+
if (login === void 0) throw new Error(`${name} is not a Login the CLI declares`);
|
|
10057
|
+
return login;
|
|
10058
|
+
}
|
|
10059
|
+
//#endregion
|
|
10060
|
+
//#region ../agent-cli/dist/login/device-flow.js
|
|
10061
|
+
const DEVICE_CODE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
10062
|
+
/** RFC 8628 §3.2: the interval when the server gives none. */
|
|
10063
|
+
const DEFAULT_INTERVAL_S = 5;
|
|
10064
|
+
/** RFC 8628 §3.5: what each `slow_down` adds to the interval. */
|
|
10065
|
+
const SLOW_DOWN_S = 5;
|
|
10066
|
+
const deviceSchema = object({
|
|
10067
|
+
device_code: string(),
|
|
10068
|
+
user_code: string(),
|
|
10069
|
+
verification_uri: string(),
|
|
10070
|
+
verification_uri_complete: string().optional(),
|
|
10071
|
+
expires_in: number$1(),
|
|
10072
|
+
interval: number$1().optional()
|
|
10073
|
+
});
|
|
10074
|
+
async function startApproval(login, { fetch, clock }) {
|
|
10075
|
+
const form = {
|
|
10076
|
+
client_id: login.clientId,
|
|
10077
|
+
...login.scope === void 0 ? {} : { scope: login.scope }
|
|
10078
|
+
};
|
|
10079
|
+
const device = parsedAnswer(deviceSchema, await postForm(fetch, login.deviceEndpoint, form), login.deviceEndpoint);
|
|
10080
|
+
return {
|
|
10081
|
+
deviceCode: device.device_code,
|
|
10082
|
+
userCode: device.user_code,
|
|
10083
|
+
verificationUri: device.verification_uri,
|
|
10084
|
+
...device.verification_uri_complete === void 0 ? {} : { verificationUriComplete: device.verification_uri_complete },
|
|
10085
|
+
expiresAt: isoAfter(clock, device.expires_in),
|
|
10086
|
+
interval: device.interval ?? DEFAULT_INTERVAL_S
|
|
10087
|
+
};
|
|
10088
|
+
}
|
|
10089
|
+
async function pollApproval(name, login, { approval, takenUp }, scope, signal) {
|
|
10090
|
+
const form = {
|
|
10091
|
+
grant_type: DEVICE_CODE_GRANT,
|
|
10092
|
+
device_code: approval.deviceCode,
|
|
10093
|
+
client_id: login.clientId
|
|
10094
|
+
};
|
|
10095
|
+
let intervalS = approval.interval;
|
|
10096
|
+
for (let first = true;; first = false) {
|
|
10097
|
+
if (!(first && takenUp)) await scope.clock.sleep(intervalS * MS_PER_S, signal);
|
|
10098
|
+
const body = await postForm(scope.fetch, login.tokenEndpoint, form);
|
|
10099
|
+
const granted = tokenSchema.safeParse(body);
|
|
10100
|
+
if (granted.success) return storedLoginOf(granted.data, scope.clock);
|
|
10101
|
+
const { error } = parsedAnswer(errorSchema, body, login.tokenEndpoint);
|
|
10102
|
+
if (error === "slow_down") intervalS += SLOW_DOWN_S;
|
|
10103
|
+
else if (error !== "authorization_pending") throw refusal(name, login, error);
|
|
10104
|
+
}
|
|
10105
|
+
}
|
|
10106
|
+
/** The error a token endpoint's answer other than pending or slow down ends the polling with. */
|
|
10107
|
+
function refusal(name, login, error) {
|
|
10108
|
+
if (error === "access_denied") return approvalDenied(name);
|
|
10109
|
+
if (error === "expired_token" || error === "invalid_grant") return approvalExpired(name);
|
|
10110
|
+
return /* @__PURE__ */ new Error(`${login.tokenEndpoint} refused the device code with ${error}`);
|
|
10111
|
+
}
|
|
10112
|
+
function endsApproval(error) {
|
|
10113
|
+
return error instanceof CliError && (error.code === "APPROVAL_DENIED" || error.code === "APPROVAL_EXPIRED");
|
|
10114
|
+
}
|
|
10115
|
+
/** Takes up the pending Approval, or asks for one and keeps it as a User file, and prints its code. */
|
|
10116
|
+
async function askedApproval(login, files, scope) {
|
|
10117
|
+
const pending = await readUnexpired(files.approval, scope.clock);
|
|
10118
|
+
const approval = pending ?? await startApproval(login, scope);
|
|
10119
|
+
if (pending === void 0) await files.approval.write(approval);
|
|
10120
|
+
scope.progress(`open ${approval.verificationUriComplete ?? approval.verificationUri} and approve code ${approval.userCode}`);
|
|
10121
|
+
return {
|
|
10122
|
+
approval,
|
|
10123
|
+
takenUp: pending !== void 0
|
|
10124
|
+
};
|
|
10125
|
+
}
|
|
10126
|
+
/** The `APPROVAL_PENDING` of the pending Approval, or of a new one, without polling it. */
|
|
10127
|
+
async function pendingApproval(name, login, files, scope, rerun) {
|
|
10128
|
+
const { approval } = await askedApproval(login, files, scope);
|
|
10129
|
+
return approvalPending(name, approval, rerun);
|
|
10130
|
+
}
|
|
10131
|
+
/**
|
|
10132
|
+
* Polls the pending Approval, or a new one, until the person approves, and stores the Login. A run
|
|
10133
|
+
* that stops first leaves the Approval for the next.
|
|
10134
|
+
*/
|
|
10135
|
+
async function signIn(name, login, files, scope, { waitS, rerun }) {
|
|
10136
|
+
const asked = await askedApproval(login, files, scope);
|
|
10137
|
+
const wait = scope.deadline(waitS * MS_PER_S);
|
|
10138
|
+
let stored;
|
|
10139
|
+
try {
|
|
10140
|
+
stored = await pollApproval(name, login, asked, scope, wait);
|
|
10141
|
+
} catch (error) {
|
|
10142
|
+
if (wait.aborted && !scope.signal.aborted) throw approvalPending(name, asked.approval, rerun);
|
|
10143
|
+
if (endsApproval(error)) await files.approval.remove();
|
|
10144
|
+
throw error;
|
|
10145
|
+
}
|
|
10146
|
+
await files.stored.write(stored);
|
|
10147
|
+
await files.approval.remove();
|
|
10148
|
+
return stored;
|
|
10149
|
+
}
|
|
10150
|
+
//#endregion
|
|
10151
|
+
//#region ../agent-cli/dist/built-ins/login-name.js
|
|
10152
|
+
/** The Login a `login` or `logout` run names, or the only one; with several, the name is needed. */
|
|
10153
|
+
function chosenName(run, given, names) {
|
|
10154
|
+
const [only, ...others] = names;
|
|
10155
|
+
if (given !== void 0) return given;
|
|
10156
|
+
if (others.length === 0) return only;
|
|
10157
|
+
throw new CliError({
|
|
10158
|
+
code: INVALID_INPUT,
|
|
10159
|
+
message: `\`${run.cliName} ${run.command}\` needs the name of a Login: ${names.join(", ")}`,
|
|
10160
|
+
hint: "run the Next step for the Login the task needs",
|
|
10161
|
+
next: names.map((name) => [run.command, name])
|
|
10162
|
+
});
|
|
10163
|
+
}
|
|
10164
|
+
/** The optional `name` arg of `login` and `logout`; `purpose` reads as "the Login to <purpose>". */
|
|
10165
|
+
function nameArgs(names, purpose) {
|
|
10166
|
+
return object({ name: _enum(names).optional().describe(`the Login to ${purpose}, of ${names.join(", ")}; needed only with several`) });
|
|
10167
|
+
}
|
|
10168
|
+
/** The name an example passes: none with one Login, the first with several. */
|
|
10169
|
+
function exampleName(names) {
|
|
10170
|
+
return names.length === 1 ? [] : [names[0]];
|
|
10171
|
+
}
|
|
10172
|
+
//#endregion
|
|
10173
|
+
//#region ../agent-cli/dist/built-ins/logout.js
|
|
10174
|
+
const logoutSchema = object({
|
|
10175
|
+
login: string(),
|
|
10176
|
+
stored: _enum([
|
|
10177
|
+
"revoked",
|
|
10178
|
+
"removed",
|
|
10179
|
+
"none"
|
|
10180
|
+
]),
|
|
10181
|
+
/** The Secret still set for this Login, which logout never touches. */
|
|
10182
|
+
secret: string().optional()
|
|
10183
|
+
});
|
|
10184
|
+
function renderLogout({ login, stored, secret }) {
|
|
10185
|
+
const head = stored === "none" ? `not signed in to ${login}: nothing to do` : `signed out of ${login}${stored === "revoked" ? ", and the server revoked its token" : ""}`;
|
|
10186
|
+
return secret === void 0 ? head : `${head}; ${login} still uses the token in ${secret}`;
|
|
10187
|
+
}
|
|
10188
|
+
function renderPlan({ login, stored, revokeEndpoint, secret }) {
|
|
10189
|
+
if (!stored) return renderLogout({
|
|
10190
|
+
login,
|
|
10191
|
+
stored: "none",
|
|
10192
|
+
...secret === void 0 ? {} : { secret }
|
|
10193
|
+
});
|
|
10194
|
+
return `would sign out of ${login}${revokeEndpoint === void 0 ? "" : `, revoking its token at ${revokeEndpoint}`}`;
|
|
10195
|
+
}
|
|
10196
|
+
/**
|
|
10197
|
+
* RFC 7009 §2.1: revoking the refresh token ends the grant, so the server should drop its access
|
|
10198
|
+
* tokens too; a Login without one revokes its access token.
|
|
10199
|
+
*/
|
|
10200
|
+
async function revoke(endpoint, { clientId }, { accessToken, refreshToken }, ctx) {
|
|
10201
|
+
const [token, hint] = refreshToken === void 0 ? [accessToken, "access_token"] : [refreshToken, "refresh_token"];
|
|
10202
|
+
const response = await ctx.fetch(endpoint, {
|
|
10203
|
+
method: "POST",
|
|
10204
|
+
once: false,
|
|
10205
|
+
body: new URLSearchParams({
|
|
10206
|
+
token,
|
|
10207
|
+
token_type_hint: hint,
|
|
10208
|
+
client_id: clientId
|
|
10209
|
+
})
|
|
10210
|
+
});
|
|
10211
|
+
if (!response.ok) throw new Error(`${endpoint} answered ${response.status} to the revocation of a token`);
|
|
10212
|
+
}
|
|
10213
|
+
/** The Built-in `logout`: revokes and removes the stored Login, never touching its Secret. */
|
|
10214
|
+
function logoutCommand(cli, logins, names) {
|
|
10215
|
+
const named = exampleName(names);
|
|
10216
|
+
return defineCommand$1.write({
|
|
10217
|
+
description: "sign out: revoke the stored Login and remove it",
|
|
10218
|
+
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.",
|
|
10219
|
+
examples: [{
|
|
10220
|
+
command: [
|
|
10221
|
+
cli.name,
|
|
10222
|
+
"logout",
|
|
10223
|
+
...named
|
|
10224
|
+
].join(" "),
|
|
10225
|
+
note: "sign out"
|
|
10226
|
+
}],
|
|
10227
|
+
args: nameArgs(names, "sign out of"),
|
|
10228
|
+
output: logoutSchema,
|
|
10229
|
+
plan: async ({ name: given }, ctx) => {
|
|
10230
|
+
const name = chosenName({
|
|
10231
|
+
cliName: cli.name,
|
|
10232
|
+
command: "logout"
|
|
10233
|
+
}, given, names);
|
|
10234
|
+
const login = declaredLogin(logins, name);
|
|
10235
|
+
return {
|
|
10236
|
+
login: name,
|
|
10237
|
+
stored: await loginFilesOf(ctx.userFile, login).stored.read() !== void 0,
|
|
10238
|
+
revokeEndpoint: login.revokeEndpoint,
|
|
10239
|
+
secret: setSecretOf(login, tokenScopeOf(ctx).secret)?.name
|
|
10240
|
+
};
|
|
10241
|
+
},
|
|
10242
|
+
apply: async ({ login: name, secret }, ctx) => {
|
|
10243
|
+
const login = declaredLogin(logins, name);
|
|
10244
|
+
const file = loginFilesOf(ctx.userFile, login).stored;
|
|
10245
|
+
const stored = await file.read();
|
|
10246
|
+
const kept = secret === void 0 ? {} : { secret };
|
|
10247
|
+
if (stored === void 0) return {
|
|
10248
|
+
login: name,
|
|
10249
|
+
stored: "none",
|
|
10250
|
+
...kept
|
|
10251
|
+
};
|
|
10252
|
+
if (login.revokeEndpoint !== void 0) await revoke(login.revokeEndpoint, login, stored, ctx);
|
|
10253
|
+
await file.remove();
|
|
10254
|
+
return {
|
|
10255
|
+
login: name,
|
|
10256
|
+
stored: login.revokeEndpoint === void 0 ? "removed" : "revoked",
|
|
10257
|
+
...kept
|
|
10258
|
+
};
|
|
10259
|
+
},
|
|
10260
|
+
render: renderLogout,
|
|
10261
|
+
renderPlan
|
|
10262
|
+
});
|
|
10263
|
+
}
|
|
10264
|
+
//#endregion
|
|
10265
|
+
//#region ../agent-cli/dist/built-ins/login.js
|
|
10266
|
+
const loginSchema = object({
|
|
10267
|
+
login: string(),
|
|
10268
|
+
source: _enum([
|
|
10269
|
+
"approved",
|
|
10270
|
+
"stored",
|
|
10271
|
+
"secret"
|
|
10272
|
+
]),
|
|
10273
|
+
secret: string().optional()
|
|
10274
|
+
});
|
|
10275
|
+
function renderLogin({ login, source, secret }) {
|
|
10276
|
+
if (source === "approved") return `signed in to ${login}`;
|
|
10277
|
+
if (source === "stored") return `already signed in to ${login}: nothing to do`;
|
|
10278
|
+
return `${login} uses the token in ${secret}: nothing to do`;
|
|
10279
|
+
}
|
|
10280
|
+
function foundData(login, { source }) {
|
|
10281
|
+
return source.kind === "secret" ? {
|
|
10282
|
+
login,
|
|
10283
|
+
source: "secret",
|
|
10284
|
+
secret: source.name
|
|
10285
|
+
} : {
|
|
10286
|
+
login,
|
|
10287
|
+
source: "stored"
|
|
10288
|
+
};
|
|
10289
|
+
}
|
|
10290
|
+
function loginCommand(cli, logins, names) {
|
|
10291
|
+
const named = exampleName(names);
|
|
10292
|
+
return defineCommand$1.write({
|
|
10293
|
+
description: "sign in through a browser, so the Commands that need a Login can run",
|
|
10294
|
+
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.",
|
|
10295
|
+
examples: [{
|
|
10296
|
+
command: [
|
|
10297
|
+
cli.name,
|
|
10298
|
+
"login",
|
|
10299
|
+
...named
|
|
10300
|
+
].join(" "),
|
|
10301
|
+
note: `sign in, waiting at most 100 s for the code to be approved`
|
|
10302
|
+
}, {
|
|
10303
|
+
command: [
|
|
10304
|
+
cli.name,
|
|
10305
|
+
"login",
|
|
10306
|
+
...named,
|
|
10307
|
+
WAIT_FLAG.flag,
|
|
10308
|
+
"30"
|
|
10309
|
+
].join(" "),
|
|
10310
|
+
note: "wait at most 30 s for the approval"
|
|
10311
|
+
}],
|
|
10312
|
+
args: nameArgs(names, "sign in to"),
|
|
10313
|
+
options: object({ wait: number$1().int().nonnegative().optional().describe(`${WAIT_FLAG.description}; 100 when absent`) }),
|
|
10314
|
+
output: loginSchema,
|
|
10315
|
+
plan: async ({ name: given, wait }, ctx) => {
|
|
10316
|
+
const name = chosenName({
|
|
10317
|
+
cliName: cli.name,
|
|
10318
|
+
command: "login"
|
|
10319
|
+
}, given, names);
|
|
10320
|
+
const login = declaredLogin(logins, name);
|
|
10321
|
+
const token = await currentTokenOf(login, tokenScopeOf(ctx));
|
|
10322
|
+
const stored = await readStored(loginFilesOf(ctx.userFile, login).stored, ctx.clock);
|
|
10323
|
+
return {
|
|
10324
|
+
login: name,
|
|
10325
|
+
wait,
|
|
10326
|
+
found: token && foundData(name, token),
|
|
10327
|
+
renew: stored?.refreshToken !== void 0
|
|
10328
|
+
};
|
|
10329
|
+
},
|
|
10330
|
+
apply: async ({ login: name, wait, found }, ctx) => {
|
|
10331
|
+
if (found !== void 0) return found;
|
|
10332
|
+
const login = declaredLogin(logins, name);
|
|
10333
|
+
const files = loginFilesOf(ctx.userFile, login);
|
|
10334
|
+
const stored = await readStored(files.stored, ctx.clock);
|
|
10335
|
+
if (stored?.refreshToken !== void 0 && await refreshed(name, login, files.stored, stored.refreshToken, ctx) !== void 0) return {
|
|
10336
|
+
login: name,
|
|
10337
|
+
source: "stored"
|
|
10338
|
+
};
|
|
10339
|
+
await signIn(name, login, files, ctx, {
|
|
10340
|
+
waitS: wait ?? 100,
|
|
10341
|
+
rerun: [
|
|
10342
|
+
"login",
|
|
10343
|
+
name,
|
|
10344
|
+
WAIT_FLAG.flag,
|
|
10345
|
+
String(100)
|
|
10346
|
+
]
|
|
10347
|
+
});
|
|
10348
|
+
return {
|
|
10349
|
+
login: name,
|
|
10350
|
+
source: "approved"
|
|
10351
|
+
};
|
|
10352
|
+
},
|
|
10353
|
+
render: renderLogin,
|
|
10354
|
+
renderPlan: ({ login, found, renew }) => {
|
|
10355
|
+
if (found !== void 0) return renderLogin(found);
|
|
10356
|
+
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`;
|
|
10357
|
+
}
|
|
10358
|
+
});
|
|
10359
|
+
}
|
|
10360
|
+
/** The Built-in `login` and `logout`, for a CLI that declares a Login. */
|
|
10361
|
+
function loginBuiltIns(cli) {
|
|
10362
|
+
const logins = cli.logins ?? {};
|
|
10363
|
+
const [first, ...rest] = Object.keys(logins);
|
|
10364
|
+
if (first === void 0) return {};
|
|
10365
|
+
const names = [first, ...rest];
|
|
10366
|
+
return {
|
|
10367
|
+
login: loginCommand(cli, logins, names),
|
|
10368
|
+
logout: logoutCommand(cli, logins, names)
|
|
10369
|
+
};
|
|
10370
|
+
}
|
|
10371
|
+
//#endregion
|
|
9530
10372
|
//#region ../agent-cli/dist/built-ins/commands.js
|
|
9531
10373
|
/**
|
|
9532
10374
|
* The Commands the Runtime adds to a multi-Command CLI: `setup` and `update` only for a Global
|
|
9533
|
-
* install,
|
|
10375
|
+
* install, `skill install` only for a skill whose copy goes under the project, and `login` only
|
|
10376
|
+
* for a CLI that declares a Login.
|
|
9534
10377
|
*/
|
|
9535
10378
|
function builtInCommands(cli, launch) {
|
|
9536
10379
|
const install = installOf(cli);
|
|
9537
10380
|
return {
|
|
9538
10381
|
doctor: doctorCommand(cli, launch),
|
|
9539
10382
|
...install.builtIns(launch),
|
|
9540
|
-
...skillOf(cli, install).builtIns
|
|
10383
|
+
...skillOf(cli, install).builtIns,
|
|
10384
|
+
...loginBuiltIns(cli)
|
|
9541
10385
|
};
|
|
9542
10386
|
}
|
|
9543
10387
|
//#endregion
|
|
@@ -9551,15 +10395,18 @@ function commandLabel(path, cliName) {
|
|
|
9551
10395
|
return path.length === 0 ? [cliName] : path;
|
|
9552
10396
|
}
|
|
9553
10397
|
function declaredOf(command) {
|
|
10398
|
+
const wait = takesWait(command);
|
|
9554
10399
|
if (!hasPlan(command)) return {
|
|
9555
10400
|
outputs: false,
|
|
9556
10401
|
force: void 0,
|
|
9557
|
-
cost: false
|
|
10402
|
+
cost: false,
|
|
10403
|
+
wait
|
|
9558
10404
|
};
|
|
9559
10405
|
return {
|
|
9560
10406
|
outputs: command.outputs !== void 0,
|
|
9561
10407
|
force: command.force,
|
|
9562
|
-
cost: command.cost !== void 0
|
|
10408
|
+
cost: command.cost !== void 0,
|
|
10409
|
+
wait
|
|
9563
10410
|
};
|
|
9564
10411
|
}
|
|
9565
10412
|
function withRunAs(example, { name, runAs }) {
|
|
@@ -9674,11 +10521,11 @@ function checkOwnCodes(errors) {
|
|
|
9674
10521
|
const redeclared = runtimeCodesIn(errors);
|
|
9675
10522
|
if (redeclared.length > 0) throw new Error(`The Error table declares ${redeclared.join(", ")}, an error code the Runtime already declares`);
|
|
9676
10523
|
}
|
|
9677
|
-
function checkTree(commands, path, table) {
|
|
10524
|
+
function checkTree(commands, path, table, logins) {
|
|
9678
10525
|
for (const [key, node] of Object.entries(commands)) {
|
|
9679
10526
|
const at = [...path, key];
|
|
9680
|
-
if (isCommand(node)) checkCommand(node, at.join(" "), table);
|
|
9681
|
-
else checkTree(node, at, table);
|
|
10527
|
+
if (isCommand(node)) checkCommand(node, at.join(" "), table, logins);
|
|
10528
|
+
else checkTree(node, at, table, logins);
|
|
9682
10529
|
}
|
|
9683
10530
|
}
|
|
9684
10531
|
function checkSkill({ name, skill, install }) {
|
|
@@ -9697,13 +10544,13 @@ function checkDeclared(cli) {
|
|
|
9697
10544
|
const table = errorTableOf(cli);
|
|
9698
10545
|
checkDocs(cli);
|
|
9699
10546
|
if (cli.commands === void 0) {
|
|
9700
|
-
checkCommand(cli.command, commandLabel([], cli.name).join(" "), table);
|
|
10547
|
+
checkCommand(cli.command, commandLabel([], cli.name).join(" "), table, {});
|
|
9701
10548
|
if (cli.skill !== void 0) throw new Error(`${cli.name} declares a skill, which only a CLI with several Commands can install`);
|
|
9702
10549
|
return cli;
|
|
9703
10550
|
}
|
|
9704
10551
|
checkSkill(cli);
|
|
9705
10552
|
const { commands } = cli;
|
|
9706
|
-
checkTree(commands, [], table);
|
|
10553
|
+
checkTree(commands, [], table, cli.logins ?? {});
|
|
9707
10554
|
for (const row of cli.doctor ?? []) checkErrors(row.errors ?? [], `the ${row.name} row of doctor`, table);
|
|
9708
10555
|
const taken = Object.keys(builtInCommands(cli, {
|
|
9709
10556
|
bin: void 0,
|
|
@@ -10013,6 +10860,12 @@ async function parseInput(spec, words, invocation, readStdin) {
|
|
|
10013
10860
|
if (only !== void 0 && unknown.length === 1 && otherLines.length === 0) throw unknownOptionError(spec.commandLine, invocation, only.flag, only.suggestion === void 0 ? [] : [only.suggestion]);
|
|
10014
10861
|
throw invalidInput(spec.commandLine, invocation, unknown, otherLines);
|
|
10015
10862
|
}
|
|
10863
|
+
function waitOf(given, commandLine) {
|
|
10864
|
+
if (given === void 0) return void 0;
|
|
10865
|
+
const parsed = WAIT_SECONDS.safeParse(given === true ? void 0 : given);
|
|
10866
|
+
if (!parsed.success) throw invalidInputError(commandLine, `option '${WAIT_FLAG.usage}' takes a whole number of seconds, not ${given === true ? "nothing" : JSON.stringify(given)}`);
|
|
10867
|
+
return parsed.data;
|
|
10868
|
+
}
|
|
10016
10869
|
/**
|
|
10017
10870
|
* The validated input and Standard flag values of the routed Command, or one usage error; the arg
|
|
10018
10871
|
* given `-` takes its text from `readStdin`.
|
|
@@ -10028,6 +10881,7 @@ async function parseCommand({ spec, words, json }, invocation, readStdin) {
|
|
|
10028
10881
|
confirm: typeof confirm === "string" ? confirm : void 0,
|
|
10029
10882
|
yes: values.get(YES_FLAG.flag) === true,
|
|
10030
10883
|
force: values.get(FORCE_FLAG.flag) === true,
|
|
10884
|
+
wait: waitOf(values.get(WAIT_FLAG.flag), spec.commandLine),
|
|
10031
10885
|
page,
|
|
10032
10886
|
fields: spec.list === void 0 ? void 0 : selectedFields(values, spec.list.rowFields, spec.commandLine)
|
|
10033
10887
|
}
|
|
@@ -10705,6 +11559,28 @@ function rootSpec(cli, launch) {
|
|
|
10705
11559
|
} : groupSpec(names, cli.commands, builtInCommands(cli, launch));
|
|
10706
11560
|
}
|
|
10707
11561
|
//#endregion
|
|
11562
|
+
//#region ../agent-cli/dist/built-ins/install/release-too-old.js
|
|
11563
|
+
function isTooOld(error) {
|
|
11564
|
+
return error instanceof CliError && error.code === "RELEASE_TOO_OLD";
|
|
11565
|
+
}
|
|
11566
|
+
function mapLeaves(error, map) {
|
|
11567
|
+
if (error instanceof AggregateError && error.errors.length > 0) return new AggregateError(error.errors.map((inner) => mapLeaves(inner, map)), error.message, { cause: error.cause });
|
|
11568
|
+
return map(error);
|
|
11569
|
+
}
|
|
11570
|
+
/** `error` with the Install's update line leading the Next steps of each `RELEASE_TOO_OLD` in it. */
|
|
11571
|
+
async function withUpdateLine(error, install, ctx) {
|
|
11572
|
+
if (!errorsOf(error).some(isTooOld)) return error;
|
|
11573
|
+
const line = await install.updateLine(ctx);
|
|
11574
|
+
if (line === void 0) return error;
|
|
11575
|
+
return mapLeaves(error, (leaf) => isTooOld(leaf) ? runtimeError({
|
|
11576
|
+
code: leaf.code,
|
|
11577
|
+
message: leaf.message,
|
|
11578
|
+
hint: leaf.hint,
|
|
11579
|
+
details: leaf.details,
|
|
11580
|
+
next: [line, ...leaf.next]
|
|
11581
|
+
}, { cause: leaf.cause }) : leaf);
|
|
11582
|
+
}
|
|
11583
|
+
//#endregion
|
|
10708
11584
|
//#region ../agent-cli/dist/context/spill.js
|
|
10709
11585
|
const STDOUT_BUDGET_CHARS = 3e4;
|
|
10710
11586
|
const SPILL_RETENTION_MS = 6048e5;
|
|
@@ -11285,6 +12161,91 @@ function resolveOptions(options) {
|
|
|
11285
12161
|
};
|
|
11286
12162
|
}
|
|
11287
12163
|
//#endregion
|
|
12164
|
+
//#region ../agent-cli/dist/context/derived.js
|
|
12165
|
+
/**
|
|
12166
|
+
* `base` with `extra`'s properties over it, built on `base` as its prototype, so a lazy getter of
|
|
12167
|
+
* `base` such as `ctx.ports` is not read, as a spread would.
|
|
12168
|
+
*/
|
|
12169
|
+
function derived(base, extra) {
|
|
12170
|
+
const child = Object.create(base);
|
|
12171
|
+
return Object.assign(child, extra);
|
|
12172
|
+
}
|
|
12173
|
+
//#endregion
|
|
12174
|
+
//#region ../agent-cli/dist/login/with-logins.js
|
|
12175
|
+
/** The rerun of an `APPROVAL_PENDING`, which waits for the approval. */
|
|
12176
|
+
function approvalRerun(ctx) {
|
|
12177
|
+
return ctx.rerun({
|
|
12178
|
+
remove: [WAIT_FLAG.flag],
|
|
12179
|
+
add: [WAIT_FLAG.flag, String(100)]
|
|
12180
|
+
});
|
|
12181
|
+
}
|
|
12182
|
+
async function tokensOf(names, logins, ctx, waitS) {
|
|
12183
|
+
const found = await Promise.all(names.map(async (name) => {
|
|
12184
|
+
const login = declaredLogin(logins, name);
|
|
12185
|
+
return {
|
|
12186
|
+
name,
|
|
12187
|
+
token: ctx.dryRun ? await currentTokenOf(login, tokenScopeOf(ctx)) : await freshTokenOf(name, login, ctx)
|
|
12188
|
+
};
|
|
12189
|
+
}));
|
|
12190
|
+
const missing = found.flatMap(({ name, token }) => token === void 0 ? [name] : []);
|
|
12191
|
+
if (missing.length > 0 && ctx.dryRun) throw loginRequired(missing, ctx.rerun());
|
|
12192
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
12193
|
+
for (const { name, token } of found) {
|
|
12194
|
+
if (token !== void 0) {
|
|
12195
|
+
tokens.set(name, token);
|
|
12196
|
+
continue;
|
|
12197
|
+
}
|
|
12198
|
+
const login = declaredLogin(logins, name);
|
|
12199
|
+
const stored = await signIn(name, login, loginFilesOf(ctx.userFile, login), ctx, {
|
|
12200
|
+
waitS,
|
|
12201
|
+
rerun: approvalRerun(ctx)
|
|
12202
|
+
});
|
|
12203
|
+
tokens.set(name, storedToken(stored));
|
|
12204
|
+
}
|
|
12205
|
+
ctx.signal.throwIfAborted();
|
|
12206
|
+
return tokens;
|
|
12207
|
+
}
|
|
12208
|
+
/** The Login a `LOGIN_REJECTED` names in `details.login`, or the Command's only one. */
|
|
12209
|
+
function rejectedName(rejected, tokens) {
|
|
12210
|
+
const { details } = rejected;
|
|
12211
|
+
if (typeof details === "object" && details !== null && "login" in details) return typeof details.login === "string" ? details.login : void 0;
|
|
12212
|
+
const [only, ...rest] = tokens.keys();
|
|
12213
|
+
return rest.length === 0 ? only : void 0;
|
|
12214
|
+
}
|
|
12215
|
+
/**
|
|
12216
|
+
* What a run whose Command threw `error` fails with: a `LOGIN_REJECTED` of a stored Login drops
|
|
12217
|
+
* it for a new Approval, one of a Secret's token fails as the operator's to fix.
|
|
12218
|
+
*/
|
|
12219
|
+
async function afterRejection(error, tokens, logins, ctx) {
|
|
12220
|
+
if (!(error instanceof CliError && error.code === "LOGIN_REJECTED")) return error;
|
|
12221
|
+
const name = rejectedName(error, tokens);
|
|
12222
|
+
const token = name === void 0 ? void 0 : tokens.get(name);
|
|
12223
|
+
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 });
|
|
12224
|
+
const login = declaredLogin(logins, name);
|
|
12225
|
+
if (token.source.kind === "secret" && login.secret !== void 0) return secretRejected(name, login.secret, error);
|
|
12226
|
+
const files = loginFilesOf(ctx.userFile, login);
|
|
12227
|
+
await files.stored.remove();
|
|
12228
|
+
if (ctx.dryRun) return loginRequired([name], ctx.rerun());
|
|
12229
|
+
return await pendingApproval(name, login, files, ctx, approvalRerun(ctx));
|
|
12230
|
+
}
|
|
12231
|
+
/**
|
|
12232
|
+
* Runs `work` with a `ctx` whose `login` gives each named Login's token, signing in first, for at
|
|
12233
|
+
* most `waitS` seconds, where one is missing. A `LOGIN_REJECTED` from `work` drops the stored Login.
|
|
12234
|
+
*/
|
|
12235
|
+
async function withLogins(ctx, names, logins, waitS, work) {
|
|
12236
|
+
const tokens = names.length === 0 ? /* @__PURE__ */ new Map() : await tokensOf(names, logins, ctx, waitS ?? 0);
|
|
12237
|
+
const loggedIn = derived(ctx, { login: (name) => tokens.get(name)?.value ?? undeclaredLogin(name) });
|
|
12238
|
+
try {
|
|
12239
|
+
return await work(loggedIn);
|
|
12240
|
+
} catch (error) {
|
|
12241
|
+
throw await afterRejection(error, tokens, logins, ctx);
|
|
12242
|
+
}
|
|
12243
|
+
}
|
|
12244
|
+
/** `ctx.login` of a name the Command does not declare, which only a cast reaches. */
|
|
12245
|
+
function undeclaredLogin(name) {
|
|
12246
|
+
throw new Error(`ctx.login("${name}") names a Login the Command does not declare`);
|
|
12247
|
+
}
|
|
12248
|
+
//#endregion
|
|
11288
12249
|
//#region ../agent-cli/dist/execute.js
|
|
11289
12250
|
const PLAN_INDENT = " ";
|
|
11290
12251
|
function outcomeOf(value, render) {
|
|
@@ -11329,17 +12290,19 @@ function dryRunOutcome(command, plan, commandLine) {
|
|
|
11329
12290
|
};
|
|
11330
12291
|
}
|
|
11331
12292
|
function serveContextOf(ctx, end) {
|
|
11332
|
-
|
|
11333
|
-
return Object.assign(serveCtx, { end });
|
|
12293
|
+
return derived(ctx, { end });
|
|
11334
12294
|
}
|
|
11335
|
-
async function execute(command, input,
|
|
11336
|
-
await checkTools(command.tools ?? [],
|
|
11337
|
-
const { env, envFile, fileError } =
|
|
12295
|
+
async function execute(command, input, unlogged, run) {
|
|
12296
|
+
await checkTools(command.tools ?? [], unlogged, unlogged.rerun());
|
|
12297
|
+
const { env, envFile, fileError } = unlogged;
|
|
11338
12298
|
checkSecrets(command.secrets ?? [], {
|
|
11339
12299
|
env,
|
|
11340
12300
|
envFile,
|
|
11341
12301
|
fileError
|
|
11342
|
-
},
|
|
12302
|
+
}, unlogged.rerun());
|
|
12303
|
+
return await withLogins(unlogged, command.logins ?? [], run.logins, run.flags.wait, (ctx) => executeLoggedIn(command, input, ctx, run));
|
|
12304
|
+
}
|
|
12305
|
+
async function executeLoggedIn(command, input, ctx, { flags, commandLine, started, outputs: runOutputs }) {
|
|
11343
12306
|
if (command.rows !== void 0) {
|
|
11344
12307
|
const { render } = command;
|
|
11345
12308
|
return outcomeOf(await command.run({
|
|
@@ -11396,6 +12359,66 @@ function manifestAt(packageRoot) {
|
|
|
11396
12359
|
return parsed.success ? parsed.data : NO_MANIFEST;
|
|
11397
12360
|
}
|
|
11398
12361
|
//#endregion
|
|
12362
|
+
//#region ../agent-cli/dist/context/user-file.js
|
|
12363
|
+
const FOLDER_MODE = 448;
|
|
12364
|
+
const FILE_MODE = 384;
|
|
12365
|
+
/** `$XDG_STATE_HOME/<cli>`, with `~/.local/state` when the variable is unset or empty. */
|
|
12366
|
+
function stateFolderOf(cliName, env) {
|
|
12367
|
+
const state = env.XDG_STATE_HOME || join(env.HOME || homedir(), ".local", "state");
|
|
12368
|
+
return join(state, cliName);
|
|
12369
|
+
}
|
|
12370
|
+
function parsed(bytes, schema) {
|
|
12371
|
+
let json;
|
|
12372
|
+
try {
|
|
12373
|
+
json = JSON.parse(new TextDecoder().decode(bytes));
|
|
12374
|
+
} catch {
|
|
12375
|
+
return;
|
|
12376
|
+
}
|
|
12377
|
+
const result = schema.safeParse(json);
|
|
12378
|
+
return result.success ? result.data : void 0;
|
|
12379
|
+
}
|
|
12380
|
+
function userFileOf({ folder, files, fileError }) {
|
|
12381
|
+
return (name, schema) => {
|
|
12382
|
+
const path = join(folder, `${name}.json`);
|
|
12383
|
+
return {
|
|
12384
|
+
read: async () => {
|
|
12385
|
+
let bytes;
|
|
12386
|
+
try {
|
|
12387
|
+
bytes = await files.read(path);
|
|
12388
|
+
} catch (error) {
|
|
12389
|
+
if (isMissingFile(error)) return void 0;
|
|
12390
|
+
throw fileError(error, {
|
|
12391
|
+
path,
|
|
12392
|
+
access: "read"
|
|
12393
|
+
});
|
|
12394
|
+
}
|
|
12395
|
+
return parsed(bytes, schema);
|
|
12396
|
+
},
|
|
12397
|
+
write: async (value) => {
|
|
12398
|
+
try {
|
|
12399
|
+
await files.mkdir(dirname(path), { mode: FOLDER_MODE });
|
|
12400
|
+
await files.write(path, `${JSON.stringify(value)}\n`, { mode: FILE_MODE });
|
|
12401
|
+
} catch (error) {
|
|
12402
|
+
throw fileError(error, {
|
|
12403
|
+
path,
|
|
12404
|
+
access: "write"
|
|
12405
|
+
});
|
|
12406
|
+
}
|
|
12407
|
+
},
|
|
12408
|
+
remove: async () => {
|
|
12409
|
+
try {
|
|
12410
|
+
await files.remove(path);
|
|
12411
|
+
} catch (error) {
|
|
12412
|
+
throw fileError(error, {
|
|
12413
|
+
path,
|
|
12414
|
+
access: "write"
|
|
12415
|
+
});
|
|
12416
|
+
}
|
|
12417
|
+
}
|
|
12418
|
+
};
|
|
12419
|
+
};
|
|
12420
|
+
}
|
|
12421
|
+
//#endregion
|
|
11399
12422
|
//#region ../agent-cli/dist/run-scope.js
|
|
11400
12423
|
function selfOf(name, bin) {
|
|
11401
12424
|
return bin === void 0 ? [name] : [
|
|
@@ -11434,12 +12457,19 @@ function runScopeOf(cli, routed, settings, write, baseInvocation) {
|
|
|
11434
12457
|
env,
|
|
11435
12458
|
platform
|
|
11436
12459
|
});
|
|
11437
|
-
const
|
|
12460
|
+
const files = settings.files ?? realFiles();
|
|
12461
|
+
const outputs = outputsOf(files, {
|
|
11438
12462
|
cwd,
|
|
11439
12463
|
fileError
|
|
11440
12464
|
});
|
|
11441
12465
|
const packageRoot = packageRootOf(settings.bin, cwd);
|
|
11442
12466
|
const progress = (message) => write.stderr(progressLine(message, routed.json));
|
|
12467
|
+
const noticed = /* @__PURE__ */ new Set();
|
|
12468
|
+
const notice = (text) => {
|
|
12469
|
+
if (noticed.has(text)) return;
|
|
12470
|
+
noticed.add(text);
|
|
12471
|
+
write.stderr(noticeLine(text, routed.json));
|
|
12472
|
+
};
|
|
11443
12473
|
const envFile = envFileOf(cli.name, env);
|
|
11444
12474
|
const spawning = {
|
|
11445
12475
|
fileError,
|
|
@@ -11453,8 +12483,15 @@ function runScopeOf(cli, routed, settings, write, baseInvocation) {
|
|
|
11453
12483
|
platform,
|
|
11454
12484
|
outputs,
|
|
11455
12485
|
progress,
|
|
11456
|
-
notice
|
|
12486
|
+
notice,
|
|
11457
12487
|
packageRoot,
|
|
12488
|
+
fetch: settings.fetch ?? ((input, init) => globalThis.fetch(input, init)),
|
|
12489
|
+
userAgent: `${cli.name}/${cli.version}`,
|
|
12490
|
+
userFile: userFileOf({
|
|
12491
|
+
folder: stateFolderOf(cli.name, env),
|
|
12492
|
+
files,
|
|
12493
|
+
fileError
|
|
12494
|
+
}),
|
|
11458
12495
|
ports: portsOf(cli, settings.ports),
|
|
11459
12496
|
portsBase: (signal) => ({
|
|
11460
12497
|
cwd,
|
|
@@ -11488,7 +12525,7 @@ function runScopeOf(cli, routed, settings, write, baseInvocation) {
|
|
|
11488
12525
|
})
|
|
11489
12526
|
};
|
|
11490
12527
|
}
|
|
11491
|
-
function contextOf(scope, control, { command, flags, project, deliver }) {
|
|
12528
|
+
function contextOf(scope, control, { command, flags, project, deliver, latestRelease }) {
|
|
11492
12529
|
const portsBase = scope.portsBase(control.signal);
|
|
11493
12530
|
const ports = once(() => scope.ports(portsBase));
|
|
11494
12531
|
const declared = new Map((command.secrets ?? []).map((declaration) => [declaration.name, declaration]));
|
|
@@ -11502,8 +12539,17 @@ function contextOf(scope, control, { command, flags, project, deliver }) {
|
|
|
11502
12539
|
return {
|
|
11503
12540
|
...portsBase,
|
|
11504
12541
|
secret,
|
|
12542
|
+
login: undeclaredLogin,
|
|
12543
|
+
userFile: scope.userFile,
|
|
11505
12544
|
fail: cliErrorOf,
|
|
11506
12545
|
deadline: (ms, options) => control.deadline(ms, options),
|
|
12546
|
+
fetch: contextFetch({
|
|
12547
|
+
fetch: scope.fetch,
|
|
12548
|
+
userAgent: scope.userAgent,
|
|
12549
|
+
deadline: (ms, options) => control.deadline(ms, options),
|
|
12550
|
+
clock: scope.clock,
|
|
12551
|
+
rerun: scope.rerun()
|
|
12552
|
+
}),
|
|
11507
12553
|
runToEnd: (name, work) => control.runToEnd(name, work),
|
|
11508
12554
|
get ports() {
|
|
11509
12555
|
return ports();
|
|
@@ -11514,7 +12560,8 @@ function contextOf(scope, control, { command, flags, project, deliver }) {
|
|
|
11514
12560
|
rerun: scope.rerun,
|
|
11515
12561
|
poll: (check, { intervalMs, signal: stop }) => poll(portsBase.clock, check, intervalMs, stop === void 0 ? control.signal : AbortSignal.any([control.signal, stop])),
|
|
11516
12562
|
deliver,
|
|
11517
|
-
notice: scope.notice
|
|
12563
|
+
notice: scope.notice,
|
|
12564
|
+
latestRelease
|
|
11518
12565
|
};
|
|
11519
12566
|
}
|
|
11520
12567
|
/** `build` runs on the first call; later calls give its value or throw its error again. */
|
|
@@ -11599,7 +12646,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11599
12646
|
}, {
|
|
11600
12647
|
clock,
|
|
11601
12648
|
failed: (error) => {
|
|
11602
|
-
if (isDebug(baseFormat))
|
|
12649
|
+
if (isDebug(baseFormat)) write.debug(progressLine(`side task failed: ${traceOf(error)}`, routed.json));
|
|
11603
12650
|
}
|
|
11604
12651
|
});
|
|
11605
12652
|
const ran = ({ result, end }, sideTask) => {
|
|
@@ -11639,10 +12686,17 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11639
12686
|
return ran(await failed(error), sideTask);
|
|
11640
12687
|
}
|
|
11641
12688
|
let delivered;
|
|
12689
|
+
const release = installOf(cli).releaseCheck({
|
|
12690
|
+
builtIn,
|
|
12691
|
+
bin: options.bin,
|
|
12692
|
+
node: options.node,
|
|
12693
|
+
platform
|
|
12694
|
+
});
|
|
11642
12695
|
const ctx = contextOf(scope, control, {
|
|
11643
12696
|
command,
|
|
11644
12697
|
flags,
|
|
11645
12698
|
project,
|
|
12699
|
+
latestRelease: (version) => release.latestRelease(version, ctx),
|
|
11646
12700
|
deliver: (data) => {
|
|
11647
12701
|
delivered = () => dataOutcome(command, data, commandLine);
|
|
11648
12702
|
}
|
|
@@ -11658,19 +12712,15 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11658
12712
|
let served = false;
|
|
11659
12713
|
let settled;
|
|
11660
12714
|
try {
|
|
11661
|
-
const outcome = await control.settle(
|
|
11662
|
-
builtIn,
|
|
11663
|
-
bin: options.bin,
|
|
11664
|
-
node: options.node,
|
|
11665
|
-
platform
|
|
11666
|
-
}, ctx).then(() => execute(command, input, ctx, {
|
|
12715
|
+
const outcome = await control.settle(release.notice(ctx).then(() => execute(command, input, ctx, {
|
|
11667
12716
|
flags,
|
|
11668
12717
|
commandLine,
|
|
11669
12718
|
started: async (outcome) => {
|
|
11670
12719
|
write.stdout(await startedOutput(outcome, format, spill));
|
|
11671
12720
|
served = true;
|
|
11672
12721
|
},
|
|
11673
|
-
outputs
|
|
12722
|
+
outputs,
|
|
12723
|
+
logins: cli.logins ?? {}
|
|
11674
12724
|
})));
|
|
11675
12725
|
if (served) return ran(STOPPED, sideTask);
|
|
11676
12726
|
settled = {
|
|
@@ -11681,7 +12731,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11681
12731
|
if (served && control.signal.aborted) return ran(STOPPED, sideTask);
|
|
11682
12732
|
settled = {
|
|
11683
12733
|
ok: false,
|
|
11684
|
-
error,
|
|
12734
|
+
error: await withUpdateLine(error, installOf(cli), ctx),
|
|
11685
12735
|
delivered
|
|
11686
12736
|
};
|
|
11687
12737
|
}
|
|
@@ -11690,9 +12740,6 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11690
12740
|
fields: flags.fields
|
|
11691
12741
|
}, spill), sideTask);
|
|
11692
12742
|
}
|
|
11693
|
-
async function noticesOf(cli, launch, ctx) {
|
|
11694
|
-
await installOf(cli).notice(launch, ctx);
|
|
11695
|
-
}
|
|
11696
12743
|
//#endregion
|
|
11697
12744
|
//#region ../agent-cli/dist/style.js
|
|
11698
12745
|
function colourEnabled(env, stdoutIsTTY) {
|
|
@@ -11739,7 +12786,16 @@ async function whileStopping(signal, onStop, work) {
|
|
|
11739
12786
|
}
|
|
11740
12787
|
/** Runs the CLI, writes its output, then waits for the Side task it started. */
|
|
11741
12788
|
async function runWithProgress(cli, argv, options, write, stopNotice) {
|
|
11742
|
-
|
|
12789
|
+
let resultWritten = false;
|
|
12790
|
+
const gated = (writeTo) => (text) => {
|
|
12791
|
+
if (!resultWritten) writeTo(text);
|
|
12792
|
+
};
|
|
12793
|
+
const { result, finishSideTask } = await routeAndRun(cli, argv, options, {
|
|
12794
|
+
stdout: gated(write.stdout),
|
|
12795
|
+
stderr: gated(write.stderr),
|
|
12796
|
+
debug: write.stderr
|
|
12797
|
+
}, stopNotice);
|
|
12798
|
+
resultWritten = true;
|
|
11743
12799
|
await write.result(result);
|
|
11744
12800
|
await finishSideTask();
|
|
11745
12801
|
return result;
|
|
@@ -11842,7 +12898,7 @@ async function runProcess(cli) {
|
|
|
11842
12898
|
//#region package.json
|
|
11843
12899
|
var package_default = {
|
|
11844
12900
|
name: "@duyquangnvx/create-agent-cli",
|
|
11845
|
-
version: "0.
|
|
12901
|
+
version: "0.31.0",
|
|
11846
12902
|
description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
11847
12903
|
type: "module",
|
|
11848
12904
|
engines: { "node": ">=22.12.0" },
|