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