@mutmutco/codex-plugin 4.5.89 → 4.5.91

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
@@ -965,6 +965,8 @@ var init_house_map = __esm({
965
965
  train: "devops",
966
966
  suite: "devops",
967
967
  // #7264: cross-repo stage and served-version visibility
968
+ packages: "devops",
969
+ // #7833: consumer adoption of a verified package export
968
970
  "wave land": "devops",
969
971
  // the write-side serial merge train is shipping, not board observation
970
972
  bootstrap: "devops",
@@ -4745,7 +4747,7 @@ function useColor() {
4745
4747
  var program = new Command();
4746
4748
 
4747
4749
  // src/command-composition.ts
4748
- var import_node_fs53 = require("node:fs");
4750
+ var import_node_fs54 = require("node:fs");
4749
4751
  init_clean_exit();
4750
4752
  init_cli_shared();
4751
4753
 
@@ -5647,8 +5649,8 @@ function resolveVerbAliasShim(argv) {
5647
5649
  }
5648
5650
 
5649
5651
  // src/command-composition.ts
5650
- var import_node_os23 = require("node:os");
5651
- var import_node_path52 = require("node:path");
5652
+ var import_node_os24 = require("node:os");
5653
+ var import_node_path53 = require("node:path");
5652
5654
 
5653
5655
  // src/board-read.ts
5654
5656
  var import_node_fs13 = require("node:fs");
@@ -5792,7 +5794,14 @@ function decidePrMergeCleanupGate(input) {
5792
5794
  message: `could not read the PR's state after the merge attempt${input.stateError ? `: ${input.stateError}` : ""} \u2014 cleanup skipped because the merge is unproven`
5793
5795
  };
5794
5796
  }
5795
- if (input.autoRequested) return { action: "enqueued" };
5797
+ if (input.autoRequested && state === "OPEN" && (input.autoMergeRequest ?? "").trim()) return { action: "enqueued" };
5798
+ if (input.autoRequested && state === "OPEN") {
5799
+ return {
5800
+ action: "refuse",
5801
+ reason: "not-merged",
5802
+ message: "the PR is OPEN and GitHub shows no queued auto-merge (or it could not be read) \u2014 the merge is neither done nor proven enqueued, so the branch and worktree were left untouched"
5803
+ };
5804
+ }
5796
5805
  return {
5797
5806
  action: "refuse",
5798
5807
  reason: "not-merged",
@@ -14524,6 +14533,24 @@ async function activateProductRuleset(repo, rulesetBody, client, enforcement = "
14524
14533
  await client.rest("POST", `repos/${repo}/rulesets`, { body: { ...rulesetBody, enforcement }, timeoutMs: 2e4 });
14525
14534
  return { action: "created", enforcement };
14526
14535
  }
14536
+ async function gateContextEnforced(repo, rest) {
14537
+ try {
14538
+ const list = await rest(`repos/${repo}/rulesets?includes_parents=true`);
14539
+ for (const entry of list) {
14540
+ if (entry.id == null) continue;
14541
+ const rs = await rest(`repos/${repo}/rulesets/${entry.id}`);
14542
+ if (rs.target !== "branch" || rs.enforcement !== "active") continue;
14543
+ const hit = (rs.rules ?? []).some((r) => r.type === "required_status_checks" && (r.parameters?.required_status_checks ?? []).some((c) => c.context === PRODUCT_GATE_CONTEXT));
14544
+ if (hit) return true;
14545
+ }
14546
+ return false;
14547
+ } catch {
14548
+ return false;
14549
+ }
14550
+ }
14551
+ async function propagateMayAutoMerge(repo, repoClass, rest) {
14552
+ return repoClass === "content" || gateContextEnforced(repo, rest);
14553
+ }
14527
14554
 
14528
14555
  // src/bootstrap-apply.ts
14529
14556
  var import_node_crypto5 = require("node:crypto");
@@ -17225,10 +17252,10 @@ var rollout_plan_default = {
17225
17252
  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)."
17226
17253
  },
17227
17254
  baseline: {
17228
- version: "4.5.89",
17229
- tag: "v4.5.89",
17230
- commit: "3288efa7a0af",
17231
- npm: "@mutmutco/cli@4.5.89"
17255
+ version: "4.5.91",
17256
+ tag: "v4.5.91",
17257
+ commit: "086f604f627d",
17258
+ npm: "@mutmutco/cli@4.5.91"
17232
17259
  },
17233
17260
  exitCriterion: "fleet-n-of-n",
17234
17261
  hubOnlyShortcut: "forbidden",
@@ -17245,14 +17272,14 @@ var rollout_plan_default = {
17245
17272
  repo: "mutmutco/mmi-hub",
17246
17273
  role: "canary",
17247
17274
  schedule: "train",
17248
- v3Target: "v4.5.89"
17275
+ v3Target: "v4.5.91"
17249
17276
  }
17250
17277
  ],
17251
17278
  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.",
17252
17279
  rollback: {
17253
17280
  independent: true,
17254
- mechanism: "npm dist-tag latest -> 4.5.89 and redeploy the Hub Lambda from tag v4.5.89 (3288efa7a0af); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
17255
- v3Target: "v4.5.89 (@mutmutco/cli@4.5.89, tag commit 3288efa7a0af \u2014 last known-good release carrying the repo-index v4-only contract)"
17281
+ mechanism: "npm dist-tag latest -> 4.5.91 and redeploy the Hub Lambda from tag v4.5.91 (086f604f627d); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
17282
+ v3Target: "v4.5.91 (@mutmutco/cli@4.5.91, tag commit 086f604f627d \u2014 last known-good release carrying the repo-index v4-only contract)"
17256
17283
  }
17257
17284
  },
17258
17285
  {
@@ -28972,7 +28999,7 @@ var PRIMARY_GROUPS = [
28972
28999
  ["Setup and support", ["bootstrap", "secrets", "docs", "tests", "spawn", "dist"]],
28973
29000
  ["Coordinate and improve", ["wave", "report", "skill-lesson", "closure-rate"]]
28974
29001
  ];
28975
- var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set(["org", "runtime", "plugin"]);
29002
+ var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set(["org", "runtime", "plugin", "packages"]);
28976
29003
  var CAMPAIGN_ORCHESTRATOR_TOP_LEVEL = /* @__PURE__ */ new Set(["pickup"]);
28977
29004
  var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "tests", "spawn", "dist", "wave", "report", "skill-lesson", "closure-rate"]);
28978
29005
  var TOP_LEVEL_ORDER = /* @__PURE__ */ new Map();
@@ -29020,6 +29047,8 @@ var COMMAND_OWNERSHIP = {
29020
29047
  train: { module_owner: "cli/src/train-commands.ts", consumer: "release-operator" },
29021
29048
  suite: { module_owner: "cli/src/suite-commands.ts", consumer: "release-operator" },
29022
29049
  // #7264
29050
+ packages: { module_owner: "cli/src/packages-adopt-export.ts", consumer: "agent-workflow" },
29051
+ // #7833
29023
29052
  bootstrap: { module_owner: "cli/src/bootstrap-commands.ts", consumer: "repo-bootstrap" },
29024
29053
  secrets: { module_owner: "cli/src/secrets-commands.ts", consumer: "authenticated-operator" },
29025
29054
  docs: { module_owner: "cli/src/docs-index-command.ts", consumer: "repo-gates" },
@@ -30595,16 +30624,6 @@ function loadPortRegistry(path2) {
30595
30624
  }
30596
30625
  return out;
30597
30626
  }
30598
- function ensurePortRange(repo, path2) {
30599
- const registry2 = loadPortRegistry(path2);
30600
- const existing = registry2[repo];
30601
- if (existing) return existing;
30602
- const range = nextPortBlock(registry2);
30603
- const raw = (0, import_node_fs25.existsSync)(path2) ? JSON.parse((0, import_node_fs25.readFileSync)(path2, "utf8")) : {};
30604
- raw[repo] = range;
30605
- (0, import_node_fs25.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
30606
- return range;
30607
- }
30608
30627
  function portCursorSeed(registry2) {
30609
30628
  return nextPortBlock(registry2)[0];
30610
30629
  }
@@ -30645,7 +30664,7 @@ function resolvePortRangeInfra(cwd, packageDir) {
30645
30664
  }
30646
30665
  return null;
30647
30666
  }
30648
- async function ensurePortRangeAtomic(repo, path2, allocate, opts = {}) {
30667
+ async function ensurePortRangeAtomic(repo, path2, allocate) {
30649
30668
  const registry2 = loadPortRegistry(path2);
30650
30669
  const existing = existingPortRange(repo, registry2);
30651
30670
  if (existing) return { range: existing, source: "existing" };
@@ -30654,8 +30673,9 @@ async function ensurePortRangeAtomic(repo, path2, allocate, opts = {}) {
30654
30673
  const range = await allocate(seed);
30655
30674
  return { range, source: "ddb" };
30656
30675
  } catch (e) {
30657
- if (!opts.quiet) console.warn(`port-registry: DDB allocator unreachable, falling back to committed file (${e.message})`);
30658
- return { range: ensurePortRange(repo, path2), source: "file" };
30676
+ const why = e.message;
30677
+ const missing = /@aws-sdk\/[\w-]+/.exec(why)?.[0];
30678
+ throw new Error(`DDB port allocator failed for ${repo} \u2014 refusing to fall back to the frozen ${path2} (it would hand out a block the cursor already gave away). ` + (missing && /cannot find|not found|ERR_MODULE_NOT_FOUND/i.test(why) ? `Missing dependency ${missing}: run \`npm ci\` in ${(0, import_node_path25.dirname)(path2)}, then retry.` : "Check AWS credentials and that the registry table is reachable, then retry.") + ` Cause: ${why}`);
30659
30679
  }
30660
30680
  }
30661
30681
 
@@ -30683,7 +30703,13 @@ async function assignPersistedPortRange(repo, slug, reg, opts) {
30683
30703
  if (!Array.isArray(parsed.range) || parsed.range.length !== 2) throw new Error("port-ddb: no range in output");
30684
30704
  return parsed.range;
30685
30705
  };
30686
- const { range: [start, end], source } = await ensurePortRangeAtomic(repo, path2, allocate);
30706
+ let result;
30707
+ try {
30708
+ result = await ensurePortRangeAtomic(repo, path2, allocate);
30709
+ } catch (e) {
30710
+ return { ok: false, error: e.message };
30711
+ }
30712
+ const { range: [start, end], source } = result;
30687
30713
  const write = await upsertProject(slug, { portRange: { start, end } }, reg);
30688
30714
  if (!write.ok && source === "ddb") {
30689
30715
  return {
@@ -34144,7 +34170,7 @@ async function prepareTargetedSeedBranch(repo, plan, target, client, gh2) {
34144
34170
  }
34145
34171
  await verifyTargetedSeedBranch(repo, plan, target, client);
34146
34172
  }
34147
- async function enableTargetedSeedAutoMerge(repo, number, plan, target, expected, client, gh2) {
34173
+ async function verifyTargetedSeedPr(repo, number, plan, target, expected, client) {
34148
34174
  const head = await verifyTargetedSeedBranch(repo, plan, target, client);
34149
34175
  const pr = await client.rest("GET", `repos/${repo}/pulls/${number}`);
34150
34176
  if (pr?.state !== "open" || pr.head?.ref !== plan.branch || pr.head?.sha !== head || pr.head.repo?.full_name?.toLowerCase() !== repo.toLowerCase() || pr.base?.ref !== plan.baseBranch || pr.base.sha !== plan.baseSha || pr.base.repo?.full_name?.toLowerCase() !== repo.toLowerCase() || pr.changed_files !== 1) throw new Error("bootstrap apply: scoped seed PR identity or diff changed");
@@ -34159,7 +34185,7 @@ async function enableTargetedSeedAutoMerge(repo, number, plan, target, expected,
34159
34185
  if (file?.content !== expected) {
34160
34186
  throw new Error("bootstrap apply: scoped seed content differs from this invocation");
34161
34187
  }
34162
- await gh2(["pr", "merge", String(number), "--repo", repo, "--auto", "--squash", "--match-head-commit", head]);
34188
+ return head;
34163
34189
  }
34164
34190
 
34165
34191
  // src/bootstrap-commands.ts
@@ -34843,8 +34869,6 @@ function registerBootstrapCommands(program3) {
34843
34869
  let seedPrUrl;
34844
34870
  const scopedHead = o.execute && scopedPlan?.branch && configGateAction?.action !== "skip" ? await verifyTargetedSeedBranch(repo, scopedPlan, onlyTarget, controlClient) : null;
34845
34871
  if (o.execute && seedPlan.mode === "pr" && seedPlan.branch && configGateAction?.action !== "skip" && (seededToBranch > 0 || scopedContent !== void 0 && scopedHead !== null && scopedHead !== scopedPlan?.baseSha)) {
34846
- await gh2(["api", "-X", "PATCH", `repos/${repo}`, "-f", "allow_auto_merge=true", "-f", "allow_squash_merge=true", "-F", `delete_branch_on_merge=${MANAGED_DELETE_BRANCH_ON_MERGE}`]).catch(() => {
34847
- });
34848
34872
  const openPrs = await gh2(["pr", "list", "--repo", repo, "--head", seedPlan.branch, "--base", baseBranch, "--state", "open", "--json", "number,url"]);
34849
34873
  const prDecision = decideSeedPrAction(JSON.parse(openPrs.stdout || "[]"));
34850
34874
  if (prDecision.action === "reuse") {
@@ -34870,22 +34894,13 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
34870
34894
  ]), "bootstrap apply pr create");
34871
34895
  seedPrUrl = created.url;
34872
34896
  }
34873
- let autoMergeEnabled = true;
34897
+ const number = Number(seedPrUrl.split("/").pop());
34874
34898
  if (scopedPlan) {
34875
34899
  if (scopedContent === void 0) throw new Error("bootstrap apply: scoped seed content is unresolved");
34876
- const number = Number(seedPrUrl.split("/").pop());
34877
34900
  if (!Number.isInteger(number) || number <= 0) throw new Error("bootstrap apply: scoped seed PR number is unreadable");
34878
- await enableTargetedSeedAutoMerge(repo, number, scopedPlan, onlyTarget, scopedContent, controlClient, gh2);
34879
- } else await gh2(["pr", "merge", seedPrUrl, "--repo", repo, "--auto", "--squash"]).catch((e) => {
34880
- const message2 = String(e.message ?? "");
34881
- if (/already/i.test(message2)) return;
34882
- if (/clean status|enablePullRequestAutoMerge/i.test(message2)) {
34883
- autoMergeEnabled = false;
34884
- return;
34885
- }
34886
- throw e;
34887
- });
34888
- applied.push(autoMergeEnabled ? `seed: PR ${seedPrUrl} (base ${baseBranch} protected; auto-merge enabled)` : `seed: PR ${seedPrUrl} (base ${baseBranch} protected; auto-merge refused \u2014 PR is already clean. Land it: mmi-cli devops pr land <n> --repo ${repo})`);
34901
+ await verifyTargetedSeedPr(repo, number, scopedPlan, onlyTarget, scopedContent, controlClient);
34902
+ }
34903
+ applied.push(`seed: PR ${seedPrUrl} (base ${baseBranch} protected; left open, never merged by apply \u2014 land it once its gate is green: mmi-cli devops pr land ${number} --repo ${repo})`);
34889
34904
  }
34890
34905
  if (o.execute && !onlyTarget) {
34891
34906
  const desiredBranches = branchesForTrack(effectiveTrack);
@@ -34932,6 +34947,7 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
34932
34947
  applied.push(`has_wiki=false (failed: ${e.message})`);
34933
34948
  }
34934
34949
  }
34950
+ const deferredFailures = [];
34935
34951
  if (o.execute && !onlyTarget && o.class === "deployable") {
34936
34952
  try {
34937
34953
  await gh2(["api", "-X", "PATCH", `repos/${repo}`, "-f", "allow_auto_merge=true", "-f", "allow_squash_merge=true", "-F", `delete_branch_on_merge=${MANAGED_DELETE_BRANCH_ON_MERGE}`]);
@@ -34947,9 +34963,7 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
34947
34963
  const client = controlClient;
34948
34964
  const gateFacts = await readBootstrapGitHubFacts(repo, effectiveTrack, client);
34949
34965
  const rulesetBlockers = bootstrapControlWriteBlockers(gateFacts, true);
34950
- if (rulesetBlockers.length) {
34951
- return failGraceful(`bootstrap apply: product ruleset facts became unreadable before PUT; refusing write: ${rulesetBlockers.join("; ")}`);
34952
- }
34966
+ if (rulesetBlockers.length) throw new Error(`facts became unreadable before PUT; refusing write: ${rulesetBlockers.join("; ")}`);
34953
34967
  const activation = await activateProductRuleset(
34954
34968
  repo,
34955
34969
  stripRulesetComment(rulesetContent),
@@ -34960,7 +34974,8 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
34960
34974
  `product ruleset: ${activation.action} (${activation.enforcement}${activation.detail ? `; ${activation.detail}` : ""})` + (activation.enforcement === "active" ? "" : ` \u2014 ${PRODUCT_GATE_PATH2} has not emitted a proven-green context on ${baseBranch}, so it is NOT enforcing. Re-run bootstrap apply after the gate passes; ci reconcile --apply remains the supported fallback.`)
34961
34975
  );
34962
34976
  } catch (e) {
34963
- return failGraceful(`bootstrap apply: product ruleset activation failed: ${e.message}`);
34977
+ deferredFailures.push(`product ruleset not written (${e.message}) \u2014 re-run bootstrap apply once ${PRODUCT_GATE_PATH2} has a completed run`);
34978
+ applied.push(`product ruleset (REFUSED: ${e.message})`);
34964
34979
  }
34965
34980
  }
34966
34981
  }
@@ -35047,6 +35062,7 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
35047
35062
  );
35048
35063
  } else {
35049
35064
  applied.push(`portRange (failed: ${assigned.error})`);
35065
+ deferredFailures.push(`portRange not assigned: ${assigned.error}`);
35050
35066
  }
35051
35067
  }
35052
35068
  } else {
@@ -35124,6 +35140,8 @@ ${renderBootstrapAncillaryPlan(ancillaryPlan, environmentPlan, repoEnvironmentSp
35124
35140
  LIVE apply to ${repo}:
35125
35141
  ${applied.join("\n ")}`);
35126
35142
  }
35143
+ if (deferredFailures.length) return failGraceful(`bootstrap apply: incomplete \u2014 every other step ran:
35144
+ ${deferredFailures.join("\n ")}`);
35127
35145
  });
35128
35146
  bootstrap.command("propagate").description("#4238: re-entrant canary\u2192wave tick \u2014 plan (or, with --execute, open) per-repo PRs fanning an org-owned seed out to the fleet").option("--target <path>", "the manifest target to propagate (an org-owned whole file or declared Hub-managed block)").option("--execute", "LIVE tick via gh (master-gated) \u2014 opens/reuses per-repo seed-propagate PRs; dry-run prints the plan only").option("--json", "machine-readable output").action(async () => {
35129
35147
  const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
@@ -35371,8 +35389,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
35371
35389
  prUrl = created.url;
35372
35390
  }
35373
35391
  if (rec.wave !== 0) {
35374
- await gh2(["pr", "merge", prUrl, "--repo", rec.repo, "--auto", "--squash"]).catch(() => {
35392
+ const enforced = await propagateMayAutoMerge(rec.repo, classOf(rec.repo), async (path2) => JSON.parse((await gh2(["api", path2])).stdout || "[]"));
35393
+ if (enforced) await gh2(["pr", "merge", prUrl, "--repo", rec.repo, "--auto", "--squash"]).catch(() => {
35375
35394
  });
35395
+ else console.error(`bootstrap propagate: ${rec.repo} does not enforce the gate \u2014 auto-merge not armed; land it with: mmi-cli devops pr land ${prUrl.split("/").pop()} --repo ${rec.repo}`);
35376
35396
  }
35377
35397
  rec.prUrl = prUrl;
35378
35398
  }
@@ -37054,9 +37074,9 @@ A hotfix branch is cut from origin/main and carries ONLY the fixes --from names,
37054
37074
  async function preflightHotfixBatchPicks(deps, sources, pickTolerated) {
37055
37075
  if (sources.length === 0) return { failures: [], skipped: [] };
37056
37076
  const { mkdtemp: mkdtemp2, rm: rm2 } = await import("node:fs/promises");
37057
- const { tmpdir: tmpdir11 } = await import("node:os");
37058
- const { join: join46 } = await import("node:path");
37059
- const dir = await mkdtemp2(join46(tmpdir11(), "mmi-hotfix-preflight-"));
37077
+ const { tmpdir: tmpdir12 } = await import("node:os");
37078
+ const { join: join47 } = await import("node:path");
37079
+ const dir = await mkdtemp2(join47(tmpdir12(), "mmi-hotfix-preflight-"));
37060
37080
  const wt = { run: (file, args) => deps.run(file, file === "git" ? ["-C", dir, ...args] : args) };
37061
37081
  const failures = [];
37062
37082
  const skipped = [];
@@ -39548,6 +39568,11 @@ function basePolicyBlocksImmediateMerge(message2) {
39548
39568
  return /base branch policy prohibits|protected branch|required status check|merge queue/i.test(message2);
39549
39569
  }
39550
39570
  var PR_MERGE_ENQUEUED_EXIT_CODE = 2;
39571
+ function prMergeGateExitCode(gate, explicitAuto) {
39572
+ if (gate.action === "cleanup") return void 0;
39573
+ if (gate.action === "enqueued") return explicitAuto ? void 0 : PR_MERGE_ENQUEUED_EXIT_CODE;
39574
+ return 1;
39575
+ }
39551
39576
  function ghPrMergeLocalBranchDeleteWarning(message2) {
39552
39577
  return /used by worktree|cannot delete branch/i.test(message2);
39553
39578
  }
@@ -45745,19 +45770,23 @@ ${list}`);
45745
45770
  return snapshot.merged ? "MERGED" : snapshot.state.toUpperCase();
45746
45771
  })
45747
45772
  );
45773
+ const autoRequested = Boolean(o.auto || upgradedToAuto);
45774
+ const autoMergeRequest = autoRequested && stateRead.ok && stateRead.state.trim().toUpperCase() === "OPEN" ? (await execFileP("gh", ["pr", "view", number, ...repoArgs, "--json", "autoMergeRequest", "--jq", '.autoMergeRequest // ""'], { timeout: GC_GH_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() : void 0;
45748
45775
  const gate = decidePrMergeCleanupGate({
45749
45776
  state: stateRead.ok ? stateRead.state : void 0,
45750
45777
  stateError: stateRead.ok ? void 0 : stateRead.error,
45751
- autoRequested: Boolean(o.auto || upgradedToAuto)
45778
+ autoRequested,
45779
+ autoMergeRequest
45752
45780
  });
45781
+ const gateExitCode = prMergeGateExitCode(gate, Boolean(o.auto));
45753
45782
  if (gate.action === "enqueued") {
45754
45783
  const state = stateRead.ok ? stateRead.state : "";
45755
45784
  const enqueued = describePrMergeEnqueuedReason(await pollGhPrChecks(number, repoArgs).catch(() => void 0));
45756
45785
  console.log(JSON.stringify({ mergeStatus: "auto-merge-enqueued", enqueuedReason: enqueued.reason, pr: number, branch: headRef, state: state || "unknown", upgradedToAuto: upgradedToAuto || void 0 }));
45757
45786
  console.warn(`pr merge: PR #${number} is ENQUEUED, not merged \u2014 ${enqueued.message}.`);
45758
- if (upgradedToAuto && !o.auto) {
45759
- console.warn(`pr merge: exiting ${PR_MERGE_ENQUEUED_EXIT_CODE} so a chained command does not treat this as a completed merge.`);
45760
- process.exitCode = PR_MERGE_ENQUEUED_EXIT_CODE;
45787
+ if (gateExitCode !== void 0) {
45788
+ console.warn(`pr merge: exiting ${gateExitCode} so a chained command does not treat this as a completed merge.`);
45789
+ process.exitCode = gateExitCode;
45761
45790
  }
45762
45791
  return;
45763
45792
  }
@@ -45772,7 +45801,7 @@ ${list}`);
45772
45801
  ...branchUpdated ? { branchUpdated } : {}
45773
45802
  }));
45774
45803
  console.error(`pr merge: ${gate.message}. Nothing was deleted \u2014 re-check the PR and retry.`);
45775
- process.exitCode = 1;
45804
+ process.exitCode = gateExitCode;
45776
45805
  return;
45777
45806
  }
45778
45807
  const primaryRoot = beforeWorktrees[0]?.path ?? (startingPath || process.cwd());
@@ -47591,16 +47620,732 @@ function suiteStatusDeps(github, registry2) {
47591
47620
  };
47592
47621
  }
47593
47622
 
47623
+ // src/packages-adopt-export.ts
47624
+ var import_node_child_process18 = require("node:child_process");
47625
+ var import_node_fs45 = require("node:fs");
47626
+ var import_node_os21 = require("node:os");
47627
+ var import_node_path44 = require("node:path");
47628
+ var import_node_util6 = require("node:util");
47629
+ init_cli_shared();
47630
+
47631
+ // ../scripts/package_export.py
47632
+ var package_export_default = `#!/usr/bin/env python3
47633
+ """Export and offline verification of exact original package registry archives (#7805)."""
47634
+ import base64
47635
+ import binascii
47636
+ from datetime import datetime, timedelta, timezone
47637
+ import gzip
47638
+ import hashlib
47639
+ import hmac
47640
+ import io
47641
+ import json
47642
+ import os
47643
+ import pathlib
47644
+ import re
47645
+ import stat
47646
+ import struct
47647
+ import sys
47648
+ import tarfile
47649
+ import zipfile
47650
+ import zlib
47651
+ import urllib.error
47652
+ import urllib.request
47653
+ from urllib.parse import urlsplit
47654
+
47655
+ PACKAGES = ("@mutmutco/mmwork-ds", "@mutmutco/mmwork-screens")
47656
+ MAX_METADATA_BYTES = 2 * 1024 * 1024
47657
+ MAX_TARBALL_BYTES = 64 * 1024 * 1024
47658
+ MAX_ARTIFACT_BYTES = 160 * 1024 * 1024
47659
+ MAX_ZIP_DIRECTORY_BYTES = 8192
47660
+ MAX_UNPACKED_BYTES = 256 * 1024 * 1024
47661
+ MAX_MEMBERS = 10000 # Yielded entries; tarfile consumes PAX/GNU extension headers internally.
47662
+ MAX_PATH_LENGTH = 1024
47663
+ MAX_PATH_DEPTH = 32
47664
+ MAX_COMPONENT_LENGTH = 255
47665
+ WINDOWS_DEVICE = re.compile(r"con|conin\\$|conout\\$|prn|aux|nul|com[1-9]|lpt[1-9]", re.IGNORECASE)
47666
+ EXACT_VERSION = re.compile(
47667
+ r"(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)"
47668
+ r"(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?"
47669
+ r"(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?"
47670
+ )
47671
+
47672
+
47673
+ def reject_constant(_):
47674
+ raise ValueError("nonstandard-json")
47675
+
47676
+
47677
+ def unique_object(pairs):
47678
+ result = {}
47679
+ for key, value in pairs:
47680
+ if key in result:
47681
+ raise ValueError("registry-metadata")
47682
+ result[key] = value
47683
+ return result
47684
+
47685
+
47686
+ def verify_artifact_digest(provider_digest, zip_bytes):
47687
+ """Bind whole bytes to an independently trusted provider digest, not an embedded claim.
47688
+
47689
+ This does not authenticate the digest's source or validate ZIP members.
47690
+ """
47691
+ if (type(zip_bytes) is not bytes or not 0 < len(zip_bytes) <= MAX_ARTIFACT_BYTES
47692
+ or type(provider_digest) is not str
47693
+ or not re.fullmatch(r"sha256:[0-9a-f]{64}", provider_digest)):
47694
+ raise ValueError("artifact-integrity")
47695
+ digest = hashlib.sha256(zip_bytes).hexdigest()
47696
+ if not hmac.compare_digest(provider_digest[7:], digest):
47697
+ raise ValueError("artifact-integrity")
47698
+ return {"artifactSha256": digest, "artifactBytes": len(zip_bytes)}
47699
+
47700
+
47701
+ def verify_artifact_receipt(provider_bytes, zip_bytes, expected, now):
47702
+ """Check separately supplied provider metadata against trusted external selectors.
47703
+
47704
+ The caller must obtain metadata and selectors independently of the artifact.
47705
+ This cannot authenticate their origin or establish workflow/requester authority.
47706
+ Optional node_id, url, archive_download_url and updated_at are ignored, not validated.
47707
+ """
47708
+ keys = {"artifactId", "artifactName", "repositoryId", "runId", "headSha", "headBranch", "digest"}
47709
+ try:
47710
+ if type(expected) is not dict:
47711
+ raise ValueError()
47712
+ expected = expected.copy()
47713
+ if set(expected) != keys:
47714
+ raise ValueError()
47715
+ if any(type(expected[key]) is not int or not 0 < expected[key] < (1 << 53)
47716
+ for key in ("artifactId", "repositoryId", "runId")):
47717
+ raise ValueError()
47718
+ if (type(expected["headSha"]) is not str or not re.fullmatch(r"[0-9a-f]{40}", expected["headSha"])
47719
+ or any(type(expected[key]) is not str or not 0 < len(expected[key]) <= 128
47720
+ or any(ord(char) < 33 or ord(char) > 126 for char in expected[key])
47721
+ for key in ("artifactName", "headBranch"))):
47722
+ raise ValueError()
47723
+ if type(provider_bytes) is not bytes or not 0 < len(provider_bytes) <= MAX_METADATA_BYTES:
47724
+ raise ValueError()
47725
+ provider = json.loads(provider_bytes.decode("utf-8"), object_pairs_hook=unique_object,
47726
+ parse_constant=reject_constant)
47727
+ required = {"id", "name", "size_in_bytes", "digest", "expired", "created_at", "expires_at", "workflow_run"}
47728
+ if (type(provider) is not dict or not required <= set(provider)
47729
+ or set(provider) - required - {"node_id", "url", "archive_download_url", "updated_at"}):
47730
+ raise ValueError()
47731
+ run = provider["workflow_run"]
47732
+ if (type(run) is not dict or set(run) != {"id", "repository_id", "head_repository_id", "head_sha", "head_branch"}
47733
+ or provider["expired"] is not False):
47734
+ raise ValueError()
47735
+ if type(now) is not datetime or now.utcoffset() != timedelta(0):
47736
+ raise ValueError()
47737
+ dates = []
47738
+ for key in ("created_at", "expires_at"):
47739
+ value = provider[key]
47740
+ if type(value) is not str or not re.fullmatch(r"[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z", value):
47741
+ raise ValueError()
47742
+ dates.append(datetime.strptime(value, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc))
47743
+ created, expires = dates
47744
+ if not created <= now < expires or not timedelta(0) < expires - created <= timedelta(days=1):
47745
+ raise ValueError()
47746
+ for actual, wanted in [(provider["id"], expected["artifactId"]),
47747
+ (run["id"], expected["runId"]),
47748
+ (run["repository_id"], expected["repositoryId"]),
47749
+ (run["head_repository_id"], expected["repositoryId"])]:
47750
+ if type(actual) is not int or actual != wanted:
47751
+ raise ValueError()
47752
+ for actual, wanted in [(provider["name"], expected["artifactName"]),
47753
+ (provider["digest"], expected["digest"]),
47754
+ (run["head_sha"], expected["headSha"]),
47755
+ (run["head_branch"], expected["headBranch"])]:
47756
+ if type(actual) is not str or actual != wanted:
47757
+ raise ValueError()
47758
+ receipt = verify_artifact_digest(expected["digest"], zip_bytes)
47759
+ if type(provider["size_in_bytes"]) is not int or provider["size_in_bytes"] != len(zip_bytes):
47760
+ raise ValueError()
47761
+ except (ValueError, RecursionError):
47762
+ raise ValueError("artifact-receipt") from None
47763
+ return {**expected, **receipt}
47764
+
47765
+
47766
+ def inspect_artifact_zip(zip_bytes):
47767
+ """Read five proposed export members in memory without extracting or executing.
47768
+
47769
+ Authenticate whole artifact bytes before this stage. Returned member bytes still
47770
+ require registry integrity checks; the manifest is untrusted, not an authority.
47771
+ Byte/output limits are not a total process-memory budget, including codec remainders.
47772
+ """
47773
+ limits = {"manifest.json": MAX_METADATA_BYTES,
47774
+ "mmwork-ds.metadata.json": MAX_METADATA_BYTES, "mmwork-ds.tgz": MAX_TARBALL_BYTES,
47775
+ "mmwork-screens.metadata.json": MAX_METADATA_BYTES, "mmwork-screens.tgz": MAX_TARBALL_BYTES}
47776
+ try:
47777
+ if type(zip_bytes) is not bytes or not 0 < len(zip_bytes) <= MAX_ARTIFACT_BYTES:
47778
+ raise ValueError()
47779
+ stream = io.BytesIO(zip_bytes)
47780
+ # ZipFile allocates every central-directory entry before application checks.
47781
+ # Use stdlib's bounded EOCD reader and struct definitions for a strict profile audit.
47782
+ end = zipfile._EndRecData(stream)
47783
+ if (end is None or end[zipfile._ECD_SIGNATURE] != zipfile.stringEndArchive
47784
+ or end[zipfile._ECD_DISK_NUMBER] != 0
47785
+ or end[zipfile._ECD_DISK_START] != 0
47786
+ or end[zipfile._ECD_ENTRIES_THIS_DISK] != len(limits)
47787
+ or end[zipfile._ECD_ENTRIES_TOTAL] != len(limits)
47788
+ or not 0 < end[zipfile._ECD_SIZE] <= MAX_ZIP_DIRECTORY_BYTES
47789
+ or end[zipfile._ECD_COMMENT_SIZE] != 0
47790
+ or end[zipfile._ECD_LOCATION] + zipfile.sizeEndCentDir != len(zip_bytes)
47791
+ or end[zipfile._ECD_OFFSET] + end[zipfile._ECD_SIZE] != end[zipfile._ECD_LOCATION]):
47792
+ raise ValueError()
47793
+ directory = memoryview(zip_bytes)[end[zipfile._ECD_OFFSET]:end[zipfile._ECD_LOCATION]]
47794
+ allowed = {name.encode("ascii") for name in limits}
47795
+ seen = set()
47796
+ cursor = 0
47797
+ # ZipFile accepts short variable-field reads, so audit declared boundaries first.
47798
+ while cursor < len(directory):
47799
+ fields = struct.unpack_from(zipfile.structCentralDir, directory, cursor)
47800
+ start = cursor + zipfile.sizeCentralDir
47801
+ stop = start + fields[zipfile._CD_FILENAME_LENGTH]
47802
+ if (fields[zipfile._CD_SIGNATURE] != zipfile.stringCentralDir
47803
+ or fields[zipfile._CD_EXTRA_FIELD_LENGTH] != 0 or fields[zipfile._CD_COMMENT_LENGTH] != 0
47804
+ or stop > len(directory)):
47805
+ raise ValueError()
47806
+ name = bytes(directory[start:stop])
47807
+ if name not in allowed or name in seen:
47808
+ raise ValueError()
47809
+ seen.add(name)
47810
+ cursor = stop
47811
+ if cursor != len(directory) or seen != allowed:
47812
+ raise ValueError()
47813
+ with zipfile.ZipFile(stream, "r") as bundle:
47814
+ entries = bundle.infolist()
47815
+ if (bundle.start_dir != end[zipfile._ECD_OFFSET]
47816
+ or len(entries) != len(limits) or {entry.orig_filename for entry in entries} != set(limits)
47817
+ or min(entry.header_offset for entry in entries) != 0):
47818
+ raise ValueError()
47819
+ result = {}
47820
+ entries.sort(key=lambda entry: entry.header_offset)
47821
+ for index, entry in enumerate(entries):
47822
+ if (entry.filename != entry.orig_filename or entry.is_dir()
47823
+ or stat.S_IFMT(entry.external_attr >> 16) not in (0, stat.S_IFREG)
47824
+ or entry.external_attr & 0x10 or entry.volume != 0
47825
+ or entry.flag_bits & ~(0x8 | 0x800)
47826
+ or entry.compress_type not in (zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED)
47827
+ or entry.comment or entry.extra
47828
+ or not 0 < entry.file_size <= limits[entry.filename]
47829
+ or not 0 < entry.compress_size <= MAX_ARTIFACT_BYTES):
47830
+ raise ValueError()
47831
+ header = entry.FileHeader(zip64=False)
47832
+ start = entry.header_offset + len(header)
47833
+ data_end = start + entry.compress_size
47834
+ next_offset = entries[index + 1].header_offset if index + 1 < len(entries) else bundle.start_dir
47835
+ if zip_bytes[entry.header_offset:start] != header or data_end > next_offset:
47836
+ raise ValueError()
47837
+ span = next_offset - data_end
47838
+ if span not in ((12, 16) if entry.flag_bits & 0x8 else (0,)):
47839
+ raise ValueError()
47840
+ descriptor = zip_bytes[data_end:next_offset]
47841
+ if entry.flag_bits & 0x8:
47842
+ fields = struct.pack("<III", entry.CRC, entry.compress_size, entry.file_size)
47843
+ if descriptor not in (fields, b"PK\\x07\\x08" + fields):
47844
+ raise ValueError()
47845
+ elif descriptor:
47846
+ raise ValueError()
47847
+ compressed = memoryview(zip_bytes)[start:data_end]
47848
+ if entry.compress_type == zipfile.ZIP_STORED:
47849
+ if entry.compress_size != entry.file_size:
47850
+ raise ValueError()
47851
+ payload = bytes(compressed)
47852
+ else:
47853
+ codec = zlib.decompressobj(wbits=-15)
47854
+ payload = codec.decompress(compressed, entry.file_size + 1)
47855
+ # ZipExtFile clips to the declared size and may ignore trailing data.
47856
+ if not codec.eof or codec.unused_data or codec.unconsumed_tail:
47857
+ raise ValueError()
47858
+ if len(payload) != entry.file_size or zlib.crc32(payload) != entry.CRC:
47859
+ raise ValueError()
47860
+ result[entry.filename] = payload
47861
+ return result
47862
+ except (ValueError, OSError, EOFError, NotImplementedError, zipfile.BadZipFile, zlib.error, struct.error):
47863
+ raise ValueError("artifact-members") from None
47864
+
47865
+
47866
+ def inspect_archive(name, version, tarball_bytes):
47867
+ seen = set()
47868
+ files = set()
47869
+ directories = set()
47870
+ package = None
47871
+ end = 0
47872
+ try:
47873
+ with gzip.GzipFile(fileobj=io.BytesIO(tarball_bytes)) as compressed:
47874
+ unpacked = compressed.read(MAX_UNPACKED_BYTES + 1)
47875
+ if len(unpacked) > MAX_UNPACKED_BYTES:
47876
+ raise ValueError()
47877
+ with tarfile.open(fileobj=io.BytesIO(unpacked), mode="r:") as archive:
47878
+ for index, entry in enumerate(archive):
47879
+ if (index >= MAX_MEMBERS or entry.size < 0 or entry.size > MAX_UNPACKED_BYTES
47880
+ or (entry.isdir() and entry.size != 0)):
47881
+ raise ValueError()
47882
+ end = entry.offset_data + ((entry.size + 511) // 512) * 512
47883
+ # Bound names before splitting or allocating retained ancestor prefixes.
47884
+ if len(entry.name) > MAX_PATH_LENGTH:
47885
+ raise ValueError()
47886
+ path = entry.name.rstrip("/") if entry.isdir() else entry.name
47887
+ if path.count("/") >= MAX_PATH_DEPTH:
47888
+ raise ValueError()
47889
+ parts = path.split("/")
47890
+ if (not (entry.isreg() or entry.isdir()) or entry.issparse()
47891
+ or parts[0] != "package" or "\\\\" in path or ":" in path
47892
+ or any(ord(char) < 32 or ord(char) > 126 for char in path)
47893
+ or any(len(part) > MAX_COMPONENT_LENGTH or part in ("", ".", "..")
47894
+ or part.rstrip(". ") != part for part in parts)
47895
+ or path.lower() in seen):
47896
+ raise ValueError()
47897
+ seen.add(path.lower())
47898
+ folded = path.lower()
47899
+ parents = ["/".join(parts[:i]).lower() for i in range(1, len(parts))]
47900
+ if any(parent in files for parent in parents) or (entry.isreg() and folded in directories):
47901
+ raise ValueError()
47902
+ directories.update(parents)
47903
+ if entry.isdir():
47904
+ directories.add(folded)
47905
+ else:
47906
+ files.add(folded)
47907
+ for part in parts:
47908
+ lower = part.lower()
47909
+ if (WINDOWS_DEVICE.fullmatch(part.split(".", 1)[0].rstrip(" "))
47910
+ or any(char in part for char in '<>"|?*')
47911
+ or lower in (".npmrc", ".netrc", ".pypirc", ".git", ".gitconfig", ".git-credentials", ".yarnrc", ".yarnrc.yml")
47912
+ or lower.startswith(".env") or lower.endswith((".env", ".pem", ".key", ".p12", ".pfx", ".log"))):
47913
+ raise ValueError()
47914
+ if path == "package/package.json":
47915
+ if not entry.isreg() or entry.size > MAX_METADATA_BYTES:
47916
+ raise ValueError()
47917
+ with archive.extractfile(entry) as file:
47918
+ package = json.loads(file.read().decode("utf-8"), object_pairs_hook=unique_object,
47919
+ parse_constant=reject_constant)
47920
+ if len(unpacked) - end < 1024 or any(unpacked[end:]):
47921
+ raise ValueError()
47922
+ if type(package) is not dict or package.get("name") != name or package.get("version") != version:
47923
+ raise ValueError()
47924
+ except (ValueError, tarfile.TarError, OSError, EOFError, RecursionError, zlib.error):
47925
+ raise ValueError("archive-members") from None
47926
+
47927
+
47928
+ def check_exact_version(version):
47929
+ match = EXACT_VERSION.fullmatch(version) if type(version) is str and len(version) <= 128 else None
47930
+ if not match or any(
47931
+ part.isdigit() and len(part) > 1 and part.startswith("0")
47932
+ for part in (match[4] or "").split(".")
47933
+ ):
47934
+ raise ValueError("package-identity")
47935
+
47936
+
47937
+ def check_tarball_url(url):
47938
+ # Accept literal paths only: never reinterpret percent escapes or empty delimiters.
47939
+ if (type(url) is not str or any(ord(char) < 33 or ord(char) > 126 for char in url)
47940
+ or any(char in url for char in "\\\\%?#")):
47941
+ raise ValueError("registry-url")
47942
+ try:
47943
+ location = urlsplit(url)
47944
+ except ValueError:
47945
+ raise ValueError("registry-url") from None
47946
+ if (location.scheme != "https" or location.netloc != "npm.pkg.github.com"
47947
+ or not location.path.startswith("/download/")
47948
+ or any(part in ("", ".", "..") for part in location.path.split("/")[1:])):
47949
+ raise ValueError("registry-url")
47950
+
47951
+
47952
+ def verify_registry_archive(name, version, metadata_bytes, tarball_bytes):
47953
+ if (type(metadata_bytes) is not bytes or not 0 < len(metadata_bytes) <= MAX_METADATA_BYTES
47954
+ or type(tarball_bytes) is not bytes or not 0 < len(tarball_bytes) <= MAX_TARBALL_BYTES):
47955
+ raise ValueError("byte-limit")
47956
+ if type(name) is not str or name not in PACKAGES:
47957
+ raise ValueError("package-identity")
47958
+ check_exact_version(version)
47959
+ try:
47960
+ metadata = json.loads(metadata_bytes.decode("utf-8"), object_pairs_hook=unique_object,
47961
+ parse_constant=reject_constant)
47962
+ if type(metadata) is not dict or type(metadata.get("dist")) is not dict:
47963
+ raise ValueError()
47964
+ full_integrity = metadata["dist"].get("integrity")
47965
+ if type(full_integrity) is not str or not full_integrity.startswith("sha512-"):
47966
+ raise ValueError()
47967
+ registry_digest = base64.b64decode(full_integrity[7:], validate=True)
47968
+ if (len(registry_digest) != hashlib.sha512().digest_size
47969
+ or base64.b64encode(registry_digest).decode() != full_integrity[7:]):
47970
+ raise ValueError()
47971
+ except (ValueError, binascii.Error, RecursionError):
47972
+ raise ValueError("registry-metadata") from None
47973
+ if metadata.get("name") != name or metadata.get("version") != version:
47974
+ raise ValueError("package-identity")
47975
+ check_tarball_url(metadata["dist"].get("tarball"))
47976
+ if not hmac.compare_digest(registry_digest, hashlib.sha512(tarball_bytes).digest()):
47977
+ raise ValueError("integrity-mismatch")
47978
+ inspect_archive(name, version, tarball_bytes)
47979
+ return {
47980
+ "name": name,
47981
+ "version": version,
47982
+ "integrity": metadata["dist"]["integrity"],
47983
+ "metadataSha256": hashlib.sha256(metadata_bytes).hexdigest(),
47984
+ "tarballSha256": hashlib.sha256(tarball_bytes).hexdigest(),
47985
+ "metadataBytes": len(metadata_bytes),
47986
+ "tarballBytes": len(tarball_bytes),
47987
+ }
47988
+
47989
+
47990
+ # --- Export (runs only inside .github/workflows/package-export.yml) ---------------------------
47991
+ TRUSTED_REPOSITORY = "mutmutco/MMI-Hub"
47992
+ TRUSTED_WORKFLOW = ".github/workflows/package-export.yml"
47993
+ TRUSTED_REF = "refs/heads/development"
47994
+ REGISTRY = "https://npm.pkg.github.com"
47995
+ MAX_PACKUMENT_BYTES = 16 * 1024 * 1024
47996
+ VERSION_INPUTS = {"@mutmutco/mmwork-ds": "DS_VERSION", "@mutmutco/mmwork-screens": "SCREENS_VERSION"}
47997
+
47998
+
47999
+ def dispatch_identity(env):
48000
+ """Refuse any run that is not this workflow, dispatched on the trusted ref of the trusted repo."""
48001
+ try:
48002
+ identity = {key: env[name] for key, name in [
48003
+ ("repository", "GITHUB_REPOSITORY"), ("repositoryId", "GITHUB_REPOSITORY_ID"),
48004
+ ("workflowRef", "GITHUB_WORKFLOW_REF"), ("ref", "GITHUB_REF"), ("headSha", "GITHUB_SHA"),
48005
+ ("runId", "GITHUB_RUN_ID"), ("runAttempt", "GITHUB_RUN_ATTEMPT"),
48006
+ ("event", "GITHUB_EVENT_NAME"), ("actor", "GITHUB_ACTOR"),
48007
+ ("triggeringActor", "GITHUB_TRIGGERING_ACTOR")]}
48008
+ except KeyError:
48009
+ raise ValueError("dispatch-identity") from None
48010
+ if (identity["repository"] != TRUSTED_REPOSITORY or identity["ref"] != TRUSTED_REF
48011
+ or identity["event"] != "workflow_dispatch"
48012
+ or identity["workflowRef"] != f"{TRUSTED_REPOSITORY}/{TRUSTED_WORKFLOW}@{TRUSTED_REF}"
48013
+ or not re.fullmatch(r"[0-9a-f]{40}", identity["headSha"])
48014
+ or not all(re.fullmatch(r"[1-9][0-9]{0,15}", identity[key])
48015
+ for key in ("repositoryId", "runId", "runAttempt"))
48016
+ or not all(re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9-]{0,38}(\\[bot\\])?", identity[key])
48017
+ for key in ("actor", "triggeringActor"))):
48018
+ raise ValueError("dispatch-identity")
48019
+ return identity
48020
+
48021
+
48022
+ class HttpsOnlyRedirect(urllib.request.HTTPRedirectHandler):
48023
+ def redirect_request(self, req, fp, code, msg, headers, newurl):
48024
+ if urlsplit(newurl).scheme != "https":
48025
+ raise ValueError("registry-url")
48026
+ return super().redirect_request(req, fp, code, msg, headers, newurl)
48027
+
48028
+
48029
+ def default_opener():
48030
+ # No proxy handler: the token goes only to the registry host named in the request.
48031
+ return urllib.request.build_opener(urllib.request.ProxyHandler({}), HttpsOnlyRedirect())
48032
+
48033
+
48034
+ def fetch(opener, url, token, limit):
48035
+ request = urllib.request.Request(url, headers={"Accept": "application/json"})
48036
+ # Unredirected: the registry's redirect to blob storage never carries the token.
48037
+ request.add_unredirected_header("Authorization", f"Bearer {token}")
48038
+ with opener.open(request, timeout=60) as response:
48039
+ body = response.read(limit + 1)
48040
+ if len(body) > limit or not body:
48041
+ raise ValueError("byte-limit")
48042
+ return body
48043
+
48044
+
48045
+ def export_package(opener, token, name, version):
48046
+ check_exact_version(version)
48047
+ try:
48048
+ packument_bytes = fetch(opener, f"{REGISTRY}/{name.replace('/', '%2f')}", token, MAX_PACKUMENT_BYTES)
48049
+ except urllib.error.HTTPError as error:
48050
+ # Name the package and the registry's own short reason (body only; no request headers).
48051
+ reason = error.read(300).decode("utf-8", "replace").replace(token, "***")
48052
+ raise ValueError(f"registry {name}: HTTP {error.code} {reason}") from None
48053
+ try:
48054
+ packument = json.loads(packument_bytes.decode("utf-8"), object_pairs_hook=unique_object,
48055
+ parse_constant=reject_constant)
48056
+ if (type(packument) is not dict or packument.get("name") != name
48057
+ or type(packument.get("versions")) is not dict):
48058
+ raise ValueError()
48059
+ document = packument["versions"][version]
48060
+ if type(document) is not dict or type(document.get("dist")) is not dict:
48061
+ raise ValueError()
48062
+ except (ValueError, KeyError, RecursionError):
48063
+ raise ValueError("registry-metadata") from None
48064
+ check_tarball_url(document["dist"].get("tarball"))
48065
+ metadata_bytes = json.dumps(document, sort_keys=True, separators=(",", ":")).encode()
48066
+ tarball_bytes = fetch(opener, document["dist"]["tarball"], token, MAX_TARBALL_BYTES)
48067
+ receipt = verify_registry_archive(name, version, metadata_bytes, tarball_bytes)
48068
+ receipt["packumentSha256"] = hashlib.sha256(packument_bytes).hexdigest()
48069
+ return metadata_bytes, tarball_bytes, receipt
48070
+
48071
+
48072
+ def export(env, out_dir, opener=None):
48073
+ """Write exactly the five artifact members into a new directory. Nothing else is written."""
48074
+ identity = dispatch_identity(env)
48075
+ versions = {name: env.get(variable) for name, variable in VERSION_INPUTS.items()}
48076
+ for version in versions.values():
48077
+ check_exact_version(version)
48078
+ token = env.get("GITHUB_PACKAGES_TOKEN")
48079
+ if type(token) is not str or not token.strip():
48080
+ raise ValueError("registry-token")
48081
+ opener = opener or default_opener()
48082
+ files = {}
48083
+ receipts = []
48084
+ for name in PACKAGES:
48085
+ metadata_bytes, tarball_bytes, receipt = export_package(opener, token, name, versions[name])
48086
+ short = name.split("/")[1]
48087
+ files[f"{short}.metadata.json"] = metadata_bytes
48088
+ files[f"{short}.tgz"] = tarball_bytes
48089
+ receipts.append(receipt)
48090
+ manifest = {"schema": 1, "registry": REGISTRY, "source": identity, "packages": receipts}
48091
+ files["manifest.json"] = json.dumps(manifest, sort_keys=True, indent=2).encode()
48092
+ if any(token.encode() in payload for payload in files.values()):
48093
+ raise ValueError("registry-token")
48094
+ out = pathlib.Path(out_dir)
48095
+ out.mkdir(parents=True, exist_ok=False)
48096
+ for member, payload in files.items():
48097
+ (out / member).write_bytes(payload)
48098
+ return manifest
48099
+
48100
+
48101
+ def verify_export(provider_bytes, zip_bytes, expected, versions, now):
48102
+ """Consumer side: bind the artifact to external selectors, then re-verify every package offline.
48103
+
48104
+ versions maps each allowlisted package to the exact version the consumer asked for. The
48105
+ manifest inside the artifact is checked against recomputed receipts, never trusted.
48106
+ """
48107
+ receipt = verify_artifact_receipt(provider_bytes, zip_bytes, expected, now)
48108
+ members = inspect_artifact_zip(zip_bytes)
48109
+ if type(versions) is not dict or set(versions) != set(PACKAGES):
48110
+ raise ValueError("package-identity")
48111
+ packages = []
48112
+ for name in PACKAGES:
48113
+ short = name.split("/")[1]
48114
+ packages.append(verify_registry_archive(name, versions[name], members[f"{short}.metadata.json"],
48115
+ members[f"{short}.tgz"]))
48116
+ try:
48117
+ manifest = json.loads(members["manifest.json"].decode("utf-8"), object_pairs_hook=unique_object,
48118
+ parse_constant=reject_constant)
48119
+ source = manifest["source"]
48120
+ claimed = manifest["packages"]
48121
+ if (manifest.get("registry") != REGISTRY or type(claimed) is not list or len(claimed) != len(packages)
48122
+ or any(type(entry) is not dict or {key: entry.get(key) for key in package} != package
48123
+ for entry, package in zip(claimed, packages))
48124
+ or source.get("repository") != TRUSTED_REPOSITORY
48125
+ or source.get("workflowRef") != f"{TRUSTED_REPOSITORY}/{TRUSTED_WORKFLOW}@{TRUSTED_REF}"
48126
+ or source.get("headSha") != expected["headSha"]
48127
+ or source.get("runId") != str(expected["runId"])
48128
+ or source.get("repositoryId") != str(expected["repositoryId"])):
48129
+ raise ValueError()
48130
+ except (ValueError, KeyError, TypeError, AttributeError, RecursionError):
48131
+ raise ValueError("artifact-manifest") from None
48132
+ return {"artifact": receipt, "packages": packages}
48133
+
48134
+
48135
+ def verify_dir(work_dir, ds_version, screens_version):
48136
+ """Consumer CLI entry (#7833): verify_export over files a caller fetched into work_dir.
48137
+
48138
+ Reads provider.json, artifact.zip and expected.json; writes only the four verified package
48139
+ members into work_dir/verified (a new directory). The caller acts only on those bytes.
48140
+ """
48141
+ work = pathlib.Path(work_dir)
48142
+ result = verify_export((work / "provider.json").read_bytes(), (work / "artifact.zip").read_bytes(),
48143
+ json.loads((work / "expected.json").read_text("utf-8")),
48144
+ {PACKAGES[0]: ds_version, PACKAGES[1]: screens_version}, datetime.now(timezone.utc))
48145
+ members = inspect_artifact_zip((work / "artifact.zip").read_bytes())
48146
+ out = work / "verified"
48147
+ out.mkdir(exist_ok=False)
48148
+ for member, payload in members.items():
48149
+ if member != "manifest.json":
48150
+ (out / member).write_bytes(payload)
48151
+ return result
48152
+
48153
+
48154
+ if __name__ == "__main__":
48155
+ if len(sys.argv) == 5 and sys.argv[1] == "verify":
48156
+ try:
48157
+ print(json.dumps(verify_dir(*sys.argv[2:]), sort_keys=True, indent=2))
48158
+ except (ValueError, OSError) as error:
48159
+ sys.exit(f"package-export verify refused: {error}")
48160
+ sys.exit(0)
48161
+ if len(sys.argv) != 3 or sys.argv[1] != "export":
48162
+ sys.exit("usage: package_export.py export <new-output-dir> | verify <work-dir> <ds-version> <screens-version>")
48163
+ try:
48164
+ result = export(os.environ, sys.argv[2])
48165
+ except (ValueError, OSError, urllib.error.URLError) as error:
48166
+ # Error text carries no request headers: HTTPError renders only the status line.
48167
+ sys.exit(f"package-export refused: {error}")
48168
+ print(json.dumps(result, sort_keys=True, indent=2))
48169
+ `;
48170
+
48171
+ // src/packages-adopt-export.ts
48172
+ var EXPORT_REPO = "mutmutco/MMI-Hub";
48173
+ var EXPORT_WORKFLOW = ".github/workflows/package-export.yml";
48174
+ var PACKAGES = [
48175
+ { name: "@mutmutco/mmwork-ds", short: "mmwork-ds" },
48176
+ { name: "@mutmutco/mmwork-screens", short: "mmwork-screens" }
48177
+ ];
48178
+ var SPEC_SECTIONS = ["dependencies", "devDependencies", "optionalDependencies"];
48179
+ var SHAPE_FIELDS = ["dependencies", "optionalDependencies", "peerDependencies", "peerDependenciesMeta", "bin", "os", "cpu", "hasInstallScript"];
48180
+ var stable = (value) => JSON.stringify(value, (_key, inner) => inner && typeof inner === "object" && !Array.isArray(inner) ? Object.fromEntries(Object.entries(inner).sort(([a], [b]) => a.localeCompare(b))) : inner);
48181
+ function lockShape(meta, field) {
48182
+ if (field === "hasInstallScript") {
48183
+ const scripts = meta.scripts ?? {};
48184
+ return meta.hasInstallScript === true || Boolean(scripts.preinstall || scripts.install || scripts.postinstall) || void 0;
48185
+ }
48186
+ if (field === "bin" && meta.bin && typeof meta.bin === "object") {
48187
+ return Object.fromEntries(Object.entries(meta.bin).map(([k, v]) => [k, String(v).replace(/^(\.\/)+/, "")]));
48188
+ }
48189
+ const value = meta[field];
48190
+ return value && typeof value === "object" && Object.keys(value).length === 0 ? void 0 : value;
48191
+ }
48192
+ function formatLike(original, value) {
48193
+ const indent = /\n([ \t]+)"/.exec(original)?.[1] ?? " ";
48194
+ const text = JSON.stringify(value, null, indent) + "\n";
48195
+ return original.includes("\r\n") ? text.replace(/\n/g, "\r\n") : text;
48196
+ }
48197
+ function planAdoption(packageText, lockText, metadata) {
48198
+ const pkg = JSON.parse(packageText);
48199
+ const lock = JSON.parse(lockText);
48200
+ const root = lock.packages?.[""];
48201
+ if (lock.lockfileVersion < 2 || !root) throw new Error("package-lock.json must be lockfileVersion 2 or 3");
48202
+ const result = { adopted: [], skipped: [] };
48203
+ for (const { name } of PACKAGES) {
48204
+ const section = SPEC_SECTIONS.find((key) => pkg[key]?.[name] !== void 0);
48205
+ if (!section) {
48206
+ result.skipped.push(name);
48207
+ continue;
48208
+ }
48209
+ const meta = metadata[name];
48210
+ const entry = lock.packages[`node_modules/${name}`];
48211
+ if (!entry) throw new Error(`${name} is in package.json but not in package-lock.json; run a normal registry install first`);
48212
+ const nested = Object.keys(lock.packages).filter((key) => key.endsWith(`/node_modules/${name}`));
48213
+ if (nested.length) throw new Error(`${name} has nested lock copies (${nested.join(", ")}); adopt-export edits only the top-level entry`);
48214
+ const changed = SHAPE_FIELDS.filter((field) => stable(field === "hasInstallScript" ? entry[field] || void 0 : entry[field]) !== stable(lockShape(meta, field)));
48215
+ if (changed.length) {
48216
+ throw new Error(`${name} ${entry.version} -> ${meta.version} changes ${changed.join(", ")}; that needs the registry, so adopt it through a normal install in CI instead`);
48217
+ }
48218
+ result.adopted.push({ name, from: entry.version, to: meta.version, resolved: meta.dist.tarball, integrity: meta.dist.integrity });
48219
+ entry.version = meta.version;
48220
+ entry.resolved = meta.dist.tarball;
48221
+ entry.integrity = meta.dist.integrity;
48222
+ for (const field of ["license", "engines"]) {
48223
+ if (meta[field] === void 0) delete entry[field];
48224
+ else entry[field] = meta[field];
48225
+ }
48226
+ pkg[section][name] = meta.version;
48227
+ root[section] = { ...root[section], [name]: meta.version };
48228
+ }
48229
+ return { packageText: formatLike(packageText, pkg), lockText: formatLike(lockText, lock), result };
48230
+ }
48231
+ function expectedFromRun(run, opts) {
48232
+ if (run.id !== opts.run || run.repository?.full_name !== EXPORT_REPO || run.path !== EXPORT_WORKFLOW) {
48233
+ throw new Error(`run ${opts.run} is not a ${EXPORT_REPO} ${EXPORT_WORKFLOW} run`);
48234
+ }
48235
+ return {
48236
+ artifactId: opts.artifact,
48237
+ artifactName: `mmi-package-export-${run.id}-${run.run_attempt}`,
48238
+ repositoryId: run.repository.id,
48239
+ runId: run.id,
48240
+ headSha: run.head_sha,
48241
+ headBranch: run.head_branch,
48242
+ digest: opts.digest
48243
+ };
48244
+ }
48245
+ async function adoptExport(opts, deps) {
48246
+ if (!/^sha256:[0-9a-f]{64}$/.test(opts.digest)) throw new Error("--digest must be sha256:<64 hex>, from the export run summary");
48247
+ const packagePath = (0, import_node_path44.join)(opts.dir, "package.json");
48248
+ const lockPath = (0, import_node_path44.join)(opts.dir, "package-lock.json");
48249
+ const packageText = (0, import_node_fs45.readFileSync)(packagePath, "utf8");
48250
+ const lockText = (0, import_node_fs45.readFileSync)(lockPath, "utf8");
48251
+ const work = (0, import_node_fs45.mkdtempSync)((0, import_node_path44.join)((0, import_node_os21.tmpdir)(), "mmi-adopt-export-"));
48252
+ try {
48253
+ const run = JSON.parse((await deps.ghApi(`repos/${EXPORT_REPO}/actions/runs/${opts.run}`)).toString("utf8"));
48254
+ (0, import_node_fs45.writeFileSync)((0, import_node_path44.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
48255
+ (0, import_node_fs45.writeFileSync)((0, import_node_path44.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
48256
+ (0, import_node_fs45.writeFileSync)((0, import_node_path44.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
48257
+ const script = (0, import_node_path44.join)(work, "package_export.py");
48258
+ (0, import_node_fs45.writeFileSync)(script, package_export_default);
48259
+ await deps.python(script, ["verify", work, opts.ds, opts.screens]);
48260
+ const verified = (0, import_node_path44.join)(work, "verified");
48261
+ const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path44.join)(verified, `${short3}.metadata.json`), "utf8"))]));
48262
+ const plan = planAdoption(packageText, lockText, metadata);
48263
+ for (const { name } of plan.result.adopted) {
48264
+ const short3 = PACKAGES.find((p) => p.name === name).short;
48265
+ await deps.npm(["cache", "add", (0, import_node_path44.join)(verified, `${short3}.tgz`)], opts.dir);
48266
+ }
48267
+ (0, import_node_fs45.writeFileSync)(packagePath, plan.packageText);
48268
+ (0, import_node_fs45.writeFileSync)(lockPath, plan.lockText);
48269
+ try {
48270
+ await deps.npm(["ci", "--offline", "--ignore-scripts", "--no-audit", "--no-fund"], opts.dir);
48271
+ } catch (error) {
48272
+ (0, import_node_fs45.writeFileSync)(packagePath, packageText);
48273
+ (0, import_node_fs45.writeFileSync)(lockPath, lockText);
48274
+ throw new Error(`npm ci --offline failed, package.json and package-lock.json restored: ${error.message}`);
48275
+ }
48276
+ return plan.result;
48277
+ } finally {
48278
+ (0, import_node_fs45.rmSync)(work, { recursive: true, force: true });
48279
+ }
48280
+ }
48281
+ var execFileBuffer = (0, import_node_util6.promisify)(import_node_child_process18.execFile);
48282
+ var MAX_OUTPUT = 200 * 1024 * 1024;
48283
+ async function findPython() {
48284
+ for (const bin of ["python3", "python"]) {
48285
+ try {
48286
+ await execFileBuffer(bin, ["-I", "-S", "-B", "-c", "import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)"], { windowsHide: true, timeout: 1e4 });
48287
+ return bin;
48288
+ } catch {
48289
+ }
48290
+ }
48291
+ throw new Error("Python 3.11+ is required to run the Hub package-export verifier");
48292
+ }
48293
+ function adoptExportProdDeps() {
48294
+ const failText = (error) => String(error?.stderr || error?.message || error).trim();
48295
+ return {
48296
+ ghApi: async (path2) => {
48297
+ try {
48298
+ return (await execFileBuffer("gh", ["api", "--method", "GET", path2], { encoding: "buffer", maxBuffer: MAX_OUTPUT, windowsHide: true, timeout: 3e5 })).stdout;
48299
+ } catch (error) {
48300
+ throw new Error(`gh api ${path2} failed: ${failText(error)}`);
48301
+ }
48302
+ },
48303
+ python: async (script, args) => {
48304
+ try {
48305
+ return (await execFileBuffer(await findPython(), ["-I", "-S", "-B", script, ...args], { encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true, timeout: 3e5 })).stdout;
48306
+ } catch (error) {
48307
+ throw new Error(failText(error));
48308
+ }
48309
+ },
48310
+ npm: async (args, cwd) => {
48311
+ const [file, argv] = process.platform === "win32" ? ["cmd.exe", ["/c", "npm", ...args]] : ["npm", args];
48312
+ const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => key.toLowerCase() !== "npm_config_allow_scripts"));
48313
+ try {
48314
+ return (await execFileBuffer(file, argv, { cwd, env, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true, timeout: 6e5 })).stdout;
48315
+ } catch (error) {
48316
+ throw new Error(`npm ${args[0]} failed: ${failText(error)}`);
48317
+ }
48318
+ }
48319
+ };
48320
+ }
48321
+ function registerPackagesCommands(program3) {
48322
+ const packages = program3.command("packages").description("consumer adoption of verified package exports (#7833)");
48323
+ packages.command("adopt-export").description("verify a package-export artifact and install its exact kit pair offline, keeping registry URLs and SRI in the lock").requiredOption("--run <id>", "package-export workflow run id").requiredOption("--artifact <id>", "artifact id from the run summary").requiredOption("--digest <sha256:hex>", "artifact digest from the run summary").requiredOption("--ds <version>", "exact @mutmutco/mmwork-ds version").requiredOption("--screens <version>", "exact @mutmutco/mmwork-screens version").option("--dir <path>", "npm project holding package.json and package-lock.json", ".").action(async (opts) => {
48324
+ const id = (value, flag) => {
48325
+ if (!/^[1-9][0-9]{0,15}$/.test(value)) fail(`${flag} must be a numeric id`);
48326
+ return Number(value);
48327
+ };
48328
+ try {
48329
+ const result = await adoptExport({ ...opts, run: id(opts.run, "--run"), artifact: id(opts.artifact, "--artifact") }, adoptExportProdDeps());
48330
+ for (const a of result.adopted) console.log(`adopted ${a.name} ${a.from} -> ${a.to} (${a.resolved}, ${a.integrity})`);
48331
+ for (const name of result.skipped) console.log(`skipped ${name}: not a dependency in this package.json`);
48332
+ console.log("npm ci --offline --ignore-scripts passed; commit package.json and package-lock.json");
48333
+ } catch (error) {
48334
+ fail(`packages adopt-export refused: ${error.message}`);
48335
+ }
48336
+ });
48337
+ }
48338
+
47594
48339
  // src/discovery-commands.ts
47595
48340
  init_cli_shared();
47596
48341
 
47597
48342
  // src/wave-status.ts
47598
- var import_node_fs45 = require("node:fs");
48343
+ var import_node_fs46 = require("node:fs");
47599
48344
  function readStageSummary(worktreePath) {
47600
48345
  const statePath = stageStatePath(worktreePath);
47601
- if (!(0, import_node_fs45.existsSync)(statePath)) return void 0;
48346
+ if (!(0, import_node_fs46.existsSync)(statePath)) return void 0;
47602
48347
  try {
47603
- const state = JSON.parse((0, import_node_fs45.readFileSync)(statePath, "utf8"));
48348
+ const state = JSON.parse((0, import_node_fs46.readFileSync)(statePath, "utf8"));
47604
48349
  const port = typeof state.port === "number" ? state.port : void 0;
47605
48350
  if (port == null || !Number.isInteger(port) || port <= 0) return void 0;
47606
48351
  return { port, url: typeof state.url === "string" ? state.url : void 0 };
@@ -47657,9 +48402,9 @@ async function collectWaveStatus(deps) {
47657
48402
  // src/discovery-commands.ts
47658
48403
  init_github_client();
47659
48404
  init_cli_shared();
47660
- var import_node_fs46 = require("node:fs");
47661
- var import_node_os21 = require("node:os");
47662
- var import_node_path44 = require("node:path");
48405
+ var import_node_fs47 = require("node:fs");
48406
+ var import_node_os22 = require("node:os");
48407
+ var import_node_path45 = require("node:path");
47663
48408
  var GC_GH_TIMEOUT_MS3 = 2e4;
47664
48409
  async function collectStatus() {
47665
48410
  const repo = await resolveRepo();
@@ -47848,10 +48593,10 @@ async function collectOnboardStatus(opts = {}) {
47848
48593
  else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
47849
48594
  else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
47850
48595
  }
47851
- const home = (0, import_node_os21.homedir)();
48596
+ const home = (0, import_node_os22.homedir)();
47852
48597
  const plugin = onboardPluginGate({
47853
- readKnown: () => readFileSyncSafe((0, import_node_path44.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs46.readFileSync),
47854
- readSettings: () => readFileSyncSafe((0, import_node_path44.join)(home, ".claude", "settings.json"), import_node_fs46.readFileSync)
48598
+ readKnown: () => readFileSyncSafe((0, import_node_path45.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs47.readFileSync),
48599
+ readSettings: () => readFileSyncSafe((0, import_node_path45.join)(home, ".claude", "settings.json"), import_node_fs47.readFileSync)
47855
48600
  });
47856
48601
  return {
47857
48602
  guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
@@ -50955,8 +51700,8 @@ function registerSessionReport(program3) {
50955
51700
  }
50956
51701
 
50957
51702
  // src/stage-commands.ts
50958
- var import_node_fs47 = require("node:fs");
50959
- var import_node_path45 = require("node:path");
51703
+ var import_node_fs48 = require("node:fs");
51704
+ var import_node_path46 = require("node:path");
50960
51705
  init_cli_shared();
50961
51706
  init_clean_exit();
50962
51707
 
@@ -51202,8 +51947,8 @@ function registerStageCommands(program3) {
51202
51947
  return {
51203
51948
  resolution: decideStage({
51204
51949
  registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
51205
- hasCompose: (0, import_node_fs47.existsSync)((0, import_node_path45.join)(process.cwd(), "docker-compose.yml")),
51206
- hasEnvExample: (0, import_node_fs47.existsSync)((0, import_node_path45.join)(process.cwd(), ".env.example")),
51950
+ hasCompose: (0, import_node_fs48.existsSync)((0, import_node_path46.join)(process.cwd(), "docker-compose.yml")),
51951
+ hasEnvExample: (0, import_node_fs48.existsSync)((0, import_node_path46.join)(process.cwd(), ".env.example")),
51207
51952
  repo
51208
51953
  }),
51209
51954
  project: project2,
@@ -51285,7 +52030,7 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
51285
52030
  if (!res.derived) return void 0;
51286
52031
  return result.port != null ? stageUrlForPort(result.port) : res.derived.url;
51287
52032
  }
51288
- program3.command("port-range <repo>").description("assign (idempotently) + print the repo's local stage port block via the atomic ORG#config.portCursor allocator (committed-file fallback)").option("--json", "machine-readable output").action(async (repo, o) => {
52033
+ program3.command("port-range <repo>").description("assign (idempotently) + print the repo's local stage port block via the atomic ORG#config.portCursor allocator (fails loud when DDB is unreachable)").option("--json", "machine-readable output").action(async (repo, o) => {
51289
52034
  const cfg = await loadConfig();
51290
52035
  const reg = registryClientDeps(cfg);
51291
52036
  const slug = slugOf(repo);
@@ -51488,19 +52233,19 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
51488
52233
 
51489
52234
  // src/tenant-artifact.ts
51490
52235
  var import_node_crypto16 = require("node:crypto");
51491
- var import_node_fs48 = require("node:fs");
52236
+ var import_node_fs49 = require("node:fs");
51492
52237
  var import_promises11 = require("node:fs/promises");
51493
- var import_node_path46 = require("node:path");
52238
+ var import_node_path47 = require("node:path");
51494
52239
  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}$/;
51495
52240
  var MAX_BYTES = 5 * 1024 * 1024 * 1024;
51496
52241
  async function sha256File(path2) {
51497
52242
  const hash = (0, import_node_crypto16.createHash)("sha256");
51498
- for await (const chunk of (0, import_node_fs48.createReadStream)(path2)) hash.update(chunk);
52243
+ for await (const chunk of (0, import_node_fs49.createReadStream)(path2)) hash.update(chunk);
51499
52244
  return hash.digest("hex");
51500
52245
  }
51501
52246
  async function putTenantArtifact(repo, stage, inputPath, deps) {
51502
52247
  if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
51503
- const path2 = (0, import_node_path46.resolve)(inputPath);
52248
+ const path2 = (0, import_node_path47.resolve)(inputPath);
51504
52249
  const info = await (0, import_promises11.stat)(path2);
51505
52250
  if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
51506
52251
  if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size > MAX_BYTES) throw new Error(`tenant artifact put: file must be 1..${MAX_BYTES} bytes`);
@@ -51518,7 +52263,7 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
51518
52263
  return [key, value];
51519
52264
  }));
51520
52265
  headers["content-length"] = String(info.size);
51521
- const stream = (0, import_node_fs48.createReadStream)(path2);
52266
+ const stream = (0, import_node_fs49.createReadStream)(path2);
51522
52267
  let uploaded;
51523
52268
  try {
51524
52269
  uploaded = await fetch(body.uploadUrl, {
@@ -51657,7 +52402,7 @@ function renderVerifySecrets(body) {
51657
52402
  }
51658
52403
 
51659
52404
  // src/command-register-developer.ts
51660
- var import_node_fs52 = require("node:fs");
52405
+ var import_node_fs53 = require("node:fs");
51661
52406
  init_clean_exit();
51662
52407
  init_cli_shared();
51663
52408
  init_error_codes();
@@ -51690,7 +52435,7 @@ async function resolveWhoami(deps) {
51690
52435
  }
51691
52436
 
51692
52437
  // src/command-register-developer.ts
51693
- var import_node_path51 = require("node:path");
52438
+ var import_node_path52 = require("node:path");
51694
52439
  init_house_map();
51695
52440
  init_hub_url();
51696
52441
 
@@ -51723,21 +52468,21 @@ async function executeWaveLand(plan, deps) {
51723
52468
  }
51724
52469
 
51725
52470
  // src/dist-drift.ts
51726
- var import_node_child_process18 = require("node:child_process");
52471
+ var import_node_child_process19 = require("node:child_process");
51727
52472
  var import_node_crypto18 = require("node:crypto");
51728
- var import_node_fs50 = require("node:fs");
51729
- var import_node_os22 = require("node:os");
51730
- var import_node_path48 = require("node:path");
52473
+ var import_node_fs51 = require("node:fs");
52474
+ var import_node_os23 = require("node:os");
52475
+ var import_node_path49 = require("node:path");
51731
52476
 
51732
52477
  // ../scripts/distribution-digest.mjs
51733
52478
  var import_node_crypto17 = require("node:crypto");
51734
- var import_node_fs49 = require("node:fs");
51735
- var import_node_path47 = require("node:path");
52479
+ var import_node_fs50 = require("node:fs");
52480
+ var import_node_path48 = require("node:path");
51736
52481
  var slash = (value) => value.replaceAll("\\", "/");
51737
52482
  function repoPath(root, declaredPath, label) {
51738
- const absoluteRoot = (0, import_node_path47.resolve)(root);
51739
- const target = (0, import_node_path47.resolve)(root, declaredPath);
51740
- if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path47.sep}`)) {
52483
+ const absoluteRoot = (0, import_node_path48.resolve)(root);
52484
+ const target = (0, import_node_path48.resolve)(root, declaredPath);
52485
+ if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path48.sep}`)) {
51741
52486
  throw new Error(`${label} ${declaredPath} escapes the repository root`);
51742
52487
  }
51743
52488
  return target;
@@ -51745,7 +52490,7 @@ function repoPath(root, declaredPath, label) {
51745
52490
  function digestFiles(files) {
51746
52491
  const hash = (0, import_node_crypto17.createHash)("sha256");
51747
52492
  for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
51748
- const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs49.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs49.readFileSync)(file.absolute);
52493
+ const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs50.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs50.readFileSync)(file.absolute);
51749
52494
  hash.update(file.relative, "utf8");
51750
52495
  hash.update("\0");
51751
52496
  hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
@@ -51760,8 +52505,8 @@ function digestFiles(files) {
51760
52505
  function digestPackedFiles(packageRoot, packedFiles) {
51761
52506
  return digestFiles(packedFiles.map((path2) => {
51762
52507
  const absolute = repoPath(packageRoot, path2, "packed artifact identity path");
51763
- if (!(0, import_node_fs49.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
51764
- return { absolute, relative: slash(path2), stat: (0, import_node_fs49.lstatSync)(absolute) };
52508
+ if (!(0, import_node_fs50.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
52509
+ return { absolute, relative: slash(path2), stat: (0, import_node_fs50.lstatSync)(absolute) };
51765
52510
  }));
51766
52511
  }
51767
52512
 
@@ -51845,13 +52590,13 @@ function renderDistDriftReceipt(receipt) {
51845
52590
  return lines2;
51846
52591
  }
51847
52592
  function readOrNull(path2) {
51848
- return (0, import_node_fs50.existsSync)(path2) ? (0, import_node_fs50.readFileSync)(path2) : null;
52593
+ return (0, import_node_fs51.existsSync)(path2) ? (0, import_node_fs51.readFileSync)(path2) : null;
51849
52594
  }
51850
52595
  function walkFiles(root) {
51851
52596
  const files = [];
51852
52597
  const walk2 = (directory) => {
51853
- for (const entry of (0, import_node_fs50.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
51854
- const child2 = (0, import_node_path48.join)(directory, entry.name);
52598
+ for (const entry of (0, import_node_fs51.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
52599
+ const child2 = (0, import_node_path49.join)(directory, entry.name);
51855
52600
  if (entry.isDirectory()) walk2(child2);
51856
52601
  else files.push(child2);
51857
52602
  }
@@ -51861,14 +52606,14 @@ function walkFiles(root) {
51861
52606
  }
51862
52607
  function bomPathFor(root) {
51863
52608
  try {
51864
- const registry2 = JSON.parse((0, import_node_fs50.readFileSync)((0, import_node_path48.join)(root, "surfaces.json"), "utf8"));
51865
- return (0, import_node_path48.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
52609
+ const registry2 = JSON.parse((0, import_node_fs51.readFileSync)((0, import_node_path49.join)(root, "surfaces.json"), "utf8"));
52610
+ return (0, import_node_path49.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
51866
52611
  } catch {
51867
- return (0, import_node_path48.join)(root, "distribution-bom.json");
52612
+ return (0, import_node_path49.join)(root, "distribution-bom.json");
51868
52613
  }
51869
52614
  }
51870
52615
  function rebuildTo(packageRoot, outDir) {
51871
- (0, import_node_child_process18.execFileSync)(process.execPath, ["build.mjs"], {
52616
+ (0, import_node_child_process19.execFileSync)(process.execPath, ["build.mjs"], {
51872
52617
  cwd: packageRoot,
51873
52618
  env: { ...process.env, MMI_DIST_OUTDIR: outDir },
51874
52619
  windowsHide: true,
@@ -51877,35 +52622,35 @@ function rebuildTo(packageRoot, outDir) {
51877
52622
  });
51878
52623
  }
51879
52624
  function runDistStatus(root) {
51880
- const stage = (0, import_node_fs50.mkdtempSync)((0, import_node_path48.join)((0, import_node_os22.tmpdir)(), "mmi-dist-drift-"));
52625
+ const stage = (0, import_node_fs51.mkdtempSync)((0, import_node_path49.join)((0, import_node_os23.tmpdir)(), "mmi-dist-drift-"));
51881
52626
  let overlayCount = 0;
51882
52627
  try {
51883
- const cliOut = (0, import_node_path48.join)(stage, "cli-dist");
51884
- const hubOut = (0, import_node_path48.join)(stage, "hub-dist");
51885
- rebuildTo((0, import_node_path48.join)(root, "cli"), cliOut);
51886
- rebuildTo((0, import_node_path48.join)(root, "updater"), hubOut);
52628
+ const cliOut = (0, import_node_path49.join)(stage, "cli-dist");
52629
+ const hubOut = (0, import_node_path49.join)(stage, "hub-dist");
52630
+ rebuildTo((0, import_node_path49.join)(root, "cli"), cliOut);
52631
+ rebuildTo((0, import_node_path49.join)(root, "updater"), hubOut);
51887
52632
  const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
51888
52633
  const rebuilt = (path2) => {
51889
52634
  const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
51890
- return spec ? readOrNull((0, import_node_path48.join)(outDirFor(spec.packageDir), spec.output)) : null;
52635
+ return spec ? readOrNull((0, import_node_path49.join)(outDirFor(spec.packageDir), spec.output)) : null;
51891
52636
  };
51892
- const committed = (path2) => readOrNull((0, import_node_path48.join)(root, path2));
51893
- const tree = (path2) => readOrNull((0, import_node_path48.join)(root, path2));
51894
- const distRoot = (0, import_node_path48.join)(root, "cli", "dist");
51895
- const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path48.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
51896
- const bom = JSON.parse((0, import_node_fs50.readFileSync)(bomPathFor(root), "utf8"));
52637
+ const committed = (path2) => readOrNull((0, import_node_path49.join)(root, path2));
52638
+ const tree = (path2) => readOrNull((0, import_node_path49.join)(root, path2));
52639
+ const distRoot = (0, import_node_path49.join)(root, "cli", "dist");
52640
+ const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path49.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
52641
+ const bom = JSON.parse((0, import_node_fs51.readFileSync)(bomPathFor(root), "utf8"));
51897
52642
  const digest = (entries) => {
51898
- const overlay = (0, import_node_path48.join)(stage, `overlay-${overlayCount++}`);
52643
+ const overlay = (0, import_node_path49.join)(stage, `overlay-${overlayCount++}`);
51899
52644
  for (const entry of entries) {
51900
- const target = (0, import_node_path48.join)(overlay, entry.path);
51901
- (0, import_node_fs50.mkdirSync)((0, import_node_path48.dirname)(target), { recursive: true });
51902
- (0, import_node_fs50.writeFileSync)(target, entry.bytes);
52645
+ const target = (0, import_node_path49.join)(overlay, entry.path);
52646
+ (0, import_node_fs51.mkdirSync)((0, import_node_path49.dirname)(target), { recursive: true });
52647
+ (0, import_node_fs51.writeFileSync)(target, entry.bytes);
51903
52648
  }
51904
52649
  return digestPackedFiles(overlay, entries.map((entry) => entry.path));
51905
52650
  };
51906
52651
  return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
51907
52652
  } finally {
51908
- (0, import_node_fs50.rmSync)(stage, { recursive: true, force: true });
52653
+ (0, import_node_fs51.rmSync)(stage, { recursive: true, force: true });
51909
52654
  }
51910
52655
  }
51911
52656
 
@@ -51984,7 +52729,7 @@ init_hub_auth();
51984
52729
 
51985
52730
  // src/schedules-lift-command.ts
51986
52731
  var import_promises12 = require("node:fs/promises");
51987
- var import_node_path49 = require("node:path");
52732
+ var import_node_path50 = require("node:path");
51988
52733
  init_clean_exit();
51989
52734
  init_cli_shared();
51990
52735
  var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
@@ -52013,7 +52758,7 @@ async function readWorkflowFiles(dir) {
52013
52758
  const files = [];
52014
52759
  for (const name of names.sort()) {
52015
52760
  if (!/\.ya?ml$/.test(name)) continue;
52016
- files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0, import_node_path49.join)(dir, name), "utf8") });
52761
+ files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0, import_node_path50.join)(dir, name), "utf8") });
52017
52762
  }
52018
52763
  return files;
52019
52764
  }
@@ -52095,9 +52840,9 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
52095
52840
  }
52096
52841
 
52097
52842
  // src/spawn-policy-core.ts
52098
- var import_node_child_process19 = require("node:child_process");
52099
- var import_node_fs51 = require("node:fs");
52100
- var import_node_path50 = require("node:path");
52843
+ var import_node_child_process20 = require("node:child_process");
52844
+ var import_node_fs52 = require("node:fs");
52845
+ var import_node_path51 = require("node:path");
52101
52846
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
52102
52847
  var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
52103
52848
  var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
@@ -52166,7 +52911,7 @@ function findViolationsInSource(raw) {
52166
52911
  return found;
52167
52912
  }
52168
52913
  function policedFiles(root) {
52169
- const r = (0, import_node_child_process19.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
52914
+ const r = (0, import_node_child_process20.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
52170
52915
  cwd: root,
52171
52916
  encoding: "utf8",
52172
52917
  windowsHide: true,
@@ -52183,7 +52928,7 @@ function runSpawnPolicy(root) {
52183
52928
  for (const file of files) {
52184
52929
  let raw;
52185
52930
  try {
52186
- raw = (0, import_node_fs51.readFileSync)((0, import_node_path50.join)(root, file), "utf8");
52931
+ raw = (0, import_node_fs52.readFileSync)((0, import_node_path51.join)(root, file), "utf8");
52187
52932
  } catch {
52188
52933
  continue;
52189
52934
  }
@@ -52203,19 +52948,19 @@ function runSpawnPolicy(root) {
52203
52948
  function registerDeveloperCommands(program3) {
52204
52949
  const rules = program3.command("rules").description("org-managed .gitignore delivery");
52205
52950
  rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").option("--json", "machine-readable output").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
52206
- const path2 = (0, import_node_path51.join)(process.cwd(), ".gitignore");
52207
- const current = (0, import_node_fs52.existsSync)(path2) ? (0, import_node_fs52.readFileSync)(path2, "utf8") : null;
52951
+ const path2 = (0, import_node_path52.join)(process.cwd(), ".gitignore");
52952
+ const current = (0, import_node_fs53.existsSync)(path2) ? (0, import_node_fs53.readFileSync)(path2, "utf8") : null;
52208
52953
  const plan = planManagedGitignore(current);
52209
52954
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
52210
52955
  if (opts.json) {
52211
- if (opts.write && plan.changed) (0, import_node_fs52.writeFileSync)(path2, plan.content, "utf8");
52956
+ if (opts.write && plan.changed) (0, import_node_fs53.writeFileSync)(path2, plan.content, "utf8");
52212
52957
  console.log(JSON.stringify(plan, null, 2));
52213
52958
  if (!opts.write && plan.changed) process.exitCode = 1;
52214
52959
  return;
52215
52960
  }
52216
52961
  if (opts.write) {
52217
52962
  if (plan.changed) {
52218
- (0, import_node_fs52.writeFileSync)(path2, plan.content, "utf8");
52963
+ (0, import_node_fs53.writeFileSync)(path2, plan.content, "utf8");
52219
52964
  console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
52220
52965
  } else {
52221
52966
  console.log("mmi-cli devops org rules gitignore: up to date");
@@ -52478,12 +53223,12 @@ ${r.stderr ?? ""}`).catch(() => "");
52478
53223
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
52479
53224
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
52480
53225
  function envHealLockPath(home) {
52481
- return (0, import_node_path52.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
53226
+ return (0, import_node_path53.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
52482
53227
  }
52483
53228
  async function withEnvHealLock(what, run) {
52484
53229
  try {
52485
53230
  return await withFileLock(
52486
- envHealLockPath((0, import_node_os23.homedir)()),
53231
+ envHealLockPath((0, import_node_os24.homedir)()),
52487
53232
  { staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
52488
53233
  run
52489
53234
  );
@@ -52580,7 +53325,7 @@ function mmiDoctorDeps(opts = {}) {
52580
53325
  const configRoot = surfaceConfigRoot(surface);
52581
53326
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
52582
53327
  const plan = buildPluginCachePlan(
52583
- (0, import_node_os23.homedir)(),
53328
+ (0, import_node_os24.homedir)(),
52584
53329
  running,
52585
53330
  pluginCacheFsDeps(configRoot, () => 0),
52586
53331
  { configRoot, includeStaging: surface !== "codex" }
@@ -52608,14 +53353,14 @@ function mmiDoctorDeps(opts = {}) {
52608
53353
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
52609
53354
  const installed = installedActivePluginVersion(surface);
52610
53355
  const plan = buildPluginCachePlan(
52611
- (0, import_node_os23.homedir)(),
53356
+ (0, import_node_os24.homedir)(),
52612
53357
  running,
52613
53358
  pluginCacheFsDeps(configRoot, () => 0),
52614
53359
  { configRoot, includeStaging: surface !== "codex", installedVersion: installed }
52615
53360
  );
52616
53361
  const result = applyPluginCachePlan(
52617
53362
  plan,
52618
- (p) => (0, import_node_fs53.rmSync)(p, { recursive: true }),
53363
+ (p) => (0, import_node_fs54.rmSync)(p, { recursive: true }),
52619
53364
  stagingApplyFsGuard(configRoot)
52620
53365
  );
52621
53366
  return {
@@ -52650,7 +53395,7 @@ function mmiDoctorDeps(opts = {}) {
52650
53395
  // has no generated routing index would
52651
53396
  // get a permanent — demanding an artifact it never asked for.
52652
53397
  docsIndexState: (root) => {
52653
- if (!(0, import_node_fs53.existsSync)((0, import_node_path52.join)(root, DOCS_INDEX_PATH))) return void 0;
53398
+ if (!(0, import_node_fs54.existsSync)((0, import_node_path53.join)(root, DOCS_INDEX_PATH))) return void 0;
52654
53399
  const real = createDocsIndexDeps(root);
52655
53400
  let docs;
52656
53401
  const listDocs = () => docs ??= real.listDocs();
@@ -52659,7 +53404,7 @@ function mmiDoctorDeps(opts = {}) {
52659
53404
  },
52660
53405
  // #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
52661
53406
  healDocsIndex: (root) => {
52662
- if (!(0, import_node_fs53.existsSync)((0, import_node_path52.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
53407
+ if (!(0, import_node_fs54.existsSync)((0, import_node_path53.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
52663
53408
  const real = createDocsIndexDeps(root);
52664
53409
  let docs;
52665
53410
  const listDocs = () => docs ??= real.listDocs();
@@ -53367,7 +54112,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
53367
54112
  if (dupe) return fail(`org project set: KEY "${dupe}" was passed to both --var and --set; --set is an alias of --var, so pass each KEY once`);
53368
54113
  if (o.secretsFile) {
53369
54114
  try {
53370
- vars.push(`secrets=${(0, import_node_fs53.readFileSync)(o.secretsFile, "utf8")}`);
54115
+ vars.push(`secrets=${(0, import_node_fs54.readFileSync)(o.secretsFile, "utf8")}`);
53371
54116
  } catch (e) {
53372
54117
  return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
53373
54118
  }
@@ -53755,16 +54500,16 @@ function ciAuditDeps2() {
53755
54500
  // gate re-seed step is skipped gracefully rather than failing mid-run.
53756
54501
  readSeedFile: (path2) => {
53757
54502
  if (!root) return null;
53758
- const fullPath = (0, import_node_path52.join)(root, path2);
53759
- return (0, import_node_fs53.existsSync)(fullPath) ? (0, import_node_fs53.readFileSync)(fullPath, "utf8") : null;
54503
+ const fullPath = (0, import_node_path53.join)(root, path2);
54504
+ return (0, import_node_fs54.existsSync)(fullPath) ? (0, import_node_fs54.readFileSync)(fullPath, "utf8") : null;
53760
54505
  }
53761
54506
  };
53762
54507
  }
53763
54508
  function hubRoot2() {
53764
- const fromPkg = (0, import_node_path52.join)(__dirname, "..", "..");
54509
+ const fromPkg = (0, import_node_path53.join)(__dirname, "..", "..");
53765
54510
  const marker = "skills/bootstrap/seeds/manifest.json";
53766
- if ((0, import_node_fs53.existsSync)((0, import_node_path52.join)(fromPkg, marker))) return fromPkg;
53767
- if ((0, import_node_fs53.existsSync)((0, import_node_path52.join)(process.cwd(), marker))) return process.cwd();
54511
+ if ((0, import_node_fs54.existsSync)((0, import_node_path53.join)(fromPkg, marker))) return fromPkg;
54512
+ if ((0, import_node_fs54.existsSync)((0, import_node_path53.join)(process.cwd(), marker))) return process.cwd();
53768
54513
  return null;
53769
54514
  }
53770
54515
  registerQueryCommands(program2);
@@ -53773,6 +54518,7 @@ registerTrainCommandsAndInventory(program2);
53773
54518
  registerFleetLockstepEnforce(program2);
53774
54519
  registerDeployCommands(program2);
53775
54520
  registerSuiteCommands(program2);
54521
+ registerPackagesCommands(program2);
53776
54522
  registerDiscoveryCommands(program2);
53777
54523
  registerExplainCommand(program2);
53778
54524
  registerPrLifecycleCommands(program2);
@@ -53888,10 +54634,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
53888
54634
  targets = resolution.targets;
53889
54635
  }
53890
54636
  const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
53891
- const fileMatrix = (0, import_node_fs53.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs53.readFileSync)("access-matrix.json", "utf8")) : {};
54637
+ const fileMatrix = (0, import_node_fs54.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs54.readFileSync)("access-matrix.json", "utf8")) : {};
53892
54638
  const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
53893
54639
  const dataAccess = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
53894
- const sanctioned = (0, import_node_fs53.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs53.readFileSync)("access-matrix.json", "utf8")) : {};
54640
+ const sanctioned = (0, import_node_fs54.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs54.readFileSync)("access-matrix.json", "utf8")) : {};
53895
54641
  const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
53896
54642
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
53897
54643
  if (!report.ok) process.exitCode = 1;
@@ -53922,16 +54668,16 @@ function directoryBytes(path2) {
53922
54668
  let total = 0;
53923
54669
  let entries;
53924
54670
  try {
53925
- entries = (0, import_node_fs53.readdirSync)(path2, { withFileTypes: true });
54671
+ entries = (0, import_node_fs54.readdirSync)(path2, { withFileTypes: true });
53926
54672
  } catch {
53927
54673
  return 0;
53928
54674
  }
53929
54675
  for (const entry of entries) {
53930
- const child2 = (0, import_node_path52.join)(path2, entry.name);
54676
+ const child2 = (0, import_node_path53.join)(path2, entry.name);
53931
54677
  if (entry.isDirectory()) total += directoryBytes(child2);
53932
54678
  else {
53933
54679
  try {
53934
- total += (0, import_node_fs53.statSync)(child2).size;
54680
+ total += (0, import_node_fs54.statSync)(child2).size;
53935
54681
  } catch {
53936
54682
  }
53937
54683
  }
@@ -53939,25 +54685,25 @@ function directoryBytes(path2) {
53939
54685
  return total;
53940
54686
  }
53941
54687
  function listDirEntries(dir) {
53942
- return (0, import_node_fs53.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
54688
+ return (0, import_node_fs54.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
53943
54689
  }
53944
54690
  function readInstalledPluginRefs(configRoot) {
53945
54691
  const p = installedPluginsPathForConfig(configRoot);
53946
- if (!(0, import_node_fs53.existsSync)(p)) return [];
54692
+ if (!(0, import_node_fs54.existsSync)(p)) return [];
53947
54693
  try {
53948
- return installedPluginPaths((0, import_node_fs53.readFileSync)(p, "utf8"));
54694
+ return installedPluginPaths((0, import_node_fs54.readFileSync)(p, "utf8"));
53949
54695
  } catch {
53950
54696
  return null;
53951
54697
  }
53952
54698
  }
53953
54699
  function pluginCacheFsDeps(configRoot, dirBytes) {
53954
54700
  return {
53955
- exists: (p) => (0, import_node_fs53.existsSync)(p),
53956
- listVersionDirs: (root) => (0, import_node_fs53.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
54701
+ exists: (p) => (0, import_node_fs54.existsSync)(p),
54702
+ listVersionDirs: (root) => (0, import_node_fs54.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
53957
54703
  dirBytes,
53958
- listStagingDirs: (root) => (0, import_node_fs53.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
54704
+ listStagingDirs: (root) => (0, import_node_fs54.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
53959
54705
  try {
53960
- return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path52.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs53.statSync)(p).mtimeMs) };
54706
+ return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path53.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs54.statSync)(p).mtimeMs) };
53961
54707
  } catch {
53962
54708
  return { name: d.name, mtimeMs: Date.now() };
53963
54709
  }
@@ -53971,10 +54717,10 @@ function stagingApplyFsGuard(configRoot) {
53971
54717
  return {
53972
54718
  referencedPaths: () => readInstalledPluginRefs(configRoot),
53973
54719
  mtimeMs: (name) => {
53974
- const p = (0, import_node_path52.join)(stagingRoot, name);
53975
- if (!(0, import_node_fs53.existsSync)(p)) return null;
54720
+ const p = (0, import_node_path53.join)(stagingRoot, name);
54721
+ if (!(0, import_node_fs54.existsSync)(p)) return null;
53976
54722
  try {
53977
- return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs53.statSync)(q).mtimeMs);
54723
+ return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs54.statSync)(q).mtimeMs);
53978
54724
  } catch {
53979
54725
  return null;
53980
54726
  }
@@ -53994,13 +54740,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
53994
54740
  return;
53995
54741
  }
53996
54742
  const plan = buildPluginCachePlan(
53997
- (0, import_node_os23.homedir)(),
54743
+ (0, import_node_os24.homedir)(),
53998
54744
  running,
53999
54745
  pluginCacheFsDeps(configRoot, directoryBytes),
54000
54746
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }
54001
54747
  );
54002
54748
  const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
54003
- const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs53.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
54749
+ const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs54.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
54004
54750
  const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
54005
54751
  if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
54006
54752
  else console.log(renderPluginCachePlan(plan, result));