@mutmutco/cli 3.135.0 → 3.136.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.cjs +612 -390
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3416,7 +3416,7 @@ var program = new Command();
|
|
|
3416
3416
|
|
|
3417
3417
|
// src/index.ts
|
|
3418
3418
|
var import_promises12 = require("node:fs/promises");
|
|
3419
|
-
var
|
|
3419
|
+
var import_node_fs48 = require("node:fs");
|
|
3420
3420
|
var import_node_child_process19 = require("node:child_process");
|
|
3421
3421
|
|
|
3422
3422
|
// src/cli-shared.ts
|
|
@@ -5502,7 +5502,9 @@ function parseWorktreeOwners(text) {
|
|
|
5502
5502
|
return parsed.entries.filter((e) => {
|
|
5503
5503
|
if (!e || typeof e !== "object") return false;
|
|
5504
5504
|
const entry = e;
|
|
5505
|
-
|
|
5505
|
+
const provenance = entry.provenance;
|
|
5506
|
+
const provenanceValid = provenance === void 0 || typeof provenance === "object" && typeof provenance.creationBaseOid === "string" && /^[0-9a-f]{40,64}$/i.test(provenance.creationBaseOid) && typeof provenance.workerBranch === "string" && provenance.workerBranch.length > 0;
|
|
5507
|
+
return typeof entry.path === "string" && entry.path.length > 0 && typeof entry.branch === "string" && typeof entry.createdAt === "string" && typeof entry.lastSeenAt === "string" && isActor(entry.actor) && provenanceValid;
|
|
5506
5508
|
});
|
|
5507
5509
|
}
|
|
5508
5510
|
function serializeWorktreeOwners(entries) {
|
|
@@ -6400,6 +6402,8 @@ function buildGcPlan(inputs) {
|
|
|
6400
6402
|
const preservedBranches2 = new Set(inputs.preservedBranches ?? []);
|
|
6401
6403
|
const preservedWorktrees = new Set((inputs.worktrees ?? []).filter((w) => w.preserved).map((w) => w.branch));
|
|
6402
6404
|
const mergedIntoBase = new Set((inputs.mergedIntoBase ?? []).map((b) => b.trim()).filter(Boolean));
|
|
6405
|
+
const exactTreeLanded = new Map((inputs.exactTreeLanded ?? []).map((p) => [p.branch.trim(), p]));
|
|
6406
|
+
const exactTreeRefusals = new Map((inputs.exactTreeRefusals ?? []).map((p) => [p.branch.trim(), p.detail]));
|
|
6403
6407
|
const prLookupFailures = new Map((inputs.prLookupFailures ?? []).map((f) => [f.branch.trim(), f.detail]));
|
|
6404
6408
|
const skipped = [];
|
|
6405
6409
|
const branches = [];
|
|
@@ -6426,8 +6430,10 @@ function buildGcPlan(inputs) {
|
|
|
6426
6430
|
skipped.push({ branch, reason: "open-pr" });
|
|
6427
6431
|
continue;
|
|
6428
6432
|
}
|
|
6429
|
-
const
|
|
6430
|
-
|
|
6433
|
+
const exactDelivery = exactTreeLanded.get(branch);
|
|
6434
|
+
const exactRefusal = exactTreeRefusals.get(branch);
|
|
6435
|
+
const state = closedState(prSet) ?? (exactDelivery ? { state: "EXACT_TREE", numbers: [], headOids: [] } : null);
|
|
6436
|
+
if (!state && !exactRefusal) continue;
|
|
6431
6437
|
if (branch === inputs.currentBranch) {
|
|
6432
6438
|
skipped.push({ branch, reason: "current-branch" });
|
|
6433
6439
|
skipTrackingBranches.add(branch);
|
|
@@ -6444,8 +6450,14 @@ function buildGcPlan(inputs) {
|
|
|
6444
6450
|
skipTrackingBranches.add(branch);
|
|
6445
6451
|
continue;
|
|
6446
6452
|
}
|
|
6453
|
+
if (!state) {
|
|
6454
|
+
skipped.push({ branch, reason: "exact-tree-proof-refused", detail: exactRefusal });
|
|
6455
|
+
skipTrackingBranches.add(branch);
|
|
6456
|
+
continue;
|
|
6457
|
+
}
|
|
6447
6458
|
const localHead = branchHeads?.get(branch);
|
|
6448
|
-
const
|
|
6459
|
+
const exactHeadStillMatches = Boolean(exactDelivery && localHead === exactDelivery.headOid);
|
|
6460
|
+
const containedInBase = mergedIntoBase.has(branch) || exactHeadStillMatches;
|
|
6449
6461
|
if (!containedInBase && (worktree2?.unpushed || branchHeads && (!localHead || !state.headOids.length || !state.headOids.includes(localHead)))) {
|
|
6450
6462
|
const detail = worktree2?.unpushed ? worktree2.path : localHead && state.headOids.length ? `${localHead} != ${state.headOids.join("|")}` : "local branch head could not be verified against the PR head";
|
|
6451
6463
|
skipped.push({ branch, reason: "unpushed-branch", detail });
|
|
@@ -6458,7 +6470,8 @@ function buildGcPlan(inputs) {
|
|
|
6458
6470
|
prNumbers: state.numbers,
|
|
6459
6471
|
worktreePath: worktree2?.path,
|
|
6460
6472
|
...state.headOids.length ? { reviewedHeadOids: state.headOids } : {},
|
|
6461
|
-
...containedInBase ? { containedInBase: true, ...localHead ? { plannedHeadOid: localHead } : {} } : {}
|
|
6473
|
+
...containedInBase ? { containedInBase: true, ...localHead ? { plannedHeadOid: localHead } : {} } : {},
|
|
6474
|
+
...exactHeadStillMatches ? { deliveredBy: exactDelivery.candidateOid } : {}
|
|
6462
6475
|
});
|
|
6463
6476
|
}
|
|
6464
6477
|
const trackingRefs = [...new Set(inputs.staleTrackingRefs ?? [])].map((ref) => {
|
|
@@ -7060,7 +7073,8 @@ function formatGcPlan(plan, apply, auditClone) {
|
|
|
7060
7073
|
for (const b of plan.branches) {
|
|
7061
7074
|
const prs = b.prNumbers.length ? ` #${b.prNumbers.join(",#")}` : "";
|
|
7062
7075
|
const wt = b.worktreePath ? ` (worktree: ${b.worktreePath})` : "";
|
|
7063
|
-
|
|
7076
|
+
const authority = b.deliveredBy ? `${b.prState}; deliveredBy ${b.deliveredBy}` : `${b.prState}${prs}`;
|
|
7077
|
+
lines.push(` - ${b.branch} (${authority})${wt}`);
|
|
7064
7078
|
}
|
|
7065
7079
|
}
|
|
7066
7080
|
const remoteBranches = plan.branches.filter((b) => (b.reviewedHeadOids?.length ?? 0) > 0);
|
|
@@ -7869,7 +7883,7 @@ function commandLadderHint() {
|
|
|
7869
7883
|
}
|
|
7870
7884
|
|
|
7871
7885
|
// src/index.ts
|
|
7872
|
-
var
|
|
7886
|
+
var import_node_path46 = require("node:path");
|
|
7873
7887
|
|
|
7874
7888
|
// src/merge-ci-policy.ts
|
|
7875
7889
|
function resolveMergeCiPolicy(input) {
|
|
@@ -9752,9 +9766,9 @@ function parseVerifyBroker(stdout) {
|
|
|
9752
9766
|
}
|
|
9753
9767
|
|
|
9754
9768
|
// src/train-apply.ts
|
|
9755
|
-
var
|
|
9769
|
+
var import_node_fs23 = require("node:fs");
|
|
9756
9770
|
var import_promises4 = require("node:fs/promises");
|
|
9757
|
-
var
|
|
9771
|
+
var import_node_path23 = require("node:path");
|
|
9758
9772
|
|
|
9759
9773
|
// src/plugin-guard-io.ts
|
|
9760
9774
|
var import_node_fs19 = require("node:fs");
|
|
@@ -11186,9 +11200,9 @@ function renderAccessReport(report) {
|
|
|
11186
11200
|
}
|
|
11187
11201
|
|
|
11188
11202
|
// src/cli-doctor-shared.ts
|
|
11189
|
-
var import_node_fs20 = require("node:fs");
|
|
11190
|
-
var import_node_path21 = require("node:path");
|
|
11191
11203
|
var import_node_fs21 = require("node:fs");
|
|
11204
|
+
var import_node_path22 = require("node:path");
|
|
11205
|
+
var import_node_fs22 = require("node:fs");
|
|
11192
11206
|
|
|
11193
11207
|
// ../infra/registry-endpoints.mjs
|
|
11194
11208
|
var PROJECTS_LIST_PATH = "/projects/list";
|
|
@@ -13630,6 +13644,142 @@ async function fileReport(deps, req) {
|
|
|
13630
13644
|
return { ok: true, body };
|
|
13631
13645
|
}
|
|
13632
13646
|
|
|
13647
|
+
// src/worktree-delivery-proof.ts
|
|
13648
|
+
var import_node_fs20 = require("node:fs");
|
|
13649
|
+
var import_node_os9 = require("node:os");
|
|
13650
|
+
var import_node_path21 = require("node:path");
|
|
13651
|
+
var OID_RE = /^[0-9a-f]{40,64}$/i;
|
|
13652
|
+
var EXACT_TREE_CANDIDATE_LIMIT = 32;
|
|
13653
|
+
async function repointDeliveredWorktreeTransactionally(input) {
|
|
13654
|
+
const symbolic = (await input.git(["symbolic-ref", "--quiet", "--short", "HEAD"]).catch(() => "")).trim();
|
|
13655
|
+
if (symbolic !== input.branch) return { action: "refuse", reason: "wrong-branch", detail: symbolic || "detached HEAD" };
|
|
13656
|
+
const current = (await input.git(["rev-parse", "--verify", `refs/heads/${input.branch}`]).catch(() => "")).trim();
|
|
13657
|
+
if (current !== input.expectedWorkerOid) {
|
|
13658
|
+
return { action: "refuse", reason: "worker-moved", detail: `${current || "unreadable"} != ${input.expectedWorkerOid}` };
|
|
13659
|
+
}
|
|
13660
|
+
const status = await input.git(["status", "--porcelain"]).catch(() => "unreadable");
|
|
13661
|
+
if (status.trim()) return { action: "refuse", reason: "dirty-worktree", detail: "tracked or untracked files present" };
|
|
13662
|
+
try {
|
|
13663
|
+
await input.git(["checkout", "-B", input.branch, input.landedTipOid]);
|
|
13664
|
+
return { action: "repointed" };
|
|
13665
|
+
} catch (error) {
|
|
13666
|
+
return { action: "refuse", reason: "checkout-failed", detail: error instanceof Error ? error.message : String(error) };
|
|
13667
|
+
}
|
|
13668
|
+
}
|
|
13669
|
+
function supportsExactTreeProofGit(versionOutput) {
|
|
13670
|
+
const match = /\bgit version (\d+)\.(\d+)(?:\.\d+)?/i.exec(versionOutput.trim());
|
|
13671
|
+
if (!match) return false;
|
|
13672
|
+
const major = Number(match[1]);
|
|
13673
|
+
const minor = Number(match[2]);
|
|
13674
|
+
return major > 2 || major === 2 && minor >= 32;
|
|
13675
|
+
}
|
|
13676
|
+
function describeExactTreeRefusal(verdict) {
|
|
13677
|
+
return `${verdict.reason}${verdict.detail ? `: ${verdict.detail}` : ""}`;
|
|
13678
|
+
}
|
|
13679
|
+
function sameChangedPathSet(a, b) {
|
|
13680
|
+
const sorted = (value) => value.split("\0").filter(Boolean).sort().join("\0");
|
|
13681
|
+
return sorted(a) === sorted(b);
|
|
13682
|
+
}
|
|
13683
|
+
async function proveExactTreeDelivery(input, deps = {}) {
|
|
13684
|
+
const base = input.creationBaseOid?.trim();
|
|
13685
|
+
if (!base || !OID_RE.test(base)) return { action: "refuse", reason: "missing-provenance" };
|
|
13686
|
+
if (!input.recordedWorkerBranch || input.recordedWorkerBranch !== input.branch) {
|
|
13687
|
+
return { action: "refuse", reason: "worker-identity-mismatch" };
|
|
13688
|
+
}
|
|
13689
|
+
if (!OID_RE.test(input.workerOid)) return { action: "refuse", reason: "proof-failed", detail: "worker OID is invalid" };
|
|
13690
|
+
const run = deps.git ?? (async (args, env) => (await execFileP2("git", ["-C", input.repoRoot, ...args], {
|
|
13691
|
+
timeout: GIT_TIMEOUT_MS,
|
|
13692
|
+
...env ? { env: { ...process.env, ...env } } : {}
|
|
13693
|
+
})).stdout);
|
|
13694
|
+
const isAncestor = (ancestor, descendant) => run(["merge-base", "--is-ancestor", ancestor, descendant]).then(() => true).catch(() => false);
|
|
13695
|
+
let version;
|
|
13696
|
+
try {
|
|
13697
|
+
version = await run(["version"]);
|
|
13698
|
+
} catch {
|
|
13699
|
+
return { action: "refuse", reason: "proof-failed", detail: "Git capability probe failed" };
|
|
13700
|
+
}
|
|
13701
|
+
if (!supportsExactTreeProofGit(version)) {
|
|
13702
|
+
return { action: "refuse", reason: "unsupported-git", detail: `requires Git >=2.32; found ${version.trim() || "unparseable version"}` };
|
|
13703
|
+
}
|
|
13704
|
+
try {
|
|
13705
|
+
const [baseType, workerType] = await Promise.all([
|
|
13706
|
+
run(["cat-file", "-t", base]).then((v) => v.trim()).catch(() => ""),
|
|
13707
|
+
run(["cat-file", "-t", input.workerOid]).then((v) => v.trim()).catch(() => "")
|
|
13708
|
+
]);
|
|
13709
|
+
if (baseType !== "commit" || workerType !== "commit" || !await isAncestor(base, input.workerOid)) {
|
|
13710
|
+
return { action: "refuse", reason: "unverified-base" };
|
|
13711
|
+
}
|
|
13712
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
13713
|
+
for (const tip of [...new Set(input.landedTips.map((v) => v.trim()).filter(Boolean))]) {
|
|
13714
|
+
if (!OID_RE.test(tip) || !await isAncestor(base, tip)) continue;
|
|
13715
|
+
const listed = await run([
|
|
13716
|
+
"rev-list",
|
|
13717
|
+
"--first-parent",
|
|
13718
|
+
"--parents",
|
|
13719
|
+
`--max-count=${EXACT_TREE_CANDIDATE_LIMIT + 1}`,
|
|
13720
|
+
`${base}..${tip}`
|
|
13721
|
+
]);
|
|
13722
|
+
const rows = listed.split(/\r?\n/).map((v) => v.trim()).filter(Boolean);
|
|
13723
|
+
if (rows.length > EXACT_TREE_CANDIDATE_LIMIT) {
|
|
13724
|
+
return {
|
|
13725
|
+
action: "refuse",
|
|
13726
|
+
reason: "candidate-limit-exceeded",
|
|
13727
|
+
detail: `more than ${EXACT_TREE_CANDIDATE_LIMIT} first-parent candidates since stored base`
|
|
13728
|
+
};
|
|
13729
|
+
}
|
|
13730
|
+
for (const row of rows) {
|
|
13731
|
+
const [oid, parent] = row.split(/\s+/);
|
|
13732
|
+
if (oid && parent && OID_RE.test(oid) && OID_RE.test(parent)) candidates.set(oid, parent);
|
|
13733
|
+
}
|
|
13734
|
+
if (candidates.size > EXACT_TREE_CANDIDATE_LIMIT) {
|
|
13735
|
+
return {
|
|
13736
|
+
action: "refuse",
|
|
13737
|
+
reason: "candidate-limit-exceeded",
|
|
13738
|
+
detail: `more than ${EXACT_TREE_CANDIDATE_LIMIT} unique first-parent candidates across landed tips`
|
|
13739
|
+
};
|
|
13740
|
+
}
|
|
13741
|
+
}
|
|
13742
|
+
if (!candidates.size) return { action: "refuse", reason: "no-candidate" };
|
|
13743
|
+
const workerPaths = await run(["diff", "--name-only", "-z", "--no-renames", base, input.workerOid]);
|
|
13744
|
+
const pathMatches = [];
|
|
13745
|
+
for (const [candidate, parent] of candidates) {
|
|
13746
|
+
const candidatePaths = await run(["diff", "--name-only", "-z", "--no-renames", parent, candidate]);
|
|
13747
|
+
if (sameChangedPathSet(workerPaths, candidatePaths)) pathMatches.push([candidate, parent]);
|
|
13748
|
+
}
|
|
13749
|
+
if (!pathMatches.length) return { action: "refuse", reason: "tree-mismatch" };
|
|
13750
|
+
const makeTempDir = deps.makeTempDir ?? (() => (0, import_node_fs20.mkdtempSync)((0, import_node_path21.join)((0, import_node_os9.tmpdir)(), "mmi-tree-proof-")));
|
|
13751
|
+
const writeTextFile = deps.writeTextFile ?? ((path2, text) => (0, import_node_fs20.writeFileSync)(path2, text, "utf8"));
|
|
13752
|
+
const removeTempDir = deps.removeTempDir ?? ((path2) => (0, import_node_fs20.rmSync)(path2, { recursive: true, force: true }));
|
|
13753
|
+
const root = makeTempDir();
|
|
13754
|
+
try {
|
|
13755
|
+
const synthesize = async (name, from, to) => {
|
|
13756
|
+
const index = (0, import_node_path21.join)(root, `${name}.index`);
|
|
13757
|
+
const patch = (0, import_node_path21.join)(root, `${name}.patch`);
|
|
13758
|
+
const env = { GIT_INDEX_FILE: index };
|
|
13759
|
+
await run(["read-tree", base], env);
|
|
13760
|
+
const delta = await run(["diff-tree", "-p", "--binary", "--full-index", "--no-renames", "--no-ext-diff", from, to]);
|
|
13761
|
+
writeTextFile(patch, delta);
|
|
13762
|
+
if (delta.length) await run(["apply", "--cached", "--binary", "--3way", patch], env);
|
|
13763
|
+
return (await run(["write-tree"], env)).trim();
|
|
13764
|
+
};
|
|
13765
|
+
const workerTreeOid = await synthesize("worker", base, input.workerOid);
|
|
13766
|
+
const matches = [];
|
|
13767
|
+
let candidateNumber = 0;
|
|
13768
|
+
for (const [candidate, parent] of pathMatches) {
|
|
13769
|
+
const tree = await synthesize(`candidate-${candidateNumber++}`, parent, candidate).catch(() => void 0);
|
|
13770
|
+
if (tree === workerTreeOid) matches.push(candidate);
|
|
13771
|
+
}
|
|
13772
|
+
if (matches.length === 1) return { action: "settle", workerTreeOid, candidateOid: matches[0] };
|
|
13773
|
+
if (matches.length > 1) return { action: "refuse", reason: "ambiguous-candidate" };
|
|
13774
|
+
return { action: "refuse", reason: "tree-mismatch" };
|
|
13775
|
+
} finally {
|
|
13776
|
+
removeTempDir(root);
|
|
13777
|
+
}
|
|
13778
|
+
} catch (error) {
|
|
13779
|
+
return { action: "refuse", reason: "proof-failed", detail: error instanceof Error ? error.message : String(error) };
|
|
13780
|
+
}
|
|
13781
|
+
}
|
|
13782
|
+
|
|
13633
13783
|
// src/cli-doctor-shared.ts
|
|
13634
13784
|
var GC_GH_TIMEOUT_MS = 2e4;
|
|
13635
13785
|
var RUN_LIST_TIMEOUT_MS = 2e4;
|
|
@@ -13732,7 +13882,7 @@ async function localBranchHeads() {
|
|
|
13732
13882
|
}
|
|
13733
13883
|
async function currentRepoWorktreeGitRoot(repoRoot2) {
|
|
13734
13884
|
const gitCommonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
13735
|
-
return gitCommonDir ? (0,
|
|
13885
|
+
return gitCommonDir ? (0, import_node_path22.resolve)(repoRoot2, gitCommonDir, "worktrees") : "";
|
|
13736
13886
|
}
|
|
13737
13887
|
async function worktreeBranches() {
|
|
13738
13888
|
const { stdout } = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS });
|
|
@@ -13752,18 +13902,18 @@ function resolveGitdirForWorktreeFile(worktreePath, content) {
|
|
|
13752
13902
|
const match = /^gitdir:\s*(.+)\s*$/im.exec(content);
|
|
13753
13903
|
if (!match?.[1]) return void 0;
|
|
13754
13904
|
const raw = match[1].trim();
|
|
13755
|
-
return (0,
|
|
13905
|
+
return (0, import_node_path22.isAbsolute)(raw) ? raw : (0, import_node_path22.resolve)(worktreePath, raw);
|
|
13756
13906
|
}
|
|
13757
13907
|
function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
13758
13908
|
if (!worktreeGitRoot) return false;
|
|
13759
13909
|
try {
|
|
13760
|
-
const entries = (0,
|
|
13910
|
+
const entries = (0, import_node_fs22.readdirSync)(worktreeGitRoot, { withFileTypes: true });
|
|
13761
13911
|
for (const ent of entries) {
|
|
13762
13912
|
if (!ent.isDirectory()) continue;
|
|
13763
13913
|
try {
|
|
13764
|
-
const gitdirPath = (0,
|
|
13765
|
-
const resolvedGitdir = (0,
|
|
13766
|
-
if (sameWorktreeMetadataPath((0,
|
|
13914
|
+
const gitdirPath = (0, import_node_fs21.readFileSync)((0, import_node_path22.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
|
|
13915
|
+
const resolvedGitdir = (0, import_node_path22.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path22.resolve)(worktreeGitRoot, ent.name, gitdirPath);
|
|
13916
|
+
if (sameWorktreeMetadataPath((0, import_node_path22.dirname)(resolvedGitdir), worktreePath)) return true;
|
|
13767
13917
|
} catch {
|
|
13768
13918
|
}
|
|
13769
13919
|
}
|
|
@@ -13773,7 +13923,7 @@ function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
|
13773
13923
|
}
|
|
13774
13924
|
function pathExistsKnown(path2) {
|
|
13775
13925
|
try {
|
|
13776
|
-
(0,
|
|
13926
|
+
(0, import_node_fs22.statSync)(path2);
|
|
13777
13927
|
return true;
|
|
13778
13928
|
} catch (e) {
|
|
13779
13929
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
@@ -13782,10 +13932,10 @@ function pathExistsKnown(path2) {
|
|
|
13782
13932
|
}
|
|
13783
13933
|
}
|
|
13784
13934
|
function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
13785
|
-
const gitPath = (0,
|
|
13935
|
+
const gitPath = (0, import_node_path22.join)(path2, ".git");
|
|
13786
13936
|
let st;
|
|
13787
13937
|
try {
|
|
13788
|
-
st = (0,
|
|
13938
|
+
st = (0, import_node_fs22.lstatSync)(gitPath);
|
|
13789
13939
|
} catch (e) {
|
|
13790
13940
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
13791
13941
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
@@ -13802,7 +13952,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
|
13802
13952
|
if (st.isDirectory()) return { path: path2, gitType: "dir" };
|
|
13803
13953
|
if (!st.isFile()) return { path: path2, gitType: "other" };
|
|
13804
13954
|
try {
|
|
13805
|
-
const gitFileContent = (0,
|
|
13955
|
+
const gitFileContent = (0, import_node_fs21.readFileSync)(gitPath, "utf8");
|
|
13806
13956
|
const gitdir = resolveGitdirForWorktreeFile(path2, gitFileContent);
|
|
13807
13957
|
const gitDirExists = gitdir ? pathExistsKnown(gitdir) : false;
|
|
13808
13958
|
return {
|
|
@@ -13819,7 +13969,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
|
13819
13969
|
}
|
|
13820
13970
|
function inspectDeadWorktreeDirContent(path2) {
|
|
13821
13971
|
try {
|
|
13822
|
-
return { entries: (0,
|
|
13972
|
+
return { entries: (0, import_node_fs22.readdirSync)(path2) };
|
|
13823
13973
|
} catch (e) {
|
|
13824
13974
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
13825
13975
|
return { error: code ? `unable to inspect directory contents (${code})` : "unable to inspect directory contents" };
|
|
@@ -13836,11 +13986,11 @@ async function preservedBranches() {
|
|
|
13836
13986
|
async function siblingWorktreeDirs(explicitRoot) {
|
|
13837
13987
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
13838
13988
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
13839
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
13989
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path22.dirname)((0, import_node_path22.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
13840
13990
|
try {
|
|
13841
13991
|
const dirs = explicitRoot ? listDirsIn(resolveExplicitScanRoot(explicitRoot, primaryRepoRoot)) : worktreeScanDirs(siblingMmiWorktreesRoot(primaryRepoRoot), primaryRepoRoot, listDirsIn, isRepoCheckoutDir);
|
|
13842
13992
|
const agentDirs = listDirsIn(agentWorktreesRoot(primaryRepoRoot));
|
|
13843
|
-
const repoLocalWorktrees = listDirsIn((0,
|
|
13993
|
+
const repoLocalWorktrees = listDirsIn((0, import_node_path22.join)(primaryRepoRoot, ".worktrees"));
|
|
13844
13994
|
return [...dirs, ...agentDirs, ...repoLocalWorktrees].map((dir) => inspectSiblingWorktreeDir(dir, worktreeGitRoot)).filter((entry) => Boolean(entry));
|
|
13845
13995
|
} catch {
|
|
13846
13996
|
return [];
|
|
@@ -13848,18 +13998,18 @@ async function siblingWorktreeDirs(explicitRoot) {
|
|
|
13848
13998
|
}
|
|
13849
13999
|
function listDirsIn(dir) {
|
|
13850
14000
|
try {
|
|
13851
|
-
return (0,
|
|
14001
|
+
return (0, import_node_fs22.readdirSync)(dir, { withFileTypes: true }).filter((ent) => ent.isDirectory()).map((ent) => (0, import_node_path22.join)(dir, ent.name));
|
|
13852
14002
|
} catch {
|
|
13853
14003
|
return [];
|
|
13854
14004
|
}
|
|
13855
14005
|
}
|
|
13856
14006
|
function isRepoCheckoutDir(dir) {
|
|
13857
|
-
return (0,
|
|
14007
|
+
return (0, import_node_fs22.existsSync)((0, import_node_path22.join)(dir, ".git"));
|
|
13858
14008
|
}
|
|
13859
14009
|
function resolveExplicitScanRoot(explicitRoot, repoRoot2) {
|
|
13860
14010
|
let rootDirs;
|
|
13861
14011
|
try {
|
|
13862
|
-
rootDirs = (0,
|
|
14012
|
+
rootDirs = (0, import_node_fs22.readdirSync)(explicitRoot, { withFileTypes: true }).filter((ent) => ent.isDirectory()).map((ent) => ent.name);
|
|
13863
14013
|
} catch {
|
|
13864
14014
|
return explicitRoot;
|
|
13865
14015
|
}
|
|
@@ -13890,6 +14040,38 @@ async function gcPlan(remote, limit, opts = {}) {
|
|
|
13890
14040
|
])].filter((b) => !isProtectedBranch(b)),
|
|
13891
14041
|
limit
|
|
13892
14042
|
);
|
|
14043
|
+
const primaryRoot = await primaryCheckoutRootOf(gitOut);
|
|
14044
|
+
const owners = primaryRoot ? readWorktreeOwners(primaryRoot) : [];
|
|
14045
|
+
const mergedSet = new Set(mergedIntoBase);
|
|
14046
|
+
const preservedSet = new Set(preserved);
|
|
14047
|
+
const failedLookups = new Set(failures.map((failure) => failure.branch));
|
|
14048
|
+
const openBranches = new Set(prs.filter((pr2) => pr2.state === "OPEN").map((pr2) => pr2.headRefName));
|
|
14049
|
+
const proofWorktrees = worktrees.filter((wt) => !wt.dirty && wt.branch !== current.trim() && !isProtectedBranch(wt.branch) && !preservedSet.has(wt.branch) && !mergedSet.has(wt.branch) && !failedLookups.has(wt.branch) && !openBranches.has(wt.branch));
|
|
14050
|
+
const trainTips = [];
|
|
14051
|
+
if (proofWorktrees.length) {
|
|
14052
|
+
for (const name of ["development", "main", "master"]) {
|
|
14053
|
+
const oid = (await gitOut(["rev-parse", "--verify", `${remote}/${name}`]).catch(() => "")).trim();
|
|
14054
|
+
if (oid) trainTips.push(oid);
|
|
14055
|
+
}
|
|
14056
|
+
}
|
|
14057
|
+
const headByBranch = new Map(heads.map((h) => [h.branch, h.oid]));
|
|
14058
|
+
const exactTreeLanded = [];
|
|
14059
|
+
const exactTreeRefusals = [];
|
|
14060
|
+
for (const wt of proofWorktrees) {
|
|
14061
|
+
const headOid = headByBranch.get(wt.branch);
|
|
14062
|
+
const owner = owners.find((entry) => sameWorktreeMetadataPath(entry.path, wt.path));
|
|
14063
|
+
if (!headOid || !owner || !primaryRoot) continue;
|
|
14064
|
+
const proof = await proveExactTreeDelivery({
|
|
14065
|
+
repoRoot: primaryRoot,
|
|
14066
|
+
branch: wt.branch,
|
|
14067
|
+
workerOid: headOid,
|
|
14068
|
+
creationBaseOid: owner.provenance?.creationBaseOid,
|
|
14069
|
+
recordedWorkerBranch: owner.provenance?.workerBranch,
|
|
14070
|
+
landedTips: trainTips
|
|
14071
|
+
});
|
|
14072
|
+
if (proof.action === "settle") exactTreeLanded.push({ branch: wt.branch, headOid, candidateOid: proof.candidateOid });
|
|
14073
|
+
else exactTreeRefusals.push({ branch: wt.branch, detail: describeExactTreeRefusal(proof) });
|
|
14074
|
+
}
|
|
13893
14075
|
return buildGcPlan({
|
|
13894
14076
|
localBranches,
|
|
13895
14077
|
prLookupFailures: failures,
|
|
@@ -13902,6 +14084,8 @@ async function gcPlan(remote, limit, opts = {}) {
|
|
|
13902
14084
|
remote,
|
|
13903
14085
|
preservedBranches: preserved,
|
|
13904
14086
|
mergedIntoBase,
|
|
14087
|
+
exactTreeLanded,
|
|
14088
|
+
exactTreeRefusals,
|
|
13905
14089
|
originBranches,
|
|
13906
14090
|
trainOnly: opts.trainOnly
|
|
13907
14091
|
});
|
|
@@ -14200,10 +14384,10 @@ var rollout_plan_default = {
|
|
|
14200
14384
|
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)."
|
|
14201
14385
|
},
|
|
14202
14386
|
baseline: {
|
|
14203
|
-
version: "3.
|
|
14204
|
-
tag: "v3.
|
|
14205
|
-
commit: "
|
|
14206
|
-
npm: "@mutmutco/cli@3.
|
|
14387
|
+
version: "3.136.0",
|
|
14388
|
+
tag: "v3.136.0",
|
|
14389
|
+
commit: "acba68c21013",
|
|
14390
|
+
npm: "@mutmutco/cli@3.136.0"
|
|
14207
14391
|
},
|
|
14208
14392
|
exitCriterion: "fleet-n-of-n",
|
|
14209
14393
|
hubOnlyShortcut: "forbidden",
|
|
@@ -14220,14 +14404,14 @@ var rollout_plan_default = {
|
|
|
14220
14404
|
repo: "mutmutco/mmi-hub",
|
|
14221
14405
|
role: "canary",
|
|
14222
14406
|
schedule: "train",
|
|
14223
|
-
v3Target: "v3.
|
|
14407
|
+
v3Target: "v3.136.0"
|
|
14224
14408
|
}
|
|
14225
14409
|
],
|
|
14226
14410
|
rollbackTrigger: "Any red inside the post-cut soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a v3 client refused while the compat window must still admit it (SUPPORTED_MINOR_WINDOW=2, MIN_CLIENT_VERSION 0.0.0 \u2014 D6a), or npm consumer install/doctor failure on the v4 dist.",
|
|
14227
14411
|
rollback: {
|
|
14228
14412
|
independent: true,
|
|
14229
|
-
mechanism: "npm dist-tag latest -> 3.
|
|
14230
|
-
v3Target: "v3.
|
|
14413
|
+
mechanism: "npm dist-tag latest -> 3.136.0 and redeploy the Hub Lambda from tag v3.136.0 (acba68c21013); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
14414
|
+
v3Target: "v3.136.0 (@mutmutco/cli@3.136.0, tag commit acba68c21013 \u2014 the preserved latest-v3 distribution, D6b)"
|
|
14231
14415
|
}
|
|
14232
14416
|
},
|
|
14233
14417
|
{
|
|
@@ -16311,7 +16495,7 @@ function planTrainApplyRepoGuard(applyRepo, cwdRepo, rerun) {
|
|
|
16311
16495
|
async function resolveFoldPaths(deps, model) {
|
|
16312
16496
|
const helper = "scripts/release-distribution.mjs";
|
|
16313
16497
|
if (model === "hub-serverless" || model === "registry-publish") {
|
|
16314
|
-
if ((0,
|
|
16498
|
+
if ((0, import_node_fs23.existsSync)(helper)) {
|
|
16315
16499
|
let out;
|
|
16316
16500
|
try {
|
|
16317
16501
|
out = await deps.run("node", [helper, "changed-files"]);
|
|
@@ -16430,7 +16614,7 @@ async function restoreMainNpmPackIdentities(deps, version) {
|
|
|
16430
16614
|
if (mainBom.version !== version) return void 0;
|
|
16431
16615
|
let localBom;
|
|
16432
16616
|
try {
|
|
16433
|
-
localBom = JSON.parse((0,
|
|
16617
|
+
localBom = JSON.parse((0, import_node_fs23.readFileSync)(bomPath, "utf8"));
|
|
16434
16618
|
} catch (e) {
|
|
16435
16619
|
throw new Error(
|
|
16436
16620
|
`version fold refused: ${bomPath} written by this fold's prepare is unreadable (${describeReadError(e)}) \u2014 cannot restore the published ${version} npm-pack identities from origin/main (#4503/#4713).`
|
|
@@ -16448,14 +16632,14 @@ async function restoreMainNpmPackIdentities(deps, version) {
|
|
|
16448
16632
|
restored += 1;
|
|
16449
16633
|
}
|
|
16450
16634
|
if (restored === 0) return void 0;
|
|
16451
|
-
(0,
|
|
16635
|
+
(0, import_node_fs23.writeFileSync)(bomPath, `${JSON.stringify(localBom, null, 2)}
|
|
16452
16636
|
`);
|
|
16453
16637
|
return `restored ${restored} npm-pack BOM identit${restored === 1 ? "y" : "ies"} from origin/main (#4503)`;
|
|
16454
16638
|
}
|
|
16455
16639
|
function publishVisibilityFor(surfaceId) {
|
|
16456
16640
|
let raw;
|
|
16457
16641
|
try {
|
|
16458
|
-
raw = (0,
|
|
16642
|
+
raw = (0, import_node_fs23.readFileSync)("surfaces.json", "utf8");
|
|
16459
16643
|
} catch (e) {
|
|
16460
16644
|
if (e.code === "ENOENT") return "unknown";
|
|
16461
16645
|
throw trainReadFailure(
|
|
@@ -16476,7 +16660,7 @@ function publishVisibilityFor(surfaceId) {
|
|
|
16476
16660
|
}
|
|
16477
16661
|
function npmPackArtifactName(packagePath) {
|
|
16478
16662
|
try {
|
|
16479
|
-
const pkg = JSON.parse((0,
|
|
16663
|
+
const pkg = JSON.parse((0, import_node_fs23.readFileSync)((0, import_node_path23.join)(packagePath, "package.json"), "utf8"));
|
|
16480
16664
|
return pkg.name || void 0;
|
|
16481
16665
|
} catch {
|
|
16482
16666
|
return void 0;
|
|
@@ -16485,7 +16669,7 @@ function npmPackArtifactName(packagePath) {
|
|
|
16485
16669
|
async function refuseDivergentPublishedNpmPack(deps, version) {
|
|
16486
16670
|
let localBom;
|
|
16487
16671
|
try {
|
|
16488
|
-
localBom = JSON.parse((0,
|
|
16672
|
+
localBom = JSON.parse((0, import_node_fs23.readFileSync)("distribution-bom.json", "utf8"));
|
|
16489
16673
|
} catch (e) {
|
|
16490
16674
|
throw new Error(
|
|
16491
16675
|
`version fold refused: distribution-bom.json written by this fold's prepare is unreadable (${e instanceof Error ? e.message.split("\n")[0] : String(e)}) \u2014 cannot compare the same-PATCH ${version} npm-pack identities against published npm (#4503/#4662).`
|
|
@@ -17647,17 +17831,17 @@ async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince,
|
|
|
17647
17831
|
return { note: `no manual dispatch: ${model} repo deploys via its own push-triggered workflow`, deployStatus: "pending" };
|
|
17648
17832
|
}
|
|
17649
17833
|
function readLocalGateWorkflows() {
|
|
17650
|
-
const dir = (0,
|
|
17834
|
+
const dir = (0, import_node_path23.join)(".github", "workflows");
|
|
17651
17835
|
let names;
|
|
17652
17836
|
try {
|
|
17653
|
-
names = (0,
|
|
17837
|
+
names = (0, import_node_fs23.readdirSync)(dir);
|
|
17654
17838
|
} catch {
|
|
17655
17839
|
return null;
|
|
17656
17840
|
}
|
|
17657
17841
|
const files = [];
|
|
17658
17842
|
for (const name of names.filter(isGateWorkflowPath)) {
|
|
17659
17843
|
try {
|
|
17660
|
-
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0,
|
|
17844
|
+
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs23.readFileSync)((0, import_node_path23.join)(dir, name), "utf8") });
|
|
17661
17845
|
} catch {
|
|
17662
17846
|
}
|
|
17663
17847
|
}
|
|
@@ -19875,12 +20059,12 @@ async function runPrLand(prNumber, options, deps) {
|
|
|
19875
20059
|
}
|
|
19876
20060
|
|
|
19877
20061
|
// src/wave-status.ts
|
|
19878
|
-
var
|
|
20062
|
+
var import_node_fs25 = require("node:fs");
|
|
19879
20063
|
|
|
19880
20064
|
// src/stage-runner.ts
|
|
19881
20065
|
var import_node_child_process8 = require("node:child_process");
|
|
19882
|
-
var
|
|
19883
|
-
var
|
|
20066
|
+
var import_node_fs24 = require("node:fs");
|
|
20067
|
+
var import_node_path24 = require("node:path");
|
|
19884
20068
|
var import_node_net = require("node:net");
|
|
19885
20069
|
var import_node_util5 = require("node:util");
|
|
19886
20070
|
|
|
@@ -20019,11 +20203,11 @@ function appendForceRecreate(up) {
|
|
|
20019
20203
|
return `${up.trimEnd()} --force-recreate`;
|
|
20020
20204
|
}
|
|
20021
20205
|
function stageStatePath(cwd = process.cwd()) {
|
|
20022
|
-
return (0,
|
|
20206
|
+
return (0, import_node_path24.join)(cwd, "tmp", "stage", "state.json");
|
|
20023
20207
|
}
|
|
20024
20208
|
function stageGlobalStatePath(cwd = process.cwd(), gitCommonDir = ".git") {
|
|
20025
|
-
const dir = (0,
|
|
20026
|
-
return (0,
|
|
20209
|
+
const dir = (0, import_node_path24.isAbsolute)(gitCommonDir) ? gitCommonDir : (0, import_node_path24.resolve)(cwd, gitCommonDir);
|
|
20210
|
+
return (0, import_node_path24.join)(dir, "mmi", "stage", "state.json");
|
|
20027
20211
|
}
|
|
20028
20212
|
function normPath3(path2) {
|
|
20029
20213
|
return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
@@ -20247,14 +20431,14 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
20247
20431
|
}
|
|
20248
20432
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
20249
20433
|
if (!config.ensureEnv) return;
|
|
20250
|
-
const target = (0,
|
|
20251
|
-
const example = (0,
|
|
20252
|
-
if (!(0,
|
|
20253
|
-
(0,
|
|
20254
|
-
} else if ((0,
|
|
20255
|
-
const stale = detectStaleEnvFile((0,
|
|
20256
|
-
exampleMtimeMs: (0,
|
|
20257
|
-
targetMtimeMs: (0,
|
|
20434
|
+
const target = (0, import_node_path24.join)(cwd, config.ensureEnv.target);
|
|
20435
|
+
const example = (0, import_node_path24.join)(cwd, config.ensureEnv.example);
|
|
20436
|
+
if (!(0, import_node_fs24.existsSync)(target) && (0, import_node_fs24.existsSync)(example)) {
|
|
20437
|
+
(0, import_node_fs24.copyFileSync)(example, target);
|
|
20438
|
+
} else if ((0, import_node_fs24.existsSync)(target) && (0, import_node_fs24.existsSync)(example)) {
|
|
20439
|
+
const stale = detectStaleEnvFile((0, import_node_fs24.readFileSync)(example, "utf8"), (0, import_node_fs24.readFileSync)(target, "utf8"), {
|
|
20440
|
+
exampleMtimeMs: (0, import_node_fs24.statSync)(example).mtimeMs,
|
|
20441
|
+
targetMtimeMs: (0, import_node_fs24.statSync)(target).mtimeMs
|
|
20258
20442
|
});
|
|
20259
20443
|
if (stale) {
|
|
20260
20444
|
const msg = `stale ${config.ensureEnv.target} (${stale}) \u2014 delete it or refresh from ${config.ensureEnv.example} before re-running /stage`;
|
|
@@ -20262,8 +20446,8 @@ async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
|
20262
20446
|
console.error(`mmi-cli stage: ${msg} (allowed via --allow-stale-env)`);
|
|
20263
20447
|
}
|
|
20264
20448
|
}
|
|
20265
|
-
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0,
|
|
20266
|
-
(0,
|
|
20449
|
+
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0, import_node_fs24.existsSync)(target)) {
|
|
20450
|
+
(0, import_node_fs24.writeFileSync)(target, mergeEnvSecretsIntoFile((0, import_node_fs24.readFileSync)(target, "utf8"), opts.vaultEnvMerge), "utf8");
|
|
20267
20451
|
}
|
|
20268
20452
|
}
|
|
20269
20453
|
async function gitText(cwd, args) {
|
|
@@ -20293,20 +20477,20 @@ async function resolveGlobalStatePath(cwd, explicit) {
|
|
|
20293
20477
|
return void 0;
|
|
20294
20478
|
}
|
|
20295
20479
|
function readState(path2) {
|
|
20296
|
-
if (!(0,
|
|
20480
|
+
if (!(0, import_node_fs24.existsSync)(path2)) return null;
|
|
20297
20481
|
try {
|
|
20298
|
-
return JSON.parse((0,
|
|
20482
|
+
return JSON.parse((0, import_node_fs24.readFileSync)(path2, "utf8"));
|
|
20299
20483
|
} catch {
|
|
20300
20484
|
return null;
|
|
20301
20485
|
}
|
|
20302
20486
|
}
|
|
20303
20487
|
function mkdirFor(path2) {
|
|
20304
20488
|
const dir = path2.slice(0, Math.max(path2.lastIndexOf("/"), path2.lastIndexOf("\\")));
|
|
20305
|
-
(0,
|
|
20489
|
+
(0, import_node_fs24.mkdirSync)(dir, { recursive: true });
|
|
20306
20490
|
}
|
|
20307
20491
|
function writeState(path2, state) {
|
|
20308
20492
|
mkdirFor(path2);
|
|
20309
|
-
(0,
|
|
20493
|
+
(0, import_node_fs24.writeFileSync)(path2, JSON.stringify(state, null, 2), "utf8");
|
|
20310
20494
|
}
|
|
20311
20495
|
function writeStagePortReservation(port, cwd, statePath, globalStatePath, now) {
|
|
20312
20496
|
const reservation = {
|
|
@@ -20326,7 +20510,7 @@ async function cleanupStageState(state, paths, timeoutMs, fallbackCwd) {
|
|
|
20326
20510
|
await shell(state.teardown.command.trim(), state.teardown.cwd || state.cwd || fallbackCwd, Math.max(timeoutMs, 1e4));
|
|
20327
20511
|
}
|
|
20328
20512
|
for (const path2 of [...new Set(paths.filter((p) => Boolean(p)))]) {
|
|
20329
|
-
(0,
|
|
20513
|
+
(0, import_node_fs24.rmSync)(path2, { force: true });
|
|
20330
20514
|
}
|
|
20331
20515
|
}
|
|
20332
20516
|
async function killTree(pid) {
|
|
@@ -20484,8 +20668,8 @@ async function runStage(config = {}, opts = {}) {
|
|
|
20484
20668
|
await ensureStageRuntimeEnv(config, opts, cwd);
|
|
20485
20669
|
if (build) await shell(sub(build), cwd, timeoutMs, stageProcessEnv(stagePort, extraEnv));
|
|
20486
20670
|
} catch (e) {
|
|
20487
|
-
(0,
|
|
20488
|
-
if (globalStatePath && globalStatePath !== statePath) (0,
|
|
20671
|
+
(0, import_node_fs24.rmSync)(statePath, { force: true });
|
|
20672
|
+
if (globalStatePath && globalStatePath !== statePath) (0, import_node_fs24.rmSync)(globalStatePath, { force: true });
|
|
20489
20673
|
throw e;
|
|
20490
20674
|
}
|
|
20491
20675
|
const started = await startStage(config, {
|
|
@@ -20506,9 +20690,9 @@ function parseNextFromHead(headText) {
|
|
|
20506
20690
|
}
|
|
20507
20691
|
function readStageSummary(worktreePath) {
|
|
20508
20692
|
const statePath = stageStatePath(worktreePath);
|
|
20509
|
-
if (!(0,
|
|
20693
|
+
if (!(0, import_node_fs25.existsSync)(statePath)) return void 0;
|
|
20510
20694
|
try {
|
|
20511
|
-
const state = JSON.parse((0,
|
|
20695
|
+
const state = JSON.parse((0, import_node_fs25.readFileSync)(statePath, "utf8"));
|
|
20512
20696
|
const port = typeof state.port === "number" ? state.port : void 0;
|
|
20513
20697
|
if (port == null || !Number.isInteger(port) || port <= 0) return void 0;
|
|
20514
20698
|
return { port, url: typeof state.url === "string" ? state.url : void 0 };
|
|
@@ -20613,13 +20797,13 @@ async function executeWaveLand(plan, deps, opts = { preserveWorktree: true }) {
|
|
|
20613
20797
|
}
|
|
20614
20798
|
|
|
20615
20799
|
// src/index.ts
|
|
20616
|
-
var
|
|
20800
|
+
var import_node_os21 = require("node:os");
|
|
20617
20801
|
|
|
20618
20802
|
// src/board.ts
|
|
20619
20803
|
var import_node_child_process9 = require("node:child_process");
|
|
20620
|
-
var
|
|
20621
|
-
var
|
|
20622
|
-
var
|
|
20804
|
+
var import_node_fs26 = require("node:fs");
|
|
20805
|
+
var import_node_os10 = require("node:os");
|
|
20806
|
+
var import_node_path25 = require("node:path");
|
|
20623
20807
|
var import_node_util6 = require("node:util");
|
|
20624
20808
|
|
|
20625
20809
|
// src/board-dependency.ts
|
|
@@ -22229,7 +22413,7 @@ var CLAIM_SESSION_ACTIVITY_MS = 30 * 6e4;
|
|
|
22229
22413
|
var CLAIM_SESSION_PROBE_CACHE_MS = 6e4;
|
|
22230
22414
|
var claimSessionProbeCache = /* @__PURE__ */ new Map();
|
|
22231
22415
|
function probeLocalClaimSession(marker, now = Date.now()) {
|
|
22232
|
-
if (!marker.session || !marker.host || marker.host.toLowerCase() !== (0,
|
|
22416
|
+
if (!marker.session || !marker.host || marker.host.toLowerCase() !== (0, import_node_os10.hostname)().toLowerCase()) return void 0;
|
|
22233
22417
|
if (!marker.surface?.toLowerCase().startsWith("claude")) return void 0;
|
|
22234
22418
|
const cacheKey = `${marker.host.toLowerCase()}/${marker.session}`;
|
|
22235
22419
|
const cached = claimSessionProbeCache.get(cacheKey);
|
|
@@ -22238,17 +22422,17 @@ function probeLocalClaimSession(marker, now = Date.now()) {
|
|
|
22238
22422
|
claimSessionProbeCache.set(cacheKey, { checkedAt: now, state });
|
|
22239
22423
|
return state;
|
|
22240
22424
|
};
|
|
22241
|
-
const root = (0,
|
|
22425
|
+
const root = (0, import_node_path25.join)((0, import_node_os10.homedir)(), ".claude", "projects");
|
|
22242
22426
|
try {
|
|
22243
22427
|
const wanted = `${marker.session}.jsonl`.toLowerCase();
|
|
22244
22428
|
const pending = [root];
|
|
22245
22429
|
while (pending.length) {
|
|
22246
22430
|
const dir = pending.pop();
|
|
22247
|
-
for (const entry of (0,
|
|
22248
|
-
const path2 = (0,
|
|
22431
|
+
for (const entry of (0, import_node_fs26.readdirSync)(dir, { withFileTypes: true })) {
|
|
22432
|
+
const path2 = (0, import_node_path25.join)(dir, entry.name);
|
|
22249
22433
|
if (entry.isDirectory()) pending.push(path2);
|
|
22250
22434
|
else if (entry.isFile() && entry.name.toLowerCase() === wanted) {
|
|
22251
|
-
return remember(now - (0,
|
|
22435
|
+
return remember(now - (0, import_node_fs26.statSync)(path2).mtimeMs <= CLAIM_SESSION_ACTIVITY_MS ? "live" : "dead");
|
|
22252
22436
|
}
|
|
22253
22437
|
}
|
|
22254
22438
|
}
|
|
@@ -22536,7 +22720,7 @@ async function resolveAutoAddBoardAttach(client, cfg, selector, priority, warn =
|
|
|
22536
22720
|
}
|
|
22537
22721
|
|
|
22538
22722
|
// src/issue-body.ts
|
|
22539
|
-
var
|
|
22723
|
+
var import_node_os11 = require("node:os");
|
|
22540
22724
|
var TextArgError = class extends Error {
|
|
22541
22725
|
constructor(message, code, offendingFlag) {
|
|
22542
22726
|
super(message);
|
|
@@ -22548,7 +22732,7 @@ var TextArgError = class extends Error {
|
|
|
22548
22732
|
offendingFlag;
|
|
22549
22733
|
};
|
|
22550
22734
|
function emptyStdinMessage(fileFlag) {
|
|
22551
|
-
if ((0,
|
|
22735
|
+
if ((0, import_node_os11.platform)() === "win32") {
|
|
22552
22736
|
return `${fileFlag} - read empty stdin (on Windows, ${fileFlag} - is unreliable through the npm .cmd shim \u2014 use ${fileFlag} <path>, or pipe to \`node cli/dist/index.cjs\` directly)`;
|
|
22553
22737
|
}
|
|
22554
22738
|
return `${fileFlag} - read empty stdin (nothing piped \u2014 pass a heredoc/pipe, or ${fileFlag} <path>)`;
|
|
@@ -22785,6 +22969,13 @@ function applyCommandTaxonomy(program3) {
|
|
|
22785
22969
|
}
|
|
22786
22970
|
program3.configureHelp({
|
|
22787
22971
|
...REQUIRED_OPTION_HELP,
|
|
22972
|
+
// #5014: the root help must show the CANONICAL house-prefixed term (`oracle board`, `devops pr`),
|
|
22973
|
+
// not the retired flat leaf name — an agent grounding on `mmi-cli help` and running the flat name
|
|
22974
|
+
// hits the Wave 3 refusal. `canonicalPathFor` is the identity for `core`, so core stays unprefixed.
|
|
22975
|
+
subcommandTerm(command) {
|
|
22976
|
+
const stock = Help.prototype.subcommandTerm.call(this, command);
|
|
22977
|
+
return `${canonicalPathFor(command.name()) ?? command.name()}${stock.slice(command.name().length)}`;
|
|
22978
|
+
},
|
|
22788
22979
|
visibleCommands(command) {
|
|
22789
22980
|
const visible = command.commands.filter((child2) => !child2._hidden);
|
|
22790
22981
|
const helpCommand = command._getHelpCommand();
|
|
@@ -23149,20 +23340,20 @@ function consolidateCommandNamespaces(program3) {
|
|
|
23149
23340
|
}
|
|
23150
23341
|
|
|
23151
23342
|
// src/claude-binary-doctor.ts
|
|
23343
|
+
var import_node_fs28 = require("node:fs");
|
|
23344
|
+
var import_node_os13 = require("node:os");
|
|
23345
|
+
var import_node_path27 = require("node:path");
|
|
23346
|
+
|
|
23347
|
+
// src/jerv-cli-spawn.ts
|
|
23152
23348
|
var import_node_fs27 = require("node:fs");
|
|
23153
23349
|
var import_node_os12 = require("node:os");
|
|
23154
23350
|
var import_node_path26 = require("node:path");
|
|
23155
|
-
|
|
23156
|
-
// src/jerv-cli-spawn.ts
|
|
23157
|
-
var import_node_fs26 = require("node:fs");
|
|
23158
|
-
var import_node_os11 = require("node:os");
|
|
23159
|
-
var import_node_path25 = require("node:path");
|
|
23160
23351
|
var WIN_NAMES = ["jerv-cli.cmd", "jerv-cli.exe", "jerv-cli"];
|
|
23161
23352
|
var POSIX_NAMES = ["jerv-cli"];
|
|
23162
|
-
var JERV_CLI_ENTRY = (0,
|
|
23353
|
+
var JERV_CLI_ENTRY = (0, import_node_path26.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
|
|
23163
23354
|
function pathEnvEntries(pathEnv, platform2 = process.platform) {
|
|
23164
23355
|
if (platform2 !== "win32") {
|
|
23165
|
-
return pathEnv.split(
|
|
23356
|
+
return pathEnv.split(import_node_path26.delimiter).map((e) => e.trim()).filter(Boolean);
|
|
23166
23357
|
}
|
|
23167
23358
|
if (pathEnv.includes(";")) {
|
|
23168
23359
|
return pathEnv.split(";").map((e) => e.trim()).filter(Boolean);
|
|
@@ -23181,7 +23372,7 @@ function normalizeSpawnPathEntry(entry, platform2 = process.platform) {
|
|
|
23181
23372
|
if (msys) return `${msys[1].toUpperCase()}:\\${msys[2].replace(/\//g, "\\")}`;
|
|
23182
23373
|
return trimmed;
|
|
23183
23374
|
}
|
|
23184
|
-
function jervCliCandidateDirs(env = process.env, home = (0,
|
|
23375
|
+
function jervCliCandidateDirs(env = process.env, home = (0, import_node_os12.homedir)(), platform2 = process.platform) {
|
|
23185
23376
|
const seen = /* @__PURE__ */ new Set();
|
|
23186
23377
|
const out = [];
|
|
23187
23378
|
const push = (dir) => {
|
|
@@ -23195,35 +23386,35 @@ function jervCliCandidateDirs(env = process.env, home = (0, import_node_os11.hom
|
|
|
23195
23386
|
push(normalizeSpawnPathEntry(entry, platform2));
|
|
23196
23387
|
}
|
|
23197
23388
|
if (platform2 === "win32") {
|
|
23198
|
-
if (env.APPDATA) push((0,
|
|
23199
|
-
if (env.LOCALAPPDATA) push((0,
|
|
23389
|
+
if (env.APPDATA) push((0, import_node_path26.join)(env.APPDATA, "npm"));
|
|
23390
|
+
if (env.LOCALAPPDATA) push((0, import_node_path26.join)(env.LOCALAPPDATA, "npm"));
|
|
23200
23391
|
} else {
|
|
23201
|
-
push((0,
|
|
23392
|
+
push((0, import_node_path26.join)(home, ".local", "bin"));
|
|
23202
23393
|
}
|
|
23203
23394
|
return out;
|
|
23204
23395
|
}
|
|
23205
|
-
function jervCliCandidatePaths(env = process.env, home = (0,
|
|
23396
|
+
function jervCliCandidatePaths(env = process.env, home = (0, import_node_os12.homedir)(), platform2 = process.platform) {
|
|
23206
23397
|
const names = platform2 === "win32" ? WIN_NAMES : POSIX_NAMES;
|
|
23207
23398
|
const out = [];
|
|
23208
23399
|
for (const dir of jervCliCandidateDirs(env, home, platform2)) {
|
|
23209
|
-
for (const name of names) out.push((0,
|
|
23400
|
+
for (const name of names) out.push((0, import_node_path26.join)(dir, name));
|
|
23210
23401
|
}
|
|
23211
23402
|
return out;
|
|
23212
23403
|
}
|
|
23213
|
-
function resolveJervCliPath(env = process.env, home = (0,
|
|
23404
|
+
function resolveJervCliPath(env = process.env, home = (0, import_node_os12.homedir)(), platform2 = process.platform, exists = import_node_fs27.existsSync) {
|
|
23214
23405
|
for (const candidate of jervCliCandidatePaths(env, home, platform2)) {
|
|
23215
23406
|
if (exists(candidate)) return candidate;
|
|
23216
23407
|
}
|
|
23217
23408
|
return void 0;
|
|
23218
23409
|
}
|
|
23219
|
-
function resolveJervCliNodeEntry(shimPath, exists =
|
|
23220
|
-
const entry = (0,
|
|
23410
|
+
function resolveJervCliNodeEntry(shimPath, exists = import_node_fs27.existsSync) {
|
|
23411
|
+
const entry = (0, import_node_path26.join)((0, import_node_path26.dirname)(shimPath), JERV_CLI_ENTRY);
|
|
23221
23412
|
return exists(entry) ? entry : void 0;
|
|
23222
23413
|
}
|
|
23223
23414
|
function jervCliExecFileArgs(args, opts = {}) {
|
|
23224
23415
|
const platform2 = opts.platform ?? process.platform;
|
|
23225
|
-
const exists = opts.exists ??
|
|
23226
|
-
const resolved = resolveJervCliPath(opts.env ?? process.env, opts.home ?? (0,
|
|
23416
|
+
const exists = opts.exists ?? import_node_fs27.existsSync;
|
|
23417
|
+
const resolved = resolveJervCliPath(opts.env ?? process.env, opts.home ?? (0, import_node_os12.homedir)(), platform2, exists);
|
|
23227
23418
|
if (resolved) {
|
|
23228
23419
|
const entry = resolveJervCliNodeEntry(resolved, exists);
|
|
23229
23420
|
if (entry) {
|
|
@@ -23312,26 +23503,26 @@ function globalNodeModulesRoots(host) {
|
|
|
23312
23503
|
out.push(dir);
|
|
23313
23504
|
};
|
|
23314
23505
|
const prefix = env.npm_config_prefix?.trim();
|
|
23315
|
-
if (prefix) push(platform2 === "win32" ? (0,
|
|
23316
|
-
for (const dir of jervCliCandidateDirs(env, host.home ?? (0,
|
|
23317
|
-
push((0,
|
|
23318
|
-
push((0,
|
|
23506
|
+
if (prefix) push(platform2 === "win32" ? (0, import_node_path27.join)(prefix, "node_modules") : (0, import_node_path27.join)(prefix, "lib", "node_modules"));
|
|
23507
|
+
for (const dir of jervCliCandidateDirs(env, host.home ?? (0, import_node_os13.homedir)(), platform2)) {
|
|
23508
|
+
push((0, import_node_path27.join)(dir, "node_modules"));
|
|
23509
|
+
push((0, import_node_path27.join)((0, import_node_path27.dirname)(dir), "lib", "node_modules"));
|
|
23319
23510
|
}
|
|
23320
23511
|
return out;
|
|
23321
23512
|
}
|
|
23322
23513
|
function readHead(path2) {
|
|
23323
23514
|
let fd;
|
|
23324
23515
|
try {
|
|
23325
|
-
fd = (0,
|
|
23516
|
+
fd = (0, import_node_fs28.openSync)(path2, "r");
|
|
23326
23517
|
const buffer = new Uint8Array(MAGIC_HEAD_BYTES);
|
|
23327
|
-
const read = (0,
|
|
23518
|
+
const read = (0, import_node_fs28.readSync)(fd, buffer, 0, MAGIC_HEAD_BYTES, 0);
|
|
23328
23519
|
return buffer.subarray(0, read);
|
|
23329
23520
|
} catch {
|
|
23330
23521
|
return void 0;
|
|
23331
23522
|
} finally {
|
|
23332
23523
|
if (fd !== void 0) {
|
|
23333
23524
|
try {
|
|
23334
|
-
(0,
|
|
23525
|
+
(0, import_node_fs28.closeSync)(fd);
|
|
23335
23526
|
} catch {
|
|
23336
23527
|
}
|
|
23337
23528
|
}
|
|
@@ -23339,7 +23530,7 @@ function readHead(path2) {
|
|
|
23339
23530
|
}
|
|
23340
23531
|
function fileBytes(path2) {
|
|
23341
23532
|
try {
|
|
23342
|
-
return (0,
|
|
23533
|
+
return (0, import_node_fs28.statSync)(path2).size;
|
|
23343
23534
|
} catch {
|
|
23344
23535
|
return void 0;
|
|
23345
23536
|
}
|
|
@@ -23353,17 +23544,17 @@ function readClaudeBinaryState(host = {}) {
|
|
|
23353
23544
|
const arch = host.arch ?? process.arch;
|
|
23354
23545
|
const magic = EXECUTABLE_MAGIC[platform2];
|
|
23355
23546
|
if (!magic) return void 0;
|
|
23356
|
-
const packageRoot = globalNodeModulesRoots(host).map((root) => (0,
|
|
23547
|
+
const packageRoot = globalNodeModulesRoots(host).map((root) => (0, import_node_path27.join)(root, ...PACKAGE.split("/"))).find((dir) => (0, import_node_fs28.existsSync)((0, import_node_path27.join)(dir, "package.json")));
|
|
23357
23548
|
if (!packageRoot) return void 0;
|
|
23358
23549
|
const keys = platformPackageKeys(platform2, arch);
|
|
23359
23550
|
const fallbackPackage = `${PACKAGE}-${keys[0]}`;
|
|
23360
23551
|
let manifest;
|
|
23361
23552
|
try {
|
|
23362
|
-
manifest = JSON.parse((0,
|
|
23553
|
+
manifest = JSON.parse((0, import_node_fs28.readFileSync)((0, import_node_path27.join)(packageRoot, "package.json"), "utf8"));
|
|
23363
23554
|
} catch (e) {
|
|
23364
23555
|
return {
|
|
23365
23556
|
state: "unreadable",
|
|
23366
|
-
binPath: (0,
|
|
23557
|
+
binPath: (0, import_node_path27.join)(packageRoot, "package.json"),
|
|
23367
23558
|
expectedMagic: magic.name,
|
|
23368
23559
|
platformPackage: fallbackPackage,
|
|
23369
23560
|
error: `package.json could not be read \u2014 ${e.message}`
|
|
@@ -23380,17 +23571,17 @@ function readClaudeBinaryState(host = {}) {
|
|
|
23380
23571
|
error: "package.json declares no `bin` entry \u2014 the executed file cannot be resolved"
|
|
23381
23572
|
};
|
|
23382
23573
|
}
|
|
23383
|
-
const binPath = (0,
|
|
23384
|
-
const binName = (0,
|
|
23574
|
+
const binPath = (0, import_node_path27.join)(packageRoot, binRelative);
|
|
23575
|
+
const binName = (0, import_node_path27.basename)(binRelative);
|
|
23385
23576
|
const optional = manifest.optionalDependencies;
|
|
23386
23577
|
const publishes = (name) => typeof optional === "object" && optional !== null && Object.prototype.hasOwnProperty.call(optional, name);
|
|
23387
23578
|
const published = keys.map((key) => `${PACKAGE}-${key}`).filter(publishes);
|
|
23388
23579
|
if (published.length === 0) return void 0;
|
|
23389
23580
|
const binIn = (name) => [
|
|
23390
|
-
(0,
|
|
23391
|
-
(0,
|
|
23581
|
+
(0, import_node_path27.join)(packageRoot, "node_modules", ...name.split("/"), binName),
|
|
23582
|
+
(0, import_node_path27.join)((0, import_node_path27.dirname)((0, import_node_path27.dirname)(packageRoot)), ...name.split("/"), binName)
|
|
23392
23583
|
];
|
|
23393
|
-
const found = published.map((name) => ({ name, path: binIn(name).find((file) => (0,
|
|
23584
|
+
const found = published.map((name) => ({ name, path: binIn(name).find((file) => (0, import_node_fs28.existsSync)(file)) })).find((c) => c.path);
|
|
23394
23585
|
const platformPackage = found?.name ?? published[0];
|
|
23395
23586
|
let source;
|
|
23396
23587
|
let sourceProblem;
|
|
@@ -23401,7 +23592,7 @@ function readClaudeBinaryState(host = {}) {
|
|
|
23401
23592
|
} else {
|
|
23402
23593
|
source = { path: found.path, bytes: fileBytes(found.path) ?? 0 };
|
|
23403
23594
|
}
|
|
23404
|
-
if (!(0,
|
|
23595
|
+
if (!(0, import_node_fs28.existsSync)(binPath)) {
|
|
23405
23596
|
return { state: "missing", binPath, expectedMagic: magic.name, platformPackage, ...source ? { source } : {}, ...sourceProblem ? { sourceProblem } : {} };
|
|
23406
23597
|
}
|
|
23407
23598
|
const head = readHead(binPath);
|
|
@@ -23444,9 +23635,9 @@ function healClaudeBinary(host = {}, onStep) {
|
|
|
23444
23635
|
const platform2 = host.platform ?? process.platform;
|
|
23445
23636
|
const aside = `${probe.binPath}.stub-${Date.now()}`;
|
|
23446
23637
|
let renamed = false;
|
|
23447
|
-
if ((0,
|
|
23638
|
+
if ((0, import_node_fs28.existsSync)(probe.binPath)) {
|
|
23448
23639
|
try {
|
|
23449
|
-
(0,
|
|
23640
|
+
(0, import_node_fs28.renameSync)(probe.binPath, aside);
|
|
23450
23641
|
renamed = true;
|
|
23451
23642
|
onStep?.(`renamed the stub aside: ${aside}`);
|
|
23452
23643
|
} catch (e) {
|
|
@@ -23455,12 +23646,12 @@ function healClaudeBinary(host = {}, onStep) {
|
|
|
23455
23646
|
}
|
|
23456
23647
|
try {
|
|
23457
23648
|
onStep?.(`copying ${probe.source.path} \u2192 ${probe.binPath} (${(probe.source.bytes / 1e6).toFixed(0)} MB)`);
|
|
23458
|
-
(0,
|
|
23459
|
-
if (platform2 !== "win32") (0,
|
|
23649
|
+
(0, import_node_fs28.copyFileSync)(probe.source.path, probe.binPath);
|
|
23650
|
+
if (platform2 !== "win32") (0, import_node_fs28.chmodSync)(probe.binPath, 493);
|
|
23460
23651
|
} catch (e) {
|
|
23461
23652
|
if (renamed) {
|
|
23462
23653
|
try {
|
|
23463
|
-
(0,
|
|
23654
|
+
(0, import_node_fs28.renameSync)(aside, probe.binPath);
|
|
23464
23655
|
} catch {
|
|
23465
23656
|
return { ok: false, detail: `copy failed (${e.message}) and the stub could not be restored \u2014 the original is at ${aside}` };
|
|
23466
23657
|
}
|
|
@@ -23474,7 +23665,7 @@ function healClaudeBinary(host = {}, onStep) {
|
|
|
23474
23665
|
let kept = false;
|
|
23475
23666
|
if (renamed) {
|
|
23476
23667
|
try {
|
|
23477
|
-
(0,
|
|
23668
|
+
(0, import_node_fs28.rmSync)(aside);
|
|
23478
23669
|
} catch {
|
|
23479
23670
|
kept = true;
|
|
23480
23671
|
}
|
|
@@ -23952,19 +24143,19 @@ function renderVerifyBroker(input) {
|
|
|
23952
24143
|
|
|
23953
24144
|
// src/tenant-artifact.ts
|
|
23954
24145
|
var import_node_crypto4 = require("node:crypto");
|
|
23955
|
-
var
|
|
24146
|
+
var import_node_fs29 = require("node:fs");
|
|
23956
24147
|
var import_promises5 = require("node:fs/promises");
|
|
23957
|
-
var
|
|
24148
|
+
var import_node_path28 = require("node:path");
|
|
23958
24149
|
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}$/;
|
|
23959
24150
|
var MAX_BYTES = 5 * 1024 * 1024 * 1024;
|
|
23960
24151
|
async function sha256File(path2) {
|
|
23961
24152
|
const hash = (0, import_node_crypto4.createHash)("sha256");
|
|
23962
|
-
for await (const chunk of (0,
|
|
24153
|
+
for await (const chunk of (0, import_node_fs29.createReadStream)(path2)) hash.update(chunk);
|
|
23963
24154
|
return hash.digest("hex");
|
|
23964
24155
|
}
|
|
23965
24156
|
async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
23966
24157
|
if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
|
|
23967
|
-
const path2 = (0,
|
|
24158
|
+
const path2 = (0, import_node_path28.resolve)(inputPath);
|
|
23968
24159
|
const info = await (0, import_promises5.stat)(path2);
|
|
23969
24160
|
if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
|
|
23970
24161
|
if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size > MAX_BYTES) throw new Error(`tenant artifact put: file must be 1..${MAX_BYTES} bytes`);
|
|
@@ -23982,7 +24173,7 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
|
23982
24173
|
return [key, value];
|
|
23983
24174
|
}));
|
|
23984
24175
|
headers["content-length"] = String(info.size);
|
|
23985
|
-
const stream = (0,
|
|
24176
|
+
const stream = (0, import_node_fs29.createReadStream)(path2);
|
|
23986
24177
|
let uploaded;
|
|
23987
24178
|
try {
|
|
23988
24179
|
uploaded = await fetch(body.uploadUrl, {
|
|
@@ -25100,8 +25291,8 @@ async function announceRelease(deps, args) {
|
|
|
25100
25291
|
// src/repo-index.ts
|
|
25101
25292
|
var import_node_crypto5 = require("node:crypto");
|
|
25102
25293
|
var import_node_child_process12 = require("node:child_process");
|
|
25103
|
-
var
|
|
25104
|
-
var
|
|
25294
|
+
var import_node_fs30 = require("node:fs");
|
|
25295
|
+
var import_node_path29 = require("node:path");
|
|
25105
25296
|
var REPO_INDEX_SCHEMA = 1;
|
|
25106
25297
|
var HARD_DENY = [
|
|
25107
25298
|
/(^|\/)\.env(\.|$)/i,
|
|
@@ -25226,11 +25417,11 @@ function loadReadmeHints(cwd, candidatePaths) {
|
|
|
25226
25417
|
}
|
|
25227
25418
|
for (const rel of readmes) {
|
|
25228
25419
|
if (isHardDeniedPath(rel)) continue;
|
|
25229
|
-
const abs = (0,
|
|
25230
|
-
if (!(0,
|
|
25420
|
+
const abs = (0, import_node_path29.join)(cwd, ...rel.split("/"));
|
|
25421
|
+
if (!(0, import_node_fs30.existsSync)(abs)) continue;
|
|
25231
25422
|
let text;
|
|
25232
25423
|
try {
|
|
25233
|
-
text = (0,
|
|
25424
|
+
text = (0, import_node_fs30.readFileSync)(abs, "utf8");
|
|
25234
25425
|
} catch {
|
|
25235
25426
|
continue;
|
|
25236
25427
|
}
|
|
@@ -25243,7 +25434,7 @@ function loadReadmeHints(cwd, candidatePaths) {
|
|
|
25243
25434
|
return hints;
|
|
25244
25435
|
}
|
|
25245
25436
|
function toPosix(p) {
|
|
25246
|
-
return p.split(
|
|
25437
|
+
return p.split(import_node_path29.sep).join("/");
|
|
25247
25438
|
}
|
|
25248
25439
|
function listCandidatePaths(cwd, exec = import_node_child_process12.execFileSync) {
|
|
25249
25440
|
try {
|
|
@@ -25265,11 +25456,11 @@ function rebuildRepoIndex(cwd, repoSlug2) {
|
|
|
25265
25456
|
for (const rel of candidates) {
|
|
25266
25457
|
if (ignored.has(rel)) continue;
|
|
25267
25458
|
if (isHardDeniedPath(rel)) continue;
|
|
25268
|
-
const abs = (0,
|
|
25269
|
-
if (!(0,
|
|
25459
|
+
const abs = (0, import_node_path29.join)(cwd, ...rel.split("/"));
|
|
25460
|
+
if (!(0, import_node_fs30.existsSync)(abs)) continue;
|
|
25270
25461
|
let text;
|
|
25271
25462
|
try {
|
|
25272
|
-
text = (0,
|
|
25463
|
+
text = (0, import_node_fs30.readFileSync)(abs, "utf8");
|
|
25273
25464
|
} catch {
|
|
25274
25465
|
continue;
|
|
25275
25466
|
}
|
|
@@ -25294,16 +25485,16 @@ function rebuildRepoIndex(cwd, repoSlug2) {
|
|
|
25294
25485
|
entries
|
|
25295
25486
|
};
|
|
25296
25487
|
const store = repoIndexStorePath(cwd);
|
|
25297
|
-
(0,
|
|
25298
|
-
(0,
|
|
25488
|
+
(0, import_node_fs30.mkdirSync)((0, import_node_path29.dirname)(store), { recursive: true });
|
|
25489
|
+
(0, import_node_fs30.writeFileSync)(store, `${JSON.stringify(projection, null, 2)}
|
|
25299
25490
|
`, "utf8");
|
|
25300
25491
|
return projection;
|
|
25301
25492
|
}
|
|
25302
25493
|
function loadRepoIndex(cwd) {
|
|
25303
25494
|
const store = repoIndexStorePath(cwd);
|
|
25304
|
-
if (!(0,
|
|
25495
|
+
if (!(0, import_node_fs30.existsSync)(store)) return null;
|
|
25305
25496
|
try {
|
|
25306
|
-
const raw = JSON.parse((0,
|
|
25497
|
+
const raw = JSON.parse((0, import_node_fs30.readFileSync)(store, "utf8"));
|
|
25307
25498
|
if (raw?.schema !== REPO_INDEX_SCHEMA || !Array.isArray(raw.entries)) return null;
|
|
25308
25499
|
return raw;
|
|
25309
25500
|
} catch {
|
|
@@ -25375,7 +25566,7 @@ function inferRepoSlug(cwd, exec = import_node_child_process12.execFileSync) {
|
|
|
25375
25566
|
if (m?.[1]) return m[1].toLowerCase();
|
|
25376
25567
|
} catch {
|
|
25377
25568
|
}
|
|
25378
|
-
return ((0,
|
|
25569
|
+
return ((0, import_node_path29.basename)(cwd) || "local").toLowerCase();
|
|
25379
25570
|
}
|
|
25380
25571
|
|
|
25381
25572
|
// src/repo-index-cloud-client.ts
|
|
@@ -25515,9 +25706,9 @@ async function gcRepoIndexCloud(deps) {
|
|
|
25515
25706
|
}
|
|
25516
25707
|
|
|
25517
25708
|
// src/repo-index-sync.ts
|
|
25518
|
-
var
|
|
25519
|
-
var
|
|
25520
|
-
var
|
|
25709
|
+
var import_node_fs31 = require("node:fs");
|
|
25710
|
+
var import_node_os14 = require("node:os");
|
|
25711
|
+
var import_node_path30 = require("node:path");
|
|
25521
25712
|
var import_node_child_process13 = require("node:child_process");
|
|
25522
25713
|
var MAX_EMBED_BACKFILL_ROUNDS = 40;
|
|
25523
25714
|
function normalizeRepo(raw) {
|
|
@@ -25561,7 +25752,7 @@ async function syncEstateRepoIndex(opts) {
|
|
|
25561
25752
|
const failed = [];
|
|
25562
25753
|
const skipped = [];
|
|
25563
25754
|
for (const repo of repos) {
|
|
25564
|
-
const dir = (0,
|
|
25755
|
+
const dir = (0, import_node_fs31.mkdtempSync)((0, import_node_path30.join)((0, import_node_os14.tmpdir)(), "mmi-repo-index-"));
|
|
25565
25756
|
try {
|
|
25566
25757
|
shallowClone(repo, dir, opts.githubToken);
|
|
25567
25758
|
const built = rebuildRepoIndex(dir, repo);
|
|
@@ -25611,7 +25802,7 @@ async function syncEstateRepoIndex(opts) {
|
|
|
25611
25802
|
failed.push({ repo, error: e.message });
|
|
25612
25803
|
} finally {
|
|
25613
25804
|
try {
|
|
25614
|
-
(0,
|
|
25805
|
+
(0, import_node_fs31.rmSync)(dir, { recursive: true, force: true });
|
|
25615
25806
|
} catch {
|
|
25616
25807
|
}
|
|
25617
25808
|
}
|
|
@@ -25620,7 +25811,7 @@ async function syncEstateRepoIndex(opts) {
|
|
|
25620
25811
|
}
|
|
25621
25812
|
|
|
25622
25813
|
// src/repo-index-health.ts
|
|
25623
|
-
var
|
|
25814
|
+
var import_node_fs32 = require("node:fs");
|
|
25624
25815
|
|
|
25625
25816
|
// testdata/repo-index-golden-queries.json
|
|
25626
25817
|
var repo_index_golden_queries_default = {
|
|
@@ -25662,7 +25853,7 @@ function assertGoldenSuite(raw, source) {
|
|
|
25662
25853
|
function loadGoldenSuite(path2) {
|
|
25663
25854
|
let text;
|
|
25664
25855
|
try {
|
|
25665
|
-
text = (0,
|
|
25856
|
+
text = (0, import_node_fs32.readFileSync)(path2, "utf8");
|
|
25666
25857
|
} catch (e) {
|
|
25667
25858
|
throw new Error(`golden suite unreadable at ${path2}: ${e.message}`);
|
|
25668
25859
|
}
|
|
@@ -25816,8 +26007,8 @@ async function runRepoIndexHealth(opts) {
|
|
|
25816
26007
|
|
|
25817
26008
|
// src/spawn-policy-core.ts
|
|
25818
26009
|
var import_node_child_process14 = require("node:child_process");
|
|
25819
|
-
var
|
|
25820
|
-
var
|
|
26010
|
+
var import_node_fs33 = require("node:fs");
|
|
26011
|
+
var import_node_path31 = require("node:path");
|
|
25821
26012
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
25822
26013
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
25823
26014
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -25903,7 +26094,7 @@ function runSpawnPolicy(root) {
|
|
|
25903
26094
|
for (const file of files) {
|
|
25904
26095
|
let raw;
|
|
25905
26096
|
try {
|
|
25906
|
-
raw = (0,
|
|
26097
|
+
raw = (0, import_node_fs33.readFileSync)((0, import_node_path31.join)(root, file), "utf8");
|
|
25907
26098
|
} catch {
|
|
25908
26099
|
continue;
|
|
25909
26100
|
}
|
|
@@ -25921,8 +26112,8 @@ function runSpawnPolicy(root) {
|
|
|
25921
26112
|
|
|
25922
26113
|
// src/test-policy-core.ts
|
|
25923
26114
|
var import_node_child_process15 = require("node:child_process");
|
|
25924
|
-
var
|
|
25925
|
-
var
|
|
26115
|
+
var import_node_fs34 = require("node:fs");
|
|
26116
|
+
var import_node_path32 = require("node:path");
|
|
25926
26117
|
var POLICY_FILE = "test-policy.json";
|
|
25927
26118
|
var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
25928
26119
|
var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
|
|
@@ -25975,7 +26166,7 @@ function isTestPath(path2) {
|
|
|
25975
26166
|
return TEST_RE.test(path2) || PY_TEST_RE.test(path2);
|
|
25976
26167
|
}
|
|
25977
26168
|
function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
25978
|
-
const raw = readFile9((0,
|
|
26169
|
+
const raw = readFile9((0, import_node_path32.join)(root, POLICY_FILE));
|
|
25979
26170
|
if (raw == null) return { mandatory: [], declared: false };
|
|
25980
26171
|
try {
|
|
25981
26172
|
return { ...JSON.parse(raw), declared: true };
|
|
@@ -25985,7 +26176,7 @@ function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
|
25985
26176
|
}
|
|
25986
26177
|
function readFileOrNull2(path2) {
|
|
25987
26178
|
try {
|
|
25988
|
-
return (0,
|
|
26179
|
+
return (0, import_node_fs34.readFileSync)(path2, "utf8");
|
|
25989
26180
|
} catch {
|
|
25990
26181
|
return null;
|
|
25991
26182
|
}
|
|
@@ -26012,12 +26203,12 @@ function classify(changed, policy, present = () => false) {
|
|
|
26012
26203
|
const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
|
|
26013
26204
|
return { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected };
|
|
26014
26205
|
}
|
|
26015
|
-
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0,
|
|
26016
|
-
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0,
|
|
26206
|
+
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs34.existsSync)(path2)) {
|
|
26207
|
+
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path32.join)(root, p)));
|
|
26017
26208
|
}
|
|
26018
|
-
function unresolvedSatisfiers(policy, root, exists = (path2) => (0,
|
|
26209
|
+
function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs34.existsSync)(path2)) {
|
|
26019
26210
|
const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
|
|
26020
|
-
return [...new Set(declared)].filter((p) => !exists((0,
|
|
26211
|
+
return [...new Set(declared)].filter((p) => !exists((0, import_node_path32.join)(root, p)));
|
|
26021
26212
|
}
|
|
26022
26213
|
function evaluate(changed, policy, present = () => false) {
|
|
26023
26214
|
const { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected } = classify(changed, policy, present);
|
|
@@ -26199,13 +26390,13 @@ function changedFilesSince(base, cwd) {
|
|
|
26199
26390
|
}
|
|
26200
26391
|
function runTestPolicy(root, deps = {}) {
|
|
26201
26392
|
const policy = deps.policy ?? loadPolicy(root);
|
|
26202
|
-
const exists = deps.exists ?? ((path2) => (0,
|
|
26393
|
+
const exists = deps.exists ?? ((path2) => (0, import_node_fs34.existsSync)(path2));
|
|
26203
26394
|
const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
|
|
26204
26395
|
const base = deps.changed ? "(injected)" : resolveBase(root, deps.base);
|
|
26205
26396
|
const refusal = deps.changed ? null : untrustworthyRange(root, base);
|
|
26206
26397
|
const changed = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
|
|
26207
26398
|
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride(base, root) : { override: null, refusals: [] };
|
|
26208
|
-
const present = (path2) => exists((0,
|
|
26399
|
+
const present = (path2) => exists((0, import_node_path32.join)(root, path2));
|
|
26209
26400
|
const removedByThisDiff = removedPaths(changed);
|
|
26210
26401
|
const staleFindings = [];
|
|
26211
26402
|
const unresolved = unresolvedProtectedEntries(policy, root, exists).filter((p) => !removedByThisDiff.has(p));
|
|
@@ -26243,8 +26434,8 @@ function runTestPolicy(root, deps = {}) {
|
|
|
26243
26434
|
}
|
|
26244
26435
|
|
|
26245
26436
|
// src/project-info-sync.ts
|
|
26246
|
-
var
|
|
26247
|
-
var
|
|
26437
|
+
var import_node_fs35 = require("node:fs");
|
|
26438
|
+
var import_node_path33 = require("node:path");
|
|
26248
26439
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
26249
26440
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
26250
26441
|
projectV2 { id }
|
|
@@ -26289,14 +26480,14 @@ function sharedName(entries, fallback) {
|
|
|
26289
26480
|
}
|
|
26290
26481
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
26291
26482
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
26292
|
-
const readmePath = (0,
|
|
26293
|
-
if (!(0,
|
|
26483
|
+
const readmePath = (0, import_node_path33.join)(repoRoot2, "README.md");
|
|
26484
|
+
if (!(0, import_node_fs35.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
26294
26485
|
const entries = entriesFor(project2, projects);
|
|
26295
26486
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
26296
26487
|
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
26297
26488
|
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
26298
26489
|
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
26299
|
-
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0,
|
|
26490
|
+
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs35.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
|
|
26300
26491
|
const lines = [
|
|
26301
26492
|
`# ${projectName}`,
|
|
26302
26493
|
"",
|
|
@@ -26315,8 +26506,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
26315
26506
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
26316
26507
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
26317
26508
|
const orgDocs = [
|
|
26318
|
-
(0,
|
|
26319
|
-
(0,
|
|
26509
|
+
(0, import_node_fs35.existsSync)((0, import_node_path33.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
|
|
26510
|
+
(0, import_node_fs35.existsSync)((0, import_node_path33.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
|
|
26320
26511
|
].filter(Boolean);
|
|
26321
26512
|
if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
|
|
26322
26513
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
|
|
@@ -27142,9 +27333,9 @@ function writeError(res) {
|
|
|
27142
27333
|
}
|
|
27143
27334
|
|
|
27144
27335
|
// src/secrets-commands.ts
|
|
27145
|
-
var
|
|
27146
|
-
var
|
|
27147
|
-
var
|
|
27336
|
+
var import_node_fs36 = require("node:fs");
|
|
27337
|
+
var import_node_path34 = require("node:path");
|
|
27338
|
+
var import_node_os15 = require("node:os");
|
|
27148
27339
|
|
|
27149
27340
|
// src/secrets-diff.ts
|
|
27150
27341
|
var TIMEOUT_MS2 = 8e3;
|
|
@@ -27246,18 +27437,18 @@ function collectMap(value, previous = []) {
|
|
|
27246
27437
|
return [...previous, value];
|
|
27247
27438
|
}
|
|
27248
27439
|
async function decryptRailsCredentials(input) {
|
|
27249
|
-
const appDir = (0,
|
|
27440
|
+
const appDir = (0, import_node_path34.resolve)(input.appDir ?? process.cwd());
|
|
27250
27441
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
27251
27442
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
27252
|
-
const credentialsPath = (0,
|
|
27253
|
-
const masterKeyPath = (0,
|
|
27443
|
+
const credentialsPath = (0, import_node_path34.resolve)(appDir, credentialsFile);
|
|
27444
|
+
const masterKeyPath = (0, import_node_path34.resolve)(appDir, masterKeyFile);
|
|
27254
27445
|
const env = {
|
|
27255
27446
|
...process.env,
|
|
27256
27447
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
27257
27448
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
27258
27449
|
};
|
|
27259
|
-
if ((0,
|
|
27260
|
-
env.RAILS_MASTER_KEY = (0,
|
|
27450
|
+
if ((0, import_node_fs36.existsSync)(masterKeyPath)) {
|
|
27451
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs36.readFileSync)(masterKeyPath, "utf8").trim();
|
|
27261
27452
|
}
|
|
27262
27453
|
const script = [
|
|
27263
27454
|
'require "json"',
|
|
@@ -27267,9 +27458,9 @@ async function decryptRailsCredentials(input) {
|
|
|
27267
27458
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
27268
27459
|
"puts JSON.generate(config.config)"
|
|
27269
27460
|
].join("\n");
|
|
27270
|
-
const scriptDir = (0,
|
|
27271
|
-
const scriptPath = (0,
|
|
27272
|
-
(0,
|
|
27461
|
+
const scriptDir = (0, import_node_fs36.mkdtempSync)((0, import_node_path34.join)((0, import_node_os15.tmpdir)(), "mmi-rails-decrypt-"));
|
|
27462
|
+
const scriptPath = (0, import_node_path34.join)(scriptDir, "decrypt.rb");
|
|
27463
|
+
(0, import_node_fs36.writeFileSync)(scriptPath, script, "utf8");
|
|
27273
27464
|
try {
|
|
27274
27465
|
const args = ["exec", "ruby", scriptPath];
|
|
27275
27466
|
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
@@ -27281,7 +27472,7 @@ async function decryptRailsCredentials(input) {
|
|
|
27281
27472
|
});
|
|
27282
27473
|
return JSON.parse(stdout);
|
|
27283
27474
|
} finally {
|
|
27284
|
-
(0,
|
|
27475
|
+
(0, import_node_fs36.rmSync)(scriptDir, { recursive: true, force: true });
|
|
27285
27476
|
}
|
|
27286
27477
|
}
|
|
27287
27478
|
async function readSecretStdin() {
|
|
@@ -27371,7 +27562,7 @@ function registerSecretsCommands(program3) {
|
|
|
27371
27562
|
let body;
|
|
27372
27563
|
if (o.file) {
|
|
27373
27564
|
try {
|
|
27374
|
-
body = (0,
|
|
27565
|
+
body = (0, import_node_fs36.readFileSync)((0, import_node_path34.resolve)(o.file), "utf8");
|
|
27375
27566
|
} catch (e) {
|
|
27376
27567
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
27377
27568
|
}
|
|
@@ -27476,7 +27667,7 @@ function registerSecretsCommands(program3) {
|
|
|
27476
27667
|
{
|
|
27477
27668
|
...d,
|
|
27478
27669
|
decryptRailsCredentials,
|
|
27479
|
-
removeFile: (path2) => (0,
|
|
27670
|
+
removeFile: (path2) => (0, import_node_fs36.unlinkSync)((0, import_node_path34.resolve)(o.appDir ?? process.cwd(), path2))
|
|
27480
27671
|
},
|
|
27481
27672
|
{
|
|
27482
27673
|
repo: o.repo,
|
|
@@ -27681,7 +27872,7 @@ function emitCliCallTelemetry(command) {
|
|
|
27681
27872
|
}
|
|
27682
27873
|
|
|
27683
27874
|
// src/box-commands.ts
|
|
27684
|
-
var
|
|
27875
|
+
var import_node_fs37 = require("node:fs");
|
|
27685
27876
|
|
|
27686
27877
|
// src/box.ts
|
|
27687
27878
|
var BOX_KEYS = {
|
|
@@ -27884,7 +28075,7 @@ function registerBoxCommands(program3) {
|
|
|
27884
28075
|
}
|
|
27885
28076
|
if (o.json) console.log(JSON.stringify({ box: found, incomplete }, null, 2));
|
|
27886
28077
|
else if (o.ssh && o.script) {
|
|
27887
|
-
(0,
|
|
28078
|
+
(0, import_node_fs37.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
|
|
27888
28079
|
console.log(`wrote ${o.script} \u2014 run: bash "${o.script}"`);
|
|
27889
28080
|
} else if (o.ssh) console.log(`${formatSshRecipe(found)}
|
|
27890
28081
|
${SSH_RECIPE_AGENT_NOTE}`);
|
|
@@ -28203,7 +28394,7 @@ function registerSchedulesCommands(program3) {
|
|
|
28203
28394
|
|
|
28204
28395
|
// src/schedules-lift-command.ts
|
|
28205
28396
|
var import_promises7 = require("node:fs/promises");
|
|
28206
|
-
var
|
|
28397
|
+
var import_node_path35 = require("node:path");
|
|
28207
28398
|
var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
|
|
28208
28399
|
var SCHEDULE_REPO_RE = /^[A-Za-z0-9_.-]+$/;
|
|
28209
28400
|
var SchedulesLiftUsageError = class extends Error {
|
|
@@ -28230,7 +28421,7 @@ async function readWorkflowFiles(dir) {
|
|
|
28230
28421
|
const files = [];
|
|
28231
28422
|
for (const name of names.sort()) {
|
|
28232
28423
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
28233
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises7.readFile)((0,
|
|
28424
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises7.readFile)((0, import_node_path35.join)(dir, name), "utf8") });
|
|
28234
28425
|
}
|
|
28235
28426
|
return files;
|
|
28236
28427
|
}
|
|
@@ -28378,9 +28569,9 @@ function registerEdgeCommands(program3) {
|
|
|
28378
28569
|
}
|
|
28379
28570
|
|
|
28380
28571
|
// src/bootstrap-commands.ts
|
|
28381
|
-
var
|
|
28382
|
-
var
|
|
28383
|
-
var
|
|
28572
|
+
var import_node_fs38 = require("node:fs");
|
|
28573
|
+
var import_node_os16 = require("node:os");
|
|
28574
|
+
var import_node_path36 = require("node:path");
|
|
28384
28575
|
|
|
28385
28576
|
// src/bootstrap-drift.ts
|
|
28386
28577
|
var import_node_crypto7 = require("node:crypto");
|
|
@@ -29260,13 +29451,13 @@ function registerBootstrapCommands(program3) {
|
|
|
29260
29451
|
client: defaultGitHubClient(),
|
|
29261
29452
|
projectMeta: meta,
|
|
29262
29453
|
deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
|
|
29263
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
29454
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs38.existsSync)(path2) ? (0, import_node_fs38.readFileSync)(path2, "utf8") : null,
|
|
29264
29455
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
29265
29456
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
29266
29457
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
29267
29458
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
29268
29459
|
// sanction, which is the pre-#3664 behaviour.
|
|
29269
|
-
sanctionedAdmins: (0,
|
|
29460
|
+
sanctionedAdmins: (0, import_node_fs38.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs38.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
29270
29461
|
requiredGcpApis: (() => {
|
|
29271
29462
|
const v = meta?.requiredGcpApis;
|
|
29272
29463
|
if (Array.isArray(v)) return v;
|
|
@@ -29319,14 +29510,14 @@ function registerBootstrapCommands(program3) {
|
|
|
29319
29510
|
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 () => {
|
|
29320
29511
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
29321
29512
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
29322
|
-
if (!(0,
|
|
29513
|
+
if (!(0, import_node_fs38.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`);
|
|
29323
29514
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
29324
29515
|
if (!seedSource.ok) return fail(`bootstrap drift: ${seedSource.reason}`);
|
|
29325
|
-
const manifest = loadBootstrapSeeds((0,
|
|
29516
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs38.readFileSync)(manifestPath, "utf8"));
|
|
29326
29517
|
const hubContents = /* @__PURE__ */ new Map();
|
|
29327
29518
|
for (const s of manifest.seeds) {
|
|
29328
29519
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
29329
|
-
hubContents.set(s.target, (0,
|
|
29520
|
+
hubContents.set(s.target, (0, import_node_fs38.existsSync)(s.target) ? (0, import_node_fs38.readFileSync)(s.target, "utf8") : null);
|
|
29330
29521
|
}
|
|
29331
29522
|
let targets;
|
|
29332
29523
|
let classOf = (_repo) => "deployable";
|
|
@@ -29405,10 +29596,10 @@ function registerBootstrapCommands(program3) {
|
|
|
29405
29596
|
return fail(`bootstrap apply: ${e.message}`);
|
|
29406
29597
|
}
|
|
29407
29598
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
29408
|
-
if (!(0,
|
|
29599
|
+
if (!(0, import_node_fs38.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`);
|
|
29409
29600
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
29410
29601
|
if (!seedSource.ok) return fail(`bootstrap apply: ${seedSource.reason}`);
|
|
29411
|
-
const manifest = loadBootstrapSeeds((0,
|
|
29602
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs38.readFileSync)(manifestPath, "utf8"));
|
|
29412
29603
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
29413
29604
|
const slug = parsedRepo.slug;
|
|
29414
29605
|
const onlyTarget = o.only.trim();
|
|
@@ -29419,16 +29610,16 @@ function registerBootstrapCommands(program3) {
|
|
|
29419
29610
|
${known}`);
|
|
29420
29611
|
}
|
|
29421
29612
|
const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
|
|
29422
|
-
const readFile9 = (p) => (0,
|
|
29613
|
+
const readFile9 = (p) => (0, import_node_fs38.existsSync)(p) ? (0, import_node_fs38.readFileSync)(p, "utf8") : null;
|
|
29423
29614
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
29424
29615
|
const putSeed = async (target, content, ref, sha) => {
|
|
29425
|
-
const tmp = (0,
|
|
29426
|
-
(0,
|
|
29616
|
+
const tmp = (0, import_node_path36.join)((0, import_node_os16.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
29617
|
+
(0, import_node_fs38.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
29427
29618
|
try {
|
|
29428
29619
|
await gh(contentPutInputArgs(repo, target, tmp));
|
|
29429
29620
|
} finally {
|
|
29430
29621
|
try {
|
|
29431
|
-
(0,
|
|
29622
|
+
(0, import_node_fs38.unlinkSync)(tmp);
|
|
29432
29623
|
} catch {
|
|
29433
29624
|
}
|
|
29434
29625
|
}
|
|
@@ -29693,10 +29884,10 @@ LIVE apply to ${repo}:
|
|
|
29693
29884
|
bootstrap.command("propagate").description("#4238: re-entrant canary\u2192wave tick \u2014 plan (or, with --execute, open) per-repo PRs fanning an org-owned seed out to the fleet").option("--target <path>", "the manifest target to propagate (an ownership:org + source:self seed, e.g. .github/workflows/agent-pr.yml)").option("--execute", "LIVE tick via gh (master-gated) \u2014 opens/reuses per-repo seed-propagate PRs; dry-run prints the plan only").option("--json", "machine-readable output").action(async () => {
|
|
29694
29885
|
const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
|
|
29695
29886
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
29696
|
-
if (!(0,
|
|
29887
|
+
if (!(0, import_node_fs38.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`);
|
|
29697
29888
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
29698
29889
|
if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
|
|
29699
|
-
const manifest = loadBootstrapSeeds((0,
|
|
29890
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs38.readFileSync)(manifestPath, "utf8"));
|
|
29700
29891
|
const propagatable = manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self");
|
|
29701
29892
|
if (!o.target) {
|
|
29702
29893
|
return fail(`bootstrap propagate: --target <path> is required \u2014 one of:
|
|
@@ -29705,8 +29896,8 @@ LIVE apply to ${repo}:
|
|
|
29705
29896
|
const seed = propagatable.find((s) => s.target === o.target);
|
|
29706
29897
|
if (!seed) return fail(`bootstrap propagate: --target '${o.target}' names no ownership:org + source:self seed in ${manifestPath}. Propagatable targets:
|
|
29707
29898
|
${propagatable.map((s) => s.target).join("\n ")}`);
|
|
29708
|
-
if (!(0,
|
|
29709
|
-
const hubContent = (0,
|
|
29899
|
+
if (!(0, import_node_fs38.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
|
|
29900
|
+
const hubContent = (0, import_node_fs38.readFileSync)(seed.target, "utf8");
|
|
29710
29901
|
const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
|
|
29711
29902
|
const cfg = await loadConfig();
|
|
29712
29903
|
const projects = await fetchProjectsList(registryClientDeps(cfg));
|
|
@@ -29715,9 +29906,9 @@ LIVE apply to ${repo}:
|
|
|
29715
29906
|
}
|
|
29716
29907
|
const rosterRepos2 = collectRegistryRepos(projects).filter((r) => r.toLowerCase() !== "mutmutco/mmi-hub");
|
|
29717
29908
|
let independentCount = rosterRepos2.length;
|
|
29718
|
-
if ((0,
|
|
29909
|
+
if ((0, import_node_fs38.existsSync)("projects.json")) {
|
|
29719
29910
|
try {
|
|
29720
|
-
const local = JSON.parse((0,
|
|
29911
|
+
const local = JSON.parse((0, import_node_fs38.readFileSync)("projects.json", "utf8"));
|
|
29721
29912
|
const localRepos = /* @__PURE__ */ new Set();
|
|
29722
29913
|
for (const p of local.projects ?? []) for (const r of p.repos ?? []) {
|
|
29723
29914
|
const full = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
|
|
@@ -29833,13 +30024,13 @@ LIVE apply to ${repo}:
|
|
|
29833
30024
|
} catch {
|
|
29834
30025
|
existingSha = void 0;
|
|
29835
30026
|
}
|
|
29836
|
-
const tmp = (0,
|
|
29837
|
-
(0,
|
|
30027
|
+
const tmp = (0, import_node_path36.join)((0, import_node_os16.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
30028
|
+
(0, import_node_fs38.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, hubContent, branch, existingSha)), "utf8");
|
|
29838
30029
|
try {
|
|
29839
30030
|
await gh(contentPutInputArgs(rec.repo, seed.target, tmp));
|
|
29840
30031
|
} finally {
|
|
29841
30032
|
try {
|
|
29842
|
-
(0,
|
|
30033
|
+
(0, import_node_fs38.unlinkSync)(tmp);
|
|
29843
30034
|
} catch {
|
|
29844
30035
|
}
|
|
29845
30036
|
}
|
|
@@ -29897,10 +30088,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
29897
30088
|
return fail(`bootstrap rollback: ${e.message}`);
|
|
29898
30089
|
}
|
|
29899
30090
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
29900
|
-
if (!(0,
|
|
30091
|
+
if (!(0, import_node_fs38.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)`);
|
|
29901
30092
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
29902
30093
|
if (!seedSource.ok) return fail(`bootstrap rollback: ${seedSource.reason}`);
|
|
29903
|
-
const manifest = loadBootstrapSeeds((0,
|
|
30094
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs38.readFileSync)(manifestPath, "utf8"));
|
|
29904
30095
|
const propagatable = manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self");
|
|
29905
30096
|
if (!o.target) {
|
|
29906
30097
|
return fail(`bootstrap rollback: --target <path> is required \u2014 one of:
|
|
@@ -29917,10 +30108,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
29917
30108
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
29918
30109
|
let candidates;
|
|
29919
30110
|
if (o.record) {
|
|
29920
|
-
if (!(0,
|
|
30111
|
+
if (!(0, import_node_fs38.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
|
|
29921
30112
|
let parsed;
|
|
29922
30113
|
try {
|
|
29923
|
-
parsed = JSON.parse((0,
|
|
30114
|
+
parsed = JSON.parse((0, import_node_fs38.readFileSync)(o.record, "utf8"));
|
|
29924
30115
|
} catch (e) {
|
|
29925
30116
|
return fail(`bootstrap rollback: --record '${o.record}' is not valid JSON: ${e.message}`);
|
|
29926
30117
|
}
|
|
@@ -29989,13 +30180,13 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
29989
30180
|
} catch {
|
|
29990
30181
|
existingSha = void 0;
|
|
29991
30182
|
}
|
|
29992
|
-
const tmp = (0,
|
|
29993
|
-
(0,
|
|
30183
|
+
const tmp = (0, import_node_path36.join)((0, import_node_os16.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
30184
|
+
(0, import_node_fs38.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
|
|
29994
30185
|
try {
|
|
29995
30186
|
await gh(contentPutInputArgs(repo, seed.target, tmp));
|
|
29996
30187
|
} finally {
|
|
29997
30188
|
try {
|
|
29998
|
-
(0,
|
|
30189
|
+
(0, import_node_fs38.unlinkSync)(tmp);
|
|
29999
30190
|
} catch {
|
|
30000
30191
|
}
|
|
30001
30192
|
}
|
|
@@ -30017,12 +30208,12 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
30017
30208
|
}
|
|
30018
30209
|
|
|
30019
30210
|
// src/stage-commands.ts
|
|
30020
|
-
var
|
|
30021
|
-
var
|
|
30211
|
+
var import_node_fs40 = require("node:fs");
|
|
30212
|
+
var import_node_path38 = require("node:path");
|
|
30022
30213
|
|
|
30023
30214
|
// src/port-registry.ts
|
|
30024
|
-
var
|
|
30025
|
-
var
|
|
30215
|
+
var import_node_fs39 = require("node:fs");
|
|
30216
|
+
var import_node_path37 = require("node:path");
|
|
30026
30217
|
|
|
30027
30218
|
// ../infra/port-geometry.mjs
|
|
30028
30219
|
var PORT_BLOCK = 100;
|
|
@@ -30036,8 +30227,8 @@ function nextPortBlock(registry2) {
|
|
|
30036
30227
|
return [base, base + PORT_SPAN];
|
|
30037
30228
|
}
|
|
30038
30229
|
function loadPortRegistry(path2) {
|
|
30039
|
-
if (!(0,
|
|
30040
|
-
const raw = JSON.parse((0,
|
|
30230
|
+
if (!(0, import_node_fs39.existsSync)(path2)) return {};
|
|
30231
|
+
const raw = JSON.parse((0, import_node_fs39.readFileSync)(path2, "utf8"));
|
|
30041
30232
|
const out = {};
|
|
30042
30233
|
for (const [key, value] of Object.entries(raw)) {
|
|
30043
30234
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -30051,9 +30242,9 @@ function ensurePortRange(repo, path2) {
|
|
|
30051
30242
|
const existing = registry2[repo];
|
|
30052
30243
|
if (existing) return existing;
|
|
30053
30244
|
const range = nextPortBlock(registry2);
|
|
30054
|
-
const raw = (0,
|
|
30245
|
+
const raw = (0, import_node_fs39.existsSync)(path2) ? JSON.parse((0, import_node_fs39.readFileSync)(path2, "utf8")) : {};
|
|
30055
30246
|
raw[repo] = range;
|
|
30056
|
-
(0,
|
|
30247
|
+
(0, import_node_fs39.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
|
|
30057
30248
|
return range;
|
|
30058
30249
|
}
|
|
30059
30250
|
function portCursorSeed(registry2) {
|
|
@@ -30075,22 +30266,22 @@ function existingPortRange(repo, registry2) {
|
|
|
30075
30266
|
return registry2[repo] ?? null;
|
|
30076
30267
|
}
|
|
30077
30268
|
function portRangeInfraAt(root, source) {
|
|
30078
|
-
const registryPath = (0,
|
|
30079
|
-
const ddbScriptPath = (0,
|
|
30080
|
-
if (!(0,
|
|
30269
|
+
const registryPath = (0, import_node_path37.join)(root, "infra", "port-ranges.json");
|
|
30270
|
+
const ddbScriptPath = (0, import_node_path37.join)(root, "infra", "port-ddb.mjs");
|
|
30271
|
+
if (!(0, import_node_fs39.existsSync)(registryPath) || !(0, import_node_fs39.existsSync)(ddbScriptPath)) return null;
|
|
30081
30272
|
return { root, source, registryPath, ddbScriptPath };
|
|
30082
30273
|
}
|
|
30083
30274
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
30084
30275
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
30085
30276
|
if (direct) return direct;
|
|
30086
|
-
for (let dir = cwd; ; dir = (0,
|
|
30087
|
-
const sibling = portRangeInfraAt((0,
|
|
30277
|
+
for (let dir = cwd; ; dir = (0, import_node_path37.dirname)(dir)) {
|
|
30278
|
+
const sibling = portRangeInfraAt((0, import_node_path37.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
30088
30279
|
if (sibling) return sibling;
|
|
30089
|
-
const parent = (0,
|
|
30280
|
+
const parent = (0, import_node_path37.dirname)(dir);
|
|
30090
30281
|
if (parent === dir) break;
|
|
30091
30282
|
}
|
|
30092
30283
|
if (packageDir) {
|
|
30093
|
-
const pkgRoot = (0,
|
|
30284
|
+
const pkgRoot = (0, import_node_path37.join)(packageDir, "..", "..");
|
|
30094
30285
|
const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
|
|
30095
30286
|
if (pkgFrom) return pkgFrom;
|
|
30096
30287
|
}
|
|
@@ -30284,8 +30475,8 @@ function registerStageCommands(program3) {
|
|
|
30284
30475
|
const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
|
|
30285
30476
|
return decideStage({
|
|
30286
30477
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
30287
|
-
hasCompose: (0,
|
|
30288
|
-
hasEnvExample: (0,
|
|
30478
|
+
hasCompose: (0, import_node_fs40.existsSync)((0, import_node_path38.join)(process.cwd(), "docker-compose.yml")),
|
|
30479
|
+
hasEnvExample: (0, import_node_fs40.existsSync)((0, import_node_path38.join)(process.cwd(), ".env.example"))
|
|
30289
30480
|
});
|
|
30290
30481
|
}
|
|
30291
30482
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -30776,10 +30967,10 @@ function registerBoardCommands(program3) {
|
|
|
30776
30967
|
}
|
|
30777
30968
|
|
|
30778
30969
|
// src/merge-cleanup.ts
|
|
30779
|
-
var
|
|
30970
|
+
var import_node_fs41 = require("node:fs");
|
|
30780
30971
|
var import_promises9 = require("node:fs/promises");
|
|
30781
|
-
var
|
|
30782
|
-
var
|
|
30972
|
+
var import_node_path40 = require("node:path");
|
|
30973
|
+
var import_node_os17 = require("node:os");
|
|
30783
30974
|
var import_node_child_process17 = require("node:child_process");
|
|
30784
30975
|
|
|
30785
30976
|
// src/board-advance.ts
|
|
@@ -30866,7 +31057,7 @@ function boardAdvanceFailureMessage(result) {
|
|
|
30866
31057
|
|
|
30867
31058
|
// src/deferred-registry-store.ts
|
|
30868
31059
|
var import_promises8 = require("node:fs/promises");
|
|
30869
|
-
var
|
|
31060
|
+
var import_node_path39 = require("node:path");
|
|
30870
31061
|
var sleep2 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
30871
31062
|
async function atomicWrite(target, contents) {
|
|
30872
31063
|
const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -30917,12 +31108,12 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
|
|
|
30917
31108
|
},
|
|
30918
31109
|
// Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
|
|
30919
31110
|
write: async (entries) => {
|
|
30920
|
-
await (0, import_promises8.mkdir)((0,
|
|
31111
|
+
await (0, import_promises8.mkdir)((0, import_node_path39.dirname)(registryPath), { recursive: true });
|
|
30921
31112
|
await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
|
|
30922
31113
|
},
|
|
30923
31114
|
// Serialized read-modify-write under the repo-wide lock (#2846).
|
|
30924
31115
|
update: async (mutate) => {
|
|
30925
|
-
await (0, import_promises8.mkdir)((0,
|
|
31116
|
+
await (0, import_promises8.mkdir)((0, import_node_path39.dirname)(registryPath), { recursive: true });
|
|
30926
31117
|
const deadline = Date.now() + opts.maxWaitMs;
|
|
30927
31118
|
for (; ; ) {
|
|
30928
31119
|
const guard = await acquireLock(lockPath, opts, deadline);
|
|
@@ -31086,17 +31277,17 @@ ${err.stderr ?? ""}`;
|
|
|
31086
31277
|
return { step, status: `failed: ${msg}` };
|
|
31087
31278
|
}
|
|
31088
31279
|
}
|
|
31089
|
-
async function bestEffortCloseMissingWorktreeLeases(leaseDir = (0,
|
|
31280
|
+
async function bestEffortCloseMissingWorktreeLeases(leaseDir = (0, import_node_path40.join)((0, import_node_os17.homedir)(), ".jerv", "leases"), exists = import_node_fs41.existsSync) {
|
|
31090
31281
|
let names = [];
|
|
31091
31282
|
try {
|
|
31092
|
-
names = (0,
|
|
31283
|
+
names = (0, import_node_fs41.readdirSync)(leaseDir);
|
|
31093
31284
|
} catch {
|
|
31094
31285
|
return;
|
|
31095
31286
|
}
|
|
31096
31287
|
for (const name of names) {
|
|
31097
31288
|
if (!name.endsWith(".json")) continue;
|
|
31098
31289
|
try {
|
|
31099
|
-
const rec = JSON.parse((0,
|
|
31290
|
+
const rec = JSON.parse((0, import_node_fs41.readFileSync)((0, import_node_path40.join)(leaseDir, name), "utf8"));
|
|
31100
31291
|
if (rec.kind !== "worktree" || rec.state === "closed" || typeof rec.ref !== "string" || !rec.ref.trim()) continue;
|
|
31101
31292
|
if (!exists(rec.ref)) await bestEffortLeaseClose(rec.ref);
|
|
31102
31293
|
} catch {
|
|
@@ -31110,7 +31301,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
31110
31301
|
);
|
|
31111
31302
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
31112
31303
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
31113
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
31304
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path40.dirname)((0, import_node_path40.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
31114
31305
|
const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
|
|
31115
31306
|
const owners = readWorktreeOwners(primaryRepoRoot);
|
|
31116
31307
|
const removalNow = Date.now();
|
|
@@ -31169,7 +31360,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
31169
31360
|
const cleanup = await cleanupPrMergeLocalBranch(branch.branch, {
|
|
31170
31361
|
beforeWorktrees,
|
|
31171
31362
|
startingPath: branch.worktreePath,
|
|
31172
|
-
pathExists: (p) => (0,
|
|
31363
|
+
pathExists: (p) => (0, import_node_fs41.existsSync)(p),
|
|
31173
31364
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
31174
31365
|
teardownWorktreeStage,
|
|
31175
31366
|
deferredStore,
|
|
@@ -31208,7 +31399,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
31208
31399
|
let removalAttempted = false;
|
|
31209
31400
|
try {
|
|
31210
31401
|
const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, cleanupRoots, {
|
|
31211
|
-
realpath: (path2) => (0,
|
|
31402
|
+
realpath: (path2) => (0, import_node_fs41.realpathSync)(path2)
|
|
31212
31403
|
});
|
|
31213
31404
|
if (!cleanupTarget.ok) {
|
|
31214
31405
|
result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
|
|
@@ -31310,13 +31501,13 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
|
|
|
31310
31501
|
const commits = JSON.parse(raw).commits ?? [];
|
|
31311
31502
|
const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
|
|
31312
31503
|
if (!body) return void 0;
|
|
31313
|
-
const dir = (0,
|
|
31314
|
-
const path2 = (0,
|
|
31315
|
-
(0,
|
|
31504
|
+
const dir = (0, import_node_fs41.mkdtempSync)((0, import_node_path40.join)((0, import_node_os17.tmpdir)(), "mmi-squash-body-"));
|
|
31505
|
+
const path2 = (0, import_node_path40.join)(dir, "body.txt");
|
|
31506
|
+
(0, import_node_fs41.writeFileSync)(path2, `${body}
|
|
31316
31507
|
`, "utf8");
|
|
31317
31508
|
return { path: path2, cleanup: () => {
|
|
31318
31509
|
try {
|
|
31319
|
-
(0,
|
|
31510
|
+
(0, import_node_fs41.rmSync)(dir, { recursive: true, force: true });
|
|
31320
31511
|
} catch {
|
|
31321
31512
|
}
|
|
31322
31513
|
} };
|
|
@@ -31439,13 +31630,13 @@ var realWorktreeDirRemover = {
|
|
|
31439
31630
|
const target = win32LongPath(p);
|
|
31440
31631
|
let st;
|
|
31441
31632
|
try {
|
|
31442
|
-
st = (0,
|
|
31633
|
+
st = (0, import_node_fs41.lstatSync)(target);
|
|
31443
31634
|
} catch {
|
|
31444
31635
|
return null;
|
|
31445
31636
|
}
|
|
31446
31637
|
if (st.isSymbolicLink()) return "link";
|
|
31447
31638
|
try {
|
|
31448
|
-
(0,
|
|
31639
|
+
(0, import_node_fs41.readlinkSync)(target);
|
|
31449
31640
|
return "link";
|
|
31450
31641
|
} catch {
|
|
31451
31642
|
}
|
|
@@ -31453,7 +31644,7 @@ var realWorktreeDirRemover = {
|
|
|
31453
31644
|
},
|
|
31454
31645
|
readdir: (p) => {
|
|
31455
31646
|
try {
|
|
31456
|
-
return (0,
|
|
31647
|
+
return (0, import_node_fs41.readdirSync)(win32LongPath(p));
|
|
31457
31648
|
} catch {
|
|
31458
31649
|
return [];
|
|
31459
31650
|
}
|
|
@@ -31463,9 +31654,9 @@ var realWorktreeDirRemover = {
|
|
|
31463
31654
|
detachLink: (p) => {
|
|
31464
31655
|
const target = win32LongPath(p);
|
|
31465
31656
|
try {
|
|
31466
|
-
(0,
|
|
31657
|
+
(0, import_node_fs41.rmdirSync)(target);
|
|
31467
31658
|
} catch {
|
|
31468
|
-
(0,
|
|
31659
|
+
(0, import_node_fs41.unlinkSync)(target);
|
|
31469
31660
|
}
|
|
31470
31661
|
},
|
|
31471
31662
|
// #4904: Windows MAX_PATH aborts git worktree remove; the fallback must use \\?\ so the dir
|
|
@@ -31488,7 +31679,7 @@ function worktreeRemoveDeps(execGit) {
|
|
|
31488
31679
|
detachReparsePoints: (worktreePath) => detachReparsePoints(worktreePath, realWorktreeDirRemover),
|
|
31489
31680
|
removeWorktreeDir: async (worktreePath) => removeWorktreeTree(worktreePath, await resolvePrimaryCheckout(execGit), realWorktreeDirRemover),
|
|
31490
31681
|
// #4850: verify the directory is actually gone before any caller reports completion.
|
|
31491
|
-
pathExists: (worktreePath) => (0,
|
|
31682
|
+
pathExists: (worktreePath) => (0, import_node_fs41.existsSync)(worktreePath)
|
|
31492
31683
|
};
|
|
31493
31684
|
}
|
|
31494
31685
|
async function worktreeHasStageState(worktreePath) {
|
|
@@ -31502,9 +31693,9 @@ async function worktreeHasStageState(worktreePath) {
|
|
|
31502
31693
|
}
|
|
31503
31694
|
}
|
|
31504
31695
|
function stageStateFileBelongsToWorktree(statePath, worktreePath) {
|
|
31505
|
-
if (!(0,
|
|
31696
|
+
if (!(0, import_node_fs41.existsSync)(statePath)) return false;
|
|
31506
31697
|
try {
|
|
31507
|
-
const state = JSON.parse((0,
|
|
31698
|
+
const state = JSON.parse((0, import_node_fs41.readFileSync)(statePath, "utf8"));
|
|
31508
31699
|
const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
|
|
31509
31700
|
return Boolean(recordedCwd && isPathUnderDirectory2(recordedCwd, worktreePath));
|
|
31510
31701
|
} catch {
|
|
@@ -31939,15 +32130,15 @@ async function checkDocsIndexAtHead(opts, deps) {
|
|
|
31939
32130
|
}
|
|
31940
32131
|
|
|
31941
32132
|
// src/worktree-lifecycle-commands.ts
|
|
31942
|
-
var
|
|
32133
|
+
var import_node_fs43 = require("node:fs");
|
|
31943
32134
|
var import_promises10 = require("node:fs/promises");
|
|
31944
|
-
var
|
|
31945
|
-
var
|
|
32135
|
+
var import_node_os18 = require("node:os");
|
|
32136
|
+
var import_node_path42 = require("node:path");
|
|
31946
32137
|
|
|
31947
32138
|
// src/worktree-install-cache.ts
|
|
31948
32139
|
var import_node_crypto8 = require("node:crypto");
|
|
31949
|
-
var
|
|
31950
|
-
var
|
|
32140
|
+
var import_node_fs42 = require("node:fs");
|
|
32141
|
+
var import_node_path41 = require("node:path");
|
|
31951
32142
|
var CACHE_DIR = "worktree-install-cache";
|
|
31952
32143
|
var MANIFEST = "manifest.json";
|
|
31953
32144
|
var NODE_MODULES2 = "node_modules";
|
|
@@ -31960,16 +32151,16 @@ var LOCKFILE_NAMES = [
|
|
|
31960
32151
|
"package-lock.json"
|
|
31961
32152
|
];
|
|
31962
32153
|
var realWorktreeInstallCacheFs = {
|
|
31963
|
-
exists:
|
|
31964
|
-
readFile: (path2) => (0,
|
|
31965
|
-
lstat: (path2) => (0,
|
|
31966
|
-
copyDir: (from, to) => (0,
|
|
32154
|
+
exists: import_node_fs42.existsSync,
|
|
32155
|
+
readFile: (path2) => (0, import_node_fs42.readFileSync)(path2, "utf8"),
|
|
32156
|
+
lstat: (path2) => (0, import_node_fs42.lstatSync)(path2),
|
|
32157
|
+
copyDir: (from, to) => (0, import_node_fs42.cpSync)(from, to, { recursive: true, force: true }),
|
|
31967
32158
|
mkdirp: (path2) => {
|
|
31968
|
-
(0,
|
|
32159
|
+
(0, import_node_fs42.mkdirSync)(path2, { recursive: true });
|
|
31969
32160
|
},
|
|
31970
|
-
writeFile: (path2, contents) => (0,
|
|
32161
|
+
writeFile: (path2, contents) => (0, import_node_fs42.writeFileSync)(path2, contents, "utf8"),
|
|
31971
32162
|
rm: (path2) => {
|
|
31972
|
-
(0,
|
|
32163
|
+
(0, import_node_fs42.rmSync)(path2, { recursive: true, force: true });
|
|
31973
32164
|
}
|
|
31974
32165
|
};
|
|
31975
32166
|
function hashLockfileBytes(contents) {
|
|
@@ -31977,7 +32168,7 @@ function hashLockfileBytes(contents) {
|
|
|
31977
32168
|
}
|
|
31978
32169
|
function resolveWorktreeInstallLockfile(packageDir, fs2 = realWorktreeInstallCacheFs) {
|
|
31979
32170
|
for (const name of LOCKFILE_NAMES) {
|
|
31980
|
-
const path2 = (0,
|
|
32171
|
+
const path2 = (0, import_node_path41.join)(packageDir, name);
|
|
31981
32172
|
if (!fs2.exists(path2)) continue;
|
|
31982
32173
|
try {
|
|
31983
32174
|
const hash = hashLockfileBytes(fs2.readFile(path2));
|
|
@@ -31992,8 +32183,8 @@ function worktreeInstallCacheEntry(primaryRoot, lockfileHash) {
|
|
|
31992
32183
|
const root = repoRuntimeStatePath(primaryRoot, CACHE_DIR, lockfileHash);
|
|
31993
32184
|
return {
|
|
31994
32185
|
root,
|
|
31995
|
-
manifestPath: (0,
|
|
31996
|
-
nodeModulesPath: (0,
|
|
32186
|
+
manifestPath: (0, import_node_path41.join)(root, MANIFEST),
|
|
32187
|
+
nodeModulesPath: (0, import_node_path41.join)(root, NODE_MODULES2)
|
|
31997
32188
|
};
|
|
31998
32189
|
}
|
|
31999
32190
|
function readWorktreeInstallCacheManifest(manifestPath, fs2 = realWorktreeInstallCacheFs) {
|
|
@@ -32033,7 +32224,7 @@ function invalidateWorktreeInstallCacheEntry(entry, fs2 = realWorktreeInstallCac
|
|
|
32033
32224
|
}
|
|
32034
32225
|
}
|
|
32035
32226
|
function removeMaterializedTree(packageDir, fs2) {
|
|
32036
|
-
const dest = (0,
|
|
32227
|
+
const dest = (0, import_node_path41.join)(packageDir, NODE_MODULES2);
|
|
32037
32228
|
if (!fs2.exists(dest)) return;
|
|
32038
32229
|
fs2.rm(dest);
|
|
32039
32230
|
if (fs2.exists(dest)) {
|
|
@@ -32041,7 +32232,7 @@ function removeMaterializedTree(packageDir, fs2) {
|
|
|
32041
32232
|
}
|
|
32042
32233
|
}
|
|
32043
32234
|
function materializeCachedNodeModules(entry, destPackageDir, fs2 = realWorktreeInstallCacheFs) {
|
|
32044
|
-
const dest = (0,
|
|
32235
|
+
const dest = (0, import_node_path41.join)(destPackageDir, NODE_MODULES2);
|
|
32045
32236
|
try {
|
|
32046
32237
|
if (fs2.exists(dest)) fs2.rm(dest);
|
|
32047
32238
|
fs2.mkdirp(destPackageDir);
|
|
@@ -32056,7 +32247,7 @@ function materializeCachedNodeModules(entry, destPackageDir, fs2 = realWorktreeI
|
|
|
32056
32247
|
}
|
|
32057
32248
|
}
|
|
32058
32249
|
async function storeWorktreeInstallCacheEntry(primaryRoot, lockfile, command, sourcePackageDir, fs2 = realWorktreeInstallCacheFs, now = Date.now()) {
|
|
32059
|
-
const source = (0,
|
|
32250
|
+
const source = (0, import_node_path41.join)(sourcePackageDir, NODE_MODULES2);
|
|
32060
32251
|
if (!isMaterializableNodeModulesDir(source, fs2)) return;
|
|
32061
32252
|
const entry = worktreeInstallCacheEntry(primaryRoot, lockfile.hash);
|
|
32062
32253
|
const manifest = {
|
|
@@ -32340,7 +32531,7 @@ function classifyStaleLeaks(input) {
|
|
|
32340
32531
|
var defaultOrphanDirScanDeps = {
|
|
32341
32532
|
listDirs: (root) => {
|
|
32342
32533
|
try {
|
|
32343
|
-
return (0,
|
|
32534
|
+
return (0, import_node_fs43.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path42.join)(root, e.name));
|
|
32344
32535
|
} catch {
|
|
32345
32536
|
return [];
|
|
32346
32537
|
}
|
|
@@ -32530,13 +32721,13 @@ function registerWorktreeCommands(program3) {
|
|
|
32530
32721
|
const detached = headBorn && !symbolicBranch;
|
|
32531
32722
|
const branch = symbolicBranch || (detached ? "HEAD" : "");
|
|
32532
32723
|
if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
|
|
32533
|
-
const gitFile = (0,
|
|
32534
|
-
const isLinked = (0,
|
|
32724
|
+
const gitFile = (0, import_node_path42.join)(wtPath, ".git");
|
|
32725
|
+
const isLinked = (0, import_node_fs43.existsSync)(gitFile) && (0, import_node_fs43.statSync)(gitFile).isFile();
|
|
32535
32726
|
if (apply && !isLinked) {
|
|
32536
32727
|
return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
|
|
32537
32728
|
}
|
|
32538
32729
|
const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
32539
|
-
const primaryCheckout = commonDir ? (0,
|
|
32730
|
+
const primaryCheckout = commonDir ? (0, import_node_path42.dirname)(commonDir) : wtPath;
|
|
32540
32731
|
const localBranchNames = await execFileP2("git", ["-C", primaryCheckout, "for-each-ref", "--format=%(refname:short)", "refs/heads"], { timeout: GIT_TIMEOUT_MS }).then(({ stdout }) => new Set((stdout || "").split("\n").map((l) => l.trim()).filter(Boolean))).catch(() => void 0);
|
|
32541
32732
|
const orphan = classifyOrphanedWorktree({
|
|
32542
32733
|
branch,
|
|
@@ -32796,7 +32987,7 @@ async function gatherWorktreeContext() {
|
|
|
32796
32987
|
const porcelain = (await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
32797
32988
|
const parsed = parseWorktreePorcelain(porcelain);
|
|
32798
32989
|
const primaryPath = parsed[0]?.path ?? repoRoot2;
|
|
32799
|
-
const worktrees = parsed.map((w) => ({ path: (0,
|
|
32990
|
+
const worktrees = parsed.map((w) => ({ path: (0, import_node_path42.resolve)(toNativePath(w.path)), branch: w.branch, primary: w.path === primaryPath }));
|
|
32800
32991
|
const branchOut = (await execFileP2("git", ["branch", "--format=%(refname:short)"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
32801
32992
|
const localBranches = branchOut.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
32802
32993
|
const currentBranch2 = (await execFileP2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || void 0;
|
|
@@ -32834,17 +33025,17 @@ async function gatherWorktreeContext() {
|
|
|
32834
33025
|
if (s) stages.push({ path: wt.path, port: s.port });
|
|
32835
33026
|
}
|
|
32836
33027
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
32837
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
33028
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path42.dirname)((0, import_node_path42.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
32838
33029
|
const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
|
|
32839
33030
|
let orphanDirs = [];
|
|
32840
|
-
if ((0,
|
|
33031
|
+
if ((0, import_node_fs43.existsSync)(wtRoot)) {
|
|
32841
33032
|
orphanDirs = scanOrphanDirs(wtRoot, worktreeGitRoot, {
|
|
32842
33033
|
...defaultOrphanDirScanDeps,
|
|
32843
33034
|
listDirs: (root) => worktreeScanDirs(root, primaryRepoRoot, defaultOrphanDirScanDeps.listDirs, isRepoCheckoutDir)
|
|
32844
33035
|
});
|
|
32845
33036
|
}
|
|
32846
33037
|
const repoContainer = worktreesRootOf(primaryRepoRoot);
|
|
32847
|
-
if ((0,
|
|
33038
|
+
if ((0, import_node_fs43.existsSync)(repoContainer)) {
|
|
32848
33039
|
for (const dir of defaultOrphanDirScanDeps.listDirs(repoContainer)) {
|
|
32849
33040
|
if (orphanDirs.some((o) => o.path === dir)) continue;
|
|
32850
33041
|
const inspected = inspectSiblingWorktreeDir(dir, worktreeGitRoot);
|
|
@@ -32868,7 +33059,7 @@ async function gatherWorktreeContext() {
|
|
|
32868
33059
|
});
|
|
32869
33060
|
const helperWorktrees = scanHelperWorktrees(primaryRepoRoot, worktreeGitRoot);
|
|
32870
33061
|
const leaseRefs = readJervWorktreeLeaseRefs();
|
|
32871
|
-
const missingLeaseRefs = leaseRefs.filter((ref) => !(0,
|
|
33062
|
+
const missingLeaseRefs = leaseRefs.filter((ref) => !(0, import_node_fs43.existsSync)(ref));
|
|
32872
33063
|
const remoteUrls = await gitRemoteUrls();
|
|
32873
33064
|
const otherCheckouts = discoverOtherPrimaries(primaryRepoRoot).map((path2) => ({
|
|
32874
33065
|
path: path2,
|
|
@@ -32898,16 +33089,16 @@ async function gatherWorktreeContext() {
|
|
|
32898
33089
|
};
|
|
32899
33090
|
}
|
|
32900
33091
|
function gitConfigRemoteUrls(checkout) {
|
|
32901
|
-
const gitPath = (0,
|
|
32902
|
-
let configPath = (0,
|
|
33092
|
+
const gitPath = (0, import_node_path42.join)(checkout, ".git");
|
|
33093
|
+
let configPath = (0, import_node_path42.join)(checkout, ".git", "config");
|
|
32903
33094
|
try {
|
|
32904
|
-
const st = (0,
|
|
33095
|
+
const st = (0, import_node_fs43.statSync)(gitPath);
|
|
32905
33096
|
if (st.isFile()) return [];
|
|
32906
33097
|
} catch {
|
|
32907
33098
|
return [];
|
|
32908
33099
|
}
|
|
32909
33100
|
try {
|
|
32910
|
-
const text = (0,
|
|
33101
|
+
const text = (0, import_node_fs43.readFileSync)(configPath, "utf8");
|
|
32911
33102
|
return [...text.matchAll(/^\s*url\s*=\s*(.+)$/gm)].map((m) => m[1].trim());
|
|
32912
33103
|
} catch {
|
|
32913
33104
|
return [];
|
|
@@ -32921,26 +33112,26 @@ async function gitRemoteUrls() {
|
|
|
32921
33112
|
}
|
|
32922
33113
|
function discoverOtherPrimaries(primaryRepoRoot) {
|
|
32923
33114
|
const found = /* @__PURE__ */ new Set();
|
|
32924
|
-
const parent = (0,
|
|
33115
|
+
const parent = (0, import_node_path42.dirname)(primaryRepoRoot);
|
|
32925
33116
|
try {
|
|
32926
|
-
for (const name of (0,
|
|
32927
|
-
const path2 = (0,
|
|
33117
|
+
for (const name of (0, import_node_fs43.readdirSync)(parent)) {
|
|
33118
|
+
const path2 = (0, import_node_path42.join)(parent, name);
|
|
32928
33119
|
if (path2 === primaryRepoRoot) continue;
|
|
32929
|
-
if ((0,
|
|
33120
|
+
if ((0, import_node_fs43.existsSync)((0, import_node_path42.join)(path2, ".git"))) found.add(path2);
|
|
32930
33121
|
}
|
|
32931
33122
|
} catch {
|
|
32932
33123
|
}
|
|
32933
|
-
const mirror = (0,
|
|
32934
|
-
if (mirror !== primaryRepoRoot && (0,
|
|
33124
|
+
const mirror = (0, import_node_path42.join)((0, import_node_os18.homedir)(), "Projects", (0, import_node_path42.basename)(primaryRepoRoot));
|
|
33125
|
+
if (mirror !== primaryRepoRoot && (0, import_node_fs43.existsSync)((0, import_node_path42.join)(mirror, ".git"))) found.add(mirror);
|
|
32935
33126
|
return [...found];
|
|
32936
33127
|
}
|
|
32937
|
-
function readJervWorktreeLeaseRefs(leaseDir = (0,
|
|
33128
|
+
function readJervWorktreeLeaseRefs(leaseDir = (0, import_node_path42.join)((0, import_node_os18.homedir)(), ".jerv", "leases")) {
|
|
32938
33129
|
try {
|
|
32939
33130
|
const refs = [];
|
|
32940
|
-
for (const name of (0,
|
|
33131
|
+
for (const name of (0, import_node_fs43.readdirSync)(leaseDir)) {
|
|
32941
33132
|
if (!name.endsWith(".json")) continue;
|
|
32942
33133
|
try {
|
|
32943
|
-
const rec = JSON.parse((0,
|
|
33134
|
+
const rec = JSON.parse((0, import_node_fs43.readFileSync)((0, import_node_path42.join)(leaseDir, name), "utf8"));
|
|
32944
33135
|
if (rec.kind === "worktree" && rec.state !== "closed" && typeof rec.ref === "string" && rec.ref.trim()) {
|
|
32945
33136
|
refs.push(rec.ref);
|
|
32946
33137
|
}
|
|
@@ -32956,9 +33147,9 @@ function scanHelperWorktrees(thisPrimary, thisWorktreeGitRoot) {
|
|
|
32956
33147
|
const primaries = [thisPrimary, ...discoverOtherPrimaries(thisPrimary)];
|
|
32957
33148
|
const out = [];
|
|
32958
33149
|
for (const primary of primaries) {
|
|
32959
|
-
const gitRoot = primary === thisPrimary ? thisWorktreeGitRoot : (0,
|
|
33150
|
+
const gitRoot = primary === thisPrimary ? thisWorktreeGitRoot : (0, import_node_path42.join)(primary, ".git", "worktrees");
|
|
32960
33151
|
for (const root of helperWorktreeRoots(primary)) {
|
|
32961
|
-
if (!(0,
|
|
33152
|
+
if (!(0, import_node_fs43.existsSync)(root)) continue;
|
|
32962
33153
|
for (const dir of defaultOrphanDirScanDeps.listDirs(root)) {
|
|
32963
33154
|
const inspected = inspectSiblingWorktreeDir(dir, gitRoot);
|
|
32964
33155
|
const classified = classifySiblingWorktreeDir(inspected);
|
|
@@ -32998,7 +33189,7 @@ ${err.stderr ?? ""}`;
|
|
|
32998
33189
|
}
|
|
32999
33190
|
|
|
33000
33191
|
// src/issue-commands.ts
|
|
33001
|
-
var
|
|
33192
|
+
var import_node_fs44 = require("node:fs");
|
|
33002
33193
|
var import_node_crypto9 = require("node:crypto");
|
|
33003
33194
|
var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
|
|
33004
33195
|
var ReparentConflictError = class extends Error {
|
|
@@ -33016,7 +33207,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
33016
33207
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
33017
33208
|
const patch = {};
|
|
33018
33209
|
let bodyChanged = false;
|
|
33019
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
33210
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs44.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
33020
33211
|
if (options.titleFile !== void 0) {
|
|
33021
33212
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
33022
33213
|
} else if (options.title !== void 0) {
|
|
@@ -33621,7 +33812,7 @@ function extendCreateCommand(issue2, batchAttach) {
|
|
|
33621
33812
|
if (opts.batch) {
|
|
33622
33813
|
let specs;
|
|
33623
33814
|
try {
|
|
33624
|
-
const raw = (0,
|
|
33815
|
+
const raw = (0, import_node_fs44.readFileSync)(opts.batch, "utf8");
|
|
33625
33816
|
specs = JSON.parse(raw);
|
|
33626
33817
|
if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
|
|
33627
33818
|
} catch (e) {
|
|
@@ -33696,8 +33887,8 @@ ${lines}`, {
|
|
|
33696
33887
|
}
|
|
33697
33888
|
|
|
33698
33889
|
// src/train-commands.ts
|
|
33699
|
-
var
|
|
33700
|
-
var
|
|
33890
|
+
var import_node_fs45 = require("node:fs");
|
|
33891
|
+
var import_node_path43 = require("node:path");
|
|
33701
33892
|
var RELEASE_BUMP_INTENTS = ["major", "minor", "patch"];
|
|
33702
33893
|
function resolveReleaseBumpIntent(raw) {
|
|
33703
33894
|
const intent = typeof raw === "string" ? raw.trim() : "";
|
|
@@ -33708,7 +33899,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
33708
33899
|
}
|
|
33709
33900
|
function readRepoVersion() {
|
|
33710
33901
|
try {
|
|
33711
|
-
return JSON.parse((0,
|
|
33902
|
+
return JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path43.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
33712
33903
|
} catch {
|
|
33713
33904
|
return void 0;
|
|
33714
33905
|
}
|
|
@@ -33865,9 +34056,9 @@ function registerDeployCommands(program3) {
|
|
|
33865
34056
|
}
|
|
33866
34057
|
|
|
33867
34058
|
// src/discovery-commands.ts
|
|
33868
|
-
var
|
|
33869
|
-
var
|
|
33870
|
-
var
|
|
34059
|
+
var import_node_fs46 = require("node:fs");
|
|
34060
|
+
var import_node_os19 = require("node:os");
|
|
34061
|
+
var import_node_path44 = require("node:path");
|
|
33871
34062
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
33872
34063
|
async function collectStatus() {
|
|
33873
34064
|
const repo = await resolveRepo();
|
|
@@ -34051,10 +34242,10 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
34051
34242
|
else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
|
|
34052
34243
|
else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
|
|
34053
34244
|
}
|
|
34054
|
-
const home = (0,
|
|
34245
|
+
const home = (0, import_node_os19.homedir)();
|
|
34055
34246
|
const plugin = onboardPluginGate({
|
|
34056
|
-
readKnown: () => readFileSyncSafe((0,
|
|
34057
|
-
readSettings: () => readFileSyncSafe((0,
|
|
34247
|
+
readKnown: () => readFileSyncSafe((0, import_node_path44.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs46.readFileSync),
|
|
34248
|
+
readSettings: () => readFileSyncSafe((0, import_node_path44.join)(home, ".claude", "settings.json"), import_node_fs46.readFileSync)
|
|
34058
34249
|
});
|
|
34059
34250
|
return { track, board, registry: registry2, secrets, plugin, estateCli, doors: opts.doors ?? [], nextCommand };
|
|
34060
34251
|
}
|
|
@@ -37835,17 +38026,17 @@ function parseOriginRepo(remoteUrl) {
|
|
|
37835
38026
|
}
|
|
37836
38027
|
function ghHostsConfigPath(env, platform2) {
|
|
37837
38028
|
const sep3 = platform2 === "win32" ? "\\" : "/";
|
|
37838
|
-
const
|
|
38029
|
+
const join41 = (...parts) => parts.join(sep3);
|
|
37839
38030
|
const explicit = env.GH_CONFIG_DIR?.trim();
|
|
37840
|
-
if (explicit) return
|
|
38031
|
+
if (explicit) return join41(explicit, "hosts.yml");
|
|
37841
38032
|
if (platform2 === "win32") {
|
|
37842
38033
|
const appData = (env.AppData ?? env.APPDATA)?.trim();
|
|
37843
|
-
return appData ?
|
|
38034
|
+
return appData ? join41(appData, "GitHub CLI", "hosts.yml") : void 0;
|
|
37844
38035
|
}
|
|
37845
38036
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
37846
|
-
if (xdg) return
|
|
38037
|
+
if (xdg) return join41(xdg, "gh", "hosts.yml");
|
|
37847
38038
|
const home = env.HOME?.trim();
|
|
37848
|
-
return home ?
|
|
38039
|
+
return home ? join41(home, ".config", "gh", "hosts.yml") : void 0;
|
|
37849
38040
|
}
|
|
37850
38041
|
function parseGhHostsAccounts(yaml, host = "github.com") {
|
|
37851
38042
|
let hostIndent = null;
|
|
@@ -37895,9 +38086,9 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
37895
38086
|
}
|
|
37896
38087
|
|
|
37897
38088
|
// src/doctor-io.ts
|
|
37898
|
-
var
|
|
37899
|
-
var
|
|
37900
|
-
var
|
|
38089
|
+
var import_node_fs47 = require("node:fs");
|
|
38090
|
+
var import_node_os20 = require("node:os");
|
|
38091
|
+
var import_node_path45 = require("node:path");
|
|
37901
38092
|
var import_node_child_process18 = require("node:child_process");
|
|
37902
38093
|
var import_node_util8 = require("node:util");
|
|
37903
38094
|
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process18.execFile);
|
|
@@ -37905,7 +38096,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
|
37905
38096
|
function installedClaudePluginVersion() {
|
|
37906
38097
|
try {
|
|
37907
38098
|
const file = JSON.parse(
|
|
37908
|
-
(0,
|
|
38099
|
+
(0, import_node_fs47.readFileSync)((0, import_node_path45.join)((0, import_node_os20.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
37909
38100
|
);
|
|
37910
38101
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
37911
38102
|
if (versions.length === 0) return void 0;
|
|
@@ -37916,7 +38107,7 @@ function installedClaudePluginVersion() {
|
|
|
37916
38107
|
}
|
|
37917
38108
|
function manifestVersion(path2) {
|
|
37918
38109
|
try {
|
|
37919
|
-
const manifest = JSON.parse((0,
|
|
38110
|
+
const manifest = JSON.parse((0, import_node_fs47.readFileSync)(path2, "utf8"));
|
|
37920
38111
|
return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
|
|
37921
38112
|
} catch {
|
|
37922
38113
|
return void 0;
|
|
@@ -37926,20 +38117,20 @@ function installedSurfacePluginVersion(surface) {
|
|
|
37926
38117
|
const token = surfaceToken(surface);
|
|
37927
38118
|
if (token === "kilo") {
|
|
37928
38119
|
try {
|
|
37929
|
-
const stamp = (0,
|
|
38120
|
+
const stamp = (0, import_node_fs47.readFileSync)((0, import_node_path45.join)((0, import_node_os20.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
|
|
37930
38121
|
return stamp || void 0;
|
|
37931
38122
|
} catch {
|
|
37932
38123
|
return void 0;
|
|
37933
38124
|
}
|
|
37934
38125
|
}
|
|
37935
38126
|
if (token === "cursor") {
|
|
37936
|
-
return manifestVersion((0,
|
|
38127
|
+
return manifestVersion((0, import_node_path45.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
37937
38128
|
}
|
|
37938
38129
|
if (token === "jervcode") {
|
|
37939
38130
|
return installedJervCodePackageVersion();
|
|
37940
38131
|
}
|
|
37941
38132
|
if (token === "kimi") {
|
|
37942
|
-
return manifestVersion((0,
|
|
38133
|
+
return manifestVersion((0, import_node_path45.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
|
|
37943
38134
|
}
|
|
37944
38135
|
if (token === "claude") return installedClaudePluginVersion();
|
|
37945
38136
|
if (token !== "codex") return void 0;
|
|
@@ -37977,13 +38168,13 @@ function worktreeRootSync() {
|
|
|
37977
38168
|
}
|
|
37978
38169
|
var gitignorePath = () => {
|
|
37979
38170
|
const root = worktreeRootSync();
|
|
37980
|
-
return root === null ? null : (0,
|
|
38171
|
+
return root === null ? null : (0, import_node_path45.join)(root, ".gitignore");
|
|
37981
38172
|
};
|
|
37982
38173
|
function readGitignore() {
|
|
37983
38174
|
const path2 = gitignorePath();
|
|
37984
38175
|
if (path2 === null) return null;
|
|
37985
38176
|
try {
|
|
37986
|
-
return (0,
|
|
38177
|
+
return (0, import_node_fs47.readFileSync)(path2, "utf8");
|
|
37987
38178
|
} catch {
|
|
37988
38179
|
return null;
|
|
37989
38180
|
}
|
|
@@ -37992,7 +38183,7 @@ function writeGitignore(content) {
|
|
|
37992
38183
|
const path2 = gitignorePath();
|
|
37993
38184
|
if (path2 === null) return false;
|
|
37994
38185
|
try {
|
|
37995
|
-
(0,
|
|
38186
|
+
(0, import_node_fs47.writeFileSync)(path2, content, "utf8");
|
|
37996
38187
|
return true;
|
|
37997
38188
|
} catch {
|
|
37998
38189
|
return false;
|
|
@@ -38011,7 +38202,7 @@ async function repoRoot() {
|
|
|
38011
38202
|
}
|
|
38012
38203
|
function hasRepoLocalWorktrees() {
|
|
38013
38204
|
const root = worktreeRootSync();
|
|
38014
|
-
return root !== null && ((0,
|
|
38205
|
+
return root !== null && ((0, import_node_fs47.existsSync)((0, import_node_path45.join)(root, ".worktrees")) || (0, import_node_fs47.existsSync)((0, import_node_path45.join)(root, ".claude", "worktrees")));
|
|
38015
38206
|
}
|
|
38016
38207
|
|
|
38017
38208
|
// src/cross-repo-filing-issue.ts
|
|
@@ -38117,8 +38308,8 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
38117
38308
|
function ghMultiAccountCaveat(announcedLogin) {
|
|
38118
38309
|
try {
|
|
38119
38310
|
const hostsPath = ghHostsConfigPath(process.env, process.platform);
|
|
38120
|
-
if (!hostsPath || !(0,
|
|
38121
|
-
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0,
|
|
38311
|
+
if (!hostsPath || !(0, import_node_fs48.existsSync)(hostsPath)) return void 0;
|
|
38312
|
+
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs48.readFileSync)(hostsPath, "utf8")));
|
|
38122
38313
|
} catch {
|
|
38123
38314
|
return void 0;
|
|
38124
38315
|
}
|
|
@@ -38126,12 +38317,12 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
38126
38317
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
38127
38318
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
38128
38319
|
function envHealLockPath(home) {
|
|
38129
|
-
return (0,
|
|
38320
|
+
return (0, import_node_path46.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
38130
38321
|
}
|
|
38131
38322
|
async function withEnvHealLock(what, run) {
|
|
38132
38323
|
try {
|
|
38133
38324
|
return await withFileLock(
|
|
38134
|
-
envHealLockPath((0,
|
|
38325
|
+
envHealLockPath((0, import_node_os21.homedir)()),
|
|
38135
38326
|
{ staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
|
|
38136
38327
|
run
|
|
38137
38328
|
);
|
|
@@ -38226,7 +38417,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
38226
38417
|
const configRoot = surfaceConfigRoot(surface);
|
|
38227
38418
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
38228
38419
|
const plan = buildPluginCachePlan(
|
|
38229
|
-
(0,
|
|
38420
|
+
(0, import_node_os21.homedir)(),
|
|
38230
38421
|
running,
|
|
38231
38422
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
38232
38423
|
{ configRoot, includeStaging: surface !== "codex" }
|
|
@@ -38250,14 +38441,14 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
38250
38441
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
38251
38442
|
const installed = installedActivePluginVersion(surface);
|
|
38252
38443
|
const plan = buildPluginCachePlan(
|
|
38253
|
-
(0,
|
|
38444
|
+
(0, import_node_os21.homedir)(),
|
|
38254
38445
|
running,
|
|
38255
38446
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
38256
38447
|
{ configRoot, includeStaging: surface !== "codex", installedVersion: installed }
|
|
38257
38448
|
);
|
|
38258
38449
|
const result = applyPluginCachePlan(
|
|
38259
38450
|
plan,
|
|
38260
|
-
(p) => (0,
|
|
38451
|
+
(p) => (0, import_node_fs48.rmSync)(p, { recursive: true }),
|
|
38261
38452
|
stagingApplyFsGuard(configRoot)
|
|
38262
38453
|
);
|
|
38263
38454
|
return {
|
|
@@ -38295,7 +38486,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
38295
38486
|
// adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
|
|
38296
38487
|
// get a permanent — demanding an artifact it never asked for.
|
|
38297
38488
|
docsIndexState: (root) => {
|
|
38298
|
-
if (!(0,
|
|
38489
|
+
if (!(0, import_node_fs48.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
38299
38490
|
const real = createDocsIndexDeps(root);
|
|
38300
38491
|
let docs2;
|
|
38301
38492
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -38304,7 +38495,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
38304
38495
|
},
|
|
38305
38496
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
38306
38497
|
healDocsIndex: (root) => {
|
|
38307
|
-
if (!(0,
|
|
38498
|
+
if (!(0, import_node_fs48.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
38308
38499
|
const real = createDocsIndexDeps(root);
|
|
38309
38500
|
let docs2;
|
|
38310
38501
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -38670,19 +38861,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
38670
38861
|
});
|
|
38671
38862
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
38672
38863
|
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) => {
|
|
38673
|
-
const path2 = (0,
|
|
38674
|
-
const current = (0,
|
|
38864
|
+
const path2 = (0, import_node_path46.join)(process.cwd(), ".gitignore");
|
|
38865
|
+
const current = (0, import_node_fs48.existsSync)(path2) ? (0, import_node_fs48.readFileSync)(path2, "utf8") : null;
|
|
38675
38866
|
const plan = planManagedGitignore(current);
|
|
38676
38867
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
38677
38868
|
if (opts.json) {
|
|
38678
|
-
if (opts.write && plan.changed) (0,
|
|
38869
|
+
if (opts.write && plan.changed) (0, import_node_fs48.writeFileSync)(path2, plan.content, "utf8");
|
|
38679
38870
|
console.log(JSON.stringify(plan, null, 2));
|
|
38680
38871
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
38681
38872
|
return;
|
|
38682
38873
|
}
|
|
38683
38874
|
if (opts.write) {
|
|
38684
38875
|
if (plan.changed) {
|
|
38685
|
-
(0,
|
|
38876
|
+
(0, import_node_fs48.writeFileSync)(path2, plan.content, "utf8");
|
|
38686
38877
|
console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
|
|
38687
38878
|
} else {
|
|
38688
38879
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
@@ -38841,8 +39032,8 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
|
|
|
38841
39032
|
if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
|
|
38842
39033
|
let root;
|
|
38843
39034
|
if (o.root !== void 0) {
|
|
38844
|
-
root = (0,
|
|
38845
|
-
if (!(0,
|
|
39035
|
+
root = (0, import_node_path46.resolve)(o.root);
|
|
39036
|
+
if (!(0, import_node_fs48.existsSync)(root) || !(0, import_node_fs48.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
|
|
38846
39037
|
const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
38847
39038
|
if (isPathUnderDirectory2(gcRepoRoot, root)) {
|
|
38848
39039
|
return fail(`worktree gc: --root ${root} contains this checkout \u2014 name a worktrees root, not the repo or an ancestor of it`);
|
|
@@ -38941,7 +39132,7 @@ async function currentWorktreeRemovalContext(command, force) {
|
|
|
38941
39132
|
};
|
|
38942
39133
|
}
|
|
38943
39134
|
async function unprovenWorktreeReason(wtPath, repoRoot2) {
|
|
38944
|
-
if (!(0,
|
|
39135
|
+
if (!(0, import_node_fs48.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
|
|
38945
39136
|
const porcelain = (await execFileP2("git", ["-C", repoRoot2, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
38946
39137
|
const registered = parseWorktreePorcelainEntries(porcelain);
|
|
38947
39138
|
if (!registered.length) {
|
|
@@ -38971,26 +39162,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
38971
39162
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
38972
39163
|
const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
|
|
38973
39164
|
const take = () => {
|
|
38974
|
-
const fd = (0,
|
|
39165
|
+
const fd = (0, import_node_fs48.openSync)(lockPath, "wx");
|
|
38975
39166
|
try {
|
|
38976
|
-
(0,
|
|
39167
|
+
(0, import_node_fs48.writeSync)(fd, String(Date.now()));
|
|
38977
39168
|
} finally {
|
|
38978
|
-
(0,
|
|
39169
|
+
(0, import_node_fs48.closeSync)(fd);
|
|
38979
39170
|
}
|
|
38980
39171
|
return () => {
|
|
38981
39172
|
try {
|
|
38982
|
-
(0,
|
|
39173
|
+
(0, import_node_fs48.rmSync)(lockPath, { force: true });
|
|
38983
39174
|
} catch {
|
|
38984
39175
|
}
|
|
38985
39176
|
};
|
|
38986
39177
|
};
|
|
38987
39178
|
try {
|
|
38988
|
-
(0,
|
|
39179
|
+
(0, import_node_fs48.mkdirSync)((0, import_node_path46.dirname)(lockPath), { recursive: true });
|
|
38989
39180
|
return take();
|
|
38990
39181
|
} catch {
|
|
38991
39182
|
try {
|
|
38992
|
-
if (Date.now() - (0,
|
|
38993
|
-
(0,
|
|
39183
|
+
if (Date.now() - (0, import_node_fs48.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
|
|
39184
|
+
(0, import_node_fs48.rmSync)(lockPath, { force: true });
|
|
38994
39185
|
return take();
|
|
38995
39186
|
}
|
|
38996
39187
|
} catch {
|
|
@@ -39070,8 +39261,10 @@ withExamples(mutating(
|
|
|
39070
39261
|
}
|
|
39071
39262
|
};
|
|
39072
39263
|
if (preferRemote && await revParseRef(preferRemote)) base = preferRemote;
|
|
39264
|
+
const resolvedBaseOid = await revParseRef(base);
|
|
39265
|
+
if (!resolvedBaseOid) return fail(`worktree create: could not resolve creation base '${base}' to an immutable commit`);
|
|
39073
39266
|
if (!o.json) {
|
|
39074
|
-
const baseSha =
|
|
39267
|
+
const baseSha = resolvedBaseOid.slice(0, 12);
|
|
39075
39268
|
const localOnly = preferRemote && base !== preferRemote ? ` (no ${preferRemote} ? local ref)` : "";
|
|
39076
39269
|
console.error(` base ${base} ${baseSha}${localOnly}`);
|
|
39077
39270
|
}
|
|
@@ -39090,23 +39283,45 @@ withExamples(mutating(
|
|
|
39090
39283
|
const status = (await execFileP2("git", ["-C", wtPath, "status", "--porcelain"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
|
|
39091
39284
|
if (status) return fail(`worktree create: refusing to resume ${wtPath} \u2014 it has uncommitted changes`);
|
|
39092
39285
|
const head = await revParseRef(`refs/heads/${branch}`);
|
|
39093
|
-
const baseOid =
|
|
39094
|
-
if (!head
|
|
39286
|
+
const baseOid = resolvedBaseOid;
|
|
39287
|
+
if (!head) return fail(`worktree create: could not prove '${branch}' and '${base}' before resume`);
|
|
39095
39288
|
const canFastForward = await execFileP2(
|
|
39096
39289
|
"git",
|
|
39097
39290
|
["-C", repoRoot2, "merge-base", "--is-ancestor", head, baseOid],
|
|
39098
39291
|
{ timeout: GIT_TIMEOUT_MS }
|
|
39099
39292
|
// io-census-allow: an unprovable ancestor probe conservatively refuses the resume below rather than fast-forwarding onto unproven history
|
|
39100
39293
|
).then(() => true).catch(() => false);
|
|
39101
|
-
if (
|
|
39102
|
-
|
|
39294
|
+
if (canFastForward) {
|
|
39295
|
+
await execFileP2("git", ["-C", wtPath, "merge", "--ff-only", base], { timeout: GIT_TIMEOUT_MS });
|
|
39296
|
+
resumed = true;
|
|
39297
|
+
} else {
|
|
39298
|
+
const owner2 = lookupWorktreeOwner(repoRoot2, wtPath);
|
|
39299
|
+
const delivered = await proveExactTreeDelivery({
|
|
39300
|
+
repoRoot: repoRoot2,
|
|
39301
|
+
branch,
|
|
39302
|
+
workerOid: head,
|
|
39303
|
+
creationBaseOid: owner2?.provenance?.creationBaseOid,
|
|
39304
|
+
recordedWorkerBranch: owner2?.provenance?.workerBranch,
|
|
39305
|
+
landedTips: [baseOid]
|
|
39306
|
+
});
|
|
39307
|
+
if (delivered.action !== "settle") {
|
|
39308
|
+
return fail(`worktree create: refusing to resume '${branch}' \u2014 it has local commits or diverges from ${base}; exact-tree delivery proof refused (${describeExactTreeRefusal(delivered)})`);
|
|
39309
|
+
}
|
|
39310
|
+
const repoint = await repointDeliveredWorktreeTransactionally({
|
|
39311
|
+
branch,
|
|
39312
|
+
expectedWorkerOid: head,
|
|
39313
|
+
landedTipOid: baseOid,
|
|
39314
|
+
git: async (args) => (await execFileP2("git", ["-C", wtPath, ...args], { timeout: GIT_TIMEOUT_MS })).stdout
|
|
39315
|
+
});
|
|
39316
|
+
if (repoint.action !== "repointed") {
|
|
39317
|
+
return fail(`worktree create: exact-tree delivery was proven by ${delivered.candidateOid}, but transactional resume refused (${repoint.reason}${repoint.detail ? `: ${repoint.detail}` : ""})`);
|
|
39318
|
+
}
|
|
39319
|
+
resumed = true;
|
|
39103
39320
|
}
|
|
39104
|
-
await execFileP2("git", ["-C", wtPath, "merge", "--ff-only", base], { timeout: GIT_TIMEOUT_MS });
|
|
39105
|
-
resumed = true;
|
|
39106
39321
|
}
|
|
39107
39322
|
if (!resumed) {
|
|
39108
39323
|
step = `git worktree add ${wtPath}`;
|
|
39109
|
-
const wtPathPreExisted = (0,
|
|
39324
|
+
const wtPathPreExisted = (0, import_node_fs48.existsSync)(wtPath);
|
|
39110
39325
|
const partialRemove = worktreeRemoveDeps(async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
39111
39326
|
await withWorktreeAddLock(repoRoot2, () => addWorktreeRobust(wtPath, branch, base, {
|
|
39112
39327
|
// #4834: `-c core.longpaths=true` rides the add command itself — a Windows worktree path
|
|
@@ -39122,7 +39337,7 @@ withExamples(mutating(
|
|
|
39122
39337
|
},
|
|
39123
39338
|
deleteBranch: (b) => execFileP2("git", ["branch", "-D", b], { timeout: GIT_TIMEOUT_MS }).then(() => void 0),
|
|
39124
39339
|
cleanupPartial: async () => {
|
|
39125
|
-
if (wtPathPreExisted || !(0,
|
|
39340
|
+
if (wtPathPreExisted || !(0, import_node_fs48.existsSync)(wtPath)) return;
|
|
39126
39341
|
partialRemove.detachReparsePoints(wtPath);
|
|
39127
39342
|
await execFileP2("git", ["worktree", "remove", "--force", wtPath], { timeout: GIT_TIMEOUT_MS }).catch(() => partialRemove.removeWorktreeDir(wtPath).then(() => void 0));
|
|
39128
39343
|
await execFileP2("git", ["worktree", "prune"], { timeout: GIT_TIMEOUT_MS }).catch(() => {
|
|
@@ -39166,7 +39381,14 @@ withExamples(mutating(
|
|
|
39166
39381
|
}
|
|
39167
39382
|
const createActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
|
|
39168
39383
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
39169
|
-
const owner = {
|
|
39384
|
+
const owner = {
|
|
39385
|
+
path: wtPath,
|
|
39386
|
+
branch,
|
|
39387
|
+
provenance: { creationBaseOid: resolvedBaseOid, workerBranch: branch },
|
|
39388
|
+
createdAt,
|
|
39389
|
+
lastSeenAt: createdAt,
|
|
39390
|
+
actor: createActor
|
|
39391
|
+
};
|
|
39170
39392
|
recordWorktreeOwner(repoRoot2, owner);
|
|
39171
39393
|
appendWorktreeEvent(repoRoot2, { action: "created", command: "worktree create", target: wtPath, branch, actor: owner.actor });
|
|
39172
39394
|
let lease;
|
|
@@ -39903,7 +40125,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
39903
40125
|
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`);
|
|
39904
40126
|
if (o.secretsFile) {
|
|
39905
40127
|
try {
|
|
39906
|
-
vars.push(`secrets=${(0,
|
|
40128
|
+
vars.push(`secrets=${(0, import_node_fs48.readFileSync)(o.secretsFile, "utf8")}`);
|
|
39907
40129
|
} catch (e) {
|
|
39908
40130
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
39909
40131
|
}
|
|
@@ -40671,11 +40893,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
40671
40893
|
}
|
|
40672
40894
|
});
|
|
40673
40895
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
40674
|
-
const wfDir = (0,
|
|
40675
|
-
if (!(0,
|
|
40676
|
-
return (0,
|
|
40896
|
+
const wfDir = (0, import_node_path46.join)(cwd, ".github", "workflows");
|
|
40897
|
+
if (!(0, import_node_fs48.existsSync)(wfDir)) return [];
|
|
40898
|
+
return (0, import_node_fs48.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
40677
40899
|
try {
|
|
40678
|
-
return workflowReportsPrChecks((0,
|
|
40900
|
+
return workflowReportsPrChecks((0, import_node_fs48.readFileSync)((0, import_node_path46.join)(wfDir, name), "utf8"));
|
|
40679
40901
|
} catch {
|
|
40680
40902
|
return true;
|
|
40681
40903
|
}
|
|
@@ -40727,16 +40949,16 @@ function ciAuditDeps() {
|
|
|
40727
40949
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
40728
40950
|
readSeedFile: (path2) => {
|
|
40729
40951
|
if (!root) return null;
|
|
40730
|
-
const fullPath = (0,
|
|
40731
|
-
return (0,
|
|
40952
|
+
const fullPath = (0, import_node_path46.join)(root, path2);
|
|
40953
|
+
return (0, import_node_fs48.existsSync)(fullPath) ? (0, import_node_fs48.readFileSync)(fullPath, "utf8") : null;
|
|
40732
40954
|
}
|
|
40733
40955
|
};
|
|
40734
40956
|
}
|
|
40735
40957
|
function hubRoot() {
|
|
40736
|
-
const fromPkg = (0,
|
|
40958
|
+
const fromPkg = (0, import_node_path46.join)(__dirname, "..", "..");
|
|
40737
40959
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
40738
|
-
if ((0,
|
|
40739
|
-
if ((0,
|
|
40960
|
+
if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(fromPkg, marker))) return fromPkg;
|
|
40961
|
+
if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(process.cwd(), marker))) return process.cwd();
|
|
40740
40962
|
return null;
|
|
40741
40963
|
}
|
|
40742
40964
|
async function waitLoopCorePool(label) {
|
|
@@ -41067,7 +41289,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
41067
41289
|
}
|
|
41068
41290
|
if (!repoForPostCleanup) throw e;
|
|
41069
41291
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
|
|
41070
|
-
const commitMessage = bodyFile ? (0,
|
|
41292
|
+
const commitMessage = bodyFile ? (0, import_node_fs48.readFileSync)(bodyFile, "utf8") : void 0;
|
|
41071
41293
|
await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
|
|
41072
41294
|
body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
|
|
41073
41295
|
timeoutMs: GH_MUTATION_TIMEOUT_MS
|
|
@@ -41163,7 +41385,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
41163
41385
|
localCleanup = await cleanupPrMergeLocalBranch(headRef, {
|
|
41164
41386
|
beforeWorktrees,
|
|
41165
41387
|
startingPath,
|
|
41166
|
-
pathExists: (p) => (0,
|
|
41388
|
+
pathExists: (p) => (0, import_node_fs48.existsSync)(p),
|
|
41167
41389
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
41168
41390
|
teardownWorktreeStage,
|
|
41169
41391
|
deferredStore,
|
|
@@ -41767,12 +41989,12 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
41767
41989
|
targets = resolution.targets;
|
|
41768
41990
|
}
|
|
41769
41991
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
41770
|
-
const fileMatrix = (0,
|
|
41992
|
+
const fileMatrix = (0, import_node_fs48.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs48.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
41771
41993
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
41772
41994
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
41773
|
-
const fileContracts = (0,
|
|
41995
|
+
const fileContracts = (0, import_node_fs48.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs48.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
41774
41996
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
41775
|
-
const sanctioned = (0,
|
|
41997
|
+
const sanctioned = (0, import_node_fs48.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs48.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
41776
41998
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
41777
41999
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
41778
42000
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -41804,16 +42026,16 @@ function directoryBytes(path2) {
|
|
|
41804
42026
|
let total = 0;
|
|
41805
42027
|
let entries;
|
|
41806
42028
|
try {
|
|
41807
|
-
entries = (0,
|
|
42029
|
+
entries = (0, import_node_fs48.readdirSync)(path2, { withFileTypes: true });
|
|
41808
42030
|
} catch {
|
|
41809
42031
|
return 0;
|
|
41810
42032
|
}
|
|
41811
42033
|
for (const entry of entries) {
|
|
41812
|
-
const child2 = (0,
|
|
42034
|
+
const child2 = (0, import_node_path46.join)(path2, entry.name);
|
|
41813
42035
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
41814
42036
|
else {
|
|
41815
42037
|
try {
|
|
41816
|
-
total += (0,
|
|
42038
|
+
total += (0, import_node_fs48.statSync)(child2).size;
|
|
41817
42039
|
} catch {
|
|
41818
42040
|
}
|
|
41819
42041
|
}
|
|
@@ -41821,25 +42043,25 @@ function directoryBytes(path2) {
|
|
|
41821
42043
|
return total;
|
|
41822
42044
|
}
|
|
41823
42045
|
function listDirEntries(dir) {
|
|
41824
|
-
return (0,
|
|
42046
|
+
return (0, import_node_fs48.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
41825
42047
|
}
|
|
41826
42048
|
function readInstalledPluginRefs(configRoot) {
|
|
41827
42049
|
const p = installedPluginsPathForConfig(configRoot);
|
|
41828
|
-
if (!(0,
|
|
42050
|
+
if (!(0, import_node_fs48.existsSync)(p)) return [];
|
|
41829
42051
|
try {
|
|
41830
|
-
return installedPluginPaths((0,
|
|
42052
|
+
return installedPluginPaths((0, import_node_fs48.readFileSync)(p, "utf8"));
|
|
41831
42053
|
} catch {
|
|
41832
42054
|
return null;
|
|
41833
42055
|
}
|
|
41834
42056
|
}
|
|
41835
42057
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
41836
42058
|
return {
|
|
41837
|
-
exists: (p) => (0,
|
|
41838
|
-
listVersionDirs: (root) => (0,
|
|
42059
|
+
exists: (p) => (0, import_node_fs48.existsSync)(p),
|
|
42060
|
+
listVersionDirs: (root) => (0, import_node_fs48.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
41839
42061
|
dirBytes,
|
|
41840
|
-
listStagingDirs: (root) => (0,
|
|
42062
|
+
listStagingDirs: (root) => (0, import_node_fs48.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
41841
42063
|
try {
|
|
41842
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
42064
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path46.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs48.statSync)(p).mtimeMs) };
|
|
41843
42065
|
} catch {
|
|
41844
42066
|
return { name: d.name, mtimeMs: Date.now() };
|
|
41845
42067
|
}
|
|
@@ -41853,10 +42075,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
41853
42075
|
return {
|
|
41854
42076
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
41855
42077
|
mtimeMs: (name) => {
|
|
41856
|
-
const p = (0,
|
|
41857
|
-
if (!(0,
|
|
42078
|
+
const p = (0, import_node_path46.join)(stagingRoot, name);
|
|
42079
|
+
if (!(0, import_node_fs48.existsSync)(p)) return null;
|
|
41858
42080
|
try {
|
|
41859
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
42081
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs48.statSync)(q).mtimeMs);
|
|
41860
42082
|
} catch {
|
|
41861
42083
|
return null;
|
|
41862
42084
|
}
|
|
@@ -41876,13 +42098,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
41876
42098
|
return;
|
|
41877
42099
|
}
|
|
41878
42100
|
const plan = buildPluginCachePlan(
|
|
41879
|
-
(0,
|
|
42101
|
+
(0, import_node_os21.homedir)(),
|
|
41880
42102
|
running,
|
|
41881
42103
|
pluginCacheFsDeps(configRoot, directoryBytes),
|
|
41882
42104
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
41883
42105
|
);
|
|
41884
42106
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
41885
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
42107
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs48.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
41886
42108
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
41887
42109
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
41888
42110
|
else console.log(renderPluginCachePlan(plan, result));
|