@mutmutco/cli 4.6.27 → 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.
- package/dist/main.cjs +126 -22
- package/package.json +1 -1
package/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, {
|
|
@@ -15621,7 +15688,9 @@ var notedVaultDenied = false;
|
|
|
15621
15688
|
var APP_SUBPROCESS_ROUTED_COMMANDS = /* @__PURE__ */ new Set([
|
|
15622
15689
|
"issue create",
|
|
15623
15690
|
"pr create",
|
|
15624
|
-
"pr merge"
|
|
15691
|
+
"pr merge",
|
|
15692
|
+
// #9871: queue enqueue + auto-merge fallback ride ghFilingAppEnv (#9490/#9528; receipt credential app_installation).
|
|
15693
|
+
"pr land"
|
|
15625
15694
|
]);
|
|
15626
15695
|
async function activateAppActor(commandPath4, env, mint, note = (line) => process.stderr.write(`${line}
|
|
15627
15696
|
`)) {
|
|
@@ -25908,7 +25977,7 @@ __export(gate_budget_exports, {
|
|
|
25908
25977
|
isGateWorkflowPath: () => isGateWorkflowPath
|
|
25909
25978
|
});
|
|
25910
25979
|
var BLESSED_RUN_WITH_BUDGET_SHA = "cb31a94c89729beac83500ad4d301132dcfe2e4c";
|
|
25911
|
-
var BLESSED_RUNNER_GATE_SHA = "
|
|
25980
|
+
var BLESSED_RUNNER_GATE_SHA = "7f9a25df2ce7198c67407b42d0c548f75ce3f430";
|
|
25912
25981
|
var BLESSED_RUNNER_CHECK_POLICY_SHA = "67bb2012710a96d21be825c29a5d5c2b4b8001e2";
|
|
25913
25982
|
var BLESSED_KIT_AGENT_FILES_SHA = "e976df208d891b215844386c1cac0f161946b6fa";
|
|
25914
25983
|
var BLESSED_JEV_FAILURES_SHA = "d5278c6133318b33727483ddfdfddc8f841e3ef6";
|
|
@@ -28179,10 +28248,10 @@ var rollout_plan_default = {
|
|
|
28179
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)."
|
|
28180
28249
|
},
|
|
28181
28250
|
baseline: {
|
|
28182
|
-
version: "4.6.
|
|
28183
|
-
tag: "v4.6.
|
|
28184
|
-
commit: "
|
|
28185
|
-
npm: "@mutmutco/cli@4.6.
|
|
28251
|
+
version: "4.6.29",
|
|
28252
|
+
tag: "v4.6.29",
|
|
28253
|
+
commit: "36e01a038f52",
|
|
28254
|
+
npm: "@mutmutco/cli@4.6.29"
|
|
28186
28255
|
},
|
|
28187
28256
|
exitCriterion: "fleet-n-of-n",
|
|
28188
28257
|
hubOnlyShortcut: "forbidden",
|
|
@@ -28199,14 +28268,14 @@ var rollout_plan_default = {
|
|
|
28199
28268
|
repo: "mutmutco/mmi-hub",
|
|
28200
28269
|
role: "canary",
|
|
28201
28270
|
schedule: "train",
|
|
28202
|
-
v3Target: "v4.6.
|
|
28271
|
+
v3Target: "v4.6.29"
|
|
28203
28272
|
}
|
|
28204
28273
|
],
|
|
28205
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.",
|
|
28206
28275
|
rollback: {
|
|
28207
28276
|
independent: true,
|
|
28208
|
-
mechanism: "npm dist-tag latest -> 4.6.
|
|
28209
|
-
v3Target: "v4.6.
|
|
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)"
|
|
28210
28279
|
}
|
|
28211
28280
|
},
|
|
28212
28281
|
{
|
|
@@ -31374,15 +31443,15 @@ function parseRuntimeIdentity(log, expected) {
|
|
|
31374
31443
|
if (proofs.length !== 1) return unavailable("invalid-proof");
|
|
31375
31444
|
const encoded = proofs[0].slice(prefix.length).trim();
|
|
31376
31445
|
if (encoded.length > 16384) return unavailable("invalid-proof");
|
|
31377
|
-
const
|
|
31446
|
+
const object2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
31378
31447
|
const keys = (v, allowed) => Object.keys(v).length === allowed.length && Object.keys(v).every((k) => allowed.includes(k));
|
|
31379
31448
|
const text = (v, re) => typeof v === "string" && re.test(v);
|
|
31380
31449
|
try {
|
|
31381
31450
|
const p = JSON.parse(encoded);
|
|
31382
|
-
if (JSON.stringify(p) !== encoded || !
|
|
31451
|
+
if (JSON.stringify(p) !== encoded || !object2(p)) return unavailable("invalid-proof");
|
|
31383
31452
|
if (p.version !== 1) return unavailable("unsupported-version");
|
|
31384
31453
|
const parts = expected.repo.split("/");
|
|
31385
|
-
if (parts.length !== 2 || !parts.every(Boolean) || !
|
|
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");
|
|
31386
31455
|
if (p.status === "unavailable") {
|
|
31387
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");
|
|
31388
31457
|
return unavailable(p.reason);
|
|
@@ -31390,7 +31459,7 @@ function parseRuntimeIdentity(log, expected) {
|
|
|
31390
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");
|
|
31391
31460
|
const seen = /* @__PURE__ */ new Set();
|
|
31392
31461
|
for (const r of p.replicas) {
|
|
31393
|
-
if (!
|
|
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");
|
|
31394
31463
|
seen.add(r.container);
|
|
31395
31464
|
}
|
|
31396
31465
|
return p;
|
|
@@ -58710,6 +58779,7 @@ function registerApiReadCommands(program3, overrides) {
|
|
|
58710
58779
|
if (zip !== (o.out !== void 0)) {
|
|
58711
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}";
|
|
58712
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.`);
|
|
58713
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.`);
|
|
58714
58784
|
}
|
|
58715
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.`);
|
|
@@ -71170,6 +71240,24 @@ function parseWindow(raw, nowMs) {
|
|
|
71170
71240
|
}
|
|
71171
71241
|
return { ok: true, value: { since: formatUtc(nowMs - span), label: value, isDefault: false } };
|
|
71172
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
|
+
}
|
|
71173
71261
|
function tenantLogUnitPrefix(slug, stage) {
|
|
71174
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` };
|
|
71175
71263
|
if (!TENANT_LOG_STAGES.includes(stage)) {
|
|
@@ -71261,6 +71349,11 @@ async function readTenantLogs(deps, opts) {
|
|
|
71261
71349
|
const nowMs = deps.now();
|
|
71262
71350
|
const window = parseWindow(opts.since, nowMs);
|
|
71263
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
|
+
}
|
|
71264
71357
|
if (opts.grep !== void 0 && opts.grep.length === 0) return { ok: false, reason: "--grep needs a non-empty literal needle" };
|
|
71265
71358
|
const slug = opts.repo.includes("/") ? (opts.repo.split("/").pop() ?? "").toLowerCase() : opts.repo.toLowerCase();
|
|
71266
71359
|
const facts = await deps.deployFacts(slug);
|
|
@@ -71271,7 +71364,8 @@ async function readTenantLogs(deps, opts) {
|
|
|
71271
71364
|
stage: coords.value.stage,
|
|
71272
71365
|
action: "logs",
|
|
71273
71366
|
lines: tail.value,
|
|
71274
|
-
logSince: window.value.since
|
|
71367
|
+
logSince: window.value.since,
|
|
71368
|
+
logUntil: until.value?.until
|
|
71275
71369
|
});
|
|
71276
71370
|
const credentials = deps.credentials?.() ?? [];
|
|
71277
71371
|
const bounded = applyBound(redactJournal(read.logs ?? "", credentials), tail.value);
|
|
@@ -71294,12 +71388,15 @@ async function readTenantLogs(deps, opts) {
|
|
|
71294
71388
|
since: window.value.since,
|
|
71295
71389
|
sinceLabel: window.value.label,
|
|
71296
71390
|
sinceIsDefault: window.value.isDefault,
|
|
71391
|
+
...until.value ? { until: until.value.until, untilLabel: until.value.label } : {},
|
|
71297
71392
|
truncated: bounded.truncated,
|
|
71298
71393
|
dropped: bounded.dropped,
|
|
71299
71394
|
received: bounded.received
|
|
71300
71395
|
},
|
|
71301
|
-
...opts.grep ? { grep: { needle: opts.grep, matched: filtered.matched } } : {},
|
|
71302
|
-
|
|
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),
|
|
71303
71400
|
lines: filtered.lines,
|
|
71304
71401
|
conclusion: read.conclusion,
|
|
71305
71402
|
...read.runUrl ? { runUrl: read.runUrl } : {},
|
|
@@ -71312,18 +71409,18 @@ function renderTenantLogs(r) {
|
|
|
71312
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)`,
|
|
71313
71410
|
` tenant ${r.slug} (DEPLOY#${r.stage})${r.box ? ` box ${r.box}` : ""}${r.deployedTag ? ` tag ${r.deployedTag}` : ""}`,
|
|
71314
71411
|
` units ${r.unitPrefix}* (journald tag namespace derived from the coords)`,
|
|
71315
|
-
` 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`,
|
|
71316
71413
|
` bound last ${r.bound.tail} line(s) of that window (max ${r.bound.tailMax}, default ${r.bound.tailDefault})`
|
|
71317
71414
|
];
|
|
71318
71415
|
if (r.bound.truncated) {
|
|
71319
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}`);
|
|
71320
71417
|
}
|
|
71321
71418
|
if (r.grep) {
|
|
71322
|
-
out.push(` grep '${r.grep.needle}' kept ${r.grep.matched} of ${r.
|
|
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`);
|
|
71323
71420
|
}
|
|
71324
71421
|
out.push(` observed ${r.units.length ? r.units.map((u) => `${u.unit} (${u.lines})`).join(", ") : `no ${r.unitPrefix}* lines in this window`}`);
|
|
71325
71422
|
if (!r.lines.length) {
|
|
71326
|
-
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)");
|
|
71327
71424
|
} else {
|
|
71328
71425
|
out.push("---8<---");
|
|
71329
71426
|
out.push(...r.lines);
|
|
@@ -73400,7 +73497,7 @@ tenant.command("status <owner/repo> <stage>").description("read tenant runtime r
|
|
|
73400
73497
|
console.log(JSON.stringify(result, null, 2));
|
|
73401
73498
|
if (result.publicProbe?.ok === false || result.edgeCertificate?.missing) process.exitCode = 1;
|
|
73402
73499
|
});
|
|
73403
|
-
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) => {
|
|
73404
73501
|
try {
|
|
73405
73502
|
const cfg = await loadConfig();
|
|
73406
73503
|
const reg = registryClientDeps(cfg);
|
|
@@ -73408,12 +73505,12 @@ tenant.command("logs <owner/repo> <stage>").description("READ-ONLY: read a tenan
|
|
|
73408
73505
|
{
|
|
73409
73506
|
deployFacts: (slug) => fetchDeployFactsBySlug(slug, reg),
|
|
73410
73507
|
// The one dispatch a log read may make. Never watched=false: the run's OUTPUT is the answer.
|
|
73411
|
-
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 }),
|
|
73412
73509
|
now: Date.now,
|
|
73413
73510
|
// The credentials this process holds, so the repo's exact-string redactor can strip them.
|
|
73414
73511
|
credentials: () => [process.env.GH_TOKEN].filter((c) => !!c)
|
|
73415
73512
|
},
|
|
73416
|
-
{ 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 }
|
|
73417
73514
|
);
|
|
73418
73515
|
if (!result.ok) return failGraceful(`runtime tenant logs: ${result.reason}`);
|
|
73419
73516
|
if (o.json) {
|
|
@@ -74130,6 +74227,13 @@ ci.command("audit").description("read-only fleet scan: gate workflow, ruleset co
|
|
|
74130
74227
|
else console.log(renderCiAuditText(report));
|
|
74131
74228
|
if (!report.ok) process.exitCode = 1;
|
|
74132
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
|
+
});
|
|
74133
74237
|
registerCiChecksWait(ci);
|
|
74134
74238
|
registerCancelOrphans(ci, async () => (await mintCommandWriteToken(appActorDeps(), "ci cancel-orphans")).token);
|
|
74135
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/package.json
CHANGED