@mutmutco/cli 4.6.23 → 4.6.24
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 +1093 -426
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -13527,7 +13527,7 @@ function useColor() {
|
|
|
13527
13527
|
var program = new Command();
|
|
13528
13528
|
|
|
13529
13529
|
// src/command-composition.ts
|
|
13530
|
-
var
|
|
13530
|
+
var import_node_fs66 = require("node:fs");
|
|
13531
13531
|
init_clean_exit();
|
|
13532
13532
|
init_cli_shared();
|
|
13533
13533
|
|
|
@@ -15195,7 +15195,7 @@ function resolveVerbAliasShim(argv) {
|
|
|
15195
15195
|
|
|
15196
15196
|
// src/command-composition.ts
|
|
15197
15197
|
var import_node_os28 = require("node:os");
|
|
15198
|
-
var
|
|
15198
|
+
var import_node_path63 = require("node:path");
|
|
15199
15199
|
|
|
15200
15200
|
// src/board-read.ts
|
|
15201
15201
|
var import_node_fs18 = require("node:fs");
|
|
@@ -15362,12 +15362,19 @@ var APP_POLL_PERMISSIONS = {
|
|
|
15362
15362
|
var APP_JWT_BACKDATE_S = 60;
|
|
15363
15363
|
var APP_JWT_TTL_S = 9 * 60;
|
|
15364
15364
|
var AppActorError = class extends Error {
|
|
15365
|
-
constructor(message2, code = "mint-failed") {
|
|
15365
|
+
constructor(message2, code = "mint-failed", mintRefusal) {
|
|
15366
15366
|
super(message2);
|
|
15367
15367
|
this.code = code;
|
|
15368
|
+
this.mintRefusal = mintRefusal;
|
|
15368
15369
|
}
|
|
15369
15370
|
code;
|
|
15371
|
+
mintRefusal;
|
|
15370
15372
|
};
|
|
15373
|
+
function sanitizeMintRefusalReason(raw) {
|
|
15374
|
+
if (typeof raw !== "string") return void 0;
|
|
15375
|
+
const cleaned = raw.replace(/-----BEGIN [^-]+-----[\s\S]*?(?:-----END [^-]+-----|$)/g, "[redacted]").replace(/\bBearer\s+\S+/gi, "Bearer [redacted]").replace(/\beyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.?[A-Za-z0-9_-]*/g, "[redacted]").replace(/\b(?:gh[pousr]|github_pat)_[A-Za-z0-9_]+/g, "[redacted]").replace(/\s+/g, " ").trim();
|
|
15376
|
+
return cleaned === "" ? void 0 : cleaned.slice(0, 200);
|
|
15377
|
+
}
|
|
15371
15378
|
function commandPathOf(cmd) {
|
|
15372
15379
|
const parts = [];
|
|
15373
15380
|
let cursor = cmd;
|
|
@@ -15425,13 +15432,16 @@ var APP_WRITE_COMMANDS = /* @__PURE__ */ new Map([
|
|
|
15425
15432
|
["run rerun", { metadata: "read", actions: "write" }],
|
|
15426
15433
|
// #9780: repository Actions variables — get/list mint only Variables:read; create/update/delete
|
|
15427
15434
|
// Variables:write. Repository scope only; no secret store, no rename, no workflow dispatch.
|
|
15428
|
-
|
|
15429
|
-
["vars
|
|
15430
|
-
["vars
|
|
15431
|
-
["vars
|
|
15432
|
-
["vars
|
|
15435
|
+
// #9790: the token key is `actions_variables` (as ci-reconcile.yml mints); `variables` was refused 422.
|
|
15436
|
+
["vars get", { metadata: "read", actions_variables: "read" }],
|
|
15437
|
+
["vars list", { metadata: "read", actions_variables: "read" }],
|
|
15438
|
+
["vars create", { metadata: "read", actions_variables: "write" }],
|
|
15439
|
+
["vars update", { metadata: "read", actions_variables: "write" }],
|
|
15440
|
+
["vars delete", { metadata: "read", actions_variables: "write" }],
|
|
15433
15441
|
// #9779: the archive-status read is a metadata:read-only repository read — nothing broader.
|
|
15434
15442
|
["repo archive-status", { metadata: "read" }],
|
|
15443
|
+
// #9788: historical merge-queue lifecycle — one fixed PullRequest.timelineItems GraphQL query; read-only.
|
|
15444
|
+
["read merge-queue lifecycle", { metadata: "read", pull_requests: "read" }],
|
|
15435
15445
|
["issue comment", { metadata: "read", issues: "write", pull_requests: "write" }],
|
|
15436
15446
|
// #9614: closing an issue is an issue-state write — it rides the org App (mmi-github-app), so the close
|
|
15437
15447
|
// is attributed to the App instead of the personal account. Nothing broader than issues.
|
|
@@ -15574,7 +15584,17 @@ async function mintFromMember(deps, permissions, keys) {
|
|
|
15574
15584
|
throw new AppActorError(`app actor: token mint transport failed (${e?.constructor?.name ?? "Error"}) \u2014 automation fails closed, no personal-token fallback`);
|
|
15575
15585
|
}
|
|
15576
15586
|
if (!res.ok) {
|
|
15577
|
-
|
|
15587
|
+
let reason;
|
|
15588
|
+
try {
|
|
15589
|
+
reason = sanitizeMintRefusalReason((await res.json())?.message);
|
|
15590
|
+
} catch {
|
|
15591
|
+
reason = void 0;
|
|
15592
|
+
}
|
|
15593
|
+
throw new AppActorError(
|
|
15594
|
+
`app actor: installation token mint failed (HTTP ${res.status}${reason ? `: ${reason}` : ""}) \u2014 automation fails closed, no personal-token fallback`,
|
|
15595
|
+
"mint-failed",
|
|
15596
|
+
{ status: res.status, ...reason ? { reason } : {} }
|
|
15597
|
+
);
|
|
15578
15598
|
}
|
|
15579
15599
|
let body;
|
|
15580
15600
|
try {
|
|
@@ -28146,10 +28166,10 @@ var rollout_plan_default = {
|
|
|
28146
28166
|
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)."
|
|
28147
28167
|
},
|
|
28148
28168
|
baseline: {
|
|
28149
|
-
version: "4.6.
|
|
28150
|
-
tag: "v4.6.
|
|
28151
|
-
commit: "
|
|
28152
|
-
npm: "@mutmutco/cli@4.6.
|
|
28169
|
+
version: "4.6.24",
|
|
28170
|
+
tag: "v4.6.24",
|
|
28171
|
+
commit: "3632dffdf8a5",
|
|
28172
|
+
npm: "@mutmutco/cli@4.6.24"
|
|
28153
28173
|
},
|
|
28154
28174
|
exitCriterion: "fleet-n-of-n",
|
|
28155
28175
|
hubOnlyShortcut: "forbidden",
|
|
@@ -28166,14 +28186,14 @@ var rollout_plan_default = {
|
|
|
28166
28186
|
repo: "mutmutco/mmi-hub",
|
|
28167
28187
|
role: "canary",
|
|
28168
28188
|
schedule: "train",
|
|
28169
|
-
v3Target: "v4.6.
|
|
28189
|
+
v3Target: "v4.6.24"
|
|
28170
28190
|
}
|
|
28171
28191
|
],
|
|
28172
28192
|
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.",
|
|
28173
28193
|
rollback: {
|
|
28174
28194
|
independent: true,
|
|
28175
|
-
mechanism: "npm dist-tag latest -> 4.6.
|
|
28176
|
-
v3Target: "v4.6.
|
|
28195
|
+
mechanism: "npm dist-tag latest -> 4.6.24 and redeploy the Hub Lambda from tag v4.6.24 (3632dffdf8a5); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
28196
|
+
v3Target: "v4.6.24 (@mutmutco/cli@4.6.24, tag commit 3632dffdf8a5 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
28177
28197
|
}
|
|
28178
28198
|
},
|
|
28179
28199
|
{
|
|
@@ -44554,6 +44574,10 @@ function resolveCanonicalRepo(raw, projects) {
|
|
|
44554
44574
|
return { kind: "unregistered" };
|
|
44555
44575
|
}
|
|
44556
44576
|
function classifyUpstreamError(e) {
|
|
44577
|
+
if (e instanceof AppActorError) {
|
|
44578
|
+
const status = e.mintRefusal?.status ?? 0;
|
|
44579
|
+
return { status, reason: "mint-refused", mint: { status, reason: e.mintRefusal?.reason ?? e.message } };
|
|
44580
|
+
}
|
|
44557
44581
|
if (e instanceof GitHubApiError) {
|
|
44558
44582
|
if (e.status === 404) return { status: 404, reason: "bare-404" };
|
|
44559
44583
|
if (e.status === 403 || e.status === 401) return { status: e.status, reason: "denied" };
|
|
@@ -44571,7 +44595,7 @@ async function call(deps, log, method, path2, body) {
|
|
|
44571
44595
|
log.push({ method, path: path2, status: res.status });
|
|
44572
44596
|
return res;
|
|
44573
44597
|
} catch (e) {
|
|
44574
|
-
log.push({ method, path: path2, status: classifyUpstreamError(e).status });
|
|
44598
|
+
if (!(e instanceof AppActorError)) log.push({ method, path: path2, status: classifyUpstreamError(e).status });
|
|
44575
44599
|
throw e;
|
|
44576
44600
|
}
|
|
44577
44601
|
}
|
|
@@ -44589,7 +44613,7 @@ function receiptBase(deps, operation, repo, ctx, now) {
|
|
|
44589
44613
|
upstream: ctx.log
|
|
44590
44614
|
};
|
|
44591
44615
|
}
|
|
44592
|
-
var VARS_PER_PAGE =
|
|
44616
|
+
var VARS_PER_PAGE = 30;
|
|
44593
44617
|
var VARS_MAX_PAGES = 100;
|
|
44594
44618
|
function parseVariableEntry(raw) {
|
|
44595
44619
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("variable entry is not an object");
|
|
@@ -44637,6 +44661,12 @@ async function enumerateRepositoryVariables(deps, repo, log = []) {
|
|
|
44637
44661
|
}
|
|
44638
44662
|
return { complete: true, entries, totalVariables: total, pages: Math.max(1, Math.ceil(total / VARS_PER_PAGE)) };
|
|
44639
44663
|
}
|
|
44664
|
+
function mintCapability(c, permission) {
|
|
44665
|
+
return c.mint ? { permission, outcome: "mint-refused", status: c.mint.status, reason: c.mint.reason } : void 0;
|
|
44666
|
+
}
|
|
44667
|
+
function mintDetail(c) {
|
|
44668
|
+
return `App token mint refused${c.mint?.status ? ` (HTTP ${c.mint.status})` : ""}: ${c.mint?.reason ?? "unknown"}`;
|
|
44669
|
+
}
|
|
44640
44670
|
function findEntry(entries, name) {
|
|
44641
44671
|
const lower = name.toLowerCase();
|
|
44642
44672
|
return entries.find((e) => e.name.toLowerCase() === lower);
|
|
@@ -44646,7 +44676,9 @@ async function readVariable(deps, repo, name, log) {
|
|
|
44646
44676
|
try {
|
|
44647
44677
|
({ body } = await call(deps, log, "GET", `repos/${repo}/actions/variables/${name}`));
|
|
44648
44678
|
} catch (e) {
|
|
44649
|
-
const
|
|
44679
|
+
const classified = classifyUpstreamError(e);
|
|
44680
|
+
const { status, reason } = classified;
|
|
44681
|
+
if (reason === "mint-refused") return { presence: "unknown", unknownReason: "mint-refused", detail: mintDetail(classified), capability: mintCapability(classified, deps.mintPermission ?? "variables:read") };
|
|
44650
44682
|
if (reason === "denied") return { presence: "unknown", unknownReason: "denied", detail: `HTTP ${status}: the App installation cannot read repository variables`, capability: { permission: "variables:read", outcome: "denied" } };
|
|
44651
44683
|
if (reason !== "bare-404") return { presence: "unknown", unknownReason: reason, detail: String(e.message).slice(0, 200) };
|
|
44652
44684
|
try {
|
|
@@ -44657,6 +44689,7 @@ async function readVariable(deps, repo, name, log) {
|
|
|
44657
44689
|
return { presence: "absent", absenceProof: { provenBy: "complete-enumeration", pages: en.pages, perPage: VARS_PER_PAGE, totalVariables: en.totalVariables, nameExcluded: true } };
|
|
44658
44690
|
} catch (proofError) {
|
|
44659
44691
|
const proof = classifyUpstreamError(proofError);
|
|
44692
|
+
if (proof.reason === "mint-refused") return { presence: "unknown", unknownReason: "mint-refused", detail: `bare 404 on GET, and the absence proof failed: ${mintDetail(proof)}`, capability: mintCapability(proof, deps.mintPermission ?? "variables:read") };
|
|
44660
44693
|
return { presence: "unknown", unknownReason: proof.reason === "bare-404" ? "transport-error" : proof.reason, detail: `bare 404 on GET, and the absence proof failed: ${String(proofError.message).slice(0, 160)}` };
|
|
44661
44694
|
}
|
|
44662
44695
|
}
|
|
@@ -44701,12 +44734,14 @@ async function listVariablesReceipt(deps, repo) {
|
|
|
44701
44734
|
unknownReason: en.reason
|
|
44702
44735
|
};
|
|
44703
44736
|
} catch (e) {
|
|
44704
|
-
const
|
|
44737
|
+
const classified = classifyUpstreamError(e);
|
|
44738
|
+
const { status, reason } = classified;
|
|
44705
44739
|
return {
|
|
44706
44740
|
...receiptBase(deps, "list", repo, ctx, now),
|
|
44707
44741
|
pagination: { pages: 0, perPage: VARS_PER_PAGE, complete: false, reason },
|
|
44708
44742
|
variables: [],
|
|
44709
44743
|
unknownReason: reason,
|
|
44744
|
+
...reason === "mint-refused" ? { capability: mintCapability(classified, deps.mintPermission ?? "variables:read"), pagination: { pages: 0, perPage: VARS_PER_PAGE, complete: false, reason, detail: mintDetail(classified) } } : {},
|
|
44710
44745
|
...reason === "denied" ? { capability: { permission: "variables:read", outcome: "denied" }, pagination: { pages: 0, perPage: VARS_PER_PAGE, complete: false, reason, detail: `HTTP ${status}: the App installation cannot list repository variables` } } : {}
|
|
44711
44746
|
};
|
|
44712
44747
|
}
|
|
@@ -44744,7 +44779,8 @@ async function createVariableReceipt(deps, repo, name, value) {
|
|
|
44744
44779
|
try {
|
|
44745
44780
|
en = await enumerateRepositoryVariables(deps, repo, ctx.log);
|
|
44746
44781
|
} catch (e) {
|
|
44747
|
-
const
|
|
44782
|
+
const classified = classifyUpstreamError(e);
|
|
44783
|
+
const { status, reason } = classified;
|
|
44748
44784
|
return {
|
|
44749
44785
|
...receiptBase(deps, "create", repo, ctx, now),
|
|
44750
44786
|
operation: "create",
|
|
@@ -44752,8 +44788,9 @@ async function createVariableReceipt(deps, repo, name, value) {
|
|
|
44752
44788
|
outcome: "not-dispatched",
|
|
44753
44789
|
casLimitation: "no-atomic-compare-and-swap",
|
|
44754
44790
|
baseline: { presence: "unknown", source: "complete-enumeration" },
|
|
44755
|
-
reason: `no observed absent baseline: ${reason === "denied" ? `HTTP ${status}: the App installation cannot list repository variables` : String(e.message).slice(0, 160)}`,
|
|
44756
|
-
...reason === "denied" ? { capability: { permission: "variables:read", outcome: "denied" } } : {}
|
|
44791
|
+
reason: `no observed absent baseline: ${reason === "denied" ? `HTTP ${status}: the App installation cannot list repository variables` : reason === "mint-refused" ? mintDetail(classified) : String(e.message).slice(0, 160)}`,
|
|
44792
|
+
...reason === "denied" ? { capability: { permission: "variables:read", outcome: "denied" } } : {},
|
|
44793
|
+
...reason === "mint-refused" ? { capability: mintCapability(classified, deps.mintPermission ?? "variables:write") } : {}
|
|
44757
44794
|
};
|
|
44758
44795
|
}
|
|
44759
44796
|
const base = { ...receiptBase(deps, "create", repo, ctx, now), operation: "create", name, casLimitation: "no-atomic-compare-and-swap" };
|
|
@@ -44767,8 +44804,10 @@ async function createVariableReceipt(deps, repo, name, value) {
|
|
|
44767
44804
|
try {
|
|
44768
44805
|
await call(deps, ctx.log, "POST", `repos/${repo}/actions/variables`, { name, value });
|
|
44769
44806
|
} catch (e) {
|
|
44770
|
-
const
|
|
44807
|
+
const classified = classifyUpstreamError(e);
|
|
44808
|
+
const { status, reason } = classified;
|
|
44771
44809
|
const absentBaseline = { presence: "absent", source: "complete-enumeration" };
|
|
44810
|
+
if (reason === "mint-refused") return { ...base, outcome: "not-dispatched", baseline: absentBaseline, reason: mintDetail(classified), capability: mintCapability(classified, deps.mintPermission ?? "variables:write") };
|
|
44772
44811
|
if (reason === "denied") return { ...base, outcome: "rejected", baseline: absentBaseline, reason: `HTTP ${status}: the App installation lacks repository Variables write`, capability: { permission: "variables:write", outcome: "denied" } };
|
|
44773
44812
|
if (status === 404) return { ...base, outcome: "rejected", baseline: absentBaseline, reason: "HTTP 404: repository missing or not visible to the installation" };
|
|
44774
44813
|
if (status === 409 || status === 422) return { ...base, outcome: "rejected", baseline: absentBaseline, reason: `HTTP ${status}: ${String(e.message).slice(0, 160)}` };
|
|
@@ -44819,7 +44858,9 @@ async function updateVariableReceipt(deps, repo, name, value) {
|
|
|
44819
44858
|
try {
|
|
44820
44859
|
await call(deps, ctx.log, "PATCH", `repos/${repo}/actions/variables/${name}`, { value });
|
|
44821
44860
|
} catch (e) {
|
|
44822
|
-
const
|
|
44861
|
+
const classified = classifyUpstreamError(e);
|
|
44862
|
+
const { status, reason } = classified;
|
|
44863
|
+
if (reason === "mint-refused") return { ...base, outcome: "not-dispatched", baseline: { presence: "present", value: baseline.value, source: "get" }, reason: mintDetail(classified), capability: mintCapability(classified, deps.mintPermission ?? "variables:write") };
|
|
44823
44864
|
if (reason === "denied") return rejectedWrite(base, { presence: "present", value: baseline.value }, `HTTP ${status}: the App installation lacks repository Variables write`, { permission: "variables:write", outcome: "denied" });
|
|
44824
44865
|
if (status === 404) return rejectedWrite(base, { presence: "present", value: baseline.value }, "HTTP 404 at write: the variable vanished after the baseline check (concurrent deletion suspected) \u2014 reconcile read-only, never create");
|
|
44825
44866
|
if (status === 409 || status === 422) return rejectedWrite(base, { presence: "present", value: baseline.value }, `HTTP ${status}: ${String(e.message).slice(0, 160)}`);
|
|
@@ -44868,7 +44909,9 @@ async function deleteVariableReceipt(deps, repo, name) {
|
|
|
44868
44909
|
try {
|
|
44869
44910
|
await call(deps, ctx.log, "DELETE", `repos/${repo}/actions/variables/${name}`);
|
|
44870
44911
|
} catch (e) {
|
|
44871
|
-
const
|
|
44912
|
+
const classified = classifyUpstreamError(e);
|
|
44913
|
+
const { status, reason } = classified;
|
|
44914
|
+
if (reason === "mint-refused") return { ...base, outcome: "not-dispatched", baseline: { presence: "present", value: baseline.value, source: "get" }, reason: mintDetail(classified), capability: mintCapability(classified, deps.mintPermission ?? "variables:write") };
|
|
44872
44915
|
if (reason === "denied") return rejectedWrite(base, { presence: "present", value: baseline.value }, `HTTP ${status}: the App installation lacks repository Variables write`, { permission: "variables:write", outcome: "denied" });
|
|
44873
44916
|
if (status === 404) return rejectedWrite(base, { presence: "present", value: baseline.value }, "HTTP 404 at write: the variable vanished after the baseline check (concurrent deletion suspected) \u2014 reconcile read-only");
|
|
44874
44917
|
if (status === 409 || status === 422) return rejectedWrite(base, { presence: "present", value: baseline.value }, `HTTP ${status}: ${String(e.message).slice(0, 160)}`);
|
|
@@ -44940,7 +44983,7 @@ function registerDevopsVarsCommands(program3, deps) {
|
|
|
44940
44983
|
}
|
|
44941
44984
|
const projects = await deps.projects();
|
|
44942
44985
|
const repo = resolveRepoOrFail(repoArg, projects, verb);
|
|
44943
|
-
const ops = { transport: await deps.appClient(commandPath4), ...deps.actor ? { actor: deps.actor() } : {} };
|
|
44986
|
+
const ops = { transport: await deps.appClient(commandPath4), mintPermission: "variables:write", ...deps.actor ? { actor: deps.actor() } : {} };
|
|
44944
44987
|
const receipt = verb === "create" ? await createVariableReceipt(ops, repo, name, exactValue) : verb === "update" ? await updateVariableReceipt(ops, repo, name, exactValue) : await deleteVariableReceipt(ops, repo, name);
|
|
44945
44988
|
if (o.json) console.log(JSON.stringify(receipt, null, 2));
|
|
44946
44989
|
else printMutationText(receipt);
|
|
@@ -44960,14 +45003,14 @@ function printReadText(receipt) {
|
|
|
44960
45003
|
const p = receipt.absenceProof;
|
|
44961
45004
|
console.log(`vars get: ${receipt.repo} ${receipt.name} absent \u2014 proven by complete enumeration (${p.pages} page(s), ${p.totalVariables} variable(s), name excluded)`);
|
|
44962
45005
|
} else {
|
|
44963
|
-
console.log(`vars get: ${receipt.repo} ${receipt.name} presence UNKNOWN (${receipt.unknownReason}${receipt.capability ?
|
|
45006
|
+
console.log(`vars get: ${receipt.repo} ${receipt.name} presence UNKNOWN (${receipt.unknownReason}${receipt.capability ? `, capability ${receipt.capability.outcome}` : ""}) \u2014 absence is never claimed from this read`);
|
|
44964
45007
|
}
|
|
44965
45008
|
return;
|
|
44966
45009
|
}
|
|
44967
45010
|
if (receipt.pagination.complete) {
|
|
44968
45011
|
console.log(`vars list: ${receipt.repo} \u2014 ${receipt.pagination.totalVariables} variable(s), complete (${receipt.pagination.pages} page(s)): ${receipt.variables.map((v) => v.name).join(", ") || "(none)"}`);
|
|
44969
45012
|
} else {
|
|
44970
|
-
console.log(`vars list: ${receipt.repo} \u2014 INCOMPLETE enumeration (${receipt.pagination.reason ?? "unknown"}); the names below are NOT the full set: ${receipt.variables.map((v) => v.name).join(", ") || "(none)"}`);
|
|
45013
|
+
console.log(`vars list: ${receipt.repo} \u2014 INCOMPLETE enumeration (${receipt.pagination.reason ?? "unknown"}${receipt.pagination.detail ? `: ${receipt.pagination.detail}` : ""}); the names below are NOT the full set: ${receipt.variables.map((v) => v.name).join(", ") || "(none)"}`);
|
|
44971
45014
|
}
|
|
44972
45015
|
}
|
|
44973
45016
|
function printMutationText(receipt) {
|
|
@@ -44988,12 +45031,17 @@ async function call2(deps, log, method, path2) {
|
|
|
44988
45031
|
log.push({ method, path: path2, status: res.status });
|
|
44989
45032
|
return res;
|
|
44990
45033
|
} catch (e) {
|
|
45034
|
+
if (e instanceof AppActorError) throw e;
|
|
44991
45035
|
const status = e instanceof GitHubApiError ? e.status : 0;
|
|
44992
45036
|
log.push({ method, path: path2, status });
|
|
44993
45037
|
throw e;
|
|
44994
45038
|
}
|
|
44995
45039
|
}
|
|
44996
45040
|
function classify2(e) {
|
|
45041
|
+
if (e instanceof AppActorError) {
|
|
45042
|
+
const status = e.mintRefusal?.status ?? 0;
|
|
45043
|
+
return { status, reason: "mint-refused", mint: { status, reason: e.mintRefusal?.reason ?? e.message } };
|
|
45044
|
+
}
|
|
44997
45045
|
if (e instanceof GitHubApiError) {
|
|
44998
45046
|
if (e.status === 404) return { status: 404, reason: "missing-or-invisible" };
|
|
44999
45047
|
if (e.status === 403 || e.status === 401) return { status: e.status, reason: "denied" };
|
|
@@ -45039,12 +45087,13 @@ async function readArchiveStatusReceipt(deps, repo) {
|
|
|
45039
45087
|
window: { ...base.window, endedAt: now().toISOString() }
|
|
45040
45088
|
};
|
|
45041
45089
|
} catch (e) {
|
|
45042
|
-
const {
|
|
45090
|
+
const { reason, mint } = classify2(e);
|
|
45043
45091
|
return {
|
|
45044
45092
|
...base,
|
|
45045
45093
|
archived: "unknown",
|
|
45046
45094
|
unknownReason: reason,
|
|
45047
45095
|
...reason === "denied" ? { capability: { permission: "metadata:read", outcome: "denied" } } : {},
|
|
45096
|
+
...mint ? { capability: { permission: "metadata:read", outcome: "mint-refused", status: mint.status, reason: mint.reason } } : {},
|
|
45048
45097
|
...reason === "missing-or-invisible" ? { sourceTime: { ...base.sourceTime, observedAt: now().toISOString(), note: `${ARCHIVE_SOURCE_TIME_NOTE} \u2014 HTTP 404: the repository is missing or not visible to the installation, which is NOT archived:false` } } : { sourceTime: { ...base.sourceTime, observedAt: now().toISOString() } },
|
|
45049
45098
|
window: { ...base.window, endedAt: now().toISOString() }
|
|
45050
45099
|
};
|
|
@@ -45058,12 +45107,618 @@ function registerRepoArchiveStatusCommand(program3, deps) {
|
|
|
45058
45107
|
const repo = resolution.kind === "resolved" ? resolution.repo : resolution.kind === "conflict" ? fail(`repo archive-status: '${o.repo ?? ""}' resolves to MULTIPLE registered repositories (${resolution.candidates.join(", ")}) \u2014 pass the exact owner/repo (#9779)`) : resolution.kind === "unavailable" ? fail("repo archive-status: the Hub project registry is unreadable; refusing to address a repo this command cannot prove registered") : fail(`repo archive-status: '${o.repo ?? ""}' is not a registered repository in the Hub registry \u2014 pass the exact owner/repo`);
|
|
45059
45108
|
const receipt = await readArchiveStatusReceipt({ transport: await deps.appClient("repo archive-status"), ...deps.actor ? { actor: deps.actor() } : {} }, repo);
|
|
45060
45109
|
if (o.json) console.log(JSON.stringify(receipt, null, 2));
|
|
45061
|
-
else if (receipt.archived === "unknown") console.log(`repo archive-status: ${receipt.repo} \u2014 archived state UNKNOWN (${receipt.unknownReason}) \u2014 never reads as not-archived`);
|
|
45110
|
+
else if (receipt.archived === "unknown") console.log(`repo archive-status: ${receipt.repo} \u2014 archived state UNKNOWN (${receipt.unknownReason}${receipt.capability?.outcome === "mint-refused" ? `: App token mint refused${receipt.capability.status ? ` (HTTP ${receipt.capability.status})` : ""}: ${receipt.capability.reason}` : ""}) \u2014 never reads as not-archived`);
|
|
45062
45111
|
else console.log(`repo archive-status: ${receipt.repo} \u2014 archived: ${receipt.archived} (observed ${receipt.sourceTime.observedAt}${receipt.sourceTime.providerUpdatedAt ? `, provider updated_at ${receipt.sourceTime.providerUpdatedAt}` : ""})`);
|
|
45063
45112
|
if (receipt.archived === "unknown") process.exitCode = 1;
|
|
45064
45113
|
});
|
|
45065
45114
|
}
|
|
45066
45115
|
|
|
45116
|
+
// src/merge-queue-lifecycle.ts
|
|
45117
|
+
var import_node_crypto15 = require("node:crypto");
|
|
45118
|
+
var import_node_fs35 = require("node:fs");
|
|
45119
|
+
var import_node_path32 = require("node:path");
|
|
45120
|
+
init_github_client();
|
|
45121
|
+
init_cli_shared();
|
|
45122
|
+
var LIFECYCLE_SCHEMA = "mmi-cli.merge-queue-lifecycle/1";
|
|
45123
|
+
var COHORT_SCHEMA = "mmi.merge-queue-cohort/1";
|
|
45124
|
+
var LIFECYCLE_SOURCE = {
|
|
45125
|
+
provider: "github-graphql",
|
|
45126
|
+
operation: "PullRequest.timelineItems",
|
|
45127
|
+
itemTypes: ["ADDED_TO_MERGE_QUEUE_EVENT", "REMOVED_FROM_MERGE_QUEUE_EVENT", "MERGED_EVENT", "HEAD_REF_FORCE_PUSHED_EVENT", "BASE_REF_CHANGED_EVENT"],
|
|
45128
|
+
schemaRef: "https://docs.github.com/public/fpt/schema.docs.graphql",
|
|
45129
|
+
version: "merge-queue-lifecycle-source/1",
|
|
45130
|
+
permission: "pull_requests:read"
|
|
45131
|
+
};
|
|
45132
|
+
var LIFECYCLE_QUERY = "query($owner:String!,$name:String!,$number:Int!,$after:String){repository(owner:$owner,name:$name){pullRequest(number:$number){number baseRefName mergedAt mergeCommit{oid} timelineItems(first:100,after:$after,itemTypes:[ADDED_TO_MERGE_QUEUE_EVENT,REMOVED_FROM_MERGE_QUEUE_EVENT,MERGED_EVENT,HEAD_REF_FORCE_PUSHED_EVENT,BASE_REF_CHANGED_EVENT]){pageInfo{hasNextPage endCursor} nodes{__typename ... on AddedToMergeQueueEvent{id createdAt} ... on RemovedFromMergeQueueEvent{id createdAt reason beforeCommit{oid}} ... on MergedEvent{id createdAt commit{oid}} ... on HeadRefForcePushedEvent{id createdAt beforeCommit{oid} afterCommit{oid}} ... on BaseRefChangedEvent{id createdAt previousRefName currentRefName}}}}}}";
|
|
45133
|
+
var UTC_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/;
|
|
45134
|
+
var SHA_RE2 = /^[0-9a-f]{40}$/;
|
|
45135
|
+
function parseUtc(raw, flag) {
|
|
45136
|
+
if (!UTC_RE.test(raw) || Number.isNaN(Date.parse(raw))) throw new Error(`${flag} must be a UTC timestamp like 2026-11-02T00:00:00Z \u2014 received '${raw.slice(0, 40)}'`);
|
|
45137
|
+
return raw;
|
|
45138
|
+
}
|
|
45139
|
+
function parseBound(raw, flag, max) {
|
|
45140
|
+
if (!/^\d+$/.test(raw) || Number(raw) < 1 || Number(raw) > max) throw new Error(`${flag} must be an integer 1-${max} \u2014 received '${raw.slice(0, 20)}'`);
|
|
45141
|
+
return Number(raw);
|
|
45142
|
+
}
|
|
45143
|
+
function optionalUtc(v, where) {
|
|
45144
|
+
if (v === void 0) return void 0;
|
|
45145
|
+
if (typeof v !== "string") throw new Error(`${where} must be a UTC timestamp string`);
|
|
45146
|
+
return parseUtc(v, where);
|
|
45147
|
+
}
|
|
45148
|
+
function parseCohortManifest(text) {
|
|
45149
|
+
let raw;
|
|
45150
|
+
try {
|
|
45151
|
+
raw = JSON.parse(text);
|
|
45152
|
+
} catch {
|
|
45153
|
+
throw new Error("cohort manifest is not valid JSON");
|
|
45154
|
+
}
|
|
45155
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("cohort manifest must be a JSON object");
|
|
45156
|
+
const m = raw;
|
|
45157
|
+
if (m.schema !== COHORT_SCHEMA) throw new Error(`cohort manifest schema must be '${COHORT_SCHEMA}'`);
|
|
45158
|
+
if (typeof m.repo !== "string" || !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(m.repo)) throw new Error("cohort manifest repo must be owner/repo");
|
|
45159
|
+
if (typeof m.base_branch !== "string" || m.base_branch.trim() === "") throw new Error("cohort manifest base_branch is required");
|
|
45160
|
+
const receipt = m.measurement_receipt;
|
|
45161
|
+
if (!receipt || typeof receipt.id !== "string" || receipt.id.trim() === "" || typeof receipt.sha256 !== "string" || !/^[0-9a-f]{64}$/.test(receipt.sha256)) {
|
|
45162
|
+
throw new Error("cohort manifest measurement_receipt needs an id and a 64-hex sha256 binding the existing measurement");
|
|
45163
|
+
}
|
|
45164
|
+
const since = parseUtc(String(m.since ?? ""), "manifest since");
|
|
45165
|
+
const until = parseUtc(String(m.until ?? ""), "manifest until");
|
|
45166
|
+
if (!Array.isArray(m.prs) || m.prs.length === 0) throw new Error("cohort manifest prs must list explicit PR identities");
|
|
45167
|
+
const seen = /* @__PURE__ */ new Set();
|
|
45168
|
+
const prs = m.prs.map((p, i) => {
|
|
45169
|
+
const row = p;
|
|
45170
|
+
if (!row || !Number.isInteger(row.number) || row.number < 1) throw new Error(`cohort manifest prs[${i}].number must be a positive integer`);
|
|
45171
|
+
const number = row.number;
|
|
45172
|
+
if (seen.has(number)) throw new Error(`cohort manifest lists PR #${number} twice`);
|
|
45173
|
+
seen.add(number);
|
|
45174
|
+
if (row.excluded !== void 0 && (typeof row.excluded !== "string" || row.excluded.trim() === "")) throw new Error(`cohort manifest prs[${i}].excluded must be a reason string`);
|
|
45175
|
+
return { number, ...typeof row.excluded === "string" ? { excluded: row.excluded } : {} };
|
|
45176
|
+
});
|
|
45177
|
+
const runs2 = m.runs === void 0 ? [] : m.runs;
|
|
45178
|
+
if (!Array.isArray(runs2)) throw new Error("cohort manifest runs must be an array");
|
|
45179
|
+
const parsedRuns = runs2.map((r, i) => {
|
|
45180
|
+
const row = r;
|
|
45181
|
+
const where = `cohort manifest runs[${i}]`;
|
|
45182
|
+
if (!row || !Number.isInteger(row.run_id) || !Number.isInteger(row.run_attempt) || row.run_attempt < 1) throw new Error(`${where} needs integer run_id and run_attempt >= 1`);
|
|
45183
|
+
if (typeof row.head_sha !== "string" || !SHA_RE2.test(row.head_sha)) throw new Error(`${where}.head_sha must be a full 40-hex SHA`);
|
|
45184
|
+
if (!Array.isArray(row.members)) throw new Error(`${where}.members must list explicit PR memberships`);
|
|
45185
|
+
const members = row.members.map((mem, j) => {
|
|
45186
|
+
const mm = mem;
|
|
45187
|
+
if (!mm || !Number.isInteger(mm.pr)) throw new Error(`${where}.members[${j}].pr must be an integer`);
|
|
45188
|
+
if (mm.admission_event_id !== void 0 && (typeof mm.admission_event_id !== "string" || mm.admission_event_id === "")) throw new Error(`${where}.members[${j}].admission_event_id must be a provider event id`);
|
|
45189
|
+
return { pr: mm.pr, ...typeof mm.admission_event_id === "string" ? { admission_event_id: mm.admission_event_id } : {} };
|
|
45190
|
+
});
|
|
45191
|
+
const started = optionalUtc(row.started_at, `${where}.started_at`);
|
|
45192
|
+
const failureResult = optionalUtc(row.failure_result_at, `${where}.failure_result_at`);
|
|
45193
|
+
const failureObserved = optionalUtc(row.failure_observed_at, `${where}.failure_observed_at`);
|
|
45194
|
+
return {
|
|
45195
|
+
run_id: row.run_id,
|
|
45196
|
+
run_attempt: row.run_attempt,
|
|
45197
|
+
head_sha: row.head_sha,
|
|
45198
|
+
members,
|
|
45199
|
+
...started ? { started_at: started } : {},
|
|
45200
|
+
...failureResult ? { failure_result_at: failureResult } : {},
|
|
45201
|
+
...failureObserved ? { failure_observed_at: failureObserved } : {}
|
|
45202
|
+
};
|
|
45203
|
+
});
|
|
45204
|
+
return { schema: COHORT_SCHEMA, repo: m.repo, base_branch: m.base_branch, measurement_receipt: { id: receipt.id, sha256: receipt.sha256 }, since, until, prs, runs: parsedRuns };
|
|
45205
|
+
}
|
|
45206
|
+
var oidOf = (v) => v && typeof v === "object" && typeof v.oid === "string" ? v.oid : null;
|
|
45207
|
+
function normalizeNodes(nodes, malformed) {
|
|
45208
|
+
const out = [];
|
|
45209
|
+
for (const node of nodes) {
|
|
45210
|
+
const n = node;
|
|
45211
|
+
if (!n || typeof n.id !== "string" || typeof n.createdAt !== "string") {
|
|
45212
|
+
malformed.push("event-missing-id-or-createdAt");
|
|
45213
|
+
continue;
|
|
45214
|
+
}
|
|
45215
|
+
const base = { id: n.id, at: n.createdAt };
|
|
45216
|
+
switch (n.__typename) {
|
|
45217
|
+
case "AddedToMergeQueueEvent":
|
|
45218
|
+
out.push({ kind: "enqueued", ...base });
|
|
45219
|
+
break;
|
|
45220
|
+
case "RemovedFromMergeQueueEvent":
|
|
45221
|
+
out.push({ kind: "removed", ...base, reason: typeof n.reason === "string" && n.reason !== "" ? n.reason : null, before_head: oidOf(n.beforeCommit) });
|
|
45222
|
+
break;
|
|
45223
|
+
case "MergedEvent":
|
|
45224
|
+
out.push({ kind: "merged", ...base, merge_sha: oidOf(n.commit) });
|
|
45225
|
+
break;
|
|
45226
|
+
case "HeadRefForcePushedEvent":
|
|
45227
|
+
out.push({ kind: "head_changed", ...base, before: oidOf(n.beforeCommit), after: oidOf(n.afterCommit) });
|
|
45228
|
+
break;
|
|
45229
|
+
case "BaseRefChangedEvent":
|
|
45230
|
+
if (typeof n.previousRefName !== "string" || typeof n.currentRefName !== "string") {
|
|
45231
|
+
malformed.push("base-change-missing-refs");
|
|
45232
|
+
break;
|
|
45233
|
+
}
|
|
45234
|
+
out.push({ kind: "base_changed", ...base, from: n.previousRefName, to: n.currentRefName });
|
|
45235
|
+
break;
|
|
45236
|
+
default:
|
|
45237
|
+
malformed.push(`unexpected-typename:${String(n.__typename).slice(0, 40)}`);
|
|
45238
|
+
}
|
|
45239
|
+
}
|
|
45240
|
+
return out;
|
|
45241
|
+
}
|
|
45242
|
+
function classifyError(e) {
|
|
45243
|
+
if (e instanceof GitHubApiError) {
|
|
45244
|
+
if (e.rateLimited) return { code: "rate-limited", status: e.status };
|
|
45245
|
+
const types = (e.graphqlErrors ?? []).map((g) => g.type ?? "");
|
|
45246
|
+
if (e.status === 401 || e.status === 403 || types.includes("FORBIDDEN")) return { code: "permission-denied", status: e.status };
|
|
45247
|
+
if (e.status === 404 || types.includes("NOT_FOUND")) return { code: "not-found-or-masked", status: e.status };
|
|
45248
|
+
if (types.some((t) => t !== "")) return { code: "unsupported-source", status: e.status };
|
|
45249
|
+
if (/did not include data/i.test(e.message)) return { code: "malformed-response", status: e.status };
|
|
45250
|
+
return { code: "transport-error", status: e.status };
|
|
45251
|
+
}
|
|
45252
|
+
const name = e?.name ?? "";
|
|
45253
|
+
if (name === "TimeoutError" || name === "AbortError" || /timeout|timed out|abort/i.test(String(e?.message ?? ""))) return { code: "timeout", status: 0 };
|
|
45254
|
+
return { code: "transport-error", status: 0 };
|
|
45255
|
+
}
|
|
45256
|
+
function classifyRemovalReason(reason, headChangedBeforeReentry) {
|
|
45257
|
+
if (headChangedBeforeReentry) return "head-change";
|
|
45258
|
+
if (reason === null) return "unknown";
|
|
45259
|
+
if (/manual|dequeued by|removed by|user/i.test(reason)) return "manual";
|
|
45260
|
+
if (/fail|timed? ?out|timeout|check|status/i.test(reason)) return "failure-linked";
|
|
45261
|
+
if (/conflict|protection|invalid|base|push|commit|head/i.test(reason)) return "invalidation";
|
|
45262
|
+
return "unknown";
|
|
45263
|
+
}
|
|
45264
|
+
var secondsBetween = (from, to) => {
|
|
45265
|
+
const d = (Date.parse(to) - Date.parse(from)) / 1e3;
|
|
45266
|
+
if (!Number.isFinite(d)) return { value: null, reason: "unparseable-timestamp" };
|
|
45267
|
+
if (d < 0) return { value: null, reason: "negative-duration" };
|
|
45268
|
+
return { value: d };
|
|
45269
|
+
};
|
|
45270
|
+
var ZERO_BOUNCES = () => ({ manual: 0, "failure-linked": 0, "head-change": 0, invalidation: 0, unknown: 0 });
|
|
45271
|
+
function buildPrLifecycle(state, manifest, joins) {
|
|
45272
|
+
const entry = manifest.prs.find((p) => p.number === state.number);
|
|
45273
|
+
const anomalies = [...state.malformed ?? []];
|
|
45274
|
+
const events = state.events.map((e, i) => ({ e, i })).filter(({ e }) => {
|
|
45275
|
+
if (UTC_RE.test(e.at) && !Number.isNaN(Date.parse(e.at))) return true;
|
|
45276
|
+
anomalies.push(`malformed-timestamp:${e.id}`);
|
|
45277
|
+
return false;
|
|
45278
|
+
});
|
|
45279
|
+
for (let k = 1; k < events.length; k++) if (Date.parse(events[k].e.at) < Date.parse(events[k - 1].e.at)) anomalies.push(`out-of-order:${events[k].e.id}`);
|
|
45280
|
+
const ordered = events.sort((a, b) => Date.parse(a.e.at) - Date.parse(b.e.at) || a.i - b.i).map(({ e }) => e);
|
|
45281
|
+
const prBase = state.pr?.base_ref ?? null;
|
|
45282
|
+
const baseChanges = ordered.filter((e) => e.kind === "base_changed");
|
|
45283
|
+
const baseAt = (at) => {
|
|
45284
|
+
const next = baseChanges.find((c) => Date.parse(c.at) > Date.parse(at));
|
|
45285
|
+
if (next) return { value: next.from, reason: "derived-from-base-ref-changed-event" };
|
|
45286
|
+
return prBase === null ? { value: null, reason: "base-unavailable" } : { value: prBase, reason: baseChanges.length ? "derived-from-base-ref-changed-event" : "current-base-no-recorded-change" };
|
|
45287
|
+
};
|
|
45288
|
+
const episodes = [];
|
|
45289
|
+
let open2 = null;
|
|
45290
|
+
let leftCensored = false;
|
|
45291
|
+
let sawQueueEvent = false;
|
|
45292
|
+
let mergedEvent = null;
|
|
45293
|
+
for (const e of ordered) {
|
|
45294
|
+
if (e.kind === "enqueued") {
|
|
45295
|
+
if (open2) {
|
|
45296
|
+
anomalies.push(`admission-without-removal:${e.id}`);
|
|
45297
|
+
episodes.push(open2);
|
|
45298
|
+
}
|
|
45299
|
+
const prev = open2 ?? episodes[episodes.length - 1] ?? null;
|
|
45300
|
+
const headChanged = prev ? ordered.some((h) => h.kind === "head_changed" && Date.parse(h.at) >= Date.parse(prev.terminal.at ?? prev.enqueued_at) && Date.parse(h.at) <= Date.parse(e.at)) : null;
|
|
45301
|
+
open2 = {
|
|
45302
|
+
episode_id: e.id,
|
|
45303
|
+
enqueued_at: e.at,
|
|
45304
|
+
head_sha: { value: null, reason: "source-omits-admission-head (AddedToMergeQueueEvent has no commit field)" },
|
|
45305
|
+
base: baseAt(e.at),
|
|
45306
|
+
terminal: { type: null, at: null, event_id: null, reason: null, reason_class: null, head_at_removal: null },
|
|
45307
|
+
reentry_of: prev && prev.terminal.type === "removed" ? prev.episode_id : null,
|
|
45308
|
+
head_changed_since_previous_episode: headChanged,
|
|
45309
|
+
runs: [],
|
|
45310
|
+
queue_wait_to_execution_seconds: { value: null, reason: "no-explicit-run-join" }
|
|
45311
|
+
};
|
|
45312
|
+
sawQueueEvent = true;
|
|
45313
|
+
} else if (e.kind === "removed") {
|
|
45314
|
+
if (!open2) {
|
|
45315
|
+
if (!sawQueueEvent) leftCensored = true;
|
|
45316
|
+
anomalies.push(`removal-without-admission:${e.id}`);
|
|
45317
|
+
sawQueueEvent = true;
|
|
45318
|
+
continue;
|
|
45319
|
+
}
|
|
45320
|
+
open2.terminal = { type: "removed", at: e.at, event_id: e.id, reason: e.reason, reason_class: null, head_at_removal: e.before_head };
|
|
45321
|
+
episodes.push(open2);
|
|
45322
|
+
open2 = null;
|
|
45323
|
+
} else if (e.kind === "merged") {
|
|
45324
|
+
mergedEvent = e;
|
|
45325
|
+
if (open2) {
|
|
45326
|
+
open2.terminal = { type: "merged", at: e.at, event_id: e.id, reason: null, reason_class: null, head_at_removal: null };
|
|
45327
|
+
episodes.push(open2);
|
|
45328
|
+
open2 = null;
|
|
45329
|
+
}
|
|
45330
|
+
}
|
|
45331
|
+
}
|
|
45332
|
+
if (open2) episodes.push(open2);
|
|
45333
|
+
let bounceCount = 0;
|
|
45334
|
+
const byReason = ZERO_BOUNCES();
|
|
45335
|
+
episodes.forEach((ep, idx) => {
|
|
45336
|
+
if (ep.terminal.type !== "removed") return;
|
|
45337
|
+
const next = episodes[idx + 1];
|
|
45338
|
+
const headChange = next ? next.head_changed_since_previous_episode === true : false;
|
|
45339
|
+
ep.terminal.reason_class = classifyRemovalReason(ep.terminal.reason, headChange);
|
|
45340
|
+
if (next && next.reentry_of === ep.episode_id) {
|
|
45341
|
+
bounceCount++;
|
|
45342
|
+
byReason[ep.terminal.reason_class]++;
|
|
45343
|
+
}
|
|
45344
|
+
});
|
|
45345
|
+
for (const run of manifest.runs) {
|
|
45346
|
+
for (const member of run.members) {
|
|
45347
|
+
if (member.pr !== state.number) continue;
|
|
45348
|
+
if (!member.admission_event_id) continue;
|
|
45349
|
+
const ep = episodes.find((x) => x.episode_id === member.admission_event_id);
|
|
45350
|
+
const join54 = joins.find((j) => j.run_id === run.run_id && j.run_attempt === run.run_attempt && j.pr === member.pr && j.admission_event_id === member.admission_event_id);
|
|
45351
|
+
if (!join54) continue;
|
|
45352
|
+
if (!ep) {
|
|
45353
|
+
if (state.status === "complete") {
|
|
45354
|
+
join54.status = "unresolved";
|
|
45355
|
+
join54.reason = "admission-event-not-in-history";
|
|
45356
|
+
} else {
|
|
45357
|
+
join54.status = "unresolved";
|
|
45358
|
+
join54.reason = "history-incomplete";
|
|
45359
|
+
}
|
|
45360
|
+
continue;
|
|
45361
|
+
}
|
|
45362
|
+
join54.status = "joined";
|
|
45363
|
+
delete join54.reason;
|
|
45364
|
+
ep.runs.push({ run_id: run.run_id, run_attempt: run.run_attempt, head_sha: run.head_sha });
|
|
45365
|
+
}
|
|
45366
|
+
}
|
|
45367
|
+
const runOf = (ep) => manifest.runs.filter((r) => ep.runs.some((x) => x.run_id === r.run_id && x.run_attempt === r.run_attempt));
|
|
45368
|
+
const earliest = (ep, field) => {
|
|
45369
|
+
const vals = runOf(ep).map((r) => r[field]).filter((v) => typeof v === "string").sort((a, b) => Date.parse(a) - Date.parse(b));
|
|
45370
|
+
return vals[0] ?? null;
|
|
45371
|
+
};
|
|
45372
|
+
for (const ep of episodes) {
|
|
45373
|
+
if (!ep.runs.length) continue;
|
|
45374
|
+
const started = earliest(ep, "started_at");
|
|
45375
|
+
ep.queue_wait_to_execution_seconds = started ? secondsBetween(ep.enqueued_at, started) : { value: null, reason: "joined-run-has-no-execution-start" };
|
|
45376
|
+
}
|
|
45377
|
+
const mergedAt = state.pr?.merged_at ?? null;
|
|
45378
|
+
let cohort;
|
|
45379
|
+
let cohortReason;
|
|
45380
|
+
if (entry.excluded || state.status === "excluded") {
|
|
45381
|
+
cohort = "excluded";
|
|
45382
|
+
cohortReason = `manifest-exclusion: ${entry.excluded ?? "excluded"}`;
|
|
45383
|
+
} else if (state.status !== "complete") {
|
|
45384
|
+
cohort = "unknown";
|
|
45385
|
+
cohortReason = state.status === "failed" || state.status === "unsupported" ? `lookup-${state.status}:${state.error?.code ?? "unknown"}` : "history-incomplete";
|
|
45386
|
+
} else if (mergedAt === null) {
|
|
45387
|
+
cohort = "right-censored";
|
|
45388
|
+
cohortReason = "not-merged-pending-or-closed";
|
|
45389
|
+
} else if (Date.parse(mergedAt) >= Date.parse(manifest.until)) {
|
|
45390
|
+
cohort = "right-censored";
|
|
45391
|
+
cohortReason = "merged-at-or-after-until";
|
|
45392
|
+
} else if (Date.parse(mergedAt) < Date.parse(manifest.since)) {
|
|
45393
|
+
cohort = "outside-window";
|
|
45394
|
+
cohortReason = "merged-before-since";
|
|
45395
|
+
} else cohort = "eligible";
|
|
45396
|
+
const blocked = () => {
|
|
45397
|
+
if (state.status === "excluded") return "excluded-by-manifest";
|
|
45398
|
+
if (state.status !== "complete") return state.status === "incomplete" || state.status === "pending" ? "history-incomplete" : `lookup-${state.status}`;
|
|
45399
|
+
if (mergedAt === null) return "not-merged";
|
|
45400
|
+
if (anomalies.some((a) => a.startsWith("malformed") || a.startsWith("event-missing") || a.startsWith("unexpected-typename") || a.startsWith("base-change-missing"))) return "malformed-history";
|
|
45401
|
+
if (anomalies.some((a) => a.startsWith("out-of-order") || a.startsWith("admission-without-removal"))) return "contradictory-ordering";
|
|
45402
|
+
if (mergedEvent && mergedEvent.at !== mergedAt && Date.parse(mergedEvent.at) !== Date.parse(mergedAt)) return "conflicting-merge-time";
|
|
45403
|
+
if (mergedEvent && state.pr?.merge_sha && mergedEvent.merge_sha && mergedEvent.merge_sha !== state.pr.merge_sha) return "conflicting-merge-sha";
|
|
45404
|
+
return null;
|
|
45405
|
+
};
|
|
45406
|
+
const block = blocked();
|
|
45407
|
+
const nul = (reason) => ({ value: null, reason });
|
|
45408
|
+
let first;
|
|
45409
|
+
if (block) first = nul(block);
|
|
45410
|
+
else if (leftCensored) first = nul("left-censored: removal precedes any recorded admission");
|
|
45411
|
+
else if (!episodes.length) first = nul("no-verified-admission-in-history (not proof of queue bypass)");
|
|
45412
|
+
else first = secondsBetween(episodes[0].enqueued_at, mergedAt);
|
|
45413
|
+
const successful = episodes.filter((ep) => ep.terminal.type === "merged");
|
|
45414
|
+
let success;
|
|
45415
|
+
let successEp = null;
|
|
45416
|
+
if (block) success = nul(block);
|
|
45417
|
+
else if (successful.length !== 1) success = nul(successful.length ? "ambiguous-successful-episode" : "no-episode-terminated-by-merge");
|
|
45418
|
+
else {
|
|
45419
|
+
successEp = successful[0];
|
|
45420
|
+
const headMoved = ordered.some((h) => h.kind === "head_changed" && Date.parse(h.at) > Date.parse(successEp.enqueued_at) && Date.parse(h.at) <= Date.parse(mergedAt));
|
|
45421
|
+
success = headMoved ? nul("head-changed-within-successful-episode") : secondsBetween(successEp.enqueued_at, mergedAt);
|
|
45422
|
+
}
|
|
45423
|
+
const wait = block ? nul(block) : successEp ? successEp.queue_wait_to_execution_seconds : nul("no-successful-episode");
|
|
45424
|
+
const failureEp = episodes.find((ep) => earliest(ep, "failure_result_at") !== null);
|
|
45425
|
+
const observedEp = episodes.find((ep) => earliest(ep, "failure_observed_at") !== null);
|
|
45426
|
+
const failureResult = failureEp ? secondsBetween(failureEp.enqueued_at, earliest(failureEp, "failure_result_at")) : nul("no-joined-failure-result-evidence");
|
|
45427
|
+
const failureObserved = observedEp ? secondsBetween(observedEp.enqueued_at, earliest(observedEp, "failure_observed_at")) : nul("no-joined-failure-observation-evidence");
|
|
45428
|
+
const historyUsable = state.status === "complete" && !leftCensored && !(block && block !== "not-merged");
|
|
45429
|
+
const failureBlock = block === "not-merged" ? null : block;
|
|
45430
|
+
return {
|
|
45431
|
+
number: state.number,
|
|
45432
|
+
lookup: state.status,
|
|
45433
|
+
...state.error ? { error: state.error } : {},
|
|
45434
|
+
cohort,
|
|
45435
|
+
...cohortReason ? { cohort_reason: cohortReason } : {},
|
|
45436
|
+
merged_at: mergedAt,
|
|
45437
|
+
merge_sha: state.pr?.merge_sha ?? null,
|
|
45438
|
+
base_ref: prBase,
|
|
45439
|
+
head_transitions: ordered.filter((e) => e.kind === "head_changed").map((e) => ({ event_id: e.id, at: e.at, before: e.before, after: e.after })),
|
|
45440
|
+
base_transitions: baseChanges.map((e) => ({ event_id: e.id, at: e.at, from: e.from, to: e.to })),
|
|
45441
|
+
episodes,
|
|
45442
|
+
events: ordered,
|
|
45443
|
+
anomalies,
|
|
45444
|
+
metrics: {
|
|
45445
|
+
first_enqueue_to_merge_seconds: first,
|
|
45446
|
+
successful_episode_enqueue_to_merge_seconds: success,
|
|
45447
|
+
queue_wait_to_execution_seconds: wait,
|
|
45448
|
+
enqueue_to_failure_result_seconds: { ...failureBlock ? nul(failureBlock) : failureResult, semantics: "provider-completion-time" },
|
|
45449
|
+
enqueue_to_first_observed_failure_seconds: { ...failureBlock ? nul(failureBlock) : failureObserved, semantics: "measurement-poll-observation" },
|
|
45450
|
+
bounce_count: historyUsable ? bounceCount : null,
|
|
45451
|
+
bounce_by_reason: historyUsable ? byReason : null
|
|
45452
|
+
}
|
|
45453
|
+
};
|
|
45454
|
+
}
|
|
45455
|
+
function median(values) {
|
|
45456
|
+
if (!values.length) return null;
|
|
45457
|
+
const s = [...values].sort((a, b) => a - b);
|
|
45458
|
+
const mid = Math.floor(s.length / 2);
|
|
45459
|
+
return s.length % 2 ? s[mid] : (s[mid - 1] + s[mid]) / 2;
|
|
45460
|
+
}
|
|
45461
|
+
function summarizeMetric(prs, key, definition, complete) {
|
|
45462
|
+
const values = [];
|
|
45463
|
+
const missing = {};
|
|
45464
|
+
for (const pr of prs) {
|
|
45465
|
+
if (pr.cohort !== "eligible") continue;
|
|
45466
|
+
const m = pr.metrics[key];
|
|
45467
|
+
if (m.value === null) missing[m.reason ?? "unknown"] = (missing[m.reason ?? "unknown"] ?? 0) + 1;
|
|
45468
|
+
else values.push(m.value);
|
|
45469
|
+
}
|
|
45470
|
+
const count = (c) => prs.filter((p) => p.cohort === c).length;
|
|
45471
|
+
const missingTotal = Object.values(missing).reduce((a, b) => a + b, 0);
|
|
45472
|
+
return {
|
|
45473
|
+
definition,
|
|
45474
|
+
label: complete && missingTotal === 0 && count("unknown") === 0 ? "completed-cohort-median" : "available-case-median",
|
|
45475
|
+
median_seconds: median(values),
|
|
45476
|
+
cohort_denominator: prs.length,
|
|
45477
|
+
eligible: values.length,
|
|
45478
|
+
missing_by_reason: missing,
|
|
45479
|
+
right_censored: count("right-censored"),
|
|
45480
|
+
excluded: count("excluded"),
|
|
45481
|
+
outside_window: count("outside-window"),
|
|
45482
|
+
unknown: count("unknown")
|
|
45483
|
+
};
|
|
45484
|
+
}
|
|
45485
|
+
async function readMergeQueueLifecycle(deps, req) {
|
|
45486
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
45487
|
+
const started = now();
|
|
45488
|
+
const deadlineMs = started.getTime() + req.deadlineSeconds * 1e3;
|
|
45489
|
+
const [owner, name] = req.repo.split("/");
|
|
45490
|
+
const prior = new Map((req.resume?.prs ?? []).map((p) => [p.number, p]));
|
|
45491
|
+
const states = req.manifest.prs.map((p) => {
|
|
45492
|
+
const was = prior.get(p.number);
|
|
45493
|
+
if (was && was.status !== "incomplete" && was.status !== "pending") return was;
|
|
45494
|
+
if (was) return { ...was, status: "pending" };
|
|
45495
|
+
return { number: p.number, status: "pending", pages: 0, cursors: [], next_cursor: null, events: [], duplicates_dropped: 0 };
|
|
45496
|
+
});
|
|
45497
|
+
let pages = 0;
|
|
45498
|
+
let eventCount = 0;
|
|
45499
|
+
const exhausted = /* @__PURE__ */ new Set();
|
|
45500
|
+
for (const st of states) {
|
|
45501
|
+
if (st.status !== "pending") continue;
|
|
45502
|
+
if (req.manifest.prs.find((p) => p.number === st.number)?.excluded) {
|
|
45503
|
+
st.status = "excluded";
|
|
45504
|
+
continue;
|
|
45505
|
+
}
|
|
45506
|
+
const ids = new Set(st.events.map((e) => e.id));
|
|
45507
|
+
for (; ; ) {
|
|
45508
|
+
if (exhausted.size) break;
|
|
45509
|
+
if (pages >= req.maxPages) {
|
|
45510
|
+
exhausted.add("max-pages");
|
|
45511
|
+
break;
|
|
45512
|
+
}
|
|
45513
|
+
if (eventCount >= req.maxEvents) {
|
|
45514
|
+
exhausted.add("max-events");
|
|
45515
|
+
break;
|
|
45516
|
+
}
|
|
45517
|
+
const remainingMs = deadlineMs - now().getTime();
|
|
45518
|
+
if (remainingMs <= 0) {
|
|
45519
|
+
exhausted.add("deadline");
|
|
45520
|
+
break;
|
|
45521
|
+
}
|
|
45522
|
+
const after = st.next_cursor;
|
|
45523
|
+
let page;
|
|
45524
|
+
try {
|
|
45525
|
+
page = await deps.transport.graphql(LIFECYCLE_QUERY, { owner, name, number: st.number, after }, { timeoutMs: Math.min(6e4, remainingMs) });
|
|
45526
|
+
} catch (e) {
|
|
45527
|
+
const err = classifyError(e);
|
|
45528
|
+
if (err.code === "rate-limited") {
|
|
45529
|
+
exhausted.add("rate-limited");
|
|
45530
|
+
st.error = err;
|
|
45531
|
+
break;
|
|
45532
|
+
}
|
|
45533
|
+
st.status = err.code === "unsupported-source" ? "unsupported" : "failed";
|
|
45534
|
+
st.error = err;
|
|
45535
|
+
break;
|
|
45536
|
+
}
|
|
45537
|
+
pages++;
|
|
45538
|
+
st.pages++;
|
|
45539
|
+
st.cursors.push(after);
|
|
45540
|
+
const pr = page?.repository?.pullRequest;
|
|
45541
|
+
const items = pr?.timelineItems;
|
|
45542
|
+
const hasNext = items?.pageInfo?.hasNextPage;
|
|
45543
|
+
const endCursor = items?.pageInfo?.endCursor;
|
|
45544
|
+
if (!pr || typeof pr.baseRefName !== "string" || !items || !Array.isArray(items.nodes) || typeof hasNext !== "boolean" || hasNext && typeof endCursor !== "string") {
|
|
45545
|
+
st.status = "failed";
|
|
45546
|
+
st.error = { code: pr === null ? "not-found-or-masked" : "malformed-response", status: 200 };
|
|
45547
|
+
break;
|
|
45548
|
+
}
|
|
45549
|
+
st.pr = { base_ref: pr.baseRefName, merged_at: typeof pr.mergedAt === "string" ? pr.mergedAt : null, merge_sha: oidOf(pr.mergeCommit) };
|
|
45550
|
+
const malformed = st.malformed ?? [];
|
|
45551
|
+
for (const ev of normalizeNodes(items.nodes, malformed)) {
|
|
45552
|
+
if (ids.has(ev.id)) {
|
|
45553
|
+
st.duplicates_dropped++;
|
|
45554
|
+
continue;
|
|
45555
|
+
}
|
|
45556
|
+
ids.add(ev.id);
|
|
45557
|
+
st.events.push(ev);
|
|
45558
|
+
eventCount++;
|
|
45559
|
+
}
|
|
45560
|
+
if (malformed.length) st.malformed = malformed;
|
|
45561
|
+
if (!hasNext) {
|
|
45562
|
+
st.status = "complete";
|
|
45563
|
+
st.next_cursor = null;
|
|
45564
|
+
break;
|
|
45565
|
+
}
|
|
45566
|
+
if (endCursor === after) {
|
|
45567
|
+
st.status = "failed";
|
|
45568
|
+
st.error = { code: "malformed-response", status: 200 };
|
|
45569
|
+
break;
|
|
45570
|
+
}
|
|
45571
|
+
st.next_cursor = endCursor;
|
|
45572
|
+
}
|
|
45573
|
+
if (st.status === "pending") st.status = "incomplete";
|
|
45574
|
+
}
|
|
45575
|
+
const joins = [];
|
|
45576
|
+
const cohortNumbers = new Set(req.manifest.prs.map((p) => p.number));
|
|
45577
|
+
for (const run of req.manifest.runs) {
|
|
45578
|
+
if (!run.members.length) {
|
|
45579
|
+
joins.push({ run_id: run.run_id, run_attempt: run.run_attempt, pr: null, admission_event_id: null, status: "unresolved", reason: "no-explicit-pr-membership" });
|
|
45580
|
+
continue;
|
|
45581
|
+
}
|
|
45582
|
+
const prsInRun = run.members.map((m) => m.pr);
|
|
45583
|
+
for (const member of run.members) {
|
|
45584
|
+
const base = { run_id: run.run_id, run_attempt: run.run_attempt, pr: member.pr, admission_event_id: member.admission_event_id ?? null };
|
|
45585
|
+
if (!cohortNumbers.has(member.pr)) joins.push({ ...base, status: "unresolved", reason: "pr-not-in-cohort" });
|
|
45586
|
+
else if (prsInRun.filter((n) => n === member.pr).length > 1) joins.push({ ...base, status: "unresolved", reason: "conflicting-membership" });
|
|
45587
|
+
else if (!member.admission_event_id) joins.push({ ...base, status: "unresolved", reason: "no-explicit-episode-identity" });
|
|
45588
|
+
else joins.push({ ...base, status: "unresolved", reason: "pending-history" });
|
|
45589
|
+
}
|
|
45590
|
+
}
|
|
45591
|
+
const joinable = {
|
|
45592
|
+
...req.manifest,
|
|
45593
|
+
runs: req.manifest.runs.map((r) => ({ ...r, members: r.members.filter((m) => joins.some((j) => j.run_id === r.run_id && j.run_attempt === r.run_attempt && j.pr === m.pr && j.reason === "pending-history")) }))
|
|
45594
|
+
};
|
|
45595
|
+
const prs = states.map((st) => buildPrLifecycle(st, joinable, joins));
|
|
45596
|
+
for (const j of joins) if (j.reason === "pending-history") j.reason = "history-incomplete";
|
|
45597
|
+
const complete = !exhausted.size && states.every((s) => s.status === "complete" || s.status === "excluded");
|
|
45598
|
+
const bounceTotals = ZERO_BOUNCES();
|
|
45599
|
+
let bounceTotal = 0;
|
|
45600
|
+
let bouncePrs = 0;
|
|
45601
|
+
for (const pr of prs) {
|
|
45602
|
+
if (pr.metrics.bounce_count === null || pr.cohort === "excluded") continue;
|
|
45603
|
+
bouncePrs++;
|
|
45604
|
+
bounceTotal += pr.metrics.bounce_count;
|
|
45605
|
+
for (const [k, v] of Object.entries(pr.metrics.bounce_by_reason)) bounceTotals[k] += v;
|
|
45606
|
+
}
|
|
45607
|
+
return {
|
|
45608
|
+
schema: LIFECYCLE_SCHEMA,
|
|
45609
|
+
repo: req.repo,
|
|
45610
|
+
provider: "github.com",
|
|
45611
|
+
base_branch: req.manifest.base_branch,
|
|
45612
|
+
cohort: { manifest_sha256: req.manifestSha256, measurement_receipt: req.manifest.measurement_receipt, pr_count: req.manifest.prs.length, run_count: req.manifest.runs.length },
|
|
45613
|
+
window: { since: req.since, until: req.until, semantics: "half-open [since, until) over merged_at; queue history is read from each PR timeline start, not truncated at since" },
|
|
45614
|
+
observation: { started_at: started.toISOString(), ended_at: now().toISOString() },
|
|
45615
|
+
source: LIFECYCLE_SOURCE,
|
|
45616
|
+
actor: deps.actor ?? { credential: "app_installation" },
|
|
45617
|
+
correlation_id: (deps.correlationId ?? import_node_crypto15.randomUUID)(),
|
|
45618
|
+
bounds: { max_pages: req.maxPages, max_events: req.maxEvents, deadline_seconds: req.deadlineSeconds },
|
|
45619
|
+
counts: { pages, events: eventCount, duplicates_dropped: states.reduce((a, s) => a + s.duplicates_dropped, 0) },
|
|
45620
|
+
complete,
|
|
45621
|
+
exhausted: [...exhausted],
|
|
45622
|
+
continuation: complete ? null : {
|
|
45623
|
+
repo: req.repo,
|
|
45624
|
+
manifest_sha256: req.manifestSha256,
|
|
45625
|
+
since: req.since,
|
|
45626
|
+
until: req.until,
|
|
45627
|
+
source_version: LIFECYCLE_SOURCE.version,
|
|
45628
|
+
remaining: states.filter((s) => s.status === "incomplete" || s.status === "pending").map((s) => ({ pr: s.number, after: s.next_cursor }))
|
|
45629
|
+
},
|
|
45630
|
+
resumed_from: req.resume?.receipt_sha256 ?? null,
|
|
45631
|
+
prs,
|
|
45632
|
+
unresolved_joins: joins.filter((j) => j.status === "unresolved"),
|
|
45633
|
+
joins,
|
|
45634
|
+
medians: {
|
|
45635
|
+
first_enqueue_to_merge_seconds: summarizeMetric(prs, "first_enqueue_to_merge_seconds", "merged_at minus the first verified admission of the complete PR queue journey (re-entries included)", complete),
|
|
45636
|
+
successful_episode_enqueue_to_merge_seconds: summarizeMetric(prs, "successful_episode_enqueue_to_merge_seconds", "merged_at minus the admission of the unique episode terminated by the merge", complete),
|
|
45637
|
+
queue_wait_to_execution_seconds: summarizeMetric(prs, "queue_wait_to_execution_seconds", "successful-episode admission to the earliest execution start of an explicitly joined run", complete)
|
|
45638
|
+
},
|
|
45639
|
+
bounces: { total: bounceTotal, by_reason: bounceTotals, prs_counted: bouncePrs },
|
|
45640
|
+
checkpoint: { prs: states }
|
|
45641
|
+
};
|
|
45642
|
+
}
|
|
45643
|
+
var sha256 = (text) => (0, import_node_crypto15.createHash)("sha256").update(text).digest("hex");
|
|
45644
|
+
function writeLifecycleReceipt(path2, receipt) {
|
|
45645
|
+
const text = `${JSON.stringify(receipt, null, 2)}
|
|
45646
|
+
`;
|
|
45647
|
+
(0, import_node_fs35.mkdirSync)((0, import_node_path32.dirname)(path2), { recursive: true });
|
|
45648
|
+
(0, import_node_fs35.writeFileSync)(path2, text, { encoding: "utf8", flag: "wx" });
|
|
45649
|
+
const back = (0, import_node_fs35.readFileSync)(path2);
|
|
45650
|
+
if (back.toString("utf8") !== text) throw new Error(`receipt read-back mismatch at ${path2}`);
|
|
45651
|
+
return { path: path2, sha256: sha256(back), bytes: back.length };
|
|
45652
|
+
}
|
|
45653
|
+
function loadCheckpoint(text, scope) {
|
|
45654
|
+
let parsed;
|
|
45655
|
+
try {
|
|
45656
|
+
parsed = JSON.parse(text);
|
|
45657
|
+
} catch {
|
|
45658
|
+
throw new Error("checkpoint receipt is not valid JSON");
|
|
45659
|
+
}
|
|
45660
|
+
const r = parsed.result;
|
|
45661
|
+
if (!r || r.schema !== LIFECYCLE_SCHEMA || !r.checkpoint || !Array.isArray(r.checkpoint.prs)) throw new Error("checkpoint is not a merge-queue lifecycle receipt");
|
|
45662
|
+
const mismatch = [];
|
|
45663
|
+
if (r.repo.toLowerCase() !== scope.repo.toLowerCase()) mismatch.push("repo");
|
|
45664
|
+
if (r.cohort?.manifest_sha256 !== scope.manifestSha256) mismatch.push("cohort manifest");
|
|
45665
|
+
if (r.window?.since !== scope.since || r.window?.until !== scope.until) mismatch.push("window");
|
|
45666
|
+
if (r.source?.version !== LIFECYCLE_SOURCE.version) mismatch.push("source version");
|
|
45667
|
+
if (mismatch.length) throw new Error(`checkpoint scope mismatch (${mismatch.join(", ")}) \u2014 a continuation is bound to the same repository, cohort, window and source; it never widens scope`);
|
|
45668
|
+
return { receipt_sha256: sha256(text), prs: r.checkpoint.prs };
|
|
45669
|
+
}
|
|
45670
|
+
var LIFECYCLE_COMMAND_PATH = "read merge-queue lifecycle";
|
|
45671
|
+
function registerMergeQueueLifecycleCommand(readRoot, deps) {
|
|
45672
|
+
readRoot.command("merge-queue").description("historical merge-queue reads (#9788)").command("lifecycle").description("typed per-PR merge-queue history for a retained cohort: one fixed GraphQL timeline query on the App pull_requests:read profile, bounded, receipt-bound; unknown values are null with a reason, never zero (#9788)").requiredOption("--repo <owner/repo>", "registered repository (owner/repo, repo name, or one unambiguous project alias)").requiredOption("--input <manifest>", `retained cohort manifest JSON (${COHORT_SCHEMA})`).requiredOption("--since <utc>", "window start, inclusive (UTC, e.g. 2026-11-02T00:00:00Z)").requiredOption("--until <utc>", "window end, exclusive (UTC)").requiredOption("--max-pages <n>", "page bound across the cohort (1-1000)").requiredOption("--max-events <n>", "event bound across the cohort (1-100000)").requiredOption("--deadline-seconds <n>", "wall-clock bound (1-3600)").requiredOption("--receipt <new-path>", "new append-only receipt file (refused if it exists)").option("--resume <prior-receipt>", "continue an incomplete read from its receipt (same repo, cohort, window and source only)").option("--json", "machine-readable result").action(async (o) => {
|
|
45673
|
+
const label = "read merge-queue lifecycle";
|
|
45674
|
+
let since, until, maxPages, maxEvents, deadlineSeconds, manifest, manifestText;
|
|
45675
|
+
try {
|
|
45676
|
+
since = parseUtc(o.since, "--since");
|
|
45677
|
+
until = parseUtc(o.until, "--until");
|
|
45678
|
+
if (Date.parse(since) >= Date.parse(until)) throw new Error("--since must be earlier than --until (half-open window)");
|
|
45679
|
+
maxPages = parseBound(o.maxPages, "--max-pages", 1e3);
|
|
45680
|
+
maxEvents = parseBound(o.maxEvents, "--max-events", 1e5);
|
|
45681
|
+
deadlineSeconds = parseBound(o.deadlineSeconds, "--deadline-seconds", 3600);
|
|
45682
|
+
manifestText = (0, import_node_fs35.readFileSync)(o.input, "utf8");
|
|
45683
|
+
manifest = parseCohortManifest(manifestText);
|
|
45684
|
+
if (manifest.since !== since || manifest.until !== until) throw new Error("--since/--until must equal the manifest window exactly; the authorized window is never widened or narrowed silently");
|
|
45685
|
+
} catch (e) {
|
|
45686
|
+
return fail(`${label}: ${e.message}`);
|
|
45687
|
+
}
|
|
45688
|
+
const resolution = resolveCanonicalRepo(o.repo, await deps.projects());
|
|
45689
|
+
const repo = resolution.kind === "resolved" ? resolution.repo : resolution.kind === "conflict" ? fail(`${label}: '${o.repo}' resolves to MULTIPLE registered repositories (${resolution.candidates.join(", ")}) \u2014 pass the exact owner/repo`) : resolution.kind === "unavailable" ? fail(`${label}: the Hub project registry is unreadable; refusing to address a repo this command cannot prove registered`) : fail(`${label}: '${o.repo}' is not a registered repository in the Hub registry \u2014 pass the exact owner/repo`);
|
|
45690
|
+
if (manifest.repo.toLowerCase() !== repo.toLowerCase()) return fail(`${label}: manifest repo ${manifest.repo} does not match --repo ${repo}; refusing before dispatch`);
|
|
45691
|
+
const manifestSha256 = sha256(manifestText);
|
|
45692
|
+
let resume;
|
|
45693
|
+
try {
|
|
45694
|
+
if (o.resume) resume = loadCheckpoint((0, import_node_fs35.readFileSync)(o.resume, "utf8"), { repo, manifestSha256, since, until });
|
|
45695
|
+
} catch (e) {
|
|
45696
|
+
return fail(`${label}: ${e.message}`);
|
|
45697
|
+
}
|
|
45698
|
+
const request = { repo, input: o.input, since, until, max_pages: maxPages, max_events: maxEvents, deadline_seconds: deadlineSeconds, resume: o.resume ?? null };
|
|
45699
|
+
const result = await readMergeQueueLifecycle(
|
|
45700
|
+
{ transport: await deps.appClient(LIFECYCLE_COMMAND_PATH), ...deps.actor ? { actor: deps.actor() } : {} },
|
|
45701
|
+
{ repo, since, until, maxPages, maxEvents, deadlineSeconds, manifestSha256, manifest, ...resume ? { resume } : {} }
|
|
45702
|
+
);
|
|
45703
|
+
let receipt;
|
|
45704
|
+
try {
|
|
45705
|
+
receipt = writeLifecycleReceipt(o.receipt, { request, result });
|
|
45706
|
+
} catch (e) {
|
|
45707
|
+
const code = e.code;
|
|
45708
|
+
return fail(`${label}: receipt not written (${code === "EEXIST" ? "path exists \u2014 receipts are append-only, pass a new path" : code ?? "write failed"})`);
|
|
45709
|
+
}
|
|
45710
|
+
const output = { ...result, receipt };
|
|
45711
|
+
if (o.json) console.log(JSON.stringify(output, null, 2));
|
|
45712
|
+
else {
|
|
45713
|
+
const m = result.medians;
|
|
45714
|
+
console.log(`${label}: ${repo} ${since}..${until} \u2014 ${result.complete ? "complete" : `INCOMPLETE (${result.exhausted.join(", ") || "lookup failures"})`}; pages ${result.counts.pages}, events ${result.counts.events}`);
|
|
45715
|
+
for (const [k, s] of Object.entries(m)) console.log(` ${k}: ${s.median_seconds ?? "UNKNOWN"} (${s.label}; eligible ${s.eligible}/${s.cohort_denominator}, right-censored ${s.right_censored}, unknown ${s.unknown})`);
|
|
45716
|
+
console.log(` bounces: ${result.bounces.total}; unresolved joins: ${result.unresolved_joins.length}; receipt ${receipt.path} sha256 ${receipt.sha256}`);
|
|
45717
|
+
}
|
|
45718
|
+
if (!result.complete) process.exitCode = 1;
|
|
45719
|
+
});
|
|
45720
|
+
}
|
|
45721
|
+
|
|
45067
45722
|
// src/board-commands.ts
|
|
45068
45723
|
init_cli_shared();
|
|
45069
45724
|
|
|
@@ -45691,9 +46346,9 @@ function registerBoardCommands(program3) {
|
|
|
45691
46346
|
}
|
|
45692
46347
|
|
|
45693
46348
|
// src/bootstrap-commands.ts
|
|
45694
|
-
var
|
|
46349
|
+
var import_node_fs39 = require("node:fs");
|
|
45695
46350
|
var import_node_os21 = require("node:os");
|
|
45696
|
-
var
|
|
46351
|
+
var import_node_path36 = require("node:path");
|
|
45697
46352
|
init_cli_shared();
|
|
45698
46353
|
init_clean_exit();
|
|
45699
46354
|
|
|
@@ -45878,8 +46533,8 @@ init_github_client();
|
|
|
45878
46533
|
init_cli_shared();
|
|
45879
46534
|
|
|
45880
46535
|
// src/port-registry.ts
|
|
45881
|
-
var
|
|
45882
|
-
var
|
|
46536
|
+
var import_node_fs36 = require("node:fs");
|
|
46537
|
+
var import_node_path33 = require("node:path");
|
|
45883
46538
|
|
|
45884
46539
|
// ../infra/port-geometry.mjs
|
|
45885
46540
|
var PORT_BLOCK = 100;
|
|
@@ -45893,8 +46548,8 @@ function nextPortBlock(registry2) {
|
|
|
45893
46548
|
return [base, base + PORT_SPAN];
|
|
45894
46549
|
}
|
|
45895
46550
|
function loadPortRegistry(path2) {
|
|
45896
|
-
if (!(0,
|
|
45897
|
-
const raw = JSON.parse((0,
|
|
46551
|
+
if (!(0, import_node_fs36.existsSync)(path2)) return {};
|
|
46552
|
+
const raw = JSON.parse((0, import_node_fs36.readFileSync)(path2, "utf8"));
|
|
45898
46553
|
const out = {};
|
|
45899
46554
|
for (const [key, value] of Object.entries(raw)) {
|
|
45900
46555
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -45922,22 +46577,22 @@ function existingPortRange(repo, registry2) {
|
|
|
45922
46577
|
return registry2[repo] ?? null;
|
|
45923
46578
|
}
|
|
45924
46579
|
function portRangeInfraAt(root, source) {
|
|
45925
|
-
const registryPath = (0,
|
|
45926
|
-
const ddbScriptPath = (0,
|
|
45927
|
-
if (!(0,
|
|
46580
|
+
const registryPath = (0, import_node_path33.join)(root, "infra", "port-ranges.json");
|
|
46581
|
+
const ddbScriptPath = (0, import_node_path33.join)(root, "infra", "port-ddb.mjs");
|
|
46582
|
+
if (!(0, import_node_fs36.existsSync)(registryPath) || !(0, import_node_fs36.existsSync)(ddbScriptPath)) return null;
|
|
45928
46583
|
return { root, source, registryPath, ddbScriptPath };
|
|
45929
46584
|
}
|
|
45930
46585
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
45931
46586
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
45932
46587
|
if (direct) return direct;
|
|
45933
|
-
for (let dir = cwd; ; dir = (0,
|
|
45934
|
-
const sibling = portRangeInfraAt((0,
|
|
46588
|
+
for (let dir = cwd; ; dir = (0, import_node_path33.dirname)(dir)) {
|
|
46589
|
+
const sibling = portRangeInfraAt((0, import_node_path33.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
45935
46590
|
if (sibling) return sibling;
|
|
45936
|
-
const parent = (0,
|
|
46591
|
+
const parent = (0, import_node_path33.dirname)(dir);
|
|
45937
46592
|
if (parent === dir) break;
|
|
45938
46593
|
}
|
|
45939
46594
|
if (packageDir) {
|
|
45940
|
-
const pkgRoot = (0,
|
|
46595
|
+
const pkgRoot = (0, import_node_path33.join)(packageDir, "..", "..");
|
|
45941
46596
|
const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
|
|
45942
46597
|
if (pkgFrom) return pkgFrom;
|
|
45943
46598
|
}
|
|
@@ -45954,7 +46609,7 @@ async function ensurePortRangeAtomic(repo, path2, allocate) {
|
|
|
45954
46609
|
} catch (e) {
|
|
45955
46610
|
const why = e.message;
|
|
45956
46611
|
const missing = /@aws-sdk\/[\w-]+/.exec(why)?.[0];
|
|
45957
|
-
throw new Error(`DDB port allocator failed for ${repo} \u2014 refusing to fall back to the frozen ${path2} (it would hand out a block the cursor already gave away). ` + (missing && /cannot find|not found|ERR_MODULE_NOT_FOUND/i.test(why) ? `Missing dependency ${missing}: run \`npm ci\` in ${(0,
|
|
46612
|
+
throw new Error(`DDB port allocator failed for ${repo} \u2014 refusing to fall back to the frozen ${path2} (it would hand out a block the cursor already gave away). ` + (missing && /cannot find|not found|ERR_MODULE_NOT_FOUND/i.test(why) ? `Missing dependency ${missing}: run \`npm ci\` in ${(0, import_node_path33.dirname)(path2)}, then retry.` : "Check AWS credentials and that the registry table is reachable, then retry.") + ` Cause: ${why}`);
|
|
45958
46613
|
}
|
|
45959
46614
|
}
|
|
45960
46615
|
|
|
@@ -46006,13 +46661,13 @@ async function assignPersistedPortRange(repo, slug, reg, opts) {
|
|
|
46006
46661
|
}
|
|
46007
46662
|
|
|
46008
46663
|
// src/box-commands.ts
|
|
46009
|
-
var
|
|
46664
|
+
var import_node_fs38 = require("node:fs");
|
|
46010
46665
|
var import_node_child_process12 = require("node:child_process");
|
|
46011
46666
|
var import_node_os20 = require("node:os");
|
|
46012
|
-
var
|
|
46667
|
+
var import_node_path35 = require("node:path");
|
|
46013
46668
|
|
|
46014
46669
|
// src/box-control.ts
|
|
46015
|
-
var
|
|
46670
|
+
var import_node_crypto16 = require("node:crypto");
|
|
46016
46671
|
var MANIFEST_PATH = "infra/box-control/manifest.json";
|
|
46017
46672
|
var ALLOWED_POST = /* @__PURE__ */ new Set(["systemctl daemon-reload"]);
|
|
46018
46673
|
var sshdPost = (dest) => `if sshd -t; then systemctl reload ssh; else rm -f ${dest}; echo 'sshd -t rejected ${dest}: removed, not reloaded' >&2; exit 1; fi`;
|
|
@@ -46116,7 +46771,7 @@ function laneCoverageAction(files, box, op) {
|
|
|
46116
46771
|
return { notice: `LANE_COVERAGE: ${mismatch}` };
|
|
46117
46772
|
}
|
|
46118
46773
|
function md5(content) {
|
|
46119
|
-
return (0,
|
|
46774
|
+
return (0, import_node_crypto16.createHash)("md5").update(content).digest("hex");
|
|
46120
46775
|
}
|
|
46121
46776
|
function planRemoteScript(dests) {
|
|
46122
46777
|
const lines2 = ["set -u"];
|
|
@@ -46234,8 +46889,8 @@ init_clean_exit();
|
|
|
46234
46889
|
init_cli_shared();
|
|
46235
46890
|
|
|
46236
46891
|
// src/secrets-commands.ts
|
|
46237
|
-
var
|
|
46238
|
-
var
|
|
46892
|
+
var import_node_fs37 = require("node:fs");
|
|
46893
|
+
var import_node_path34 = require("node:path");
|
|
46239
46894
|
var import_node_os19 = require("node:os");
|
|
46240
46895
|
init_cli_shared();
|
|
46241
46896
|
init_hub_auth();
|
|
@@ -47432,18 +48087,18 @@ function collectMap(value, previous = []) {
|
|
|
47432
48087
|
return [...previous, value];
|
|
47433
48088
|
}
|
|
47434
48089
|
async function decryptRailsCredentials(input) {
|
|
47435
|
-
const appDir = (0,
|
|
48090
|
+
const appDir = (0, import_node_path34.resolve)(input.appDir ?? process.cwd());
|
|
47436
48091
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
47437
48092
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
47438
|
-
const credentialsPath = (0,
|
|
47439
|
-
const masterKeyPath = (0,
|
|
48093
|
+
const credentialsPath = (0, import_node_path34.resolve)(appDir, credentialsFile);
|
|
48094
|
+
const masterKeyPath = (0, import_node_path34.resolve)(appDir, masterKeyFile);
|
|
47440
48095
|
const env = {
|
|
47441
48096
|
...process.env,
|
|
47442
48097
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
47443
48098
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
47444
48099
|
};
|
|
47445
|
-
if ((0,
|
|
47446
|
-
env.RAILS_MASTER_KEY = (0,
|
|
48100
|
+
if ((0, import_node_fs37.existsSync)(masterKeyPath)) {
|
|
48101
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs37.readFileSync)(masterKeyPath, "utf8").trim();
|
|
47447
48102
|
}
|
|
47448
48103
|
const script = [
|
|
47449
48104
|
'require "json"',
|
|
@@ -47453,9 +48108,9 @@ async function decryptRailsCredentials(input) {
|
|
|
47453
48108
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
47454
48109
|
"puts JSON.generate(config.config)"
|
|
47455
48110
|
].join("\n");
|
|
47456
|
-
const scriptDir = (0,
|
|
47457
|
-
const scriptPath = (0,
|
|
47458
|
-
(0,
|
|
48111
|
+
const scriptDir = (0, import_node_fs37.mkdtempSync)((0, import_node_path34.join)((0, import_node_os19.tmpdir)(), "mmi-rails-decrypt-"));
|
|
48112
|
+
const scriptPath = (0, import_node_path34.join)(scriptDir, "decrypt.rb");
|
|
48113
|
+
(0, import_node_fs37.writeFileSync)(scriptPath, script, "utf8");
|
|
47459
48114
|
try {
|
|
47460
48115
|
const args = ["exec", "ruby", scriptPath];
|
|
47461
48116
|
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
@@ -47467,7 +48122,7 @@ async function decryptRailsCredentials(input) {
|
|
|
47467
48122
|
});
|
|
47468
48123
|
return JSON.parse(stdout);
|
|
47469
48124
|
} finally {
|
|
47470
|
-
(0,
|
|
48125
|
+
(0, import_node_fs37.rmSync)(scriptDir, { recursive: true, force: true });
|
|
47471
48126
|
}
|
|
47472
48127
|
}
|
|
47473
48128
|
async function readSecretStdin() {
|
|
@@ -47557,7 +48212,7 @@ function registerSecretsCommands(program3) {
|
|
|
47557
48212
|
let body;
|
|
47558
48213
|
if (o.file) {
|
|
47559
48214
|
try {
|
|
47560
|
-
body = (0,
|
|
48215
|
+
body = (0, import_node_fs37.readFileSync)((0, import_node_path34.resolve)(o.file), "utf8");
|
|
47561
48216
|
} catch (e) {
|
|
47562
48217
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
47563
48218
|
}
|
|
@@ -47693,7 +48348,7 @@ function registerSecretsCommands(program3) {
|
|
|
47693
48348
|
{
|
|
47694
48349
|
...d,
|
|
47695
48350
|
decryptRailsCredentials,
|
|
47696
|
-
removeFile: (path2) => (0,
|
|
48351
|
+
removeFile: (path2) => (0, import_node_fs37.unlinkSync)((0, import_node_path34.resolve)(o.appDir ?? process.cwd(), path2))
|
|
47697
48352
|
},
|
|
47698
48353
|
{
|
|
47699
48354
|
repo: o.repo,
|
|
@@ -48009,7 +48664,7 @@ function registerBoxCommands(program3) {
|
|
|
48009
48664
|
const stdinSource = o.body ? `"${o.body}"` : "<body.sh>";
|
|
48010
48665
|
const wroteScript = o.ssh && o.script ? o.script : null;
|
|
48011
48666
|
const runLine = wroteScript ? useStdin ? `bash "${wroteScript}" < ${stdinSource}` : `bash "${wroteScript}"` : void 0;
|
|
48012
|
-
if (wroteScript) (0,
|
|
48667
|
+
if (wroteScript) (0, import_node_fs38.writeFileSync)(wroteScript, sshRecipeScript(found, remote), "utf8");
|
|
48013
48668
|
if (o.json) {
|
|
48014
48669
|
console.log(JSON.stringify({
|
|
48015
48670
|
box: found,
|
|
@@ -48136,17 +48791,17 @@ function hubGitShow(ref) {
|
|
|
48136
48791
|
return (path2) => git3(["show", `${ref}:${path2}`]);
|
|
48137
48792
|
}
|
|
48138
48793
|
function runOnBox(found, script) {
|
|
48139
|
-
const dir = (0,
|
|
48794
|
+
const dir = (0, import_node_fs38.mkdtempSync)((0, import_node_path35.join)((0, import_node_os20.tmpdir)(), "mmi-box-control-"));
|
|
48140
48795
|
try {
|
|
48141
|
-
const recipe = (0,
|
|
48142
|
-
(0,
|
|
48796
|
+
const recipe = (0, import_node_path35.join)(dir, "recipe.sh");
|
|
48797
|
+
(0, import_node_fs38.writeFileSync)(recipe, sshRecipeScript(found, "sh -s"), "utf8");
|
|
48143
48798
|
const r = (0, import_node_child_process12.spawnSync)("bash", [recipe], { input: script, encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 18e4, windowsHide: true });
|
|
48144
48799
|
if (r.status !== 0) {
|
|
48145
48800
|
throw new UnreadableError(`box control: the ssh session to ${found.name} failed (exit ${r.status ?? r.signal}): ${(r.stderr || "").trim().split("\n").slice(-3).join(" | ")}`);
|
|
48146
48801
|
}
|
|
48147
48802
|
return r.stdout;
|
|
48148
48803
|
} finally {
|
|
48149
|
-
(0,
|
|
48804
|
+
(0, import_node_fs38.rmSync)(dir, { recursive: true, force: true });
|
|
48150
48805
|
}
|
|
48151
48806
|
}
|
|
48152
48807
|
function formatControl(mode, box, rows) {
|
|
@@ -48274,7 +48929,7 @@ function renderDeployPortDoctor(report) {
|
|
|
48274
48929
|
}
|
|
48275
48930
|
|
|
48276
48931
|
// src/bootstrap-drift.ts
|
|
48277
|
-
var
|
|
48932
|
+
var import_node_crypto17 = require("node:crypto");
|
|
48278
48933
|
function byteComparableSeeds(manifest, cls) {
|
|
48279
48934
|
return manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self" && s.classes.includes(cls));
|
|
48280
48935
|
}
|
|
@@ -48294,7 +48949,7 @@ function compareSeedBytes(hubContent, repoContent) {
|
|
|
48294
48949
|
return normalize(hubContent) === normalize(repoContent) ? "match" : "drift";
|
|
48295
48950
|
}
|
|
48296
48951
|
function seedContentHash(content) {
|
|
48297
|
-
return (0,
|
|
48952
|
+
return (0, import_node_crypto17.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
|
|
48298
48953
|
}
|
|
48299
48954
|
function auditRepoSeedDrift(repo, seeds, hubContents, repoReads) {
|
|
48300
48955
|
const byTarget = new Map(repoReads.map((r) => [r.target, r.content]));
|
|
@@ -49544,7 +50199,7 @@ function renderBootstrapVerifyReport(report) {
|
|
|
49544
50199
|
}
|
|
49545
50200
|
|
|
49546
50201
|
// src/bootstrap-seed-delivery.ts
|
|
49547
|
-
var
|
|
50202
|
+
var import_node_crypto18 = require("node:crypto");
|
|
49548
50203
|
init_github_client();
|
|
49549
50204
|
var SHA = /^[a-f0-9]{40}$/;
|
|
49550
50205
|
async function readSeedFile(repo, target, ref, client) {
|
|
@@ -49577,7 +50232,7 @@ async function targetedSeedPlan(repo, slug, target, baseBranch, sourceSha, clien
|
|
|
49577
50232
|
}
|
|
49578
50233
|
const baseSha = base?.object?.sha;
|
|
49579
50234
|
if (!baseSha || !SHA.test(baseSha) || !SHA.test(sourceSha)) throw new Error("bootstrap apply: seed identity is unreadable");
|
|
49580
|
-
const identity = (0,
|
|
50235
|
+
const identity = (0, import_node_crypto18.createHash)("sha256").update(JSON.stringify([repo.toLowerCase(), target, baseSha, sourceSha])).digest("hex").slice(0, 24);
|
|
49581
50236
|
const delivery = planSeedDelivery(rules, slug, baseBranch, `bootstrap-seed-${identity}`);
|
|
49582
50237
|
const scoped = forcePr && delivery.mode === "direct" ? { mode: "pr", ref: `bootstrap-seed-${identity}-${slug}`, branch: `bootstrap-seed-${identity}-${slug}`, reason: "registry gate render requires review" } : delivery;
|
|
49583
50238
|
return { ...scoped, baseSha, baseBranch };
|
|
@@ -49647,12 +50302,12 @@ var declaredProjectAdmins = (meta) => {
|
|
|
49647
50302
|
};
|
|
49648
50303
|
var execGitForSeedSource = async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
49649
50304
|
async function gateRenderDiff(current, rendered) {
|
|
49650
|
-
const dir = (0,
|
|
49651
|
-
const before = (0,
|
|
49652
|
-
const after = (0,
|
|
50305
|
+
const dir = (0, import_node_fs39.mkdtempSync)((0, import_node_path36.join)((0, import_node_os21.tmpdir)(), "mmi-gate-diff-"));
|
|
50306
|
+
const before = (0, import_node_path36.join)(dir, "committed-gate.yml");
|
|
50307
|
+
const after = (0, import_node_path36.join)(dir, "registry-render.yml");
|
|
49653
50308
|
try {
|
|
49654
|
-
(0,
|
|
49655
|
-
(0,
|
|
50309
|
+
(0, import_node_fs39.writeFileSync)(before, current);
|
|
50310
|
+
(0, import_node_fs39.writeFileSync)(after, rendered);
|
|
49656
50311
|
try {
|
|
49657
50312
|
return (await execFileP("git", ["diff", "--no-index", "--", before, after], { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
49658
50313
|
} catch (e) {
|
|
@@ -49661,7 +50316,7 @@ async function gateRenderDiff(current, rendered) {
|
|
|
49661
50316
|
throw e;
|
|
49662
50317
|
}
|
|
49663
50318
|
} finally {
|
|
49664
|
-
(0,
|
|
50319
|
+
(0, import_node_fs39.rmSync)(dir, { recursive: true, force: true });
|
|
49665
50320
|
}
|
|
49666
50321
|
}
|
|
49667
50322
|
function bootstrapTrainDoctorDeps() {
|
|
@@ -49682,9 +50337,9 @@ async function propagationScope(classes, what) {
|
|
|
49682
50337
|
}
|
|
49683
50338
|
const rosterRepos = collectRegistryRepos(projects).filter((r) => r.toLowerCase() !== "mutmutco/mmi-hub");
|
|
49684
50339
|
let independentCount = rosterRepos.length;
|
|
49685
|
-
if ((0,
|
|
50340
|
+
if ((0, import_node_fs39.existsSync)("projects.json")) {
|
|
49686
50341
|
try {
|
|
49687
|
-
const local = JSON.parse((0,
|
|
50342
|
+
const local = JSON.parse((0, import_node_fs39.readFileSync)("projects.json", "utf8"));
|
|
49688
50343
|
const localRepos = /* @__PURE__ */ new Set();
|
|
49689
50344
|
for (const p of local.projects ?? []) for (const r of p.repos ?? []) {
|
|
49690
50345
|
const full = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
|
|
@@ -49809,13 +50464,13 @@ function registerBootstrapCommands(program3) {
|
|
|
49809
50464
|
})),
|
|
49810
50465
|
pushAllowlistOwners: pushAllowlistAuthority(verifyOwners, declaredProjectAdmins(meta)),
|
|
49811
50466
|
pushAllowlistOwnersRead: verifyOwnersRead,
|
|
49812
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
50467
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs39.existsSync)(path2) ? (0, import_node_fs39.readFileSync)(path2, "utf8") : null,
|
|
49813
50468
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
49814
50469
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
49815
50470
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
49816
50471
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
49817
50472
|
// sanction, which is the pre-#3664 behaviour.
|
|
49818
|
-
sanctionedAdmins: (0,
|
|
50473
|
+
sanctionedAdmins: (0, import_node_fs39.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs39.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
49819
50474
|
requiredGcpApis: (() => {
|
|
49820
50475
|
const v = meta?.requiredGcpApis;
|
|
49821
50476
|
if (Array.isArray(v)) return v;
|
|
@@ -49893,14 +50548,14 @@ function registerBootstrapCommands(program3) {
|
|
|
49893
50548
|
bootstrap.command("drift").description("#3818: compare every org-owned whole-file seed against MMI-Hub's copy across the registry roster; read-only").option("--repo <owner/repo>", "audit one repo instead of the roster (never a fleet verdict)").option("--json", "machine-readable output").action(async () => {
|
|
49894
50549
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
49895
50550
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
49896
|
-
if (!(0,
|
|
50551
|
+
if (!(0, import_node_fs39.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
|
|
49897
50552
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
49898
50553
|
if (!seedSource.ok) return fail(`bootstrap drift: ${seedSource.reason}`);
|
|
49899
|
-
const manifest = loadBootstrapSeeds((0,
|
|
50554
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
|
|
49900
50555
|
const hubContents = /* @__PURE__ */ new Map();
|
|
49901
50556
|
for (const s of manifest.seeds) {
|
|
49902
50557
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
49903
|
-
hubContents.set(s.target, (0,
|
|
50558
|
+
hubContents.set(s.target, (0, import_node_fs39.existsSync)(s.target) ? (0, import_node_fs39.readFileSync)(s.target, "utf8") : null);
|
|
49904
50559
|
}
|
|
49905
50560
|
let targets;
|
|
49906
50561
|
let classOf = (_repo) => "deployable";
|
|
@@ -50156,24 +50811,24 @@ function registerBootstrapCommands(program3) {
|
|
|
50156
50811
|
return;
|
|
50157
50812
|
}
|
|
50158
50813
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
50159
|
-
if (!(0,
|
|
50814
|
+
if (!(0, import_node_fs39.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
|
|
50160
50815
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
50161
50816
|
if (!seedSource.ok) return fail(`bootstrap apply: ${seedSource.reason}`);
|
|
50162
|
-
const manifest = loadBootstrapSeeds((0,
|
|
50817
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
|
|
50163
50818
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
50164
50819
|
const slug = parsedRepo.slug;
|
|
50165
50820
|
const onlyTarget = o.fromConfig && o.only.trim() === "gate.yml" ? ".github/workflows/gate.yml" : o.only.trim();
|
|
50166
50821
|
const gh2 = async (args) => execFileP("gh", args, { timeout: 2e4 });
|
|
50167
|
-
const readFile10 = (p) => (0,
|
|
50822
|
+
const readFile10 = (p) => (0, import_node_fs39.existsSync)(p) ? (0, import_node_fs39.readFileSync)(p, "utf8") : null;
|
|
50168
50823
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
50169
50824
|
const putSeed = async (target, content, ref, sha) => {
|
|
50170
|
-
const tmp = (0,
|
|
50171
|
-
(0,
|
|
50825
|
+
const tmp = (0, import_node_path36.join)((0, import_node_os21.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
50826
|
+
(0, import_node_fs39.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
50172
50827
|
try {
|
|
50173
50828
|
await gh2(contentPutInputArgs(repo, target, tmp));
|
|
50174
50829
|
} finally {
|
|
50175
50830
|
try {
|
|
50176
|
-
(0,
|
|
50831
|
+
(0, import_node_fs39.unlinkSync)(tmp);
|
|
50177
50832
|
} catch {
|
|
50178
50833
|
}
|
|
50179
50834
|
}
|
|
@@ -50811,10 +51466,10 @@ LIVE apply to ${repo}:
|
|
|
50811
51466
|
if (rawFlag("--pins")) return propagatePins(rawFlag("--apply"), rawFlag("--json"));
|
|
50812
51467
|
const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
|
|
50813
51468
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
50814
|
-
if (!(0,
|
|
51469
|
+
if (!(0, import_node_fs39.existsSync)(manifestPath)) return fail(`bootstrap propagate: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the desired state this tick propagates`);
|
|
50815
51470
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
50816
51471
|
if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
|
|
50817
|
-
const manifest = loadBootstrapSeeds((0,
|
|
51472
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
|
|
50818
51473
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
50819
51474
|
if (!o.target) {
|
|
50820
51475
|
return fail(`bootstrap propagate: --target <path> is required \u2014 one of:
|
|
@@ -50823,9 +51478,9 @@ LIVE apply to ${repo}:
|
|
|
50823
51478
|
const seed = propagatable.find((s) => s.target === o.target);
|
|
50824
51479
|
if (!seed) return fail(`bootstrap propagate: --target '${o.target}' names no centrally propagatable seed in ${manifestPath}. Propagatable targets:
|
|
50825
51480
|
${propagatable.map((s) => s.target).join("\n ")}`);
|
|
50826
|
-
if (!seed.managedBlock && !(0,
|
|
50827
|
-
const hubContent = seed.managedBlock ? null : (0,
|
|
50828
|
-
const readSeedFile2 = (path2) => (0,
|
|
51481
|
+
if (!seed.managedBlock && !(0, import_node_fs39.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
|
|
51482
|
+
const hubContent = seed.managedBlock ? null : (0, import_node_fs39.readFileSync)(seed.target, "utf8");
|
|
51483
|
+
const readSeedFile2 = (path2) => (0, import_node_fs39.existsSync)(path2) ? (0, import_node_fs39.readFileSync)(path2, "utf8") : null;
|
|
50829
51484
|
const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
|
|
50830
51485
|
const { projects, classOf, inClassRepos, canarySlug, sourcePilot } = await propagationScope(seed.classes, `target '${seed.target}'`);
|
|
50831
51486
|
const repos = inClassRepos.map((repo) => {
|
|
@@ -50944,7 +51599,7 @@ LIVE apply to ${repo}:
|
|
|
50944
51599
|
if (plan.refusedNoCanary) return fail("bootstrap propagate --execute: no canary declared for this target \u2014 refusing to write (set seedCanary:true on exactly one registry repo)");
|
|
50945
51600
|
const headSha = seedSource.sha;
|
|
50946
51601
|
const sourceBlob = (await execGitForSeedSource(["rev-parse", `${headSha}:${seedSourcePath(seed)}`])).trim();
|
|
50947
|
-
const runbookUnaffected = (0,
|
|
51602
|
+
const runbookUnaffected = (0, import_node_fs39.existsSync)(RUNBOOK_UNAFFECTED_PATH) ? runbookUnaffectedReason(JSON.parse((0, import_node_fs39.readFileSync)(RUNBOOK_UNAFFECTED_PATH, "utf8")), seed.target, sourceBlob) : void 0;
|
|
50948
51603
|
for (const rec of plan.records) {
|
|
50949
51604
|
if (rec.action !== "open-pr") continue;
|
|
50950
51605
|
const baseBranch = classOf(rec.repo) === "content" ? "main" : "development";
|
|
@@ -50966,15 +51621,15 @@ LIVE apply to ${repo}:
|
|
|
50966
51621
|
} catch {
|
|
50967
51622
|
existingSha = void 0;
|
|
50968
51623
|
}
|
|
50969
|
-
const tmp = (0,
|
|
51624
|
+
const tmp = (0, import_node_path36.join)((0, import_node_os21.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
50970
51625
|
const desiredContent = desiredByRepo.get(rec.repo);
|
|
50971
51626
|
if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
|
|
50972
|
-
(0,
|
|
51627
|
+
(0, import_node_fs39.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha, runbookUnaffected)), "utf8");
|
|
50973
51628
|
try {
|
|
50974
51629
|
await gh2(contentPutInputArgs(rec.repo, seed.target, tmp));
|
|
50975
51630
|
} finally {
|
|
50976
51631
|
try {
|
|
50977
|
-
(0,
|
|
51632
|
+
(0, import_node_fs39.unlinkSync)(tmp);
|
|
50978
51633
|
} catch {
|
|
50979
51634
|
}
|
|
50980
51635
|
}
|
|
@@ -51043,10 +51698,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
51043
51698
|
return fail(`bootstrap rollback: ${e.message}`);
|
|
51044
51699
|
}
|
|
51045
51700
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
51046
|
-
if (!(0,
|
|
51701
|
+
if (!(0, import_node_fs39.existsSync)(manifestPath)) return fail(`bootstrap rollback: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the manifest names which targets are org-owned and therefore propagated (and rollback-able)`);
|
|
51047
51702
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
51048
51703
|
if (!seedSource.ok) return fail(`bootstrap rollback: ${seedSource.reason}`);
|
|
51049
|
-
const manifest = loadBootstrapSeeds((0,
|
|
51704
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
|
|
51050
51705
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
51051
51706
|
if (!o.target) {
|
|
51052
51707
|
return fail(`bootstrap rollback: --target <path> is required \u2014 one of:
|
|
@@ -51063,10 +51718,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
51063
51718
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
51064
51719
|
let candidates;
|
|
51065
51720
|
if (o.record) {
|
|
51066
|
-
if (!(0,
|
|
51721
|
+
if (!(0, import_node_fs39.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
|
|
51067
51722
|
let parsed;
|
|
51068
51723
|
try {
|
|
51069
|
-
parsed = JSON.parse((0,
|
|
51724
|
+
parsed = JSON.parse((0, import_node_fs39.readFileSync)(o.record, "utf8"));
|
|
51070
51725
|
} catch (e) {
|
|
51071
51726
|
return fail(`bootstrap rollback: --record '${o.record}' is not valid JSON: ${e.message}`);
|
|
51072
51727
|
}
|
|
@@ -51143,13 +51798,13 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
51143
51798
|
} catch {
|
|
51144
51799
|
existingSha = void 0;
|
|
51145
51800
|
}
|
|
51146
|
-
const tmp = (0,
|
|
51147
|
-
(0,
|
|
51801
|
+
const tmp = (0, import_node_path36.join)((0, import_node_os21.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
51802
|
+
(0, import_node_fs39.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
|
|
51148
51803
|
try {
|
|
51149
51804
|
await gh2(contentPutInputArgs(repo, seed.target, tmp));
|
|
51150
51805
|
} finally {
|
|
51151
51806
|
try {
|
|
51152
|
-
(0,
|
|
51807
|
+
(0, import_node_fs39.unlinkSync)(tmp);
|
|
51153
51808
|
} catch {
|
|
51154
51809
|
}
|
|
51155
51810
|
}
|
|
@@ -51462,7 +52117,7 @@ init_github_client();
|
|
|
51462
52117
|
init_cli_shared();
|
|
51463
52118
|
|
|
51464
52119
|
// src/suite-status.ts
|
|
51465
|
-
var
|
|
52120
|
+
var import_node_crypto19 = require("node:crypto");
|
|
51466
52121
|
var SUITE_REPO = "mutmutco/MM-Work";
|
|
51467
52122
|
var REPOS = {
|
|
51468
52123
|
"mmi-scout": "mutmutco/MMI-Scout",
|
|
@@ -51490,7 +52145,7 @@ function parseSuiteMapping(body) {
|
|
|
51490
52145
|
return void 0;
|
|
51491
52146
|
}
|
|
51492
52147
|
}
|
|
51493
|
-
var
|
|
52148
|
+
var sha2562 = (b) => (0, import_node_crypto19.createHash)("sha256").update(b).digest("hex");
|
|
51494
52149
|
async function proveBundle(url, tag, fetchUrl) {
|
|
51495
52150
|
const get = async (u) => {
|
|
51496
52151
|
try {
|
|
@@ -51502,7 +52157,7 @@ async function proveBundle(url, tag, fetchUrl) {
|
|
|
51502
52157
|
const bundle = await get(url);
|
|
51503
52158
|
if (typeof bundle === "string") return { state: "unprovable", detail: `bundle ${bundle}` };
|
|
51504
52159
|
if (bundle.status !== 200) return { state: "unprovable", detail: `bundle HTTP ${bundle.status}` };
|
|
51505
|
-
const served =
|
|
52160
|
+
const served = sha2562(bundle.body);
|
|
51506
52161
|
const recordUrl = new URL(url);
|
|
51507
52162
|
recordUrl.search = "";
|
|
51508
52163
|
recordUrl.pathname = recordUrl.pathname.replace(/[^/]*$/, "module.json");
|
|
@@ -51745,9 +52400,9 @@ async function waitSuiteDev(deps, opts) {
|
|
|
51745
52400
|
}
|
|
51746
52401
|
|
|
51747
52402
|
// src/suite-train.ts
|
|
51748
|
-
var
|
|
51749
|
-
var
|
|
51750
|
-
var
|
|
52403
|
+
var import_node_fs40 = require("node:fs");
|
|
52404
|
+
var import_node_path37 = require("node:path");
|
|
52405
|
+
var import_node_crypto20 = require("node:crypto");
|
|
51751
52406
|
|
|
51752
52407
|
// src/kit-version.ts
|
|
51753
52408
|
var parse = (v) => /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(typeof v === "string" ? v.trim() : "");
|
|
@@ -51832,7 +52487,7 @@ function suiteReleaseTag(rc) {
|
|
|
51832
52487
|
function readSuiteCut(path2) {
|
|
51833
52488
|
let raw;
|
|
51834
52489
|
try {
|
|
51835
|
-
raw = (0,
|
|
52490
|
+
raw = (0, import_node_fs40.readFileSync)(path2, "utf8");
|
|
51836
52491
|
} catch (e) {
|
|
51837
52492
|
if (e.code === "ENOENT") return void 0;
|
|
51838
52493
|
throw e;
|
|
@@ -51848,11 +52503,11 @@ function readSuiteCut(path2) {
|
|
|
51848
52503
|
return record2;
|
|
51849
52504
|
}
|
|
51850
52505
|
function writeSuiteCut(path2, record2) {
|
|
51851
|
-
(0,
|
|
51852
|
-
const temp = `${path2}.${(0,
|
|
52506
|
+
(0, import_node_fs40.mkdirSync)((0, import_node_path37.dirname)(path2), { recursive: true });
|
|
52507
|
+
const temp = `${path2}.${(0, import_node_crypto20.randomBytes)(8).toString("hex")}.tmp`;
|
|
51853
52508
|
try {
|
|
51854
|
-
(0,
|
|
51855
|
-
(0,
|
|
52509
|
+
(0, import_node_fs40.writeFileSync)(temp, JSON.stringify(record2, null, 2), { flag: "wx" });
|
|
52510
|
+
(0, import_node_fs40.renameSync)(temp, path2);
|
|
51856
52511
|
} catch (e) {
|
|
51857
52512
|
throw new Error(`suite cut record write failed at ${path2}: ${e.message}; stop, verify the file before retrying`);
|
|
51858
52513
|
}
|
|
@@ -51901,25 +52556,25 @@ async function suiteMarkAbandoned(deps, ownerGo = false) {
|
|
|
51901
52556
|
return tags.map((t) => `${t.repo}@${t.tag}`);
|
|
51902
52557
|
}
|
|
51903
52558
|
async function retireFailedLeadRcLedger(deps, ownerGo) {
|
|
51904
|
-
if (!(0,
|
|
52559
|
+
if (!(0, import_node_fs40.existsSync)(deps.ledgerPath)) return void 0;
|
|
51905
52560
|
return withLedgerLock(deps.ledgerPath, async (fence) => {
|
|
51906
|
-
const ledger = parseReleaseLedger((0,
|
|
52561
|
+
const ledger = parseReleaseLedger((0, import_node_fs40.readFileSync)(deps.ledgerPath, "utf8"));
|
|
51907
52562
|
const states = Object.values(ledger.phases).map((p) => p.state);
|
|
51908
52563
|
if (ledger.repo !== SUITE_REPO || ledger.lane !== "rcand" || ledger.phases.promotion.state === "complete" || !states.includes("failed")) return void 0;
|
|
51909
52564
|
if (!ownerGo) throw new Error(`the lead's failed rcand ledger for ${ledger.tag} blocks every train; retire it with suite abandon --owner-go`);
|
|
51910
52565
|
if ((await deps.rcHead())?.toLowerCase() === ledger.tagSha.toLowerCase()) throw new Error(`origin rc already points at ${ledger.tag}; it was promoted, so its ledger is not retired`);
|
|
51911
|
-
const archive = (0,
|
|
51912
|
-
if ((0,
|
|
52566
|
+
const archive = (0, import_node_path37.join)((0, import_node_path37.dirname)(deps.ledgerPath), `phases.archive-${ledger.tag}.json`);
|
|
52567
|
+
if ((0, import_node_fs40.existsSync)(archive)) throw new Error(`ledger archive already exists at ${archive}; inspect before retiring again`);
|
|
51913
52568
|
await deps.markAbandoned(SUITE_REPO, ledger.tag, ledger.tagSha);
|
|
51914
|
-
await fence(() => (0,
|
|
52569
|
+
await fence(() => (0, import_node_fs40.renameSync)(deps.ledgerPath, archive));
|
|
51915
52570
|
return `${ledger.tag} (ledger archived to ${archive})`;
|
|
51916
52571
|
});
|
|
51917
52572
|
}
|
|
51918
52573
|
function suiteAbandon(recordPath, ownerGo = false, now = /* @__PURE__ */ new Date()) {
|
|
51919
52574
|
const record2 = abandonableCut(recordPath, ownerGo);
|
|
51920
52575
|
const archive = `${recordPath}.abandoned-${record2.leadDevelopment.slice(0, 12)}-${now.toISOString().replace(/[:.]/g, "")}.json`;
|
|
51921
|
-
if ((0,
|
|
51922
|
-
(0,
|
|
52576
|
+
if ((0, import_node_fs40.existsSync)(archive)) throw new Error(`abandoned-cut archive already exists at ${archive}; inspect before abandoning again`);
|
|
52577
|
+
(0, import_node_fs40.renameSync)(recordPath, archive);
|
|
51923
52578
|
return { archive, orphanTags: record2.members.filter((m) => m.tag).map((m) => `${m.repo}@${m.tag}`) };
|
|
51924
52579
|
}
|
|
51925
52580
|
async function suiteRestoreMainPins(deps) {
|
|
@@ -52006,12 +52661,12 @@ async function suiteRcandRun(deps, opts) {
|
|
|
52006
52661
|
}
|
|
52007
52662
|
if ((record2?.announcement === "sent" || record2?.announcement === "skipped") && record2.leadReleased) {
|
|
52008
52663
|
const archive = `${deps.recordPath}.${record2.leadRcTag}.json`;
|
|
52009
|
-
if ((0,
|
|
52010
|
-
(0,
|
|
52664
|
+
if ((0, import_node_fs40.existsSync)(archive)) throw new Error(`suite cut archive already exists at ${archive}; inspect before a new cut`);
|
|
52665
|
+
(0, import_node_fs40.renameSync)(deps.recordPath, archive);
|
|
52011
52666
|
record2 = void 0;
|
|
52012
52667
|
}
|
|
52013
52668
|
if (record2 && !record2.pinPr && !record2.leadRcTag && record2.members.every((m) => !m.tag)) {
|
|
52014
|
-
(0,
|
|
52669
|
+
(0, import_node_fs40.rmSync)(deps.recordPath, { force: true });
|
|
52015
52670
|
record2 = void 0;
|
|
52016
52671
|
}
|
|
52017
52672
|
if (!record2) {
|
|
@@ -52065,7 +52720,7 @@ async function suiteRcandRun(deps, opts) {
|
|
|
52065
52720
|
quiet.push(member);
|
|
52066
52721
|
}
|
|
52067
52722
|
if (record2.members.every((m) => m.carried || quiet.includes(m)) && await deps.contains(SUITE_REPO, record2.leadDevelopment, record2.leadPreviousRc)) {
|
|
52068
|
-
(0,
|
|
52723
|
+
(0, import_node_fs40.rmSync)(deps.recordPath, { force: true });
|
|
52069
52724
|
throw new Error("nothing to promote: neither MM-Work nor any module has new development commits since its rc; no suite cut started");
|
|
52070
52725
|
}
|
|
52071
52726
|
for (const member of quiet) {
|
|
@@ -52083,7 +52738,7 @@ async function suiteRcandRun(deps, opts) {
|
|
|
52083
52738
|
}
|
|
52084
52739
|
if (refusals.length) {
|
|
52085
52740
|
const untouched = record2.members.every((m) => !m.tag) && !record2.pinPr;
|
|
52086
|
-
if (untouched) (0,
|
|
52741
|
+
if (untouched) (0, import_node_fs40.rmSync)(deps.recordPath, { force: true });
|
|
52087
52742
|
throw new Error(`module rc serving is not ready; ${untouched ? "no suite cut started" : "no new tag was cut"}:
|
|
52088
52743
|
${refusals.join("\n")}
|
|
52089
52744
|
Each module must ship a live release that bind-mounts <deployPath>/data read-only and serves <data>/rc/module.js at /rc/module.js (docs/Guides/suite-trains.md).`);
|
|
@@ -52275,11 +52930,11 @@ Each member's live release must serve <data>/main/module.js at its main pin path
|
|
|
52275
52930
|
}
|
|
52276
52931
|
return record2;
|
|
52277
52932
|
}
|
|
52278
|
-
var hotfixRecordPath = (deps) => (0,
|
|
52933
|
+
var hotfixRecordPath = (deps) => (0, import_node_path37.join)((0, import_node_path37.dirname)(deps.recordPath), "hotfix.json");
|
|
52279
52934
|
function readSuiteHotfix(path2) {
|
|
52280
52935
|
let raw;
|
|
52281
52936
|
try {
|
|
52282
|
-
raw = (0,
|
|
52937
|
+
raw = (0, import_node_fs40.readFileSync)(path2, "utf8");
|
|
52283
52938
|
} catch (e) {
|
|
52284
52939
|
if (e.code === "ENOENT") return void 0;
|
|
52285
52940
|
throw e;
|
|
@@ -52322,7 +52977,7 @@ async function suiteHotfix(deps, opts) {
|
|
|
52322
52977
|
if (completed) record2 = void 0;
|
|
52323
52978
|
if (record2 && record2.repo !== found.repo) throw new Error(`an unfinished suite hotfix for ${record2.repo} is recorded; finish it (rerun with that member) first`);
|
|
52324
52979
|
if (!record2?.tag) await deps.validateHotfixSummary();
|
|
52325
|
-
if (completed) (0,
|
|
52980
|
+
if (completed) (0, import_node_fs40.renameSync)(path2, `${path2}.${completed.leadTag}.json`);
|
|
52326
52981
|
const rec = record2 ?? { schema: "mmi-suite-hotfix/v1", member: found.id, repo: found.repo, from: opts.from, ...opts.ack?.length ? { ack: opts.ack } : {} };
|
|
52327
52982
|
const save = () => writeSuiteHotfix(path2, rec);
|
|
52328
52983
|
save();
|
|
@@ -52590,9 +53245,9 @@ async function runRcDataSwitch(deps, opts) {
|
|
|
52590
53245
|
|
|
52591
53246
|
// src/suite-train-io.ts
|
|
52592
53247
|
var import_node_child_process19 = require("node:child_process");
|
|
52593
|
-
var import_node_path49 = require("node:path");
|
|
52594
|
-
var import_node_fs53 = require("node:fs");
|
|
52595
53248
|
var import_node_path50 = require("node:path");
|
|
53249
|
+
var import_node_fs54 = require("node:fs");
|
|
53250
|
+
var import_node_path51 = require("node:path");
|
|
52596
53251
|
init_cli_shared();
|
|
52597
53252
|
|
|
52598
53253
|
// src/command-register-train-operations.ts
|
|
@@ -54284,8 +54939,8 @@ function checkHotfixCarries(options) {
|
|
|
54284
54939
|
}
|
|
54285
54940
|
|
|
54286
54941
|
// src/merge-cleanup.ts
|
|
54287
|
-
var
|
|
54288
|
-
var
|
|
54942
|
+
var import_node_fs41 = require("node:fs");
|
|
54943
|
+
var import_node_path38 = require("node:path");
|
|
54289
54944
|
var import_node_os22 = require("node:os");
|
|
54290
54945
|
init_cli_shared();
|
|
54291
54946
|
init_github_client();
|
|
@@ -54693,13 +55348,13 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
|
|
|
54693
55348
|
return rewritten === base ? null : rewritten;
|
|
54694
55349
|
}
|
|
54695
55350
|
function writeSquashBodyFile(body) {
|
|
54696
|
-
const dir = (0,
|
|
54697
|
-
const path2 = (0,
|
|
54698
|
-
(0,
|
|
55351
|
+
const dir = (0, import_node_fs41.mkdtempSync)((0, import_node_path38.join)((0, import_node_os22.tmpdir)(), "mmi-squash-body-"));
|
|
55352
|
+
const path2 = (0, import_node_path38.join)(dir, "body.txt");
|
|
55353
|
+
(0, import_node_fs41.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
|
|
54699
55354
|
`, "utf8");
|
|
54700
55355
|
return { path: path2, cleanup: () => {
|
|
54701
55356
|
try {
|
|
54702
|
-
(0,
|
|
55357
|
+
(0, import_node_fs41.rmSync)(dir, { recursive: true, force: true });
|
|
54703
55358
|
} catch {
|
|
54704
55359
|
}
|
|
54705
55360
|
} };
|
|
@@ -54950,9 +55605,9 @@ async function deleteMergedRemoteBranch(options) {
|
|
|
54950
55605
|
init_github_client();
|
|
54951
55606
|
|
|
54952
55607
|
// src/release-custody.ts
|
|
54953
|
-
var
|
|
54954
|
-
var
|
|
54955
|
-
var
|
|
55608
|
+
var import_node_crypto21 = require("node:crypto");
|
|
55609
|
+
var import_node_fs42 = require("node:fs");
|
|
55610
|
+
var import_node_path39 = require("node:path");
|
|
54956
55611
|
init_github_client();
|
|
54957
55612
|
|
|
54958
55613
|
// src/release-custody-bootstrap.ts
|
|
@@ -55173,7 +55828,7 @@ var ReleaseCustodyError = class extends Error {
|
|
|
55173
55828
|
this.name = "ReleaseCustodyError";
|
|
55174
55829
|
}
|
|
55175
55830
|
};
|
|
55176
|
-
var custodyDigest = (value) => (0,
|
|
55831
|
+
var custodyDigest = (value) => (0, import_node_crypto21.createHash)("sha256").update(value).digest("hex");
|
|
55177
55832
|
function parseCustody(raw, repo) {
|
|
55178
55833
|
const s = JSON.parse(raw);
|
|
55179
55834
|
if (s.schema !== "mmi-release-custody/v1" || s.repo !== repo || !s.precutId || !Number.isSafeInteger(s.revision) || s.revision < 1 || !s.controller || !Number.isSafeInteger(s.controller.runId) || s.controller.runId <= 0 || !Number.isSafeInteger(s.controller.runAttempt) || s.controller.runAttempt <= 0 || !s.controller.token || !(s.ledger === null || typeof s.ledger === "string") || !s.archives || !s.effects) {
|
|
@@ -55261,7 +55916,7 @@ var ReleaseCustodySession = class _ReleaseCustodySession {
|
|
|
55261
55916
|
throw new ReleaseCustodyError(`previous controller run ${found.state.controller.runId} attempt ${found.state.controller.runAttempt} is not proven terminal`);
|
|
55262
55917
|
}
|
|
55263
55918
|
let state = {
|
|
55264
|
-
...found.state ?? { schema: "mmi-release-custody/v1", repo, precutId: (0,
|
|
55919
|
+
...found.state ?? { schema: "mmi-release-custody/v1", repo, precutId: (0, import_node_crypto21.randomUUID)(), effects: {}, ...await initialize() },
|
|
55265
55920
|
revision: (found.state?.revision ?? 0) + 1,
|
|
55266
55921
|
controller
|
|
55267
55922
|
};
|
|
@@ -55322,7 +55977,7 @@ var ReleaseCustodySession = class _ReleaseCustodySession {
|
|
|
55322
55977
|
proof: { kind: "ledger-lifecycle", controller: state.controller }
|
|
55323
55978
|
};
|
|
55324
55979
|
}
|
|
55325
|
-
return { ...state, ledger: raw, archives, precutId: state.ledger && !raw ? (0,
|
|
55980
|
+
return { ...state, ledger: raw, archives, precutId: state.ledger && !raw ? (0, import_node_crypto21.randomUUID)() : state.precutId };
|
|
55326
55981
|
});
|
|
55327
55982
|
}
|
|
55328
55983
|
async preserveLocal(raw) {
|
|
@@ -55422,7 +56077,7 @@ function reclassifySupersededArchives(state) {
|
|
|
55422
56077
|
}
|
|
55423
56078
|
function localRaw(path2) {
|
|
55424
56079
|
try {
|
|
55425
|
-
return (0,
|
|
56080
|
+
return (0, import_node_fs42.readFileSync)(path2, "utf8");
|
|
55426
56081
|
} catch (e) {
|
|
55427
56082
|
if (e.code === "ENOENT") return null;
|
|
55428
56083
|
throw e;
|
|
@@ -55431,14 +56086,14 @@ function localRaw(path2) {
|
|
|
55431
56086
|
function hydrateLocal(path2, raw) {
|
|
55432
56087
|
const previous = localRaw(path2);
|
|
55433
56088
|
if (previous === raw) return;
|
|
55434
|
-
(0,
|
|
56089
|
+
(0, import_node_fs42.mkdirSync)((0, import_node_path39.dirname)(path2), { recursive: true });
|
|
55435
56090
|
if (previous !== null) {
|
|
55436
56091
|
const ledger = parseReleaseLedger(previous);
|
|
55437
|
-
const archive = (0,
|
|
55438
|
-
if (!(0,
|
|
55439
|
-
if (raw === null) (0,
|
|
56092
|
+
const archive = (0, import_node_path39.join)((0, import_node_path39.dirname)(path2), `phases.local-${ledger.tagSha}-${custodyDigest(previous)}.json`);
|
|
56093
|
+
if (!(0, import_node_fs42.existsSync)(archive)) (0, import_node_fs42.writeFileSync)(archive, previous, { flag: "wx" });
|
|
56094
|
+
if (raw === null) (0, import_node_fs42.renameSync)(path2, `${path2}.retired-${custodyDigest(previous)}`);
|
|
55440
56095
|
}
|
|
55441
|
-
if (raw !== null) (0,
|
|
56096
|
+
if (raw !== null) (0, import_node_fs42.writeFileSync)(path2, raw, "utf8");
|
|
55442
56097
|
}
|
|
55443
56098
|
async function commandIdentity(file, args, run, repo) {
|
|
55444
56099
|
if (file === "git" && args[0] === "push") {
|
|
@@ -55548,7 +56203,7 @@ function withHubReleaseCustody(deps) {
|
|
|
55548
56203
|
if (!authority.ok || !authority.train || authority.authenticatedDelegation !== true) {
|
|
55549
56204
|
throw new ReleaseCustodyError("repository custody requires the server-verified owner-triggered train session");
|
|
55550
56205
|
}
|
|
55551
|
-
const controller = { ...runIdentity, token: (0,
|
|
56206
|
+
const controller = { ...runIdentity, token: (0, import_node_crypto21.randomUUID)() };
|
|
55552
56207
|
const raw = localRaw(path2);
|
|
55553
56208
|
const requiredContexts = () => discoverRequiredCheckContexts(
|
|
55554
56209
|
deps,
|
|
@@ -55628,8 +56283,8 @@ function withHubReleaseCustody(deps) {
|
|
|
55628
56283
|
}
|
|
55629
56284
|
|
|
55630
56285
|
// src/project-info-sync.ts
|
|
55631
|
-
var
|
|
55632
|
-
var
|
|
56286
|
+
var import_node_fs43 = require("node:fs");
|
|
56287
|
+
var import_node_path40 = require("node:path");
|
|
55633
56288
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
55634
56289
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
55635
56290
|
projectV2 { id }
|
|
@@ -55676,14 +56331,14 @@ function sharedName(entries, fallback) {
|
|
|
55676
56331
|
}
|
|
55677
56332
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
55678
56333
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
55679
|
-
const readmePath = (0,
|
|
55680
|
-
if (!(0,
|
|
56334
|
+
const readmePath = (0, import_node_path40.join)(repoRoot2, "README.md");
|
|
56335
|
+
if (!(0, import_node_fs43.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
55681
56336
|
const entries = entriesFor(project2, projects);
|
|
55682
56337
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
55683
56338
|
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
55684
56339
|
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
55685
56340
|
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
55686
|
-
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0,
|
|
56341
|
+
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs43.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
|
|
55687
56342
|
const lines2 = [
|
|
55688
56343
|
`# ${projectName}`,
|
|
55689
56344
|
"",
|
|
@@ -55702,8 +56357,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
55702
56357
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
55703
56358
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
55704
56359
|
const orgDocs = [
|
|
55705
|
-
(0,
|
|
55706
|
-
(0,
|
|
56360
|
+
(0, import_node_fs43.existsSync)((0, import_node_path40.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
|
|
56361
|
+
(0, import_node_fs43.existsSync)((0, import_node_path40.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
|
|
55707
56362
|
].filter(Boolean);
|
|
55708
56363
|
if (orgDocs.length) lines2.push("", "## Organisation docs", "", ...orgDocs);
|
|
55709
56364
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines2.join("\n")}
|
|
@@ -55725,8 +56380,8 @@ async function syncProjectInfo(plan, client, apply) {
|
|
|
55725
56380
|
}
|
|
55726
56381
|
|
|
55727
56382
|
// src/train-commands.ts
|
|
55728
|
-
var
|
|
55729
|
-
var
|
|
56383
|
+
var import_node_fs44 = require("node:fs");
|
|
56384
|
+
var import_node_path41 = require("node:path");
|
|
55730
56385
|
init_cli_shared();
|
|
55731
56386
|
init_clean_exit();
|
|
55732
56387
|
init_client_version();
|
|
@@ -55741,7 +56396,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
55741
56396
|
}
|
|
55742
56397
|
function readRepoVersion() {
|
|
55743
56398
|
try {
|
|
55744
|
-
return JSON.parse((0,
|
|
56399
|
+
return JSON.parse((0, import_node_fs44.readFileSync)((0, import_node_path41.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
55745
56400
|
} catch {
|
|
55746
56401
|
return void 0;
|
|
55747
56402
|
}
|
|
@@ -55852,7 +56507,7 @@ function releaseProgressVerdict(status, subject = "Release") {
|
|
|
55852
56507
|
|
|
55853
56508
|
// src/suite-pin.ts
|
|
55854
56509
|
var import_promises6 = require("node:fs/promises");
|
|
55855
|
-
var
|
|
56510
|
+
var import_node_path42 = require("node:path");
|
|
55856
56511
|
var SuitePinError = class _SuitePinError extends Error {
|
|
55857
56512
|
diagnostic;
|
|
55858
56513
|
constructor(repo, tag, error) {
|
|
@@ -55920,8 +56575,8 @@ async function openSuiteCutPinPr(deps, input) {
|
|
|
55920
56575
|
if (input.carriedOnly) return { status: "unchanged" };
|
|
55921
56576
|
throw new Error("pins already match without a merged cut PR \u2014 provenance unverified");
|
|
55922
56577
|
}
|
|
55923
|
-
await (0, import_promises6.mkdir)((0,
|
|
55924
|
-
scratch = await (0, import_promises6.mkdtemp)((0,
|
|
56578
|
+
await (0, import_promises6.mkdir)((0, import_node_path42.join)(deps.root, "tmp"), { recursive: true });
|
|
56579
|
+
scratch = await (0, import_promises6.mkdtemp)((0, import_node_path42.join)(deps.root, "tmp", "suite-pin-"));
|
|
55925
56580
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
55926
56581
|
await run("git", ["clone", "--quiet", "--branch", prs.length ? branch : "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
55927
56582
|
if (prs.length) {
|
|
@@ -55933,7 +56588,7 @@ async function openSuiteCutPinPr(deps, input) {
|
|
|
55933
56588
|
await run("git", ["switch", "-c", branch]);
|
|
55934
56589
|
}
|
|
55935
56590
|
for (const stage of ["rc", "main"]) {
|
|
55936
|
-
const path2 = (0,
|
|
56591
|
+
const path2 = (0, import_node_path42.join)(scratch, `${stage}-pins.json`);
|
|
55937
56592
|
await (0, import_promises6.writeFile)(path2, JSON.stringify(input.pins.map((p) => ({ module: p.module, url: p[stage] }))));
|
|
55938
56593
|
await run("node", ["scripts/pin-modules.mjs", "--stage", stage, "--pins", path2]);
|
|
55939
56594
|
}
|
|
@@ -55986,12 +56641,12 @@ async function openSuiteRestorePinPr(deps, input) {
|
|
|
55986
56641
|
}
|
|
55987
56642
|
});
|
|
55988
56643
|
if (!todo.length) return void 0;
|
|
55989
|
-
await (0, import_promises6.mkdir)((0,
|
|
55990
|
-
scratch = await (0, import_promises6.mkdtemp)((0,
|
|
56644
|
+
await (0, import_promises6.mkdir)((0, import_node_path42.join)(deps.root, "tmp"), { recursive: true });
|
|
56645
|
+
scratch = await (0, import_promises6.mkdtemp)((0, import_node_path42.join)(deps.root, "tmp", "suite-pin-"));
|
|
55991
56646
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
55992
56647
|
await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
55993
56648
|
await run("git", ["switch", "-c", branch]);
|
|
55994
|
-
const path2 = (0,
|
|
56649
|
+
const path2 = (0, import_node_path42.join)(scratch, "main-pins.json");
|
|
55995
56650
|
await (0, import_promises6.writeFile)(path2, JSON.stringify(todo.map((p) => ({ module: p.module, url: p.prior }))));
|
|
55996
56651
|
await run("node", ["scripts/pin-modules.mjs", "--stage", "main", "--pins", path2]);
|
|
55997
56652
|
await run("git", ["add", "seed/modules.json"]);
|
|
@@ -56065,8 +56720,8 @@ async function openSuitePinPr(deps, input) {
|
|
|
56065
56720
|
if (newer.length) return { status: "superseded", pr: newer[0].url, url: pin };
|
|
56066
56721
|
const open2 = onBranch.find((p) => p.state === "OPEN");
|
|
56067
56722
|
if (open2 && tagOf(open2.title ?? "") === tag) return { status: "pending", pr: open2.url, url: pin };
|
|
56068
|
-
await (0, import_promises6.mkdir)((0,
|
|
56069
|
-
scratch = await (0, import_promises6.mkdtemp)((0,
|
|
56723
|
+
await (0, import_promises6.mkdir)((0, import_node_path42.join)(deps.root, "tmp"), { recursive: true });
|
|
56724
|
+
scratch = await (0, import_promises6.mkdtemp)((0, import_node_path42.join)(deps.root, "tmp", "suite-pin-"));
|
|
56070
56725
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
56071
56726
|
await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
56072
56727
|
await run("git", ["switch", "-c", branch]);
|
|
@@ -56118,7 +56773,7 @@ function prTitleRefusal(title) {
|
|
|
56118
56773
|
|
|
56119
56774
|
// src/command-register-collaboration.ts
|
|
56120
56775
|
var import_node_child_process18 = require("node:child_process");
|
|
56121
|
-
var
|
|
56776
|
+
var import_node_fs53 = require("node:fs");
|
|
56122
56777
|
var import_promises8 = require("node:fs/promises");
|
|
56123
56778
|
init_clean_exit();
|
|
56124
56779
|
init_cli_shared();
|
|
@@ -56142,7 +56797,7 @@ function spawnDetachedSelf(args, deps, opts = {}) {
|
|
|
56142
56797
|
}
|
|
56143
56798
|
|
|
56144
56799
|
// src/command-register-collaboration.ts
|
|
56145
|
-
var
|
|
56800
|
+
var import_node_path49 = require("node:path");
|
|
56146
56801
|
init_house_map();
|
|
56147
56802
|
|
|
56148
56803
|
// src/issue-body.ts
|
|
@@ -56259,8 +56914,8 @@ init_github_client();
|
|
|
56259
56914
|
init_github_quota();
|
|
56260
56915
|
|
|
56261
56916
|
// src/issue-commands.ts
|
|
56262
|
-
var
|
|
56263
|
-
var
|
|
56917
|
+
var import_node_fs45 = require("node:fs");
|
|
56918
|
+
var import_node_crypto22 = require("node:crypto");
|
|
56264
56919
|
init_cli_shared();
|
|
56265
56920
|
init_clean_exit();
|
|
56266
56921
|
init_error_codes();
|
|
@@ -56452,7 +57107,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
56452
57107
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
56453
57108
|
const patch = {};
|
|
56454
57109
|
let bodyChanged = false;
|
|
56455
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
57110
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs45.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
56456
57111
|
if (options.titleFile !== void 0) {
|
|
56457
57112
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
56458
57113
|
} else if (options.title !== void 0) {
|
|
@@ -56737,7 +57392,7 @@ function rowIdempotencyKey(batchKey, spec) {
|
|
|
56737
57392
|
const identity = `${spec.type}
|
|
56738
57393
|
${spec.title.trim()}
|
|
56739
57394
|
${spec.body ?? ""}`;
|
|
56740
|
-
const hash = (0,
|
|
57395
|
+
const hash = (0, import_node_crypto22.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
|
|
56741
57396
|
return `${batchKey}:${hash}`;
|
|
56742
57397
|
}
|
|
56743
57398
|
var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
|
|
@@ -57119,7 +57774,7 @@ function extendCreateCommand(issue, batchAttach) {
|
|
|
57119
57774
|
if (opts.batch) {
|
|
57120
57775
|
let specs;
|
|
57121
57776
|
try {
|
|
57122
|
-
const raw = (0,
|
|
57777
|
+
const raw = (0, import_node_fs45.readFileSync)(opts.batch, "utf8");
|
|
57123
57778
|
specs = JSON.parse(raw);
|
|
57124
57779
|
if (!Array.isArray(specs)) {
|
|
57125
57780
|
const top = specs === null ? "null" : typeof specs === "object" ? `an object with keys: ${Object.keys(specs).join(", ") || "(none)"}` : `a ${typeof specs}`;
|
|
@@ -57196,8 +57851,8 @@ ${lines2}`, {
|
|
|
57196
57851
|
}
|
|
57197
57852
|
|
|
57198
57853
|
// src/post-merge-recon.ts
|
|
57199
|
-
var
|
|
57200
|
-
var
|
|
57854
|
+
var import_node_fs46 = require("node:fs");
|
|
57855
|
+
var import_node_path43 = require("node:path");
|
|
57201
57856
|
|
|
57202
57857
|
// src/cross-repo-filing-issue.ts
|
|
57203
57858
|
init_github_client();
|
|
@@ -57367,16 +58022,16 @@ function buildPostMergeReconRecovery(input) {
|
|
|
57367
58022
|
}
|
|
57368
58023
|
function writePostMergeReconRecovery(cwd, recovery) {
|
|
57369
58024
|
const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
|
|
57370
|
-
(0,
|
|
57371
|
-
(0,
|
|
58025
|
+
(0, import_node_fs46.mkdirSync)((0, import_node_path43.dirname)(path2), { recursive: true });
|
|
58026
|
+
(0, import_node_fs46.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
|
|
57372
58027
|
`, "utf8");
|
|
57373
58028
|
return path2;
|
|
57374
58029
|
}
|
|
57375
58030
|
function clearPostMergeReconRecovery(cwd, repo, pr) {
|
|
57376
58031
|
const path2 = postMergeReconStatePath(cwd, repo, pr);
|
|
57377
|
-
if (!(0,
|
|
58032
|
+
if (!(0, import_node_fs46.existsSync)(path2)) return;
|
|
57378
58033
|
try {
|
|
57379
|
-
(0,
|
|
58034
|
+
(0, import_node_fs46.unlinkSync)(path2);
|
|
57380
58035
|
} catch {
|
|
57381
58036
|
}
|
|
57382
58037
|
}
|
|
@@ -57411,7 +58066,7 @@ var import_promises7 = require("node:fs/promises");
|
|
|
57411
58066
|
init_cli_shared();
|
|
57412
58067
|
|
|
57413
58068
|
// src/api-read.ts
|
|
57414
|
-
var
|
|
58069
|
+
var import_node_fs47 = require("node:fs");
|
|
57415
58070
|
init_cli_shared();
|
|
57416
58071
|
var API_ACCEPT_HEADERS = {
|
|
57417
58072
|
json: "application/vnd.github+json",
|
|
@@ -57676,7 +58331,7 @@ function registerApiReadCommands(program3, overrides) {
|
|
|
57676
58331
|
if (req.workflowRuns) body = sliceWorkflowRunsPage(body, req.workflowRuns);
|
|
57677
58332
|
if (o.jq !== void 0) body = await jqRun(o.jq, body, localJqEnv(), ...bindings.length ? [bindings] : []);
|
|
57678
58333
|
if (zip) {
|
|
57679
|
-
(0,
|
|
58334
|
+
(0, import_node_fs47.writeFileSync)(o.out, body, "latin1");
|
|
57680
58335
|
return;
|
|
57681
58336
|
}
|
|
57682
58337
|
} catch (e) {
|
|
@@ -58088,7 +58743,7 @@ function parseBrokerPrState(raw) {
|
|
|
58088
58743
|
function buildPrListSpec(o) {
|
|
58089
58744
|
const repo = parseBrokerRepo(o.repo, "--repo");
|
|
58090
58745
|
const fields = rejectPrViewQueueFields(parseBrokerJsonFields(o.json), "<number>", repo, "list");
|
|
58091
|
-
const args = withJson(withRepo(["pr", "list"], repo), fields, o.json);
|
|
58746
|
+
const args = withJson(withRepo(["pr", "list"], repo), fields, o.json, PR_LIST_JSON_FIELDS);
|
|
58092
58747
|
const state = parseBrokerPrState(o.state);
|
|
58093
58748
|
if (state) args.push("--state", state);
|
|
58094
58749
|
for (const flag of ["base", "head"]) {
|
|
@@ -58107,9 +58762,11 @@ function buildPrListSpec(o) {
|
|
|
58107
58762
|
function withRepo(args, repo) {
|
|
58108
58763
|
return repo ? [...args, "--repo", repo] : args;
|
|
58109
58764
|
}
|
|
58110
|
-
function withJson(args, jsonFields, jsonFlag) {
|
|
58765
|
+
function withJson(args, jsonFields, jsonFlag, bareDefault) {
|
|
58111
58766
|
if (jsonFlag === void 0 || jsonFlag === false) return args;
|
|
58112
|
-
|
|
58767
|
+
const fields = typeof jsonFields === "string" ? jsonFields : bareDefault;
|
|
58768
|
+
if (fields === void 0) throw new Error(`read: --json needs a comma-separated field list for gh ${args.slice(0, 2).join(" ")} (e.g. --json number,title,state)`);
|
|
58769
|
+
return [...args, "--json", fields];
|
|
58113
58770
|
}
|
|
58114
58771
|
function buildRunViewSpec(id, o) {
|
|
58115
58772
|
const args = withJson(withRepo(["run", "view", parseBrokerId(id, "run id"), ...o.attempt !== void 0 ? ["--attempt", String(parseBrokerInt(o.attempt, "--attempt", 1, 1e3))] : []], parseBrokerRepo(o.repo, "--repo")), parseBrokerJsonFields(o.json), o.json);
|
|
@@ -58127,8 +58784,11 @@ function parseBrokerCommit(v) {
|
|
|
58127
58784
|
if (!/^[0-9a-f]{40}$/i.test(v)) throw new Error(`read: --commit must be a full 40-character commit SHA (received '${v}')`);
|
|
58128
58785
|
return v;
|
|
58129
58786
|
}
|
|
58787
|
+
var PR_LIST_JSON_FIELDS = "number,title,state,isDraft,headRefName,baseRefName,author,createdAt,updatedAt,url";
|
|
58788
|
+
var ISSUE_LIST_JSON_FIELDS = "number,title,state,labels,assignees,createdAt,updatedAt,url";
|
|
58789
|
+
var RUN_LIST_JSON_FIELDS = "attempt,conclusion,createdAt,databaseId,displayTitle,event,headBranch,headSha,name,number,startedAt,status,updatedAt,url,workflowDatabaseId,workflowName";
|
|
58130
58790
|
function buildRunListSpec(o) {
|
|
58131
|
-
const args = withJson(withRepo(["run", "list"], parseBrokerRepo(o.repo, "--repo")), parseBrokerJsonFields(o.json), o.json);
|
|
58791
|
+
const args = withJson(withRepo(["run", "list"], parseBrokerRepo(o.repo, "--repo")), parseBrokerJsonFields(o.json), o.json, RUN_LIST_JSON_FIELDS);
|
|
58132
58792
|
const workflow = parseBrokerWorkflow(o.workflow);
|
|
58133
58793
|
if (workflow) args.push("--workflow", workflow);
|
|
58134
58794
|
const branch = parseBrokerBranch(o.branch);
|
|
@@ -58211,7 +58871,7 @@ function parseBrokerLabel(raw) {
|
|
|
58211
58871
|
return raw;
|
|
58212
58872
|
}
|
|
58213
58873
|
function buildIssueListSpec(o) {
|
|
58214
|
-
const args = withJson(withRepo(["issue", "list"], parseBrokerRepo(o.repo, "--repo")), parseBrokerJsonFields(o.json), o.json);
|
|
58874
|
+
const args = withJson(withRepo(["issue", "list"], parseBrokerRepo(o.repo, "--repo")), parseBrokerJsonFields(o.json), o.json, ISSUE_LIST_JSON_FIELDS);
|
|
58215
58875
|
const search = parseBrokerSearch(o.search);
|
|
58216
58876
|
if (search !== void 0) args.push("--search", search);
|
|
58217
58877
|
const assignee = parseBrokerAssignee(o.assignee);
|
|
@@ -59212,9 +59872,9 @@ function registerPrLifecycleCommands(program3) {
|
|
|
59212
59872
|
|
|
59213
59873
|
// src/review-verdict.ts
|
|
59214
59874
|
var import_node_child_process15 = require("node:child_process");
|
|
59215
|
-
var
|
|
59875
|
+
var import_node_fs48 = require("node:fs");
|
|
59216
59876
|
var import_node_os24 = require("node:os");
|
|
59217
|
-
var
|
|
59877
|
+
var import_node_path44 = require("node:path");
|
|
59218
59878
|
init_cli_shared();
|
|
59219
59879
|
var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
|
|
59220
59880
|
var REVIEW_VERDICTS = ["PROCEED", "CORRECT", "ESCALATE"];
|
|
@@ -59379,15 +60039,15 @@ async function readPrIssueComments(number, repo) {
|
|
|
59379
60039
|
return comments;
|
|
59380
60040
|
}
|
|
59381
60041
|
async function postPrCommentFromFile(number, repo, body) {
|
|
59382
|
-
const dir = (0,
|
|
59383
|
-
const path2 = (0,
|
|
60042
|
+
const dir = (0, import_node_fs48.mkdtempSync)((0, import_node_path44.join)((0, import_node_os24.tmpdir)(), "mmi-review-verdict-"));
|
|
60043
|
+
const path2 = (0, import_node_path44.join)(dir, "body.md");
|
|
59384
60044
|
try {
|
|
59385
|
-
(0,
|
|
60045
|
+
(0, import_node_fs48.writeFileSync)(path2, body, "utf8");
|
|
59386
60046
|
const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
59387
60047
|
return stdout.trim();
|
|
59388
60048
|
} finally {
|
|
59389
60049
|
try {
|
|
59390
|
-
(0,
|
|
60050
|
+
(0, import_node_fs48.rmSync)(dir, { recursive: true, force: true });
|
|
59391
60051
|
} catch {
|
|
59392
60052
|
}
|
|
59393
60053
|
}
|
|
@@ -59800,18 +60460,18 @@ function createPreparationDeps(input, io = {}) {
|
|
|
59800
60460
|
}
|
|
59801
60461
|
|
|
59802
60462
|
// src/worktree-residue-sweep.ts
|
|
59803
|
-
var
|
|
59804
|
-
var
|
|
60463
|
+
var import_node_fs52 = require("node:fs");
|
|
60464
|
+
var import_node_path48 = require("node:path");
|
|
59805
60465
|
|
|
59806
60466
|
// src/worktree-merge-cleanup.ts
|
|
59807
|
-
var
|
|
59808
|
-
var
|
|
60467
|
+
var import_node_fs51 = require("node:fs");
|
|
60468
|
+
var import_node_path47 = require("node:path");
|
|
59809
60469
|
init_cli_shared();
|
|
59810
60470
|
|
|
59811
60471
|
// src/stage-runner.ts
|
|
59812
60472
|
var import_node_child_process17 = require("node:child_process");
|
|
59813
|
-
var
|
|
59814
|
-
var
|
|
60473
|
+
var import_node_fs50 = require("node:fs");
|
|
60474
|
+
var import_node_path46 = require("node:path");
|
|
59815
60475
|
var import_node_net = require("node:net");
|
|
59816
60476
|
var import_node_util5 = require("node:util");
|
|
59817
60477
|
|
|
@@ -59821,8 +60481,8 @@ function normalizeEol2(s) {
|
|
|
59821
60481
|
}
|
|
59822
60482
|
|
|
59823
60483
|
// src/env-detection.ts
|
|
59824
|
-
var
|
|
59825
|
-
var
|
|
60484
|
+
var import_node_fs49 = require("node:fs");
|
|
60485
|
+
var import_node_path45 = require("node:path");
|
|
59826
60486
|
var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".jerv", ".pi", ".venv", "venv"]);
|
|
59827
60487
|
function isEnvBaseName(name) {
|
|
59828
60488
|
return name.toLowerCase() === ".env";
|
|
@@ -59832,7 +60492,7 @@ function findEnvFiles(root) {
|
|
|
59832
60492
|
const walk2 = (dir, rel) => {
|
|
59833
60493
|
let entries;
|
|
59834
60494
|
try {
|
|
59835
|
-
entries = (0,
|
|
60495
|
+
entries = (0, import_node_fs49.readdirSync)(dir, { withFileTypes: true });
|
|
59836
60496
|
} catch {
|
|
59837
60497
|
return;
|
|
59838
60498
|
}
|
|
@@ -59840,13 +60500,13 @@ function findEnvFiles(root) {
|
|
|
59840
60500
|
const relPath = rel ? `${rel}/${entry.name}` : entry.name;
|
|
59841
60501
|
if (entry.isDirectory()) {
|
|
59842
60502
|
if (SKIPPED_DIRS.has(entry.name)) continue;
|
|
59843
|
-
walk2((0,
|
|
60503
|
+
walk2((0, import_node_path45.join)(dir, entry.name), relPath);
|
|
59844
60504
|
continue;
|
|
59845
60505
|
}
|
|
59846
60506
|
if (isEnvBaseName(entry.name)) {
|
|
59847
60507
|
let isDir = false;
|
|
59848
60508
|
try {
|
|
59849
|
-
isDir = (0,
|
|
60509
|
+
isDir = (0, import_node_fs49.lstatSync)((0, import_node_path45.join)(dir, entry.name)).isDirectory();
|
|
59850
60510
|
} catch {
|
|
59851
60511
|
}
|
|
59852
60512
|
if (!isDir) found.push(relPath);
|
|
@@ -59985,12 +60645,12 @@ var DOCKER_TIMEOUT_MS = 15e3;
|
|
|
59985
60645
|
function stageBash() {
|
|
59986
60646
|
if (process.platform !== "win32") return "bash";
|
|
59987
60647
|
const configured = process.env.SHELL;
|
|
59988
|
-
if (configured && (0,
|
|
60648
|
+
if (configured && (0, import_node_path46.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs50.existsSync)(configured)) return configured;
|
|
59989
60649
|
const candidates = [
|
|
59990
|
-
(0,
|
|
59991
|
-
...process.env.LOCALAPPDATA ? [(0,
|
|
60650
|
+
(0, import_node_path46.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
|
|
60651
|
+
...process.env.LOCALAPPDATA ? [(0, import_node_path46.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
|
|
59992
60652
|
];
|
|
59993
|
-
const bash = candidates.find((path2) => (0,
|
|
60653
|
+
const bash = candidates.find((path2) => (0, import_node_fs50.existsSync)(path2));
|
|
59994
60654
|
if (!bash) throw new Error("Local stages require Git Bash on Windows. Install Git for Windows or set SHELL to its absolute bash.exe path.");
|
|
59995
60655
|
return bash;
|
|
59996
60656
|
}
|
|
@@ -60128,7 +60788,7 @@ function appendForceRecreate(up) {
|
|
|
60128
60788
|
return `${up.trimEnd()} --force-recreate`;
|
|
60129
60789
|
}
|
|
60130
60790
|
function stageStatePath(cwd = process.cwd()) {
|
|
60131
|
-
return (0,
|
|
60791
|
+
return (0, import_node_path46.join)(cwd, "tmp", "stage", "state.json");
|
|
60132
60792
|
}
|
|
60133
60793
|
function normPath(path2) {
|
|
60134
60794
|
return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
@@ -60345,8 +61005,8 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
60345
61005
|
}
|
|
60346
61006
|
function composeResolvesPort(cwd) {
|
|
60347
61007
|
if (process.env.PORT) return true;
|
|
60348
|
-
const envFile = (0,
|
|
60349
|
-
return (0,
|
|
61008
|
+
const envFile = (0, import_node_path46.join)(cwd, ".env");
|
|
61009
|
+
return (0, import_node_fs50.existsSync)(envFile) && envFileKeys((0, import_node_fs50.readFileSync)(envFile, "utf8")).has("PORT");
|
|
60350
61010
|
}
|
|
60351
61011
|
function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
60352
61012
|
const composeProject = config.env?.COMPOSE_PROJECT_NAME ?? vaultEnvMerge?.COMPOSE_PROJECT_NAME ?? stageComposeProjectName(cwd);
|
|
@@ -60361,13 +61021,13 @@ function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
|
60361
61021
|
function stageComposeFiles(cwd) {
|
|
60362
61022
|
const files = ["docker-compose.yml"];
|
|
60363
61023
|
for (const conventional of ["docker-compose.override.yml", "docker-compose.override.yaml"]) {
|
|
60364
|
-
if ((0,
|
|
61024
|
+
if ((0, import_node_fs50.existsSync)((0, import_node_path46.join)(cwd, conventional))) files.push(conventional);
|
|
60365
61025
|
}
|
|
60366
61026
|
return [...files, RUNTIME_ENV_OVERRIDE_RELPATH];
|
|
60367
61027
|
}
|
|
60368
61028
|
async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
60369
61029
|
const keys = Object.keys(vaultEnvMerge ?? {});
|
|
60370
|
-
if (!keys.length || !(0,
|
|
61030
|
+
if (!keys.length || !(0, import_node_fs50.existsSync)((0, import_node_path46.join)(cwd, "docker-compose.yml"))) return {};
|
|
60371
61031
|
let configJson;
|
|
60372
61032
|
try {
|
|
60373
61033
|
const { stdout } = await execFileP3("docker", ["compose", "config", "--format", "json"], {
|
|
@@ -60383,21 +61043,21 @@ async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
|
60383
61043
|
}
|
|
60384
61044
|
const body = renderRuntimeEnvOverride(runtimeEnvMarkedServices(configJson), keys, runtimeEnvExcludes(configJson), runtimeEnvSelects(configJson));
|
|
60385
61045
|
if (!body) return {};
|
|
60386
|
-
const overridePath = (0,
|
|
60387
|
-
(0,
|
|
60388
|
-
(0,
|
|
61046
|
+
const overridePath = (0, import_node_path46.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
|
|
61047
|
+
(0, import_node_fs50.mkdirSync)((0, import_node_path46.join)(cwd, "tmp", "stage"), { recursive: true });
|
|
61048
|
+
(0, import_node_fs50.writeFileSync)(overridePath, body, "utf8");
|
|
60389
61049
|
return stageComposeFileEnv(stageComposeFiles(cwd));
|
|
60390
61050
|
}
|
|
60391
61051
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
60392
61052
|
if (!config.ensureEnv) return;
|
|
60393
|
-
const target = (0,
|
|
60394
|
-
const example = (0,
|
|
60395
|
-
if (!(0,
|
|
60396
|
-
(0,
|
|
60397
|
-
} else if ((0,
|
|
60398
|
-
const stale = detectStaleEnvFile((0,
|
|
60399
|
-
exampleMtimeMs: (0,
|
|
60400
|
-
targetMtimeMs: (0,
|
|
61053
|
+
const target = (0, import_node_path46.join)(cwd, config.ensureEnv.target);
|
|
61054
|
+
const example = (0, import_node_path46.join)(cwd, config.ensureEnv.example);
|
|
61055
|
+
if (!(0, import_node_fs50.existsSync)(target) && (0, import_node_fs50.existsSync)(example)) {
|
|
61056
|
+
(0, import_node_fs50.copyFileSync)(example, target);
|
|
61057
|
+
} else if ((0, import_node_fs50.existsSync)(target) && (0, import_node_fs50.existsSync)(example)) {
|
|
61058
|
+
const stale = detectStaleEnvFile((0, import_node_fs50.readFileSync)(example, "utf8"), (0, import_node_fs50.readFileSync)(target, "utf8"), {
|
|
61059
|
+
exampleMtimeMs: (0, import_node_fs50.statSync)(example).mtimeMs,
|
|
61060
|
+
targetMtimeMs: (0, import_node_fs50.statSync)(target).mtimeMs
|
|
60401
61061
|
});
|
|
60402
61062
|
if (stale) {
|
|
60403
61063
|
const msg = `stale ${config.ensureEnv.target} (${stale}) \u2014 delete it or refresh from ${config.ensureEnv.example} before re-running mmi-cli stage`;
|
|
@@ -60405,8 +61065,8 @@ async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
|
60405
61065
|
console.error(`mmi-cli stage: ${msg} (allowed via --allow-stale-env)`);
|
|
60406
61066
|
}
|
|
60407
61067
|
}
|
|
60408
|
-
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0,
|
|
60409
|
-
(0,
|
|
61068
|
+
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0, import_node_fs50.existsSync)(target)) {
|
|
61069
|
+
(0, import_node_fs50.writeFileSync)(target, mergeEnvSecretsIntoFile((0, import_node_fs50.readFileSync)(target, "utf8"), opts.vaultEnvMerge), "utf8");
|
|
60410
61070
|
}
|
|
60411
61071
|
}
|
|
60412
61072
|
async function gitText(cwd, args) {
|
|
@@ -60436,20 +61096,20 @@ async function resolveGlobalStatePath(cwd, explicit) {
|
|
|
60436
61096
|
return void 0;
|
|
60437
61097
|
}
|
|
60438
61098
|
function readState(path2) {
|
|
60439
|
-
if (!(0,
|
|
61099
|
+
if (!(0, import_node_fs50.existsSync)(path2)) return null;
|
|
60440
61100
|
try {
|
|
60441
|
-
return JSON.parse((0,
|
|
61101
|
+
return JSON.parse((0, import_node_fs50.readFileSync)(path2, "utf8"));
|
|
60442
61102
|
} catch {
|
|
60443
61103
|
return null;
|
|
60444
61104
|
}
|
|
60445
61105
|
}
|
|
60446
61106
|
function mkdirFor(path2) {
|
|
60447
61107
|
const dir = path2.slice(0, Math.max(path2.lastIndexOf("/"), path2.lastIndexOf("\\")));
|
|
60448
|
-
(0,
|
|
61108
|
+
(0, import_node_fs50.mkdirSync)(dir, { recursive: true });
|
|
60449
61109
|
}
|
|
60450
61110
|
function writeState(path2, state) {
|
|
60451
61111
|
mkdirFor(path2);
|
|
60452
|
-
(0,
|
|
61112
|
+
(0, import_node_fs50.writeFileSync)(path2, JSON.stringify(state, null, 2), "utf8");
|
|
60453
61113
|
}
|
|
60454
61114
|
function writeStagePortReservation(port, cwd, statePath, globalStatePath, now) {
|
|
60455
61115
|
const reservation = {
|
|
@@ -60478,7 +61138,7 @@ async function cleanupStageState(state, paths, timeoutMs, fallbackCwd, currentEn
|
|
|
60478
61138
|
);
|
|
60479
61139
|
}
|
|
60480
61140
|
for (const path2 of [...new Set(paths.filter((p) => Boolean(p)))]) {
|
|
60481
|
-
(0,
|
|
61141
|
+
(0, import_node_fs50.rmSync)(path2, { force: true });
|
|
60482
61142
|
}
|
|
60483
61143
|
}
|
|
60484
61144
|
async function killTree(pid) {
|
|
@@ -60725,8 +61385,8 @@ async function runStage(config = {}, opts = {}) {
|
|
|
60725
61385
|
});
|
|
60726
61386
|
}
|
|
60727
61387
|
} catch (e) {
|
|
60728
|
-
(0,
|
|
60729
|
-
if (globalStatePath && globalStatePath !== statePath) (0,
|
|
61388
|
+
(0, import_node_fs50.rmSync)(statePath, { force: true });
|
|
61389
|
+
if (globalStatePath && globalStatePath !== statePath) (0, import_node_fs50.rmSync)(globalStatePath, { force: true });
|
|
60730
61390
|
throw e;
|
|
60731
61391
|
}
|
|
60732
61392
|
const started = await startStage(config, {
|
|
@@ -60745,9 +61405,9 @@ function normPath2(p) {
|
|
|
60745
61405
|
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
60746
61406
|
}
|
|
60747
61407
|
function unlinkNodeModulesJunction(wtPath) {
|
|
60748
|
-
const nm = (0,
|
|
61408
|
+
const nm = (0, import_node_path47.join)(wtPath, "node_modules");
|
|
60749
61409
|
try {
|
|
60750
|
-
if ((0,
|
|
61410
|
+
if ((0, import_node_fs51.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs51.rmdirSync)(nm);
|
|
60751
61411
|
return { ok: true };
|
|
60752
61412
|
} catch (e) {
|
|
60753
61413
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -60758,8 +61418,8 @@ function defaultSleep2(ms) {
|
|
|
60758
61418
|
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
60759
61419
|
}
|
|
60760
61420
|
async function removeResidueDirectory(wtPath, options = {}) {
|
|
60761
|
-
const { sleep: sleep2 = defaultSleep2, removeEmptyDir =
|
|
60762
|
-
const removeRecursive = options.removeRecursive ?? ((path2) => (0,
|
|
61421
|
+
const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs51.rmdirSync, exists = import_node_fs51.existsSync } = options;
|
|
61422
|
+
const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs51.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
|
|
60763
61423
|
const junction = unlinkNodeModulesJunction(wtPath);
|
|
60764
61424
|
if (!junction.ok) return junction;
|
|
60765
61425
|
try {
|
|
@@ -60882,28 +61542,28 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
|
|
|
60882
61542
|
}
|
|
60883
61543
|
async function listNestedIgnoredNodeModules(wtPath, execGit) {
|
|
60884
61544
|
const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
|
|
60885
|
-
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0,
|
|
61545
|
+
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path47.join)(wtPath, line.slice(0, -1)));
|
|
60886
61546
|
}
|
|
60887
61547
|
function safeRemoveTree(path2) {
|
|
60888
|
-
const stat4 = (0,
|
|
61548
|
+
const stat4 = (0, import_node_fs51.lstatSync)(path2);
|
|
60889
61549
|
if (stat4.isSymbolicLink()) {
|
|
60890
61550
|
try {
|
|
60891
|
-
(0,
|
|
61551
|
+
(0, import_node_fs51.rmdirSync)(path2);
|
|
60892
61552
|
} catch {
|
|
60893
|
-
(0,
|
|
61553
|
+
(0, import_node_fs51.unlinkSync)(path2);
|
|
60894
61554
|
}
|
|
60895
61555
|
return;
|
|
60896
61556
|
}
|
|
60897
61557
|
if (stat4.isDirectory()) {
|
|
60898
|
-
for (const entry of (0,
|
|
60899
|
-
(0,
|
|
61558
|
+
for (const entry of (0, import_node_fs51.readdirSync)(path2)) safeRemoveTree((0, import_node_path47.join)(path2, entry));
|
|
61559
|
+
(0, import_node_fs51.rmdirSync)(path2);
|
|
60900
61560
|
return;
|
|
60901
61561
|
}
|
|
60902
|
-
(0,
|
|
61562
|
+
(0, import_node_fs51.unlinkSync)(path2);
|
|
60903
61563
|
}
|
|
60904
61564
|
async function removeWorktreeNodeModules(wtPath) {
|
|
60905
61565
|
try {
|
|
60906
|
-
safeRemoveTree((0,
|
|
61566
|
+
safeRemoveTree((0, import_node_path47.join)(wtPath, "node_modules"));
|
|
60907
61567
|
return { ok: true };
|
|
60908
61568
|
} catch (e) {
|
|
60909
61569
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -60915,7 +61575,7 @@ function errorMessage(e) {
|
|
|
60915
61575
|
}
|
|
60916
61576
|
function resolvedOrRaw(path2) {
|
|
60917
61577
|
try {
|
|
60918
|
-
return normPath2((0,
|
|
61578
|
+
return normPath2((0, import_node_fs51.realpathSync)(path2));
|
|
60919
61579
|
} catch {
|
|
60920
61580
|
return normPath2(path2);
|
|
60921
61581
|
}
|
|
@@ -60923,10 +61583,10 @@ function resolvedOrRaw(path2) {
|
|
|
60923
61583
|
function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
60924
61584
|
let realRoot;
|
|
60925
61585
|
try {
|
|
60926
|
-
if ((0,
|
|
61586
|
+
if ((0, import_node_fs51.lstatSync)(root).isSymbolicLink()) {
|
|
60927
61587
|
return { ok: false, error: `delete root ${normPath2(root)} is a reparse point resolving to ${resolvedOrRaw(root)}` };
|
|
60928
61588
|
}
|
|
60929
|
-
realRoot = normPath2((0,
|
|
61589
|
+
realRoot = normPath2((0, import_node_fs51.realpathSync)(root));
|
|
60930
61590
|
} catch (e) {
|
|
60931
61591
|
if (e.code === "ENOENT") return { ok: true, unlinked: [] };
|
|
60932
61592
|
return { ok: false, error: `cannot resolve delete root ${normPath2(root)}: ${errorMessage(e)}` };
|
|
@@ -60942,17 +61602,17 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
|
60942
61602
|
const dir = stack.pop();
|
|
60943
61603
|
let entries;
|
|
60944
61604
|
try {
|
|
60945
|
-
entries = (0,
|
|
61605
|
+
entries = (0, import_node_fs51.readdirSync)(dir, { withFileTypes: true });
|
|
60946
61606
|
} catch (e) {
|
|
60947
61607
|
if (e.code === "ENOENT") continue;
|
|
60948
61608
|
return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
|
|
60949
61609
|
}
|
|
60950
61610
|
for (const entry of entries) {
|
|
60951
|
-
const child2 = (0,
|
|
61611
|
+
const child2 = (0, import_node_path47.join)(dir, entry.name);
|
|
60952
61612
|
if (entry.isSymbolicLink()) {
|
|
60953
61613
|
let target = "";
|
|
60954
61614
|
try {
|
|
60955
|
-
target = normPath2((0,
|
|
61615
|
+
target = normPath2((0, import_node_fs51.realpathSync)(child2));
|
|
60956
61616
|
} catch {
|
|
60957
61617
|
target = "";
|
|
60958
61618
|
}
|
|
@@ -61135,7 +61795,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
61135
61795
|
return report;
|
|
61136
61796
|
}
|
|
61137
61797
|
const execGit = options.execGit ?? (async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
61138
|
-
const pathExists = options.pathExists ??
|
|
61798
|
+
const pathExists = options.pathExists ?? import_node_fs51.existsSync;
|
|
61139
61799
|
let afterWorktrees = [];
|
|
61140
61800
|
try {
|
|
61141
61801
|
afterWorktrees = parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"]));
|
|
@@ -61275,7 +61935,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
61275
61935
|
const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
|
|
61276
61936
|
if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
|
|
61277
61937
|
unlinkedReparsePoints.push(...preHelperGuard.unlinked);
|
|
61278
|
-
if (pathExists((0,
|
|
61938
|
+
if (pathExists((0, import_node_path47.join)(wtPath, "node_modules"))) {
|
|
61279
61939
|
const nmRemoved = await (options.removeRealNodeModules ?? removeWorktreeNodeModules)(wtPath);
|
|
61280
61940
|
if (!nmRemoved.ok) {
|
|
61281
61941
|
report.worktree = {
|
|
@@ -61411,18 +62071,18 @@ function renderPrMergeCleanupLines(cleanup) {
|
|
|
61411
62071
|
// src/worktree-residue-sweep.ts
|
|
61412
62072
|
var RESIDUE_LEDGER_FILE = "mmi-worktree-residue.json";
|
|
61413
62073
|
function canonicalWorktreeRoot(primary) {
|
|
61414
|
-
return (0,
|
|
62074
|
+
return (0, import_node_path48.join)((0, import_node_path48.dirname)(primary), "mmi-worktrees", (0, import_node_path48.basename)(primary));
|
|
61415
62075
|
}
|
|
61416
62076
|
function norm(p) {
|
|
61417
62077
|
const s = p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
61418
62078
|
return process.platform === "win32" ? s.toLowerCase() : s;
|
|
61419
62079
|
}
|
|
61420
62080
|
function residueLedgerPath(gitCommonDir) {
|
|
61421
|
-
return (0,
|
|
62081
|
+
return (0, import_node_path48.join)(gitCommonDir, RESIDUE_LEDGER_FILE);
|
|
61422
62082
|
}
|
|
61423
62083
|
function readPaths(file, deps) {
|
|
61424
62084
|
try {
|
|
61425
|
-
const parsed = JSON.parse((deps.readLedger ?? ((f) => (0,
|
|
62085
|
+
const parsed = JSON.parse((deps.readLedger ?? ((f) => (0, import_node_fs52.readFileSync)(f, "utf8")))(file));
|
|
61426
62086
|
return Array.isArray(parsed) ? parsed.filter((p) => typeof p === "string") : [];
|
|
61427
62087
|
} catch {
|
|
61428
62088
|
return [];
|
|
@@ -61431,8 +62091,8 @@ function readPaths(file, deps) {
|
|
|
61431
62091
|
function writePaths(file, paths, deps) {
|
|
61432
62092
|
try {
|
|
61433
62093
|
const write = deps.writeLedger ?? ((f, t) => {
|
|
61434
|
-
(0,
|
|
61435
|
-
(0,
|
|
62094
|
+
(0, import_node_fs52.mkdirSync)((0, import_node_path48.dirname)(f), { recursive: true });
|
|
62095
|
+
(0, import_node_fs52.writeFileSync)(f, t, "utf8");
|
|
61436
62096
|
});
|
|
61437
62097
|
write(file, `${JSON.stringify([...new Set(paths)], null, 1)}
|
|
61438
62098
|
`);
|
|
@@ -61446,17 +62106,17 @@ function recordResidue(gitCommonDir, path2, deps = {}) {
|
|
|
61446
62106
|
writePaths(file, paths, deps);
|
|
61447
62107
|
}
|
|
61448
62108
|
async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
61449
|
-
const exists = deps.exists ??
|
|
61450
|
-
const readDir = deps.readDir ?? ((p) => (0,
|
|
62109
|
+
const exists = deps.exists ?? import_node_fs52.existsSync;
|
|
62110
|
+
const readDir = deps.readDir ?? ((p) => (0, import_node_fs52.readdirSync)(p));
|
|
61451
62111
|
const isDir = deps.isDir ?? ((p) => {
|
|
61452
62112
|
try {
|
|
61453
|
-
return (0,
|
|
62113
|
+
return (0, import_node_fs52.statSync)(p).isDirectory();
|
|
61454
62114
|
} catch {
|
|
61455
62115
|
return false;
|
|
61456
62116
|
}
|
|
61457
62117
|
});
|
|
61458
62118
|
const removeRecorded = deps.removeRecorded ?? ((p) => removeResidueDirectory(p));
|
|
61459
|
-
const removeEmpty = deps.removeEmpty ?? ((p) => (0,
|
|
62119
|
+
const removeEmpty = deps.removeEmpty ?? ((p) => (0, import_node_fs52.rmdirSync)(p));
|
|
61460
62120
|
const report = { swept: [], kept: [], dropped: [], emptyRemoved: [], unrecorded: [] };
|
|
61461
62121
|
const live = new Set(registered.map(norm));
|
|
61462
62122
|
const primary = registered[0] ? norm(registered[0]) : void 0;
|
|
@@ -61491,7 +62151,7 @@ async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
|
61491
62151
|
} catch {
|
|
61492
62152
|
}
|
|
61493
62153
|
for (const name of children) {
|
|
61494
|
-
const child2 = (0,
|
|
62154
|
+
const child2 = (0, import_node_path48.join)(root, name);
|
|
61495
62155
|
const key = norm(child2);
|
|
61496
62156
|
if (live.has(key) || key === primary || keptKeys.has(key) || !isDir(child2)) continue;
|
|
61497
62157
|
let entries;
|
|
@@ -61534,10 +62194,10 @@ function argvWantsJson2() {
|
|
|
61534
62194
|
return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
|
|
61535
62195
|
}
|
|
61536
62196
|
function hubRoot() {
|
|
61537
|
-
const fromPkg = (0,
|
|
62197
|
+
const fromPkg = (0, import_node_path49.join)(__dirname, "..", "..");
|
|
61538
62198
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
61539
|
-
if ((0,
|
|
61540
|
-
if ((0,
|
|
62199
|
+
if ((0, import_node_fs53.existsSync)((0, import_node_path49.join)(fromPkg, marker))) return fromPkg;
|
|
62200
|
+
if ((0, import_node_fs53.existsSync)((0, import_node_path49.join)(process.cwd(), marker))) return process.cwd();
|
|
61541
62201
|
return null;
|
|
61542
62202
|
}
|
|
61543
62203
|
function ciAuditDeps() {
|
|
@@ -61549,8 +62209,8 @@ function ciAuditDeps() {
|
|
|
61549
62209
|
getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
|
|
61550
62210
|
readSeedFile: (path2) => {
|
|
61551
62211
|
if (!root) return null;
|
|
61552
|
-
const fullPath = (0,
|
|
61553
|
-
return (0,
|
|
62212
|
+
const fullPath = (0, import_node_path49.join)(root, path2);
|
|
62213
|
+
return (0, import_node_fs53.existsSync)(fullPath) ? (0, import_node_fs53.readFileSync)(fullPath, "utf8") : null;
|
|
61554
62214
|
}
|
|
61555
62215
|
};
|
|
61556
62216
|
}
|
|
@@ -62417,11 +63077,11 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
62417
63077
|
}
|
|
62418
63078
|
});
|
|
62419
63079
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
62420
|
-
const wfDir = (0,
|
|
62421
|
-
if (!(0,
|
|
62422
|
-
return (0,
|
|
63080
|
+
const wfDir = (0, import_node_path49.join)(cwd, ".github", "workflows");
|
|
63081
|
+
if (!(0, import_node_fs53.existsSync)(wfDir)) return [];
|
|
63082
|
+
return (0, import_node_fs53.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
62423
63083
|
try {
|
|
62424
|
-
return workflowReportsPrChecks((0,
|
|
63084
|
+
return workflowReportsPrChecks((0, import_node_fs53.readFileSync)((0, import_node_path49.join)(wfDir, name), "utf8"));
|
|
62425
63085
|
} catch {
|
|
62426
63086
|
return true;
|
|
62427
63087
|
}
|
|
@@ -62626,7 +63286,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
62626
63286
|
jsonParity(pr.command("review-verdict <number>").description("post an explicit review verdict comment: computes the PR diff patch-id and head sha, renders the v1 contract body, and posts it as a PR comment").requiredOption("--repo <owner/repo>", "target repo").requiredOption("--verdict <verdict>", `one of ${REVIEW_VERDICTS.join("|")}`).requiredOption("--scope <text>", "one line: what was reviewed").requiredOption("--risk <text>", "one line: the residual risk").option("--unverified <text>", "a claim the reviewer could not verify (repeatable)", (v, acc) => [...acc, v], []).requiredOption("--reviewer <id>", "reviewer seat or model id").option("--findings-file <path>", "Markdown findings appended after the JSON block")).action(async (number, o) => {
|
|
62627
63287
|
if (!isReviewVerdict(o.verdict)) return fail(`pr review-verdict: --verdict must be one of ${REVIEW_VERDICTS.join("|")} (got ${o.verdict})`);
|
|
62628
63288
|
const verdict = o.verdict;
|
|
62629
|
-
const findings = o.findingsFile ? (0,
|
|
63289
|
+
const findings = o.findingsFile ? (0, import_node_fs53.readFileSync)(o.findingsFile, "utf8") : void 0;
|
|
62630
63290
|
let patch;
|
|
62631
63291
|
let head;
|
|
62632
63292
|
try {
|
|
@@ -62904,7 +63564,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
62904
63564
|
const cleanupMerged = async (r) => {
|
|
62905
63565
|
if (opts.preserveWorktree) return "cleanup skipped (--preserve-worktree)";
|
|
62906
63566
|
const ran = await execFileP(process.execPath, [process.argv[1], ...canonicalArgvFor(["pr", "merge", r.pr, "--repo", r.repo])], {
|
|
62907
|
-
cwd: (0,
|
|
63567
|
+
cwd: (0, import_node_fs53.existsSync)(cwd0) ? cwd0 : primaryRoot0,
|
|
62908
63568
|
timeout: GH_MUTATION_TIMEOUT_MS * 3
|
|
62909
63569
|
}).then((x) => ({ stdout: x.stdout, stderr: x.stderr }), (e) => ({ stdout: e.stdout ?? "", stderr: e.stderr ?? "" }));
|
|
62910
63570
|
const json = ran.stdout.split("\n").reverse().find((l) => l.startsWith("{"));
|
|
@@ -62975,7 +63635,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
62975
63635
|
const mergeSquashBody = squashBodyTextForMerge(
|
|
62976
63636
|
closingGuardInput,
|
|
62977
63637
|
method === "--squash",
|
|
62978
|
-
o.squashBodyFile ? (0,
|
|
63638
|
+
o.squashBodyFile ? (0, import_node_fs53.readFileSync)(o.squashBodyFile, "utf8") : void 0
|
|
62979
63639
|
);
|
|
62980
63640
|
if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
|
|
62981
63641
|
const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
|
|
@@ -63007,7 +63667,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63007
63667
|
const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
|
|
63008
63668
|
let foreignCheckout;
|
|
63009
63669
|
if (foreignCwd) {
|
|
63010
|
-
const sibling = (0,
|
|
63670
|
+
const sibling = (0, import_node_path49.join)((0, import_node_path49.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
|
|
63011
63671
|
const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
|
|
63012
63672
|
if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
|
|
63013
63673
|
const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
|
|
@@ -63126,7 +63786,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63126
63786
|
}
|
|
63127
63787
|
if (!repoForPostCleanup) throw e;
|
|
63128
63788
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
|
|
63129
|
-
const commitMessage = bodyFile ? (0,
|
|
63789
|
+
const commitMessage = bodyFile ? (0, import_node_fs53.readFileSync)(bodyFile, "utf8") : void 0;
|
|
63130
63790
|
await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
|
|
63131
63791
|
body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
|
|
63132
63792
|
timeoutMs: GH_MUTATION_TIMEOUT_MS
|
|
@@ -63241,7 +63901,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63241
63901
|
let residueSweep;
|
|
63242
63902
|
if (!(foreignCwd && !foreignCheckout)) {
|
|
63243
63903
|
try {
|
|
63244
|
-
gitCommonDir = (0,
|
|
63904
|
+
gitCommonDir = (0, import_node_path49.resolve)(primaryRoot, (await execFileP("git", ["-C", primaryRoot, "rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim());
|
|
63245
63905
|
residueSweep = await sweepWorktreeResidue(gitCommonDir, beforeWorktrees.map((w) => w.path));
|
|
63246
63906
|
} catch {
|
|
63247
63907
|
}
|
|
@@ -63259,7 +63919,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63259
63919
|
preserveWorktree: o.preserveWorktree,
|
|
63260
63920
|
gcAcknowledged: o.gc,
|
|
63261
63921
|
expectedHeadOid: headRefOid,
|
|
63262
|
-
pathExists: (p) => (0,
|
|
63922
|
+
pathExists: (p) => (0, import_node_fs53.existsSync)(p),
|
|
63263
63923
|
// #5899: pin cleanup git calls to the main checkout — the task worktree this process may be
|
|
63264
63924
|
// standing in is removed mid-cleanup, so a cwd-relative invocation fails with
|
|
63265
63925
|
// 'fatal: not a git repository' and leaves a spurious partial-cleanup exit.
|
|
@@ -64525,7 +65185,7 @@ function rcRunCauseFromLog(log) {
|
|
|
64525
65185
|
function memberAnnounceSummaryFile(repo, summaryFile, memberSummaryFiles) {
|
|
64526
65186
|
return repo === SUITE_REPO ? summaryFile : memberSummaryFiles[repo];
|
|
64527
65187
|
}
|
|
64528
|
-
var suiteStateDir = (root) => (0,
|
|
65188
|
+
var suiteStateDir = (root) => (0, import_node_path51.join)(resolvePrimaryGitDir(root), "mmi-runtime", "suite");
|
|
64529
65189
|
async function runSuiteMemberRcand(repo, previousRc, rcWasAbsent) {
|
|
64530
65190
|
const deps = trainApplyDeps();
|
|
64531
65191
|
deps.suiteCut = suiteCutContext(repo, SUITE_REPO);
|
|
@@ -64572,16 +65232,16 @@ async function rcandInCheckout(repo, deps, previousRc, rcWasAbsent) {
|
|
|
64572
65232
|
}
|
|
64573
65233
|
}
|
|
64574
65234
|
function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr, memberSummaryFilesTr = {}) {
|
|
64575
|
-
const recordPath = (0,
|
|
64576
|
-
const base = (0,
|
|
65235
|
+
const recordPath = (0, import_node_path51.join)(suiteStateDir(root), "cut.json");
|
|
65236
|
+
const base = (0, import_node_path51.join)(suiteStateDir(root), "checkouts");
|
|
64577
65237
|
const checkout = async (repo) => {
|
|
64578
65238
|
if (repo === SUITE_REPO) return root;
|
|
64579
|
-
const target = (0,
|
|
64580
|
-
if (!(0,
|
|
64581
|
-
(0,
|
|
65239
|
+
const target = (0, import_node_path51.join)(base, repo.split("/")[1]);
|
|
65240
|
+
if (!(0, import_node_fs54.existsSync)(target)) {
|
|
65241
|
+
(0, import_node_fs54.mkdirSync)(base, { recursive: true });
|
|
64582
65242
|
await exec("git", ["clone", "--quiet", "--branch", "development", `https://github.com/${repo}.git`, target], root, WATCH_TIMEOUT);
|
|
64583
65243
|
}
|
|
64584
|
-
if (!(0,
|
|
65244
|
+
if (!(0, import_node_fs54.existsSync)((0, import_node_path51.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
|
|
64585
65245
|
const origin = await exec("git", ["remote", "get-url", "origin"], target);
|
|
64586
65246
|
if (!origin.toLowerCase().endsWith(`/${repo.toLowerCase()}.git`) && !origin.toLowerCase().endsWith(`/${repo.toLowerCase()}`))
|
|
64587
65247
|
throw new Error(`${repo}: suite checkout origin mismatch at ${target}; stop`);
|
|
@@ -64645,7 +65305,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
64645
65305
|
if (repo === SUITE_REPO) return inTrain(repo, (deps) => rcandInCheckout(repo, deps, previousRc, rcWasAbsent));
|
|
64646
65306
|
const target = await checkout(repo);
|
|
64647
65307
|
return new Promise((resolveResult, reject2) => {
|
|
64648
|
-
const child2 = (0, import_node_child_process19.fork)((0,
|
|
65308
|
+
const child2 = (0, import_node_child_process19.fork)((0, import_node_path50.resolve)(process.argv[1]), ["devops", "suite", "member-rcand-internal", repo, previousRc ?? "", ...rcWasAbsent ? ["--rc-was-absent"] : []], {
|
|
64649
65309
|
cwd: target,
|
|
64650
65310
|
stdio: ["ignore", "inherit", "inherit", "ipc"],
|
|
64651
65311
|
execArgv: []
|
|
@@ -64888,9 +65548,9 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
64888
65548
|
const body = await gh(["release", "view", leadTag, "--repo", SUITE_REPO, "--json", "body", "--jq", ".body"]);
|
|
64889
65549
|
if (parseSuiteMapping(body)) return;
|
|
64890
65550
|
const dir = suiteStateDir(root);
|
|
64891
|
-
(0,
|
|
64892
|
-
const file = (0,
|
|
64893
|
-
(0,
|
|
65551
|
+
(0, import_node_fs54.mkdirSync)(dir, { recursive: true });
|
|
65552
|
+
const file = (0, import_node_path51.join)(dir, `notes-${leadTag}.md`);
|
|
65553
|
+
(0, import_node_fs54.writeFileSync)(file, `${body}
|
|
64894
65554
|
|
|
64895
65555
|
${suiteMappingBlock(leadTag, mapping)}`);
|
|
64896
65556
|
await gh(["release", "edit", leadTag, "--repo", SUITE_REPO, "--notes-file", file]);
|
|
@@ -64931,7 +65591,7 @@ ${suiteMappingBlock(leadTag, mapping)}`);
|
|
|
64931
65591
|
|
|
64932
65592
|
// src/suite-commands.ts
|
|
64933
65593
|
init_cli_shared();
|
|
64934
|
-
var
|
|
65594
|
+
var import_node_fs55 = require("node:fs");
|
|
64935
65595
|
function parseMemberSummaryFiles(entries) {
|
|
64936
65596
|
const memberSummaryFiles = {};
|
|
64937
65597
|
for (const entry of entries) {
|
|
@@ -65025,7 +65685,7 @@ function registerSuiteCommands(program3) {
|
|
|
65025
65685
|
try {
|
|
65026
65686
|
const io = suiteTrainIo(root);
|
|
65027
65687
|
retired = await retireFailedLeadRcLedger({ ledgerPath: releaseLedgerPath(root), rcHead: () => io.head(SUITE_REPO, "rc").then((h) => h ?? void 0), markAbandoned: io.markAbandoned }, o.ownerGo === true);
|
|
65028
|
-
if (retired && !(0,
|
|
65688
|
+
if (retired && !(0, import_node_fs55.existsSync)(io.recordPath)) {
|
|
65029
65689
|
console.log(o.json ? JSON.stringify({ retiredLeadRc: retired }, null, 2) : `suite abandon: retired the lead's failed, never-promoted rc candidate ${retired}; marked abandoned on origin (#7854)`);
|
|
65030
65690
|
return;
|
|
65031
65691
|
}
|
|
@@ -65265,9 +65925,9 @@ function registerJevCommands(program3) {
|
|
|
65265
65925
|
|
|
65266
65926
|
// src/packages-adopt-export.ts
|
|
65267
65927
|
var import_node_child_process20 = require("node:child_process");
|
|
65268
|
-
var
|
|
65928
|
+
var import_node_fs56 = require("node:fs");
|
|
65269
65929
|
var import_node_os25 = require("node:os");
|
|
65270
|
-
var
|
|
65930
|
+
var import_node_path52 = require("node:path");
|
|
65271
65931
|
var import_node_util6 = require("node:util");
|
|
65272
65932
|
init_cli_shared();
|
|
65273
65933
|
|
|
@@ -65887,38 +66547,38 @@ function expectedFromRun(run, opts) {
|
|
|
65887
66547
|
}
|
|
65888
66548
|
async function adoptExport(opts, deps) {
|
|
65889
66549
|
if (!/^sha256:[0-9a-f]{64}$/.test(opts.digest)) throw new Error("--digest must be sha256:<64 hex>, from the export run summary");
|
|
65890
|
-
const packagePath = (0,
|
|
65891
|
-
const lockPath = (0,
|
|
65892
|
-
const packageText = (0,
|
|
65893
|
-
const lockText = (0,
|
|
65894
|
-
const work = (0,
|
|
66550
|
+
const packagePath = (0, import_node_path52.join)(opts.dir, "package.json");
|
|
66551
|
+
const lockPath = (0, import_node_path52.join)(opts.dir, "package-lock.json");
|
|
66552
|
+
const packageText = (0, import_node_fs56.readFileSync)(packagePath, "utf8");
|
|
66553
|
+
const lockText = (0, import_node_fs56.readFileSync)(lockPath, "utf8");
|
|
66554
|
+
const work = (0, import_node_fs56.mkdtempSync)((0, import_node_path52.join)((0, import_node_os25.tmpdir)(), "mmi-adopt-export-"));
|
|
65895
66555
|
try {
|
|
65896
66556
|
const run = JSON.parse((await deps.ghApi(`repos/${EXPORT_REPO}/actions/runs/${opts.run}`)).toString("utf8"));
|
|
65897
|
-
(0,
|
|
65898
|
-
(0,
|
|
65899
|
-
(0,
|
|
65900
|
-
const script = (0,
|
|
65901
|
-
(0,
|
|
66557
|
+
(0, import_node_fs56.writeFileSync)((0, import_node_path52.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
|
|
66558
|
+
(0, import_node_fs56.writeFileSync)((0, import_node_path52.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
|
|
66559
|
+
(0, import_node_fs56.writeFileSync)((0, import_node_path52.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
|
|
66560
|
+
const script = (0, import_node_path52.join)(work, "package_export.py");
|
|
66561
|
+
(0, import_node_fs56.writeFileSync)(script, package_export_default);
|
|
65902
66562
|
await deps.python(script, ["verify", work, opts.ds, opts.screens]);
|
|
65903
|
-
const verified = (0,
|
|
65904
|
-
const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0,
|
|
66563
|
+
const verified = (0, import_node_path52.join)(work, "verified");
|
|
66564
|
+
const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs56.readFileSync)((0, import_node_path52.join)(verified, `${short3}.metadata.json`), "utf8"))]));
|
|
65905
66565
|
const plan = planAdoption(packageText, lockText, metadata);
|
|
65906
66566
|
for (const { name } of plan.result.adopted) {
|
|
65907
66567
|
const short3 = PACKAGES.find((p) => p.name === name).short;
|
|
65908
|
-
await deps.npm(["cache", "add", (0,
|
|
66568
|
+
await deps.npm(["cache", "add", (0, import_node_path52.join)(verified, `${short3}.tgz`)], opts.dir);
|
|
65909
66569
|
}
|
|
65910
|
-
(0,
|
|
65911
|
-
(0,
|
|
66570
|
+
(0, import_node_fs56.writeFileSync)(packagePath, plan.packageText);
|
|
66571
|
+
(0, import_node_fs56.writeFileSync)(lockPath, plan.lockText);
|
|
65912
66572
|
try {
|
|
65913
66573
|
await deps.npm(["ci", "--offline", "--ignore-scripts", "--no-audit", "--no-fund"], opts.dir);
|
|
65914
66574
|
} catch (error) {
|
|
65915
|
-
(0,
|
|
65916
|
-
(0,
|
|
66575
|
+
(0, import_node_fs56.writeFileSync)(packagePath, packageText);
|
|
66576
|
+
(0, import_node_fs56.writeFileSync)(lockPath, lockText);
|
|
65917
66577
|
throw new Error(`npm ci --offline failed, package.json and package-lock.json restored: ${error.message}`);
|
|
65918
66578
|
}
|
|
65919
66579
|
return plan.result;
|
|
65920
66580
|
} finally {
|
|
65921
|
-
(0,
|
|
66581
|
+
(0, import_node_fs56.rmSync)(work, { recursive: true, force: true });
|
|
65922
66582
|
}
|
|
65923
66583
|
}
|
|
65924
66584
|
var execFileBuffer = (0, import_node_util6.promisify)(import_node_child_process20.execFile);
|
|
@@ -66022,12 +66682,12 @@ function registerPackagesCommands(program3) {
|
|
|
66022
66682
|
init_cli_shared();
|
|
66023
66683
|
|
|
66024
66684
|
// src/wave-status.ts
|
|
66025
|
-
var
|
|
66685
|
+
var import_node_fs57 = require("node:fs");
|
|
66026
66686
|
function observeStage(worktreePath) {
|
|
66027
66687
|
const statePath = stageStatePath(worktreePath);
|
|
66028
66688
|
let raw;
|
|
66029
66689
|
try {
|
|
66030
|
-
raw = (0,
|
|
66690
|
+
raw = (0, import_node_fs57.readFileSync)(statePath, "utf8");
|
|
66031
66691
|
} catch (error) {
|
|
66032
66692
|
if (error.code === "ENOENT") return { state: "ok", value: { running: false } };
|
|
66033
66693
|
return { state: "unavailable", reason: "stage state could not be read" };
|
|
@@ -66094,9 +66754,9 @@ async function collectWaveStatus(deps) {
|
|
|
66094
66754
|
// src/discovery-commands.ts
|
|
66095
66755
|
init_github_client();
|
|
66096
66756
|
init_cli_shared();
|
|
66097
|
-
var
|
|
66757
|
+
var import_node_fs58 = require("node:fs");
|
|
66098
66758
|
var import_node_os26 = require("node:os");
|
|
66099
|
-
var
|
|
66759
|
+
var import_node_path53 = require("node:path");
|
|
66100
66760
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
66101
66761
|
var defaultStatusDeps = {
|
|
66102
66762
|
repo: resolveRepo,
|
|
@@ -66317,8 +66977,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
66317
66977
|
}
|
|
66318
66978
|
const home = (0, import_node_os26.homedir)();
|
|
66319
66979
|
const plugin = onboardPluginGate({
|
|
66320
|
-
readKnown: () => readFileSyncSafe((0,
|
|
66321
|
-
readSettings: () => readFileSyncSafe((0,
|
|
66980
|
+
readKnown: () => readFileSyncSafe((0, import_node_path53.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs58.readFileSync),
|
|
66981
|
+
readSettings: () => readFileSyncSafe((0, import_node_path53.join)(home, ".claude", "settings.json"), import_node_fs58.readFileSync)
|
|
66322
66982
|
});
|
|
66323
66983
|
return {
|
|
66324
66984
|
guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
|
|
@@ -69449,8 +70109,8 @@ function registerSessionReport(program3) {
|
|
|
69449
70109
|
}
|
|
69450
70110
|
|
|
69451
70111
|
// src/stage-commands.ts
|
|
69452
|
-
var
|
|
69453
|
-
var
|
|
70112
|
+
var import_node_fs59 = require("node:fs");
|
|
70113
|
+
var import_node_path54 = require("node:path");
|
|
69454
70114
|
init_cli_shared();
|
|
69455
70115
|
init_clean_exit();
|
|
69456
70116
|
|
|
@@ -69698,8 +70358,8 @@ function registerStageCommands(program3) {
|
|
|
69698
70358
|
const repo = Array.isArray(project2?.repos) && typeof project2.repos[0] === "string" ? project2.repos[0] : slug;
|
|
69699
70359
|
const resolution = decideStage({
|
|
69700
70360
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
69701
|
-
hasCompose: (0,
|
|
69702
|
-
hasEnvExample: (0,
|
|
70361
|
+
hasCompose: (0, import_node_fs59.existsSync)((0, import_node_path54.join)(process.cwd(), "docker-compose.yml")),
|
|
70362
|
+
hasEnvExample: (0, import_node_fs59.existsSync)((0, import_node_path54.join)(process.cwd(), ".env.example")),
|
|
69703
70363
|
repo
|
|
69704
70364
|
});
|
|
69705
70365
|
if (resolution.source === "derived" && resolution.derived) {
|
|
@@ -69991,25 +70651,25 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
|
|
|
69991
70651
|
}
|
|
69992
70652
|
|
|
69993
70653
|
// src/tenant-artifact.ts
|
|
69994
|
-
var
|
|
69995
|
-
var
|
|
70654
|
+
var import_node_crypto23 = require("node:crypto");
|
|
70655
|
+
var import_node_fs60 = require("node:fs");
|
|
69996
70656
|
var import_promises11 = require("node:fs/promises");
|
|
69997
|
-
var
|
|
70657
|
+
var import_node_path55 = require("node:path");
|
|
69998
70658
|
var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
69999
70659
|
var MAX_BYTES2 = 5 * 1024 * 1024 * 1024;
|
|
70000
70660
|
async function sha256File(path2) {
|
|
70001
|
-
const hash = (0,
|
|
70002
|
-
for await (const chunk of (0,
|
|
70661
|
+
const hash = (0, import_node_crypto23.createHash)("sha256");
|
|
70662
|
+
for await (const chunk of (0, import_node_fs60.createReadStream)(path2)) hash.update(chunk);
|
|
70003
70663
|
return hash.digest("hex");
|
|
70004
70664
|
}
|
|
70005
70665
|
async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
70006
70666
|
if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
|
|
70007
|
-
const path2 = (0,
|
|
70667
|
+
const path2 = (0, import_node_path55.resolve)(inputPath);
|
|
70008
70668
|
const info = await (0, import_promises11.stat)(path2);
|
|
70009
70669
|
if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
|
|
70010
70670
|
if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size > MAX_BYTES2) throw new Error(`tenant artifact put: file must be 1..${MAX_BYTES2} bytes`);
|
|
70011
|
-
const
|
|
70012
|
-
const prepared = await tenantArtifactUpload({ repo, stage, size: info.size, sha256:
|
|
70671
|
+
const sha2564 = await sha256File(path2);
|
|
70672
|
+
const prepared = await tenantArtifactUpload({ repo, stage, size: info.size, sha256: sha2564 }, deps);
|
|
70013
70673
|
if (!prepared.ok) {
|
|
70014
70674
|
const detail = prepared.body?.error ?? prepared.error ?? `HTTP ${prepared.status}`;
|
|
70015
70675
|
throw new Error(`tenant artifact put: ${detail}`);
|
|
@@ -70022,7 +70682,7 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
|
70022
70682
|
return [key, value];
|
|
70023
70683
|
}));
|
|
70024
70684
|
headers["content-length"] = String(info.size);
|
|
70025
|
-
const stream = (0,
|
|
70685
|
+
const stream = (0, import_node_fs60.createReadStream)(path2);
|
|
70026
70686
|
let uploaded;
|
|
70027
70687
|
try {
|
|
70028
70688
|
uploaded = await fetch(body.uploadUrl, {
|
|
@@ -70037,8 +70697,8 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
|
70037
70697
|
throw error;
|
|
70038
70698
|
}
|
|
70039
70699
|
if (!uploaded.ok) throw new Error(`tenant artifact put: object upload failed (HTTP ${uploaded.status})`);
|
|
70040
|
-
if (body.size !== info.size || body.sha256 !==
|
|
70041
|
-
return { artifactId: body.artifactId, repo, stage, size: info.size, sha256:
|
|
70700
|
+
if (body.size !== info.size || body.sha256 !== sha2564 || typeof body.expiresAt !== "string" || !Number.isFinite(Date.parse(body.expiresAt))) throw new Error("tenant artifact put: Hub returned inconsistent artifact metadata");
|
|
70701
|
+
return { artifactId: body.artifactId, repo, stage, size: info.size, sha256: sha2564, expiresAt: body.expiresAt };
|
|
70042
70702
|
}
|
|
70043
70703
|
|
|
70044
70704
|
// src/tenant-logs.ts
|
|
@@ -70376,7 +71036,7 @@ function renderVerifySecrets(body) {
|
|
|
70376
71036
|
}
|
|
70377
71037
|
|
|
70378
71038
|
// src/command-register-developer.ts
|
|
70379
|
-
var
|
|
71039
|
+
var import_node_fs65 = require("node:fs");
|
|
70380
71040
|
init_clean_exit();
|
|
70381
71041
|
init_cli_shared();
|
|
70382
71042
|
init_error_codes();
|
|
@@ -70409,12 +71069,12 @@ async function resolveWhoami(deps) {
|
|
|
70409
71069
|
}
|
|
70410
71070
|
|
|
70411
71071
|
// src/command-register-developer.ts
|
|
70412
|
-
var
|
|
71072
|
+
var import_node_path62 = require("node:path");
|
|
70413
71073
|
|
|
70414
71074
|
// src/doc-refs-command-source.ts
|
|
70415
71075
|
var import_node_child_process21 = require("node:child_process");
|
|
70416
|
-
var
|
|
70417
|
-
var
|
|
71076
|
+
var import_node_fs61 = require("node:fs");
|
|
71077
|
+
var import_node_path56 = require("node:path");
|
|
70418
71078
|
var BUNDLE_MANIFEST_TIMEOUT_MS = 12e4;
|
|
70419
71079
|
var BUNDLE_MANIFEST_MAX_BUFFER = 64 * 1024 * 1024;
|
|
70420
71080
|
var flagsOf = (options) => new Set((options ?? []).flatMap((option) => String(option?.flags ?? "").match(/--[\w-]+/g) ?? []));
|
|
@@ -70435,7 +71095,7 @@ function inventoryFromManifest(manifest, source) {
|
|
|
70435
71095
|
function readBundleManifest(bundle, exec3 = import_node_child_process21.execFileSync) {
|
|
70436
71096
|
try {
|
|
70437
71097
|
const text = exec3(process.execPath, [bundle, "commands", "--all", "--json"], {
|
|
70438
|
-
cwd: (0,
|
|
71098
|
+
cwd: (0, import_node_path56.dirname)(bundle),
|
|
70439
71099
|
encoding: "utf8",
|
|
70440
71100
|
windowsHide: true,
|
|
70441
71101
|
timeout: BUNDLE_MANIFEST_TIMEOUT_MS,
|
|
@@ -70451,19 +71111,19 @@ function readBundleManifest(bundle, exec3 = import_node_child_process21.execFile
|
|
|
70451
71111
|
function isRunningBundle(bundle, argv1 = process.argv[1], platform2 = process.platform) {
|
|
70452
71112
|
if (!argv1) return false;
|
|
70453
71113
|
const norm2 = (path2) => {
|
|
70454
|
-
const absolute = (0,
|
|
71114
|
+
const absolute = (0, import_node_path56.resolve)(path2);
|
|
70455
71115
|
return platform2 === "win32" ? absolute.toLowerCase() : absolute;
|
|
70456
71116
|
};
|
|
70457
71117
|
const running = norm2(argv1);
|
|
70458
|
-
if (!["main.cjs", "index.cjs"].includes((0,
|
|
70459
|
-
return (0,
|
|
71118
|
+
if (!["main.cjs", "index.cjs"].includes((0, import_node_path56.basename)(running))) return false;
|
|
71119
|
+
return (0, import_node_path56.dirname)(running) === (0, import_node_path56.dirname)(norm2(bundle));
|
|
70460
71120
|
}
|
|
70461
71121
|
function resolveCommandInventory(deps) {
|
|
70462
|
-
const exists = deps.exists ??
|
|
71122
|
+
const exists = deps.exists ?? import_node_fs61.existsSync;
|
|
70463
71123
|
const read = deps.read ?? ((bundle) => readBundleManifest(bundle));
|
|
70464
71124
|
const warnings = [];
|
|
70465
71125
|
if (deps.from) {
|
|
70466
|
-
const bundle = (0,
|
|
71126
|
+
const bundle = (0, import_node_path56.isAbsolute)(deps.from) ? deps.from : (0, import_node_path56.resolve)(deps.root, deps.from);
|
|
70467
71127
|
if (!exists(bundle)) {
|
|
70468
71128
|
throw new Error(`docs refs --commands-from: ${bundle} does not exist \u2014 name the CLI bundle to read commands from (usually cli/dist/main.cjs)`);
|
|
70469
71129
|
}
|
|
@@ -70473,7 +71133,7 @@ function resolveCommandInventory(deps) {
|
|
|
70473
71133
|
}
|
|
70474
71134
|
return { inventory: inventory2, warnings };
|
|
70475
71135
|
}
|
|
70476
|
-
const treeBundle = (0,
|
|
71136
|
+
const treeBundle = (0, import_node_path56.join)(deps.root, "cli", "dist", "main.cjs");
|
|
70477
71137
|
if (!exists(treeBundle) || isRunningBundle(treeBundle, deps.argv1, deps.platform)) {
|
|
70478
71138
|
return { inventory: deps.inProcess(), warnings };
|
|
70479
71139
|
}
|
|
@@ -70590,28 +71250,28 @@ async function executeWaveLand(plan, deps) {
|
|
|
70590
71250
|
|
|
70591
71251
|
// src/dist-drift.ts
|
|
70592
71252
|
var import_node_child_process22 = require("node:child_process");
|
|
70593
|
-
var
|
|
70594
|
-
var
|
|
71253
|
+
var import_node_crypto25 = require("node:crypto");
|
|
71254
|
+
var import_node_fs63 = require("node:fs");
|
|
70595
71255
|
var import_node_os27 = require("node:os");
|
|
70596
|
-
var
|
|
71256
|
+
var import_node_path58 = require("node:path");
|
|
70597
71257
|
|
|
70598
71258
|
// ../scripts/distribution-digest.mjs
|
|
70599
|
-
var
|
|
70600
|
-
var
|
|
70601
|
-
var
|
|
71259
|
+
var import_node_crypto24 = require("node:crypto");
|
|
71260
|
+
var import_node_fs62 = require("node:fs");
|
|
71261
|
+
var import_node_path57 = require("node:path");
|
|
70602
71262
|
var slash = (value) => value.replaceAll("\\", "/");
|
|
70603
71263
|
function repoPath(root, declaredPath, label) {
|
|
70604
|
-
const absoluteRoot = (0,
|
|
70605
|
-
const target = (0,
|
|
70606
|
-
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${
|
|
71264
|
+
const absoluteRoot = (0, import_node_path57.resolve)(root);
|
|
71265
|
+
const target = (0, import_node_path57.resolve)(root, declaredPath);
|
|
71266
|
+
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path57.sep}`)) {
|
|
70607
71267
|
throw new Error(`${label} ${declaredPath} escapes the repository root`);
|
|
70608
71268
|
}
|
|
70609
71269
|
return target;
|
|
70610
71270
|
}
|
|
70611
71271
|
function digestFiles(files) {
|
|
70612
|
-
const hash = (0,
|
|
71272
|
+
const hash = (0, import_node_crypto24.createHash)("sha256");
|
|
70613
71273
|
for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
|
|
70614
|
-
const content = file.stat.isSymbolicLink() ? Buffer.from((0,
|
|
71274
|
+
const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs62.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs62.readFileSync)(file.absolute);
|
|
70615
71275
|
hash.update(file.relative, "utf8");
|
|
70616
71276
|
hash.update("\0");
|
|
70617
71277
|
hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
|
|
@@ -70626,8 +71286,8 @@ function digestFiles(files) {
|
|
|
70626
71286
|
function digestPackedFiles(packageRoot, packedFiles) {
|
|
70627
71287
|
return digestFiles(packedFiles.map((path2) => {
|
|
70628
71288
|
const absolute = repoPath(packageRoot, path2, "packed artifact identity path");
|
|
70629
|
-
if (!(0,
|
|
70630
|
-
return { absolute, relative: slash(path2), stat: (0,
|
|
71289
|
+
if (!(0, import_node_fs62.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
|
|
71290
|
+
return { absolute, relative: slash(path2), stat: (0, import_node_fs62.lstatSync)(absolute) };
|
|
70631
71291
|
}));
|
|
70632
71292
|
}
|
|
70633
71293
|
|
|
@@ -70640,10 +71300,10 @@ var DIST_ARTIFACTS = [
|
|
|
70640
71300
|
];
|
|
70641
71301
|
var BOM_DIST_TREE_ID = "mmi-cli-dist";
|
|
70642
71302
|
var ABSENT = "absent";
|
|
70643
|
-
var
|
|
71303
|
+
var sha2563 = (bytes) => `sha256:${(0, import_node_crypto25.createHash)("sha256").update(bytes).digest("hex")}`;
|
|
70644
71304
|
function artifactDrift(path2, committedBytes, rebuiltBytes) {
|
|
70645
|
-
const committed = committedBytes ?
|
|
70646
|
-
const rebuiltExpected = rebuiltBytes ?
|
|
71305
|
+
const committed = committedBytes ? sha2563(committedBytes) : ABSENT;
|
|
71306
|
+
const rebuiltExpected = rebuiltBytes ? sha2563(rebuiltBytes) : ABSENT;
|
|
70647
71307
|
return { path: path2, status: committed === rebuiltExpected ? "current" : "stale", committed, rebuiltExpected };
|
|
70648
71308
|
}
|
|
70649
71309
|
function distTreeIdentity(labels, rebuilt, committed, digest) {
|
|
@@ -70712,13 +71372,13 @@ function renderDistDriftReceipt(receipt) {
|
|
|
70712
71372
|
return lines2;
|
|
70713
71373
|
}
|
|
70714
71374
|
function readOrNull(path2) {
|
|
70715
|
-
return (0,
|
|
71375
|
+
return (0, import_node_fs63.existsSync)(path2) ? (0, import_node_fs63.readFileSync)(path2) : null;
|
|
70716
71376
|
}
|
|
70717
71377
|
function walkFiles(root) {
|
|
70718
71378
|
const files = [];
|
|
70719
71379
|
const walk2 = (directory) => {
|
|
70720
|
-
for (const entry of (0,
|
|
70721
|
-
const child2 = (0,
|
|
71380
|
+
for (const entry of (0, import_node_fs63.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
71381
|
+
const child2 = (0, import_node_path58.join)(directory, entry.name);
|
|
70722
71382
|
if (entry.isDirectory()) walk2(child2);
|
|
70723
71383
|
else files.push(child2);
|
|
70724
71384
|
}
|
|
@@ -70728,10 +71388,10 @@ function walkFiles(root) {
|
|
|
70728
71388
|
}
|
|
70729
71389
|
function bomPathFor(root) {
|
|
70730
71390
|
try {
|
|
70731
|
-
const registry2 = JSON.parse((0,
|
|
70732
|
-
return (0,
|
|
71391
|
+
const registry2 = JSON.parse((0, import_node_fs63.readFileSync)((0, import_node_path58.join)(root, "surfaces.json"), "utf8"));
|
|
71392
|
+
return (0, import_node_path58.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
|
|
70733
71393
|
} catch {
|
|
70734
|
-
return (0,
|
|
71394
|
+
return (0, import_node_path58.join)(root, "distribution-bom.json");
|
|
70735
71395
|
}
|
|
70736
71396
|
}
|
|
70737
71397
|
function rebuildTo(packageRoot, outDir) {
|
|
@@ -70744,40 +71404,40 @@ function rebuildTo(packageRoot, outDir) {
|
|
|
70744
71404
|
});
|
|
70745
71405
|
}
|
|
70746
71406
|
function runDistStatus(root) {
|
|
70747
|
-
const stage = (0,
|
|
71407
|
+
const stage = (0, import_node_fs63.mkdtempSync)((0, import_node_path58.join)((0, import_node_os27.tmpdir)(), "mmi-dist-drift-"));
|
|
70748
71408
|
let overlayCount = 0;
|
|
70749
71409
|
try {
|
|
70750
|
-
const cliOut = (0,
|
|
70751
|
-
const hubOut = (0,
|
|
70752
|
-
rebuildTo((0,
|
|
70753
|
-
rebuildTo((0,
|
|
71410
|
+
const cliOut = (0, import_node_path58.join)(stage, "cli-dist");
|
|
71411
|
+
const hubOut = (0, import_node_path58.join)(stage, "hub-dist");
|
|
71412
|
+
rebuildTo((0, import_node_path58.join)(root, "cli"), cliOut);
|
|
71413
|
+
rebuildTo((0, import_node_path58.join)(root, "updater"), hubOut);
|
|
70754
71414
|
const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
|
|
70755
71415
|
const rebuilt = (path2) => {
|
|
70756
71416
|
const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
|
|
70757
|
-
return spec ? readOrNull((0,
|
|
71417
|
+
return spec ? readOrNull((0, import_node_path58.join)(outDirFor(spec.packageDir), spec.output)) : null;
|
|
70758
71418
|
};
|
|
70759
|
-
const committed = (path2) => readOrNull((0,
|
|
70760
|
-
const tree = (path2) => readOrNull((0,
|
|
70761
|
-
const distRoot = (0,
|
|
70762
|
-
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0,
|
|
70763
|
-
const bom = JSON.parse((0,
|
|
71419
|
+
const committed = (path2) => readOrNull((0, import_node_path58.join)(root, path2));
|
|
71420
|
+
const tree = (path2) => readOrNull((0, import_node_path58.join)(root, path2));
|
|
71421
|
+
const distRoot = (0, import_node_path58.join)(root, "cli", "dist");
|
|
71422
|
+
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path58.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
|
|
71423
|
+
const bom = JSON.parse((0, import_node_fs63.readFileSync)(bomPathFor(root), "utf8"));
|
|
70764
71424
|
const digest = (entries) => {
|
|
70765
|
-
const overlay = (0,
|
|
71425
|
+
const overlay = (0, import_node_path58.join)(stage, `overlay-${overlayCount++}`);
|
|
70766
71426
|
for (const entry of entries) {
|
|
70767
|
-
const target = (0,
|
|
70768
|
-
(0,
|
|
70769
|
-
(0,
|
|
71427
|
+
const target = (0, import_node_path58.join)(overlay, entry.path);
|
|
71428
|
+
(0, import_node_fs63.mkdirSync)((0, import_node_path58.dirname)(target), { recursive: true });
|
|
71429
|
+
(0, import_node_fs63.writeFileSync)(target, entry.bytes);
|
|
70770
71430
|
}
|
|
70771
71431
|
return digestPackedFiles(overlay, entries.map((entry) => entry.path));
|
|
70772
71432
|
};
|
|
70773
71433
|
return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
|
|
70774
71434
|
} finally {
|
|
70775
|
-
(0,
|
|
71435
|
+
(0, import_node_fs63.rmSync)(stage, { recursive: true, force: true });
|
|
70776
71436
|
}
|
|
70777
71437
|
}
|
|
70778
71438
|
|
|
70779
71439
|
// src/doc-refs-live.ts
|
|
70780
|
-
var
|
|
71440
|
+
var import_node_path59 = require("node:path");
|
|
70781
71441
|
var concrete = (value) => !!value && !/[<>{}$]/.test(value);
|
|
70782
71442
|
var liveOption = (tokens2, flag) => {
|
|
70783
71443
|
const index = tokens2.indexOf(flag);
|
|
@@ -70850,7 +71510,7 @@ async function runLiveDocRefs(root, deps, live) {
|
|
|
70850
71510
|
const offline = runDocRefs(root, { ...deps, runbooksOnly: true });
|
|
70851
71511
|
const read = deps.readFile ?? readFileOrNull;
|
|
70852
71512
|
const docs = Object.fromEntries((deps.listDocs ?? defaultListDocs)(root).flatMap((doc) => {
|
|
70853
|
-
const body = read((0,
|
|
71513
|
+
const body = read((0, import_node_path59.join)(root, doc));
|
|
70854
71514
|
return body !== null && isRunbook(doc, body) && !deps.isIgnored?.([doc]).has(doc) ? [[doc, body]] : [];
|
|
70855
71515
|
}));
|
|
70856
71516
|
const remote = await checkRunbookLive(docs, live);
|
|
@@ -71061,7 +71721,7 @@ async function diagnoseHub(deps) {
|
|
|
71061
71721
|
|
|
71062
71722
|
// src/schedules-lift-command.ts
|
|
71063
71723
|
var import_promises12 = require("node:fs/promises");
|
|
71064
|
-
var
|
|
71724
|
+
var import_node_path60 = require("node:path");
|
|
71065
71725
|
init_clean_exit();
|
|
71066
71726
|
init_cli_shared();
|
|
71067
71727
|
var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
|
|
@@ -71090,7 +71750,7 @@ async function readWorkflowFiles(dir) {
|
|
|
71090
71750
|
const files = [];
|
|
71091
71751
|
for (const name of names.sort()) {
|
|
71092
71752
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
71093
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0,
|
|
71753
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0, import_node_path60.join)(dir, name), "utf8") });
|
|
71094
71754
|
}
|
|
71095
71755
|
return files;
|
|
71096
71756
|
}
|
|
@@ -71173,8 +71833,8 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
|
|
|
71173
71833
|
|
|
71174
71834
|
// src/spawn-policy-core.ts
|
|
71175
71835
|
var import_node_child_process24 = require("node:child_process");
|
|
71176
|
-
var
|
|
71177
|
-
var
|
|
71836
|
+
var import_node_fs64 = require("node:fs");
|
|
71837
|
+
var import_node_path61 = require("node:path");
|
|
71178
71838
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
71179
71839
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
71180
71840
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -71260,7 +71920,7 @@ function runSpawnPolicy(root) {
|
|
|
71260
71920
|
for (const file of files) {
|
|
71261
71921
|
let raw;
|
|
71262
71922
|
try {
|
|
71263
|
-
raw = (0,
|
|
71923
|
+
raw = (0, import_node_fs64.readFileSync)((0, import_node_path61.join)(root, file), "utf8");
|
|
71264
71924
|
} catch {
|
|
71265
71925
|
continue;
|
|
71266
71926
|
}
|
|
@@ -71280,19 +71940,19 @@ function runSpawnPolicy(root) {
|
|
|
71280
71940
|
function registerDeveloperCommands(program3) {
|
|
71281
71941
|
const rules = program3.command("rules").description("org-managed .gitignore delivery");
|
|
71282
71942
|
rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").option("--json", "machine-readable output").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
|
|
71283
|
-
const path2 = (0,
|
|
71284
|
-
const current = (0,
|
|
71943
|
+
const path2 = (0, import_node_path62.join)(process.cwd(), ".gitignore");
|
|
71944
|
+
const current = (0, import_node_fs65.existsSync)(path2) ? (0, import_node_fs65.readFileSync)(path2, "utf8") : null;
|
|
71285
71945
|
const plan = planManagedGitignore(current);
|
|
71286
71946
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
71287
71947
|
if (opts.json) {
|
|
71288
|
-
if (opts.write && plan.changed) (0,
|
|
71948
|
+
if (opts.write && plan.changed) (0, import_node_fs65.writeFileSync)(path2, plan.content, "utf8");
|
|
71289
71949
|
console.log(JSON.stringify(plan, null, 2));
|
|
71290
71950
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
71291
71951
|
return;
|
|
71292
71952
|
}
|
|
71293
71953
|
if (opts.write) {
|
|
71294
71954
|
if (plan.changed) {
|
|
71295
|
-
(0,
|
|
71955
|
+
(0, import_node_fs65.writeFileSync)(path2, plan.content, "utf8");
|
|
71296
71956
|
console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
|
|
71297
71957
|
} else {
|
|
71298
71958
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
@@ -71609,7 +72269,7 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
71609
72269
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
71610
72270
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
71611
72271
|
function envHealLockPath(home) {
|
|
71612
|
-
return (0,
|
|
72272
|
+
return (0, import_node_path63.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
71613
72273
|
}
|
|
71614
72274
|
async function withEnvHealLock(what, run) {
|
|
71615
72275
|
try {
|
|
@@ -71747,7 +72407,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
71747
72407
|
);
|
|
71748
72408
|
const result = applyPluginCachePlan(
|
|
71749
72409
|
plan,
|
|
71750
|
-
(p) => (0,
|
|
72410
|
+
(p) => (0, import_node_fs66.rmSync)(p, { recursive: true }),
|
|
71751
72411
|
stagingApplyFsGuard(configRoot)
|
|
71752
72412
|
);
|
|
71753
72413
|
return {
|
|
@@ -71782,7 +72442,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
71782
72442
|
// has no generated routing index would
|
|
71783
72443
|
// get a permanent — demanding an artifact it never asked for.
|
|
71784
72444
|
docsIndexState: (root) => {
|
|
71785
|
-
if (!(0,
|
|
72445
|
+
if (!(0, import_node_fs66.existsSync)((0, import_node_path63.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
71786
72446
|
const real = createDocsIndexDeps(root);
|
|
71787
72447
|
let docs;
|
|
71788
72448
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -71791,7 +72451,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
71791
72451
|
},
|
|
71792
72452
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
71793
72453
|
healDocsIndex: (root) => {
|
|
71794
|
-
if (!(0,
|
|
72454
|
+
if (!(0, import_node_fs66.existsSync)((0, import_node_path63.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
71795
72455
|
const real = createDocsIndexDeps(root);
|
|
71796
72456
|
let docs;
|
|
71797
72457
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -72193,7 +72853,9 @@ registerPreAction.call(program2, "preAction", async (_thisCommand, actionCommand
|
|
|
72193
72853
|
"vars create",
|
|
72194
72854
|
"vars update",
|
|
72195
72855
|
"vars delete",
|
|
72196
|
-
"repo archive-status"
|
|
72856
|
+
"repo archive-status",
|
|
72857
|
+
// #9788: the lifecycle read validates its manifest/window/bounds before its scoped mint.
|
|
72858
|
+
"read merge-queue lifecycle"
|
|
72197
72859
|
]);
|
|
72198
72860
|
if (LAZY_APP_MINT_COMMANDS.has(commandPath4)) return;
|
|
72199
72861
|
try {
|
|
@@ -72592,7 +73254,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
72592
73254
|
if (dupe) return fail(`org project set: KEY "${dupe}" was passed to both --var and --set; --set is an alias of --var, so pass each KEY once`);
|
|
72593
73255
|
if (o.secretsFile) {
|
|
72594
73256
|
try {
|
|
72595
|
-
vars.push(`secrets=${(0,
|
|
73257
|
+
vars.push(`secrets=${(0, import_node_fs66.readFileSync)(o.secretsFile, "utf8")}`);
|
|
72596
73258
|
} catch (e) {
|
|
72597
73259
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
72598
73260
|
}
|
|
@@ -72984,16 +73646,16 @@ function ciAuditDeps2() {
|
|
|
72984
73646
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
72985
73647
|
readSeedFile: (path2) => {
|
|
72986
73648
|
if (!root) return null;
|
|
72987
|
-
const fullPath = (0,
|
|
72988
|
-
return (0,
|
|
73649
|
+
const fullPath = (0, import_node_path63.join)(root, path2);
|
|
73650
|
+
return (0, import_node_fs66.existsSync)(fullPath) ? (0, import_node_fs66.readFileSync)(fullPath, "utf8") : null;
|
|
72989
73651
|
}
|
|
72990
73652
|
};
|
|
72991
73653
|
}
|
|
72992
73654
|
function hubRoot2() {
|
|
72993
|
-
const fromPkg = (0,
|
|
73655
|
+
const fromPkg = (0, import_node_path63.join)(__dirname, "..", "..");
|
|
72994
73656
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
72995
|
-
if ((0,
|
|
72996
|
-
if ((0,
|
|
73657
|
+
if ((0, import_node_fs66.existsSync)((0, import_node_path63.join)(fromPkg, marker))) return fromPkg;
|
|
73658
|
+
if ((0, import_node_fs66.existsSync)((0, import_node_path63.join)(process.cwd(), marker))) return process.cwd();
|
|
72997
73659
|
return null;
|
|
72998
73660
|
}
|
|
72999
73661
|
registerQueryCommands(program2);
|
|
@@ -73046,6 +73708,11 @@ var varsProjects = async () => fetchProjectsList(registryClientDeps(await loadCo
|
|
|
73046
73708
|
var varsActor = () => ({ credential: "app_installation", ...activeAppQuotaScope() ? { quotaScope: activeAppQuotaScope() } : {} });
|
|
73047
73709
|
registerDevopsVarsCommands(program2, { appClient: (commandPath4) => varsAppClient(commandPath4)(), projects: varsProjects, actor: varsActor });
|
|
73048
73710
|
registerRepoArchiveStatusCommand(program2, { appClient: (commandPath4) => varsAppClient(commandPath4)(), projects: varsProjects, actor: varsActor });
|
|
73711
|
+
registerMergeQueueLifecycleCommand(program2.commands.find((c) => c.name() === "read"), {
|
|
73712
|
+
appClient: async (commandPath4) => createGitHubClient({ token: async () => activeAppToken() ?? (await mintCommandWriteToken(appActorDeps(), commandPath4)).token }),
|
|
73713
|
+
projects: varsProjects,
|
|
73714
|
+
actor: varsActor
|
|
73715
|
+
});
|
|
73049
73716
|
ci.command("reconcile").description("audit + optionally apply merge settings and product ruleset activation (master-admin)").option("--json", "machine-readable output").option("--repo <owner/repo>", "reconcile one repo instead of the full registry").option("--apply", "PATCH merge settings + activate product ruleset when missing (master role required); activation is skipped while the repo's gate has never passed (#3694)").option("--park-ruleset", "stop the product ruleset enforcing without deleting it \u2014 keeps its required contexts (master role required)").action(async (o) => {
|
|
73050
73717
|
if (o.apply && o.parkRuleset) {
|
|
73051
73718
|
return fail("ci reconcile: --apply and --park-ruleset ask for opposite things; pass one");
|
|
@@ -73145,10 +73812,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
73145
73812
|
targets = resolution.targets;
|
|
73146
73813
|
}
|
|
73147
73814
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
73148
|
-
const fileMatrix = (0,
|
|
73815
|
+
const fileMatrix = (0, import_node_fs66.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs66.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
73149
73816
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
73150
73817
|
const dataAccess = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
73151
|
-
const sanctioned = (0,
|
|
73818
|
+
const sanctioned = (0, import_node_fs66.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs66.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
73152
73819
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
73153
73820
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
73154
73821
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -73185,16 +73852,16 @@ function directoryBytes(path2) {
|
|
|
73185
73852
|
let total = 0;
|
|
73186
73853
|
let entries;
|
|
73187
73854
|
try {
|
|
73188
|
-
entries = (0,
|
|
73855
|
+
entries = (0, import_node_fs66.readdirSync)(path2, { withFileTypes: true });
|
|
73189
73856
|
} catch {
|
|
73190
73857
|
return 0;
|
|
73191
73858
|
}
|
|
73192
73859
|
for (const entry of entries) {
|
|
73193
|
-
const child2 = (0,
|
|
73860
|
+
const child2 = (0, import_node_path63.join)(path2, entry.name);
|
|
73194
73861
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
73195
73862
|
else {
|
|
73196
73863
|
try {
|
|
73197
|
-
total += (0,
|
|
73864
|
+
total += (0, import_node_fs66.statSync)(child2).size;
|
|
73198
73865
|
} catch {
|
|
73199
73866
|
}
|
|
73200
73867
|
}
|
|
@@ -73202,25 +73869,25 @@ function directoryBytes(path2) {
|
|
|
73202
73869
|
return total;
|
|
73203
73870
|
}
|
|
73204
73871
|
function listDirEntries(dir) {
|
|
73205
|
-
return (0,
|
|
73872
|
+
return (0, import_node_fs66.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
73206
73873
|
}
|
|
73207
73874
|
function readInstalledPluginRefs(configRoot) {
|
|
73208
73875
|
const p = installedPluginsPathForConfig(configRoot);
|
|
73209
|
-
if (!(0,
|
|
73876
|
+
if (!(0, import_node_fs66.existsSync)(p)) return [];
|
|
73210
73877
|
try {
|
|
73211
|
-
return installedPluginPaths((0,
|
|
73878
|
+
return installedPluginPaths((0, import_node_fs66.readFileSync)(p, "utf8"));
|
|
73212
73879
|
} catch {
|
|
73213
73880
|
return null;
|
|
73214
73881
|
}
|
|
73215
73882
|
}
|
|
73216
73883
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
73217
73884
|
return {
|
|
73218
|
-
exists: (p) => (0,
|
|
73219
|
-
listVersionDirs: (root) => (0,
|
|
73885
|
+
exists: (p) => (0, import_node_fs66.existsSync)(p),
|
|
73886
|
+
listVersionDirs: (root) => (0, import_node_fs66.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
73220
73887
|
dirBytes,
|
|
73221
|
-
listStagingDirs: (root) => (0,
|
|
73888
|
+
listStagingDirs: (root) => (0, import_node_fs66.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
73222
73889
|
try {
|
|
73223
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
73890
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path63.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs66.statSync)(p).mtimeMs) };
|
|
73224
73891
|
} catch {
|
|
73225
73892
|
return { name: d.name, mtimeMs: Date.now() };
|
|
73226
73893
|
}
|
|
@@ -73234,10 +73901,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
73234
73901
|
return {
|
|
73235
73902
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
73236
73903
|
mtimeMs: (name) => {
|
|
73237
|
-
const p = (0,
|
|
73238
|
-
if (!(0,
|
|
73904
|
+
const p = (0, import_node_path63.join)(stagingRoot, name);
|
|
73905
|
+
if (!(0, import_node_fs66.existsSync)(p)) return null;
|
|
73239
73906
|
try {
|
|
73240
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
73907
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs66.statSync)(q).mtimeMs);
|
|
73241
73908
|
} catch {
|
|
73242
73909
|
return null;
|
|
73243
73910
|
}
|
|
@@ -73263,7 +73930,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
73263
73930
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
73264
73931
|
);
|
|
73265
73932
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
73266
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
73933
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs66.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
73267
73934
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
73268
73935
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
73269
73936
|
else console.log(renderPluginCachePlan(plan, result));
|
|
@@ -73273,7 +73940,7 @@ program2.command("archive-prune").description("inventory worktree evidence archi
|
|
|
73273
73940
|
let primary = o.repoRoot;
|
|
73274
73941
|
if (!primary) {
|
|
73275
73942
|
const common = await gitOut(["rev-parse", "--path-format=absolute", "--git-common-dir"]);
|
|
73276
|
-
primary = common ? (0,
|
|
73943
|
+
primary = common ? (0, import_node_path63.dirname)(common) : "";
|
|
73277
73944
|
}
|
|
73278
73945
|
if (!primary) {
|
|
73279
73946
|
const detail = "archive-prune: not inside a git checkout (pass --repo-root <primary checkout>)";
|