@mutmutco/codex-plugin 4.6.28 → 4.6.29

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.6.28",
3
+ "version": "4.6.29",
4
4
  "mmiCompat": "4.x",
5
5
  "description": "MMI workflow skills and org gates delivery.",
6
6
  "author": {
package/cli/dist/main.cjs CHANGED
@@ -9092,6 +9092,73 @@ var require_dist = __commonJS({
9092
9092
  }
9093
9093
  });
9094
9094
 
9095
+ // src/ci-hosted-usage.ts
9096
+ var ci_hosted_usage_exports = {};
9097
+ __export(ci_hosted_usage_exports, {
9098
+ projectHostedUsage: () => projectHostedUsage,
9099
+ renderHostedUsage: () => renderHostedUsage
9100
+ });
9101
+ function projectHostedUsage(config, now = Date.now()) {
9102
+ const summary = object(config.hostedUsage);
9103
+ const state = object(summary?.state);
9104
+ const tick = object(state?.lastTick);
9105
+ const ok = object(state?.lastOk);
9106
+ const record2 = object(summary?.record);
9107
+ const unknowns = [];
9108
+ let lastTick = null;
9109
+ if (tick && typeof tick.ok === "boolean") {
9110
+ lastTick = { ok: tick.ok };
9111
+ if (iso(tick.at)) lastTick.at = tick.at;
9112
+ if (typeof tick.complete === "boolean") lastTick.complete = tick.complete;
9113
+ for (const k of ["scanned", "usedUsd", "remainingUsd", "paceUsdPerDay"]) {
9114
+ if (typeof tick[k] === "number" && Number.isFinite(tick[k])) lastTick[k] = tick[k];
9115
+ }
9116
+ if (iso(tick.asOf)) lastTick.asOf = tick.asOf;
9117
+ }
9118
+ const lastOk = ok && iso(ok.at) && typeof ok.scanned === "number" && Number.isSafeInteger(ok.scanned) && ok.scanned >= 0 ? { at: ok.at, scanned: ok.scanned, ...typeof ok.complete === "boolean" && { complete: ok.complete } } : null;
9119
+ const allowanceInputs = record2 && ["usedUsd", "remainingUsd", "paceUsdPerDay"].every((k) => typeof record2[k] === "number" && Number.isFinite(record2[k])) && iso(record2.asOf) && typeof record2.source === "string" && /^[\w .:/-]{1,64}$/.test(record2.source) ? { usedUsd: record2.usedUsd, remainingUsd: record2.remainingUsd, paceUsdPerDay: record2.paceUsdPerDay, asOf: record2.asOf, source: record2.source } : null;
9120
+ const deployedRevision = typeof summary?.deployedRevision === "string" && /^[\w.+-]{1,128}$/.test(summary.deployedRevision) ? summary.deployedRevision : null;
9121
+ if (!summary) unknowns.push("hostedUsage unavailable (server may predate this read or summary read failed)");
9122
+ if (!deployedRevision) unknowns.push("deployedRevision unavailable");
9123
+ if (!lastTick?.at) unknowns.push("lastTick.at unavailable");
9124
+ if (!lastOk) unknowns.push("lastOk unavailable");
9125
+ if (lastTick?.complete === void 0) unknowns.push("tick completeness unavailable");
9126
+ if (!allowanceInputs) unknowns.push("allowance inputs and freshness unavailable");
9127
+ if (summary && summary.record !== null && !allowanceInputs) unknowns.push("unrecognized allowance projection");
9128
+ const age = allowanceInputs ? now - Date.parse(allowanceInputs.asOf) : null;
9129
+ return {
9130
+ source: { route: "GET /org/config", projection: "ORG#config hostedUsage", state: "CIHOSTED#v1/STATE", usage: "CIHOSTED#v1/USAGE" },
9131
+ deployedRevision,
9132
+ observedAt: new Date(now).toISOString(),
9133
+ lastTick,
9134
+ lastOk,
9135
+ allowanceInputs,
9136
+ decision: allowanceInputs ? "allowance served" : summary?.record === null ? "no allowance: record absent/malformed" : null,
9137
+ complete: lastTick?.complete ?? null,
9138
+ fresh: age === null ? null : age >= 0 && age < 12 * 36e5,
9139
+ unknowns
9140
+ };
9141
+ }
9142
+ function renderHostedUsage(read) {
9143
+ const a = read.allowanceInputs;
9144
+ return [
9145
+ `${read.source.route} \u2014 ${read.source.projection} (${read.source.state}, ${read.source.usage})`,
9146
+ `revision=${read.deployedRevision ?? "unknown"} observedAt=${read.observedAt}`,
9147
+ `${read.decision ?? "decision unknown"}; complete=${read.complete ?? "unknown"} fresh=${read.fresh ?? "unknown"}`,
9148
+ `lastTick=${read.lastTick?.at ?? "unknown"} ok=${read.lastTick?.ok ?? "unknown"}; lastOk=${read.lastOk?.at ?? "unknown"}`,
9149
+ ...a ? [`Runtime allowance: used=$${a.usedUsd} remaining=$${a.remainingUsd} pace=$${a.paceUsdPerDay}/day asOf=${a.asOf} source=${a.source}`] : [],
9150
+ ...read.unknowns.length ? [`Unknowns: ${read.unknowns.join("; ")}`] : []
9151
+ ].join("\n");
9152
+ }
9153
+ var object, iso;
9154
+ var init_ci_hosted_usage = __esm({
9155
+ "src/ci-hosted-usage.ts"() {
9156
+ "use strict";
9157
+ object = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
9158
+ iso = (v) => typeof v === "string" && Number.isFinite(Date.parse(v)) && new Date(v).toISOString() === v;
9159
+ }
9160
+ });
9161
+
9095
9162
  // src/index.ts
9096
9163
  var index_exports = {};
9097
9164
  __export(index_exports, {
@@ -28181,10 +28248,10 @@ var rollout_plan_default = {
28181
28248
  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)."
28182
28249
  },
28183
28250
  baseline: {
28184
- version: "4.6.28",
28185
- tag: "v4.6.28",
28186
- commit: "e21db51c4641",
28187
- npm: "@mutmutco/cli@4.6.28"
28251
+ version: "4.6.29",
28252
+ tag: "v4.6.29",
28253
+ commit: "36e01a038f52",
28254
+ npm: "@mutmutco/cli@4.6.29"
28188
28255
  },
28189
28256
  exitCriterion: "fleet-n-of-n",
28190
28257
  hubOnlyShortcut: "forbidden",
@@ -28201,14 +28268,14 @@ var rollout_plan_default = {
28201
28268
  repo: "mutmutco/mmi-hub",
28202
28269
  role: "canary",
28203
28270
  schedule: "train",
28204
- v3Target: "v4.6.28"
28271
+ v3Target: "v4.6.29"
28205
28272
  }
28206
28273
  ],
28207
28274
  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.",
28208
28275
  rollback: {
28209
28276
  independent: true,
28210
- mechanism: "npm dist-tag latest -> 4.6.28 and redeploy the Hub Lambda from tag v4.6.28 (e21db51c4641); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
28211
- v3Target: "v4.6.28 (@mutmutco/cli@4.6.28, tag commit e21db51c4641 \u2014 last known-good release carrying the repo-index v4-only contract)"
28277
+ mechanism: "npm dist-tag latest -> 4.6.29 and redeploy the Hub Lambda from tag v4.6.29 (36e01a038f52); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
28278
+ v3Target: "v4.6.29 (@mutmutco/cli@4.6.29, tag commit 36e01a038f52 \u2014 last known-good release carrying the repo-index v4-only contract)"
28212
28279
  }
28213
28280
  },
28214
28281
  {
@@ -31376,15 +31443,15 @@ function parseRuntimeIdentity(log, expected) {
31376
31443
  if (proofs.length !== 1) return unavailable("invalid-proof");
31377
31444
  const encoded = proofs[0].slice(prefix.length).trim();
31378
31445
  if (encoded.length > 16384) return unavailable("invalid-proof");
31379
- const object = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
31446
+ const object2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
31380
31447
  const keys = (v, allowed) => Object.keys(v).length === allowed.length && Object.keys(v).every((k) => allowed.includes(k));
31381
31448
  const text = (v, re) => typeof v === "string" && re.test(v);
31382
31449
  try {
31383
31450
  const p = JSON.parse(encoded);
31384
- if (JSON.stringify(p) !== encoded || !object(p)) return unavailable("invalid-proof");
31451
+ if (JSON.stringify(p) !== encoded || !object2(p)) return unavailable("invalid-proof");
31385
31452
  if (p.version !== 1) return unavailable("unsupported-version");
31386
31453
  const parts = expected.repo.split("/");
31387
- if (parts.length !== 2 || !parts.every(Boolean) || !object(p.target) || !keys(p.target, ["slug", "stage"]) || p.target.slug !== parts[1].toLowerCase() || p.target.stage !== expected.stage) return unavailable("target-mismatch");
31454
+ if (parts.length !== 2 || !parts.every(Boolean) || !object2(p.target) || !keys(p.target, ["slug", "stage"]) || p.target.slug !== parts[1].toLowerCase() || p.target.stage !== expected.stage) return unavailable("target-mismatch");
31388
31455
  if (p.status === "unavailable") {
31389
31456
  if (!keys(p, ["version", "target", "status", "reason"]) || typeof p.reason !== "string" || !["selection-unavailable", "inspection-failed", "snapshot-changed", "output-too-large"].includes(p.reason)) return unavailable("invalid-proof");
31390
31457
  return unavailable(p.reason);
@@ -31392,7 +31459,7 @@ function parseRuntimeIdentity(log, expected) {
31392
31459
  if (!keys(p, ["version", "target", "status", "slot", "replicas"]) || p.status !== "available" || p.slot !== "blue" && p.slot !== "green" || !Array.isArray(p.replicas) || !p.replicas.length) return unavailable("invalid-proof");
31393
31460
  const seen = /* @__PURE__ */ new Set();
31394
31461
  for (const r of p.replicas) {
31395
- if (!object(r) || !keys(r, ["service", "container", "configuredRef", "actualImageID", "repoDigests", "health", "state"]) || !text(r.service, /^[A-Za-z0-9][A-Za-z0-9_-]*$/) || !text(r.container, /^[a-f0-9]{64}$/) || !text(r.configuredRef, /^[A-Za-z0-9][A-Za-z0-9._/@:-]*$/) || !text(r.actualImageID, /^sha256:[a-f0-9]{64}$/) || !Array.isArray(r.repoDigests) || !r.repoDigests.every((d) => text(d, /^[A-Za-z0-9][A-Za-z0-9._/:-]*@sha256:[a-f0-9]{64}$/)) || (typeof r.health !== "string" || !["healthy", "unhealthy", "starting", "not-configured", "unknown"].includes(r.health)) || r.state !== "running" || seen.has(r.container)) return unavailable("invalid-proof");
31462
+ if (!object2(r) || !keys(r, ["service", "container", "configuredRef", "actualImageID", "repoDigests", "health", "state"]) || !text(r.service, /^[A-Za-z0-9][A-Za-z0-9_-]*$/) || !text(r.container, /^[a-f0-9]{64}$/) || !text(r.configuredRef, /^[A-Za-z0-9][A-Za-z0-9._/@:-]*$/) || !text(r.actualImageID, /^sha256:[a-f0-9]{64}$/) || !Array.isArray(r.repoDigests) || !r.repoDigests.every((d) => text(d, /^[A-Za-z0-9][A-Za-z0-9._/:-]*@sha256:[a-f0-9]{64}$/)) || (typeof r.health !== "string" || !["healthy", "unhealthy", "starting", "not-configured", "unknown"].includes(r.health)) || r.state !== "running" || seen.has(r.container)) return unavailable("invalid-proof");
31396
31463
  seen.add(r.container);
31397
31464
  }
31398
31465
  return p;
@@ -58712,6 +58779,7 @@ function registerApiReadCommands(program3, overrides) {
58712
58779
  if (zip !== (o.out !== void 0)) {
58713
58780
  const shapes = "api get: --out <file> is required for, and only valid with, repos/{owner}/{repo}/actions/artifacts/{id}/zip and repos/{owner}/{repo}/actions/jobs/{id}/logs and repos/{owner}/{repo}/releases/assets/{id}";
58714
58781
  if (!zip && o.out !== void 0) {
58782
+ if (req.kind === "contents") return fail(`${shapes} \u2014 for this file's bytes, drop --out and use \`--accept raw\` with a redirect: \`mmi-cli devops api get "${req.path}" --accept raw > <file>\` (decoded bytes on stdout, no base64 or JSON.parse). This refusal exits 1 on stderr with nothing written: check the exit code and never discard stderr (2>&1 / >/dev/null) before parsing.`);
58715
58783
  return fail(`${shapes} \u2014 drop --out and let the JSON body go to stdout (pipe it into node/jq, or use --jq <expr>), or use the typed read (e.g. \`mmi-cli devops read run list --repo <owner/repo> --json databaseId,workflowName,event,status,conclusion\`). This refusal exits 1 on stderr with nothing written: check the exit code and never discard stderr (2>&1 / >/dev/null) before parsing.`);
58716
58784
  }
58717
58785
  return fail(`${shapes} \u2014 append \`--out <file>\` to this command (e.g. \`MMI_ACTOR=app mmi-cli devops api get ${req.path} --out .jerv/tmp/job.log\`), or for job logs use the typed read \`MMI_ACTOR=app mmi-cli devops read run log <runId> --repo <owner/repo> --job <name|id>\`. Shell redirection (\`>\` / \`2>&1\`) is not a substitute: this refusal exits 1 on stderr with nothing written to stdout, so \`>\` leaves a zero-byte file \u2014 delete it and check the exit code.`);
@@ -71172,6 +71240,24 @@ function parseWindow(raw, nowMs) {
71172
71240
  }
71173
71241
  return { ok: true, value: { since: formatUtc(nowMs - span), label: value, isDefault: false } };
71174
71242
  }
71243
+ function parseUntil(raw, nowMs) {
71244
+ if (raw === void 0) return { ok: true, value: void 0 };
71245
+ const value = raw.trim();
71246
+ if (ABSOLUTE_WINDOW_RE.test(value)) return { ok: true, value: { until: value, label: value } };
71247
+ const rel = RELATIVE_WINDOW_RE.exec(value);
71248
+ if (!rel) {
71249
+ return {
71250
+ ok: false,
71251
+ reason: `--until must be a relative window like 15m, 6h or 2d, or a UTC time like 2026-10-07T13:10Z (got '${value}')`
71252
+ };
71253
+ }
71254
+ const unit = rel[2] === "m" ? MINUTE_MS : rel[2] === "h" ? 60 * MINUTE_MS : 24 * 60 * MINUTE_MS;
71255
+ const span = Number(rel[1]) * unit;
71256
+ if (span > TENANT_LOG_WINDOW_MAX_MS) {
71257
+ return { ok: false, reason: `--until ${value} is wider than 30d \u2014 the journal keeps 7 days, so a wider window buys no history and unbounded work` };
71258
+ }
71259
+ return { ok: true, value: { until: formatUtc(nowMs - span), label: value } };
71260
+ }
71175
71261
  function tenantLogUnitPrefix(slug, stage) {
71176
71262
  if (!SLUG_RE.test(slug)) return { ok: false, reason: `'${slug}' is not a tenant slug \u2014 refusing to build a unit namespace from it` };
71177
71263
  if (!TENANT_LOG_STAGES.includes(stage)) {
@@ -71263,6 +71349,11 @@ async function readTenantLogs(deps, opts) {
71263
71349
  const nowMs = deps.now();
71264
71350
  const window = parseWindow(opts.since, nowMs);
71265
71351
  if (!window.ok) return window;
71352
+ const until = parseUntil(opts.until, nowMs);
71353
+ if (!until.ok) return until;
71354
+ if (until.value && Date.parse(until.value.until) <= Date.parse(window.value.since)) {
71355
+ return { ok: false, reason: `--until ${until.value.label} must be after --since ${window.value.label}` };
71356
+ }
71266
71357
  if (opts.grep !== void 0 && opts.grep.length === 0) return { ok: false, reason: "--grep needs a non-empty literal needle" };
71267
71358
  const slug = opts.repo.includes("/") ? (opts.repo.split("/").pop() ?? "").toLowerCase() : opts.repo.toLowerCase();
71268
71359
  const facts = await deps.deployFacts(slug);
@@ -71273,7 +71364,8 @@ async function readTenantLogs(deps, opts) {
71273
71364
  stage: coords.value.stage,
71274
71365
  action: "logs",
71275
71366
  lines: tail.value,
71276
- logSince: window.value.since
71367
+ logSince: window.value.since,
71368
+ logUntil: until.value?.until
71277
71369
  });
71278
71370
  const credentials = deps.credentials?.() ?? [];
71279
71371
  const bounded = applyBound(redactJournal(read.logs ?? "", credentials), tail.value);
@@ -71296,12 +71388,15 @@ async function readTenantLogs(deps, opts) {
71296
71388
  since: window.value.since,
71297
71389
  sinceLabel: window.value.label,
71298
71390
  sinceIsDefault: window.value.isDefault,
71391
+ ...until.value ? { until: until.value.until, untilLabel: until.value.label } : {},
71299
71392
  truncated: bounded.truncated,
71300
71393
  dropped: bounded.dropped,
71301
71394
  received: bounded.received
71302
71395
  },
71303
- ...opts.grep ? { grep: { needle: opts.grep, matched: filtered.matched } } : {},
71304
- units: observedUnits(filtered.lines, coords.value.unitPrefix),
71396
+ ...opts.grep ? { grep: { needle: opts.grep, matched: filtered.matched, scanned: lines2.length } } : {},
71397
+ // #9878: report units from the BOUNDED lines, not the grep survivors — a grep miss must still say
71398
+ // which units answered, or a filter miss reads as data loss.
71399
+ units: observedUnits(lines2, coords.value.unitPrefix),
71305
71400
  lines: filtered.lines,
71306
71401
  conclusion: read.conclusion,
71307
71402
  ...read.runUrl ? { runUrl: read.runUrl } : {},
@@ -71314,18 +71409,18 @@ function renderTenantLogs(r) {
71314
71409
  `runtime tenant logs ${r.repo} ${r.stage} \u2014 journald on the stage's own box, slot resolved from the tenant's coords (no unit name, no slot flag)`,
71315
71410
  ` tenant ${r.slug} (DEPLOY#${r.stage})${r.box ? ` box ${r.box}` : ""}${r.deployedTag ? ` tag ${r.deployedTag}` : ""}`,
71316
71411
  ` units ${r.unitPrefix}* (journald tag namespace derived from the coords)`,
71317
- ` window since ${r.bound.since}${r.bound.sinceIsDefault ? " (default 1h)" : ""} \u2014 journald keys by container NAME, so a deploy-recreated container still answers`,
71412
+ ` window since ${r.bound.since}${r.bound.sinceIsDefault ? " (default 1h)" : ""}${r.bound.until ? ` until ${r.bound.until}` : ""} \u2014 journald keys by container NAME, so a deploy-recreated container still answers`,
71318
71413
  ` bound last ${r.bound.tail} line(s) of that window (max ${r.bound.tailMax}, default ${r.bound.tailDefault})`
71319
71414
  ];
71320
71415
  if (r.bound.truncated) {
71321
71416
  out.push(` NOTE the box returned ${r.bound.received} line(s); the bound kept the last ${r.bound.tail} and dropped ${r.bound.dropped}`);
71322
71417
  }
71323
71418
  if (r.grep) {
71324
- out.push(` grep '${r.grep.needle}' kept ${r.grep.matched} of ${r.lines.length === r.grep.matched ? r.lines.length : r.bound.tail} returned line(s) \u2014 the filter runs on the BOUNDED tail, not the whole journal; widen --tail/--since to search further back`);
71419
+ out.push(` grep '${r.grep.needle}' kept ${r.grep.matched} of ${r.grep.scanned} bounded line(s) \u2014 the filter runs on the BOUNDED tail, not the whole journal; widen --tail or narrow with --since/--until to search further back`);
71325
71420
  }
71326
71421
  out.push(` observed ${r.units.length ? r.units.map((u) => `${u.unit} (${u.lines})`).join(", ") : `no ${r.unitPrefix}* lines in this window`}`);
71327
71422
  if (!r.lines.length) {
71328
- out.push(" (no lines \u2014 the window is empty for this tenant stage; widen --since rather than concluding the app was quiet)");
71423
+ out.push(r.grep && r.grep.scanned > 0 ? ` NOTE grep matched 0 of ${r.grep.scanned} bounded line(s) \u2014 that is a filter miss, not an empty window; narrow with --since/--until or widen --tail` : " (no lines \u2014 the window is empty for this tenant stage; widen --since rather than concluding the app was quiet)");
71329
71424
  } else {
71330
71425
  out.push("---8<---");
71331
71426
  out.push(...r.lines);
@@ -73402,7 +73497,7 @@ tenant.command("status <owner/repo> <stage>").description("read tenant runtime r
73402
73497
  console.log(JSON.stringify(result, null, 2));
73403
73498
  if (result.publicProbe?.ok === false || result.edgeCertificate?.missing) process.exitCode = 1;
73404
73499
  });
73405
- tenant.command("logs <owner/repo> <stage>").description("READ-ONLY: read a tenant stage's application journal (journald, so a deploy-recreated container still answers) \u2014 the unit namespace comes from the tenant's DEPLOY coords and the blue/green slot is chosen on the box, never from a flag; bounded and redacted (#9230)").option("--tail <n>", `lines to return (1-${TENANT_LOG_TAIL_MAX}, default ${TENANT_LOG_TAIL_DEFAULT}); the applied bound is printed`).option("--since <window>", "window start: a relative window like 15m, 6h or 2d, or a UTC time like 2026-10-07T13:10Z (default 1h)").option("--grep <needle>", "keep only returned lines containing this literal text (applied to the bounded tail, not the whole journal)").option("--json", "machine-readable output").action(async (repo, stage, o) => {
73500
+ tenant.command("logs <owner/repo> <stage>").description("READ-ONLY: read a tenant stage's application journal (journald, so a deploy-recreated container still answers) \u2014 the unit namespace comes from the tenant's DEPLOY coords and the blue/green slot is chosen on the box, never from a flag; bounded and redacted (#9230)").option("--tail <n>", `lines to return (1-${TENANT_LOG_TAIL_MAX}, default ${TENANT_LOG_TAIL_DEFAULT}); the applied bound is printed`).option("--since <window>", "window start: a relative window like 15m, 6h or 2d, or a UTC time like 2026-10-07T13:10Z (default 1h)").option("--until <time>", "window end: same forms as --since (default: now) \u2014 the box returns the newest N lines OF the window, so end it earlier to reach older lines on a busy tenant (#9878)").option("--grep <needle>", "keep only returned lines containing this literal text (applied to the bounded tail, not the whole journal)").option("--json", "machine-readable output").action(async (repo, stage, o) => {
73406
73501
  try {
73407
73502
  const cfg = await loadConfig();
73408
73503
  const reg = registryClientDeps(cfg);
@@ -73410,12 +73505,12 @@ tenant.command("logs <owner/repo> <stage>").description("READ-ONLY: read a tenan
73410
73505
  {
73411
73506
  deployFacts: (slug) => fetchDeployFactsBySlug(slug, reg),
73412
73507
  // The one dispatch a log read may make. Never watched=false: the run's OUTPUT is the answer.
73413
- control: (opts) => runTenantControl(trainApplyDeps(), { repo: opts.repo, stage: opts.stage, action: opts.action, watch: true, lines: opts.lines, logSince: opts.logSince }),
73508
+ control: (opts) => runTenantControl(trainApplyDeps(), { repo: opts.repo, stage: opts.stage, action: opts.action, watch: true, lines: opts.lines, logSince: opts.logSince, logUntil: opts.logUntil }),
73414
73509
  now: Date.now,
73415
73510
  // The credentials this process holds, so the repo's exact-string redactor can strip them.
73416
73511
  credentials: () => [process.env.GH_TOKEN].filter((c) => !!c)
73417
73512
  },
73418
- { repo, stage, tail: o.tail, since: o.since, grep: o.grep }
73513
+ { repo, stage, tail: o.tail, since: o.since, until: o.until, grep: o.grep }
73419
73514
  );
73420
73515
  if (!result.ok) return failGraceful(`runtime tenant logs: ${result.reason}`);
73421
73516
  if (o.json) {
@@ -74132,6 +74227,13 @@ ci.command("audit").description("read-only fleet scan: gate workflow, ruleset co
74132
74227
  else console.log(renderCiAuditText(report));
74133
74228
  if (!report.ok) process.exitCode = 1;
74134
74229
  });
74230
+ ci.command("hosted-usage").description("read-only hosted tick and lane-demand allowance proof from org config").option("--json", "machine-readable output").action(async (o) => {
74231
+ const org = await fetchOrgConfig(registryClientDeps(await loadConfig()));
74232
+ if (!org) return failGraceful("ci hosted-usage: Hub org config unavailable");
74233
+ const { projectHostedUsage: projectHostedUsage2, renderHostedUsage: renderHostedUsage2 } = await Promise.resolve().then(() => (init_ci_hosted_usage(), ci_hosted_usage_exports));
74234
+ const read = projectHostedUsage2(org);
74235
+ console.log(o.json ? JSON.stringify(read, null, 2) : renderHostedUsage2(read));
74236
+ });
74135
74237
  registerCiChecksWait(ci);
74136
74238
  registerCancelOrphans(ci, async () => (await mintCommandWriteToken(appActorDeps(), "ci cancel-orphans")).token);
74137
74239
  var runRoot = program2.command("run").description("bounded workflow-run writes \u2014 `run cancel <id>` and `run rerun <id> [--failed]`, both on the org App actions:write profile");
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.6.28",
3
+ "version": "4.6.29",
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.6.28",
3
+ "version": "4.6.29",
4
4
  "description": "MMI workflow skills and org gates delivery.",
5
5
  "author": {
6
6
  "name": "MMI Future",