@mutmutco/claude-plugin 4.6.22 → 4.6.23

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mmi",
3
3
  "displayName": "MMI",
4
- "version": "4.6.22",
4
+ "version": "4.6.23",
5
5
  "mmiCompat": "4.x",
6
6
  "description": "MMI workflow skills and org gates delivery.",
7
7
  "author": {
package/cli/dist/main.cjs CHANGED
@@ -710,6 +710,10 @@ function createGitHubClient(options = {}) {
710
710
  }
711
711
  return read();
712
712
  },
713
+ async restWithStatus(method, path2, init) {
714
+ const res = await request(method, joinUrl(baseUrl, path2), init);
715
+ return { status: res.status, body: await parseJson(res) };
716
+ },
713
717
  async restPaginate(path2, init) {
714
718
  const items = [];
715
719
  let url = withPerPage(joinUrl(baseUrl, path2));
@@ -1237,6 +1241,10 @@ var init_house_map = __esm({
1237
1241
  // allowlisted GitHub REST GET reads for automation (pull files, compare, contents)
1238
1242
  run: "devops",
1239
1243
  // #9479: one named workflow run — the bounded `run cancel <id>` and `run rerun <id>` writes
1244
+ vars: "devops",
1245
+ // #9780: repository Actions variables — typed get/list/create/update/delete receipts
1246
+ repo: "devops",
1247
+ // #9779: canonical repository typed reads (archive status)
1240
1248
  ci: "devops",
1241
1249
  // the CI/gate audit
1242
1250
  jev: "devops",
@@ -1616,10 +1624,10 @@ var init_cli_shared = __esm({
1616
1624
  );
1617
1625
  const appEnv = options.env ? void 0 : await ghReadAppEnv(file, args) ?? await ghFilingAppEnv(file, args);
1618
1626
  const reportsCredential = !options.env && /(^|[\\/])gh(\.exe)?$/i.test(file) && isAppFilingWrite(args);
1619
- const tag = async (call, credential) => {
1620
- if (!reportsCredential) return call;
1627
+ const tag = async (call3, credential) => {
1628
+ if (!reportsCredential) return call3;
1621
1629
  try {
1622
- return { ...await call, credential };
1630
+ return { ...await call3, credential };
1623
1631
  } catch (e) {
1624
1632
  if (e && typeof e === "object") e.credential = credential;
1625
1633
  throw e;
@@ -15415,6 +15423,15 @@ var APP_WRITE_COMMANDS = /* @__PURE__ */ new Map([
15415
15423
  ["run cancel", { metadata: "read", actions: "write" }],
15416
15424
  // #9587: one named run rerun (all or failed jobs) — same actions:write profile, nothing broader.
15417
15425
  ["run rerun", { metadata: "read", actions: "write" }],
15426
+ // #9780: repository Actions variables — get/list mint only Variables:read; create/update/delete
15427
+ // Variables:write. Repository scope only; no secret store, no rename, no workflow dispatch.
15428
+ ["vars get", { metadata: "read", variables: "read" }],
15429
+ ["vars list", { metadata: "read", variables: "read" }],
15430
+ ["vars create", { metadata: "read", variables: "write" }],
15431
+ ["vars update", { metadata: "read", variables: "write" }],
15432
+ ["vars delete", { metadata: "read", variables: "write" }],
15433
+ // #9779: the archive-status read is a metadata:read-only repository read — nothing broader.
15434
+ ["repo archive-status", { metadata: "read" }],
15418
15435
  ["issue comment", { metadata: "read", issues: "write", pull_requests: "write" }],
15419
15436
  // #9614: closing an issue is an issue-state write — it rides the org App (mmi-github-app), so the close
15420
15437
  // is attributed to the App instead of the personal account. Nothing broader than issues.
@@ -17934,7 +17951,45 @@ function jobReportsPullRequestCheck(job) {
17934
17951
  });
17935
17952
  if (!directIf) return true;
17936
17953
  const expression = directIf.replace(/^\s*if:\s*/, "").trim().replace(/^\$\{\{\s*/, "").replace(/\s*\}\}$/, "").trim();
17937
- return !/^(?:github\.event_name\s*==\s*['"]push['"]|['"]push['"]\s*==\s*github\.event_name)$/.test(expression);
17954
+ if (/pull_request/i.test(expression)) return true;
17955
+ let rest = expression;
17956
+ let valid = true;
17957
+ const take = (pattern) => {
17958
+ const match = rest.match(pattern);
17959
+ if (!match) return void 0;
17960
+ rest = rest.slice(match[0].length).trimStart();
17961
+ return match[0];
17962
+ };
17963
+ const atom = () => {
17964
+ if (take(/^\(/)) {
17965
+ const possible2 = disjunction();
17966
+ if (!take(/^\)/)) valid = false;
17967
+ return possible2;
17968
+ }
17969
+ if (take(/^(?:github\.event_name\s*==\s*'[^']*'|github\.event_name\s*==\s*"[^"]*"|'[^']*'\s*==\s*github\.event_name|"[^"]*"\s*==\s*github\.event_name)/)) return false;
17970
+ if (take(/^always\(\s*\)/)) return true;
17971
+ if (take(/^needs\.[A-Za-z0-9_-]+\.outputs\.[A-Za-z0-9_-]+\s*(?:==|!=)\s*(?:'[^']*'|"[^"]*")/)) return true;
17972
+ valid = false;
17973
+ return true;
17974
+ };
17975
+ const conjunction = () => {
17976
+ let possible2 = atom();
17977
+ while (take(/^&&/)) {
17978
+ const next = atom();
17979
+ possible2 = possible2 && next;
17980
+ }
17981
+ return possible2;
17982
+ };
17983
+ const disjunction = () => {
17984
+ let possible2 = conjunction();
17985
+ while (take(/^\|\|/)) {
17986
+ const next = conjunction();
17987
+ possible2 = possible2 || next;
17988
+ }
17989
+ return possible2;
17990
+ };
17991
+ const possible = disjunction();
17992
+ return !valid || rest.length > 0 || possible;
17938
17993
  }
17939
17994
  function workflowTriggersPullRequest(yaml) {
17940
17995
  const onBlock = extractOnBlock(yaml);
@@ -25428,7 +25483,9 @@ function checkRunbookClaims(root, docs, deps) {
25428
25483
  if (token === "--") break;
25429
25484
  const flag = token.split("=")[0];
25430
25485
  if (/^--[\w-]+$/.test(flag) && flag !== "--help" && !placeholder(flag) && !deps.commandOptions?.get(resolved)?.has(flag)) {
25431
- report("unknown-flag", `mmi-cli ${resolved}: ${flag}`);
25486
+ const known = deps.commandOptions?.get(resolved);
25487
+ const knownFlags = known?.size ? ` (known flags: ${[...known].sort().join(" ")})` : "";
25488
+ report("unknown-flag", `mmi-cli ${resolved}: ${flag}${knownFlags} (source: ${deps.commandSource ?? "unspecified"})`);
25432
25489
  }
25433
25490
  }
25434
25491
  } else if (cmd === "npm") {
@@ -25667,7 +25724,7 @@ function runDocRefs(root, deps = {}) {
25667
25724
  findings.push(...checkCommands(docs, commandPaths).findings);
25668
25725
  }
25669
25726
  const runbooks = Object.fromEntries(Object.entries(docs).filter(([doc, body]) => isRunbook(doc, body)));
25670
- findings.push(...checkRunbookClaims(root, runbooks, { readFile: readFile10, exists, isIgnored, trackedFirstSegments, commandPaths, commandOptions: deps.commandOptions }));
25727
+ findings.push(...checkRunbookClaims(root, runbooks, { readFile: readFile10, exists, isIgnored, trackedFirstSegments, commandPaths, commandOptions: deps.commandOptions, commandSource: deps.commandSource }));
25671
25728
  const warnings = [...refResult.warnings];
25672
25729
  if (deps.runbooksOnly && !Object.keys(runbooks).length) {
25673
25730
  warnings.push({ kind: "runbook-missing", doc: "docs/Guides/operator.md", line: 0, detail: "no operator runbook or <!-- runbook --> document exists" });
@@ -28089,10 +28146,10 @@ var rollout_plan_default = {
28089
28146
  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)."
28090
28147
  },
28091
28148
  baseline: {
28092
- version: "4.6.22",
28093
- tag: "v4.6.22",
28094
- commit: "bc3629b50994",
28095
- npm: "@mutmutco/cli@4.6.22"
28149
+ version: "4.6.23",
28150
+ tag: "v4.6.23",
28151
+ commit: "27b8f434196b",
28152
+ npm: "@mutmutco/cli@4.6.23"
28096
28153
  },
28097
28154
  exitCriterion: "fleet-n-of-n",
28098
28155
  hubOnlyShortcut: "forbidden",
@@ -28109,14 +28166,14 @@ var rollout_plan_default = {
28109
28166
  repo: "mutmutco/mmi-hub",
28110
28167
  role: "canary",
28111
28168
  schedule: "train",
28112
- v3Target: "v4.6.22"
28169
+ v3Target: "v4.6.23"
28113
28170
  }
28114
28171
  ],
28115
28172
  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.",
28116
28173
  rollback: {
28117
28174
  independent: true,
28118
- mechanism: "npm dist-tag latest -> 4.6.22 and redeploy the Hub Lambda from tag v4.6.22 (bc3629b50994); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
28119
- v3Target: "v4.6.22 (@mutmutco/cli@4.6.22, tag commit bc3629b50994 \u2014 last known-good release carrying the repo-index v4-only contract)"
28175
+ mechanism: "npm dist-tag latest -> 4.6.23 and redeploy the Hub Lambda from tag v4.6.23 (27b8f434196b); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
28176
+ v3Target: "v4.6.23 (@mutmutco/cli@4.6.23, tag commit 27b8f434196b \u2014 last known-good release carrying the repo-index v4-only contract)"
28120
28177
  }
28121
28178
  },
28122
28179
  {
@@ -41185,7 +41242,7 @@ var PRIMARY_GROUPS = [
41185
41242
  ["Setup and support", ["bootstrap", "secrets", "docs", "tests", "spawn", "dist"]],
41186
41243
  ["Coordinate and improve", ["wave", "report", "skill-lesson", "closure-rate"]]
41187
41244
  ];
41188
- var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set(["oauth", "org", "runtime", "plugin", "packages", "github", "archive-prune", "intercom", "jev"]);
41245
+ var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set(["oauth", "org", "runtime", "plugin", "packages", "github", "archive-prune", "intercom", "jev", "vars", "repo"]);
41189
41246
  var CAMPAIGN_ORCHESTRATOR_TOP_LEVEL = /* @__PURE__ */ new Set(["pickup", "read", "run"]);
41190
41247
  var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "tests", "spawn", "dist", "wave", "report", "skill-lesson", "closure-rate"]);
41191
41248
  var TOP_LEVEL_ORDER = /* @__PURE__ */ new Map();
@@ -41234,6 +41291,10 @@ var COMMAND_OWNERSHIP = {
41234
41291
  read: { module_owner: "cli/src/app-read-broker.ts", consumer: "agent-workflow" },
41235
41292
  run: { module_owner: "cli/src/merge-group-orphans.ts", consumer: "agent-workflow" },
41236
41293
  // #9479
41294
+ vars: { module_owner: "cli/src/devops-vars.ts", consumer: "agent-workflow" },
41295
+ // #9780 repository Actions variables
41296
+ repo: { module_owner: "cli/src/repo-archive-status.ts", consumer: "agent-workflow" },
41297
+ // #9779 archive-status read
41237
41298
  ci: { module_owner: "cli/src/ci-audit.ts", consumer: "release-operator" },
41238
41299
  rcand: { module_owner: "cli/src/train-commands.ts", consumer: "release-operator" },
41239
41300
  release: { module_owner: "cli/src/train-commands.ts", consumer: "release-operator" },
@@ -44436,6 +44497,573 @@ function registerRunRerun(run, writeToken, projects) {
44436
44497
  });
44437
44498
  }
44438
44499
 
44500
+ // src/devops-vars.ts
44501
+ var import_node_crypto13 = require("node:crypto");
44502
+ init_github_client();
44503
+ init_cli_shared();
44504
+ var VARS_RECEIPT_SCHEMA = "mmi-cli.devops-vars/1";
44505
+ function parseVarsName(raw) {
44506
+ const name = raw.trim();
44507
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || name.length > 256) {
44508
+ throw new Error(`<name> must match [A-Za-z_][A-Za-z0-9_]* (\u2264256 chars) \u2014 received '${raw.slice(0, 64)}'`);
44509
+ }
44510
+ return name;
44511
+ }
44512
+ function parseVarsValue(raw) {
44513
+ if (typeof raw !== "string") throw new Error(`--value must be a string \u2014 received ${raw === null ? "null" : typeof raw}; values are preserved exactly, never coerced`);
44514
+ if (raw.length > 49152) throw new Error(`--value exceeds GitHub's 48KB variable size limit (${raw.length} chars)`);
44515
+ return raw;
44516
+ }
44517
+ function resolveCanonicalRepo(raw, projects) {
44518
+ if (!Array.isArray(projects)) return { kind: "unavailable" };
44519
+ const input = raw.trim();
44520
+ if (!/^[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)?$/.test(input) || input.split("/").some((part) => part === "." || part === "..")) {
44521
+ return { kind: "unregistered" };
44522
+ }
44523
+ const key = registryRepoKey(qualifyRegistryRepo(input));
44524
+ const byKey = /* @__PURE__ */ new Map();
44525
+ const aliasRepos = [];
44526
+ const lower = input.toLowerCase();
44527
+ for (const project2 of projects) {
44528
+ if (!project2 || typeof project2 !== "object" || Array.isArray(project2)) continue;
44529
+ if (!input.includes("/")) {
44530
+ const alias = typeof project2.slug === "string" ? project2.slug : typeof project2.name === "string" ? project2.name : void 0;
44531
+ if (typeof alias === "string" && alias.trim() !== "" && alias.toLowerCase() === lower && Array.isArray(project2.repos)) {
44532
+ for (const r of project2.repos) if (typeof r === "string" && r.trim() !== "") aliasRepos.push(qualifyRegistryRepo(r.trim()));
44533
+ }
44534
+ }
44535
+ if (!Array.isArray(project2.repos)) continue;
44536
+ for (const r of project2.repos) {
44537
+ if (typeof r !== "string" || r.trim() === "") continue;
44538
+ const qualified = qualifyRegistryRepo(r.trim());
44539
+ if (registryRepoKey(qualified) === key) byKey.set(key, byKey.get(key) ?? qualified);
44540
+ }
44541
+ }
44542
+ const distinct = /* @__PURE__ */ new Map();
44543
+ if (byKey.has(key)) distinct.set(key, byKey.get(key));
44544
+ for (const r of aliasRepos) {
44545
+ const rk = registryRepoKey(r);
44546
+ if (!distinct.has(rk)) distinct.set(rk, r);
44547
+ }
44548
+ const hits = [...distinct.entries()];
44549
+ if (hits.length === 1) {
44550
+ const [hitKey, repo] = hits[0];
44551
+ return { kind: "resolved", repo, via: hitKey === key ? "repo-name" : "project-alias" };
44552
+ }
44553
+ if (hits.length > 1) return { kind: "conflict", candidates: hits.map(([, repo]) => repo) };
44554
+ return { kind: "unregistered" };
44555
+ }
44556
+ function classifyUpstreamError(e) {
44557
+ if (e instanceof GitHubApiError) {
44558
+ if (e.status === 404) return { status: 404, reason: "bare-404" };
44559
+ if (e.status === 403 || e.status === 401) return { status: e.status, reason: "denied" };
44560
+ return { status: e.status, reason: "transport-error" };
44561
+ }
44562
+ const name = e?.name ?? "";
44563
+ if (name === "TimeoutError" || name === "AbortError" || /timeout|abort/i.test(String(e?.message ?? ""))) {
44564
+ return { status: 0, reason: "timeout" };
44565
+ }
44566
+ return { status: 0, reason: "transport-error" };
44567
+ }
44568
+ async function call(deps, log, method, path2, body) {
44569
+ try {
44570
+ const res = await deps.transport.restWithStatus(method, path2, body !== void 0 ? { body } : void 0);
44571
+ log.push({ method, path: path2, status: res.status });
44572
+ return res;
44573
+ } catch (e) {
44574
+ log.push({ method, path: path2, status: classifyUpstreamError(e).status });
44575
+ throw e;
44576
+ }
44577
+ }
44578
+ function beginOp() {
44579
+ return { startedAt: /* @__PURE__ */ new Date(), log: [] };
44580
+ }
44581
+ function receiptBase(deps, operation, repo, ctx, now) {
44582
+ return {
44583
+ schema: VARS_RECEIPT_SCHEMA,
44584
+ operation,
44585
+ repo,
44586
+ window: { startedAt: ctx.startedAt.toISOString(), endedAt: now().toISOString(), atomicSnapshot: false },
44587
+ actor: deps.actor ?? { credential: "app_installation" },
44588
+ correlationId: (deps.correlationId ?? import_node_crypto13.randomUUID)(),
44589
+ upstream: ctx.log
44590
+ };
44591
+ }
44592
+ var VARS_PER_PAGE = 100;
44593
+ var VARS_MAX_PAGES = 100;
44594
+ function parseVariableEntry(raw) {
44595
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("variable entry is not an object");
44596
+ const e = raw;
44597
+ if (typeof e.name !== "string" || e.name === "") throw new Error("variable entry has no name");
44598
+ if (e.value !== void 0 && typeof e.value !== "string") throw new Error(`variable '${e.name}' value is ${typeof e.value}, not a string \u2014 refusing to coerce`);
44599
+ if (e.created_at !== void 0 && typeof e.created_at !== "string") throw new Error(`variable '${e.name}' created_at is not a string`);
44600
+ if (e.updated_at !== void 0 && typeof e.updated_at !== "string") throw new Error(`variable '${e.name}' updated_at is not a string`);
44601
+ return {
44602
+ name: e.name,
44603
+ ...e.value !== void 0 ? { value: e.value } : {},
44604
+ ...e.created_at !== void 0 ? { createdAt: e.created_at } : {},
44605
+ ...e.updated_at !== void 0 ? { updatedAt: e.updated_at } : {}
44606
+ };
44607
+ }
44608
+ async function enumerateRepositoryVariables(deps, repo, log = []) {
44609
+ const entries = [];
44610
+ let total = -1;
44611
+ for (let page = 1; page <= VARS_MAX_PAGES; page++) {
44612
+ const { body } = await call(deps, log, "GET", `repos/${repo}/actions/variables?per_page=${VARS_PER_PAGE}&page=${page}`);
44613
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
44614
+ return { complete: false, reason: "malformed-page", entries, pages: page, detail: `page ${page} body is not a JSON object` };
44615
+ }
44616
+ const envelope = body;
44617
+ if (typeof envelope.total_count !== "number" || !Number.isInteger(envelope.total_count) || envelope.total_count < 0 || !Array.isArray(envelope.variables)) {
44618
+ return { complete: false, reason: "malformed-page", entries, pages: page, detail: `page ${page} envelope has no integer total_count / variables array` };
44619
+ }
44620
+ if (total >= 0 && envelope.total_count !== total) {
44621
+ return { complete: false, reason: "incomplete-pagination", entries, pages: page, detail: `total_count moved between pages (${total} \u2192 ${envelope.total_count}); the enumeration is not a snapshot` };
44622
+ }
44623
+ total = envelope.total_count;
44624
+ try {
44625
+ for (const raw of envelope.variables) entries.push(parseVariableEntry(raw));
44626
+ } catch (e) {
44627
+ return { complete: false, reason: "malformed-page", entries, pages: page, detail: `page ${page}: ${e.message}` };
44628
+ }
44629
+ const expectedPages = Math.max(1, Math.ceil(total / VARS_PER_PAGE));
44630
+ if (expectedPages > VARS_MAX_PAGES) {
44631
+ return { complete: false, reason: "page-bound-exceeded", entries, pages: page, detail: `total_count ${total} needs ${expectedPages} pages; the bounded read stops at ${VARS_MAX_PAGES}` };
44632
+ }
44633
+ if (page >= expectedPages) break;
44634
+ }
44635
+ if (entries.length !== total) {
44636
+ return { complete: false, reason: "incomplete-pagination", entries, pages: Math.max(1, Math.ceil(total / VARS_PER_PAGE)), detail: `accumulated ${entries.length} entries but total_count is ${total}` };
44637
+ }
44638
+ return { complete: true, entries, totalVariables: total, pages: Math.max(1, Math.ceil(total / VARS_PER_PAGE)) };
44639
+ }
44640
+ function findEntry(entries, name) {
44641
+ const lower = name.toLowerCase();
44642
+ return entries.find((e) => e.name.toLowerCase() === lower);
44643
+ }
44644
+ async function readVariable(deps, repo, name, log) {
44645
+ let body;
44646
+ try {
44647
+ ({ body } = await call(deps, log, "GET", `repos/${repo}/actions/variables/${name}`));
44648
+ } catch (e) {
44649
+ const { status, reason } = classifyUpstreamError(e);
44650
+ if (reason === "denied") return { presence: "unknown", unknownReason: "denied", detail: `HTTP ${status}: the App installation cannot read repository variables`, capability: { permission: "variables:read", outcome: "denied" } };
44651
+ if (reason !== "bare-404") return { presence: "unknown", unknownReason: reason, detail: String(e.message).slice(0, 200) };
44652
+ try {
44653
+ const en = await enumerateRepositoryVariables(deps, repo, log);
44654
+ if (!en.complete) return { presence: "unknown", unknownReason: en.reason, detail: `bare 404 on GET, and the absence proof failed: ${en.detail}` };
44655
+ const hit = findEntry(en.entries, name);
44656
+ if (hit) return { presence: "present", value: hit.value, observed: { ...hit.createdAt ? { createdAt: hit.createdAt } : {}, ...hit.updatedAt ? { updatedAt: hit.updatedAt } : {}, source: "enumeration" } };
44657
+ return { presence: "absent", absenceProof: { provenBy: "complete-enumeration", pages: en.pages, perPage: VARS_PER_PAGE, totalVariables: en.totalVariables, nameExcluded: true } };
44658
+ } catch (proofError) {
44659
+ const proof = classifyUpstreamError(proofError);
44660
+ return { presence: "unknown", unknownReason: proof.reason === "bare-404" ? "transport-error" : proof.reason, detail: `bare 404 on GET, and the absence proof failed: ${String(proofError.message).slice(0, 160)}` };
44661
+ }
44662
+ }
44663
+ try {
44664
+ const entry = parseVariableEntry(body);
44665
+ return { presence: "present", value: entry.value, observed: { ...entry.createdAt ? { createdAt: entry.createdAt } : {}, ...entry.updatedAt ? { updatedAt: entry.updatedAt } : {}, source: "get" } };
44666
+ } catch (e) {
44667
+ return { presence: "unknown", unknownReason: "parser-refused", detail: `HTTP 200 but the body could not be parsed as a variable: ${e.message}` };
44668
+ }
44669
+ }
44670
+ async function getVariableReceipt(deps, repo, name) {
44671
+ const ctx = beginOp();
44672
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
44673
+ const read = await readVariable(deps, repo, name, ctx.log);
44674
+ return {
44675
+ ...receiptBase(deps, "get", repo, ctx, now),
44676
+ name,
44677
+ presence: read.presence,
44678
+ ...read.value !== void 0 ? { value: read.value } : {},
44679
+ ...read.observed ? { observed: read.observed } : {},
44680
+ ...read.absenceProof ? { absenceProof: read.absenceProof } : {},
44681
+ ...read.unknownReason ? { unknownReason: read.unknownReason } : {},
44682
+ ...read.capability ? { capability: read.capability } : {}
44683
+ };
44684
+ }
44685
+ async function listVariablesReceipt(deps, repo) {
44686
+ const ctx = beginOp();
44687
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
44688
+ try {
44689
+ const en = await enumerateRepositoryVariables(deps, repo, ctx.log);
44690
+ if (en.complete) {
44691
+ return {
44692
+ ...receiptBase(deps, "list", repo, ctx, now),
44693
+ pagination: { pages: en.pages, perPage: VARS_PER_PAGE, complete: true, totalVariables: en.totalVariables },
44694
+ variables: en.entries.map(({ name, createdAt, updatedAt }) => ({ name, ...createdAt ? { createdAt } : {}, ...updatedAt ? { updatedAt } : {} }))
44695
+ };
44696
+ }
44697
+ return {
44698
+ ...receiptBase(deps, "list", repo, ctx, now),
44699
+ pagination: { pages: en.pages, perPage: VARS_PER_PAGE, complete: false, reason: en.reason, detail: en.detail },
44700
+ variables: en.entries.map(({ name, createdAt, updatedAt }) => ({ name, ...createdAt ? { createdAt } : {}, ...updatedAt ? { updatedAt } : {} })),
44701
+ unknownReason: en.reason
44702
+ };
44703
+ } catch (e) {
44704
+ const { status, reason } = classifyUpstreamError(e);
44705
+ return {
44706
+ ...receiptBase(deps, "list", repo, ctx, now),
44707
+ pagination: { pages: 0, perPage: VARS_PER_PAGE, complete: false, reason },
44708
+ variables: [],
44709
+ unknownReason: reason,
44710
+ ...reason === "denied" ? { capability: { permission: "variables:read", outcome: "denied" }, pagination: { pages: 0, perPage: VARS_PER_PAGE, complete: false, reason, detail: `HTTP ${status}: the App installation cannot list repository variables` } } : {}
44711
+ };
44712
+ }
44713
+ }
44714
+ async function readBaseline(deps, repo, name, log) {
44715
+ const read = await readVariable(deps, repo, name, log);
44716
+ if (read.presence === "present") return { presence: "present", value: read.value };
44717
+ if (read.presence === "absent") return { presence: "absent" };
44718
+ return { presence: "unknown", unknownReason: read.unknownReason, ...read.capability ? { capability: read.capability } : {} };
44719
+ }
44720
+ function rejectedWrite(base, baseline, reason, capability) {
44721
+ return {
44722
+ ...base,
44723
+ outcome: "rejected",
44724
+ casLimitation: "no-atomic-compare-and-swap",
44725
+ baseline: { presence: "present", value: baseline.value, source: "get" },
44726
+ reason,
44727
+ ...capability ? { capability } : {}
44728
+ };
44729
+ }
44730
+ function unknownWrite(base, baseline, reason, readback) {
44731
+ return {
44732
+ ...base,
44733
+ outcome: "outcome-unknown",
44734
+ casLimitation: "no-atomic-compare-and-swap",
44735
+ baseline: baseline.presence === "present" ? { presence: "present", value: baseline.value, source: "get" } : baseline,
44736
+ ...readback ? { readback } : {},
44737
+ reason
44738
+ };
44739
+ }
44740
+ async function createVariableReceipt(deps, repo, name, value) {
44741
+ const ctx = beginOp();
44742
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
44743
+ let en;
44744
+ try {
44745
+ en = await enumerateRepositoryVariables(deps, repo, ctx.log);
44746
+ } catch (e) {
44747
+ const { status, reason } = classifyUpstreamError(e);
44748
+ return {
44749
+ ...receiptBase(deps, "create", repo, ctx, now),
44750
+ operation: "create",
44751
+ name,
44752
+ outcome: "not-dispatched",
44753
+ casLimitation: "no-atomic-compare-and-swap",
44754
+ baseline: { presence: "unknown", source: "complete-enumeration" },
44755
+ reason: `no observed absent baseline: ${reason === "denied" ? `HTTP ${status}: the App installation cannot list repository variables` : String(e.message).slice(0, 160)}`,
44756
+ ...reason === "denied" ? { capability: { permission: "variables:read", outcome: "denied" } } : {}
44757
+ };
44758
+ }
44759
+ const base = { ...receiptBase(deps, "create", repo, ctx, now), operation: "create", name, casLimitation: "no-atomic-compare-and-swap" };
44760
+ if (!en.complete) {
44761
+ return { ...base, outcome: "not-dispatched", baseline: { presence: "unknown", source: "complete-enumeration" }, reason: `no observed absent baseline: ${en.reason} (${en.detail})` };
44762
+ }
44763
+ const existing = findEntry(en.entries, name);
44764
+ if (existing) {
44765
+ return { ...base, outcome: "not-dispatched", baseline: { presence: "present", ...existing.value !== void 0 ? { value: existing.value } : {}, source: "complete-enumeration" }, reason: "variable already present \u2014 create only dispatches against an observed absent baseline (use update)" };
44766
+ }
44767
+ try {
44768
+ await call(deps, ctx.log, "POST", `repos/${repo}/actions/variables`, { name, value });
44769
+ } catch (e) {
44770
+ const { status, reason } = classifyUpstreamError(e);
44771
+ const absentBaseline = { presence: "absent", source: "complete-enumeration" };
44772
+ if (reason === "denied") return { ...base, outcome: "rejected", baseline: absentBaseline, reason: `HTTP ${status}: the App installation lacks repository Variables write`, capability: { permission: "variables:write", outcome: "denied" } };
44773
+ if (status === 404) return { ...base, outcome: "rejected", baseline: absentBaseline, reason: "HTTP 404: repository missing or not visible to the installation" };
44774
+ if (status === 409 || status === 422) return { ...base, outcome: "rejected", baseline: absentBaseline, reason: `HTTP ${status}: ${String(e.message).slice(0, 160)}` };
44775
+ return unknownWrite({ ...base, schema: VARS_RECEIPT_SCHEMA, operation: "create", repo }, { presence: "absent", source: "complete-enumeration" }, `${reason === "timeout" ? "dispatch timed out" : `HTTP ${status || "no response"}`}; the create MAY have applied \u2014 reconcile read-only, never replay`);
44776
+ }
44777
+ const rb = await readVariable(deps, repo, name, ctx.log);
44778
+ if (rb.presence === "present") {
44779
+ if (rb.value === value) {
44780
+ return { ...base, outcome: "verified", baseline: { presence: "absent", source: "complete-enumeration" }, readback: { presence: "present", value: rb.value } };
44781
+ }
44782
+ return unknownWrite(base, { presence: "absent", source: "complete-enumeration" }, "read-back shows a different value than dispatched \u2014 a concurrent outside write is possible; reconcile read-only", { presence: "present", value: rb.value });
44783
+ }
44784
+ if (rb.presence === "absent") {
44785
+ return unknownWrite(base, { presence: "absent", source: "complete-enumeration" }, "accepted but the read-back proves absence \u2014 reconcile read-only before any retry", { presence: "absent" });
44786
+ }
44787
+ return {
44788
+ ...base,
44789
+ outcome: "accepted-but-unverified",
44790
+ baseline: { presence: "absent", source: "complete-enumeration" },
44791
+ readback: { presence: "unknown" },
44792
+ reason: `create accepted but the read-back could not verify: ${rb.unknownReason ?? "unknown"}`
44793
+ };
44794
+ }
44795
+ async function updateVariableReceipt(deps, repo, name, value) {
44796
+ const ctx = beginOp();
44797
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
44798
+ const base = { ...receiptBase(deps, "update", repo, ctx, now), operation: "update", name, casLimitation: "no-atomic-compare-and-swap" };
44799
+ const baseline = await readBaseline(deps, repo, name, ctx.log);
44800
+ if (baseline.presence !== "present") {
44801
+ return {
44802
+ ...base,
44803
+ outcome: "not-dispatched",
44804
+ baseline: { presence: baseline.presence, ...baseline.value !== void 0 ? { value: baseline.value } : {}, source: "get" },
44805
+ reason: baseline.presence === "absent" ? "no present baseline \u2014 the enumeration proved absence (use create)" : `no retained present baseline (${baseline.unknownReason ?? "unknown"}); a bare 404 is never a present baseline`,
44806
+ ...baseline.capability ? { capability: baseline.capability } : {}
44807
+ };
44808
+ }
44809
+ const live = await readVariable(deps, repo, name, ctx.log);
44810
+ if (live.presence !== "present" || live.value !== baseline.value) {
44811
+ return {
44812
+ ...base,
44813
+ outcome: "not-dispatched",
44814
+ baseline: { presence: "present", value: baseline.value, source: "get" },
44815
+ ...live.presence === "present" ? { readback: { presence: "present", value: live.value } } : {},
44816
+ reason: live.presence === "present" ? "concurrent change: the live value no longer matches the retained baseline \u2014 stopped for reconciliation" : `baseline unconfirmed before dispatch (${live.unknownReason ?? "unknown"}) \u2014 stopped`
44817
+ };
44818
+ }
44819
+ try {
44820
+ await call(deps, ctx.log, "PATCH", `repos/${repo}/actions/variables/${name}`, { value });
44821
+ } catch (e) {
44822
+ const { status, reason } = classifyUpstreamError(e);
44823
+ if (reason === "denied") return rejectedWrite(base, { presence: "present", value: baseline.value }, `HTTP ${status}: the App installation lacks repository Variables write`, { permission: "variables:write", outcome: "denied" });
44824
+ if (status === 404) return rejectedWrite(base, { presence: "present", value: baseline.value }, "HTTP 404 at write: the variable vanished after the baseline check (concurrent deletion suspected) \u2014 reconcile read-only, never create");
44825
+ if (status === 409 || status === 422) return rejectedWrite(base, { presence: "present", value: baseline.value }, `HTTP ${status}: ${String(e.message).slice(0, 160)}`);
44826
+ return unknownWrite(base, { presence: "present", value: baseline.value }, `${reason === "timeout" ? "dispatch timed out" : `HTTP ${status || "no response"}`}; the update MAY have applied \u2014 reconcile read-only, never replay`);
44827
+ }
44828
+ const rb = await readVariable(deps, repo, name, ctx.log);
44829
+ if (rb.presence === "present") {
44830
+ if (rb.value === value) return { ...base, outcome: "verified", baseline: { presence: "present", value: baseline.value, source: "get" }, readback: { presence: "present", value: rb.value } };
44831
+ return unknownWrite(base, { presence: "present", value: baseline.value }, "read-back shows a different value than dispatched \u2014 a concurrent outside write is possible; reconcile read-only", { presence: "present", value: rb.value });
44832
+ }
44833
+ if (rb.presence === "absent") {
44834
+ return unknownWrite(base, { presence: "present", value: baseline.value }, "accepted but the read-back proves absence \u2014 reconcile read-only before any retry", { presence: "absent" });
44835
+ }
44836
+ return {
44837
+ ...base,
44838
+ outcome: "accepted-but-unverified",
44839
+ baseline: { presence: "present", value: baseline.value, source: "get" },
44840
+ readback: { presence: "unknown" },
44841
+ reason: `update accepted but the read-back could not verify: ${rb.unknownReason ?? "unknown"}`
44842
+ };
44843
+ }
44844
+ async function deleteVariableReceipt(deps, repo, name) {
44845
+ const ctx = beginOp();
44846
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
44847
+ const base = { ...receiptBase(deps, "delete", repo, ctx, now), operation: "delete", name, casLimitation: "no-atomic-compare-and-swap" };
44848
+ const baseline = await readBaseline(deps, repo, name, ctx.log);
44849
+ if (baseline.presence !== "present") {
44850
+ return {
44851
+ ...base,
44852
+ outcome: "not-dispatched",
44853
+ baseline: { presence: baseline.presence, ...baseline.value !== void 0 ? { value: baseline.value } : {}, source: "get" },
44854
+ reason: baseline.presence === "absent" ? "no present baseline \u2014 the enumeration already proves absence; nothing to delete" : `no retained present baseline (${baseline.unknownReason ?? "unknown"}); a bare 404 never authorizes a delete`,
44855
+ ...baseline.capability ? { capability: baseline.capability } : {}
44856
+ };
44857
+ }
44858
+ const live = await readVariable(deps, repo, name, ctx.log);
44859
+ if (live.presence !== "present" || live.value !== baseline.value) {
44860
+ return {
44861
+ ...base,
44862
+ outcome: "not-dispatched",
44863
+ baseline: { presence: "present", value: baseline.value, source: "get" },
44864
+ ...live.presence === "present" ? { readback: { presence: "present", value: live.value } } : {},
44865
+ reason: live.presence === "present" ? "concurrent change: the live value no longer matches the retained baseline \u2014 stopped for reconciliation" : `baseline unconfirmed before dispatch (${live.unknownReason ?? "unknown"}) \u2014 stopped`
44866
+ };
44867
+ }
44868
+ try {
44869
+ await call(deps, ctx.log, "DELETE", `repos/${repo}/actions/variables/${name}`);
44870
+ } catch (e) {
44871
+ const { status, reason } = classifyUpstreamError(e);
44872
+ if (reason === "denied") return rejectedWrite(base, { presence: "present", value: baseline.value }, `HTTP ${status}: the App installation lacks repository Variables write`, { permission: "variables:write", outcome: "denied" });
44873
+ if (status === 404) return rejectedWrite(base, { presence: "present", value: baseline.value }, "HTTP 404 at write: the variable vanished after the baseline check (concurrent deletion suspected) \u2014 reconcile read-only");
44874
+ if (status === 409 || status === 422) return rejectedWrite(base, { presence: "present", value: baseline.value }, `HTTP ${status}: ${String(e.message).slice(0, 160)}`);
44875
+ return unknownWrite(base, { presence: "present", value: baseline.value }, `${reason === "timeout" ? "dispatch timed out" : `HTTP ${status || "no response"}`}; the delete MAY have applied \u2014 reconcile read-only, never replay`);
44876
+ }
44877
+ try {
44878
+ const en = await enumerateRepositoryVariables(deps, repo);
44879
+ if (en.complete && !findEntry(en.entries, name)) {
44880
+ return { ...base, outcome: "verified", baseline: { presence: "present", value: baseline.value, source: "get" }, readback: { presence: "absent" }, reason: `absence proven by complete enumeration (${en.pages} page(s), ${en.totalVariables} variable(s), name excluded)` };
44881
+ }
44882
+ if (en.complete) {
44883
+ const still = findEntry(en.entries, name);
44884
+ return unknownWrite(base, { presence: "present", value: baseline.value }, "delete accepted but the variable is still enumerated \u2014 reconcile read-only, never replay", { presence: "present", ...still.value !== void 0 ? { value: still.value } : {} });
44885
+ }
44886
+ return {
44887
+ ...base,
44888
+ outcome: "accepted-but-unverified",
44889
+ baseline: { presence: "present", value: baseline.value, source: "get" },
44890
+ reason: `delete accepted but absence is unproven: ${en.reason} (${en.detail})`
44891
+ };
44892
+ } catch (e) {
44893
+ return {
44894
+ ...base,
44895
+ outcome: "accepted-but-unverified",
44896
+ baseline: { presence: "present", value: baseline.value, source: "get" },
44897
+ reason: `delete accepted but absence is unproven (${classifyUpstreamError(e).reason}): ${String(e.message).slice(0, 160)}`
44898
+ };
44899
+ }
44900
+ }
44901
+ var VARS_HELP_AFTER = [
44902
+ "",
44903
+ "Presence semantics (#9780): present | absent | unknown. A bare 404 is UNKNOWN, never absence \u2014",
44904
+ "absence is claimed only from a complete bounded enumeration excluding the exact name, with",
44905
+ "pagination evidence. Truncated pagination, malformed pages, denial, parser refusal or timeout is",
44906
+ "unknown. Values are preserved exactly (including `off`) and never coerced.",
44907
+ "Mutations verify by read-back (delete: complete enumeration) \u2014 an HTTP success alone is never",
44908
+ "verified; an ambiguous result is reported unknown and reconciled READ-ONLY, never replayed.",
44909
+ "The pre-write check is NOT atomic compare-and-swap: a concurrent outside write between the check",
44910
+ "and the dispatch is not prevented. Repository scope only \u2014 no organization/environment scope, no",
44911
+ "secret store, no rename, no workflow dispatch, no credential fallback."
44912
+ ].join("\n");
44913
+ function resolveRepoOrFail(raw, projects, verb) {
44914
+ const resolution = resolveCanonicalRepo(raw, projects);
44915
+ if (resolution.kind === "resolved") return resolution.repo;
44916
+ if (resolution.kind === "conflict") return fail(`vars ${verb}: '${raw}' resolves to MULTIPLE registered repositories (${resolution.candidates.join(", ")}) \u2014 pass the exact owner/repo; ambiguous aliases are refused (#9780)`);
44917
+ if (resolution.kind === "unavailable") return fail(`vars ${verb}: the Hub project registry is unreadable; refusing to address a repo this command cannot prove registered`);
44918
+ return fail(`vars ${verb}: '${raw}' is not a registered repository in the Hub registry; App variables access is limited to registered repos \u2014 pass the exact owner/repo`);
44919
+ }
44920
+ function registerDevopsVarsCommands(program3, deps) {
44921
+ const vars = program3.command("vars").description("repository Actions variables \u2014 typed get/list/create/update/delete on the org App (Variables read/write), Hub-registered repos only (#9780)");
44922
+ const runRead = async (commandPath4, verb, repoArg, name, o) => {
44923
+ const projects = await deps.projects();
44924
+ const repo = resolveRepoOrFail(repoArg, projects, verb);
44925
+ const ops = { transport: await deps.appClient(commandPath4), ...deps.actor ? { actor: deps.actor() } : {} };
44926
+ const receipt = verb === "get" && name !== void 0 ? await getVariableReceipt(ops, repo, name) : await listVariablesReceipt(ops, repo);
44927
+ if (o.json) console.log(JSON.stringify(receipt, null, 2));
44928
+ else printReadText(receipt);
44929
+ const failed2 = receipt.operation === "get" ? receipt.presence === "unknown" : !receipt.pagination.complete;
44930
+ if (failed2) process.exitCode = 1;
44931
+ };
44932
+ const runMutation = async (commandPath4, verb, nameArg, value, repoArg, o) => {
44933
+ let name;
44934
+ let exactValue;
44935
+ try {
44936
+ name = parseVarsName(nameArg);
44937
+ if (verb !== "delete") exactValue = parseVarsValue(value);
44938
+ } catch (e) {
44939
+ return fail(`vars ${verb}: ${e.message}`);
44940
+ }
44941
+ const projects = await deps.projects();
44942
+ const repo = resolveRepoOrFail(repoArg, projects, verb);
44943
+ const ops = { transport: await deps.appClient(commandPath4), ...deps.actor ? { actor: deps.actor() } : {} };
44944
+ const receipt = verb === "create" ? await createVariableReceipt(ops, repo, name, exactValue) : verb === "update" ? await updateVariableReceipt(ops, repo, name, exactValue) : await deleteVariableReceipt(ops, repo, name);
44945
+ if (o.json) console.log(JSON.stringify(receipt, null, 2));
44946
+ else printMutationText(receipt);
44947
+ if (receipt.outcome !== "verified") process.exitCode = 1;
44948
+ };
44949
+ vars.command("get <name>").description("exact variable baseline or typed observed absence/unknown \u2014 a bare 404 is unknown; absence needs a complete enumeration (#9780)").requiredOption("--repo <owner/repo>", "registered repository (owner/repo, repo name, or one unambiguous project alias)").option("--json", "machine-readable receipt").addHelpText("after", VARS_HELP_AFTER).action(async (name, o) => runRead("vars get", "get", o.repo ?? "", name, o));
44950
+ vars.command("list").description("exhaustive bounded enumeration: names + provider timestamps only (never values), with pagination/completeness evidence (#9780)").requiredOption("--repo <owner/repo>", "registered repository (owner/repo, repo name, or one unambiguous project alias)").option("--json", "machine-readable receipt").addHelpText("after", VARS_HELP_AFTER).action(async (o) => runRead("vars list", "list", o.repo ?? "", void 0, o));
44951
+ vars.command("create <name>").description("create ONLY against an observed absent baseline (complete enumeration); value preserved exactly; verified by read-back (#9780)").requiredOption("--value <value>", "exact string value (never coerced; e.g. the literal `off`)").requiredOption("--repo <owner/repo>", "registered repository (owner/repo, repo name, or one unambiguous project alias)").option("--json", "machine-readable receipt").addHelpText("after", VARS_HELP_AFTER).action(async (name, o) => runMutation("vars create", "create", name, o.value, o.repo ?? "", o));
44952
+ vars.command("update <name>").description("replace ONLY against a retained present baseline, re-checked just before dispatch; concurrent changes stop the write (#9780)").requiredOption("--value <value>", "exact string value (never coerced)").requiredOption("--repo <owner/repo>", "registered repository (owner/repo, repo name, or one unambiguous project alias)").option("--json", "machine-readable receipt").addHelpText("after", VARS_HELP_AFTER).action(async (name, o) => runMutation("vars update", "update", name, o.value, o.repo ?? "", o));
44953
+ vars.command("delete <name>").description("delete ONLY against a retained present baseline; absence proven afterwards by a complete enumeration (#9780)").requiredOption("--repo <owner/repo>", "registered repository (owner/repo, repo name, or one unambiguous project alias)").option("--json", "machine-readable receipt").addHelpText("after", VARS_HELP_AFTER).action(async (name, o) => runMutation("vars delete", "delete", name, void 0, o.repo ?? "", o));
44954
+ }
44955
+ function printReadText(receipt) {
44956
+ if (receipt.operation === "get") {
44957
+ if (receipt.presence === "present") {
44958
+ console.log(`vars get: ${receipt.repo} ${receipt.name} = ${JSON.stringify(receipt.value ?? "")} (presence: present, source: ${receipt.observed?.source ?? "get"})`);
44959
+ } else if (receipt.presence === "absent") {
44960
+ const p = receipt.absenceProof;
44961
+ console.log(`vars get: ${receipt.repo} ${receipt.name} absent \u2014 proven by complete enumeration (${p.pages} page(s), ${p.totalVariables} variable(s), name excluded)`);
44962
+ } else {
44963
+ console.log(`vars get: ${receipt.repo} ${receipt.name} presence UNKNOWN (${receipt.unknownReason}${receipt.capability ? ", capability denied" : ""}) \u2014 absence is never claimed from this read`);
44964
+ }
44965
+ return;
44966
+ }
44967
+ if (receipt.pagination.complete) {
44968
+ console.log(`vars list: ${receipt.repo} \u2014 ${receipt.pagination.totalVariables} variable(s), complete (${receipt.pagination.pages} page(s)): ${receipt.variables.map((v) => v.name).join(", ") || "(none)"}`);
44969
+ } else {
44970
+ console.log(`vars list: ${receipt.repo} \u2014 INCOMPLETE enumeration (${receipt.pagination.reason ?? "unknown"}); the names below are NOT the full set: ${receipt.variables.map((v) => v.name).join(", ") || "(none)"}`);
44971
+ }
44972
+ }
44973
+ function printMutationText(receipt) {
44974
+ const baseline = receipt.baseline.presence === "present" ? `baseline ${JSON.stringify(receipt.baseline.value ?? "")}` : `baseline ${receipt.baseline.presence}`;
44975
+ const readback = receipt.readback ? `, read-back ${receipt.readback.presence}${receipt.readback.value !== void 0 ? ` ${JSON.stringify(receipt.readback.value)}` : ""}` : "";
44976
+ console.log(`vars ${receipt.operation}: ${receipt.repo} ${receipt.name} \u2014 ${receipt.outcome} (${baseline}${readback})${receipt.reason ? ` \u2014 ${receipt.reason}` : ""} [no atomic compare-and-swap; writes are never replayed]`);
44977
+ }
44978
+
44979
+ // src/repo-archive-status.ts
44980
+ var import_node_crypto14 = require("node:crypto");
44981
+ init_github_client();
44982
+ init_cli_shared();
44983
+ var ARCHIVE_STATUS_RECEIPT_SCHEMA = "mmi-cli.repo-archive-status/1";
44984
+ var ARCHIVE_SOURCE_TIME_NOTE = "GitHub exposes no archived_at; observedAt is this read's window end and the provider timestamps are the repository's own updated_at/pushed_at from the same response that carried the archived flag";
44985
+ async function call2(deps, log, method, path2) {
44986
+ try {
44987
+ const res = await deps.transport.restWithStatus(method, path2);
44988
+ log.push({ method, path: path2, status: res.status });
44989
+ return res;
44990
+ } catch (e) {
44991
+ const status = e instanceof GitHubApiError ? e.status : 0;
44992
+ log.push({ method, path: path2, status });
44993
+ throw e;
44994
+ }
44995
+ }
44996
+ function classify2(e) {
44997
+ if (e instanceof GitHubApiError) {
44998
+ if (e.status === 404) return { status: 404, reason: "missing-or-invisible" };
44999
+ if (e.status === 403 || e.status === 401) return { status: e.status, reason: "denied" };
45000
+ return { status: e.status, reason: "transport-error" };
45001
+ }
45002
+ const name = e?.name ?? "";
45003
+ if (name === "TimeoutError" || name === "AbortError" || /timeout|abort/i.test(String(e?.message ?? ""))) return { status: 0, reason: "timeout" };
45004
+ return { status: 0, reason: "transport-error" };
45005
+ }
45006
+ async function readArchiveStatusReceipt(deps, repo) {
45007
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
45008
+ const startedAt = now();
45009
+ const log = [];
45010
+ const base = {
45011
+ schema: ARCHIVE_STATUS_RECEIPT_SCHEMA,
45012
+ repo,
45013
+ sourceTime: { observedAt: "", note: ARCHIVE_SOURCE_TIME_NOTE },
45014
+ window: { startedAt: startedAt.toISOString(), endedAt: "", atomicSnapshot: false },
45015
+ actor: deps.actor ?? { credential: "app_installation" },
45016
+ correlationId: (deps.correlationId ?? import_node_crypto14.randomUUID)(),
45017
+ upstream: log
45018
+ };
45019
+ try {
45020
+ const { body } = await call2(deps, log, "GET", `repos/${repo}`);
45021
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
45022
+ return { ...base, archived: "unknown", unknownReason: "parser-refused", sourceTime: { ...base.sourceTime, observedAt: now().toISOString() }, window: { ...base.window, endedAt: now().toISOString() } };
45023
+ }
45024
+ const payload = body;
45025
+ if (typeof payload.archived !== "boolean") {
45026
+ return { ...base, archived: "unknown", unknownReason: "parser-refused", sourceTime: { ...base.sourceTime, observedAt: now().toISOString() }, window: { ...base.window, endedAt: now().toISOString() } };
45027
+ }
45028
+ if (payload.updated_at !== void 0 && typeof payload.updated_at !== "string") payload.updated_at = void 0;
45029
+ if (payload.pushed_at !== void 0 && typeof payload.pushed_at !== "string") payload.pushed_at = void 0;
45030
+ return {
45031
+ ...base,
45032
+ archived: payload.archived,
45033
+ sourceTime: {
45034
+ observedAt: now().toISOString(),
45035
+ ...typeof payload.updated_at === "string" ? { providerUpdatedAt: payload.updated_at } : {},
45036
+ ...typeof payload.pushed_at === "string" ? { providerPushedAt: payload.pushed_at } : {},
45037
+ note: ARCHIVE_SOURCE_TIME_NOTE
45038
+ },
45039
+ window: { ...base.window, endedAt: now().toISOString() }
45040
+ };
45041
+ } catch (e) {
45042
+ const { status, reason } = classify2(e);
45043
+ return {
45044
+ ...base,
45045
+ archived: "unknown",
45046
+ unknownReason: reason,
45047
+ ...reason === "denied" ? { capability: { permission: "metadata:read", outcome: "denied" } } : {},
45048
+ ...reason === "missing-or-invisible" ? { sourceTime: { ...base.sourceTime, observedAt: now().toISOString(), note: `${ARCHIVE_SOURCE_TIME_NOTE} \u2014 HTTP 404: the repository is missing or not visible to the installation, which is NOT archived:false` } } : { sourceTime: { ...base.sourceTime, observedAt: now().toISOString() } },
45049
+ window: { ...base.window, endedAt: now().toISOString() }
45050
+ };
45051
+ }
45052
+ }
45053
+ function registerRepoArchiveStatusCommand(program3, deps) {
45054
+ const repoRoot2 = program3.command("repo").description("canonical repository typed reads \u2014 archive status (#9779)");
45055
+ repoRoot2.command("archive-status").description("typed archived-state read for one canonical repository: archived + source time, App metadata:read; a 404 is missing-or-invisible, never archived:false (#9779)").requiredOption("--repo <owner/repo>", "registered repository (owner/repo, repo name, or one unambiguous project alias)").option("--json", "machine-readable receipt").action(async (o) => {
45056
+ const projects = await deps.projects();
45057
+ const resolution = resolveCanonicalRepo(o.repo ?? "", projects);
45058
+ const repo = resolution.kind === "resolved" ? resolution.repo : resolution.kind === "conflict" ? fail(`repo archive-status: '${o.repo ?? ""}' resolves to MULTIPLE registered repositories (${resolution.candidates.join(", ")}) \u2014 pass the exact owner/repo (#9779)`) : resolution.kind === "unavailable" ? fail("repo archive-status: the Hub project registry is unreadable; refusing to address a repo this command cannot prove registered") : fail(`repo archive-status: '${o.repo ?? ""}' is not a registered repository in the Hub registry \u2014 pass the exact owner/repo`);
45059
+ const receipt = await readArchiveStatusReceipt({ transport: await deps.appClient("repo archive-status"), ...deps.actor ? { actor: deps.actor() } : {} }, repo);
45060
+ if (o.json) console.log(JSON.stringify(receipt, null, 2));
45061
+ else if (receipt.archived === "unknown") console.log(`repo archive-status: ${receipt.repo} \u2014 archived state UNKNOWN (${receipt.unknownReason}) \u2014 never reads as not-archived`);
45062
+ else console.log(`repo archive-status: ${receipt.repo} \u2014 archived: ${receipt.archived} (observed ${receipt.sourceTime.observedAt}${receipt.sourceTime.providerUpdatedAt ? `, provider updated_at ${receipt.sourceTime.providerUpdatedAt}` : ""})`);
45063
+ if (receipt.archived === "unknown") process.exitCode = 1;
45064
+ });
45065
+ }
45066
+
44439
45067
  // src/board-commands.ts
44440
45068
  init_cli_shared();
44441
45069
 
@@ -45384,7 +46012,7 @@ var import_node_os20 = require("node:os");
45384
46012
  var import_node_path34 = require("node:path");
45385
46013
 
45386
46014
  // src/box-control.ts
45387
- var import_node_crypto13 = require("node:crypto");
46015
+ var import_node_crypto15 = require("node:crypto");
45388
46016
  var MANIFEST_PATH = "infra/box-control/manifest.json";
45389
46017
  var ALLOWED_POST = /* @__PURE__ */ new Set(["systemctl daemon-reload"]);
45390
46018
  var sshdPost = (dest) => `if sshd -t; then systemctl reload ssh; else rm -f ${dest}; echo 'sshd -t rejected ${dest}: removed, not reloaded' >&2; exit 1; fi`;
@@ -45488,7 +46116,7 @@ function laneCoverageAction(files, box, op) {
45488
46116
  return { notice: `LANE_COVERAGE: ${mismatch}` };
45489
46117
  }
45490
46118
  function md5(content) {
45491
- return (0, import_node_crypto13.createHash)("md5").update(content).digest("hex");
46119
+ return (0, import_node_crypto15.createHash)("md5").update(content).digest("hex");
45492
46120
  }
45493
46121
  function planRemoteScript(dests) {
45494
46122
  const lines2 = ["set -u"];
@@ -47646,7 +48274,7 @@ function renderDeployPortDoctor(report) {
47646
48274
  }
47647
48275
 
47648
48276
  // src/bootstrap-drift.ts
47649
- var import_node_crypto14 = require("node:crypto");
48277
+ var import_node_crypto16 = require("node:crypto");
47650
48278
  function byteComparableSeeds(manifest, cls) {
47651
48279
  return manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self" && s.classes.includes(cls));
47652
48280
  }
@@ -47666,7 +48294,7 @@ function compareSeedBytes(hubContent, repoContent) {
47666
48294
  return normalize(hubContent) === normalize(repoContent) ? "match" : "drift";
47667
48295
  }
47668
48296
  function seedContentHash(content) {
47669
- return (0, import_node_crypto14.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
48297
+ return (0, import_node_crypto16.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
47670
48298
  }
47671
48299
  function auditRepoSeedDrift(repo, seeds, hubContents, repoReads) {
47672
48300
  const byTarget = new Map(repoReads.map((r) => [r.target, r.content]));
@@ -48916,7 +49544,7 @@ function renderBootstrapVerifyReport(report) {
48916
49544
  }
48917
49545
 
48918
49546
  // src/bootstrap-seed-delivery.ts
48919
- var import_node_crypto15 = require("node:crypto");
49547
+ var import_node_crypto17 = require("node:crypto");
48920
49548
  init_github_client();
48921
49549
  var SHA = /^[a-f0-9]{40}$/;
48922
49550
  async function readSeedFile(repo, target, ref, client) {
@@ -48949,7 +49577,7 @@ async function targetedSeedPlan(repo, slug, target, baseBranch, sourceSha, clien
48949
49577
  }
48950
49578
  const baseSha = base?.object?.sha;
48951
49579
  if (!baseSha || !SHA.test(baseSha) || !SHA.test(sourceSha)) throw new Error("bootstrap apply: seed identity is unreadable");
48952
- const identity = (0, import_node_crypto15.createHash)("sha256").update(JSON.stringify([repo.toLowerCase(), target, baseSha, sourceSha])).digest("hex").slice(0, 24);
49580
+ const identity = (0, import_node_crypto17.createHash)("sha256").update(JSON.stringify([repo.toLowerCase(), target, baseSha, sourceSha])).digest("hex").slice(0, 24);
48953
49581
  const delivery = planSeedDelivery(rules, slug, baseBranch, `bootstrap-seed-${identity}`);
48954
49582
  const scoped = forcePr && delivery.mode === "direct" ? { mode: "pr", ref: `bootstrap-seed-${identity}-${slug}`, branch: `bootstrap-seed-${identity}-${slug}`, reason: "registry gate render requires review" } : delivery;
48955
49583
  return { ...scoped, baseSha, baseBranch };
@@ -50834,7 +51462,7 @@ init_github_client();
50834
51462
  init_cli_shared();
50835
51463
 
50836
51464
  // src/suite-status.ts
50837
- var import_node_crypto16 = require("node:crypto");
51465
+ var import_node_crypto18 = require("node:crypto");
50838
51466
  var SUITE_REPO = "mutmutco/MM-Work";
50839
51467
  var REPOS = {
50840
51468
  "mmi-scout": "mutmutco/MMI-Scout",
@@ -50862,7 +51490,7 @@ function parseSuiteMapping(body) {
50862
51490
  return void 0;
50863
51491
  }
50864
51492
  }
50865
- var sha256 = (b) => (0, import_node_crypto16.createHash)("sha256").update(b).digest("hex");
51493
+ var sha256 = (b) => (0, import_node_crypto18.createHash)("sha256").update(b).digest("hex");
50866
51494
  async function proveBundle(url, tag, fetchUrl) {
50867
51495
  const get = async (u) => {
50868
51496
  try {
@@ -51119,7 +51747,7 @@ async function waitSuiteDev(deps, opts) {
51119
51747
  // src/suite-train.ts
51120
51748
  var import_node_fs39 = require("node:fs");
51121
51749
  var import_node_path36 = require("node:path");
51122
- var import_node_crypto17 = require("node:crypto");
51750
+ var import_node_crypto19 = require("node:crypto");
51123
51751
 
51124
51752
  // src/kit-version.ts
51125
51753
  var parse = (v) => /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(typeof v === "string" ? v.trim() : "");
@@ -51221,7 +51849,7 @@ function readSuiteCut(path2) {
51221
51849
  }
51222
51850
  function writeSuiteCut(path2, record2) {
51223
51851
  (0, import_node_fs39.mkdirSync)((0, import_node_path36.dirname)(path2), { recursive: true });
51224
- const temp = `${path2}.${(0, import_node_crypto17.randomBytes)(8).toString("hex")}.tmp`;
51852
+ const temp = `${path2}.${(0, import_node_crypto19.randomBytes)(8).toString("hex")}.tmp`;
51225
51853
  try {
51226
51854
  (0, import_node_fs39.writeFileSync)(temp, JSON.stringify(record2, null, 2), { flag: "wx" });
51227
51855
  (0, import_node_fs39.renameSync)(temp, path2);
@@ -53702,16 +54330,16 @@ async function ghWithAppOn401(label, args, options) {
53702
54330
  }
53703
54331
  function withAppOn401(client, label = "post-merge") {
53704
54332
  let app;
53705
- const run = async (call) => {
54333
+ const run = async (call3) => {
53706
54334
  try {
53707
- return await call(app ?? client);
54335
+ return await call3(app ?? client);
53708
54336
  } catch (e) {
53709
54337
  if (!isUnauthorized(e)) throw e;
53710
54338
  if (app) throw new PostMergeCredentialError(label, "still HTTP 401 with the App installation token (expired or revoked App credential)");
53711
54339
  const token = await appTokenFor(label, e);
53712
54340
  app = createGitHubClient({ token: async () => token });
53713
54341
  try {
53714
- return await call(app);
54342
+ return await call3(app);
53715
54343
  } catch (e2) {
53716
54344
  if (isUnauthorized(e2)) throw new PostMergeCredentialError(label, "still HTTP 401 with the App installation token (expired or revoked App credential)");
53717
54345
  throw e2;
@@ -54322,7 +54950,7 @@ async function deleteMergedRemoteBranch(options) {
54322
54950
  init_github_client();
54323
54951
 
54324
54952
  // src/release-custody.ts
54325
- var import_node_crypto18 = require("node:crypto");
54953
+ var import_node_crypto20 = require("node:crypto");
54326
54954
  var import_node_fs41 = require("node:fs");
54327
54955
  var import_node_path38 = require("node:path");
54328
54956
  init_github_client();
@@ -54545,7 +55173,7 @@ var ReleaseCustodyError = class extends Error {
54545
55173
  this.name = "ReleaseCustodyError";
54546
55174
  }
54547
55175
  };
54548
- var custodyDigest = (value) => (0, import_node_crypto18.createHash)("sha256").update(value).digest("hex");
55176
+ var custodyDigest = (value) => (0, import_node_crypto20.createHash)("sha256").update(value).digest("hex");
54549
55177
  function parseCustody(raw, repo) {
54550
55178
  const s = JSON.parse(raw);
54551
55179
  if (s.schema !== "mmi-release-custody/v1" || s.repo !== repo || !s.precutId || !Number.isSafeInteger(s.revision) || s.revision < 1 || !s.controller || !Number.isSafeInteger(s.controller.runId) || s.controller.runId <= 0 || !Number.isSafeInteger(s.controller.runAttempt) || s.controller.runAttempt <= 0 || !s.controller.token || !(s.ledger === null || typeof s.ledger === "string") || !s.archives || !s.effects) {
@@ -54633,7 +55261,7 @@ var ReleaseCustodySession = class _ReleaseCustodySession {
54633
55261
  throw new ReleaseCustodyError(`previous controller run ${found.state.controller.runId} attempt ${found.state.controller.runAttempt} is not proven terminal`);
54634
55262
  }
54635
55263
  let state = {
54636
- ...found.state ?? { schema: "mmi-release-custody/v1", repo, precutId: (0, import_node_crypto18.randomUUID)(), effects: {}, ...await initialize() },
55264
+ ...found.state ?? { schema: "mmi-release-custody/v1", repo, precutId: (0, import_node_crypto20.randomUUID)(), effects: {}, ...await initialize() },
54637
55265
  revision: (found.state?.revision ?? 0) + 1,
54638
55266
  controller
54639
55267
  };
@@ -54694,7 +55322,7 @@ var ReleaseCustodySession = class _ReleaseCustodySession {
54694
55322
  proof: { kind: "ledger-lifecycle", controller: state.controller }
54695
55323
  };
54696
55324
  }
54697
- return { ...state, ledger: raw, archives, precutId: state.ledger && !raw ? (0, import_node_crypto18.randomUUID)() : state.precutId };
55325
+ return { ...state, ledger: raw, archives, precutId: state.ledger && !raw ? (0, import_node_crypto20.randomUUID)() : state.precutId };
54698
55326
  });
54699
55327
  }
54700
55328
  async preserveLocal(raw) {
@@ -54920,7 +55548,7 @@ function withHubReleaseCustody(deps) {
54920
55548
  if (!authority.ok || !authority.train || authority.authenticatedDelegation !== true) {
54921
55549
  throw new ReleaseCustodyError("repository custody requires the server-verified owner-triggered train session");
54922
55550
  }
54923
- const controller = { ...runIdentity, token: (0, import_node_crypto18.randomUUID)() };
55551
+ const controller = { ...runIdentity, token: (0, import_node_crypto20.randomUUID)() };
54924
55552
  const raw = localRaw(path2);
54925
55553
  const requiredContexts = () => discoverRequiredCheckContexts(
54926
55554
  deps,
@@ -55632,7 +56260,7 @@ init_github_quota();
55632
56260
 
55633
56261
  // src/issue-commands.ts
55634
56262
  var import_node_fs44 = require("node:fs");
55635
- var import_node_crypto19 = require("node:crypto");
56263
+ var import_node_crypto21 = require("node:crypto");
55636
56264
  init_cli_shared();
55637
56265
  init_clean_exit();
55638
56266
  init_error_codes();
@@ -56109,7 +56737,7 @@ function rowIdempotencyKey(batchKey, spec) {
56109
56737
  const identity = `${spec.type}
56110
56738
  ${spec.title.trim()}
56111
56739
  ${spec.body ?? ""}`;
56112
- const hash = (0, import_node_crypto19.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
56740
+ const hash = (0, import_node_crypto21.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
56113
56741
  return `${batchKey}:${hash}`;
56114
56742
  }
56115
56743
  var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
@@ -57147,6 +57775,8 @@ function registerApiReadCommands(program3, overrides) {
57147
57775
  // src/run-log-read.ts
57148
57776
  init_cli_shared();
57149
57777
  var FAILED2 = /* @__PURE__ */ new Set(["failure", "cancelled", "timed_out"]);
57778
+ var RUN_LOG_RANGE_LINE_CAP = 2e3;
57779
+ var RUN_LOG_RANGE_CHAR_CAP = 32 * 1024;
57150
57780
  function logRepo(raw) {
57151
57781
  const repo = parseBrokerRepo(raw, "--repo");
57152
57782
  if (repo.split("/").some((s) => s === "." || s === "..")) throw new Error("read run log: --repo refuses dot segments");
@@ -57165,6 +57795,78 @@ function sliceRunLog(text, tail) {
57165
57795
  });
57166
57796
  return { errorLines: lines2.filter((_, i) => context.has(i)), tail: lines2.slice(-tail) };
57167
57797
  }
57798
+ function parseLineRange(raw) {
57799
+ const match = /^(\d+):(\d+)$/.exec(raw.trim());
57800
+ if (!match) throw new Error(`read run log: --lines must be <start:end> line numbers, e.g. --lines 1:2000 (received '${raw}')`);
57801
+ const start = Number(match[1]);
57802
+ const end = Number(match[2]);
57803
+ if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 1 || end < start) {
57804
+ throw new Error(`read run log: --lines must be a 1-based start:end range with start <= end and safe-integer bounds (received '${raw}')`);
57805
+ }
57806
+ return { start, end };
57807
+ }
57808
+ async function readRunLogRange(id, o, runner, env = brokerChildEnv()) {
57809
+ const { start, end: requestedEnd } = parseLineRange(o.lines);
57810
+ if (o.tail !== void 0) throw new Error("read run log: --lines and --tail are mutually exclusive (the range already bounds the returned lines)");
57811
+ if (o.failed) throw new Error("read run log: --lines and --failed are mutually exclusive (--lines reads exactly one named job)");
57812
+ if (o.attempt === void 0) throw new Error("read run log: --lines requires an explicit --attempt <n> (1-1000) \u2014 Actions job ids are minted per attempt, so the id must name one attempt");
57813
+ if (o.job === void 0 || !/^\d+$/.test(o.job.trim())) throw new Error("read run log: --lines requires a numeric --job <id> \u2014 job names are ambiguous across attempts; take the id from the attempt-pinned jobs read");
57814
+ const runId = parseBrokerId(id, "run id");
57815
+ const repo = logRepo(o.repo);
57816
+ const attempt = parseBrokerInt(o.attempt, "--attempt", 1, 1e3);
57817
+ const jobId = Number(parseBrokerId(o.job, "job id"));
57818
+ if (!Number.isSafeInteger(jobId) || jobId < 1) throw new Error(`read run log: --lines requires a numeric --job id (received '${o.job}')`);
57819
+ const pages = JSON.parse(await readAppApi(`repos/${repo}/actions/runs/${runId}/attempts/${attempt}/jobs`, runner));
57820
+ const listed = pages.flatMap((page) => page.jobs);
57821
+ const job = listed.find((candidate) => candidate.id === jobId);
57822
+ if (!job) {
57823
+ const shown = listed.slice(0, 30).map((j) => `${j.name} (${j.id})`).join(", ");
57824
+ return {
57825
+ result: { runId, repo, jobs: [] },
57826
+ exitCode: 1,
57827
+ message: `read run log: job ${jobId} is not in run ${runId} attempt ${attempt} \u2014 Actions job ids are minted per workflow run and per re-run attempt, so only an id from this attempt's listing matches; jobs in run ${runId} attempt ${attempt}: ${shown || "none"}${listed.length > 30 ? `, \u2026+${listed.length - 30} more` : ""}`
57828
+ };
57829
+ }
57830
+ const text = await readAppApi(`repos/${repo}/actions/jobs/${job.id}/logs`, runner);
57831
+ const redacted = redactPrivateCredential(text, env);
57832
+ const all = redacted.replace(/\r\n/g, "\n").split("\n");
57833
+ if (all.at(-1) === "") all.pop();
57834
+ const total = all.length;
57835
+ if (start > total) {
57836
+ return {
57837
+ result: { runId, repo, jobs: [] },
57838
+ exitCode: 1,
57839
+ message: `read run log: --lines ${start}:${requestedEnd} starts beyond the end of the log \u2014 job ${job.name} (${job.id}) attempt ${attempt} retains ${total} lines; nothing to return`
57840
+ };
57841
+ }
57842
+ const requestedCount = requestedEnd - start + 1;
57843
+ const selected = all.slice(start - 1, Math.min(requestedEnd, start - 1 + RUN_LOG_RANGE_LINE_CAP));
57844
+ let truncated = requestedCount > selected.length;
57845
+ let section = selected.join("\n");
57846
+ let charCut = false;
57847
+ if (section.length > RUN_LOG_RANGE_CHAR_CAP) {
57848
+ section = section.slice(0, RUN_LOG_RANGE_CHAR_CAP);
57849
+ charCut = true;
57850
+ truncated = true;
57851
+ }
57852
+ const lines2 = section.split("\n");
57853
+ if (charCut && lines2.at(-1) === "") lines2.pop();
57854
+ return {
57855
+ result: {
57856
+ runId,
57857
+ repo,
57858
+ attempt,
57859
+ job: { id: job.id, name: job.name, conclusion: job.conclusion },
57860
+ lines: lines2,
57861
+ start,
57862
+ end: start + lines2.length - 1,
57863
+ requestedEnd,
57864
+ total,
57865
+ truncated
57866
+ },
57867
+ exitCode: 0
57868
+ };
57869
+ }
57168
57870
  async function readRunLog(id, o, runner) {
57169
57871
  const runId = parseBrokerId(id, "run id");
57170
57872
  const repo = logRepo(o.repo);
@@ -57190,14 +57892,17 @@ async function readRunLog(id, o, runner) {
57190
57892
  return { result, exitCode: 0 };
57191
57893
  }
57192
57894
  function registerRunLogRead(run, runner) {
57193
- run.command("log").description("read workflow job logs on the App pool: error lines with \xB13 context and bounded tail (#8136); defaults to failed jobs, never personal").requiredOption("--repo <owner/repo>", "target repo on github.com", logRepo).addOption(new Option("--failed", "select failure/cancelled/timed_out jobs (default)").conflicts("job")).addOption(new Option("--job <name>", "select jobs with this exact name or numeric job id").argParser(logJob).conflicts("failed")).option("--tail <n>", "last lines per job (1-2000; default 200)", (v) => String(parseBrokerInt(v, "--tail", 1, 2e3)), "200").option("--attempt <n>", "run attempt (1-1000; default latest)", (v) => String(parseBrokerInt(v, "--attempt", 1, 1e3))).option("--json", "emit runId, repo and jobs with failedStep/errorLines/tail").argument("<runId>", "workflow run number", (v) => parseBrokerId(v, "run id")).action(async (id, o) => {
57895
+ run.command("log").description("read workflow job logs on the App pool: error lines with \xB13 context and bounded tail (#8136), or a bounded --lines range from one attempt-pinned job (#9773); defaults to failed jobs, never personal").requiredOption("--repo <owner/repo>", "target repo on github.com", logRepo).addOption(new Option("--failed", "select failure/cancelled/timed_out jobs (default)").conflicts("job")).addOption(new Option("--job <name>", "select jobs with this exact name or numeric job id").argParser(logJob).conflicts("failed")).option("--tail <n>", "last lines per job (1-2000; default 200)", (v) => String(parseBrokerInt(v, "--tail", 1, 2e3))).option("--attempt <n>", "run attempt (1-1000; default latest)", (v) => String(parseBrokerInt(v, "--attempt", 1, 1e3))).addOption(new Option("--lines <start:end>", "read lines start:end of one job (needs --attempt and a numeric --job id; capped at 2000 lines and 32KiB characters; conflicts with --tail/--failed)").conflicts("failed")).option("--json", "emit the read as JSON (jobs with failedStep/errorLines/tail, or the --lines selection)").argument("<runId>", "workflow run number", (v) => parseBrokerId(v, "run id")).action(async (id, o) => {
57194
57896
  requireBrokerApp("read run log");
57195
57897
  const env = brokerChildEnv();
57196
57898
  try {
57197
- const { result, message: message2, exitCode } = await readRunLog(id, o, runner);
57198
- if (message2 && (!o.json || exitCode !== 0)) printLine(message2);
57899
+ const { result, message: message2, exitCode } = o.lines === void 0 ? await readRunLog(id, o, runner) : await readRunLogRange(id, o, runner, env);
57900
+ if (message2 && (!o.json || exitCode !== 0)) printLine(redactPrivateCredential(message2, env));
57199
57901
  else if (o.json) printLine(redactPrivateCredential(JSON.stringify(result), env));
57200
- else for (const job of result.jobs) {
57902
+ else if ("lines" in result) {
57903
+ printLine(redactPrivateCredential(`== ${result.job.name} (${result.job.id}) ${result.job.conclusion}: lines ${result.start}-${result.end} of ${result.total} (attempt ${result.attempt}${result.truncated ? ", truncated" : ""})`, env));
57904
+ for (const line of result.lines) printLine(redactPrivateCredential(line, env));
57905
+ } else for (const job of result.jobs) {
57201
57906
  printLine(redactPrivateCredential(`== ${job.name} (${job.id}) ${job.conclusion}: failing step "${job.failedStep ?? "none"}"`, env));
57202
57907
  for (const line of [...job.errorLines, ...job.tail]) printLine(redactPrivateCredential(line, env));
57203
57908
  }
@@ -69286,14 +69991,14 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
69286
69991
  }
69287
69992
 
69288
69993
  // src/tenant-artifact.ts
69289
- var import_node_crypto20 = require("node:crypto");
69994
+ var import_node_crypto22 = require("node:crypto");
69290
69995
  var import_node_fs59 = require("node:fs");
69291
69996
  var import_promises11 = require("node:fs/promises");
69292
69997
  var import_node_path54 = require("node:path");
69293
69998
  var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
69294
69999
  var MAX_BYTES2 = 5 * 1024 * 1024 * 1024;
69295
70000
  async function sha256File(path2) {
69296
- const hash = (0, import_node_crypto20.createHash)("sha256");
70001
+ const hash = (0, import_node_crypto22.createHash)("sha256");
69297
70002
  for await (const chunk of (0, import_node_fs59.createReadStream)(path2)) hash.update(chunk);
69298
70003
  return hash.digest("hex");
69299
70004
  }
@@ -69885,13 +70590,13 @@ async function executeWaveLand(plan, deps) {
69885
70590
 
69886
70591
  // src/dist-drift.ts
69887
70592
  var import_node_child_process22 = require("node:child_process");
69888
- var import_node_crypto22 = require("node:crypto");
70593
+ var import_node_crypto24 = require("node:crypto");
69889
70594
  var import_node_fs62 = require("node:fs");
69890
70595
  var import_node_os27 = require("node:os");
69891
70596
  var import_node_path57 = require("node:path");
69892
70597
 
69893
70598
  // ../scripts/distribution-digest.mjs
69894
- var import_node_crypto21 = require("node:crypto");
70599
+ var import_node_crypto23 = require("node:crypto");
69895
70600
  var import_node_fs61 = require("node:fs");
69896
70601
  var import_node_path56 = require("node:path");
69897
70602
  var slash = (value) => value.replaceAll("\\", "/");
@@ -69904,7 +70609,7 @@ function repoPath(root, declaredPath, label) {
69904
70609
  return target;
69905
70610
  }
69906
70611
  function digestFiles(files) {
69907
- const hash = (0, import_node_crypto21.createHash)("sha256");
70612
+ const hash = (0, import_node_crypto23.createHash)("sha256");
69908
70613
  for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
69909
70614
  const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs61.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs61.readFileSync)(file.absolute);
69910
70615
  hash.update(file.relative, "utf8");
@@ -69935,7 +70640,7 @@ var DIST_ARTIFACTS = [
69935
70640
  ];
69936
70641
  var BOM_DIST_TREE_ID = "mmi-cli-dist";
69937
70642
  var ABSENT = "absent";
69938
- var sha2562 = (bytes) => `sha256:${(0, import_node_crypto22.createHash)("sha256").update(bytes).digest("hex")}`;
70643
+ var sha2562 = (bytes) => `sha256:${(0, import_node_crypto24.createHash)("sha256").update(bytes).digest("hex")}`;
69939
70644
  function artifactDrift(path2, committedBytes, rebuiltBytes) {
69940
70645
  const committed = committedBytes ? sha2562(committedBytes) : ABSENT;
69941
70646
  const rebuiltExpected = rebuiltBytes ? sha2562(rebuiltBytes) : ABSENT;
@@ -70504,10 +71209,10 @@ function findViolationsInSource(raw) {
70504
71209
  if (imported.size === 0) return [];
70505
71210
  const rawLines = raw.split("\n");
70506
71211
  const src = stripComments(raw);
70507
- const call = new RegExp(CALL_SOURCE, "g");
71212
+ const call3 = new RegExp(CALL_SOURCE, "g");
70508
71213
  const found = [];
70509
71214
  let m;
70510
- while (m = call.exec(src)) {
71215
+ while (m = call3.exec(src)) {
70511
71216
  const callee = m[2];
70512
71217
  if (!imported.has(callee)) continue;
70513
71218
  const open2 = m.index + m[0].length - 1;
@@ -70779,8 +71484,8 @@ function registerDeveloperCommands(program3) {
70779
71484
  return { commandPaths: new Set(manifest.index.map((entry) => entry.path)), commandOptions: commandOptions2, source: "in-process" };
70780
71485
  };
70781
71486
  const resolved = resolveCommandInventory({ root, from: o.commandsFrom, inProcess });
70782
- const { commandPaths, commandOptions } = resolved.inventory;
70783
- const deps = { commandPaths, commandOptions, base: o.base, runbooksOnly: o.runbooksOnly };
71487
+ const { commandPaths, commandOptions, source } = resolved.inventory;
71488
+ const deps = { commandPaths, commandOptions, commandSource: source, base: o.base, runbooksOnly: o.runbooksOnly };
70784
71489
  const result = o.live ? await runLiveDocRefs(root, deps, createRunbookLiveDeps(process.argv[1])) : runDocRefs(root, deps);
70785
71490
  result.warnings.push(...resolved.warnings);
70786
71491
  if (o.json) {
@@ -71477,7 +72182,20 @@ registerPreAction.call(program2, "preAction", async (_thisCommand, actionCommand
71477
72182
  appActorCommand = commandPath4;
71478
72183
  setAuditCommand(commandPath4);
71479
72184
  if (commandPath4 !== "whoami diagnose-hub") emitCliCallTelemetry(commandPath4);
71480
- if (commandPath4 === "ci cancel-orphans" || commandPath4 === "run cancel" || commandPath4 === "run rerun") return;
72185
+ const LAZY_APP_MINT_COMMANDS = /* @__PURE__ */ new Set([
72186
+ "ci cancel-orphans",
72187
+ "run cancel",
72188
+ "run rerun",
72189
+ // #9779/#9780: the vars verbs and archive-status validate --repo/name/value before the first
72190
+ // dispatch, so their scoped App mint waits for the action handler like the run writes above.
72191
+ "vars get",
72192
+ "vars list",
72193
+ "vars create",
72194
+ "vars update",
72195
+ "vars delete",
72196
+ "repo archive-status"
72197
+ ]);
72198
+ if (LAZY_APP_MINT_COMMANDS.has(commandPath4)) return;
71481
72199
  try {
71482
72200
  await activateAppActor(commandPath4, process.env, () => APP_WRITE_COMMANDS.has(commandPath4) ? mintCommandWriteToken(appActorDeps(), commandPath4) : mintInstallationToken(appActorDeps()));
71483
72201
  } catch (e) {
@@ -72319,6 +73037,15 @@ registerRunRerun(
72319
73037
  async () => activeAppToken() ?? (await mintCommandWriteToken(appActorDeps(), "run rerun")).token,
72320
73038
  async () => fetchProjectsList(registryClientDeps(await loadConfig()))
72321
73039
  );
73040
+ var varsAppClient = (commandPath4) => async () => (
73041
+ // The concrete client always provides restWithStatus (optional on the interface only so hand-built
73042
+ // test stubs of GitHubClient keep compiling).
73043
+ createGitHubClient({ token: async () => activeAppToken() ?? (await mintCommandWriteToken(appActorDeps(), commandPath4)).token })
73044
+ );
73045
+ var varsProjects = async () => fetchProjectsList(registryClientDeps(await loadConfig()));
73046
+ var varsActor = () => ({ credential: "app_installation", ...activeAppQuotaScope() ? { quotaScope: activeAppQuotaScope() } : {} });
73047
+ registerDevopsVarsCommands(program2, { appClient: (commandPath4) => varsAppClient(commandPath4)(), projects: varsProjects, actor: varsActor });
73048
+ registerRepoArchiveStatusCommand(program2, { appClient: (commandPath4) => varsAppClient(commandPath4)(), projects: varsProjects, actor: varsActor });
72322
73049
  ci.command("reconcile").description("audit + optionally apply merge settings and product ruleset activation (master-admin)").option("--json", "machine-readable output").option("--repo <owner/repo>", "reconcile one repo instead of the full registry").option("--apply", "PATCH merge settings + activate product ruleset when missing (master role required); activation is skipped while the repo's gate has never passed (#3694)").option("--park-ruleset", "stop the product ruleset enforcing without deleting it \u2014 keeps its required contexts (master role required)").action(async (o) => {
72323
73050
  if (o.apply && o.parkRuleset) {
72324
73051
  return fail("ci reconcile: --apply and --park-ruleset ask for opposite things; pass one");
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.6.22",
3
+ "version": "4.6.23",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/claude-plugin",
3
- "version": "4.6.22",
3
+ "version": "4.6.23",
4
4
  "description": "MMI workflow skills and org gates delivery.",
5
5
  "author": {
6
6
  "name": "MMI Future",