@great-arrow/cli 0.1.68 → 0.7.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/dist/index.js +311 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9373,6 +9373,52 @@ function classifyRequestFailure(error62) {
|
|
|
9373
9373
|
}
|
|
9374
9374
|
return { kind, message, hint: FAILURE_HINTS[kind] };
|
|
9375
9375
|
}
|
|
9376
|
+
var DEFAULT_ORIGIN = "https://www.greatarrow.ai";
|
|
9377
|
+
var LEVEL_WORDS = {
|
|
9378
|
+
passkey: "a passkey",
|
|
9379
|
+
verified: "a verified identity"
|
|
9380
|
+
};
|
|
9381
|
+
function actionLink(url2, apiUrl) {
|
|
9382
|
+
try {
|
|
9383
|
+
return new URL(url2, apiUrl || DEFAULT_ORIGIN).toString();
|
|
9384
|
+
} catch {
|
|
9385
|
+
return url2;
|
|
9386
|
+
}
|
|
9387
|
+
}
|
|
9388
|
+
function policyRefusal(body, apiUrl) {
|
|
9389
|
+
let record2 = body;
|
|
9390
|
+
if (typeof body === "string") {
|
|
9391
|
+
try {
|
|
9392
|
+
record2 = JSON.parse(body);
|
|
9393
|
+
} catch {
|
|
9394
|
+
return null;
|
|
9395
|
+
}
|
|
9396
|
+
}
|
|
9397
|
+
if (typeof record2 !== "object" || record2 === null) return null;
|
|
9398
|
+
let r = record2;
|
|
9399
|
+
if (typeof r.error === "object" && r.error !== null) {
|
|
9400
|
+
const data = r.error.data;
|
|
9401
|
+
if (typeof data !== "object" || data === null) return null;
|
|
9402
|
+
r = data;
|
|
9403
|
+
}
|
|
9404
|
+
const url2 = typeof r.actionUrl === "string" && r.actionUrl ? actionLink(r.actionUrl, apiUrl) : null;
|
|
9405
|
+
if (r.error === "identity_assurance_required") {
|
|
9406
|
+
const need = LEVEL_WORDS[typeof r.required === "string" ? r.required : ""] ?? "a stronger sign-in";
|
|
9407
|
+
return {
|
|
9408
|
+
code: r.error,
|
|
9409
|
+
message: `This workspace requires ${need} on your account.`,
|
|
9410
|
+
hint: url2 ? `Open ${url2} to add it, then try again.` : "Add it under Account \u2192 Security, then try again."
|
|
9411
|
+
};
|
|
9412
|
+
}
|
|
9413
|
+
if (r.error === "owner_approval_required") {
|
|
9414
|
+
return {
|
|
9415
|
+
code: r.error,
|
|
9416
|
+
message: typeof r.message === "string" && r.message ? r.message : "Only the workspace owner can do this.",
|
|
9417
|
+
hint: url2 ? `Open ${url2} to continue.` : "Ask the workspace owner to do it, or to change it on the Access page."
|
|
9418
|
+
};
|
|
9419
|
+
}
|
|
9420
|
+
return null;
|
|
9421
|
+
}
|
|
9376
9422
|
var SERVER_CODE = /^[a-z][a-z0-9_]{0,63}$/;
|
|
9377
9423
|
function withServerCode(error62, body) {
|
|
9378
9424
|
try {
|
|
@@ -9437,7 +9483,7 @@ function isNodeError(value) {
|
|
|
9437
9483
|
|
|
9438
9484
|
// src/lib/version.ts
|
|
9439
9485
|
var CLI_PACKAGE_NAME = "@great-arrow/cli";
|
|
9440
|
-
var CLI_VERSION = "0.
|
|
9486
|
+
var CLI_VERSION = "0.7.0";
|
|
9441
9487
|
var GAD_INSTALL_VERSION = "2026.08.08";
|
|
9442
9488
|
var GAD_INSTALL_MIN_VERSION = "2026.06.22";
|
|
9443
9489
|
var GAD_INSTALL_HEADER = "X-GAD-Install-Version";
|
|
@@ -10919,11 +10965,11 @@ var EXIT_SESSION_EXPIRED = 2;
|
|
|
10919
10965
|
var EXIT_NOT_OAUTH = 3;
|
|
10920
10966
|
function authCommand() {
|
|
10921
10967
|
const cmd = new Command("auth").description("Authentication helpers");
|
|
10922
|
-
cmd.command("token").description("Print a fresh OAuth access token, refreshing it if needed").option("--json", "Output {accessToken, accessExpiresAt, apiUrl} as JSON").action(async (options,
|
|
10968
|
+
cmd.command("token").description("Print a fresh OAuth access token, refreshing it if needed").option("--json", "Output {accessToken, accessExpiresAt, apiUrl} as JSON").action(async (options, actionCommand2) => {
|
|
10923
10969
|
try {
|
|
10924
10970
|
const t = await getFreshOAuthAccessToken();
|
|
10925
10971
|
writeCliLocator();
|
|
10926
|
-
const jsonOutput = options.json ||
|
|
10972
|
+
const jsonOutput = options.json || actionCommand2.optsWithGlobals().json;
|
|
10927
10973
|
process.stdout.write(
|
|
10928
10974
|
jsonOutput ? `${JSON.stringify(t)}
|
|
10929
10975
|
` : `${t.accessToken}
|
|
@@ -11056,6 +11102,8 @@ var ERROR_ENVELOPE_KEYS = /* @__PURE__ */ new Set([
|
|
|
11056
11102
|
"details",
|
|
11057
11103
|
"error_description",
|
|
11058
11104
|
"reauth_url",
|
|
11105
|
+
"actionUrl",
|
|
11106
|
+
"achieved",
|
|
11059
11107
|
"required",
|
|
11060
11108
|
"ok",
|
|
11061
11109
|
"success",
|
|
@@ -11073,6 +11121,8 @@ function cleanErrorText(text3) {
|
|
|
11073
11121
|
}
|
|
11074
11122
|
function hintFrom(record2) {
|
|
11075
11123
|
if (nonEmpty(record2.hint)) return record2.hint;
|
|
11124
|
+
if (nonEmpty(record2.actionUrl))
|
|
11125
|
+
return `Open ${actionLink(record2.actionUrl, process.env.GAD_API_URL)} to continue.`;
|
|
11076
11126
|
if (nonEmpty(record2.reauth_url))
|
|
11077
11127
|
return `Re-authorize this connection: ${record2.reauth_url}`;
|
|
11078
11128
|
return void 0;
|
|
@@ -11700,6 +11750,13 @@ var GadClient = class {
|
|
|
11700
11750
|
const status = response.status;
|
|
11701
11751
|
const body = await response.text().catch(() => "");
|
|
11702
11752
|
if (status === 403) {
|
|
11753
|
+
const refusal2 = policyRefusal(body, this.apiUrl);
|
|
11754
|
+
if (refusal2) {
|
|
11755
|
+
throw withServerCode(
|
|
11756
|
+
new CliError(refusal2.message, 1 /* AUTH_ERROR */, refusal2.hint),
|
|
11757
|
+
JSON.stringify({ error: refusal2.code })
|
|
11758
|
+
);
|
|
11759
|
+
}
|
|
11703
11760
|
throw withServerCode(
|
|
11704
11761
|
new CliError(
|
|
11705
11762
|
"Permission denied (HTTP 403).",
|
|
@@ -11792,6 +11849,10 @@ var GadClient = class {
|
|
|
11792
11849
|
message: data.error.message
|
|
11793
11850
|
}
|
|
11794
11851
|
});
|
|
11852
|
+
const refusal2 = policyRefusal(data, this.apiUrl);
|
|
11853
|
+
if (refusal2) {
|
|
11854
|
+
throw new CliError(refusal2.message, 1 /* AUTH_ERROR */, refusal2.hint);
|
|
11855
|
+
}
|
|
11795
11856
|
const code = data.error.code === -32001 ? 1 /* AUTH_ERROR */ : 2 /* NETWORK_ERROR */;
|
|
11796
11857
|
throw new CliError(
|
|
11797
11858
|
data.error.message,
|
|
@@ -40833,9 +40894,9 @@ var ZodString2 = class _ZodString2 extends ZodType2 {
|
|
|
40833
40894
|
}
|
|
40834
40895
|
return { status: status.value, value: input2.data };
|
|
40835
40896
|
}
|
|
40836
|
-
_regex(regex2,
|
|
40897
|
+
_regex(regex2, validation2, message) {
|
|
40837
40898
|
return this.refinement((data) => regex2.test(data), {
|
|
40838
|
-
validation,
|
|
40899
|
+
validation: validation2,
|
|
40839
40900
|
code: ZodIssueCode2.invalid_string,
|
|
40840
40901
|
...errorUtil.errToObj(message)
|
|
40841
40902
|
});
|
|
@@ -50175,6 +50236,248 @@ function callCommand() {
|
|
|
50175
50236
|
});
|
|
50176
50237
|
}
|
|
50177
50238
|
|
|
50239
|
+
// src/commands/board.ts
|
|
50240
|
+
var TOOL = "board_autopilot";
|
|
50241
|
+
function asObject4(json3) {
|
|
50242
|
+
if (!json3.startsWith("{")) return null;
|
|
50243
|
+
try {
|
|
50244
|
+
const data = JSON.parse(json3);
|
|
50245
|
+
return data && typeof data === "object" && !Array.isArray(data) ? data : null;
|
|
50246
|
+
} catch (error62) {
|
|
50247
|
+
void error62;
|
|
50248
|
+
return null;
|
|
50249
|
+
}
|
|
50250
|
+
}
|
|
50251
|
+
function parseToolResult(result) {
|
|
50252
|
+
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
50253
|
+
return { text: "", data: result };
|
|
50254
|
+
}
|
|
50255
|
+
if (typeof result !== "string") return { text: "", data: null };
|
|
50256
|
+
const cut = result.lastIndexOf("\n");
|
|
50257
|
+
const tail = result.slice(cut + 1).trim();
|
|
50258
|
+
const candidates = [
|
|
50259
|
+
[result.slice(0, Math.max(cut, 0)), tail],
|
|
50260
|
+
["", result.trim()]
|
|
50261
|
+
];
|
|
50262
|
+
for (const [text3, json3] of candidates) {
|
|
50263
|
+
const data = asObject4(json3);
|
|
50264
|
+
if (data) return { text: text3, data };
|
|
50265
|
+
}
|
|
50266
|
+
return { text: result, data: null };
|
|
50267
|
+
}
|
|
50268
|
+
function validation(message, hint) {
|
|
50269
|
+
return new CliError(message, 3 /* VALIDATION_ERROR */, hint);
|
|
50270
|
+
}
|
|
50271
|
+
function hostedP90(plan) {
|
|
50272
|
+
const q = plan?.quotes?.find((x) => x.executor === "hosted");
|
|
50273
|
+
return typeof q?.p90Usd === "number" ? q.p90Usd : null;
|
|
50274
|
+
}
|
|
50275
|
+
function buildStartArgs(runId, plan, opts) {
|
|
50276
|
+
if (!opts.healthUrl)
|
|
50277
|
+
throw validation(
|
|
50278
|
+
"--yes needs --health-url <url>.",
|
|
50279
|
+
"The run watches this URL after every merge and rolls back when it fails."
|
|
50280
|
+
);
|
|
50281
|
+
if (!opts.versionUrl)
|
|
50282
|
+
throw validation(
|
|
50283
|
+
"--yes needs --version-url <url>.",
|
|
50284
|
+
"The run reads the serving version here to know a merge reached production."
|
|
50285
|
+
);
|
|
50286
|
+
let cap;
|
|
50287
|
+
if (opts.cap !== void 0) {
|
|
50288
|
+
cap = Number(opts.cap);
|
|
50289
|
+
if (!Number.isFinite(cap) || cap <= 0)
|
|
50290
|
+
throw validation("--cap must be a positive dollar amount.");
|
|
50291
|
+
} else {
|
|
50292
|
+
cap = hostedP90(plan);
|
|
50293
|
+
}
|
|
50294
|
+
if (cap === null)
|
|
50295
|
+
throw validation(
|
|
50296
|
+
"The plan carried no hosted quote to default the cap from.",
|
|
50297
|
+
"Pass --cap <usd>."
|
|
50298
|
+
);
|
|
50299
|
+
if (cap <= 0)
|
|
50300
|
+
throw validation(
|
|
50301
|
+
"The hosted quote is $0, so there is no cap to default to.",
|
|
50302
|
+
"Pass --cap <usd>."
|
|
50303
|
+
);
|
|
50304
|
+
const rejected = new Set(opts.reject ?? []);
|
|
50305
|
+
if (plan?.findings) {
|
|
50306
|
+
const known = new Set(plan.findings.map((f) => f.id));
|
|
50307
|
+
const unknown2 = [...rejected].filter((id) => !known.has(id));
|
|
50308
|
+
if (unknown2.length > 0)
|
|
50309
|
+
throw validation(
|
|
50310
|
+
`--reject: unknown finding id(s): ${unknown2.join(", ")}.`,
|
|
50311
|
+
"Run without --yes to list finding ids."
|
|
50312
|
+
);
|
|
50313
|
+
}
|
|
50314
|
+
const args = {
|
|
50315
|
+
action: "start",
|
|
50316
|
+
run_id: runId,
|
|
50317
|
+
accepted_finding_ids: (plan?.findings ?? []).map((f) => f.id).filter((id) => !rejected.has(id)),
|
|
50318
|
+
cap_usd: cap,
|
|
50319
|
+
notify: { email: opts.email !== false, push: opts.push !== false },
|
|
50320
|
+
health_check_url: opts.healthUrl,
|
|
50321
|
+
version_url: opts.versionUrl
|
|
50322
|
+
};
|
|
50323
|
+
if (opts.types)
|
|
50324
|
+
args.card_types = opts.types.split(",").map((t) => t.trim()).filter(Boolean);
|
|
50325
|
+
return args;
|
|
50326
|
+
}
|
|
50327
|
+
async function callBoard(client2, args) {
|
|
50328
|
+
let result;
|
|
50329
|
+
try {
|
|
50330
|
+
result = await client2.mcpCall("gad_call", gadCallArgs(TOOL, args));
|
|
50331
|
+
} catch (error62) {
|
|
50332
|
+
if (!(error62 instanceof McpToolError)) throw error62;
|
|
50333
|
+
throw validation(`${TOOL}: ${error62.message}`, error62.hint);
|
|
50334
|
+
}
|
|
50335
|
+
const failure2 = callFailure(result);
|
|
50336
|
+
if (failure2) {
|
|
50337
|
+
throw new CliError(
|
|
50338
|
+
`${TOOL}: ${failure2.message}`,
|
|
50339
|
+
failure2.code === "feature_disabled" || failure2.code === "insufficient_scope" ? 1 /* AUTH_ERROR */ : 3 /* VALIDATION_ERROR */
|
|
50340
|
+
);
|
|
50341
|
+
}
|
|
50342
|
+
return result;
|
|
50343
|
+
}
|
|
50344
|
+
function printPlan(fmt, plan) {
|
|
50345
|
+
const row = (r) => r;
|
|
50346
|
+
const f = plan.findings ?? [];
|
|
50347
|
+
const c = plan.cards ?? [];
|
|
50348
|
+
const q = plan.quotes ?? [];
|
|
50349
|
+
process.stdout.write(`
|
|
50350
|
+
Board cleanups (${f.length})
|
|
50351
|
+
`);
|
|
50352
|
+
fmt.table(f.map(row), [
|
|
50353
|
+
{ key: "id", header: "Finding" },
|
|
50354
|
+
{ key: "kind", header: "Kind" },
|
|
50355
|
+
{ key: "todoId", header: "Card" }
|
|
50356
|
+
]);
|
|
50357
|
+
process.stdout.write(`
|
|
50358
|
+
Cards (${c.length})
|
|
50359
|
+
`);
|
|
50360
|
+
fmt.table(
|
|
50361
|
+
c.map((x) => row({ ...x, completable: x.completable ? "yes" : "no" })),
|
|
50362
|
+
[
|
|
50363
|
+
{ key: "title", header: "Title" },
|
|
50364
|
+
{ key: "type", header: "Type" },
|
|
50365
|
+
{ key: "completable", header: "Runs alone" },
|
|
50366
|
+
{ key: "reason", header: "Why" }
|
|
50367
|
+
]
|
|
50368
|
+
);
|
|
50369
|
+
process.stdout.write("\nQuotes (USD)\n");
|
|
50370
|
+
fmt.table(
|
|
50371
|
+
q.map(
|
|
50372
|
+
(x) => row({
|
|
50373
|
+
...x,
|
|
50374
|
+
status: x.available ? "available" : x.comingSoon ? "soon" : "n/a"
|
|
50375
|
+
})
|
|
50376
|
+
),
|
|
50377
|
+
[
|
|
50378
|
+
{ key: "label", header: "Executor" },
|
|
50379
|
+
{ key: "status", header: "Status" },
|
|
50380
|
+
{ key: "p50Usd", header: "p50" },
|
|
50381
|
+
{ key: "p90Usd", header: "p90" }
|
|
50382
|
+
]
|
|
50383
|
+
);
|
|
50384
|
+
}
|
|
50385
|
+
function makeFormatter(command) {
|
|
50386
|
+
const g = globalOutputOpts(command);
|
|
50387
|
+
return {
|
|
50388
|
+
g,
|
|
50389
|
+
fmt: createFormatter({
|
|
50390
|
+
json: g.json,
|
|
50391
|
+
quiet: g.quiet,
|
|
50392
|
+
isTTY: isTTY(),
|
|
50393
|
+
noColor: g.noColor
|
|
50394
|
+
})
|
|
50395
|
+
};
|
|
50396
|
+
}
|
|
50397
|
+
async function makeClient() {
|
|
50398
|
+
const auth = await resolveAuth();
|
|
50399
|
+
return new GadClient({
|
|
50400
|
+
token: auth.token,
|
|
50401
|
+
getToken: auth.getToken,
|
|
50402
|
+
apiUrl: auth.apiUrl
|
|
50403
|
+
});
|
|
50404
|
+
}
|
|
50405
|
+
function emit(result, json3, quiet = false) {
|
|
50406
|
+
const { text: text3, data } = parseToolResult(result);
|
|
50407
|
+
if (json3) {
|
|
50408
|
+
process.stdout.write(
|
|
50409
|
+
`${typeof result === "string" ? result : JSON.stringify(result)}
|
|
50410
|
+
`
|
|
50411
|
+
);
|
|
50412
|
+
} else if (text3 && !quiet) {
|
|
50413
|
+
process.stdout.write(`${text3}
|
|
50414
|
+
`);
|
|
50415
|
+
}
|
|
50416
|
+
return data;
|
|
50417
|
+
}
|
|
50418
|
+
async function runClear(opts, command) {
|
|
50419
|
+
if (opts.yes)
|
|
50420
|
+
buildStartArgs(
|
|
50421
|
+
"pending",
|
|
50422
|
+
{ quotes: [{ executor: "hosted", p90Usd: 1 }] },
|
|
50423
|
+
opts
|
|
50424
|
+
);
|
|
50425
|
+
const { g, fmt } = makeFormatter(command);
|
|
50426
|
+
const client2 = await makeClient();
|
|
50427
|
+
const planResult = await callBoard(client2, {
|
|
50428
|
+
action: "plan",
|
|
50429
|
+
...opts.goal ? { goal_id: opts.goal } : {}
|
|
50430
|
+
});
|
|
50431
|
+
const data = emit(planResult, g.json && !opts.yes, g.quiet);
|
|
50432
|
+
const run4 = data?.run ?? null;
|
|
50433
|
+
if (!g.json && !g.quiet && run4?.plan) printPlan(fmt, run4.plan);
|
|
50434
|
+
if (!opts.yes) {
|
|
50435
|
+
if (!g.json && !g.quiet && run4?.id)
|
|
50436
|
+
process.stdout.write(
|
|
50437
|
+
`
|
|
50438
|
+
Nothing has changed. Start with: gad board clear${opts.goal ? ` --goal ${opts.goal}` : ""} --yes --health-url <url> --version-url <url>
|
|
50439
|
+
`
|
|
50440
|
+
);
|
|
50441
|
+
return;
|
|
50442
|
+
}
|
|
50443
|
+
if (!run4?.id) throw validation("The plan reply carried no run id.");
|
|
50444
|
+
const startResult = await callBoard(
|
|
50445
|
+
client2,
|
|
50446
|
+
buildStartArgs(run4.id, run4.plan, opts)
|
|
50447
|
+
);
|
|
50448
|
+
emit(startResult, g.json, g.quiet);
|
|
50449
|
+
}
|
|
50450
|
+
function actionCommand(action) {
|
|
50451
|
+
return new Command(action).description(`${action} a clear-the-board run`).option("--run <id>", "Run id (status defaults to the latest run)").option("--card <id>", "Undo only this card (undo)").action(async (opts, command) => {
|
|
50452
|
+
if (action !== "status" && !opts.run)
|
|
50453
|
+
throw validation(`${action} needs --run <id>.`);
|
|
50454
|
+
if (opts.card && action !== "undo")
|
|
50455
|
+
throw validation("--card only applies to undo.");
|
|
50456
|
+
const { g } = makeFormatter(command);
|
|
50457
|
+
const client2 = await makeClient();
|
|
50458
|
+
const result = await callBoard(client2, {
|
|
50459
|
+
action,
|
|
50460
|
+
...opts.run ? { run_id: opts.run } : {},
|
|
50461
|
+
...opts.card ? { card_id: opts.card } : {}
|
|
50462
|
+
});
|
|
50463
|
+
emit(result, g.json, g.quiet);
|
|
50464
|
+
});
|
|
50465
|
+
}
|
|
50466
|
+
function boardCommand() {
|
|
50467
|
+
const clear = new Command("clear").description("Preview, start, watch, stop or undo a clear-the-board run").option("--goal <id>", "Limit the run to one Goal").option("--yes", "Start the run after planning").option("--health-url <url>", "URL probed after each merge (needs --yes)").option(
|
|
50468
|
+
"--version-url <url>",
|
|
50469
|
+
"URL that reports the serving sha (needs --yes)"
|
|
50470
|
+
).option("--cap <usd>", "Spend cap in USD (default: hosted p90 quote)").option("--types <list>", "Comma-separated card types, e.g. code").option("--no-email", "Do not email when the run finishes").option("--no-push", "Do not push-notify when the run finishes").option(
|
|
50471
|
+
"--reject <findingId...>",
|
|
50472
|
+
"Finding ids to leave out of the accepted set"
|
|
50473
|
+
).option("--json", "Output the raw tool result");
|
|
50474
|
+
clear.action(runClear);
|
|
50475
|
+
clear.addCommand(actionCommand("status"));
|
|
50476
|
+
clear.addCommand(actionCommand("stop"));
|
|
50477
|
+
clear.addCommand(actionCommand("undo"));
|
|
50478
|
+
return new Command("board").description("Clear the board: tidy cards and run the safe ones").addCommand(clear);
|
|
50479
|
+
}
|
|
50480
|
+
|
|
50178
50481
|
// src/commands/keys.ts
|
|
50179
50482
|
import { createInterface as createInterface3 } from "node:readline";
|
|
50180
50483
|
import { Writable } from "node:stream";
|
|
@@ -55000,8 +55303,8 @@ var program2 = new Command().name("gad").description("Great Arrow Digital CLI \u
|
|
|
55000
55303
|
);
|
|
55001
55304
|
program2.hook(
|
|
55002
55305
|
"preAction",
|
|
55003
|
-
async (_thisCommand,
|
|
55004
|
-
const options =
|
|
55306
|
+
async (_thisCommand, actionCommand2) => {
|
|
55307
|
+
const options = actionCommand2.optsWithGlobals();
|
|
55005
55308
|
const config2 = await loadConfig();
|
|
55006
55309
|
setCommandWorkspaceTarget(options.workspace, config2.default_workspace);
|
|
55007
55310
|
}
|
|
@@ -55021,6 +55324,7 @@ program2.addCommand(todo_default);
|
|
|
55021
55324
|
program2.addCommand(nextCommand());
|
|
55022
55325
|
program2.addCommand(discoverCommand());
|
|
55023
55326
|
program2.addCommand(callCommand());
|
|
55327
|
+
program2.addCommand(boardCommand());
|
|
55024
55328
|
program2.addCommand(claim_default);
|
|
55025
55329
|
program2.addCommand(work_default());
|
|
55026
55330
|
program2.addCommand(worktree_default);
|