@mutmutco/cli 3.69.0 → 3.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.cjs +133 -45
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -33,9 +33,11 @@ __export(index_exports, {
33
33
  DEFAULT_PRIORITY: () => DEFAULT_PRIORITY,
34
34
  awsCallerArn: () => awsCallerArn,
35
35
  classifyParseError: () => classifyParseError,
36
+ commandOwnLongFlags: () => commandOwnLongFlags,
36
37
  envHealLockPath: () => envHealLockPath,
37
38
  gcPlan: () => gcPlan,
38
39
  isOrgRegisteredRepo: () => isOrgRegisteredRepo,
40
+ positionalTargetForm: () => positionalTargetForm,
39
41
  registryClientDeps: () => registryClientDeps,
40
42
  repoSlug: () => repoSlug,
41
43
  shouldMarkWorktreeActivity: () => shouldMarkWorktreeActivity,
@@ -7752,7 +7754,13 @@ function collectRegistryRepos(projects) {
7752
7754
  if (![...seen].some((r) => r.toLowerCase() === HUB_REPO.toLowerCase())) seen.add(HUB_REPO);
7753
7755
  return [...seen].sort((a, b) => a.localeCompare(b));
7754
7756
  }
7755
- async function resolveRepoMergeCiPolicy(repo, deps) {
7757
+ async function prTriggeredWorkflowsOnRef(deps, repo, branch, repoClass) {
7758
+ const hasGate = repoClass === "hub" ? true : await contentExists(deps, repo, branch, PRODUCT_GATE_PATH);
7759
+ const workflowPaths = repoClass === "hub" ? [".github/workflows/gate.yml"] : hasGate ? [PRODUCT_GATE_PATH] : await listWorkflowPaths(deps, repo, branch);
7760
+ if (workflowPaths === void 0) return void 0;
7761
+ return filterPullRequestTriggered(deps, repo, branch, workflowPaths);
7762
+ }
7763
+ async function resolveRepoMergeCiPolicy(repo, deps, headRef) {
7756
7764
  const meta = await deps.getProjectMeta(slugFromRepo(repo));
7757
7765
  const repoClass = classifyRepo(repo, meta);
7758
7766
  if (repoClass === "content") {
@@ -7763,17 +7771,25 @@ async function resolveRepoMergeCiPolicy(repo, deps) {
7763
7771
  });
7764
7772
  }
7765
7773
  const baseBranch = "development";
7766
- const hasGate = repoClass === "hub" ? true : await contentExists(deps, repo, baseBranch, PRODUCT_GATE_PATH);
7767
- const workflowPaths = repoClass === "hub" ? [".github/workflows/gate.yml"] : hasGate ? [PRODUCT_GATE_PATH] : await listWorkflowPaths(deps, repo, baseBranch);
7768
- if (workflowPaths === void 0) {
7774
+ const prTriggered = await prTriggeredWorkflowsOnRef(deps, repo, baseBranch, repoClass);
7775
+ if (prTriggered === void 0) {
7769
7776
  return { policy: "wait-for-checks", reason: "workflows listing unreadable (API error) \u2014 failing safe to wait-for-checks" };
7770
7777
  }
7771
- const prTriggered = await filterPullRequestTriggered(deps, repo, baseBranch, workflowPaths);
7772
- return resolveMergeCiPolicy({
7773
- workflowPaths: prTriggered,
7778
+ let effective = prTriggered;
7779
+ let headReason = "";
7780
+ if (!effective.length && headRef && headRef !== baseBranch) {
7781
+ const onHead = await prTriggeredWorkflowsOnRef(deps, repo, headRef, repoClass);
7782
+ if (onHead?.length) {
7783
+ effective = onHead;
7784
+ headReason = ` (on PR head ${headRef}; absent on ${baseBranch})`;
7785
+ }
7786
+ }
7787
+ const resolved = resolveMergeCiPolicy({
7788
+ workflowPaths: effective,
7774
7789
  registryCi: meta?.ci,
7775
7790
  registryRequiredChecks: meta?.requiredChecks
7776
7791
  });
7792
+ return headReason ? { ...resolved, reason: `${resolved.reason}${headReason}` } : resolved;
7777
7793
  }
7778
7794
  async function filterPullRequestTriggered(deps, repo, branch, paths) {
7779
7795
  const kept = [];
@@ -9465,7 +9481,8 @@ var OWNER = "mutmutco";
9465
9481
  var SSM_ROOT = "/mmi-future";
9466
9482
  var PROJECT_TIER_SEGMENT = "dev";
9467
9483
  var ORG_INFRA_SLUG = "_org";
9468
- var KEY_RE = /^(?:[a-z][a-z0-9-]*\/)?[A-Za-z][A-Za-z0-9_]*$/;
9484
+ var KEY_MAX_PATH_SEGMENTS = 4;
9485
+ var KEY_RE = new RegExp(`^(?:[a-z0-9][a-z0-9-]*/){0,${KEY_MAX_PATH_SEGMENTS}}[A-Za-z][A-Za-z0-9_]*$`);
9469
9486
  function isValidSecretKey(key) {
9470
9487
  if (!key || key.length > 256) return false;
9471
9488
  if (key.includes("..") || key.startsWith("/") || key.includes("*")) return false;
@@ -9664,7 +9681,7 @@ function formatCapabilities(r) {
9664
9681
  no vault credentials visible`;
9665
9682
  const width = Math.max(...items.map((i) => i.scope.length));
9666
9683
  const lines = items.map(
9667
- (i) => `${i.accessible ? "+" : " "} ${i.scope.padEnd(width)} ${i.tier.padEnd(7)} ${i.reason}`
9684
+ (i) => `${i.accessible ? "+" : " "} ${i.scope.padEnd(width)} ${i.tier.padEnd(7)} ${i.grantScope ? `${i.reason} (${i.grantScope})` : i.reason}`
9668
9685
  );
9669
9686
  const reachable = items.filter((i) => i.accessible).length;
9670
9687
  return [
@@ -9675,6 +9692,7 @@ no vault credentials visible`;
9675
9692
  ...lines,
9676
9693
  "",
9677
9694
  "+ = usable now. Consume keyless: `mmi-cli secrets use <KEY> -- <cmd>` (inject, never printed); validate: `mmi-cli secrets verify <KEY>`. No command prints a raw value (#2844).",
9695
+ " `grant (read)` = keyless use only \u2014 rotating or removing that key still needs an rw grant (#3652).",
9678
9696
  ` Org-infra (#3463): \`--slug\` is ALWAYS \`${ORG_INFRA_SLUG}\`; the provider is the key's FIRST SEGMENT \u2014 \`mmi-cli secrets use cloudflare/GLOBAL_API_KEY --slug ${ORG_INFRA_SLUG} -- <cmd>\` (master-gated).`
9679
9697
  ].join("\n");
9680
9698
  }
@@ -10339,11 +10357,29 @@ async function secretsRemove(deps, key, opts) {
10339
10357
  }
10340
10358
  deps.log(`removed ${key}`);
10341
10359
  }
10342
- async function secretsGrant(deps, repo, login, key, _opts) {
10360
+ var WILDCARD_GRANT_KEY_RE = /^(?:[a-z][a-z0-9-]*\/)?\*$/;
10361
+ function isWildcardGrantKey(key) {
10362
+ return WILDCARD_GRANT_KEY_RE.test(key);
10363
+ }
10364
+ function isValidGrantKey(key) {
10365
+ if (!key || key.length > 256) return false;
10366
+ if (key.includes("..") || key.startsWith("/")) return false;
10367
+ return isWildcardGrantKey(key) || isValidSecretKey(key);
10368
+ }
10369
+ async function secretsGrant(deps, repo, login, key, opts) {
10370
+ if (!isValidGrantKey(key)) {
10371
+ deps.err(`invalid grant key ${JSON.stringify(key)} \u2014 expected <provider>/<KEY>, or a read-only wildcard \`*\` / \`<provider>/*\``);
10372
+ return;
10373
+ }
10374
+ if (isWildcardGrantKey(key) && !opts.read) {
10375
+ deps.err(`secrets grant: ${key} is a wildcard \u2014 pass --read. A wildcard never confers write, so granting it rw is refused rather than silently narrowed.`);
10376
+ return;
10377
+ }
10378
+ const scope = opts.read ? "read" : "rw";
10343
10379
  const res = await deps.fetch(`${deps.apiUrl}/secrets/grant`, {
10344
10380
  method: "POST",
10345
10381
  headers: await deps.headers({ "content-type": "application/json" }),
10346
- body: JSON.stringify({ repo, login, key }),
10382
+ body: JSON.stringify({ repo, login, key, scope }),
10347
10383
  signal: AbortSignal.timeout(TIMEOUT_MS)
10348
10384
  });
10349
10385
  if (!res.ok) {
@@ -10352,7 +10388,7 @@ async function secretsGrant(deps, repo, login, key, _opts) {
10352
10388
  );
10353
10389
  return;
10354
10390
  }
10355
- deps.log(`granted @${login} access to ${key} in ${repo}`);
10391
+ deps.log(`granted @${login} ${scope === "read" ? "read-only" : "read/write"} access to ${key} in ${repo}`);
10356
10392
  }
10357
10393
  async function secretsRevoke(deps, repo, login, key, _opts) {
10358
10394
  const res = await deps.fetch(`${deps.apiUrl}/secrets/revoke`, {
@@ -15944,12 +15980,15 @@ async function resolveOwners(deps) {
15944
15980
  function collaboratorRole(c) {
15945
15981
  return c.role_name ?? (c.permissions?.admin ? "admin" : c.permissions?.maintain ? "maintain" : "write");
15946
15982
  }
15947
- async function auditRepoCollaborators(repo, owners, deps) {
15983
+ async function auditRepoCollaborators(repo, owners, deps, projectAdmins = /* @__PURE__ */ new Set(), sanctionedAdmins = /* @__PURE__ */ new Set()) {
15948
15984
  const collabs = await restPagedJson(deps, `repos/${repo}/collaborators?affiliation=direct`, []);
15949
15985
  const findings = [];
15986
+ const writeOrBetter = /* @__PURE__ */ new Set();
15950
15987
  for (const c of collabs) {
15951
15988
  if (owners.has(c.login)) continue;
15952
15989
  const role = collaboratorRole(c);
15990
+ writeOrBetter.add(c.login);
15991
+ if (OVERGRANT_ROLES.has(role) && sanctionedAdmins.has(c.login)) continue;
15953
15992
  if (OVERGRANT_ROLES.has(role)) {
15954
15993
  findings.push({
15955
15994
  repo,
@@ -15961,6 +16000,17 @@ async function auditRepoCollaborators(repo, owners, deps) {
15961
16000
  });
15962
16001
  }
15963
16002
  }
16003
+ for (const login of projectAdmins) {
16004
+ if (owners.has(login) || writeOrBetter.has(login)) continue;
16005
+ findings.push({
16006
+ repo,
16007
+ kind: "collaborator-undergrant",
16008
+ severity: "medium",
16009
+ actor: login,
16010
+ detail: `@${login} is a declared project-admin of ${repo} in the registry but holds no direct collaborator role; secrets self-serve and train authority fail closed for them`,
16011
+ remediation: `gh api -X PUT repos/${repo}/collaborators/${login} -f permission=push`
16012
+ });
16013
+ }
15964
16014
  return findings;
15965
16015
  }
15966
16016
  async function auditTrainBranch(repo, branch, owners, deps, projectAdmins = /* @__PURE__ */ new Set()) {
@@ -16008,6 +16058,18 @@ async function auditTrainBranch(repo, branch, owners, deps, projectAdmins = /* @
16008
16058
  });
16009
16059
  }
16010
16060
  }
16061
+ for (const login of projectAdmins) {
16062
+ if (owners.has(login) || users.includes(login)) continue;
16063
+ findings.push({
16064
+ repo,
16065
+ branch,
16066
+ kind: "train-allowlist-missing",
16067
+ severity: "medium",
16068
+ actor: login,
16069
+ detail: `declared project-admin @${login} is missing from the ${branch} push allowlist \u2014 the registry grants them train authority the branch lock denies`,
16070
+ remediation: `gh api -X POST repos/${repo}/branches/${branch}/protection/restrictions/users -f users[]="${login}"`
16071
+ });
16072
+ }
16011
16073
  const apps = (restrictions.apps ?? []).map((a) => a.slug);
16012
16074
  if (!apps.includes(LOCKED_APP)) {
16013
16075
  findings.push({
@@ -16040,9 +16102,9 @@ function auditDataAccessContracts(repo, contracts = { consumers: {} }) {
16040
16102
  }
16041
16103
  return findings;
16042
16104
  }
16043
- async function auditRepoAccess(repo, repoClass, owners, deps, projectAdmins = /* @__PURE__ */ new Set(), dataAccess, releaseTrack) {
16105
+ async function auditRepoAccess(repo, repoClass, owners, deps, projectAdmins = /* @__PURE__ */ new Set(), dataAccess, releaseTrack, sanctionedAdmins = /* @__PURE__ */ new Set()) {
16044
16106
  const findings = [];
16045
- findings.push(...await auditRepoCollaborators(repo, owners, deps));
16107
+ findings.push(...await auditRepoCollaborators(repo, owners, deps, projectAdmins, sanctionedAdmins));
16046
16108
  if (dataAccess) findings.push(...auditDataAccessContracts(repo, dataAccess));
16047
16109
  const track = releaseTrack ?? (repoClass === "content" ? "trunk" : void 0);
16048
16110
  for (const branch of lockedBranches(repoClass, track)) {
@@ -16082,7 +16144,7 @@ async function auditPluginReadAccess(owners, projectAdmins, deps) {
16082
16144
  }
16083
16145
  return findings;
16084
16146
  }
16085
- async function auditOrgAccess(targets, deps, matrix = {}, dataAccess) {
16147
+ async function auditOrgAccess(targets, deps, matrix = {}, dataAccess, sanctioned = {}) {
16086
16148
  if (targets.length === 0) {
16087
16149
  return {
16088
16150
  ok: false,
@@ -16119,7 +16181,16 @@ async function auditOrgAccess(targets, deps, matrix = {}, dataAccess) {
16119
16181
  orgFindings.push(...await auditPluginReadAccess(owners, allProjectAdmins, deps));
16120
16182
  const repos = [];
16121
16183
  for (const target of targets) {
16122
- repos.push(await auditRepoAccess(target.repo, target.class, owners, deps, new Set(entriesValueByCanonicalRepo(matrix, target.repo) ?? []), dataAccess, target.releaseTrack));
16184
+ repos.push(await auditRepoAccess(
16185
+ target.repo,
16186
+ target.class,
16187
+ owners,
16188
+ deps,
16189
+ new Set(entriesValueByCanonicalRepo(matrix, target.repo) ?? []),
16190
+ dataAccess,
16191
+ target.releaseTrack,
16192
+ new Set(entriesValueByCanonicalRepo(sanctioned, target.repo) ?? [])
16193
+ ));
16123
16194
  }
16124
16195
  const ok = orgFindings.every((f) => f.severity !== "high") && repos.every((r) => r.ok);
16125
16196
  return { ok, owners: [...owners], orgFindings, repos };
@@ -16158,6 +16229,10 @@ function loadAccessMatrix(matrixJson) {
16158
16229
  if (!matrixJson) return {};
16159
16230
  return safeJson(matrixJson, {}).projectAdmins ?? {};
16160
16231
  }
16232
+ function loadSanctionedAdmins(matrixJson) {
16233
+ if (!matrixJson) return {};
16234
+ return safeJson(matrixJson, {}).sanctionedAdmins ?? {};
16235
+ }
16161
16236
  function loadDataAccessContracts(dataAccessJson) {
16162
16237
  if (!dataAccessJson) return { consumers: {} };
16163
16238
  const parsed = safeJson(dataAccessJson, { consumers: {} });
@@ -18073,8 +18148,8 @@ function registerSecretsCommands(program3) {
18073
18148
  if (ok === false) process.exitCode = 1;
18074
18149
  });
18075
18150
  });
18076
- secrets.command("grant <repo> <login> <key>").description("MASTER-ONLY: grant a project-admin standing access to one specific org-infra secret").action((repo, login, key) => withSecrets((d) => secretsGrant(d, repo, login, key, {})));
18077
- secrets.command("revoke <repo> <login> <key>").description("MASTER-ONLY: withdraw a previously granted org-infra secret access").action((repo, login, key) => withSecrets((d) => secretsRevoke(d, repo, login, key, {})));
18151
+ secrets.command("grant <repo> <login> <key>").description("MASTER-ONLY: grant a project-admin standing access to an org-infra secret. Default is read/write on one exact key; --read grants keyless USE only, and only --read accepts a wildcard key (`*` = the whole namespace, `<provider>/*` = one provider group) (#3652)").option("--read", "read-only: permits keyless `secrets use`, never `set`/`rm`. Required for a wildcard key.").action((repo, login, key, o) => withSecrets((d) => secretsGrant(d, repo, login, key, { read: o.read })));
18152
+ secrets.command("revoke <repo> <login> <key>").description("MASTER-ONLY: withdraw a previously granted org-infra secret access (pass the grant key exactly as granted, wildcard included)").action((repo, login, key) => withSecrets((d) => secretsRevoke(d, repo, login, key, {})));
18078
18153
  }
18079
18154
 
18080
18155
  // src/app-actor.ts
@@ -25801,23 +25876,6 @@ async function requireFreshTrainCli(commandName) {
25801
25876
  if (report.ok) return;
25802
25877
  throw new Error(staleTrainCliMessage(report, commandName));
25803
25878
  }
25804
- var cachedLongFlags;
25805
- function allLongFlags() {
25806
- if (cachedLongFlags) return cachedLongFlags;
25807
- const acc = /* @__PURE__ */ new Set();
25808
- const walk2 = (node) => {
25809
- if (!isCanonicalSuggestion(node)) return;
25810
- for (const opt of node.options) {
25811
- if (opt.discovery) continue;
25812
- const m = /--[\w-]+/.exec(opt.flags);
25813
- if (m) acc.add(m[0]);
25814
- }
25815
- for (const child2 of node.subcommands) walk2(child2);
25816
- };
25817
- walk2(buildCommandManifest(program2).tree);
25818
- cachedLongFlags = [...acc];
25819
- return cachedLongFlags;
25820
- }
25821
25879
  var cachedCommandPaths;
25822
25880
  function allCommandPaths() {
25823
25881
  if (cachedCommandPaths) return cachedCommandPaths;
@@ -25884,6 +25942,22 @@ function commandPath2(cmd) {
25884
25942
  }
25885
25943
  return parts.join(" ");
25886
25944
  }
25945
+ function commandOwnLongFlags(cmd) {
25946
+ if (!cmd) return [];
25947
+ const flags = /* @__PURE__ */ new Set();
25948
+ for (const opt of cmd.options) {
25949
+ if (opt.long) flags.add(opt.long);
25950
+ }
25951
+ return [...flags];
25952
+ }
25953
+ var TARGET_SELECTOR_FLAGS = /* @__PURE__ */ new Set(["--repo", "--ref", "--target", "--project"]);
25954
+ function positionalTargetForm(cmd) {
25955
+ if (!cmd) return void 0;
25956
+ const args = cmd.registeredArguments ?? [];
25957
+ const first = args[0];
25958
+ if (!first) return void 0;
25959
+ return `mmi-cli ${commandPath2(cmd)} <${first.name()}>`;
25960
+ }
25887
25961
  function resolveParseHint() {
25888
25962
  if (lastParseErrorKind === "unknown-command") {
25889
25963
  const path3 = lastUnknownCommand ? suggestCommandPath(lastUnknownCommand, allCommandPaths()) : void 0;
@@ -25912,10 +25986,13 @@ function envelopeAwareWriteErr(str) {
25912
25986
  if (match) {
25913
25987
  const flag = match[1];
25914
25988
  if (argvWantsJson2()) {
25915
- const suggestion = didYouMean(flag, allLongFlags());
25989
+ const invoked = resolveCommandFromArgv(program2, process.argv.slice(2));
25990
+ const suggestion = didYouMean(flag, commandOwnLongFlags(invoked));
25916
25991
  const corrected = suggestion ? `mmi-cli ${process.argv.slice(2).map((a) => a === flag ? suggestion : a).join(" ")}` : void 0;
25992
+ const positional = !suggestion && TARGET_SELECTOR_FLAGS.has(flag) ? positionalTargetForm(invoked) : void 0;
25993
+ const message = positional ? `unknown option '${flag}' \u2014 this command takes its target as a positional: ${positional}` : `unknown option '${flag}'`;
25917
25994
  process.stderr.write(
25918
- formatErrorEnvelope(`unknown option '${flag}'`, {
25995
+ formatErrorEnvelope(message, {
25919
25996
  code: ERROR_CODES.ERR_UNKNOWN_FLAG,
25920
25997
  offending_flag: flag,
25921
25998
  ...suggestion ? { did_you_mean: suggestion } : {},
@@ -27371,14 +27448,19 @@ async function listCiWorkflowPaths(cwd = process.cwd()) {
27371
27448
  }
27372
27449
  }).map((name) => `.github/workflows/${name}`);
27373
27450
  }
27374
- async function resolveMergeCiPolicyForCheckout(repoOpt) {
27451
+ async function resolveMergeCiPolicyForCheckout(repoOpt, headRef) {
27375
27452
  const repo = repoOpt ?? await resolveRepo();
27376
27453
  if (repo) {
27377
- return resolveRepoMergeCiPolicy(repo, ciAuditDeps());
27454
+ return resolveRepoMergeCiPolicy(repo, ciAuditDeps(), headRef);
27378
27455
  }
27379
27456
  const workflowPaths = await listCiWorkflowPaths();
27380
27457
  return resolveMergeCiPolicy({ workflowPaths });
27381
27458
  }
27459
+ async function prHeadRefForCiProbe(prNumber, repo) {
27460
+ const snapshot = await fetchRestPrSnapshot(prNumber, repo).catch(() => null);
27461
+ if (!snapshot || snapshot.headIsFork || !snapshot.headRef) return void 0;
27462
+ return snapshot.headRef;
27463
+ }
27382
27464
  function ciAuditDeps() {
27383
27465
  const cfgPromise = loadConfig();
27384
27466
  const root = hubRoot();
@@ -27417,9 +27499,11 @@ pr.command("checks-wait <number>").description(`bounded wait for PR checks; skip
27417
27499
  timeoutMs = Math.round(minutes * 6e4);
27418
27500
  }
27419
27501
  const repo = await requireRepo(o.repo);
27420
- const baseBranch = await fetchRestPrSnapshot(number, repo).then((s) => s.baseRef).catch(() => "development");
27502
+ const snapshot = await fetchRestPrSnapshot(number, repo).catch(() => null);
27503
+ const baseBranch = snapshot?.baseRef ?? "development";
27504
+ const ciHeadRef = snapshot && !snapshot.headIsFork && snapshot.headRef ? snapshot.headRef : void 0;
27421
27505
  const result = await waitForPrChecks({
27422
- resolvePolicy: () => resolveMergeCiPolicyForCheckout(o.repo),
27506
+ resolvePolicy: () => resolveMergeCiPolicyForCheckout(o.repo, ciHeadRef),
27423
27507
  pollChecks: () => pollRestPrChecks(number, repo),
27424
27508
  pollMergeable: () => pollRestPrMergeable(number, repo),
27425
27509
  pollRateLimit: () => fetchRestCorePool(),
@@ -27564,12 +27648,13 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
27564
27648
  const beforeWorktrees = parseWorktreePorcelain(
27565
27649
  (await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout
27566
27650
  );
27567
- const ciPolicy = await resolveMergeCiPolicyForCheckout(o.repo);
27651
+ const ciHeadRef = repoForPostCleanup ? await prHeadRefForCiProbe(number, repoForPostCleanup) : void 0;
27652
+ const ciPolicy = await resolveMergeCiPolicyForCheckout(o.repo, ciHeadRef);
27568
27653
  if (o.wait) {
27569
27654
  const repo = await requireRepo(o.repo);
27570
27655
  const baseBranch = await fetchRestPrSnapshot(number, repo).then((s) => s.baseRef).catch(() => "development");
27571
27656
  const wait = await waitForPrChecks({
27572
- resolvePolicy: () => resolveMergeCiPolicyForCheckout(o.repo),
27657
+ resolvePolicy: () => resolveMergeCiPolicyForCheckout(o.repo, ciHeadRef),
27573
27658
  pollChecks: () => pollRestPrChecks(number, repo),
27574
27659
  pollMergeable: () => pollRestPrMergeable(number, repo),
27575
27660
  pollRateLimit: () => fetchRestCorePool(),
@@ -28031,7 +28116,8 @@ access.command("audit").description("audit collaborator roles + train-branch pus
28031
28116
  const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
28032
28117
  const fileContracts = (0, import_node_fs33.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs33.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
28033
28118
  const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
28034
- const report = await auditOrgAccess(targets, deps, matrix, dataAccess);
28119
+ const sanctioned = (0, import_node_fs33.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs33.readFileSync)("access-matrix.json", "utf8")) : {};
28120
+ const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
28035
28121
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
28036
28122
  if (!report.ok) process.exitCode = 1;
28037
28123
  });
@@ -28214,9 +28300,11 @@ program2.parseAsync(process.argv).then(() => finishCliRun()).catch((e) => failGr
28214
28300
  DEFAULT_PRIORITY,
28215
28301
  awsCallerArn,
28216
28302
  classifyParseError,
28303
+ commandOwnLongFlags,
28217
28304
  envHealLockPath,
28218
28305
  gcPlan,
28219
28306
  isOrgRegisteredRepo,
28307
+ positionalTargetForm,
28220
28308
  registryClientDeps,
28221
28309
  repoSlug,
28222
28310
  shouldMarkWorktreeActivity,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "3.69.0",
3
+ "version": "3.71.0",
4
4
  "description": "MMI Future CLI — the org dev toolbox (board, registry, keyless secrets, release train, bootstrap, doctor) and the cross-IDE engine the plugin's session-start hook drives.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",