@mutmutco/codex-plugin 4.5.110 → 4.5.112

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mmi",
3
- "version": "4.5.110",
3
+ "version": "4.5.112",
4
4
  "mmiCompat": "4.x",
5
5
  "description": "MMI workflow skills and org gates delivery.",
6
6
  "author": {
package/cli/dist/main.cjs CHANGED
@@ -5851,42 +5851,11 @@ async function mintInstallationToken(deps) {
5851
5851
  async function pollTokenOrUndefined(deps) {
5852
5852
  try {
5853
5853
  return (await mintInstallationToken(deps)).token;
5854
- } catch {
5854
+ } catch (e) {
5855
+ if (e instanceof AppActorError && e.code === "mint-failed") throw e;
5855
5856
  return void 0;
5856
5857
  }
5857
5858
  }
5858
- var APP_POLL_HEADROOM_FRACTION = 0.4;
5859
- var APP_POLL_RESET_DEFAULT_MS = 36e5;
5860
- var appPollGuardState = {};
5861
- function appPollGuardSnapshot() {
5862
- return { ...appPollGuardState };
5863
- }
5864
- function appPollAllowsApp(nowMs) {
5865
- if (appPollGuardState.fallbackUntilMs !== void 0) {
5866
- if (nowMs < appPollGuardState.fallbackUntilMs) return false;
5867
- appPollGuardState = {};
5868
- }
5869
- const { remaining, limit } = appPollGuardState;
5870
- if (remaining === void 0 || limit === void 0 || limit <= 0) return true;
5871
- return remaining / limit >= APP_POLL_HEADROOM_FRACTION;
5872
- }
5873
- function latchAppPollFallback(nowMs) {
5874
- appPollGuardState.fallbackUntilMs = appPollGuardState.resetAtMs ?? nowMs + APP_POLL_RESET_DEFAULT_MS;
5875
- }
5876
- function noteAppPollResponse(headers, rateLimited, nowMs) {
5877
- if (headers.remaining !== void 0) appPollGuardState.remaining = headers.remaining;
5878
- if (headers.limit !== void 0) appPollGuardState.limit = headers.limit;
5879
- if (headers.resetSec !== void 0 && headers.resetSec > 0) appPollGuardState.resetAtMs = headers.resetSec * 1e3;
5880
- const { remaining, limit } = appPollGuardState;
5881
- const belowFloor = !rateLimited && remaining !== void 0 && limit !== void 0 && limit > 0 && remaining / limit < APP_POLL_HEADROOM_FRACTION;
5882
- const headroomUnverifiable = !rateLimited && (headers.remaining === void 0 || headers.limit === void 0);
5883
- if (rateLimited || belowFloor || headroomUnverifiable && appPollGuardState.resetAtMs !== void 0) {
5884
- latchAppPollFallback(nowMs);
5885
- }
5886
- }
5887
- function resetAppPollGuard() {
5888
- appPollGuardState = {};
5889
- }
5890
5859
  var notedIneligibleCommand = false;
5891
5860
  async function activateAppActor(commandPath3, env, mint, note = (line) => process.stderr.write(`${line}
5892
5861
  `)) {
@@ -6072,7 +6041,7 @@ function waitReadFailureMessage(e) {
6072
6041
  const stderr = typeof err?.stderr === "string" ? err.stderr.trim() : "";
6073
6042
  return (stderr || err?.message || String(e)).replace(/\s+/g, " ").slice(0, 300);
6074
6043
  }
6075
- var PR_CHECKS_POLL_MS = 3e4;
6044
+ var PR_CHECKS_POLL_MS = 6e4;
6076
6045
  function pollIntervalMs(elapsedMs) {
6077
6046
  return Math.min(4 * PR_CHECKS_POLL_MS, PR_CHECKS_POLL_MS * 2 ** Math.floor(Math.max(0, elapsedMs) / 3e5));
6078
6047
  }
@@ -6324,15 +6293,21 @@ function lastRestPollSource() {
6324
6293
  function setPollTokenProvider(provider) {
6325
6294
  pollTokenProvider = provider;
6326
6295
  pollTokenOnce = void 0;
6327
- resetAppPollGuard();
6328
6296
  lastPollSource = void 0;
6329
6297
  observedPool.clear();
6330
6298
  }
6331
6299
  async function pollToken() {
6332
6300
  if (!pollTokenProvider) return void 0;
6333
- pollTokenOnce ??= pollTokenProvider().catch(() => void 0);
6301
+ pollTokenOnce ??= pollTokenProvider().catch((e) => {
6302
+ if (e instanceof AppActorError && e.code === "mint-failed") throw e;
6303
+ return void 0;
6304
+ });
6334
6305
  return pollTokenOnce;
6335
6306
  }
6307
+ async function ghReadEnv() {
6308
+ const appToken = await pollToken();
6309
+ return appToken ? { ...process.env, GH_TOKEN: appToken } : void 0;
6310
+ }
6336
6311
  var INCLUDED_HEADER_BLOCK = /^(?:\r?\n)?HTTP\/\d(?:\.\d)? (\d{3})[^\n]*\n(?:[^\n]+\n)*?\r?\n/gm;
6337
6312
  function parseIncludedOutput(out) {
6338
6313
  let status;
@@ -6373,11 +6348,6 @@ function describeRefusal(out) {
6373
6348
  return [parts.join(" "), text ? `"${text}"` : ""].filter(Boolean).join(" ") || "no rate-limit headers or message";
6374
6349
  }
6375
6350
  var etagCache = /* @__PURE__ */ new Map();
6376
- function appPoolDetail() {
6377
- const guard = appPollGuardSnapshot();
6378
- const counts = guard.remaining !== void 0 && guard.limit !== void 0 ? `App pool at ${guard.remaining}/${guard.limit}` : "App pool headroom unverified";
6379
- return guard.resetAtMs !== void 0 ? `${counts}, resets ${new Date(guard.resetAtMs).toISOString()}` : counts;
6380
- }
6381
6351
  var observedPool = /* @__PURE__ */ new Map();
6382
6352
  var OBSERVED_POOL_MAX_AGE_MS = 3 * 6e4;
6383
6353
  function notePoolHeaders(source, out, nowMs) {
@@ -6393,59 +6363,46 @@ function observedCorePool(source, nowMs) {
6393
6363
  }
6394
6364
  async function defaultGhApi(args) {
6395
6365
  const appToken = await pollToken();
6396
- let source = appToken && appPollAllowsApp(Date.now()) ? "app" : "user";
6397
- if (lastPollSource !== void 0 && lastPollSource !== source) {
6398
- console.warn(source === "user" ? `poll credential: ${appPoolDetail()} \u2014 below the ${APP_POLL_HEADROOM_FRACTION * 100}% App-poll headroom floor (#7843); REST poll reads switch to the ambient user token until the App pool resets` : "poll credential: App pool reset passed \u2014 REST poll reads return to the App installation token (#7843)");
6399
- }
6400
- for (; ; ) {
6401
- const key = `${source}\0${args.join("\0")}`;
6402
- const cached = etagCache.get(key);
6403
- let stdout;
6404
- try {
6405
- ({ stdout } = await execFileP("gh", ["api", "--include", ...cached ? ["-H", `If-None-Match: ${cached.etag}`] : [], ...args], {
6406
- timeout: REST_GH_TIMEOUT_MS,
6407
- // Only on the App source: passing `env` at all replaces the inherited environment wholesale,
6408
- // and `gh` needs the rest of it (PATH, GH_HOST, config home). Ambient rides the inherited env.
6409
- ...source === "app" && appToken ? { env: { ...process.env, GH_TOKEN: appToken } } : {}
6410
- }));
6411
- } catch (e) {
6412
- const out = e.stdout;
6413
- const errOut = typeof out === "string" ? out : void 0;
6414
- const status = errOut !== void 0 ? parseIncludedOutput(errOut).status : void 0;
6415
- if (errOut !== void 0) notePoolHeaders(source, errOut, Date.now());
6416
- if (cached && status === 304) {
6417
- lastPollSource = source;
6418
- return cached.body;
6419
- }
6420
- if (source === "app" && appToken && status === 403 && /Resource not accessible by integration/i.test(describeRefusal(errOut))) {
6421
- console.warn(`poll credential: App token lacks permission for this read (HTTP 403, response: ${describeRefusal(errOut)}) \u2014 this read uses the ambient user token; the App pool stays in use (#7978)`);
6422
- try {
6423
- const { stdout: userOut } = await execFileP("gh", ["api", "--include", ...args], { timeout: REST_GH_TIMEOUT_MS });
6424
- notePoolHeaders("user", userOut, Date.now());
6425
- return parseIncludedOutput(userOut).body;
6426
- } catch (userErr) {
6427
- const uo = userErr.stdout;
6428
- if (typeof uo === "string") notePoolHeaders("user", uo, Date.now());
6429
- throw userErr;
6430
- }
6431
- }
6432
- if (source === "app" && appToken && (status === 403 || status === 429)) {
6433
- noteAppPollResponse(parseRateLimitHeaders(errOut), true, Date.now());
6434
- console.warn(`poll credential: App pool refused the read (HTTP ${status}, ${appPoolDetail()}; response: ${describeRefusal(errOut)}) \u2014 REST poll reads switch to the ambient user token until the App pool resets (#7843)`);
6435
- lastPollSource = "user";
6436
- source = "user";
6437
- continue;
6366
+ const source = appToken ? "app" : "user";
6367
+ const key = `${source}\0${args.join("\0")}`;
6368
+ const cached = etagCache.get(key);
6369
+ let stdout;
6370
+ try {
6371
+ ({ stdout } = await execFileP("gh", ["api", "--include", ...cached ? ["-H", `If-None-Match: ${cached.etag}`] : [], ...args], {
6372
+ timeout: REST_GH_TIMEOUT_MS,
6373
+ // Only on the App source: passing `env` at all replaces the inherited environment wholesale,
6374
+ // and `gh` needs the rest of it (PATH, GH_HOST, config home). Ambient rides the inherited env.
6375
+ ...source === "app" && appToken ? { env: { ...process.env, GH_TOKEN: appToken } } : {}
6376
+ }));
6377
+ } catch (e) {
6378
+ const out = e.stdout;
6379
+ const errOut = typeof out === "string" ? out : void 0;
6380
+ const status = errOut !== void 0 ? parseIncludedOutput(errOut).status : void 0;
6381
+ if (errOut !== void 0) notePoolHeaders(source, errOut, Date.now());
6382
+ if (cached && status === 304) {
6383
+ lastPollSource = source;
6384
+ return cached.body;
6385
+ }
6386
+ if (status === 403 || status === 429) {
6387
+ const path2 = args.filter((a) => !a.startsWith("-")).join(" ") || args.join(" ");
6388
+ const refusal = errOut !== void 0 ? describeRefusal(errOut) : "no rate-limit headers or message";
6389
+ if (status === 403 && /Resource not accessible by integration/i.test(refusal)) {
6390
+ throw new Error(
6391
+ `poll read refused: App token lacks permission for ${path2} (HTTP 403, response: ${refusal}) \u2014 reads never fall back to the owner's token (#8030); add the permission to APP_POLL_PERMISSIONS / the App installation`
6392
+ );
6438
6393
  }
6439
- throw e;
6394
+ throw Object.assign(new Error(
6395
+ `poll read refused on the ${source} pool (HTTP ${status}, response: ${refusal}) \u2014 reads stay on the ${source === "app" ? "App" : "ambient identity"} (#8030); the wait re-arms after reset`
6396
+ ), { status, stdout: errOut, stderr: e.stderr });
6440
6397
  }
6441
- const { body, etag, pages } = parseIncludedOutput(stdout);
6442
- notePoolHeaders(source, stdout, Date.now());
6443
- if (source === "app") noteAppPollResponse(parseRateLimitHeaders(stdout), false, Date.now());
6444
- lastPollSource = source;
6445
- if (etag && pages === 1) etagCache.set(key, { etag, body });
6446
- else etagCache.delete(key);
6447
- return body;
6398
+ throw e;
6448
6399
  }
6400
+ const { body, etag, pages } = parseIncludedOutput(stdout);
6401
+ notePoolHeaders(source, stdout, Date.now());
6402
+ lastPollSource = source;
6403
+ if (etag && pages === 1) etagCache.set(key, { etag, body });
6404
+ else etagCache.delete(key);
6405
+ return body;
6449
6406
  }
6450
6407
  function classifyCheckRun(run) {
6451
6408
  if (run.status !== "completed") return "pending";
@@ -6913,7 +6870,7 @@ async function fetchRequiredCheckContexts(repo, branch, gh2 = defaultGhApi) {
6913
6870
  return contexts;
6914
6871
  }
6915
6872
  async function fetchRestCorePool(gh2 = defaultGhApi, nowMs = Date.now()) {
6916
- const seen = gh2 === defaultGhApi ? observedCorePool(await pollToken() && appPollAllowsApp(nowMs) ? "app" : "user", nowMs) : void 0;
6873
+ const seen = gh2 === defaultGhApi ? observedCorePool(await pollToken() ? "app" : "user", nowMs) : void 0;
6917
6874
  if (seen) return { state: "ok", ...seen };
6918
6875
  let core;
6919
6876
  try {
@@ -6939,7 +6896,12 @@ function withAppClaimMarkerRead(client, appClient) {
6939
6896
  return await client.restPaginate(path2, init);
6940
6897
  } catch (e) {
6941
6898
  if (!commentsPath.test(path2) || !(e instanceof GitHubApiError) || e.status !== 429 && (e.status !== 403 || !isRateLimitRefusal(e))) throw e;
6942
- const token = appClient ? void 0 : await pollToken();
6899
+ let token;
6900
+ try {
6901
+ token = appClient ? void 0 : await pollToken();
6902
+ } catch {
6903
+ throw e;
6904
+ }
6943
6905
  if (!appClient && !token) throw e;
6944
6906
  return (appClient ?? createGitHubClient({ token: async () => token })).restPaginate(path2, init);
6945
6907
  }
@@ -16926,10 +16888,24 @@ function foldPortManualCommand(model, version) {
16926
16888
  return model === "hub-serverless" ? `node scripts/release-distribution.mjs prepare ${version}` : `npm version ${version} --no-git-tag-version --allow-same-version`;
16927
16889
  }
16928
16890
  var HUB_REPO2 = "mutmutco/MMI-Hub";
16929
- var GH_RUN_WATCH_INTERVAL_S = 30;
16891
+ var GH_RUN_WATCH_INTERVAL_S = 60;
16930
16892
  function ghRunWatchArgs(runId, repo) {
16931
16893
  return ["run", "watch", String(runId), "--repo", repo, "--exit-status", "--interval", String(GH_RUN_WATCH_INTERVAL_S)];
16932
16894
  }
16895
+ function isAppRoutableGhReadArgs(args) {
16896
+ if (args.some((a) => a === "--mine" || a === "viewer" || a.includes("@me"))) return false;
16897
+ const [cmd, sub] = args;
16898
+ if (cmd === "run" && (sub === "view" || sub === "list" || sub === "watch")) return true;
16899
+ if (cmd === "pr" && sub === "view") return true;
16900
+ if (cmd === "api") {
16901
+ const rest = args.slice(1);
16902
+ if (rest.some((a) => /^(-X|--method|-f|--field|-F|--raw-field|--input)/.test(a))) return false;
16903
+ const path2 = rest.find((a) => !a.startsWith("-"));
16904
+ if (path2 === "user" || path2?.startsWith("user/") || path2?.startsWith("user?")) return false;
16905
+ return true;
16906
+ }
16907
+ return false;
16908
+ }
16933
16909
  var JERV_HUB_REPO = "mutmutco/Jerv-Hub";
16934
16910
  function isHubControlRepo(repo) {
16935
16911
  return repo.toLowerCase() === HUB_REPO2.toLowerCase();
@@ -17716,10 +17692,10 @@ var rollout_plan_default = {
17716
17692
  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)."
17717
17693
  },
17718
17694
  baseline: {
17719
- version: "4.5.110",
17720
- tag: "v4.5.110",
17721
- commit: "b27eb42f5d5e",
17722
- npm: "@mutmutco/cli@4.5.110"
17695
+ version: "4.5.112",
17696
+ tag: "v4.5.112",
17697
+ commit: "45f0c868cba4",
17698
+ npm: "@mutmutco/cli@4.5.112"
17723
17699
  },
17724
17700
  exitCriterion: "fleet-n-of-n",
17725
17701
  hubOnlyShortcut: "forbidden",
@@ -17736,14 +17712,14 @@ var rollout_plan_default = {
17736
17712
  repo: "mutmutco/mmi-hub",
17737
17713
  role: "canary",
17738
17714
  schedule: "train",
17739
- v3Target: "v4.5.110"
17715
+ v3Target: "v4.5.112"
17740
17716
  }
17741
17717
  ],
17742
17718
  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.",
17743
17719
  rollback: {
17744
17720
  independent: true,
17745
- mechanism: "npm dist-tag latest -> 4.5.110 and redeploy the Hub Lambda from tag v4.5.110 (b27eb42f5d5e); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
17746
- v3Target: "v4.5.110 (@mutmutco/cli@4.5.110, tag commit b27eb42f5d5e \u2014 last known-good release carrying the repo-index v4-only contract)"
17721
+ mechanism: "npm dist-tag latest -> 4.5.112 and redeploy the Hub Lambda from tag v4.5.112 (45f0c868cba4); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
17722
+ v3Target: "v4.5.112 (@mutmutco/cli@4.5.112, tag commit 45f0c868cba4 \u2014 last known-good release carrying the repo-index v4-only contract)"
17747
17723
  }
17748
17724
  },
17749
17725
  {
@@ -23731,7 +23707,7 @@ function deriveReleasePhaseStatus(ledger) {
23731
23707
  return { phaseStatus: worst === 0 ? "phases-failed" : worst === 1 ? "phases-pending" : "phases-green", legs };
23732
23708
  }
23733
23709
  function safeRunAction(runId, runRepo, state) {
23734
- return state === "failed" ? `next: diagnose with gh run view ${runId} --repo ${runRepo} --log-failed; rerun by hand only when the cause is fixed: gh run rerun ${runId} --repo ${runRepo} --failed (never re-tag)` : `next: gh run watch ${runId} --repo ${runRepo} --exit-status --interval 30`;
23710
+ return state === "failed" ? `next: diagnose with gh run view ${runId} --repo ${runRepo} --log-failed; rerun by hand only when the cause is fixed: gh run rerun ${runId} --repo ${runRepo} --failed (never re-tag)` : `next: gh run watch ${runId} --repo ${runRepo} --exit-status --interval 60`;
23735
23711
  }
23736
23712
  function formatReleaseLedgerReport(report) {
23737
23713
  const lines2 = [`release phase ledger${report.lane ? ` [${report.lane}]` : ""}: ${report.phaseStatus.toUpperCase()} (${report.path}) \u2014 phase status of the recorded boundaries, NOT the release verdict`];
@@ -24029,7 +24005,7 @@ async function transientRecoveryForFailedPhase(deps, input) {
24029
24005
  recovered: false,
24030
24006
  runId: candidate.runId,
24031
24007
  runRepo,
24032
- note: `the automatic rerun of ${failedRunIdentity(candidate, runRepo)} did not resolve within the bounded watch \u2014 typed PENDING (the one automatic rerun is spent); safe next action: gh run watch ${candidate.runId} --repo ${runRepo} --exit-status --interval 30`
24008
+ note: `the automatic rerun of ${failedRunIdentity(candidate, runRepo)} did not resolve within the bounded watch \u2014 typed PENDING (the one automatic rerun is spent); safe next action: gh run watch ${candidate.runId} --repo ${runRepo} --exit-status --interval 60`
24033
24009
  };
24034
24010
  }
24035
24011
  async function transientRecoveryEnumerate(deps, repo, sha) {
@@ -31748,7 +31724,8 @@ function parseTenantTasksVar(raw) {
31748
31724
  if (typeof task.service !== "string" || !serviceRe.test(task.service)) throw new Error(`org project set: tenantTasks.${name}.service has an unsafe shape`);
31749
31725
  if (!Array.isArray(task.command) || task.command.length < 1 || task.command.length > 32 || task.command.some((arg) => typeof arg !== "string" || arg.length < 1 || arg.length > 256 || /[\u0000\r\n]/.test(arg))) throw new Error(`org project set: tenantTasks.${name}.command must be 1-32 bounded argv strings`);
31750
31726
  if (!Array.isArray(task.stages) || task.stages.length < 1 || task.stages.length > 3 || task.stages.some((stage) => typeof stage !== "string" || !validStages.has(stage)) || new Set(task.stages).size !== task.stages.length) throw new Error(`org project set: tenantTasks.${name}.stages must be unique dev/rc/main values`);
31751
- if (task.timeoutSeconds !== void 0 && (!Number.isInteger(task.timeoutSeconds) || task.timeoutSeconds < 1 || task.timeoutSeconds > 3600)) throw new Error(`org project set: tenantTasks.${name}.timeoutSeconds must be an integer 1..3600`);
31727
+ const maxTimeout = task.secretOutput !== void 0 ? 3600 : 7200;
31728
+ if (task.timeoutSeconds !== void 0 && (!Number.isInteger(task.timeoutSeconds) || task.timeoutSeconds < 1 || task.timeoutSeconds > maxTimeout)) throw new Error(`org project set: tenantTasks.${name}.timeoutSeconds must be an integer 1..${maxTimeout}`);
31752
31729
  const artifact = task.artifact ?? "none";
31753
31730
  if (artifact !== "none" && artifact !== "required") throw new Error(`org project set: tenantTasks.${name}.artifact must be none or required`);
31754
31731
  const carriesPlaceholder = task.command.some((arg) => arg.includes("{artifact}"));
@@ -39148,7 +39125,7 @@ async function updatePrHeadFromBase(input) {
39148
39125
  }
39149
39126
  throw new Error(`GitHub accepted the update of ${input.head} from ${input.base} but the head did not move \u2014 check the PR, then rerun`);
39150
39127
  }
39151
- var PR_LAND_POLL_MS = 3e4;
39128
+ var PR_LAND_POLL_MS = 6e4;
39152
39129
  var PR_LAND_ENQUEUE_TIMEOUT_MS = 10 * 6e4;
39153
39130
  var PR_LAND_STATE_READ_RETRIES = 3;
39154
39131
  var PR_LAND_STATE_READ_DELAY_MS = 2e3;
@@ -46094,7 +46071,8 @@ ${list}`);
46094
46071
  }
46095
46072
  if (!repoFromGh || !base) {
46096
46073
  const args = repoOpt ? ["--repo", repoOpt] : repoArgs;
46097
- const viewed = (await execFileP("gh", ["pr", "view", prNumber, ...args, "--json", "headRepository,baseRefName", "--jq", '.headRepository.nameWithOwner + " " + .baseRefName'], { timeout: GC_GH_TIMEOUT_MS })).stdout.trim();
46074
+ const readEnv = await ghReadEnv();
46075
+ const viewed = (await execFileP("gh", ["pr", "view", prNumber, ...args, "--json", "headRepository,baseRefName", "--jq", '.headRepository.nameWithOwner + " " + .baseRefName'], { timeout: GC_GH_TIMEOUT_MS, ...readEnv ? { env: readEnv } : {} })).stdout.trim();
46098
46076
  [repoFromGh, base] = viewed.split(/\s+/);
46099
46077
  }
46100
46078
  const repo = repoOpt ?? repoFromGh;
@@ -46158,7 +46136,8 @@ ${list}`);
46158
46136
  },
46159
46137
  readPrState: async (prNumber, repo) => {
46160
46138
  const args = repo ? ["--repo", repo] : [];
46161
- const result2 = await readGhPrStateWithRetry(async () => (await execFileP("gh", ["pr", "view", prNumber, ...args, "--json", "mergeStateStatus,mergeable,state"], { timeout: GC_GH_TIMEOUT_MS })).stdout);
46139
+ const readEnv = await ghReadEnv();
46140
+ const result2 = await readGhPrStateWithRetry(async () => (await execFileP("gh", ["pr", "view", prNumber, ...args, "--json", "mergeStateStatus,mergeable,state"], { timeout: GC_GH_TIMEOUT_MS, ...readEnv ? { env: readEnv } : {} })).stdout);
46162
46141
  if (!result2.ok) {
46163
46142
  if (isGitHubRateLimitError(result2.error)) return fetchRestPrLandState(prNumber, repo);
46164
46143
  throw new Error(`could not read PR state: ${result2.error}`);
@@ -46776,8 +46755,9 @@ function trainApplyDeps() {
46776
46755
  trainWriter: (verb, cwd) => acquireTrainWriterLock(cwd, `mmi-cli devops ${verb}`),
46777
46756
  run: async (file, args) => {
46778
46757
  const timeout = file === "node" && args[1] === "prepare" ? NODE_PREPARE_TIMEOUT_MS : file === "node" && args[1] === "verify" ? NODE_VERIFY_TIMEOUT_MS : file === "npm" && (args[0] === "version" || args[0] === "ci" || args[0] === "install") ? NPM_FOLD_TIMEOUT_MS : file === "npm" ? NPM_TRAIN_TIMEOUT_MS : file === "bash" && /jerv-gateway-release-deploy\.sh$/.test(args[0] ?? "") ? JERV_GATEWAY_DEPLOY_TIMEOUT_MS : file === "git" && args[0] === "commit" ? GIT_COMMIT_TIMEOUT_MS : file !== "gh" ? GIT_TIMEOUT_MS : args[0] === "run" && args[1] === "watch" ? GH_RUN_WATCH_TIMEOUT_MS : GH_TRAIN_TIMEOUT_MS;
46758
+ const readEnv = file === "gh" && isAppRoutableGhReadArgs(args) ? await ghReadEnv() : void 0;
46779
46759
  try {
46780
- return isWin3 && file === "npm" ? (await execFileP("cmd.exe", ["/c", "npm", ...args], { timeout })).stdout : (await execFileP(file, file === "git" ? trainGitArgs(args) : args, { timeout })).stdout;
46760
+ return isWin3 && file === "npm" ? (await execFileP("cmd.exe", ["/c", "npm", ...args], { timeout })).stdout : (await execFileP(file, file === "git" ? trainGitArgs(args) : args, { timeout, ...readEnv ? { env: readEnv } : {} })).stdout;
46781
46761
  } catch (e) {
46782
46762
  throw surfaceTrainSubprocessFailure(e, file, args);
46783
46763
  }
@@ -46896,7 +46876,7 @@ function renderDeployLine(d) {
46896
46876
  else if (d.runUrl) parts.push(`run ${d.runUrl}`);
46897
46877
  if (d.deployStatus === "success") parts.push("deploy: SUCCEEDED");
46898
46878
  else if (d.deployStatus === "failure") parts.push(`deploy: FAILED (${TRAIN_DEPLOY_FAILED_NOTE})`);
46899
- else if (d.runId != null) parts.push(`deploy: UNVERIFIED \u2014 dispatched, not resolved (watch: gh run watch ${d.runId} --repo mutmutco/MMI-Hub --exit-status --interval 30)`);
46879
+ else if (d.runId != null) parts.push(`deploy: UNVERIFIED \u2014 dispatched, not resolved (watch: gh run watch ${d.runId} --repo mutmutco/MMI-Hub --exit-status --interval 60)`);
46900
46880
  else if (d.workflowRuns?.length) parts.push("deploy: UNVERIFIED \u2014 the runs above are enumerated, not resolved; watch each to conclusion before calling this release healthy (#3322)");
46901
46881
  else parts.push("deploy: UNVERIFIED \u2014 no run correlated or watched; resolve every workflow run on the release SHA before calling this release healthy (#3322)");
46902
46882
  return parts.join("; ");
@@ -47702,11 +47682,12 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
47702
47682
  init_github_client();
47703
47683
  var GH_TIMEOUT = 3e4;
47704
47684
  var WATCH_TIMEOUT = 25 * 6e4;
47705
- async function exec(file, args, cwd, timeout = GH_TIMEOUT) {
47706
- return (await execFileP(file, args, { ...cwd ? { cwd } : {}, timeout })).stdout.trim();
47685
+ async function exec(file, args, cwd, timeout = GH_TIMEOUT, env) {
47686
+ return (await execFileP(file, args, { ...cwd ? { cwd } : {}, timeout, ...env ? { env } : {} })).stdout.trim();
47707
47687
  }
47708
47688
  async function gh(args) {
47709
- return exec("gh", args);
47689
+ const readEnv = isAppRoutableGhReadArgs(args) ? await ghReadEnv() : void 0;
47690
+ return exec("gh", args, void 0, GH_TIMEOUT, readEnv ?? void 0);
47710
47691
  }
47711
47692
  function isMissingRef(message2) {
47712
47693
  return /404|Not Found|No commit found for SHA/.test(message2);
@@ -47930,7 +47911,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
47930
47911
  }
47931
47912
  if (!candidate) throw new Error(`workflow dispatch outcome ambiguous for "${title}"; inspect Actions before retrying`);
47932
47913
  try {
47933
- await exec("gh", ["run", "watch", String(candidate.databaseId), "--repo", "mutmutco/MMI-Hub", "--exit-status"], void 0, WATCH_TIMEOUT);
47914
+ await exec("gh", ghRunWatchArgs(candidate.databaseId, "mutmutco/MMI-Hub"), void 0, WATCH_TIMEOUT, await ghReadEnv() ?? void 0);
47934
47915
  } catch {
47935
47916
  }
47936
47917
  const verdict = JSON.parse(await gh(["run", "view", String(candidate.databaseId), "--repo", "mutmutco/MMI-Hub", "--json", "conclusion"]));
@@ -47962,7 +47943,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
47962
47943
  };
47963
47944
  const rcRunCause = async (id) => {
47964
47945
  try {
47965
- return rcRunCauseFromLog(await exec("gh", ["run", "view", String(id), "--repo", "mutmutco/MMI-Hub", "--log-failed"]));
47946
+ return rcRunCauseFromLog(await gh(["run", "view", String(id), "--repo", "mutmutco/MMI-Hub", "--log-failed"]));
47966
47947
  } catch {
47967
47948
  return "";
47968
47949
  }
@@ -55166,7 +55147,7 @@ ${filelessTransitionGuide(target, o.stage)}`
55166
55147
  return reportWrite("org project set-deploy", res);
55167
55148
  });
55168
55149
  var projectTenantTasks = project.command("tenant-tasks").description("project-admin self-declare of tenant tasks for every stage of your own repo \u2014 new-task-only; editing an existing task stays master-only (`org project set --var tenantTasks=...`)");
55169
- projectTenantTasks.command("declare <name>").description("register a NEW tenant task \u2014 becomes runnable via `runtime tenant control \u2026 run-task` on its declared dev/rc/main stages. Body shape {service,command[],stages[],timeoutSeconds?,artifact?}; required artifact tasks must carry {artifact} in the command argv.").option("--service <name>", "service in the tenant runtime the task runs in (required unless --var carries it)").option("--command <argv...>", `task command as argv (repeatable; required unless --var carries it). An argv token that starts with "-" (python -m mod) is read as an option and the whole call fails to parse: declare such a command with --var '{"service":"<svc>","command":["python","-m","mod"],"stages":["dev"]}' instead (#7668)`).option("--stages <list>", "comma-separated dev,rc,main stages this task may run on (required unless --var carries it)").option("--timeout-seconds <n>", "optional wall-clock ceiling 1..3600").option("--artifact <none|required>", "artifact contract (default none; required tasks must contain {artifact} in argv)").option("--var <json>", "full task object {service,command[],stages[],timeoutSeconds?,artifact?} \u2014 use instead of the individual flags. A task carrying secretOutput:{path,vault:{repo,key},overwrite?} is master-only; declare that via `org project set --var tenantTasks=...`").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "machine-readable output").action(async (name, o) => {
55150
+ projectTenantTasks.command("declare <name>").description("register a NEW tenant task \u2014 becomes runnable via `runtime tenant control \u2026 run-task` on its declared dev/rc/main stages. Body shape {service,command[],stages[],timeoutSeconds?,artifact?}; required artifact tasks must carry {artifact} in the command argv.").option("--service <name>", "service in the tenant runtime the task runs in (required unless --var carries it)").option("--command <argv...>", `task command as argv (repeatable; required unless --var carries it). An argv token that starts with "-" (python -m mod) is read as an option and the whole call fails to parse: declare such a command with --var '{"service":"<svc>","command":["python","-m","mod"],"stages":["dev"]}' instead (#7668)`).option("--stages <list>", "comma-separated dev,rc,main stages this task may run on (required unless --var carries it)").option("--timeout-seconds <n>", "optional wall-clock ceiling 1..7200 (1..3600 with a secretOutput)").option("--artifact <none|required>", "artifact contract (default none; required tasks must contain {artifact} in argv)").option("--var <json>", "full task object {service,command[],stages[],timeoutSeconds?,artifact?} \u2014 use instead of the individual flags. A task carrying secretOutput:{path,vault:{repo,key},overwrite?} is master-only; declare that via `org project set --var tenantTasks=...`").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "machine-readable output").action(async (name, o) => {
55170
55151
  const cfg = await loadConfig();
55171
55152
  let target;
55172
55153
  try {
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.110",
3
+ "version": "4.5.112",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/codex-plugin",
3
- "version": "4.5.110",
3
+ "version": "4.5.112",
4
4
  "description": "MMI workflow skills and org gates delivery.",
5
5
  "author": {
6
6
  "name": "MMI Future",