@mutmutco/codex-plugin 4.6.1 → 4.6.2
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/cli/dist/main.cjs
CHANGED
|
@@ -189,6 +189,7 @@ async function finishCliRun(watchdogMs = CLI_EXIT_WATCHDOG_MS) {
|
|
|
189
189
|
}, watchdogMs);
|
|
190
190
|
timer.unref?.();
|
|
191
191
|
await closeHttpPool();
|
|
192
|
+
await flushStdio();
|
|
192
193
|
}
|
|
193
194
|
function describeActiveHandles() {
|
|
194
195
|
let summary;
|
|
@@ -555,6 +556,7 @@ async function githubKeyringTokenRead() {
|
|
|
555
556
|
return cachedGhCliTokenRead;
|
|
556
557
|
}
|
|
557
558
|
async function githubTokenRead() {
|
|
559
|
+
if (process.env.MMI_GH_POOL === "app" && process.env.GH_TOKEN) return { state: "ok", token: process.env.GH_TOKEN };
|
|
558
560
|
const mmiPat = process.env.MMI_GH_PAT?.trim();
|
|
559
561
|
if (mmiPat) return { state: "ok", token: mmiPat };
|
|
560
562
|
if (process.env.GH_TOKEN) return { state: "ok", token: process.env.GH_TOKEN };
|
|
@@ -15445,8 +15447,10 @@ async function activateAppActor(commandPath4, env, mint, note = (line) => proces
|
|
|
15445
15447
|
}
|
|
15446
15448
|
return "personal";
|
|
15447
15449
|
}
|
|
15450
|
+
delete env.MMI_GH_PAT;
|
|
15448
15451
|
throw e;
|
|
15449
15452
|
}
|
|
15453
|
+
delete env.MMI_GH_PAT;
|
|
15450
15454
|
env.GH_TOKEN = minted.token;
|
|
15451
15455
|
env.MMI_GH_POOL = "app";
|
|
15452
15456
|
activeQuotaScope = minted.quotaScope;
|
|
@@ -21561,7 +21565,8 @@ async function postIssueComment(client, input) {
|
|
|
21561
21565
|
number: parsed.number,
|
|
21562
21566
|
repo,
|
|
21563
21567
|
url: `https://github.com/${repo}/issues/${parsed.number}`,
|
|
21564
|
-
commentUrl: comment.html_url
|
|
21568
|
+
commentUrl: comment.html_url,
|
|
21569
|
+
...comment.user?.login ? { author: comment.user.login } : {}
|
|
21565
21570
|
};
|
|
21566
21571
|
}
|
|
21567
21572
|
|
|
@@ -22079,19 +22084,19 @@ function evaluateClaim(item, login) {
|
|
|
22079
22084
|
if (others.length && item.status === "In Review") {
|
|
22080
22085
|
return {
|
|
22081
22086
|
ok: false,
|
|
22082
|
-
reason: `${item.ref} is not claimable: In Review and held by @${others.join(", @")} \u2014 ask the holder, or wait for the review to land`
|
|
22087
|
+
reason: `${item.ref} is not claimable: In Review and held by @${others.join(", @")} \u2014 ask the holder, or wait for the review to land (never chain separate claims with && \u2014 a refused claim aborts the chain; pass all refs in one batch call, e.g. \`mmi-cli oracle board claim <ref> <ref>\`)`
|
|
22083
22088
|
};
|
|
22084
22089
|
}
|
|
22085
|
-
if (others.length) return { ok: false, reason: `${item.ref} is already assigned to @${others.join(", @")}` };
|
|
22090
|
+
if (others.length) return { ok: false, reason: `${item.ref} is already assigned to @${others.join(", @")} (never chain separate claims with && \u2014 a refused claim aborts the chain; pass all refs in one batch call, e.g. \`mmi-cli oracle board claim <ref> <ref>\`)` };
|
|
22086
22091
|
if (item.status === "In Progress" && mine) return { ok: true, alreadyClaimed: true };
|
|
22087
22092
|
if (item.status === "Done") {
|
|
22088
22093
|
return {
|
|
22089
22094
|
ok: false,
|
|
22090
|
-
reason: `${item.ref} is not claimable: Status is Done \u2014 claim does not reopen terminal cards; skip completed items and pass only open issues (never chain separate claims with &&)`
|
|
22095
|
+
reason: `${item.ref} is not claimable: Status is Done \u2014 claim does not reopen terminal cards; skip completed items and pass only open issues (never chain separate claims with && \u2014 a refused claim aborts the chain; pass all refs in one batch call, e.g. \`mmi-cli oracle board claim <ref> <ref>\`)`
|
|
22091
22096
|
};
|
|
22092
22097
|
}
|
|
22093
22098
|
if (item.status !== "Todo" && item.status !== "In Progress" && item.status !== "In Review") {
|
|
22094
|
-
return { ok: false, reason: `${item.ref} is not claimable: Status is ${item.status}` };
|
|
22099
|
+
return { ok: false, reason: `${item.ref} is not claimable: Status is ${item.status} (never chain separate claims with && \u2014 a refused claim aborts the chain; pass all refs in one batch call, e.g. \`mmi-cli oracle board claim <ref> <ref>\`)` };
|
|
22095
22100
|
}
|
|
22096
22101
|
return { ok: true, alreadyClaimed: false };
|
|
22097
22102
|
}
|
|
@@ -23600,10 +23605,11 @@ async function claimOneBoardItem(ctx, selector, options) {
|
|
|
23600
23605
|
let staleStamp;
|
|
23601
23606
|
const tookOver = () => ({ outcome: "took-over", holder, previousHolder, ...staleStamp ? { staleStamp } : {} });
|
|
23602
23607
|
const claimedReceipt = () => ({
|
|
23608
|
+
held: true,
|
|
23603
23609
|
...item.status === "In Review" ? { reclaimedFrom: "In Review" } : {},
|
|
23604
23610
|
...previousHolder ? resumeEvidence ? { outcome: "resumed", holder, previousHolder, resumeEvidence } : tookOver() : { outcome: "claimed", holder }
|
|
23605
23611
|
});
|
|
23606
|
-
const heldReceipt = () => previousHolder ? resumeEvidence ? { outcome: "resumed", holder, previousHolder, resumeEvidence } : tookOver() : { outcome: "held", holder };
|
|
23612
|
+
const heldReceipt = () => ({ held: true, ...previousHolder ? resumeEvidence ? { outcome: "resumed", holder, previousHolder, resumeEvidence } : tookOver() : { outcome: "held", holder } });
|
|
23607
23613
|
if (flatItem.contentType !== "Issue") throw new Error(`${flatItem.ref} is not an issue`);
|
|
23608
23614
|
const pre = evaluateClaim(flatItem, assignedLogin);
|
|
23609
23615
|
if (!pre.ok) throw new NotClaimableError(pre.reason, { ref: flatItem.ref, status: flatItem.status });
|
|
@@ -23714,7 +23720,7 @@ async function claimBoardIssues(options, deps = {}) {
|
|
|
23714
23720
|
const ref = `${selector.repo}#${selector.number}`;
|
|
23715
23721
|
try {
|
|
23716
23722
|
const result = await claimOneBoardItem(ctx, selector, { ...options, bulk: true });
|
|
23717
|
-
results[index] = { ref: result.item.ref, claimed: true, item: result.item, status: result.status, partial: result.partial, warning: result.warning, outcome: result.outcome, holder: result.holder, previousHolder: result.previousHolder, resumeEvidence: result.resumeEvidence, staleStamp: result.staleStamp, reclaimedFrom: result.reclaimedFrom, alreadyClaimed: result.alreadyClaimed, renewed: result.renewed, checked: result.checked, credential: result.credential };
|
|
23723
|
+
results[index] = { ref: result.item.ref, claimed: true, item: result.item, status: result.status, partial: result.partial, warning: result.warning, outcome: result.outcome, held: result.held, holder: result.holder, previousHolder: result.previousHolder, resumeEvidence: result.resumeEvidence, staleStamp: result.staleStamp, reclaimedFrom: result.reclaimedFrom, alreadyClaimed: result.alreadyClaimed, renewed: result.renewed, checked: result.checked, credential: result.credential };
|
|
23718
23724
|
} catch (e) {
|
|
23719
23725
|
results[index] = { ref, claimed: false, reason: e.message };
|
|
23720
23726
|
}
|
|
@@ -24816,7 +24822,7 @@ function checkRunbookCoverage(docs, base, git3) {
|
|
|
24816
24822
|
const globs = lines2.slice(heading.start, markerEnd - 1).flatMap((text) => /<!--\s*runbook-covers:\s*(.*?)\s*-->/.exec(text)?.[1].split(",").map((s) => s.trim()) ?? []);
|
|
24817
24823
|
const affected = changed.filter((path2) => globs.some((glob) => matches(glob, path2)) && !changesOnlyUses(path2));
|
|
24818
24824
|
if (affected.length && !ranges.some((r) => r.count > 0 && r.start < end && r.start + r.count > heading.start)) {
|
|
24819
|
-
findings.push({ kind: "runbook-section-stale", doc, line: heading.start, detail: `${heading.title}: changed ${affected.join(", ")}; update this section's text or runbook-covers list, or add a \`Runbook-Unaffected: <reason>\` trailer to a commit message in this PR`, section: { heading: heading.title, level: heading.level, startLine: heading.start, endLine: end - 1, covers: globs }, sources: affected });
|
|
24825
|
+
findings.push({ kind: "runbook-section-stale", doc, line: heading.start, detail: `${heading.title} (lines ${heading.start}\u2013${end - 1}): changed ${affected.join(", ")}; update this section's text (lines ${heading.start}\u2013${end - 1}) or runbook-covers list, or add a \`Runbook-Unaffected: <reason>\` trailer to a commit message in this PR`, section: { heading: heading.title, level: heading.level, startLine: heading.start, endLine: end - 1, covers: globs }, sources: affected });
|
|
24820
24826
|
}
|
|
24821
24827
|
}
|
|
24822
24828
|
}
|
|
@@ -25117,7 +25123,7 @@ function createDocsIndexDeps(repoRoot2) {
|
|
|
25117
25123
|
// src/gate-budget.ts
|
|
25118
25124
|
var BLESSED_RUN_WITH_BUDGET_SHA = "5ce541add569e54e7ca96de67a768577d3d604e4";
|
|
25119
25125
|
var BLESSED_RUNNER_GATE_SHA = "a19e9392e5ad9b0675f02e4304d68dd933fad60f";
|
|
25120
|
-
var BLESSED_KIT_AGENT_FILES_SHA = "
|
|
25126
|
+
var BLESSED_KIT_AGENT_FILES_SHA = "e976df208d891b215844386c1cac0f161946b6fa";
|
|
25121
25127
|
var BLESSED_JEV_FAILURES_SHA = "d5278c6133318b33727483ddfdfddc8f841e3ef6";
|
|
25122
25128
|
var BLESSED_RUNBOOK_GUARD_SHA = "e661e0aedc0e58b73c738bf6cdfeac65e5914584";
|
|
25123
25129
|
var REMOTE_USE = /^mutmutco\/MMI-Hub\/\.github\/actions\/run-with-budget@(\S+)$/;
|
|
@@ -27280,10 +27286,10 @@ var rollout_plan_default = {
|
|
|
27280
27286
|
note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
|
|
27281
27287
|
},
|
|
27282
27288
|
baseline: {
|
|
27283
|
-
version: "4.6.
|
|
27284
|
-
tag: "v4.6.
|
|
27285
|
-
commit: "
|
|
27286
|
-
npm: "@mutmutco/cli@4.6.
|
|
27289
|
+
version: "4.6.2",
|
|
27290
|
+
tag: "v4.6.2",
|
|
27291
|
+
commit: "1708e0fdc55b",
|
|
27292
|
+
npm: "@mutmutco/cli@4.6.2"
|
|
27287
27293
|
},
|
|
27288
27294
|
exitCriterion: "fleet-n-of-n",
|
|
27289
27295
|
hubOnlyShortcut: "forbidden",
|
|
@@ -27300,14 +27306,14 @@ var rollout_plan_default = {
|
|
|
27300
27306
|
repo: "mutmutco/mmi-hub",
|
|
27301
27307
|
role: "canary",
|
|
27302
27308
|
schedule: "train",
|
|
27303
|
-
v3Target: "v4.6.
|
|
27309
|
+
v3Target: "v4.6.2"
|
|
27304
27310
|
}
|
|
27305
27311
|
],
|
|
27306
27312
|
rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
|
|
27307
27313
|
rollback: {
|
|
27308
27314
|
independent: true,
|
|
27309
|
-
mechanism: "npm dist-tag latest -> 4.6.
|
|
27310
|
-
v3Target: "v4.6.
|
|
27315
|
+
mechanism: "npm dist-tag latest -> 4.6.2 and redeploy the Hub Lambda from tag v4.6.2 (1708e0fdc55b); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
27316
|
+
v3Target: "v4.6.2 (@mutmutco/cli@4.6.2, tag commit 1708e0fdc55b \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
27311
27317
|
}
|
|
27312
27318
|
},
|
|
27313
27319
|
{
|
|
@@ -58054,10 +58060,11 @@ function registerCollaborationCommands(program3) {
|
|
|
58054
58060
|
return;
|
|
58055
58061
|
} catch (reRead) {
|
|
58056
58062
|
const reErr = reRead;
|
|
58057
|
-
return
|
|
58063
|
+
return await failGraceful(`issue view: ${repo}#${n} was transferred to ${moved.repo}#${moved.number} (${moved.url}), but re-reading it there failed: ${(reErr.stderr || reErr.message || String(reRead)).trim()}`);
|
|
58058
58064
|
}
|
|
58059
58065
|
}
|
|
58060
|
-
|
|
58066
|
+
const repoHint = /Could not resolve to an issue/i.test(raw) ? ` (if #${n} is in another repository, pass --repo <owner/repo>)` : "";
|
|
58067
|
+
return await failGraceful(`issue view: ${raw}${repoHint}${argvNote}`);
|
|
58061
58068
|
}
|
|
58062
58069
|
});
|
|
58063
58070
|
issue.command("discover-related").description("find related issues for an existing issue and post only high-confidence links").requiredOption("--number <number>", "created issue number").requiredOption("--title <title>", "created issue title").requiredOption("--body <body>", "created issue body").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "print candidates instead of posting").option("--fail-soft", "suppress errors (used by the background auto-caller; direct runs fail loudly)").action(async (o) => {
|
|
@@ -58723,7 +58730,8 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
58723
58730
|
readMergeQueue: readPrMergeQueue,
|
|
58724
58731
|
findMergeGroupRun: findPrMergeGroupRun,
|
|
58725
58732
|
queueProgress: (receipt) => {
|
|
58726
|
-
|
|
58733
|
+
const line = prMergeQueueLine(receipt);
|
|
58734
|
+
if (line) (o.json ? console.warn : printLine)(line);
|
|
58727
58735
|
},
|
|
58728
58736
|
resolveRepo: async (prNumber, repoOpt) => {
|
|
58729
58737
|
const knownRepo = repoOpt ?? await resolveRepo(o.repo).catch(() => void 0) ?? o.repo;
|
|
@@ -64567,6 +64575,7 @@ function parseBrokerJsonFields(raw, allowed) {
|
|
|
64567
64575
|
return value;
|
|
64568
64576
|
}
|
|
64569
64577
|
var BROKER_JSON_FLAGS_NOTE = "gh formatter flags are refused (--template can read child credentials; --web opens a browser) \u2014 use --json with field selection; --jq filters the returned JSON locally with the system jq, never inside gh";
|
|
64578
|
+
var JQ_HELP = "filter the returned JSON locally with the system jq (gh --jq habit; JSON stdout only; jq never sees a credential) (#8966)";
|
|
64570
64579
|
var JQ_MAX_LENGTH = 4e3;
|
|
64571
64580
|
function parseBrokerJq(raw) {
|
|
64572
64581
|
if (!raw.trim() || raw.length > JQ_MAX_LENGTH) {
|
|
@@ -64611,6 +64620,14 @@ function parseBrokerIssueState(raw) {
|
|
|
64611
64620
|
return v;
|
|
64612
64621
|
}
|
|
64613
64622
|
var RUN_LIST_STATUSES = ["queued", "completed", "in_progress", "requested", "waiting", "pending", "action_required", "cancelled", "failure", "neutral", "skipped", "stale", "startup_failure", "success", "timed_out"];
|
|
64623
|
+
function parseBrokerRunEvent(raw) {
|
|
64624
|
+
if (raw === void 0) return void 0;
|
|
64625
|
+
const event = raw.trim();
|
|
64626
|
+
if (!/^[A-Za-z0-9_]{1,64}$/.test(event)) {
|
|
64627
|
+
throw new Error(`read: --event must be a workflow trigger event name like pull_request, merge_group, push, workflow_dispatch (received '${raw}')`);
|
|
64628
|
+
}
|
|
64629
|
+
return event;
|
|
64630
|
+
}
|
|
64614
64631
|
function parseBrokerRunStatus(raw) {
|
|
64615
64632
|
if (raw === void 0) return void 0;
|
|
64616
64633
|
const v = raw.trim().toLowerCase();
|
|
@@ -64706,6 +64723,8 @@ function buildRunListSpec(o) {
|
|
|
64706
64723
|
if (branch) args.push("--branch", branch);
|
|
64707
64724
|
const commit = parseBrokerCommit(o.commit);
|
|
64708
64725
|
if (commit) args.push("--commit", commit);
|
|
64726
|
+
const event = parseBrokerRunEvent(o.event);
|
|
64727
|
+
if (event) args.push("--event", event);
|
|
64709
64728
|
const status = parseBrokerRunStatus(o.status);
|
|
64710
64729
|
if (status) args.push("--status", status);
|
|
64711
64730
|
const created = parseBrokerCreated(o.created);
|
|
@@ -64721,17 +64740,21 @@ function buildRunWatchSpec(id, o) {
|
|
|
64721
64740
|
const timeoutMin = o.timeoutMin !== void 0 ? parseBrokerInt(o.timeoutMin, "--timeout", 1, 120) : 30;
|
|
64722
64741
|
return { program: "gh", args, timeoutMs: timeoutMin * 6e4 };
|
|
64723
64742
|
}
|
|
64724
|
-
function rejectPrViewQueueFields(fields, number) {
|
|
64725
|
-
|
|
64743
|
+
function rejectPrViewQueueFields(fields, number, repo) {
|
|
64744
|
+
const badField = fields?.split(",").find((field) => field === "mergeQueueEntry" || field === "isInMergeQueue");
|
|
64745
|
+
if (!badField) return;
|
|
64746
|
+
const targetRepo2 = repo ?? "<owner/repo>";
|
|
64747
|
+
const remaining = fields?.split(",").filter((f) => f && f !== "mergeQueueEntry" && f !== "isInMergeQueue").join(",") ?? "";
|
|
64726
64748
|
throw new Error(
|
|
64727
|
-
`read pr view: '
|
|
64749
|
+
`read pr view: '${badField}' is not a gh pr view --json field \u2014 read the merge queue with \`mmi-cli devops read pr queue ${number} --repo ${targetRepo2} --json\` and drop it from --json${remaining ? `; for the remaining fields run \`mmi-cli devops read pr view ${number}${repo ? ` --repo ${repo}` : ""} --json ${remaining}\`` : ""}`
|
|
64728
64750
|
);
|
|
64729
64751
|
}
|
|
64730
64752
|
function buildPrViewSpec(ref, o) {
|
|
64731
64753
|
const number = parseBrokerId(ref, "PR number");
|
|
64732
64754
|
const jsonFields = parseBrokerJsonFields(o.json);
|
|
64733
|
-
|
|
64734
|
-
|
|
64755
|
+
const repo = parseBrokerRepo(o.repo, "--repo");
|
|
64756
|
+
rejectPrViewQueueFields(jsonFields, number, repo);
|
|
64757
|
+
const args = withJson(withRepo(["pr", "view", number], repo), jsonFields, o.json);
|
|
64735
64758
|
if (o.comments) args.push("--comments");
|
|
64736
64759
|
return { program: "gh", args, timeoutMs: SINGLE_SHOT_TIMEOUT_MS };
|
|
64737
64760
|
}
|
|
@@ -64807,8 +64830,8 @@ function ghExitCodeOf(error) {
|
|
|
64807
64830
|
function brokerGhFailureHint(failureText, args) {
|
|
64808
64831
|
const target = args[0] === "pr" && args[1] === "view" ? args[2] : void 0;
|
|
64809
64832
|
const unknownField = /Unknown JSON field: "([^"]+)"/.exec(failureText)?.[1] ?? /Unknown JSON field: ([A-Za-z][A-Za-z0-9]*)/.exec(failureText)?.[1];
|
|
64810
|
-
if (unknownField === "mergeQueueEntry") {
|
|
64811
|
-
return `'
|
|
64833
|
+
if (unknownField === "mergeQueueEntry" || unknownField === "isInMergeQueue") {
|
|
64834
|
+
return `'${unknownField}' is not a gh pr view --json field \u2014 read the merge queue with \`mmi-cli devops read pr queue ${target ?? "<number>"} --repo <owner/repo> --json\` and drop it from --json; repeating this read cannot succeed`;
|
|
64812
64835
|
}
|
|
64813
64836
|
if (unknownField) {
|
|
64814
64837
|
return `gh rejected --json field '${unknownField}' \u2014 drop it or use the typed read whose fields cover it (the "Available fields" list gh printed is its own vocabulary)`;
|
|
@@ -64950,7 +64973,6 @@ function registerAppReadBrokerCommands(program3, resolveRepo2, overrides) {
|
|
|
64950
64973
|
const runStatusOpt = (v) => parseBrokerRunStatus(v);
|
|
64951
64974
|
const assigneeOpt = (v) => parseBrokerAssignee(v);
|
|
64952
64975
|
const jqOpt = (v) => parseBrokerJq(v);
|
|
64953
|
-
const JQ_HELP = "filter the returned JSON locally with the system jq (gh --jq habit; needs --json; jq never sees a credential) (#8966)";
|
|
64954
64976
|
const read = program3.command("read").description(`closed typed GitHub reads on the App pool (${BROKER_JSON_FLAGS_NOTE})`);
|
|
64955
64977
|
const run = read.command("run").description("workflow-run reads (view/list/watch pass gh through; log is a bounded App REST read)");
|
|
64956
64978
|
registerRunLogRead(run, overrides?.apiRunner);
|
|
@@ -64964,7 +64986,7 @@ function registerAppReadBrokerCommands(program3, resolveRepo2, overrides) {
|
|
|
64964
64986
|
}
|
|
64965
64987
|
await dispatchGh(spec, "read run view", o.jq);
|
|
64966
64988
|
});
|
|
64967
|
-
run.command("list").description("list workflow runs (workflow/branch/commit/status/created/limit/json; formatters refused)").option("--repo <owner/repo>", "target repo (defaults to the current repo)", repoOpt).option("--json [fields]", "machine-readable output (comma-separated gh fields)", jsonOpt).option("--workflow <name>", "filter by workflow name or file", workflowOpt).option("--branch <name>", "filter by branch", branchOpt).option("--commit <sha>", "filter by the full 40-character head commit SHA (#8937)", (v) => parseBrokerCommit(v)).option("--status <status>", "filter by run status", runStatusOpt).option("--created <filter>", 'only runs created in this window, e.g. ">=2026-10-11T00:30:00Z" (#8096)', (v) => parseBrokerCreated(v)).option("--limit <n>", "maximum runs to fetch (1-100; larger values clamp to 100)", limitOpt).option("--jq <expr>", JQ_HELP, jqOpt).action(async (o) => {
|
|
64989
|
+
run.command("list").description("list workflow runs (workflow/branch/commit/status/event/created/limit/json; formatters refused)").option("--repo <owner/repo>", "target repo (defaults to the current repo)", repoOpt).option("--json [fields]", "machine-readable output (comma-separated gh fields)", jsonOpt).option("--workflow <name>", "filter by workflow name or file", workflowOpt).option("--branch <name>", "filter by branch", branchOpt).option("--commit <sha>", "filter by the full 40-character head commit SHA (#8937)", (v) => parseBrokerCommit(v)).option("--status <status>", "filter by run status", runStatusOpt).option("-e, --event <event>", "filter by workflow trigger event (e.g. pull_request, merge_group, push, workflow_dispatch)", (v) => parseBrokerRunEvent(v)).option("--created <filter>", 'only runs created in this window, e.g. ">=2026-10-11T00:30:00Z" (#8096)', (v) => parseBrokerCreated(v)).option("--limit <n>", "maximum runs to fetch (1-100; larger values clamp to 100)", limitOpt).option("--jq <expr>", JQ_HELP, jqOpt).action(async (o) => {
|
|
64968
64990
|
let spec;
|
|
64969
64991
|
try {
|
|
64970
64992
|
requireJqJson(o.jq, o.json);
|
|
@@ -65173,11 +65195,18 @@ function orgScopedHint(path2) {
|
|
|
65173
65195
|
const surface = runners ? `Runner/lane state has no App-token read: a human token holder answers it with \`gh api orgs/${org}/actions/runners\` (docs/Guides/gh-runner-runbook.md).` : "It needs a human token holder with an org-scoped token, or a dedicated mmi-cli verb when one exists for this resource.";
|
|
65174
65196
|
return ` This route is ORG-SCOPED (\`orgs/${org}/...\`) and is deliberately outside the allowlist \u2014 no App-token read exists for org-level resources, so retrying path variants cannot work and no \`repos/{owner}/{repo}/...\` respelling of it will be accepted. ${surface} A refusal writes nothing to stdout: check the exit code and never merge stderr into the JSON pipe (2>&1).`;
|
|
65175
65197
|
}
|
|
65198
|
+
function tagsReadHint(path2) {
|
|
65199
|
+
const match = new RegExp(`^repos/(${NAME})/(${NAME})/(?:tags|git/(?:refs|matching-refs|tags|commits))(?:[/?#]|$)`).exec(path2);
|
|
65200
|
+
if (!match) return "";
|
|
65201
|
+
const slug = `${match[1]}/${match[2]}`;
|
|
65202
|
+
return ` No App-token REST read exists for git tags or refs \u2014 read git tags directly with \`git ls-remote --tags origin 'refs/tags/*'\` (no token needed; \`git ls-remote origin refs/tags/<tag>\` for one tag), or read tag-backed GitHub Releases with \`MMI_ACTOR=app mmi-cli devops api get "repos/${slug}/releases?per_page=20"\`.`;
|
|
65203
|
+
}
|
|
65176
65204
|
function parseApiGetPath(rawPath) {
|
|
65177
65205
|
const path2 = rawPath.trim();
|
|
65178
65206
|
const refuse = () => {
|
|
65179
65207
|
const hint = /^[A-Za-z]:[/\\]/.test(path2) ? " Git Bash/MSYS may have rewritten a leading-slash argument; retry with MSYS_NO_PATHCONV=1 and the slash-free repos/... path. The endpoint must still be allowlisted." : "";
|
|
65180
|
-
|
|
65208
|
+
const routeHints = `${typedReadHint(path2)}${runsReadHint(path2)}${orgScopedHint(path2)}${tagsReadHint(path2)}${hint}`;
|
|
65209
|
+
return new Error(`api get: refusing '${rawPath}' \u2014${routeHints}${API_GET_RECOVERY} Only these GET reads are allowed (#7219): ${API_GET_ALLOWED_SHAPES}.`);
|
|
65181
65210
|
};
|
|
65182
65211
|
if (/[%#\\]/.test(path2) || path2.split("?")[0].split("/").some((seg) => seg === "." || seg === ".." || seg === "")) throw refuse();
|
|
65183
65212
|
const q = path2.indexOf("?");
|
|
@@ -65225,11 +65254,13 @@ async function readAppApi(path2, runner = apiReadRunner) {
|
|
|
65225
65254
|
}
|
|
65226
65255
|
function registerApiReadCommands(program3, overrides) {
|
|
65227
65256
|
const spawn7 = overrides?.runner ?? apiReadRunner;
|
|
65257
|
+
const jqRun = overrides?.jqRunner ?? systemJqRunner;
|
|
65228
65258
|
const api = program3.command("api").description("bounded GitHub REST reads for automation \u2014 GET only, allowlisted paths (pull files, compare, contents, actions runs/jobs/artifacts, releases)");
|
|
65229
65259
|
api.command("get").description("GET one allowlisted REST path and print the response body to stdout (JSON, or raw file bytes with --accept raw); refuses everything outside the allowlist").option("--accept <type>", "response media type: json (default) or raw (file bytes)", (v) => {
|
|
65230
65260
|
acceptHeaderFor(v);
|
|
65231
65261
|
return v.trim().toLowerCase();
|
|
65232
|
-
}, "json").option("--out <file>", "write response bytes to a file (required for, and only valid with, actions artifact zips, job logs, and release assets)").argument("<path>", "allowlisted GET path (shapes named on refusal)", (v) => parseApiGetPath(v)).action(async (req, o) => {
|
|
65262
|
+
}, "json").option("--jq <expr>", JQ_HELP, (v) => parseBrokerJq(v)).option("--out <file>", "write response bytes to a file (required for, and only valid with, actions artifact zips, job logs, and release assets)").argument("<path>", "allowlisted GET path (shapes named on refusal)", (v) => parseApiGetPath(v)).action(async (req, o) => {
|
|
65263
|
+
if (o.jq !== void 0 && (o.accept === "raw" || o.out !== void 0)) return fail("api get: --jq is only valid with JSON stdout reads (not --accept raw or --out)");
|
|
65233
65264
|
const zip = req.kind === "actions-artifact-zip" || req.kind === "actions-job-logs" || req.kind === "release-asset";
|
|
65234
65265
|
if (zip !== (o.out !== void 0)) return fail(`api get: --out <file> is required for, and only valid with, repos/{owner}/{repo}/actions/artifacts/{id}/zip and repos/{owner}/{repo}/actions/jobs/{id}/logs and repos/{owner}/{repo}/releases/assets/{id}`);
|
|
65235
65266
|
const appRoute = req.kind.startsWith("actions-") || req.kind.startsWith("release-");
|
|
@@ -65242,6 +65273,7 @@ function registerApiReadCommands(program3, overrides) {
|
|
|
65242
65273
|
req.path,
|
|
65243
65274
|
o.accept
|
|
65244
65275
|
);
|
|
65276
|
+
if (o.jq !== void 0) body = await jqRun(o.jq, body, localJqEnv());
|
|
65245
65277
|
if (zip) {
|
|
65246
65278
|
(0, import_node_fs56.writeFileSync)(o.out, body, "latin1");
|
|
65247
65279
|
return;
|
|
@@ -68188,7 +68220,9 @@ function registerDeveloperCommands(program3) {
|
|
|
68188
68220
|
);
|
|
68189
68221
|
return;
|
|
68190
68222
|
}
|
|
68191
|
-
|
|
68223
|
+
const hasStale = result.findings.some((f) => f.kind === "runbook-section-stale" || f.kind === "seed-runbook-unreviewed");
|
|
68224
|
+
const hasRefs = result.findings.some((f) => f.kind !== "runbook-section-stale" && f.kind !== "seed-runbook-unreviewed");
|
|
68225
|
+
console.error(hasStale && !hasRefs ? "doc refs: stale runbook section(s)" : hasStale && hasRefs ? "doc refs: unresolved references and stale runbook section(s)" : "doc refs: unresolved references");
|
|
68192
68226
|
for (const f of result.findings) console.error(`- ${f.doc}:${f.line} [${f.kind}] ${f.detail}`);
|
|
68193
68227
|
process.exitCode = 1;
|
|
68194
68228
|
} catch (e) {
|
package/cli/package.json
CHANGED
package/package.json
CHANGED
|
@@ -151,9 +151,11 @@ Impeccable **v4.7.0 / engine v0.1.16** details:
|
|
|
151
151
|
`NODE_AUTH_TOKEN=$(gh auth token) npm view @mutmutco/mmwork-ds@<v> version --registry https://npm.pkg.github.com`;
|
|
152
152
|
from a root without that line, pass the token directly —
|
|
153
153
|
`npm view @mutmutco/mmwork-ds@<v> version --registry https://npm.pkg.github.com --//npm.pkg.github.com/:_authToken=$(gh auth token)`.
|
|
154
|
-
To see **which** kit versions are actually published,
|
|
155
|
-
|
|
156
|
-
(newest first,
|
|
154
|
+
To see **which** kit versions are actually published, run MMI-Hub's
|
|
155
|
+
`node scripts/kit-adopt.mjs --list-versions [--limit 5] [--json]` (#9426) instead of deriving it: it reads GitHub
|
|
156
|
+
Packages `/orgs/mutmutco/packages/npm/mmwork-ds/versions` (newest first, falling back to `npm view` against
|
|
157
|
+
`https://npm.pkg.github.com` with `GH_PACKAGES_READ_TOKEN`/`NODE_AUTH_TOKEN`), reports the local cache's latest
|
|
158
|
+
version, and never hits the `registry.npmjs.org` 404 of a bare `npm view @mutmutco/mmwork-ds` (#9264). That is the supported listing read:
|
|
157
159
|
`mmi-cli devops packages` covers consumer adoption (`adopt-export`) only, so it never names a published
|
|
158
160
|
version — never carry the packages API as a stopgap.
|
|
159
161
|
- Boot once: `impeccable context --target <path>`; `init` captures PRODUCT.md, `document` maintains
|