@mutmutco/codex-plugin 4.5.89 → 4.5.90

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");
@@ -14524,6 +14526,24 @@ async function activateProductRuleset(repo, rulesetBody, client, enforcement = "
14524
14526
  await client.rest("POST", `repos/${repo}/rulesets`, { body: { ...rulesetBody, enforcement }, timeoutMs: 2e4 });
14525
14527
  return { action: "created", enforcement };
14526
14528
  }
14529
+ async function gateContextEnforced(repo, rest) {
14530
+ try {
14531
+ const list = await rest(`repos/${repo}/rulesets?includes_parents=true`);
14532
+ for (const entry of list) {
14533
+ if (entry.id == null) continue;
14534
+ const rs = await rest(`repos/${repo}/rulesets/${entry.id}`);
14535
+ if (rs.target !== "branch" || rs.enforcement !== "active") continue;
14536
+ const hit = (rs.rules ?? []).some((r) => r.type === "required_status_checks" && (r.parameters?.required_status_checks ?? []).some((c) => c.context === PRODUCT_GATE_CONTEXT));
14537
+ if (hit) return true;
14538
+ }
14539
+ return false;
14540
+ } catch {
14541
+ return false;
14542
+ }
14543
+ }
14544
+ async function propagateMayAutoMerge(repo, repoClass, rest) {
14545
+ return repoClass === "content" || gateContextEnforced(repo, rest);
14546
+ }
14527
14547
 
14528
14548
  // src/bootstrap-apply.ts
14529
14549
  var import_node_crypto5 = require("node:crypto");
@@ -17225,10 +17245,10 @@ var rollout_plan_default = {
17225
17245
  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
17246
  },
17227
17247
  baseline: {
17228
- version: "4.5.89",
17229
- tag: "v4.5.89",
17230
- commit: "3288efa7a0af",
17231
- npm: "@mutmutco/cli@4.5.89"
17248
+ version: "4.5.90",
17249
+ tag: "v4.5.90",
17250
+ commit: "f096038f4a17",
17251
+ npm: "@mutmutco/cli@4.5.90"
17232
17252
  },
17233
17253
  exitCriterion: "fleet-n-of-n",
17234
17254
  hubOnlyShortcut: "forbidden",
@@ -17245,14 +17265,14 @@ var rollout_plan_default = {
17245
17265
  repo: "mutmutco/mmi-hub",
17246
17266
  role: "canary",
17247
17267
  schedule: "train",
17248
- v3Target: "v4.5.89"
17268
+ v3Target: "v4.5.90"
17249
17269
  }
17250
17270
  ],
17251
17271
  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
17272
  rollback: {
17253
17273
  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)"
17274
+ mechanism: "npm dist-tag latest -> 4.5.90 and redeploy the Hub Lambda from tag v4.5.90 (f096038f4a17); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
17275
+ v3Target: "v4.5.90 (@mutmutco/cli@4.5.90, tag commit f096038f4a17 \u2014 last known-good release carrying the repo-index v4-only contract)"
17256
17276
  }
17257
17277
  },
17258
17278
  {
@@ -28972,7 +28992,7 @@ var PRIMARY_GROUPS = [
28972
28992
  ["Setup and support", ["bootstrap", "secrets", "docs", "tests", "spawn", "dist"]],
28973
28993
  ["Coordinate and improve", ["wave", "report", "skill-lesson", "closure-rate"]]
28974
28994
  ];
28975
- var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set(["org", "runtime", "plugin"]);
28995
+ var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set(["org", "runtime", "plugin", "packages"]);
28976
28996
  var CAMPAIGN_ORCHESTRATOR_TOP_LEVEL = /* @__PURE__ */ new Set(["pickup"]);
28977
28997
  var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "tests", "spawn", "dist", "wave", "report", "skill-lesson", "closure-rate"]);
28978
28998
  var TOP_LEVEL_ORDER = /* @__PURE__ */ new Map();
@@ -29020,6 +29040,8 @@ var COMMAND_OWNERSHIP = {
29020
29040
  train: { module_owner: "cli/src/train-commands.ts", consumer: "release-operator" },
29021
29041
  suite: { module_owner: "cli/src/suite-commands.ts", consumer: "release-operator" },
29022
29042
  // #7264
29043
+ packages: { module_owner: "cli/src/packages-adopt-export.ts", consumer: "agent-workflow" },
29044
+ // #7833
29023
29045
  bootstrap: { module_owner: "cli/src/bootstrap-commands.ts", consumer: "repo-bootstrap" },
29024
29046
  secrets: { module_owner: "cli/src/secrets-commands.ts", consumer: "authenticated-operator" },
29025
29047
  docs: { module_owner: "cli/src/docs-index-command.ts", consumer: "repo-gates" },
@@ -30595,16 +30617,6 @@ function loadPortRegistry(path2) {
30595
30617
  }
30596
30618
  return out;
30597
30619
  }
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
30620
  function portCursorSeed(registry2) {
30609
30621
  return nextPortBlock(registry2)[0];
30610
30622
  }
@@ -30645,7 +30657,7 @@ function resolvePortRangeInfra(cwd, packageDir) {
30645
30657
  }
30646
30658
  return null;
30647
30659
  }
30648
- async function ensurePortRangeAtomic(repo, path2, allocate, opts = {}) {
30660
+ async function ensurePortRangeAtomic(repo, path2, allocate) {
30649
30661
  const registry2 = loadPortRegistry(path2);
30650
30662
  const existing = existingPortRange(repo, registry2);
30651
30663
  if (existing) return { range: existing, source: "existing" };
@@ -30654,8 +30666,9 @@ async function ensurePortRangeAtomic(repo, path2, allocate, opts = {}) {
30654
30666
  const range = await allocate(seed);
30655
30667
  return { range, source: "ddb" };
30656
30668
  } 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" };
30669
+ const why = e.message;
30670
+ const missing = /@aws-sdk\/[\w-]+/.exec(why)?.[0];
30671
+ 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
30672
  }
30660
30673
  }
30661
30674
 
@@ -30683,7 +30696,13 @@ async function assignPersistedPortRange(repo, slug, reg, opts) {
30683
30696
  if (!Array.isArray(parsed.range) || parsed.range.length !== 2) throw new Error("port-ddb: no range in output");
30684
30697
  return parsed.range;
30685
30698
  };
30686
- const { range: [start, end], source } = await ensurePortRangeAtomic(repo, path2, allocate);
30699
+ let result;
30700
+ try {
30701
+ result = await ensurePortRangeAtomic(repo, path2, allocate);
30702
+ } catch (e) {
30703
+ return { ok: false, error: e.message };
30704
+ }
30705
+ const { range: [start, end], source } = result;
30687
30706
  const write = await upsertProject(slug, { portRange: { start, end } }, reg);
30688
30707
  if (!write.ok && source === "ddb") {
30689
30708
  return {
@@ -34144,7 +34163,7 @@ async function prepareTargetedSeedBranch(repo, plan, target, client, gh2) {
34144
34163
  }
34145
34164
  await verifyTargetedSeedBranch(repo, plan, target, client);
34146
34165
  }
34147
- async function enableTargetedSeedAutoMerge(repo, number, plan, target, expected, client, gh2) {
34166
+ async function verifyTargetedSeedPr(repo, number, plan, target, expected, client) {
34148
34167
  const head = await verifyTargetedSeedBranch(repo, plan, target, client);
34149
34168
  const pr = await client.rest("GET", `repos/${repo}/pulls/${number}`);
34150
34169
  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 +34178,7 @@ async function enableTargetedSeedAutoMerge(repo, number, plan, target, expected,
34159
34178
  if (file?.content !== expected) {
34160
34179
  throw new Error("bootstrap apply: scoped seed content differs from this invocation");
34161
34180
  }
34162
- await gh2(["pr", "merge", String(number), "--repo", repo, "--auto", "--squash", "--match-head-commit", head]);
34181
+ return head;
34163
34182
  }
34164
34183
 
34165
34184
  // src/bootstrap-commands.ts
@@ -34843,8 +34862,6 @@ function registerBootstrapCommands(program3) {
34843
34862
  let seedPrUrl;
34844
34863
  const scopedHead = o.execute && scopedPlan?.branch && configGateAction?.action !== "skip" ? await verifyTargetedSeedBranch(repo, scopedPlan, onlyTarget, controlClient) : null;
34845
34864
  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
34865
  const openPrs = await gh2(["pr", "list", "--repo", repo, "--head", seedPlan.branch, "--base", baseBranch, "--state", "open", "--json", "number,url"]);
34849
34866
  const prDecision = decideSeedPrAction(JSON.parse(openPrs.stdout || "[]"));
34850
34867
  if (prDecision.action === "reuse") {
@@ -34870,22 +34887,13 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
34870
34887
  ]), "bootstrap apply pr create");
34871
34888
  seedPrUrl = created.url;
34872
34889
  }
34873
- let autoMergeEnabled = true;
34890
+ const number = Number(seedPrUrl.split("/").pop());
34874
34891
  if (scopedPlan) {
34875
34892
  if (scopedContent === void 0) throw new Error("bootstrap apply: scoped seed content is unresolved");
34876
- const number = Number(seedPrUrl.split("/").pop());
34877
34893
  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})`);
34894
+ await verifyTargetedSeedPr(repo, number, scopedPlan, onlyTarget, scopedContent, controlClient);
34895
+ }
34896
+ 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
34897
  }
34890
34898
  if (o.execute && !onlyTarget) {
34891
34899
  const desiredBranches = branchesForTrack(effectiveTrack);
@@ -34932,6 +34940,7 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
34932
34940
  applied.push(`has_wiki=false (failed: ${e.message})`);
34933
34941
  }
34934
34942
  }
34943
+ const deferredFailures = [];
34935
34944
  if (o.execute && !onlyTarget && o.class === "deployable") {
34936
34945
  try {
34937
34946
  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 +34956,7 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
34947
34956
  const client = controlClient;
34948
34957
  const gateFacts = await readBootstrapGitHubFacts(repo, effectiveTrack, client);
34949
34958
  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
- }
34959
+ if (rulesetBlockers.length) throw new Error(`facts became unreadable before PUT; refusing write: ${rulesetBlockers.join("; ")}`);
34953
34960
  const activation = await activateProductRuleset(
34954
34961
  repo,
34955
34962
  stripRulesetComment(rulesetContent),
@@ -34960,7 +34967,8 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
34960
34967
  `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
34968
  );
34962
34969
  } catch (e) {
34963
- return failGraceful(`bootstrap apply: product ruleset activation failed: ${e.message}`);
34970
+ deferredFailures.push(`product ruleset not written (${e.message}) \u2014 re-run bootstrap apply once ${PRODUCT_GATE_PATH2} has a completed run`);
34971
+ applied.push(`product ruleset (REFUSED: ${e.message})`);
34964
34972
  }
34965
34973
  }
34966
34974
  }
@@ -35047,6 +35055,7 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
35047
35055
  );
35048
35056
  } else {
35049
35057
  applied.push(`portRange (failed: ${assigned.error})`);
35058
+ deferredFailures.push(`portRange not assigned: ${assigned.error}`);
35050
35059
  }
35051
35060
  }
35052
35061
  } else {
@@ -35124,6 +35133,8 @@ ${renderBootstrapAncillaryPlan(ancillaryPlan, environmentPlan, repoEnvironmentSp
35124
35133
  LIVE apply to ${repo}:
35125
35134
  ${applied.join("\n ")}`);
35126
35135
  }
35136
+ if (deferredFailures.length) return failGraceful(`bootstrap apply: incomplete \u2014 every other step ran:
35137
+ ${deferredFailures.join("\n ")}`);
35127
35138
  });
35128
35139
  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
35140
  const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
@@ -35371,8 +35382,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
35371
35382
  prUrl = created.url;
35372
35383
  }
35373
35384
  if (rec.wave !== 0) {
35374
- await gh2(["pr", "merge", prUrl, "--repo", rec.repo, "--auto", "--squash"]).catch(() => {
35385
+ const enforced = await propagateMayAutoMerge(rec.repo, classOf(rec.repo), async (path2) => JSON.parse((await gh2(["api", path2])).stdout || "[]"));
35386
+ if (enforced) await gh2(["pr", "merge", prUrl, "--repo", rec.repo, "--auto", "--squash"]).catch(() => {
35375
35387
  });
35388
+ 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
35389
  }
35377
35390
  rec.prUrl = prUrl;
35378
35391
  }
@@ -37054,9 +37067,9 @@ A hotfix branch is cut from origin/main and carries ONLY the fixes --from names,
37054
37067
  async function preflightHotfixBatchPicks(deps, sources, pickTolerated) {
37055
37068
  if (sources.length === 0) return { failures: [], skipped: [] };
37056
37069
  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-"));
37070
+ const { tmpdir: tmpdir12 } = await import("node:os");
37071
+ const { join: join47 } = await import("node:path");
37072
+ const dir = await mkdtemp2(join47(tmpdir12(), "mmi-hotfix-preflight-"));
37060
37073
  const wt = { run: (file, args) => deps.run(file, file === "git" ? ["-C", dir, ...args] : args) };
37061
37074
  const failures = [];
37062
37075
  const skipped = [];
@@ -47591,16 +47604,732 @@ function suiteStatusDeps(github, registry2) {
47591
47604
  };
47592
47605
  }
47593
47606
 
47607
+ // src/packages-adopt-export.ts
47608
+ var import_node_child_process18 = require("node:child_process");
47609
+ var import_node_fs45 = require("node:fs");
47610
+ var import_node_os21 = require("node:os");
47611
+ var import_node_path44 = require("node:path");
47612
+ var import_node_util6 = require("node:util");
47613
+ init_cli_shared();
47614
+
47615
+ // ../scripts/package_export.py
47616
+ var package_export_default = `#!/usr/bin/env python3
47617
+ """Export and offline verification of exact original package registry archives (#7805)."""
47618
+ import base64
47619
+ import binascii
47620
+ from datetime import datetime, timedelta, timezone
47621
+ import gzip
47622
+ import hashlib
47623
+ import hmac
47624
+ import io
47625
+ import json
47626
+ import os
47627
+ import pathlib
47628
+ import re
47629
+ import stat
47630
+ import struct
47631
+ import sys
47632
+ import tarfile
47633
+ import zipfile
47634
+ import zlib
47635
+ import urllib.error
47636
+ import urllib.request
47637
+ from urllib.parse import urlsplit
47638
+
47639
+ PACKAGES = ("@mutmutco/mmwork-ds", "@mutmutco/mmwork-screens")
47640
+ MAX_METADATA_BYTES = 2 * 1024 * 1024
47641
+ MAX_TARBALL_BYTES = 64 * 1024 * 1024
47642
+ MAX_ARTIFACT_BYTES = 160 * 1024 * 1024
47643
+ MAX_ZIP_DIRECTORY_BYTES = 8192
47644
+ MAX_UNPACKED_BYTES = 256 * 1024 * 1024
47645
+ MAX_MEMBERS = 10000 # Yielded entries; tarfile consumes PAX/GNU extension headers internally.
47646
+ MAX_PATH_LENGTH = 1024
47647
+ MAX_PATH_DEPTH = 32
47648
+ MAX_COMPONENT_LENGTH = 255
47649
+ WINDOWS_DEVICE = re.compile(r"con|conin\\$|conout\\$|prn|aux|nul|com[1-9]|lpt[1-9]", re.IGNORECASE)
47650
+ EXACT_VERSION = re.compile(
47651
+ r"(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)"
47652
+ r"(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?"
47653
+ r"(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?"
47654
+ )
47655
+
47656
+
47657
+ def reject_constant(_):
47658
+ raise ValueError("nonstandard-json")
47659
+
47660
+
47661
+ def unique_object(pairs):
47662
+ result = {}
47663
+ for key, value in pairs:
47664
+ if key in result:
47665
+ raise ValueError("registry-metadata")
47666
+ result[key] = value
47667
+ return result
47668
+
47669
+
47670
+ def verify_artifact_digest(provider_digest, zip_bytes):
47671
+ """Bind whole bytes to an independently trusted provider digest, not an embedded claim.
47672
+
47673
+ This does not authenticate the digest's source or validate ZIP members.
47674
+ """
47675
+ if (type(zip_bytes) is not bytes or not 0 < len(zip_bytes) <= MAX_ARTIFACT_BYTES
47676
+ or type(provider_digest) is not str
47677
+ or not re.fullmatch(r"sha256:[0-9a-f]{64}", provider_digest)):
47678
+ raise ValueError("artifact-integrity")
47679
+ digest = hashlib.sha256(zip_bytes).hexdigest()
47680
+ if not hmac.compare_digest(provider_digest[7:], digest):
47681
+ raise ValueError("artifact-integrity")
47682
+ return {"artifactSha256": digest, "artifactBytes": len(zip_bytes)}
47683
+
47684
+
47685
+ def verify_artifact_receipt(provider_bytes, zip_bytes, expected, now):
47686
+ """Check separately supplied provider metadata against trusted external selectors.
47687
+
47688
+ The caller must obtain metadata and selectors independently of the artifact.
47689
+ This cannot authenticate their origin or establish workflow/requester authority.
47690
+ Optional node_id, url, archive_download_url and updated_at are ignored, not validated.
47691
+ """
47692
+ keys = {"artifactId", "artifactName", "repositoryId", "runId", "headSha", "headBranch", "digest"}
47693
+ try:
47694
+ if type(expected) is not dict:
47695
+ raise ValueError()
47696
+ expected = expected.copy()
47697
+ if set(expected) != keys:
47698
+ raise ValueError()
47699
+ if any(type(expected[key]) is not int or not 0 < expected[key] < (1 << 53)
47700
+ for key in ("artifactId", "repositoryId", "runId")):
47701
+ raise ValueError()
47702
+ if (type(expected["headSha"]) is not str or not re.fullmatch(r"[0-9a-f]{40}", expected["headSha"])
47703
+ or any(type(expected[key]) is not str or not 0 < len(expected[key]) <= 128
47704
+ or any(ord(char) < 33 or ord(char) > 126 for char in expected[key])
47705
+ for key in ("artifactName", "headBranch"))):
47706
+ raise ValueError()
47707
+ if type(provider_bytes) is not bytes or not 0 < len(provider_bytes) <= MAX_METADATA_BYTES:
47708
+ raise ValueError()
47709
+ provider = json.loads(provider_bytes.decode("utf-8"), object_pairs_hook=unique_object,
47710
+ parse_constant=reject_constant)
47711
+ required = {"id", "name", "size_in_bytes", "digest", "expired", "created_at", "expires_at", "workflow_run"}
47712
+ if (type(provider) is not dict or not required <= set(provider)
47713
+ or set(provider) - required - {"node_id", "url", "archive_download_url", "updated_at"}):
47714
+ raise ValueError()
47715
+ run = provider["workflow_run"]
47716
+ if (type(run) is not dict or set(run) != {"id", "repository_id", "head_repository_id", "head_sha", "head_branch"}
47717
+ or provider["expired"] is not False):
47718
+ raise ValueError()
47719
+ if type(now) is not datetime or now.utcoffset() != timedelta(0):
47720
+ raise ValueError()
47721
+ dates = []
47722
+ for key in ("created_at", "expires_at"):
47723
+ value = provider[key]
47724
+ 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):
47725
+ raise ValueError()
47726
+ dates.append(datetime.strptime(value, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc))
47727
+ created, expires = dates
47728
+ if not created <= now < expires or not timedelta(0) < expires - created <= timedelta(days=1):
47729
+ raise ValueError()
47730
+ for actual, wanted in [(provider["id"], expected["artifactId"]),
47731
+ (run["id"], expected["runId"]),
47732
+ (run["repository_id"], expected["repositoryId"]),
47733
+ (run["head_repository_id"], expected["repositoryId"])]:
47734
+ if type(actual) is not int or actual != wanted:
47735
+ raise ValueError()
47736
+ for actual, wanted in [(provider["name"], expected["artifactName"]),
47737
+ (provider["digest"], expected["digest"]),
47738
+ (run["head_sha"], expected["headSha"]),
47739
+ (run["head_branch"], expected["headBranch"])]:
47740
+ if type(actual) is not str or actual != wanted:
47741
+ raise ValueError()
47742
+ receipt = verify_artifact_digest(expected["digest"], zip_bytes)
47743
+ if type(provider["size_in_bytes"]) is not int or provider["size_in_bytes"] != len(zip_bytes):
47744
+ raise ValueError()
47745
+ except (ValueError, RecursionError):
47746
+ raise ValueError("artifact-receipt") from None
47747
+ return {**expected, **receipt}
47748
+
47749
+
47750
+ def inspect_artifact_zip(zip_bytes):
47751
+ """Read five proposed export members in memory without extracting or executing.
47752
+
47753
+ Authenticate whole artifact bytes before this stage. Returned member bytes still
47754
+ require registry integrity checks; the manifest is untrusted, not an authority.
47755
+ Byte/output limits are not a total process-memory budget, including codec remainders.
47756
+ """
47757
+ limits = {"manifest.json": MAX_METADATA_BYTES,
47758
+ "mmwork-ds.metadata.json": MAX_METADATA_BYTES, "mmwork-ds.tgz": MAX_TARBALL_BYTES,
47759
+ "mmwork-screens.metadata.json": MAX_METADATA_BYTES, "mmwork-screens.tgz": MAX_TARBALL_BYTES}
47760
+ try:
47761
+ if type(zip_bytes) is not bytes or not 0 < len(zip_bytes) <= MAX_ARTIFACT_BYTES:
47762
+ raise ValueError()
47763
+ stream = io.BytesIO(zip_bytes)
47764
+ # ZipFile allocates every central-directory entry before application checks.
47765
+ # Use stdlib's bounded EOCD reader and struct definitions for a strict profile audit.
47766
+ end = zipfile._EndRecData(stream)
47767
+ if (end is None or end[zipfile._ECD_SIGNATURE] != zipfile.stringEndArchive
47768
+ or end[zipfile._ECD_DISK_NUMBER] != 0
47769
+ or end[zipfile._ECD_DISK_START] != 0
47770
+ or end[zipfile._ECD_ENTRIES_THIS_DISK] != len(limits)
47771
+ or end[zipfile._ECD_ENTRIES_TOTAL] != len(limits)
47772
+ or not 0 < end[zipfile._ECD_SIZE] <= MAX_ZIP_DIRECTORY_BYTES
47773
+ or end[zipfile._ECD_COMMENT_SIZE] != 0
47774
+ or end[zipfile._ECD_LOCATION] + zipfile.sizeEndCentDir != len(zip_bytes)
47775
+ or end[zipfile._ECD_OFFSET] + end[zipfile._ECD_SIZE] != end[zipfile._ECD_LOCATION]):
47776
+ raise ValueError()
47777
+ directory = memoryview(zip_bytes)[end[zipfile._ECD_OFFSET]:end[zipfile._ECD_LOCATION]]
47778
+ allowed = {name.encode("ascii") for name in limits}
47779
+ seen = set()
47780
+ cursor = 0
47781
+ # ZipFile accepts short variable-field reads, so audit declared boundaries first.
47782
+ while cursor < len(directory):
47783
+ fields = struct.unpack_from(zipfile.structCentralDir, directory, cursor)
47784
+ start = cursor + zipfile.sizeCentralDir
47785
+ stop = start + fields[zipfile._CD_FILENAME_LENGTH]
47786
+ if (fields[zipfile._CD_SIGNATURE] != zipfile.stringCentralDir
47787
+ or fields[zipfile._CD_EXTRA_FIELD_LENGTH] != 0 or fields[zipfile._CD_COMMENT_LENGTH] != 0
47788
+ or stop > len(directory)):
47789
+ raise ValueError()
47790
+ name = bytes(directory[start:stop])
47791
+ if name not in allowed or name in seen:
47792
+ raise ValueError()
47793
+ seen.add(name)
47794
+ cursor = stop
47795
+ if cursor != len(directory) or seen != allowed:
47796
+ raise ValueError()
47797
+ with zipfile.ZipFile(stream, "r") as bundle:
47798
+ entries = bundle.infolist()
47799
+ if (bundle.start_dir != end[zipfile._ECD_OFFSET]
47800
+ or len(entries) != len(limits) or {entry.orig_filename for entry in entries} != set(limits)
47801
+ or min(entry.header_offset for entry in entries) != 0):
47802
+ raise ValueError()
47803
+ result = {}
47804
+ entries.sort(key=lambda entry: entry.header_offset)
47805
+ for index, entry in enumerate(entries):
47806
+ if (entry.filename != entry.orig_filename or entry.is_dir()
47807
+ or stat.S_IFMT(entry.external_attr >> 16) not in (0, stat.S_IFREG)
47808
+ or entry.external_attr & 0x10 or entry.volume != 0
47809
+ or entry.flag_bits & ~(0x8 | 0x800)
47810
+ or entry.compress_type not in (zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED)
47811
+ or entry.comment or entry.extra
47812
+ or not 0 < entry.file_size <= limits[entry.filename]
47813
+ or not 0 < entry.compress_size <= MAX_ARTIFACT_BYTES):
47814
+ raise ValueError()
47815
+ header = entry.FileHeader(zip64=False)
47816
+ start = entry.header_offset + len(header)
47817
+ data_end = start + entry.compress_size
47818
+ next_offset = entries[index + 1].header_offset if index + 1 < len(entries) else bundle.start_dir
47819
+ if zip_bytes[entry.header_offset:start] != header or data_end > next_offset:
47820
+ raise ValueError()
47821
+ span = next_offset - data_end
47822
+ if span not in ((12, 16) if entry.flag_bits & 0x8 else (0,)):
47823
+ raise ValueError()
47824
+ descriptor = zip_bytes[data_end:next_offset]
47825
+ if entry.flag_bits & 0x8:
47826
+ fields = struct.pack("<III", entry.CRC, entry.compress_size, entry.file_size)
47827
+ if descriptor not in (fields, b"PK\\x07\\x08" + fields):
47828
+ raise ValueError()
47829
+ elif descriptor:
47830
+ raise ValueError()
47831
+ compressed = memoryview(zip_bytes)[start:data_end]
47832
+ if entry.compress_type == zipfile.ZIP_STORED:
47833
+ if entry.compress_size != entry.file_size:
47834
+ raise ValueError()
47835
+ payload = bytes(compressed)
47836
+ else:
47837
+ codec = zlib.decompressobj(wbits=-15)
47838
+ payload = codec.decompress(compressed, entry.file_size + 1)
47839
+ # ZipExtFile clips to the declared size and may ignore trailing data.
47840
+ if not codec.eof or codec.unused_data or codec.unconsumed_tail:
47841
+ raise ValueError()
47842
+ if len(payload) != entry.file_size or zlib.crc32(payload) != entry.CRC:
47843
+ raise ValueError()
47844
+ result[entry.filename] = payload
47845
+ return result
47846
+ except (ValueError, OSError, EOFError, NotImplementedError, zipfile.BadZipFile, zlib.error, struct.error):
47847
+ raise ValueError("artifact-members") from None
47848
+
47849
+
47850
+ def inspect_archive(name, version, tarball_bytes):
47851
+ seen = set()
47852
+ files = set()
47853
+ directories = set()
47854
+ package = None
47855
+ end = 0
47856
+ try:
47857
+ with gzip.GzipFile(fileobj=io.BytesIO(tarball_bytes)) as compressed:
47858
+ unpacked = compressed.read(MAX_UNPACKED_BYTES + 1)
47859
+ if len(unpacked) > MAX_UNPACKED_BYTES:
47860
+ raise ValueError()
47861
+ with tarfile.open(fileobj=io.BytesIO(unpacked), mode="r:") as archive:
47862
+ for index, entry in enumerate(archive):
47863
+ if (index >= MAX_MEMBERS or entry.size < 0 or entry.size > MAX_UNPACKED_BYTES
47864
+ or (entry.isdir() and entry.size != 0)):
47865
+ raise ValueError()
47866
+ end = entry.offset_data + ((entry.size + 511) // 512) * 512
47867
+ # Bound names before splitting or allocating retained ancestor prefixes.
47868
+ if len(entry.name) > MAX_PATH_LENGTH:
47869
+ raise ValueError()
47870
+ path = entry.name.rstrip("/") if entry.isdir() else entry.name
47871
+ if path.count("/") >= MAX_PATH_DEPTH:
47872
+ raise ValueError()
47873
+ parts = path.split("/")
47874
+ if (not (entry.isreg() or entry.isdir()) or entry.issparse()
47875
+ or parts[0] != "package" or "\\\\" in path or ":" in path
47876
+ or any(ord(char) < 32 or ord(char) > 126 for char in path)
47877
+ or any(len(part) > MAX_COMPONENT_LENGTH or part in ("", ".", "..")
47878
+ or part.rstrip(". ") != part for part in parts)
47879
+ or path.lower() in seen):
47880
+ raise ValueError()
47881
+ seen.add(path.lower())
47882
+ folded = path.lower()
47883
+ parents = ["/".join(parts[:i]).lower() for i in range(1, len(parts))]
47884
+ if any(parent in files for parent in parents) or (entry.isreg() and folded in directories):
47885
+ raise ValueError()
47886
+ directories.update(parents)
47887
+ if entry.isdir():
47888
+ directories.add(folded)
47889
+ else:
47890
+ files.add(folded)
47891
+ for part in parts:
47892
+ lower = part.lower()
47893
+ if (WINDOWS_DEVICE.fullmatch(part.split(".", 1)[0].rstrip(" "))
47894
+ or any(char in part for char in '<>"|?*')
47895
+ or lower in (".npmrc", ".netrc", ".pypirc", ".git", ".gitconfig", ".git-credentials", ".yarnrc", ".yarnrc.yml")
47896
+ or lower.startswith(".env") or lower.endswith((".env", ".pem", ".key", ".p12", ".pfx", ".log"))):
47897
+ raise ValueError()
47898
+ if path == "package/package.json":
47899
+ if not entry.isreg() or entry.size > MAX_METADATA_BYTES:
47900
+ raise ValueError()
47901
+ with archive.extractfile(entry) as file:
47902
+ package = json.loads(file.read().decode("utf-8"), object_pairs_hook=unique_object,
47903
+ parse_constant=reject_constant)
47904
+ if len(unpacked) - end < 1024 or any(unpacked[end:]):
47905
+ raise ValueError()
47906
+ if type(package) is not dict or package.get("name") != name or package.get("version") != version:
47907
+ raise ValueError()
47908
+ except (ValueError, tarfile.TarError, OSError, EOFError, RecursionError, zlib.error):
47909
+ raise ValueError("archive-members") from None
47910
+
47911
+
47912
+ def check_exact_version(version):
47913
+ match = EXACT_VERSION.fullmatch(version) if type(version) is str and len(version) <= 128 else None
47914
+ if not match or any(
47915
+ part.isdigit() and len(part) > 1 and part.startswith("0")
47916
+ for part in (match[4] or "").split(".")
47917
+ ):
47918
+ raise ValueError("package-identity")
47919
+
47920
+
47921
+ def check_tarball_url(url):
47922
+ # Accept literal paths only: never reinterpret percent escapes or empty delimiters.
47923
+ if (type(url) is not str or any(ord(char) < 33 or ord(char) > 126 for char in url)
47924
+ or any(char in url for char in "\\\\%?#")):
47925
+ raise ValueError("registry-url")
47926
+ try:
47927
+ location = urlsplit(url)
47928
+ except ValueError:
47929
+ raise ValueError("registry-url") from None
47930
+ if (location.scheme != "https" or location.netloc != "npm.pkg.github.com"
47931
+ or not location.path.startswith("/download/")
47932
+ or any(part in ("", ".", "..") for part in location.path.split("/")[1:])):
47933
+ raise ValueError("registry-url")
47934
+
47935
+
47936
+ def verify_registry_archive(name, version, metadata_bytes, tarball_bytes):
47937
+ if (type(metadata_bytes) is not bytes or not 0 < len(metadata_bytes) <= MAX_METADATA_BYTES
47938
+ or type(tarball_bytes) is not bytes or not 0 < len(tarball_bytes) <= MAX_TARBALL_BYTES):
47939
+ raise ValueError("byte-limit")
47940
+ if type(name) is not str or name not in PACKAGES:
47941
+ raise ValueError("package-identity")
47942
+ check_exact_version(version)
47943
+ try:
47944
+ metadata = json.loads(metadata_bytes.decode("utf-8"), object_pairs_hook=unique_object,
47945
+ parse_constant=reject_constant)
47946
+ if type(metadata) is not dict or type(metadata.get("dist")) is not dict:
47947
+ raise ValueError()
47948
+ full_integrity = metadata["dist"].get("integrity")
47949
+ if type(full_integrity) is not str or not full_integrity.startswith("sha512-"):
47950
+ raise ValueError()
47951
+ registry_digest = base64.b64decode(full_integrity[7:], validate=True)
47952
+ if (len(registry_digest) != hashlib.sha512().digest_size
47953
+ or base64.b64encode(registry_digest).decode() != full_integrity[7:]):
47954
+ raise ValueError()
47955
+ except (ValueError, binascii.Error, RecursionError):
47956
+ raise ValueError("registry-metadata") from None
47957
+ if metadata.get("name") != name or metadata.get("version") != version:
47958
+ raise ValueError("package-identity")
47959
+ check_tarball_url(metadata["dist"].get("tarball"))
47960
+ if not hmac.compare_digest(registry_digest, hashlib.sha512(tarball_bytes).digest()):
47961
+ raise ValueError("integrity-mismatch")
47962
+ inspect_archive(name, version, tarball_bytes)
47963
+ return {
47964
+ "name": name,
47965
+ "version": version,
47966
+ "integrity": metadata["dist"]["integrity"],
47967
+ "metadataSha256": hashlib.sha256(metadata_bytes).hexdigest(),
47968
+ "tarballSha256": hashlib.sha256(tarball_bytes).hexdigest(),
47969
+ "metadataBytes": len(metadata_bytes),
47970
+ "tarballBytes": len(tarball_bytes),
47971
+ }
47972
+
47973
+
47974
+ # --- Export (runs only inside .github/workflows/package-export.yml) ---------------------------
47975
+ TRUSTED_REPOSITORY = "mutmutco/MMI-Hub"
47976
+ TRUSTED_WORKFLOW = ".github/workflows/package-export.yml"
47977
+ TRUSTED_REF = "refs/heads/development"
47978
+ REGISTRY = "https://npm.pkg.github.com"
47979
+ MAX_PACKUMENT_BYTES = 16 * 1024 * 1024
47980
+ VERSION_INPUTS = {"@mutmutco/mmwork-ds": "DS_VERSION", "@mutmutco/mmwork-screens": "SCREENS_VERSION"}
47981
+
47982
+
47983
+ def dispatch_identity(env):
47984
+ """Refuse any run that is not this workflow, dispatched on the trusted ref of the trusted repo."""
47985
+ try:
47986
+ identity = {key: env[name] for key, name in [
47987
+ ("repository", "GITHUB_REPOSITORY"), ("repositoryId", "GITHUB_REPOSITORY_ID"),
47988
+ ("workflowRef", "GITHUB_WORKFLOW_REF"), ("ref", "GITHUB_REF"), ("headSha", "GITHUB_SHA"),
47989
+ ("runId", "GITHUB_RUN_ID"), ("runAttempt", "GITHUB_RUN_ATTEMPT"),
47990
+ ("event", "GITHUB_EVENT_NAME"), ("actor", "GITHUB_ACTOR"),
47991
+ ("triggeringActor", "GITHUB_TRIGGERING_ACTOR")]}
47992
+ except KeyError:
47993
+ raise ValueError("dispatch-identity") from None
47994
+ if (identity["repository"] != TRUSTED_REPOSITORY or identity["ref"] != TRUSTED_REF
47995
+ or identity["event"] != "workflow_dispatch"
47996
+ or identity["workflowRef"] != f"{TRUSTED_REPOSITORY}/{TRUSTED_WORKFLOW}@{TRUSTED_REF}"
47997
+ or not re.fullmatch(r"[0-9a-f]{40}", identity["headSha"])
47998
+ or not all(re.fullmatch(r"[1-9][0-9]{0,15}", identity[key])
47999
+ for key in ("repositoryId", "runId", "runAttempt"))
48000
+ or not all(re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9-]{0,38}(\\[bot\\])?", identity[key])
48001
+ for key in ("actor", "triggeringActor"))):
48002
+ raise ValueError("dispatch-identity")
48003
+ return identity
48004
+
48005
+
48006
+ class HttpsOnlyRedirect(urllib.request.HTTPRedirectHandler):
48007
+ def redirect_request(self, req, fp, code, msg, headers, newurl):
48008
+ if urlsplit(newurl).scheme != "https":
48009
+ raise ValueError("registry-url")
48010
+ return super().redirect_request(req, fp, code, msg, headers, newurl)
48011
+
48012
+
48013
+ def default_opener():
48014
+ # No proxy handler: the token goes only to the registry host named in the request.
48015
+ return urllib.request.build_opener(urllib.request.ProxyHandler({}), HttpsOnlyRedirect())
48016
+
48017
+
48018
+ def fetch(opener, url, token, limit):
48019
+ request = urllib.request.Request(url, headers={"Accept": "application/json"})
48020
+ # Unredirected: the registry's redirect to blob storage never carries the token.
48021
+ request.add_unredirected_header("Authorization", f"Bearer {token}")
48022
+ with opener.open(request, timeout=60) as response:
48023
+ body = response.read(limit + 1)
48024
+ if len(body) > limit or not body:
48025
+ raise ValueError("byte-limit")
48026
+ return body
48027
+
48028
+
48029
+ def export_package(opener, token, name, version):
48030
+ check_exact_version(version)
48031
+ try:
48032
+ packument_bytes = fetch(opener, f"{REGISTRY}/{name.replace('/', '%2f')}", token, MAX_PACKUMENT_BYTES)
48033
+ except urllib.error.HTTPError as error:
48034
+ # Name the package and the registry's own short reason (body only; no request headers).
48035
+ reason = error.read(300).decode("utf-8", "replace").replace(token, "***")
48036
+ raise ValueError(f"registry {name}: HTTP {error.code} {reason}") from None
48037
+ try:
48038
+ packument = json.loads(packument_bytes.decode("utf-8"), object_pairs_hook=unique_object,
48039
+ parse_constant=reject_constant)
48040
+ if (type(packument) is not dict or packument.get("name") != name
48041
+ or type(packument.get("versions")) is not dict):
48042
+ raise ValueError()
48043
+ document = packument["versions"][version]
48044
+ if type(document) is not dict or type(document.get("dist")) is not dict:
48045
+ raise ValueError()
48046
+ except (ValueError, KeyError, RecursionError):
48047
+ raise ValueError("registry-metadata") from None
48048
+ check_tarball_url(document["dist"].get("tarball"))
48049
+ metadata_bytes = json.dumps(document, sort_keys=True, separators=(",", ":")).encode()
48050
+ tarball_bytes = fetch(opener, document["dist"]["tarball"], token, MAX_TARBALL_BYTES)
48051
+ receipt = verify_registry_archive(name, version, metadata_bytes, tarball_bytes)
48052
+ receipt["packumentSha256"] = hashlib.sha256(packument_bytes).hexdigest()
48053
+ return metadata_bytes, tarball_bytes, receipt
48054
+
48055
+
48056
+ def export(env, out_dir, opener=None):
48057
+ """Write exactly the five artifact members into a new directory. Nothing else is written."""
48058
+ identity = dispatch_identity(env)
48059
+ versions = {name: env.get(variable) for name, variable in VERSION_INPUTS.items()}
48060
+ for version in versions.values():
48061
+ check_exact_version(version)
48062
+ token = env.get("GITHUB_PACKAGES_TOKEN")
48063
+ if type(token) is not str or not token.strip():
48064
+ raise ValueError("registry-token")
48065
+ opener = opener or default_opener()
48066
+ files = {}
48067
+ receipts = []
48068
+ for name in PACKAGES:
48069
+ metadata_bytes, tarball_bytes, receipt = export_package(opener, token, name, versions[name])
48070
+ short = name.split("/")[1]
48071
+ files[f"{short}.metadata.json"] = metadata_bytes
48072
+ files[f"{short}.tgz"] = tarball_bytes
48073
+ receipts.append(receipt)
48074
+ manifest = {"schema": 1, "registry": REGISTRY, "source": identity, "packages": receipts}
48075
+ files["manifest.json"] = json.dumps(manifest, sort_keys=True, indent=2).encode()
48076
+ if any(token.encode() in payload for payload in files.values()):
48077
+ raise ValueError("registry-token")
48078
+ out = pathlib.Path(out_dir)
48079
+ out.mkdir(parents=True, exist_ok=False)
48080
+ for member, payload in files.items():
48081
+ (out / member).write_bytes(payload)
48082
+ return manifest
48083
+
48084
+
48085
+ def verify_export(provider_bytes, zip_bytes, expected, versions, now):
48086
+ """Consumer side: bind the artifact to external selectors, then re-verify every package offline.
48087
+
48088
+ versions maps each allowlisted package to the exact version the consumer asked for. The
48089
+ manifest inside the artifact is checked against recomputed receipts, never trusted.
48090
+ """
48091
+ receipt = verify_artifact_receipt(provider_bytes, zip_bytes, expected, now)
48092
+ members = inspect_artifact_zip(zip_bytes)
48093
+ if type(versions) is not dict or set(versions) != set(PACKAGES):
48094
+ raise ValueError("package-identity")
48095
+ packages = []
48096
+ for name in PACKAGES:
48097
+ short = name.split("/")[1]
48098
+ packages.append(verify_registry_archive(name, versions[name], members[f"{short}.metadata.json"],
48099
+ members[f"{short}.tgz"]))
48100
+ try:
48101
+ manifest = json.loads(members["manifest.json"].decode("utf-8"), object_pairs_hook=unique_object,
48102
+ parse_constant=reject_constant)
48103
+ source = manifest["source"]
48104
+ claimed = manifest["packages"]
48105
+ if (manifest.get("registry") != REGISTRY or type(claimed) is not list or len(claimed) != len(packages)
48106
+ or any(type(entry) is not dict or {key: entry.get(key) for key in package} != package
48107
+ for entry, package in zip(claimed, packages))
48108
+ or source.get("repository") != TRUSTED_REPOSITORY
48109
+ or source.get("workflowRef") != f"{TRUSTED_REPOSITORY}/{TRUSTED_WORKFLOW}@{TRUSTED_REF}"
48110
+ or source.get("headSha") != expected["headSha"]
48111
+ or source.get("runId") != str(expected["runId"])
48112
+ or source.get("repositoryId") != str(expected["repositoryId"])):
48113
+ raise ValueError()
48114
+ except (ValueError, KeyError, TypeError, AttributeError, RecursionError):
48115
+ raise ValueError("artifact-manifest") from None
48116
+ return {"artifact": receipt, "packages": packages}
48117
+
48118
+
48119
+ def verify_dir(work_dir, ds_version, screens_version):
48120
+ """Consumer CLI entry (#7833): verify_export over files a caller fetched into work_dir.
48121
+
48122
+ Reads provider.json, artifact.zip and expected.json; writes only the four verified package
48123
+ members into work_dir/verified (a new directory). The caller acts only on those bytes.
48124
+ """
48125
+ work = pathlib.Path(work_dir)
48126
+ result = verify_export((work / "provider.json").read_bytes(), (work / "artifact.zip").read_bytes(),
48127
+ json.loads((work / "expected.json").read_text("utf-8")),
48128
+ {PACKAGES[0]: ds_version, PACKAGES[1]: screens_version}, datetime.now(timezone.utc))
48129
+ members = inspect_artifact_zip((work / "artifact.zip").read_bytes())
48130
+ out = work / "verified"
48131
+ out.mkdir(exist_ok=False)
48132
+ for member, payload in members.items():
48133
+ if member != "manifest.json":
48134
+ (out / member).write_bytes(payload)
48135
+ return result
48136
+
48137
+
48138
+ if __name__ == "__main__":
48139
+ if len(sys.argv) == 5 and sys.argv[1] == "verify":
48140
+ try:
48141
+ print(json.dumps(verify_dir(*sys.argv[2:]), sort_keys=True, indent=2))
48142
+ except (ValueError, OSError) as error:
48143
+ sys.exit(f"package-export verify refused: {error}")
48144
+ sys.exit(0)
48145
+ if len(sys.argv) != 3 or sys.argv[1] != "export":
48146
+ sys.exit("usage: package_export.py export <new-output-dir> | verify <work-dir> <ds-version> <screens-version>")
48147
+ try:
48148
+ result = export(os.environ, sys.argv[2])
48149
+ except (ValueError, OSError, urllib.error.URLError) as error:
48150
+ # Error text carries no request headers: HTTPError renders only the status line.
48151
+ sys.exit(f"package-export refused: {error}")
48152
+ print(json.dumps(result, sort_keys=True, indent=2))
48153
+ `;
48154
+
48155
+ // src/packages-adopt-export.ts
48156
+ var EXPORT_REPO = "mutmutco/MMI-Hub";
48157
+ var EXPORT_WORKFLOW = ".github/workflows/package-export.yml";
48158
+ var PACKAGES = [
48159
+ { name: "@mutmutco/mmwork-ds", short: "mmwork-ds" },
48160
+ { name: "@mutmutco/mmwork-screens", short: "mmwork-screens" }
48161
+ ];
48162
+ var SPEC_SECTIONS = ["dependencies", "devDependencies", "optionalDependencies"];
48163
+ var SHAPE_FIELDS = ["dependencies", "optionalDependencies", "peerDependencies", "peerDependenciesMeta", "bin", "os", "cpu", "hasInstallScript"];
48164
+ 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);
48165
+ function lockShape(meta, field) {
48166
+ if (field === "hasInstallScript") {
48167
+ const scripts = meta.scripts ?? {};
48168
+ return meta.hasInstallScript === true || Boolean(scripts.preinstall || scripts.install || scripts.postinstall) || void 0;
48169
+ }
48170
+ if (field === "bin" && meta.bin && typeof meta.bin === "object") {
48171
+ return Object.fromEntries(Object.entries(meta.bin).map(([k, v]) => [k, String(v).replace(/^(\.\/)+/, "")]));
48172
+ }
48173
+ const value = meta[field];
48174
+ return value && typeof value === "object" && Object.keys(value).length === 0 ? void 0 : value;
48175
+ }
48176
+ function formatLike(original, value) {
48177
+ const indent = /\n([ \t]+)"/.exec(original)?.[1] ?? " ";
48178
+ const text = JSON.stringify(value, null, indent) + "\n";
48179
+ return original.includes("\r\n") ? text.replace(/\n/g, "\r\n") : text;
48180
+ }
48181
+ function planAdoption(packageText, lockText, metadata) {
48182
+ const pkg = JSON.parse(packageText);
48183
+ const lock = JSON.parse(lockText);
48184
+ const root = lock.packages?.[""];
48185
+ if (lock.lockfileVersion < 2 || !root) throw new Error("package-lock.json must be lockfileVersion 2 or 3");
48186
+ const result = { adopted: [], skipped: [] };
48187
+ for (const { name } of PACKAGES) {
48188
+ const section = SPEC_SECTIONS.find((key) => pkg[key]?.[name] !== void 0);
48189
+ if (!section) {
48190
+ result.skipped.push(name);
48191
+ continue;
48192
+ }
48193
+ const meta = metadata[name];
48194
+ const entry = lock.packages[`node_modules/${name}`];
48195
+ if (!entry) throw new Error(`${name} is in package.json but not in package-lock.json; run a normal registry install first`);
48196
+ const nested = Object.keys(lock.packages).filter((key) => key.endsWith(`/node_modules/${name}`));
48197
+ if (nested.length) throw new Error(`${name} has nested lock copies (${nested.join(", ")}); adopt-export edits only the top-level entry`);
48198
+ const changed = SHAPE_FIELDS.filter((field) => stable(field === "hasInstallScript" ? entry[field] || void 0 : entry[field]) !== stable(lockShape(meta, field)));
48199
+ if (changed.length) {
48200
+ 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`);
48201
+ }
48202
+ result.adopted.push({ name, from: entry.version, to: meta.version, resolved: meta.dist.tarball, integrity: meta.dist.integrity });
48203
+ entry.version = meta.version;
48204
+ entry.resolved = meta.dist.tarball;
48205
+ entry.integrity = meta.dist.integrity;
48206
+ for (const field of ["license", "engines"]) {
48207
+ if (meta[field] === void 0) delete entry[field];
48208
+ else entry[field] = meta[field];
48209
+ }
48210
+ pkg[section][name] = meta.version;
48211
+ root[section] = { ...root[section], [name]: meta.version };
48212
+ }
48213
+ return { packageText: formatLike(packageText, pkg), lockText: formatLike(lockText, lock), result };
48214
+ }
48215
+ function expectedFromRun(run, opts) {
48216
+ if (run.id !== opts.run || run.repository?.full_name !== EXPORT_REPO || run.path !== EXPORT_WORKFLOW) {
48217
+ throw new Error(`run ${opts.run} is not a ${EXPORT_REPO} ${EXPORT_WORKFLOW} run`);
48218
+ }
48219
+ return {
48220
+ artifactId: opts.artifact,
48221
+ artifactName: `mmi-package-export-${run.id}-${run.run_attempt}`,
48222
+ repositoryId: run.repository.id,
48223
+ runId: run.id,
48224
+ headSha: run.head_sha,
48225
+ headBranch: run.head_branch,
48226
+ digest: opts.digest
48227
+ };
48228
+ }
48229
+ async function adoptExport(opts, deps) {
48230
+ if (!/^sha256:[0-9a-f]{64}$/.test(opts.digest)) throw new Error("--digest must be sha256:<64 hex>, from the export run summary");
48231
+ const packagePath = (0, import_node_path44.join)(opts.dir, "package.json");
48232
+ const lockPath = (0, import_node_path44.join)(opts.dir, "package-lock.json");
48233
+ const packageText = (0, import_node_fs45.readFileSync)(packagePath, "utf8");
48234
+ const lockText = (0, import_node_fs45.readFileSync)(lockPath, "utf8");
48235
+ const work = (0, import_node_fs45.mkdtempSync)((0, import_node_path44.join)((0, import_node_os21.tmpdir)(), "mmi-adopt-export-"));
48236
+ try {
48237
+ const run = JSON.parse((await deps.ghApi(`repos/${EXPORT_REPO}/actions/runs/${opts.run}`)).toString("utf8"));
48238
+ (0, import_node_fs45.writeFileSync)((0, import_node_path44.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
48239
+ (0, import_node_fs45.writeFileSync)((0, import_node_path44.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
48240
+ (0, import_node_fs45.writeFileSync)((0, import_node_path44.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
48241
+ const script = (0, import_node_path44.join)(work, "package_export.py");
48242
+ (0, import_node_fs45.writeFileSync)(script, package_export_default);
48243
+ await deps.python(script, ["verify", work, opts.ds, opts.screens]);
48244
+ const verified = (0, import_node_path44.join)(work, "verified");
48245
+ 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"))]));
48246
+ const plan = planAdoption(packageText, lockText, metadata);
48247
+ for (const { name } of plan.result.adopted) {
48248
+ const short3 = PACKAGES.find((p) => p.name === name).short;
48249
+ await deps.npm(["cache", "add", (0, import_node_path44.join)(verified, `${short3}.tgz`)], opts.dir);
48250
+ }
48251
+ (0, import_node_fs45.writeFileSync)(packagePath, plan.packageText);
48252
+ (0, import_node_fs45.writeFileSync)(lockPath, plan.lockText);
48253
+ try {
48254
+ await deps.npm(["ci", "--offline", "--ignore-scripts", "--no-audit", "--no-fund"], opts.dir);
48255
+ } catch (error) {
48256
+ (0, import_node_fs45.writeFileSync)(packagePath, packageText);
48257
+ (0, import_node_fs45.writeFileSync)(lockPath, lockText);
48258
+ throw new Error(`npm ci --offline failed, package.json and package-lock.json restored: ${error.message}`);
48259
+ }
48260
+ return plan.result;
48261
+ } finally {
48262
+ (0, import_node_fs45.rmSync)(work, { recursive: true, force: true });
48263
+ }
48264
+ }
48265
+ var execFileBuffer = (0, import_node_util6.promisify)(import_node_child_process18.execFile);
48266
+ var MAX_OUTPUT = 200 * 1024 * 1024;
48267
+ async function findPython() {
48268
+ for (const bin of ["python3", "python"]) {
48269
+ try {
48270
+ await execFileBuffer(bin, ["-I", "-S", "-B", "-c", "import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)"], { windowsHide: true, timeout: 1e4 });
48271
+ return bin;
48272
+ } catch {
48273
+ }
48274
+ }
48275
+ throw new Error("Python 3.11+ is required to run the Hub package-export verifier");
48276
+ }
48277
+ function adoptExportProdDeps() {
48278
+ const failText = (error) => String(error?.stderr || error?.message || error).trim();
48279
+ return {
48280
+ ghApi: async (path2) => {
48281
+ try {
48282
+ return (await execFileBuffer("gh", ["api", "--method", "GET", path2], { encoding: "buffer", maxBuffer: MAX_OUTPUT, windowsHide: true, timeout: 3e5 })).stdout;
48283
+ } catch (error) {
48284
+ throw new Error(`gh api ${path2} failed: ${failText(error)}`);
48285
+ }
48286
+ },
48287
+ python: async (script, args) => {
48288
+ try {
48289
+ return (await execFileBuffer(await findPython(), ["-I", "-S", "-B", script, ...args], { encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true, timeout: 3e5 })).stdout;
48290
+ } catch (error) {
48291
+ throw new Error(failText(error));
48292
+ }
48293
+ },
48294
+ npm: async (args, cwd) => {
48295
+ const [file, argv] = process.platform === "win32" ? ["cmd.exe", ["/c", "npm", ...args]] : ["npm", args];
48296
+ const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => key.toLowerCase() !== "npm_config_allow_scripts"));
48297
+ try {
48298
+ return (await execFileBuffer(file, argv, { cwd, env, encoding: "utf8", maxBuffer: MAX_OUTPUT, windowsHide: true, timeout: 6e5 })).stdout;
48299
+ } catch (error) {
48300
+ throw new Error(`npm ${args[0]} failed: ${failText(error)}`);
48301
+ }
48302
+ }
48303
+ };
48304
+ }
48305
+ function registerPackagesCommands(program3) {
48306
+ const packages = program3.command("packages").description("consumer adoption of verified package exports (#7833)");
48307
+ 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) => {
48308
+ const id = (value, flag) => {
48309
+ if (!/^[1-9][0-9]{0,15}$/.test(value)) fail(`${flag} must be a numeric id`);
48310
+ return Number(value);
48311
+ };
48312
+ try {
48313
+ const result = await adoptExport({ ...opts, run: id(opts.run, "--run"), artifact: id(opts.artifact, "--artifact") }, adoptExportProdDeps());
48314
+ for (const a of result.adopted) console.log(`adopted ${a.name} ${a.from} -> ${a.to} (${a.resolved}, ${a.integrity})`);
48315
+ for (const name of result.skipped) console.log(`skipped ${name}: not a dependency in this package.json`);
48316
+ console.log("npm ci --offline --ignore-scripts passed; commit package.json and package-lock.json");
48317
+ } catch (error) {
48318
+ fail(`packages adopt-export refused: ${error.message}`);
48319
+ }
48320
+ });
48321
+ }
48322
+
47594
48323
  // src/discovery-commands.ts
47595
48324
  init_cli_shared();
47596
48325
 
47597
48326
  // src/wave-status.ts
47598
- var import_node_fs45 = require("node:fs");
48327
+ var import_node_fs46 = require("node:fs");
47599
48328
  function readStageSummary(worktreePath) {
47600
48329
  const statePath = stageStatePath(worktreePath);
47601
- if (!(0, import_node_fs45.existsSync)(statePath)) return void 0;
48330
+ if (!(0, import_node_fs46.existsSync)(statePath)) return void 0;
47602
48331
  try {
47603
- const state = JSON.parse((0, import_node_fs45.readFileSync)(statePath, "utf8"));
48332
+ const state = JSON.parse((0, import_node_fs46.readFileSync)(statePath, "utf8"));
47604
48333
  const port = typeof state.port === "number" ? state.port : void 0;
47605
48334
  if (port == null || !Number.isInteger(port) || port <= 0) return void 0;
47606
48335
  return { port, url: typeof state.url === "string" ? state.url : void 0 };
@@ -47657,9 +48386,9 @@ async function collectWaveStatus(deps) {
47657
48386
  // src/discovery-commands.ts
47658
48387
  init_github_client();
47659
48388
  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");
48389
+ var import_node_fs47 = require("node:fs");
48390
+ var import_node_os22 = require("node:os");
48391
+ var import_node_path45 = require("node:path");
47663
48392
  var GC_GH_TIMEOUT_MS3 = 2e4;
47664
48393
  async function collectStatus() {
47665
48394
  const repo = await resolveRepo();
@@ -47848,10 +48577,10 @@ async function collectOnboardStatus(opts = {}) {
47848
48577
  else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
47849
48578
  else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
47850
48579
  }
47851
- const home = (0, import_node_os21.homedir)();
48580
+ const home = (0, import_node_os22.homedir)();
47852
48581
  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)
48582
+ readKnown: () => readFileSyncSafe((0, import_node_path45.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs47.readFileSync),
48583
+ readSettings: () => readFileSyncSafe((0, import_node_path45.join)(home, ".claude", "settings.json"), import_node_fs47.readFileSync)
47855
48584
  });
47856
48585
  return {
47857
48586
  guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
@@ -50955,8 +51684,8 @@ function registerSessionReport(program3) {
50955
51684
  }
50956
51685
 
50957
51686
  // src/stage-commands.ts
50958
- var import_node_fs47 = require("node:fs");
50959
- var import_node_path45 = require("node:path");
51687
+ var import_node_fs48 = require("node:fs");
51688
+ var import_node_path46 = require("node:path");
50960
51689
  init_cli_shared();
50961
51690
  init_clean_exit();
50962
51691
 
@@ -51202,8 +51931,8 @@ function registerStageCommands(program3) {
51202
51931
  return {
51203
51932
  resolution: decideStage({
51204
51933
  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")),
51934
+ hasCompose: (0, import_node_fs48.existsSync)((0, import_node_path46.join)(process.cwd(), "docker-compose.yml")),
51935
+ hasEnvExample: (0, import_node_fs48.existsSync)((0, import_node_path46.join)(process.cwd(), ".env.example")),
51207
51936
  repo
51208
51937
  }),
51209
51938
  project: project2,
@@ -51285,7 +52014,7 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
51285
52014
  if (!res.derived) return void 0;
51286
52015
  return result.port != null ? stageUrlForPort(result.port) : res.derived.url;
51287
52016
  }
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) => {
52017
+ 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
52018
  const cfg = await loadConfig();
51290
52019
  const reg = registryClientDeps(cfg);
51291
52020
  const slug = slugOf(repo);
@@ -51488,19 +52217,19 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
51488
52217
 
51489
52218
  // src/tenant-artifact.ts
51490
52219
  var import_node_crypto16 = require("node:crypto");
51491
- var import_node_fs48 = require("node:fs");
52220
+ var import_node_fs49 = require("node:fs");
51492
52221
  var import_promises11 = require("node:fs/promises");
51493
- var import_node_path46 = require("node:path");
52222
+ var import_node_path47 = require("node:path");
51494
52223
  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
52224
  var MAX_BYTES = 5 * 1024 * 1024 * 1024;
51496
52225
  async function sha256File(path2) {
51497
52226
  const hash = (0, import_node_crypto16.createHash)("sha256");
51498
- for await (const chunk of (0, import_node_fs48.createReadStream)(path2)) hash.update(chunk);
52227
+ for await (const chunk of (0, import_node_fs49.createReadStream)(path2)) hash.update(chunk);
51499
52228
  return hash.digest("hex");
51500
52229
  }
51501
52230
  async function putTenantArtifact(repo, stage, inputPath, deps) {
51502
52231
  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);
52232
+ const path2 = (0, import_node_path47.resolve)(inputPath);
51504
52233
  const info = await (0, import_promises11.stat)(path2);
51505
52234
  if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
51506
52235
  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 +52247,7 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
51518
52247
  return [key, value];
51519
52248
  }));
51520
52249
  headers["content-length"] = String(info.size);
51521
- const stream = (0, import_node_fs48.createReadStream)(path2);
52250
+ const stream = (0, import_node_fs49.createReadStream)(path2);
51522
52251
  let uploaded;
51523
52252
  try {
51524
52253
  uploaded = await fetch(body.uploadUrl, {
@@ -51657,7 +52386,7 @@ function renderVerifySecrets(body) {
51657
52386
  }
51658
52387
 
51659
52388
  // src/command-register-developer.ts
51660
- var import_node_fs52 = require("node:fs");
52389
+ var import_node_fs53 = require("node:fs");
51661
52390
  init_clean_exit();
51662
52391
  init_cli_shared();
51663
52392
  init_error_codes();
@@ -51690,7 +52419,7 @@ async function resolveWhoami(deps) {
51690
52419
  }
51691
52420
 
51692
52421
  // src/command-register-developer.ts
51693
- var import_node_path51 = require("node:path");
52422
+ var import_node_path52 = require("node:path");
51694
52423
  init_house_map();
51695
52424
  init_hub_url();
51696
52425
 
@@ -51723,21 +52452,21 @@ async function executeWaveLand(plan, deps) {
51723
52452
  }
51724
52453
 
51725
52454
  // src/dist-drift.ts
51726
- var import_node_child_process18 = require("node:child_process");
52455
+ var import_node_child_process19 = require("node:child_process");
51727
52456
  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");
52457
+ var import_node_fs51 = require("node:fs");
52458
+ var import_node_os23 = require("node:os");
52459
+ var import_node_path49 = require("node:path");
51731
52460
 
51732
52461
  // ../scripts/distribution-digest.mjs
51733
52462
  var import_node_crypto17 = require("node:crypto");
51734
- var import_node_fs49 = require("node:fs");
51735
- var import_node_path47 = require("node:path");
52463
+ var import_node_fs50 = require("node:fs");
52464
+ var import_node_path48 = require("node:path");
51736
52465
  var slash = (value) => value.replaceAll("\\", "/");
51737
52466
  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}`)) {
52467
+ const absoluteRoot = (0, import_node_path48.resolve)(root);
52468
+ const target = (0, import_node_path48.resolve)(root, declaredPath);
52469
+ if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path48.sep}`)) {
51741
52470
  throw new Error(`${label} ${declaredPath} escapes the repository root`);
51742
52471
  }
51743
52472
  return target;
@@ -51745,7 +52474,7 @@ function repoPath(root, declaredPath, label) {
51745
52474
  function digestFiles(files) {
51746
52475
  const hash = (0, import_node_crypto17.createHash)("sha256");
51747
52476
  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);
52477
+ const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs50.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs50.readFileSync)(file.absolute);
51749
52478
  hash.update(file.relative, "utf8");
51750
52479
  hash.update("\0");
51751
52480
  hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
@@ -51760,8 +52489,8 @@ function digestFiles(files) {
51760
52489
  function digestPackedFiles(packageRoot, packedFiles) {
51761
52490
  return digestFiles(packedFiles.map((path2) => {
51762
52491
  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) };
52492
+ if (!(0, import_node_fs50.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
52493
+ return { absolute, relative: slash(path2), stat: (0, import_node_fs50.lstatSync)(absolute) };
51765
52494
  }));
51766
52495
  }
51767
52496
 
@@ -51845,13 +52574,13 @@ function renderDistDriftReceipt(receipt) {
51845
52574
  return lines2;
51846
52575
  }
51847
52576
  function readOrNull(path2) {
51848
- return (0, import_node_fs50.existsSync)(path2) ? (0, import_node_fs50.readFileSync)(path2) : null;
52577
+ return (0, import_node_fs51.existsSync)(path2) ? (0, import_node_fs51.readFileSync)(path2) : null;
51849
52578
  }
51850
52579
  function walkFiles(root) {
51851
52580
  const files = [];
51852
52581
  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);
52582
+ for (const entry of (0, import_node_fs51.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
52583
+ const child2 = (0, import_node_path49.join)(directory, entry.name);
51855
52584
  if (entry.isDirectory()) walk2(child2);
51856
52585
  else files.push(child2);
51857
52586
  }
@@ -51861,14 +52590,14 @@ function walkFiles(root) {
51861
52590
  }
51862
52591
  function bomPathFor(root) {
51863
52592
  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");
52593
+ const registry2 = JSON.parse((0, import_node_fs51.readFileSync)((0, import_node_path49.join)(root, "surfaces.json"), "utf8"));
52594
+ return (0, import_node_path49.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
51866
52595
  } catch {
51867
- return (0, import_node_path48.join)(root, "distribution-bom.json");
52596
+ return (0, import_node_path49.join)(root, "distribution-bom.json");
51868
52597
  }
51869
52598
  }
51870
52599
  function rebuildTo(packageRoot, outDir) {
51871
- (0, import_node_child_process18.execFileSync)(process.execPath, ["build.mjs"], {
52600
+ (0, import_node_child_process19.execFileSync)(process.execPath, ["build.mjs"], {
51872
52601
  cwd: packageRoot,
51873
52602
  env: { ...process.env, MMI_DIST_OUTDIR: outDir },
51874
52603
  windowsHide: true,
@@ -51877,35 +52606,35 @@ function rebuildTo(packageRoot, outDir) {
51877
52606
  });
51878
52607
  }
51879
52608
  function runDistStatus(root) {
51880
- const stage = (0, import_node_fs50.mkdtempSync)((0, import_node_path48.join)((0, import_node_os22.tmpdir)(), "mmi-dist-drift-"));
52609
+ const stage = (0, import_node_fs51.mkdtempSync)((0, import_node_path49.join)((0, import_node_os23.tmpdir)(), "mmi-dist-drift-"));
51881
52610
  let overlayCount = 0;
51882
52611
  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);
52612
+ const cliOut = (0, import_node_path49.join)(stage, "cli-dist");
52613
+ const hubOut = (0, import_node_path49.join)(stage, "hub-dist");
52614
+ rebuildTo((0, import_node_path49.join)(root, "cli"), cliOut);
52615
+ rebuildTo((0, import_node_path49.join)(root, "updater"), hubOut);
51887
52616
  const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
51888
52617
  const rebuilt = (path2) => {
51889
52618
  const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
51890
- return spec ? readOrNull((0, import_node_path48.join)(outDirFor(spec.packageDir), spec.output)) : null;
52619
+ return spec ? readOrNull((0, import_node_path49.join)(outDirFor(spec.packageDir), spec.output)) : null;
51891
52620
  };
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"));
52621
+ const committed = (path2) => readOrNull((0, import_node_path49.join)(root, path2));
52622
+ const tree = (path2) => readOrNull((0, import_node_path49.join)(root, path2));
52623
+ const distRoot = (0, import_node_path49.join)(root, "cli", "dist");
52624
+ const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path49.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
52625
+ const bom = JSON.parse((0, import_node_fs51.readFileSync)(bomPathFor(root), "utf8"));
51897
52626
  const digest = (entries) => {
51898
- const overlay = (0, import_node_path48.join)(stage, `overlay-${overlayCount++}`);
52627
+ const overlay = (0, import_node_path49.join)(stage, `overlay-${overlayCount++}`);
51899
52628
  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);
52629
+ const target = (0, import_node_path49.join)(overlay, entry.path);
52630
+ (0, import_node_fs51.mkdirSync)((0, import_node_path49.dirname)(target), { recursive: true });
52631
+ (0, import_node_fs51.writeFileSync)(target, entry.bytes);
51903
52632
  }
51904
52633
  return digestPackedFiles(overlay, entries.map((entry) => entry.path));
51905
52634
  };
51906
52635
  return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
51907
52636
  } finally {
51908
- (0, import_node_fs50.rmSync)(stage, { recursive: true, force: true });
52637
+ (0, import_node_fs51.rmSync)(stage, { recursive: true, force: true });
51909
52638
  }
51910
52639
  }
51911
52640
 
@@ -51984,7 +52713,7 @@ init_hub_auth();
51984
52713
 
51985
52714
  // src/schedules-lift-command.ts
51986
52715
  var import_promises12 = require("node:fs/promises");
51987
- var import_node_path49 = require("node:path");
52716
+ var import_node_path50 = require("node:path");
51988
52717
  init_clean_exit();
51989
52718
  init_cli_shared();
51990
52719
  var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
@@ -52013,7 +52742,7 @@ async function readWorkflowFiles(dir) {
52013
52742
  const files = [];
52014
52743
  for (const name of names.sort()) {
52015
52744
  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") });
52745
+ files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0, import_node_path50.join)(dir, name), "utf8") });
52017
52746
  }
52018
52747
  return files;
52019
52748
  }
@@ -52095,9 +52824,9 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
52095
52824
  }
52096
52825
 
52097
52826
  // 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");
52827
+ var import_node_child_process20 = require("node:child_process");
52828
+ var import_node_fs52 = require("node:fs");
52829
+ var import_node_path51 = require("node:path");
52101
52830
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
52102
52831
  var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
52103
52832
  var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
@@ -52166,7 +52895,7 @@ function findViolationsInSource(raw) {
52166
52895
  return found;
52167
52896
  }
52168
52897
  function policedFiles(root) {
52169
- const r = (0, import_node_child_process19.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
52898
+ const r = (0, import_node_child_process20.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
52170
52899
  cwd: root,
52171
52900
  encoding: "utf8",
52172
52901
  windowsHide: true,
@@ -52183,7 +52912,7 @@ function runSpawnPolicy(root) {
52183
52912
  for (const file of files) {
52184
52913
  let raw;
52185
52914
  try {
52186
- raw = (0, import_node_fs51.readFileSync)((0, import_node_path50.join)(root, file), "utf8");
52915
+ raw = (0, import_node_fs52.readFileSync)((0, import_node_path51.join)(root, file), "utf8");
52187
52916
  } catch {
52188
52917
  continue;
52189
52918
  }
@@ -52203,19 +52932,19 @@ function runSpawnPolicy(root) {
52203
52932
  function registerDeveloperCommands(program3) {
52204
52933
  const rules = program3.command("rules").description("org-managed .gitignore delivery");
52205
52934
  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;
52935
+ const path2 = (0, import_node_path52.join)(process.cwd(), ".gitignore");
52936
+ const current = (0, import_node_fs53.existsSync)(path2) ? (0, import_node_fs53.readFileSync)(path2, "utf8") : null;
52208
52937
  const plan = planManagedGitignore(current);
52209
52938
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
52210
52939
  if (opts.json) {
52211
- if (opts.write && plan.changed) (0, import_node_fs52.writeFileSync)(path2, plan.content, "utf8");
52940
+ if (opts.write && plan.changed) (0, import_node_fs53.writeFileSync)(path2, plan.content, "utf8");
52212
52941
  console.log(JSON.stringify(plan, null, 2));
52213
52942
  if (!opts.write && plan.changed) process.exitCode = 1;
52214
52943
  return;
52215
52944
  }
52216
52945
  if (opts.write) {
52217
52946
  if (plan.changed) {
52218
- (0, import_node_fs52.writeFileSync)(path2, plan.content, "utf8");
52947
+ (0, import_node_fs53.writeFileSync)(path2, plan.content, "utf8");
52219
52948
  console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
52220
52949
  } else {
52221
52950
  console.log("mmi-cli devops org rules gitignore: up to date");
@@ -52478,12 +53207,12 @@ ${r.stderr ?? ""}`).catch(() => "");
52478
53207
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
52479
53208
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
52480
53209
  function envHealLockPath(home) {
52481
- return (0, import_node_path52.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
53210
+ return (0, import_node_path53.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
52482
53211
  }
52483
53212
  async function withEnvHealLock(what, run) {
52484
53213
  try {
52485
53214
  return await withFileLock(
52486
- envHealLockPath((0, import_node_os23.homedir)()),
53215
+ envHealLockPath((0, import_node_os24.homedir)()),
52487
53216
  { staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
52488
53217
  run
52489
53218
  );
@@ -52580,7 +53309,7 @@ function mmiDoctorDeps(opts = {}) {
52580
53309
  const configRoot = surfaceConfigRoot(surface);
52581
53310
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
52582
53311
  const plan = buildPluginCachePlan(
52583
- (0, import_node_os23.homedir)(),
53312
+ (0, import_node_os24.homedir)(),
52584
53313
  running,
52585
53314
  pluginCacheFsDeps(configRoot, () => 0),
52586
53315
  { configRoot, includeStaging: surface !== "codex" }
@@ -52608,14 +53337,14 @@ function mmiDoctorDeps(opts = {}) {
52608
53337
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
52609
53338
  const installed = installedActivePluginVersion(surface);
52610
53339
  const plan = buildPluginCachePlan(
52611
- (0, import_node_os23.homedir)(),
53340
+ (0, import_node_os24.homedir)(),
52612
53341
  running,
52613
53342
  pluginCacheFsDeps(configRoot, () => 0),
52614
53343
  { configRoot, includeStaging: surface !== "codex", installedVersion: installed }
52615
53344
  );
52616
53345
  const result = applyPluginCachePlan(
52617
53346
  plan,
52618
- (p) => (0, import_node_fs53.rmSync)(p, { recursive: true }),
53347
+ (p) => (0, import_node_fs54.rmSync)(p, { recursive: true }),
52619
53348
  stagingApplyFsGuard(configRoot)
52620
53349
  );
52621
53350
  return {
@@ -52650,7 +53379,7 @@ function mmiDoctorDeps(opts = {}) {
52650
53379
  // has no generated routing index would
52651
53380
  // get a permanent — demanding an artifact it never asked for.
52652
53381
  docsIndexState: (root) => {
52653
- if (!(0, import_node_fs53.existsSync)((0, import_node_path52.join)(root, DOCS_INDEX_PATH))) return void 0;
53382
+ if (!(0, import_node_fs54.existsSync)((0, import_node_path53.join)(root, DOCS_INDEX_PATH))) return void 0;
52654
53383
  const real = createDocsIndexDeps(root);
52655
53384
  let docs;
52656
53385
  const listDocs = () => docs ??= real.listDocs();
@@ -52659,7 +53388,7 @@ function mmiDoctorDeps(opts = {}) {
52659
53388
  },
52660
53389
  // #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
52661
53390
  healDocsIndex: (root) => {
52662
- if (!(0, import_node_fs53.existsSync)((0, import_node_path52.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
53391
+ if (!(0, import_node_fs54.existsSync)((0, import_node_path53.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
52663
53392
  const real = createDocsIndexDeps(root);
52664
53393
  let docs;
52665
53394
  const listDocs = () => docs ??= real.listDocs();
@@ -53367,7 +54096,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
53367
54096
  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
54097
  if (o.secretsFile) {
53369
54098
  try {
53370
- vars.push(`secrets=${(0, import_node_fs53.readFileSync)(o.secretsFile, "utf8")}`);
54099
+ vars.push(`secrets=${(0, import_node_fs54.readFileSync)(o.secretsFile, "utf8")}`);
53371
54100
  } catch (e) {
53372
54101
  return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
53373
54102
  }
@@ -53755,16 +54484,16 @@ function ciAuditDeps2() {
53755
54484
  // gate re-seed step is skipped gracefully rather than failing mid-run.
53756
54485
  readSeedFile: (path2) => {
53757
54486
  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;
54487
+ const fullPath = (0, import_node_path53.join)(root, path2);
54488
+ return (0, import_node_fs54.existsSync)(fullPath) ? (0, import_node_fs54.readFileSync)(fullPath, "utf8") : null;
53760
54489
  }
53761
54490
  };
53762
54491
  }
53763
54492
  function hubRoot2() {
53764
- const fromPkg = (0, import_node_path52.join)(__dirname, "..", "..");
54493
+ const fromPkg = (0, import_node_path53.join)(__dirname, "..", "..");
53765
54494
  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();
54495
+ if ((0, import_node_fs54.existsSync)((0, import_node_path53.join)(fromPkg, marker))) return fromPkg;
54496
+ if ((0, import_node_fs54.existsSync)((0, import_node_path53.join)(process.cwd(), marker))) return process.cwd();
53768
54497
  return null;
53769
54498
  }
53770
54499
  registerQueryCommands(program2);
@@ -53773,6 +54502,7 @@ registerTrainCommandsAndInventory(program2);
53773
54502
  registerFleetLockstepEnforce(program2);
53774
54503
  registerDeployCommands(program2);
53775
54504
  registerSuiteCommands(program2);
54505
+ registerPackagesCommands(program2);
53776
54506
  registerDiscoveryCommands(program2);
53777
54507
  registerExplainCommand(program2);
53778
54508
  registerPrLifecycleCommands(program2);
@@ -53888,10 +54618,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
53888
54618
  targets = resolution.targets;
53889
54619
  }
53890
54620
  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")) : {};
54621
+ const fileMatrix = (0, import_node_fs54.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs54.readFileSync)("access-matrix.json", "utf8")) : {};
53892
54622
  const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
53893
54623
  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")) : {};
54624
+ const sanctioned = (0, import_node_fs54.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs54.readFileSync)("access-matrix.json", "utf8")) : {};
53895
54625
  const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
53896
54626
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
53897
54627
  if (!report.ok) process.exitCode = 1;
@@ -53922,16 +54652,16 @@ function directoryBytes(path2) {
53922
54652
  let total = 0;
53923
54653
  let entries;
53924
54654
  try {
53925
- entries = (0, import_node_fs53.readdirSync)(path2, { withFileTypes: true });
54655
+ entries = (0, import_node_fs54.readdirSync)(path2, { withFileTypes: true });
53926
54656
  } catch {
53927
54657
  return 0;
53928
54658
  }
53929
54659
  for (const entry of entries) {
53930
- const child2 = (0, import_node_path52.join)(path2, entry.name);
54660
+ const child2 = (0, import_node_path53.join)(path2, entry.name);
53931
54661
  if (entry.isDirectory()) total += directoryBytes(child2);
53932
54662
  else {
53933
54663
  try {
53934
- total += (0, import_node_fs53.statSync)(child2).size;
54664
+ total += (0, import_node_fs54.statSync)(child2).size;
53935
54665
  } catch {
53936
54666
  }
53937
54667
  }
@@ -53939,25 +54669,25 @@ function directoryBytes(path2) {
53939
54669
  return total;
53940
54670
  }
53941
54671
  function listDirEntries(dir) {
53942
- return (0, import_node_fs53.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
54672
+ return (0, import_node_fs54.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
53943
54673
  }
53944
54674
  function readInstalledPluginRefs(configRoot) {
53945
54675
  const p = installedPluginsPathForConfig(configRoot);
53946
- if (!(0, import_node_fs53.existsSync)(p)) return [];
54676
+ if (!(0, import_node_fs54.existsSync)(p)) return [];
53947
54677
  try {
53948
- return installedPluginPaths((0, import_node_fs53.readFileSync)(p, "utf8"));
54678
+ return installedPluginPaths((0, import_node_fs54.readFileSync)(p, "utf8"));
53949
54679
  } catch {
53950
54680
  return null;
53951
54681
  }
53952
54682
  }
53953
54683
  function pluginCacheFsDeps(configRoot, dirBytes) {
53954
54684
  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),
54685
+ exists: (p) => (0, import_node_fs54.existsSync)(p),
54686
+ listVersionDirs: (root) => (0, import_node_fs54.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
53957
54687
  dirBytes,
53958
- listStagingDirs: (root) => (0, import_node_fs53.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
54688
+ listStagingDirs: (root) => (0, import_node_fs54.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
53959
54689
  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) };
54690
+ return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path53.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs54.statSync)(p).mtimeMs) };
53961
54691
  } catch {
53962
54692
  return { name: d.name, mtimeMs: Date.now() };
53963
54693
  }
@@ -53971,10 +54701,10 @@ function stagingApplyFsGuard(configRoot) {
53971
54701
  return {
53972
54702
  referencedPaths: () => readInstalledPluginRefs(configRoot),
53973
54703
  mtimeMs: (name) => {
53974
- const p = (0, import_node_path52.join)(stagingRoot, name);
53975
- if (!(0, import_node_fs53.existsSync)(p)) return null;
54704
+ const p = (0, import_node_path53.join)(stagingRoot, name);
54705
+ if (!(0, import_node_fs54.existsSync)(p)) return null;
53976
54706
  try {
53977
- return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs53.statSync)(q).mtimeMs);
54707
+ return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs54.statSync)(q).mtimeMs);
53978
54708
  } catch {
53979
54709
  return null;
53980
54710
  }
@@ -53994,13 +54724,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
53994
54724
  return;
53995
54725
  }
53996
54726
  const plan = buildPluginCachePlan(
53997
- (0, import_node_os23.homedir)(),
54727
+ (0, import_node_os24.homedir)(),
53998
54728
  running,
53999
54729
  pluginCacheFsDeps(configRoot, directoryBytes),
54000
54730
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }
54001
54731
  );
54002
54732
  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;
54733
+ const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs54.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
54004
54734
  const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
54005
54735
  if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
54006
54736
  else console.log(renderPluginCachePlan(plan, result));