@mutmutco/cli 4.5.42 → 4.5.43
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 +379 -189
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -5496,8 +5496,8 @@ function resolveVerbAliasShim(argv) {
|
|
|
5496
5496
|
}
|
|
5497
5497
|
|
|
5498
5498
|
// src/command-composition.ts
|
|
5499
|
-
var
|
|
5500
|
-
var
|
|
5499
|
+
var import_node_os23 = require("node:os");
|
|
5500
|
+
var import_node_path52 = require("node:path");
|
|
5501
5501
|
|
|
5502
5502
|
// src/board-read.ts
|
|
5503
5503
|
var import_node_fs13 = require("node:fs");
|
|
@@ -16871,10 +16871,10 @@ var rollout_plan_default = {
|
|
|
16871
16871
|
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)."
|
|
16872
16872
|
},
|
|
16873
16873
|
baseline: {
|
|
16874
|
-
version: "4.5.
|
|
16875
|
-
tag: "v4.5.
|
|
16876
|
-
commit: "
|
|
16877
|
-
npm: "@mutmutco/cli@4.5.
|
|
16874
|
+
version: "4.5.43",
|
|
16875
|
+
tag: "v4.5.43",
|
|
16876
|
+
commit: "daaa2fa607cd",
|
|
16877
|
+
npm: "@mutmutco/cli@4.5.43"
|
|
16878
16878
|
},
|
|
16879
16879
|
exitCriterion: "fleet-n-of-n",
|
|
16880
16880
|
hubOnlyShortcut: "forbidden",
|
|
@@ -16891,14 +16891,14 @@ var rollout_plan_default = {
|
|
|
16891
16891
|
repo: "mutmutco/mmi-hub",
|
|
16892
16892
|
role: "canary",
|
|
16893
16893
|
schedule: "train",
|
|
16894
|
-
v3Target: "v4.5.
|
|
16894
|
+
v3Target: "v4.5.43"
|
|
16895
16895
|
}
|
|
16896
16896
|
],
|
|
16897
16897
|
rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
|
|
16898
16898
|
rollback: {
|
|
16899
16899
|
independent: true,
|
|
16900
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
16901
|
-
v3Target: "v4.5.
|
|
16900
|
+
mechanism: "npm dist-tag latest -> 4.5.43 and redeploy the Hub Lambda from tag v4.5.43 (daaa2fa607cd); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
16901
|
+
v3Target: "v4.5.43 (@mutmutco/cli@4.5.43, tag commit daaa2fa607cd \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
16902
16902
|
}
|
|
16903
16903
|
},
|
|
16904
16904
|
{
|
|
@@ -29577,8 +29577,8 @@ function registerBoardCommands(program3) {
|
|
|
29577
29577
|
|
|
29578
29578
|
// src/bootstrap-commands.ts
|
|
29579
29579
|
var import_node_fs28 = require("node:fs");
|
|
29580
|
-
var
|
|
29581
|
-
var
|
|
29580
|
+
var import_node_os17 = require("node:os");
|
|
29581
|
+
var import_node_path28 = require("node:path");
|
|
29582
29582
|
init_cli_shared();
|
|
29583
29583
|
init_clean_exit();
|
|
29584
29584
|
|
|
@@ -29890,6 +29890,122 @@ async function assignPersistedPortRange(repo, slug, reg, opts) {
|
|
|
29890
29890
|
|
|
29891
29891
|
// src/box-commands.ts
|
|
29892
29892
|
var import_node_fs27 = require("node:fs");
|
|
29893
|
+
var import_node_child_process10 = require("node:child_process");
|
|
29894
|
+
var import_node_os16 = require("node:os");
|
|
29895
|
+
var import_node_path27 = require("node:path");
|
|
29896
|
+
|
|
29897
|
+
// src/box-control.ts
|
|
29898
|
+
var import_node_crypto8 = require("node:crypto");
|
|
29899
|
+
var MANIFEST_PATH = "infra/box-control/manifest.json";
|
|
29900
|
+
var ALLOWED_POST = /* @__PURE__ */ new Set(["systemctl daemon-reload"]);
|
|
29901
|
+
function parseManifest(text) {
|
|
29902
|
+
const raw = JSON.parse(text);
|
|
29903
|
+
if (!Array.isArray(raw.files)) throw new Error('box control manifest: "files" must be an array');
|
|
29904
|
+
const seen = /* @__PURE__ */ new Set();
|
|
29905
|
+
return raw.files.map((f, i) => {
|
|
29906
|
+
const where = `box control manifest: files[${i}]`;
|
|
29907
|
+
if (typeof f.src !== "string" || !f.src || f.src.startsWith("/") || f.src.includes("..")) throw new Error(`${where}: src must be a repo-relative path`);
|
|
29908
|
+
if (f.mode !== "0644" && f.mode !== "0755") throw new Error(`${where} (${f.src}): mode must be "0644" or "0755"`);
|
|
29909
|
+
if (f.post !== void 0 && (typeof f.post !== "string" || !ALLOWED_POST.has(f.post))) {
|
|
29910
|
+
throw new Error(`${where} (${f.src}): post must be one of ${[...ALLOWED_POST].join(", ")}`);
|
|
29911
|
+
}
|
|
29912
|
+
const targets = [];
|
|
29913
|
+
if (Array.isArray(f.targets)) targets.push(...f.targets);
|
|
29914
|
+
if (f.lanes !== void 0) targets.push(...expandLanes(f.lanes, where));
|
|
29915
|
+
if (!targets.length) throw new Error(`${where} (${f.src}): needs targets or lanes`);
|
|
29916
|
+
for (const t of targets) {
|
|
29917
|
+
if (typeof t.box !== "string" || !/^[a-z0-9-]+$/.test(t.box)) throw new Error(`${where} (${f.src}): bad box name`);
|
|
29918
|
+
if (typeof t.dest !== "string" || !/^\/[A-Za-z0-9._\/-]+$/.test(t.dest) || t.dest.includes("..")) {
|
|
29919
|
+
throw new Error(`${where} (${f.src}): dest must be an absolute path of plain characters`);
|
|
29920
|
+
}
|
|
29921
|
+
if (t.dest.startsWith("/opt/mmi-control/secrets") || t.dest.endsWith(".env")) {
|
|
29922
|
+
throw new Error(`${where} (${f.src}): ${t.dest} is vault-owned and cannot be a manifest destination`);
|
|
29923
|
+
}
|
|
29924
|
+
const key = `${t.box}:${t.dest}`;
|
|
29925
|
+
if (seen.has(key)) throw new Error(`${where} (${f.src}): duplicate destination ${key}`);
|
|
29926
|
+
seen.add(key);
|
|
29927
|
+
}
|
|
29928
|
+
return { src: f.src, mode: f.mode, ...f.post ? { post: f.post } : {}, targets };
|
|
29929
|
+
});
|
|
29930
|
+
}
|
|
29931
|
+
function expandLanes(l, where) {
|
|
29932
|
+
if (!l || typeof l.box !== "string" || !Number.isInteger(l.from) || !Number.isInteger(l.to) || l.from < 1 || l.to < l.from || typeof l.dest !== "string" || !l.dest.includes("{N}")) {
|
|
29933
|
+
throw new Error(`${where}: lanes needs box, integer from <= to, and a dest containing {N}`);
|
|
29934
|
+
}
|
|
29935
|
+
const out = [];
|
|
29936
|
+
for (let n = l.from; n <= l.to; n += 1) out.push({ box: l.box, dest: l.dest.replace("{N}", String(n)) });
|
|
29937
|
+
return out;
|
|
29938
|
+
}
|
|
29939
|
+
function filesForBox(files, box) {
|
|
29940
|
+
return files.flatMap((file) => file.targets.filter((t) => t.box === box).map((t) => ({ file, dest: t.dest })));
|
|
29941
|
+
}
|
|
29942
|
+
function md5(content) {
|
|
29943
|
+
return (0, import_node_crypto8.createHash)("md5").update(content).digest("hex");
|
|
29944
|
+
}
|
|
29945
|
+
function planRemoteScript(dests) {
|
|
29946
|
+
const lines2 = ["set -u"];
|
|
29947
|
+
for (const d of dests) {
|
|
29948
|
+
lines2.push(`if [ -f '${d}' ]; then printf '%s %s %s\\n' "$(md5sum < '${d}' | cut -d' ' -f1)" "$(stat -c %a '${d}')" '${d}'; else printf 'MISSING - %s\\n' '${d}'; fi`);
|
|
29949
|
+
}
|
|
29950
|
+
return `${lines2.join("\n")}
|
|
29951
|
+
`;
|
|
29952
|
+
}
|
|
29953
|
+
function parsePlanOutput(output, rows) {
|
|
29954
|
+
const seen = /* @__PURE__ */ new Map();
|
|
29955
|
+
for (const line of output.split("\n")) {
|
|
29956
|
+
const m = /^([0-9a-f]{32}|MISSING) (\d{3,4}|-) (\/\S+)$/.exec(line.trim());
|
|
29957
|
+
if (m) seen.set(m[3], { md5: m[1], mode: m[2] });
|
|
29958
|
+
}
|
|
29959
|
+
return rows.map(({ file, dest, expectedMd5 }) => {
|
|
29960
|
+
const expectedMode = file.mode.replace(/^0/, "");
|
|
29961
|
+
const got = seen.get(dest);
|
|
29962
|
+
const base = { src: file.src, dest, expectedMd5, expectedMode: file.mode };
|
|
29963
|
+
if (!got) return { ...base, status: "unread", installedMd5: null, installedMode: null };
|
|
29964
|
+
if (got.md5 === "MISSING") return { ...base, status: "missing", installedMd5: null, installedMode: null };
|
|
29965
|
+
const status = got.md5 !== expectedMd5 ? "drift" : got.mode !== expectedMode ? "mode-drift" : "in-sync";
|
|
29966
|
+
return { ...base, status, installedMd5: got.md5, installedMode: `0${got.mode}` };
|
|
29967
|
+
});
|
|
29968
|
+
}
|
|
29969
|
+
function selectApplyRows(rows, sel) {
|
|
29970
|
+
const unknown = sel.only.filter((s) => !rows.some((r) => r.file.src === s));
|
|
29971
|
+
if (unknown.length) return `not a manifest src for this box: ${unknown.join(", ")}`;
|
|
29972
|
+
const chosen = rows.filter((r) => sel.only.includes(r.file.src));
|
|
29973
|
+
const strayDest = sel.dest.filter((d) => !chosen.some((r) => r.dest === d));
|
|
29974
|
+
if (strayDest.length) return `--dest is not a destination of the chosen --only source(s): ${strayDest.join(", ")}`;
|
|
29975
|
+
const out = [];
|
|
29976
|
+
for (const src of sel.only) {
|
|
29977
|
+
const targets = chosen.filter((r) => r.file.src === src);
|
|
29978
|
+
if (targets.length === 1 || sel.allTargets) {
|
|
29979
|
+
out.push(...targets);
|
|
29980
|
+
continue;
|
|
29981
|
+
}
|
|
29982
|
+
const named = targets.filter((r) => sel.dest.includes(r.dest));
|
|
29983
|
+
if (!named.length) {
|
|
29984
|
+
return `${src} has ${targets.length} destinations on this box; name each with --dest <path>, or pass --all-targets`;
|
|
29985
|
+
}
|
|
29986
|
+
out.push(...named);
|
|
29987
|
+
}
|
|
29988
|
+
return out;
|
|
29989
|
+
}
|
|
29990
|
+
function backupPath(dest, date) {
|
|
29991
|
+
if (dest.startsWith("/etc/cron.d/")) return `/root/${dest.slice("/etc/cron.d/".length)}.cron.bak-${date}`;
|
|
29992
|
+
return `${dest}.bak-${date}`;
|
|
29993
|
+
}
|
|
29994
|
+
function applyRemoteScript(items, date) {
|
|
29995
|
+
const lines2 = ["set -eu", "tmp=$(mktemp)", `trap 'rm -f "$tmp"' EXIT`];
|
|
29996
|
+
for (const it of items) {
|
|
29997
|
+
const b64 = Buffer.from(it.content).toString("base64");
|
|
29998
|
+
lines2.push(`if [ -f '${it.dest}' ]; then cp -p '${it.dest}' '${backupPath(it.dest, date)}'; fi`);
|
|
29999
|
+
lines2.push(`printf '%s' '${b64}' | base64 -d > "$tmp"`);
|
|
30000
|
+
lines2.push(`install -D -m ${it.mode} "$tmp" '${it.dest}'`);
|
|
30001
|
+
}
|
|
30002
|
+
for (const post of [...new Set(items.map((i) => i.post).filter((p) => !!p))]) lines2.push(post);
|
|
30003
|
+
lines2.push(planRemoteScript(items.map((i) => i.dest)).replace(/^set -u\n/, ""));
|
|
30004
|
+
return `${lines2.join("\n")}
|
|
30005
|
+
`;
|
|
30006
|
+
}
|
|
30007
|
+
|
|
30008
|
+
// src/box-commands.ts
|
|
29893
30009
|
init_clean_exit();
|
|
29894
30010
|
|
|
29895
30011
|
// src/secrets-commands.ts
|
|
@@ -31479,6 +31595,80 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
31479
31595
|
await failGraceful(e.message);
|
|
31480
31596
|
}
|
|
31481
31597
|
});
|
|
31598
|
+
box.command("control <name>").description("box-side control files (#7402): --plan (default, read-only) reports drift from the repo; --apply --only <src> installs with backups").option("--plan", "read-only drift report (the default)").option("--apply", "install the files named by --only on this box, backing up each previous copy").option("--only <src>", "with --apply: a manifest src to install (repeatable; approval is file by file)", (v, acc) => [...acc, v], []).option("--dest <path>", "with --apply: a destination of a multi-target --only source to install (repeatable, #7409)", (v, acc) => [...acc, v], []).option("--all-targets", "with --apply: install a multi-target --only source to every one of its destinations on this box").option("--ref <ref>", "git ref the repo content is read from", "origin/development").option("--json", "machine-readable output").action(async (name, o) => {
|
|
31599
|
+
try {
|
|
31600
|
+
if (o.apply && o.plan) throw new UsageError("box control: --plan and --apply are exclusive");
|
|
31601
|
+
if (o.apply && !o.only.length) throw new UsageError("box control: --apply needs --only <src> for each file to install; approval is file by file");
|
|
31602
|
+
if (!o.apply && (o.only.length || o.dest.length || o.allTargets)) throw new UsageError("box control: --only, --dest and --all-targets apply to --apply");
|
|
31603
|
+
const show = hubGitShow(o.ref);
|
|
31604
|
+
const files = parseManifest(show(MANIFEST_PATH).toString("utf8"));
|
|
31605
|
+
const { boxes, incomplete } = await fetchInventory();
|
|
31606
|
+
const found = lookupBox(name, boxes, incomplete);
|
|
31607
|
+
if (found.kind !== "found") {
|
|
31608
|
+
const why = found.kind === "unknown" ? `the inventory is incomplete (${found.reasons.join("; ")})` : `no box named '${name}'`;
|
|
31609
|
+
throw new UnreadableError(`box control: ${why}`);
|
|
31610
|
+
}
|
|
31611
|
+
const rows = filesForBox(files, found.box.name).map(({ file, dest }) => {
|
|
31612
|
+
const content = show(file.src);
|
|
31613
|
+
return { file, dest, content, expectedMd5: md5(content) };
|
|
31614
|
+
});
|
|
31615
|
+
if (!rows.length) throw new UsageError(`box control: ${MANIFEST_PATH} lists no files for ${found.box.name}`);
|
|
31616
|
+
let probeRows = rows;
|
|
31617
|
+
if (o.apply) {
|
|
31618
|
+
const picked = selectApplyRows(rows, { only: o.only, dest: o.dest, allTargets: !!o.allTargets });
|
|
31619
|
+
if (typeof picked === "string") throw new UsageError(`box control: ${picked}`);
|
|
31620
|
+
probeRows = picked;
|
|
31621
|
+
}
|
|
31622
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "");
|
|
31623
|
+
const script = o.apply ? applyRemoteScript(probeRows.map((r) => ({ dest: r.dest, mode: r.file.mode, content: r.content, post: r.file.post })), date) : planRemoteScript(probeRows.map((r) => r.dest));
|
|
31624
|
+
const output = runOnBox(found.box, script);
|
|
31625
|
+
const results = parsePlanOutput(output, probeRows);
|
|
31626
|
+
const drift = results.filter((r) => r.status !== "in-sync").length;
|
|
31627
|
+
const unread = results.some((r) => r.status === "unread");
|
|
31628
|
+
const payload = { box: found.box.name, ref: o.ref, mode: o.apply ? "apply" : "plan", files: results, drift, ok: drift === 0 };
|
|
31629
|
+
if (o.json) console.log(JSON.stringify(payload, null, 2));
|
|
31630
|
+
else console.log(formatControl(payload.mode, found.box.name, results));
|
|
31631
|
+
process.exitCode = unread ? 2 : drift ? 1 : 0;
|
|
31632
|
+
} catch (e) {
|
|
31633
|
+
if (o.json) console.log(JSON.stringify({ box: name, ref: o.ref, ok: false, error: e.message }, null, 2));
|
|
31634
|
+
else console.error(e.message);
|
|
31635
|
+
process.exitCode = 2;
|
|
31636
|
+
}
|
|
31637
|
+
});
|
|
31638
|
+
}
|
|
31639
|
+
var UsageError = class extends Error {
|
|
31640
|
+
};
|
|
31641
|
+
var UnreadableError = class extends Error {
|
|
31642
|
+
};
|
|
31643
|
+
function hubGitShow(ref) {
|
|
31644
|
+
const git3 = (args) => (0, import_node_child_process10.execFileSync)("git", args, { maxBuffer: 64 * 1024 * 1024, stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
|
|
31645
|
+
try {
|
|
31646
|
+
git3(["cat-file", "-e", `HEAD:${MANIFEST_PATH}`]);
|
|
31647
|
+
} catch {
|
|
31648
|
+
throw new UsageError(`box control: run this from an MMI-Hub checkout (no ${MANIFEST_PATH} here)`);
|
|
31649
|
+
}
|
|
31650
|
+
if (ref.startsWith("origin/")) git3(["fetch", "--quiet", "origin"]);
|
|
31651
|
+
return (path2) => git3(["show", `${ref}:${path2}`]);
|
|
31652
|
+
}
|
|
31653
|
+
function runOnBox(found, script) {
|
|
31654
|
+
const dir = (0, import_node_fs27.mkdtempSync)((0, import_node_path27.join)((0, import_node_os16.tmpdir)(), "mmi-box-control-"));
|
|
31655
|
+
try {
|
|
31656
|
+
const recipe = (0, import_node_path27.join)(dir, "recipe.sh");
|
|
31657
|
+
(0, import_node_fs27.writeFileSync)(recipe, sshRecipeScript(found, "sh -s"), "utf8");
|
|
31658
|
+
const r = (0, import_node_child_process10.spawnSync)("bash", [recipe], { input: script, encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 18e4, windowsHide: true });
|
|
31659
|
+
if (r.status !== 0) {
|
|
31660
|
+
throw new UnreadableError(`box control: the ssh session to ${found.name} failed (exit ${r.status ?? r.signal}): ${(r.stderr || "").trim().split("\n").slice(-3).join(" | ")}`);
|
|
31661
|
+
}
|
|
31662
|
+
return r.stdout;
|
|
31663
|
+
} finally {
|
|
31664
|
+
(0, import_node_fs27.rmSync)(dir, { recursive: true, force: true });
|
|
31665
|
+
}
|
|
31666
|
+
}
|
|
31667
|
+
function formatControl(mode, box, rows) {
|
|
31668
|
+
const lines2 = rows.map((r) => `${r.status.padEnd(10)} ${r.dest} <- ${r.src}`);
|
|
31669
|
+
const bad = rows.filter((r) => r.status !== "in-sync").length;
|
|
31670
|
+
lines2.push(`${box}: ${mode} \u2014 ${rows.length} file(s), ${bad} not in sync`);
|
|
31671
|
+
return lines2.join("\n");
|
|
31482
31672
|
}
|
|
31483
31673
|
|
|
31484
31674
|
// src/bootstrap-box-ports.ts
|
|
@@ -31599,7 +31789,7 @@ function renderDeployPortDoctor(report) {
|
|
|
31599
31789
|
}
|
|
31600
31790
|
|
|
31601
31791
|
// src/bootstrap-drift.ts
|
|
31602
|
-
var
|
|
31792
|
+
var import_node_crypto9 = require("node:crypto");
|
|
31603
31793
|
function byteComparableSeeds(manifest, cls) {
|
|
31604
31794
|
return manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self" && s.classes.includes(cls));
|
|
31605
31795
|
}
|
|
@@ -31619,7 +31809,7 @@ function compareSeedBytes(hubContent, repoContent) {
|
|
|
31619
31809
|
return normalize(hubContent) === normalize(repoContent) ? "match" : "drift";
|
|
31620
31810
|
}
|
|
31621
31811
|
function seedContentHash(content) {
|
|
31622
|
-
return (0,
|
|
31812
|
+
return (0, import_node_crypto9.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
|
|
31623
31813
|
}
|
|
31624
31814
|
function auditRepoSeedDrift(repo, seeds, hubContents, repoReads) {
|
|
31625
31815
|
const byTarget = new Map(repoReads.map((r) => [r.target, r.content]));
|
|
@@ -31781,7 +31971,7 @@ function renderPropagationReport(plan) {
|
|
|
31781
31971
|
}
|
|
31782
31972
|
|
|
31783
31973
|
// src/bootstrap-propagation-identity.ts
|
|
31784
|
-
var
|
|
31974
|
+
var import_node_crypto10 = require("node:crypto");
|
|
31785
31975
|
var PROPAGATION_BRANCH_PREFIX = "seed-propagate-";
|
|
31786
31976
|
var TARGET_MARKER_NAME = "mmi-bootstrap-propagation-target";
|
|
31787
31977
|
function safeBranchPart(value, maxLength, fallback) {
|
|
@@ -31794,7 +31984,7 @@ function repoSlug2(repo) {
|
|
|
31794
31984
|
function propagationBranch(repo, target) {
|
|
31795
31985
|
const repoPart = safeBranchPart(repoSlug2(repo), 32, "repo");
|
|
31796
31986
|
const targetPart = safeBranchPart(target, 48, "target");
|
|
31797
|
-
const hash = (0,
|
|
31987
|
+
const hash = (0, import_node_crypto10.createHash)("sha256").update(repo.trim().toLowerCase()).update("\0").update(target).digest("hex").slice(0, 12);
|
|
31798
31988
|
return `${PROPAGATION_BRANCH_PREFIX}${repoPart}-${targetPart}-${hash}`;
|
|
31799
31989
|
}
|
|
31800
31990
|
function legacyPropagationBranch(repo) {
|
|
@@ -32950,7 +33140,7 @@ function renderBootstrapVerifyReport(report) {
|
|
|
32950
33140
|
}
|
|
32951
33141
|
|
|
32952
33142
|
// src/bootstrap-seed-delivery.ts
|
|
32953
|
-
var
|
|
33143
|
+
var import_node_crypto11 = require("node:crypto");
|
|
32954
33144
|
init_github_client();
|
|
32955
33145
|
var SHA = /^[a-f0-9]{40}$/;
|
|
32956
33146
|
async function readSeedFile(repo, target, ref, client) {
|
|
@@ -32983,7 +33173,7 @@ async function targetedSeedPlan(repo, slug, target, baseBranch, sourceSha, clien
|
|
|
32983
33173
|
}
|
|
32984
33174
|
const baseSha = base?.object?.sha;
|
|
32985
33175
|
if (!baseSha || !SHA.test(baseSha) || !SHA.test(sourceSha)) throw new Error("bootstrap apply: seed identity is unreadable");
|
|
32986
|
-
const identity = (0,
|
|
33176
|
+
const identity = (0, import_node_crypto11.createHash)("sha256").update(JSON.stringify([repo.toLowerCase(), target, baseSha, sourceSha])).digest("hex").slice(0, 24);
|
|
32987
33177
|
return { ...planSeedDelivery(rules, slug, baseBranch, `bootstrap-seed-${identity}`), baseSha, baseBranch };
|
|
32988
33178
|
}
|
|
32989
33179
|
async function verifyTargetedSeedBranch(repo, plan, target, client, allowMissing = false) {
|
|
@@ -33378,7 +33568,7 @@ function registerBootstrapCommands(program3) {
|
|
|
33378
33568
|
const readFile9 = (p) => (0, import_node_fs28.existsSync)(p) ? (0, import_node_fs28.readFileSync)(p, "utf8") : null;
|
|
33379
33569
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
33380
33570
|
const putSeed = async (target, content, ref, sha) => {
|
|
33381
|
-
const tmp = (0,
|
|
33571
|
+
const tmp = (0, import_node_path28.join)((0, import_node_os17.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
33382
33572
|
(0, import_node_fs28.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
33383
33573
|
try {
|
|
33384
33574
|
await gh2(contentPutInputArgs(repo, target, tmp));
|
|
@@ -34125,7 +34315,7 @@ LIVE apply to ${repo}:
|
|
|
34125
34315
|
} catch {
|
|
34126
34316
|
existingSha = void 0;
|
|
34127
34317
|
}
|
|
34128
|
-
const tmp = (0,
|
|
34318
|
+
const tmp = (0, import_node_path28.join)((0, import_node_os17.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
34129
34319
|
const desiredContent = desiredByRepo.get(rec.repo);
|
|
34130
34320
|
if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
|
|
34131
34321
|
(0, import_node_fs28.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha)), "utf8");
|
|
@@ -34300,7 +34490,7 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
34300
34490
|
} catch {
|
|
34301
34491
|
existingSha = void 0;
|
|
34302
34492
|
}
|
|
34303
|
-
const tmp = (0,
|
|
34493
|
+
const tmp = (0, import_node_path28.join)((0, import_node_os17.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
34304
34494
|
(0, import_node_fs28.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
|
|
34305
34495
|
try {
|
|
34306
34496
|
await gh2(contentPutInputArgs(repo, seed.target, tmp));
|
|
@@ -34586,8 +34776,8 @@ function formatSuiteStatus(report) {
|
|
|
34586
34776
|
|
|
34587
34777
|
// src/suite-train.ts
|
|
34588
34778
|
var import_node_fs29 = require("node:fs");
|
|
34589
|
-
var
|
|
34590
|
-
var
|
|
34779
|
+
var import_node_path29 = require("node:path");
|
|
34780
|
+
var import_node_crypto12 = require("node:crypto");
|
|
34591
34781
|
var SCHEMA = "mmi-suite-cut/v1";
|
|
34592
34782
|
var SHA2 = /^[a-f0-9]{40}$/i;
|
|
34593
34783
|
var RC_TAG = /^v\d+\.\d+\.\d+-rc\.\d+$/;
|
|
@@ -34622,8 +34812,8 @@ function readSuiteCut(path2) {
|
|
|
34622
34812
|
return record;
|
|
34623
34813
|
}
|
|
34624
34814
|
function writeSuiteCut(path2, record) {
|
|
34625
|
-
(0, import_node_fs29.mkdirSync)((0,
|
|
34626
|
-
const temp = `${path2}.${(0,
|
|
34815
|
+
(0, import_node_fs29.mkdirSync)((0, import_node_path29.dirname)(path2), { recursive: true });
|
|
34816
|
+
const temp = `${path2}.${(0, import_node_crypto12.randomBytes)(8).toString("hex")}.tmp`;
|
|
34627
34817
|
try {
|
|
34628
34818
|
(0, import_node_fs29.writeFileSync)(temp, JSON.stringify(record, null, 2), { flag: "wx" });
|
|
34629
34819
|
(0, import_node_fs29.renameSync)(temp, path2);
|
|
@@ -34828,7 +35018,7 @@ async function suiteRelease(deps, ownerGo) {
|
|
|
34828
35018
|
|
|
34829
35019
|
// src/suite-train-io.ts
|
|
34830
35020
|
var import_node_fs43 = require("node:fs");
|
|
34831
|
-
var
|
|
35021
|
+
var import_node_path43 = require("node:path");
|
|
34832
35022
|
init_cli_shared();
|
|
34833
35023
|
|
|
34834
35024
|
// src/command-register-train-operations.ts
|
|
@@ -35108,9 +35298,9 @@ A hotfix branch is cut from origin/main and carries ONLY the fixes --from names,
|
|
|
35108
35298
|
async function preflightHotfixBatchPicks(deps, sources, pickTolerated) {
|
|
35109
35299
|
if (sources.length === 0) return { failures: [], skipped: [] };
|
|
35110
35300
|
const { mkdtemp: mkdtemp2, rm: rm2 } = await import("node:fs/promises");
|
|
35111
|
-
const { tmpdir:
|
|
35112
|
-
const { join:
|
|
35113
|
-
const dir = await mkdtemp2(
|
|
35301
|
+
const { tmpdir: tmpdir11 } = await import("node:os");
|
|
35302
|
+
const { join: join45 } = await import("node:path");
|
|
35303
|
+
const dir = await mkdtemp2(join45(tmpdir11(), "mmi-hotfix-preflight-"));
|
|
35114
35304
|
const wt = { run: (file, args) => deps.run(file, file === "git" ? ["-C", dir, ...args] : args) };
|
|
35115
35305
|
const failures = [];
|
|
35116
35306
|
const skipped = [];
|
|
@@ -36309,13 +36499,13 @@ async function findInFlightHotfixVersion(deps, ctx, latestMainTag, workflows = H
|
|
|
36309
36499
|
}
|
|
36310
36500
|
|
|
36311
36501
|
// src/hotfix-coverage.ts
|
|
36312
|
-
var
|
|
36502
|
+
var import_node_child_process11 = require("node:child_process");
|
|
36313
36503
|
var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
|
|
36314
36504
|
var GIT_MAX_BUFFER = 512 * 1024 * 1024;
|
|
36315
36505
|
function checkHotfixCoverage(options = {}) {
|
|
36316
36506
|
const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
|
|
36317
36507
|
const ack = (options.ack ?? []).filter(Boolean);
|
|
36318
|
-
const git3 = options.git ?? ((args, opts) => (0,
|
|
36508
|
+
const git3 = options.git ?? ((args, opts) => (0, import_node_child_process11.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"], maxBuffer: GIT_MAX_BUFFER }));
|
|
36319
36509
|
const revList = (range) => {
|
|
36320
36510
|
const out = git3(["rev-list", "--no-merges", range]).trim();
|
|
36321
36511
|
return out ? out.split("\n") : [];
|
|
@@ -36383,7 +36573,7 @@ function checkHotfixCoverage(options = {}) {
|
|
|
36383
36573
|
}
|
|
36384
36574
|
function checkHotfixCarries(options) {
|
|
36385
36575
|
const { cwd = process.cwd(), branch, baseRef, targets } = options;
|
|
36386
|
-
const git3 = options.git ?? ((args, opts) => (0,
|
|
36576
|
+
const git3 = options.git ?? ((args, opts) => (0, import_node_child_process11.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"], maxBuffer: GIT_MAX_BUFFER }));
|
|
36387
36577
|
const isAncestor = (sha, ref) => {
|
|
36388
36578
|
try {
|
|
36389
36579
|
git3(["merge-base", "--is-ancestor", sha, ref]);
|
|
@@ -36416,8 +36606,8 @@ function checkHotfixCarries(options) {
|
|
|
36416
36606
|
|
|
36417
36607
|
// src/merge-cleanup.ts
|
|
36418
36608
|
var import_node_fs31 = require("node:fs");
|
|
36419
|
-
var
|
|
36420
|
-
var
|
|
36609
|
+
var import_node_path31 = require("node:path");
|
|
36610
|
+
var import_node_os18 = require("node:os");
|
|
36421
36611
|
init_cli_shared();
|
|
36422
36612
|
init_github_client();
|
|
36423
36613
|
|
|
@@ -36706,12 +36896,12 @@ async function runPrLand(prNumber, options, deps) {
|
|
|
36706
36896
|
}
|
|
36707
36897
|
|
|
36708
36898
|
// src/test-policy-core.ts
|
|
36709
|
-
var
|
|
36899
|
+
var import_node_child_process13 = require("node:child_process");
|
|
36710
36900
|
var import_node_fs30 = require("node:fs");
|
|
36711
|
-
var
|
|
36901
|
+
var import_node_path30 = require("node:path");
|
|
36712
36902
|
|
|
36713
36903
|
// src/test-command-policy-shared.mjs
|
|
36714
|
-
var
|
|
36904
|
+
var import_node_child_process12 = require("node:child_process");
|
|
36715
36905
|
var TEST_COMMAND_CLASS = "test";
|
|
36716
36906
|
var TRAILER_KEY = "Test-Policy-Override";
|
|
36717
36907
|
var OVERRIDE_RE = /^Test-Policy-Override:\s*(.+)$/im;
|
|
@@ -36828,7 +37018,7 @@ function evaluateTestCommandPolicy({ paths, mandatory, regulated = true, overrid
|
|
|
36828
37018
|
};
|
|
36829
37019
|
}
|
|
36830
37020
|
function git(args, cwd) {
|
|
36831
|
-
return (0,
|
|
37021
|
+
return (0, import_node_child_process12.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
36832
37022
|
}
|
|
36833
37023
|
function parseScope(value) {
|
|
36834
37024
|
const scoped = /^\[([^\]]*)\]\s*([\s\S]*)$/.exec(value);
|
|
@@ -37184,7 +37374,7 @@ function isMeaningfulRow(file) {
|
|
|
37184
37374
|
return file.meaningful !== false;
|
|
37185
37375
|
}
|
|
37186
37376
|
function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
37187
|
-
const raw = readFile9((0,
|
|
37377
|
+
const raw = readFile9((0, import_node_path30.join)(root, POLICY_FILE));
|
|
37188
37378
|
if (raw == null) return { mandatory: [], declared: false };
|
|
37189
37379
|
return parsePolicy(raw, POLICY_FILE);
|
|
37190
37380
|
}
|
|
@@ -37290,11 +37480,11 @@ function classify(changed, policy, present = () => false) {
|
|
|
37290
37480
|
return { mandatoryHits, untestedHits, testChanges, meaningfulTestChanges, addedTests, removedProtected };
|
|
37291
37481
|
}
|
|
37292
37482
|
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs30.existsSync)(path2)) {
|
|
37293
|
-
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0,
|
|
37483
|
+
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path30.join)(root, p)));
|
|
37294
37484
|
}
|
|
37295
37485
|
function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs30.existsSync)(path2)) {
|
|
37296
37486
|
const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
|
|
37297
|
-
return [...new Set(declared)].filter((p) => !exists((0,
|
|
37487
|
+
return [...new Set(declared)].filter((p) => !exists((0, import_node_path30.join)(root, p)));
|
|
37298
37488
|
}
|
|
37299
37489
|
function evaluate(changed, policy, present = () => false) {
|
|
37300
37490
|
const { mandatoryHits, untestedHits, meaningfulTestChanges, addedTests, removedProtected } = classify(changed, policy, present);
|
|
@@ -37327,14 +37517,14 @@ function evaluate(changed, policy, present = () => false) {
|
|
|
37327
37517
|
return findings;
|
|
37328
37518
|
}
|
|
37329
37519
|
function git2(args, cwd) {
|
|
37330
|
-
return (0,
|
|
37520
|
+
return (0, import_node_child_process13.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
37331
37521
|
}
|
|
37332
37522
|
var COAUTHOR_KEY = "Co-authored-by";
|
|
37333
37523
|
var GH_MESSAGE_SEPARATOR = /^-{5,}$/;
|
|
37334
37524
|
var LIFTED_KEYS = new RegExp(`^(?:${TRAILER_KEY}|${COAUTHOR_KEY}):`, "i");
|
|
37335
37525
|
function parseTrailers(message2, cwd) {
|
|
37336
37526
|
try {
|
|
37337
|
-
return (0,
|
|
37527
|
+
return (0, import_node_child_process13.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
|
|
37338
37528
|
windowsHide: true,
|
|
37339
37529
|
cwd,
|
|
37340
37530
|
input: message2,
|
|
@@ -37440,7 +37630,7 @@ function runTestPolicy(root, deps = {}) {
|
|
|
37440
37630
|
const raw = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
|
|
37441
37631
|
const changed = deps.changed ? raw : annotateChangeMeaning(raw, policy, (path2) => ({
|
|
37442
37632
|
before: blobAt(base, path2, root),
|
|
37443
|
-
after: readFileOrNull2((0,
|
|
37633
|
+
after: readFileOrNull2((0, import_node_path30.join)(root, path2))
|
|
37444
37634
|
}));
|
|
37445
37635
|
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride2(base, root) : { override: null, refusals: [] };
|
|
37446
37636
|
const policyRefusals = policy.declared === false ? [{
|
|
@@ -37450,8 +37640,8 @@ function runTestPolicy(root, deps = {}) {
|
|
|
37450
37640
|
A gate with no policy cannot decide which test commands are required or permitted.
|
|
37451
37641
|
` + (deps.base === HOTFIX_DIFF_BASE ? ` Hotfix lane (--base ${HOTFIX_DIFF_BASE}): pass --policy-ref <exact-${HOTFIX_POLICY_REF}-commit> so the current policy is read from ${HOTFIX_POLICY_REF}.` : ` Restore ${POLICY_FILE} on this branch; the policy file belongs on every non-hotfix lane.`)
|
|
37452
37642
|
}] : [];
|
|
37453
|
-
const present = (path2) => exists((0,
|
|
37454
|
-
const policyTree = policySource.sha ? new Set(git2(["ls-tree", "-r", "-z", "--name-only", policySource.sha], root).split("\0").filter(Boolean).map((p) => (0,
|
|
37643
|
+
const present = (path2) => exists((0, import_node_path30.join)(root, path2));
|
|
37644
|
+
const policyTree = policySource.sha ? new Set(git2(["ls-tree", "-r", "-z", "--name-only", policySource.sha], root).split("\0").filter(Boolean).map((p) => (0, import_node_path30.join)(root, p))) : null;
|
|
37455
37645
|
const policyTreeExists = policyTree ? (abs) => policyTree.has(abs) : exists;
|
|
37456
37646
|
const where = policySource.sha ? `${policySource.ref}@${policySource.sha}` : "this worktree";
|
|
37457
37647
|
const removedByThisDiff = removedPaths(changed);
|
|
@@ -37696,8 +37886,8 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
|
|
|
37696
37886
|
return rewritten === base ? null : rewritten;
|
|
37697
37887
|
}
|
|
37698
37888
|
function writeSquashBodyFile(body) {
|
|
37699
|
-
const dir = (0, import_node_fs31.mkdtempSync)((0,
|
|
37700
|
-
const path2 = (0,
|
|
37889
|
+
const dir = (0, import_node_fs31.mkdtempSync)((0, import_node_path31.join)((0, import_node_os18.tmpdir)(), "mmi-squash-body-"));
|
|
37890
|
+
const path2 = (0, import_node_path31.join)(dir, "body.txt");
|
|
37701
37891
|
(0, import_node_fs31.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
|
|
37702
37892
|
`, "utf8");
|
|
37703
37893
|
return { path: path2, cleanup: () => {
|
|
@@ -37910,7 +38100,7 @@ async function deleteMergedRemoteBranch(options) {
|
|
|
37910
38100
|
|
|
37911
38101
|
// src/project-info-sync.ts
|
|
37912
38102
|
var import_node_fs32 = require("node:fs");
|
|
37913
|
-
var
|
|
38103
|
+
var import_node_path32 = require("node:path");
|
|
37914
38104
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
37915
38105
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
37916
38106
|
projectV2 { id }
|
|
@@ -37957,7 +38147,7 @@ function sharedName(entries, fallback) {
|
|
|
37957
38147
|
}
|
|
37958
38148
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
37959
38149
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
37960
|
-
const readmePath = (0,
|
|
38150
|
+
const readmePath = (0, import_node_path32.join)(repoRoot2, "README.md");
|
|
37961
38151
|
if (!(0, import_node_fs32.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
37962
38152
|
const entries = entriesFor(project2, projects);
|
|
37963
38153
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
@@ -37983,8 +38173,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
37983
38173
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
37984
38174
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
37985
38175
|
const orgDocs = [
|
|
37986
|
-
(0, import_node_fs32.existsSync)((0,
|
|
37987
|
-
(0, import_node_fs32.existsSync)((0,
|
|
38176
|
+
(0, import_node_fs32.existsSync)((0, import_node_path32.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
|
|
38177
|
+
(0, import_node_fs32.existsSync)((0, import_node_path32.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
|
|
37988
38178
|
].filter(Boolean);
|
|
37989
38179
|
if (orgDocs.length) lines2.push("", "## Organisation docs", "", ...orgDocs);
|
|
37990
38180
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines2.join("\n")}
|
|
@@ -38007,7 +38197,7 @@ async function syncProjectInfo(plan, client, apply) {
|
|
|
38007
38197
|
|
|
38008
38198
|
// src/train-commands.ts
|
|
38009
38199
|
var import_node_fs33 = require("node:fs");
|
|
38010
|
-
var
|
|
38200
|
+
var import_node_path33 = require("node:path");
|
|
38011
38201
|
init_cli_shared();
|
|
38012
38202
|
init_clean_exit();
|
|
38013
38203
|
init_client_version();
|
|
@@ -38022,7 +38212,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
38022
38212
|
}
|
|
38023
38213
|
function readRepoVersion() {
|
|
38024
38214
|
try {
|
|
38025
|
-
return JSON.parse((0, import_node_fs33.readFileSync)((0,
|
|
38215
|
+
return JSON.parse((0, import_node_fs33.readFileSync)((0, import_node_path33.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
38026
38216
|
} catch {
|
|
38027
38217
|
return void 0;
|
|
38028
38218
|
}
|
|
@@ -38133,7 +38323,7 @@ function releaseProgressVerdict(status, subject = "Release") {
|
|
|
38133
38323
|
|
|
38134
38324
|
// src/suite-pin.ts
|
|
38135
38325
|
var import_promises5 = require("node:fs/promises");
|
|
38136
|
-
var
|
|
38326
|
+
var import_node_path34 = require("node:path");
|
|
38137
38327
|
async function openSuiteCutPinPr(deps, input) {
|
|
38138
38328
|
if (!/^[a-f0-9]{40}$/.test(input.cut) || !input.pins.length) throw new Error("suite pin PR needs an anchored cut and at least one module");
|
|
38139
38329
|
const ids = input.pins.map((p) => p.module);
|
|
@@ -38169,15 +38359,15 @@ async function openSuiteCutPinPr(deps, input) {
|
|
|
38169
38359
|
const row = rows.find((r) => r.id === p.module);
|
|
38170
38360
|
return row?.bundles.rc === p.rc && row?.bundles.main === p.main;
|
|
38171
38361
|
})) throw new Error("pins already match without a merged cut PR \u2014 provenance unverified");
|
|
38172
|
-
await (0, import_promises5.mkdir)((0,
|
|
38173
|
-
scratch = await (0, import_promises5.mkdtemp)((0,
|
|
38362
|
+
await (0, import_promises5.mkdir)((0, import_node_path34.join)(deps.root, "tmp"), { recursive: true });
|
|
38363
|
+
scratch = await (0, import_promises5.mkdtemp)((0, import_node_path34.join)(deps.root, "tmp", "suite-pin-"));
|
|
38174
38364
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
38175
38365
|
await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
38176
38366
|
if ((await run("git", ["rev-parse", "HEAD"])).trim().toLowerCase() !== input.cut.toLowerCase())
|
|
38177
38367
|
throw new Error("MM-Work development moved after the suite snapshot; no pin PR opened");
|
|
38178
38368
|
await run("git", ["switch", "-c", branch]);
|
|
38179
38369
|
for (const stage of ["rc", "main"]) {
|
|
38180
|
-
const path2 = (0,
|
|
38370
|
+
const path2 = (0, import_node_path34.join)(scratch, `${stage}-pins.json`);
|
|
38181
38371
|
await (0, import_promises5.writeFile)(path2, JSON.stringify(input.pins.map((p) => ({ module: p.module, url: p[stage] }))));
|
|
38182
38372
|
await run("node", ["scripts/pin-modules.mjs", "--stage", stage, "--pins", path2]);
|
|
38183
38373
|
}
|
|
@@ -38223,8 +38413,8 @@ async function openSuitePinPr(deps, input) {
|
|
|
38223
38413
|
url.searchParams.set("v", tag);
|
|
38224
38414
|
const pin = url.toString();
|
|
38225
38415
|
const branch = `suite-pin/${stage}-${member.id}-${tag}`;
|
|
38226
|
-
await (0, import_promises5.mkdir)((0,
|
|
38227
|
-
scratch = await (0, import_promises5.mkdtemp)((0,
|
|
38416
|
+
await (0, import_promises5.mkdir)((0, import_node_path34.join)(deps.root, "tmp"), { recursive: true });
|
|
38417
|
+
scratch = await (0, import_promises5.mkdtemp)((0, import_node_path34.join)(deps.root, "tmp", "suite-pin-"));
|
|
38228
38418
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
38229
38419
|
await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
38230
38420
|
const existing = JSON.parse(await run("gh", ["pr", "list", "--repo", SUITE_REPO, "--head", branch, "--state", "all", "--json", "url,state"]));
|
|
@@ -38267,7 +38457,7 @@ async function openSuitePinPr(deps, input) {
|
|
|
38267
38457
|
}
|
|
38268
38458
|
|
|
38269
38459
|
// src/command-register-collaboration.ts
|
|
38270
|
-
var
|
|
38460
|
+
var import_node_child_process17 = require("node:child_process");
|
|
38271
38461
|
var import_node_fs42 = require("node:fs");
|
|
38272
38462
|
var import_promises7 = require("node:fs/promises");
|
|
38273
38463
|
init_clean_exit();
|
|
@@ -38292,11 +38482,11 @@ function spawnDetachedSelf(args, deps, opts = {}) {
|
|
|
38292
38482
|
}
|
|
38293
38483
|
|
|
38294
38484
|
// src/command-register-collaboration.ts
|
|
38295
|
-
var
|
|
38485
|
+
var import_node_path42 = require("node:path");
|
|
38296
38486
|
init_house_map();
|
|
38297
38487
|
|
|
38298
38488
|
// src/issue-body.ts
|
|
38299
|
-
var
|
|
38489
|
+
var import_node_os19 = require("node:os");
|
|
38300
38490
|
init_error_codes();
|
|
38301
38491
|
var TextArgError = class extends Error {
|
|
38302
38492
|
constructor(message2, code, offendingFlag) {
|
|
@@ -38309,7 +38499,7 @@ var TextArgError = class extends Error {
|
|
|
38309
38499
|
offendingFlag;
|
|
38310
38500
|
};
|
|
38311
38501
|
function emptyStdinMessage(fileFlag) {
|
|
38312
|
-
if ((0,
|
|
38502
|
+
if ((0, import_node_os19.platform)() === "win32") {
|
|
38313
38503
|
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)`;
|
|
38314
38504
|
}
|
|
38315
38505
|
return `${fileFlag} - read empty stdin (nothing piped \u2014 pass a heredoc/pipe, or ${fileFlag} <path>)`;
|
|
@@ -38402,7 +38592,7 @@ init_github_client();
|
|
|
38402
38592
|
|
|
38403
38593
|
// src/issue-commands.ts
|
|
38404
38594
|
var import_node_fs34 = require("node:fs");
|
|
38405
|
-
var
|
|
38595
|
+
var import_node_crypto13 = require("node:crypto");
|
|
38406
38596
|
init_cli_shared();
|
|
38407
38597
|
init_clean_exit();
|
|
38408
38598
|
init_error_codes();
|
|
@@ -38875,7 +39065,7 @@ function rowIdempotencyKey(batchKey, spec) {
|
|
|
38875
39065
|
const identity = `${spec.type}
|
|
38876
39066
|
${spec.title.trim()}
|
|
38877
39067
|
${spec.body ?? ""}`;
|
|
38878
|
-
const hash = (0,
|
|
39068
|
+
const hash = (0, import_node_crypto13.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
|
|
38879
39069
|
return `${batchKey}:${hash}`;
|
|
38880
39070
|
}
|
|
38881
39071
|
var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
|
|
@@ -39335,7 +39525,7 @@ ${lines2}`, {
|
|
|
39335
39525
|
|
|
39336
39526
|
// src/post-merge-recon.ts
|
|
39337
39527
|
var import_node_fs35 = require("node:fs");
|
|
39338
|
-
var
|
|
39528
|
+
var import_node_path35 = require("node:path");
|
|
39339
39529
|
|
|
39340
39530
|
// src/cross-repo-filing-issue.ts
|
|
39341
39531
|
init_github_client();
|
|
@@ -39502,7 +39692,7 @@ function buildPostMergeReconRecovery(input) {
|
|
|
39502
39692
|
}
|
|
39503
39693
|
function writePostMergeReconRecovery(cwd, recovery) {
|
|
39504
39694
|
const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
|
|
39505
|
-
(0, import_node_fs35.mkdirSync)((0,
|
|
39695
|
+
(0, import_node_fs35.mkdirSync)((0, import_node_path35.dirname)(path2), { recursive: true });
|
|
39506
39696
|
(0, import_node_fs35.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
|
|
39507
39697
|
`, "utf8");
|
|
39508
39698
|
return path2;
|
|
@@ -39937,10 +40127,10 @@ function registerPrLifecycleCommands(program3) {
|
|
|
39937
40127
|
}
|
|
39938
40128
|
|
|
39939
40129
|
// src/review-verdict.ts
|
|
39940
|
-
var
|
|
40130
|
+
var import_node_child_process14 = require("node:child_process");
|
|
39941
40131
|
var import_node_fs36 = require("node:fs");
|
|
39942
|
-
var
|
|
39943
|
-
var
|
|
40132
|
+
var import_node_os20 = require("node:os");
|
|
40133
|
+
var import_node_path36 = require("node:path");
|
|
39944
40134
|
init_cli_shared();
|
|
39945
40135
|
var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
|
|
39946
40136
|
var REVIEW_VERDICTS = ["PROCEED", "CORRECT", "ESCALATE"];
|
|
@@ -40037,8 +40227,8 @@ async function checkPrReview(number, repo, head, deps = {
|
|
|
40037
40227
|
}
|
|
40038
40228
|
function computePrPatchId(number, repo) {
|
|
40039
40229
|
return new Promise((resolve7, reject) => {
|
|
40040
|
-
const gh2 = (0,
|
|
40041
|
-
const git3 = (0,
|
|
40230
|
+
const gh2 = (0, import_node_child_process14.spawn)("gh", ["pr", "diff", number, "--repo", repo], { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
40231
|
+
const git3 = (0, import_node_child_process14.spawn)("git", ["patch-id", "--stable"], { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
|
|
40042
40232
|
let out = "";
|
|
40043
40233
|
let ghErr = "";
|
|
40044
40234
|
let gitErr = "";
|
|
@@ -40105,8 +40295,8 @@ async function readPrIssueComments(number, repo) {
|
|
|
40105
40295
|
return comments;
|
|
40106
40296
|
}
|
|
40107
40297
|
async function postPrCommentFromFile(number, repo, body) {
|
|
40108
|
-
const dir = (0, import_node_fs36.mkdtempSync)((0,
|
|
40109
|
-
const path2 = (0,
|
|
40298
|
+
const dir = (0, import_node_fs36.mkdtempSync)((0, import_node_path36.join)((0, import_node_os20.tmpdir)(), "mmi-review-verdict-"));
|
|
40299
|
+
const path2 = (0, import_node_path36.join)(dir, "body.md");
|
|
40110
40300
|
try {
|
|
40111
40301
|
(0, import_node_fs36.writeFileSync)(path2, body, "utf8");
|
|
40112
40302
|
const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
@@ -40120,7 +40310,7 @@ async function postPrCommentFromFile(number, repo, body) {
|
|
|
40120
40310
|
}
|
|
40121
40311
|
|
|
40122
40312
|
// src/pr-create-docs-check.ts
|
|
40123
|
-
var
|
|
40313
|
+
var import_node_child_process15 = require("node:child_process");
|
|
40124
40314
|
init_cli_shared();
|
|
40125
40315
|
var GIT_TIMEOUT_MS2 = 15e3;
|
|
40126
40316
|
function catFileBatch(root, ref, paths) {
|
|
@@ -40128,7 +40318,7 @@ function catFileBatch(root, ref, paths) {
|
|
|
40128
40318
|
return new Promise((resolve7) => {
|
|
40129
40319
|
const chunks = [];
|
|
40130
40320
|
let settled = false;
|
|
40131
|
-
const child2 = (0,
|
|
40321
|
+
const child2 = (0, import_node_child_process15.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
|
|
40132
40322
|
const finish = () => {
|
|
40133
40323
|
if (settled) return;
|
|
40134
40324
|
settled = true;
|
|
@@ -40340,17 +40530,17 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
|
|
|
40340
40530
|
|
|
40341
40531
|
// src/worktree-residue-sweep.ts
|
|
40342
40532
|
var import_node_fs41 = require("node:fs");
|
|
40343
|
-
var
|
|
40533
|
+
var import_node_path41 = require("node:path");
|
|
40344
40534
|
|
|
40345
40535
|
// src/worktree-merge-cleanup.ts
|
|
40346
40536
|
var import_node_fs40 = require("node:fs");
|
|
40347
|
-
var
|
|
40537
|
+
var import_node_path40 = require("node:path");
|
|
40348
40538
|
init_cli_shared();
|
|
40349
40539
|
|
|
40350
40540
|
// src/stage-runner.ts
|
|
40351
|
-
var
|
|
40541
|
+
var import_node_child_process16 = require("node:child_process");
|
|
40352
40542
|
var import_node_fs38 = require("node:fs");
|
|
40353
|
-
var
|
|
40543
|
+
var import_node_path38 = require("node:path");
|
|
40354
40544
|
var import_node_net = require("node:net");
|
|
40355
40545
|
var import_node_util5 = require("node:util");
|
|
40356
40546
|
|
|
@@ -40361,7 +40551,7 @@ function normalizeEol2(s) {
|
|
|
40361
40551
|
|
|
40362
40552
|
// src/env-detection.ts
|
|
40363
40553
|
var import_node_fs37 = require("node:fs");
|
|
40364
|
-
var
|
|
40554
|
+
var import_node_path37 = require("node:path");
|
|
40365
40555
|
var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".jerv", ".pi", ".venv", "venv"]);
|
|
40366
40556
|
function isEnvBaseName(name) {
|
|
40367
40557
|
return name.toLowerCase() === ".env";
|
|
@@ -40379,13 +40569,13 @@ function findEnvFiles(root) {
|
|
|
40379
40569
|
const relPath = rel ? `${rel}/${entry.name}` : entry.name;
|
|
40380
40570
|
if (entry.isDirectory()) {
|
|
40381
40571
|
if (SKIPPED_DIRS.has(entry.name)) continue;
|
|
40382
|
-
walk2((0,
|
|
40572
|
+
walk2((0, import_node_path37.join)(dir, entry.name), relPath);
|
|
40383
40573
|
continue;
|
|
40384
40574
|
}
|
|
40385
40575
|
if (isEnvBaseName(entry.name)) {
|
|
40386
40576
|
let isDir = false;
|
|
40387
40577
|
try {
|
|
40388
|
-
isDir = (0, import_node_fs37.lstatSync)((0,
|
|
40578
|
+
isDir = (0, import_node_fs37.lstatSync)((0, import_node_path37.join)(dir, entry.name)).isDirectory();
|
|
40389
40579
|
} catch {
|
|
40390
40580
|
}
|
|
40391
40581
|
if (!isDir) found.push(relPath);
|
|
@@ -40442,15 +40632,15 @@ function stageComposeFileEnv(files) {
|
|
|
40442
40632
|
}
|
|
40443
40633
|
|
|
40444
40634
|
// src/stage-runner.ts
|
|
40445
|
-
var execFileP3 = (0, import_node_util5.promisify)(
|
|
40635
|
+
var execFileP3 = (0, import_node_util5.promisify)(import_node_child_process16.execFile);
|
|
40446
40636
|
var DOCKER_TIMEOUT_MS = 15e3;
|
|
40447
40637
|
function stageBash() {
|
|
40448
40638
|
if (process.platform !== "win32") return "bash";
|
|
40449
40639
|
const configured = process.env.SHELL;
|
|
40450
|
-
if (configured && (0,
|
|
40640
|
+
if (configured && (0, import_node_path38.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs38.existsSync)(configured)) return configured;
|
|
40451
40641
|
const candidates = [
|
|
40452
|
-
(0,
|
|
40453
|
-
...process.env.LOCALAPPDATA ? [(0,
|
|
40642
|
+
(0, import_node_path38.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
|
|
40643
|
+
...process.env.LOCALAPPDATA ? [(0, import_node_path38.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
|
|
40454
40644
|
];
|
|
40455
40645
|
const bash = candidates.find((path2) => (0, import_node_fs38.existsSync)(path2));
|
|
40456
40646
|
if (!bash) throw new Error("Local stages require Git Bash on Windows. Install Git for Windows or set SHELL to its absolute bash.exe path.");
|
|
@@ -40583,7 +40773,7 @@ function appendForceRecreate(up) {
|
|
|
40583
40773
|
return `${up.trimEnd()} --force-recreate`;
|
|
40584
40774
|
}
|
|
40585
40775
|
function stageStatePath(cwd = process.cwd()) {
|
|
40586
|
-
return (0,
|
|
40776
|
+
return (0, import_node_path38.join)(cwd, "tmp", "stage", "state.json");
|
|
40587
40777
|
}
|
|
40588
40778
|
function normPath(path2) {
|
|
40589
40779
|
return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
@@ -40800,7 +40990,7 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
40800
40990
|
}
|
|
40801
40991
|
function composeResolvesPort(cwd) {
|
|
40802
40992
|
if (process.env.PORT) return true;
|
|
40803
|
-
const envFile = (0,
|
|
40993
|
+
const envFile = (0, import_node_path38.join)(cwd, ".env");
|
|
40804
40994
|
return (0, import_node_fs38.existsSync)(envFile) && envFileKeys((0, import_node_fs38.readFileSync)(envFile, "utf8")).has("PORT");
|
|
40805
40995
|
}
|
|
40806
40996
|
function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
@@ -40814,13 +41004,13 @@ function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
|
40814
41004
|
function stageComposeFiles(cwd) {
|
|
40815
41005
|
const files = ["docker-compose.yml"];
|
|
40816
41006
|
for (const conventional of ["docker-compose.override.yml", "docker-compose.override.yaml"]) {
|
|
40817
|
-
if ((0, import_node_fs38.existsSync)((0,
|
|
41007
|
+
if ((0, import_node_fs38.existsSync)((0, import_node_path38.join)(cwd, conventional))) files.push(conventional);
|
|
40818
41008
|
}
|
|
40819
41009
|
return [...files, RUNTIME_ENV_OVERRIDE_RELPATH];
|
|
40820
41010
|
}
|
|
40821
41011
|
async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
40822
41012
|
const keys = Object.keys(vaultEnvMerge ?? {});
|
|
40823
|
-
if (!keys.length || !(0, import_node_fs38.existsSync)((0,
|
|
41013
|
+
if (!keys.length || !(0, import_node_fs38.existsSync)((0, import_node_path38.join)(cwd, "docker-compose.yml"))) return {};
|
|
40824
41014
|
let configJson;
|
|
40825
41015
|
try {
|
|
40826
41016
|
const { stdout } = await execFileP3("docker", ["compose", "config", "--format", "json"], {
|
|
@@ -40836,15 +41026,15 @@ async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
|
40836
41026
|
}
|
|
40837
41027
|
const body = renderRuntimeEnvOverride(runtimeEnvMarkedServices(configJson), keys);
|
|
40838
41028
|
if (!body) return {};
|
|
40839
|
-
const overridePath = (0,
|
|
40840
|
-
(0, import_node_fs38.mkdirSync)((0,
|
|
41029
|
+
const overridePath = (0, import_node_path38.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
|
|
41030
|
+
(0, import_node_fs38.mkdirSync)((0, import_node_path38.join)(cwd, "tmp", "stage"), { recursive: true });
|
|
40841
41031
|
(0, import_node_fs38.writeFileSync)(overridePath, body, "utf8");
|
|
40842
41032
|
return stageComposeFileEnv(stageComposeFiles(cwd));
|
|
40843
41033
|
}
|
|
40844
41034
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
40845
41035
|
if (!config.ensureEnv) return;
|
|
40846
|
-
const target = (0,
|
|
40847
|
-
const example = (0,
|
|
41036
|
+
const target = (0, import_node_path38.join)(cwd, config.ensureEnv.target);
|
|
41037
|
+
const example = (0, import_node_path38.join)(cwd, config.ensureEnv.example);
|
|
40848
41038
|
if (!(0, import_node_fs38.existsSync)(target) && (0, import_node_fs38.existsSync)(example)) {
|
|
40849
41039
|
(0, import_node_fs38.copyFileSync)(example, target);
|
|
40850
41040
|
} else if ((0, import_node_fs38.existsSync)(target) && (0, import_node_fs38.existsSync)(example)) {
|
|
@@ -41025,7 +41215,7 @@ async function startStage(config = {}, opts = {}) {
|
|
|
41025
41215
|
let up = sub(config.up.trim());
|
|
41026
41216
|
if (opts.forceRecreate) up = appendForceRecreate(up);
|
|
41027
41217
|
const identity = await resolveStageIdentity(cwd);
|
|
41028
|
-
const child2 = (0,
|
|
41218
|
+
const child2 = (0, import_node_child_process16.spawn)(bash, ["-c", up], {
|
|
41029
41219
|
cwd,
|
|
41030
41220
|
// POSIX-only: the process group exists for the group-kill in stopStage. On win32 teardown is
|
|
41031
41221
|
// `taskkill /T /F` (no group needed), and detached+shell defeats windowsHide — every spawn would
|
|
@@ -41135,7 +41325,7 @@ async function runStage(config = {}, opts = {}) {
|
|
|
41135
41325
|
|
|
41136
41326
|
// src/worktree-evidence-archive.ts
|
|
41137
41327
|
var import_node_fs39 = require("node:fs");
|
|
41138
|
-
var
|
|
41328
|
+
var import_node_path39 = require("node:path");
|
|
41139
41329
|
var JERV_ARTIFACT_RUN_SCOPE_ENV_VARS = ["JERV_RUN_ID", ...SESSION_ID_ENV_VARS];
|
|
41140
41330
|
function sanitizeArchiveSegment(value, max = 80) {
|
|
41141
41331
|
const scrubbed = value.replace(/[^A-Za-z0-9._@+-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -41167,7 +41357,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
41167
41357
|
const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
|
|
41168
41358
|
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
41169
41359
|
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
41170
|
-
const source = (0,
|
|
41360
|
+
const source = (0, import_node_path39.join)(args.worktreePath, ".jerv");
|
|
41171
41361
|
if (!exists(source)) return { status: "absent" };
|
|
41172
41362
|
if (!isDirectory(source)) return { status: "skipped", reason: "jerv-not-a-directory" };
|
|
41173
41363
|
const runScope = resolveArtifactRunScope(env);
|
|
@@ -41175,7 +41365,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
41175
41365
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
41176
41366
|
const dest = resolveRoot(args.primaryRoot, "jerv-artifacts", runScope, branchSlug, stamp, ".jerv");
|
|
41177
41367
|
try {
|
|
41178
|
-
mkdirp((0,
|
|
41368
|
+
mkdirp((0, import_node_path39.dirname)(dest));
|
|
41179
41369
|
copyDir(source, dest);
|
|
41180
41370
|
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
41181
41371
|
return { status: "archived", path: dest, runScope };
|
|
@@ -41191,7 +41381,7 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
|
41191
41381
|
const exists = deps.exists ?? import_node_fs39.existsSync;
|
|
41192
41382
|
const stat4 = deps.stat ?? defaultStat;
|
|
41193
41383
|
const readdir2 = deps.readdir ?? import_node_fs39.readdirSync;
|
|
41194
|
-
const tmpRoot = (0,
|
|
41384
|
+
const tmpRoot = (0, import_node_path39.join)(worktreePath, "tmp");
|
|
41195
41385
|
if (!exists(tmpRoot)) return [];
|
|
41196
41386
|
const entries = [];
|
|
41197
41387
|
const walk2 = (dir) => {
|
|
@@ -41202,14 +41392,14 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
|
41202
41392
|
return;
|
|
41203
41393
|
}
|
|
41204
41394
|
for (const name of names) {
|
|
41205
|
-
const full = (0,
|
|
41395
|
+
const full = (0, import_node_path39.join)(dir, name);
|
|
41206
41396
|
let st;
|
|
41207
41397
|
try {
|
|
41208
41398
|
st = stat4(full);
|
|
41209
41399
|
} catch {
|
|
41210
41400
|
continue;
|
|
41211
41401
|
}
|
|
41212
|
-
const relPath = (0,
|
|
41402
|
+
const relPath = (0, import_node_path39.relative)(worktreePath, full).replace(/\\/g, "/");
|
|
41213
41403
|
if (st.isDirectory()) {
|
|
41214
41404
|
if (st.mtimeMs > newerThanMs) entries.push({ relPath, bytes: 0, mtimeMs: st.mtimeMs });
|
|
41215
41405
|
walk2(full);
|
|
@@ -41234,7 +41424,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
|
41234
41424
|
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
41235
41425
|
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
41236
41426
|
const scanned = scanWorktreeTmpEvidence(args.worktreePath, args.newerThanMs, deps);
|
|
41237
|
-
const source = (0,
|
|
41427
|
+
const source = (0, import_node_path39.join)(args.worktreePath, "tmp");
|
|
41238
41428
|
if (!scanned.length) return { status: "absent" };
|
|
41239
41429
|
if (!exists(source)) return { status: "absent" };
|
|
41240
41430
|
const runScope = resolveArtifactRunScope(env);
|
|
@@ -41242,7 +41432,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
|
41242
41432
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
41243
41433
|
const dest = resolveRoot(args.primaryRoot, "worktree-artifacts", runScope, branchSlug, stamp, "tmp");
|
|
41244
41434
|
try {
|
|
41245
|
-
mkdirp((0,
|
|
41435
|
+
mkdirp((0, import_node_path39.dirname)(dest));
|
|
41246
41436
|
copyDir(source, dest);
|
|
41247
41437
|
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
41248
41438
|
const bytes = scanned.reduce((sum, e) => sum + e.bytes, 0);
|
|
@@ -41311,7 +41501,7 @@ function normPath2(p) {
|
|
|
41311
41501
|
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
41312
41502
|
}
|
|
41313
41503
|
function unlinkNodeModulesJunction(wtPath) {
|
|
41314
|
-
const nm = (0,
|
|
41504
|
+
const nm = (0, import_node_path40.join)(wtPath, "node_modules");
|
|
41315
41505
|
try {
|
|
41316
41506
|
if ((0, import_node_fs40.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs40.rmdirSync)(nm);
|
|
41317
41507
|
return { ok: true };
|
|
@@ -41448,7 +41638,7 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
|
|
|
41448
41638
|
}
|
|
41449
41639
|
async function listNestedIgnoredNodeModules(wtPath, execGit) {
|
|
41450
41640
|
const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
|
|
41451
|
-
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0,
|
|
41641
|
+
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path40.join)(wtPath, line.slice(0, -1)));
|
|
41452
41642
|
}
|
|
41453
41643
|
function safeRemoveTree(path2) {
|
|
41454
41644
|
const stat4 = (0, import_node_fs40.lstatSync)(path2);
|
|
@@ -41461,7 +41651,7 @@ function safeRemoveTree(path2) {
|
|
|
41461
41651
|
return;
|
|
41462
41652
|
}
|
|
41463
41653
|
if (stat4.isDirectory()) {
|
|
41464
|
-
for (const entry of (0, import_node_fs40.readdirSync)(path2)) safeRemoveTree((0,
|
|
41654
|
+
for (const entry of (0, import_node_fs40.readdirSync)(path2)) safeRemoveTree((0, import_node_path40.join)(path2, entry));
|
|
41465
41655
|
(0, import_node_fs40.rmdirSync)(path2);
|
|
41466
41656
|
return;
|
|
41467
41657
|
}
|
|
@@ -41469,7 +41659,7 @@ function safeRemoveTree(path2) {
|
|
|
41469
41659
|
}
|
|
41470
41660
|
async function removeWorktreeNodeModules(wtPath) {
|
|
41471
41661
|
try {
|
|
41472
|
-
safeRemoveTree((0,
|
|
41662
|
+
safeRemoveTree((0, import_node_path40.join)(wtPath, "node_modules"));
|
|
41473
41663
|
return { ok: true };
|
|
41474
41664
|
} catch (e) {
|
|
41475
41665
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -41513,7 +41703,7 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
|
41513
41703
|
return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
|
|
41514
41704
|
}
|
|
41515
41705
|
for (const entry of entries) {
|
|
41516
|
-
const child2 = (0,
|
|
41706
|
+
const child2 = (0, import_node_path40.join)(dir, entry.name);
|
|
41517
41707
|
if (entry.isSymbolicLink()) {
|
|
41518
41708
|
let target = "";
|
|
41519
41709
|
try {
|
|
@@ -41840,7 +42030,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
41840
42030
|
const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
|
|
41841
42031
|
if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
|
|
41842
42032
|
unlinkedReparsePoints.push(...preHelperGuard.unlinked);
|
|
41843
|
-
if (pathExists((0,
|
|
42033
|
+
if (pathExists((0, import_node_path40.join)(wtPath, "node_modules"))) {
|
|
41844
42034
|
const nmRemoved = await (options.removeRealNodeModules ?? removeWorktreeNodeModules)(wtPath);
|
|
41845
42035
|
if (!nmRemoved.ok) {
|
|
41846
42036
|
report.worktree = {
|
|
@@ -41980,7 +42170,7 @@ function norm(p) {
|
|
|
41980
42170
|
return process.platform === "win32" ? s.toLowerCase() : s;
|
|
41981
42171
|
}
|
|
41982
42172
|
function residueLedgerPath(gitCommonDir) {
|
|
41983
|
-
return (0,
|
|
42173
|
+
return (0, import_node_path41.join)(gitCommonDir, RESIDUE_LEDGER_FILE);
|
|
41984
42174
|
}
|
|
41985
42175
|
function readPaths(file, deps) {
|
|
41986
42176
|
try {
|
|
@@ -41993,7 +42183,7 @@ function readPaths(file, deps) {
|
|
|
41993
42183
|
function writePaths(file, paths, deps) {
|
|
41994
42184
|
try {
|
|
41995
42185
|
const write = deps.writeLedger ?? ((f, t) => {
|
|
41996
|
-
(0, import_node_fs41.mkdirSync)((0,
|
|
42186
|
+
(0, import_node_fs41.mkdirSync)((0, import_node_path41.dirname)(f), { recursive: true });
|
|
41997
42187
|
(0, import_node_fs41.writeFileSync)(f, t, "utf8");
|
|
41998
42188
|
});
|
|
41999
42189
|
write(file, `${JSON.stringify([...new Set(paths)], null, 1)}
|
|
@@ -42044,7 +42234,7 @@ async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
|
42044
42234
|
}
|
|
42045
42235
|
if (recorded.length) writePaths(file, remaining, deps);
|
|
42046
42236
|
const roots = /* @__PURE__ */ new Set();
|
|
42047
|
-
for (const p of [...registered.slice(1), ...recorded]) roots.add((0,
|
|
42237
|
+
for (const p of [...registered.slice(1), ...recorded]) roots.add((0, import_node_path41.dirname)(p));
|
|
42048
42238
|
const keptKeys = new Set(remaining.map(norm));
|
|
42049
42239
|
for (const root of roots) {
|
|
42050
42240
|
if (!isDir(root) || live.has(norm(root)) || norm(root) === primary) continue;
|
|
@@ -42055,7 +42245,7 @@ async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
|
42055
42245
|
continue;
|
|
42056
42246
|
}
|
|
42057
42247
|
for (const name of children) {
|
|
42058
|
-
const child2 = (0,
|
|
42248
|
+
const child2 = (0, import_node_path41.join)(root, name);
|
|
42059
42249
|
const key = norm(child2);
|
|
42060
42250
|
if (live.has(key) || key === primary || keptKeys.has(key) || !isDir(child2)) continue;
|
|
42061
42251
|
let entries;
|
|
@@ -42123,10 +42313,10 @@ function argvWantsJson2() {
|
|
|
42123
42313
|
return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
|
|
42124
42314
|
}
|
|
42125
42315
|
function hubRoot() {
|
|
42126
|
-
const fromPkg = (0,
|
|
42316
|
+
const fromPkg = (0, import_node_path42.join)(__dirname, "..", "..");
|
|
42127
42317
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
42128
|
-
if ((0, import_node_fs42.existsSync)((0,
|
|
42129
|
-
if ((0, import_node_fs42.existsSync)((0,
|
|
42318
|
+
if ((0, import_node_fs42.existsSync)((0, import_node_path42.join)(fromPkg, marker))) return fromPkg;
|
|
42319
|
+
if ((0, import_node_fs42.existsSync)((0, import_node_path42.join)(process.cwd(), marker))) return process.cwd();
|
|
42130
42320
|
return null;
|
|
42131
42321
|
}
|
|
42132
42322
|
function ciAuditDeps() {
|
|
@@ -42138,7 +42328,7 @@ function ciAuditDeps() {
|
|
|
42138
42328
|
getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
|
|
42139
42329
|
readSeedFile: (path2) => {
|
|
42140
42330
|
if (!root) return null;
|
|
42141
|
-
const fullPath = (0,
|
|
42331
|
+
const fullPath = (0, import_node_path42.join)(root, path2);
|
|
42142
42332
|
return (0, import_node_fs42.existsSync)(fullPath) ? (0, import_node_fs42.readFileSync)(fullPath, "utf8") : null;
|
|
42143
42333
|
}
|
|
42144
42334
|
};
|
|
@@ -42246,7 +42436,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
42246
42436
|
try {
|
|
42247
42437
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
|
|
42248
42438
|
if (o.repo) args.push("--repo", o.repo);
|
|
42249
|
-
spawnDetachedSelf(args, { spawn:
|
|
42439
|
+
spawnDetachedSelf(args, { spawn: import_node_child_process17.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
|
|
42250
42440
|
} catch {
|
|
42251
42441
|
}
|
|
42252
42442
|
}
|
|
@@ -42897,11 +43087,11 @@ ${list}`);
|
|
|
42897
43087
|
}
|
|
42898
43088
|
});
|
|
42899
43089
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
42900
|
-
const wfDir = (0,
|
|
43090
|
+
const wfDir = (0, import_node_path42.join)(cwd, ".github", "workflows");
|
|
42901
43091
|
if (!(0, import_node_fs42.existsSync)(wfDir)) return [];
|
|
42902
43092
|
return (0, import_node_fs42.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
42903
43093
|
try {
|
|
42904
|
-
return workflowReportsPrChecks((0, import_node_fs42.readFileSync)((0,
|
|
43094
|
+
return workflowReportsPrChecks((0, import_node_fs42.readFileSync)((0, import_node_path42.join)(wfDir, name), "utf8"));
|
|
42905
43095
|
} catch {
|
|
42906
43096
|
return true;
|
|
42907
43097
|
}
|
|
@@ -43406,7 +43596,7 @@ ${list}`);
|
|
|
43406
43596
|
const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
|
|
43407
43597
|
let foreignCheckout;
|
|
43408
43598
|
if (foreignCwd) {
|
|
43409
|
-
const sibling = (0,
|
|
43599
|
+
const sibling = (0, import_node_path42.join)((0, import_node_path42.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
|
|
43410
43600
|
const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
|
|
43411
43601
|
if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
|
|
43412
43602
|
const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
|
|
@@ -43615,7 +43805,7 @@ ${list}`);
|
|
|
43615
43805
|
let residueSweep;
|
|
43616
43806
|
if (!(foreignCwd && !foreignCheckout)) {
|
|
43617
43807
|
try {
|
|
43618
|
-
gitCommonDir = (0,
|
|
43808
|
+
gitCommonDir = (0, import_node_path42.resolve)(primaryRoot, (await execFileP("git", ["-C", primaryRoot, "rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim());
|
|
43619
43809
|
residueSweep = await sweepWorktreeResidue(gitCommonDir, beforeWorktrees.map((w) => w.path));
|
|
43620
43810
|
} catch {
|
|
43621
43811
|
}
|
|
@@ -44709,15 +44899,15 @@ function rcRunCauseFromLog(log) {
|
|
|
44709
44899
|
}
|
|
44710
44900
|
function suiteTrainIo(root, summaryFile) {
|
|
44711
44901
|
const recordPath = repoRuntimeStatePath(root, "suite", "cut.json");
|
|
44712
|
-
const base = (0,
|
|
44902
|
+
const base = (0, import_node_path43.join)(repoRuntimeStatePath(root, "suite"), "checkouts");
|
|
44713
44903
|
const checkout = async (repo) => {
|
|
44714
44904
|
if (repo === SUITE_REPO) return root;
|
|
44715
|
-
const target = (0,
|
|
44905
|
+
const target = (0, import_node_path43.join)(base, repo.split("/")[1]);
|
|
44716
44906
|
if (!(0, import_node_fs43.existsSync)(target)) {
|
|
44717
44907
|
(0, import_node_fs43.mkdirSync)(base, { recursive: true });
|
|
44718
44908
|
await exec("git", ["clone", "--quiet", "--branch", "development", `https://github.com/${repo}.git`, target], root, WATCH_TIMEOUT);
|
|
44719
44909
|
}
|
|
44720
|
-
if (!(0, import_node_fs43.existsSync)((0,
|
|
44910
|
+
if (!(0, import_node_fs43.existsSync)((0, import_node_path43.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
|
|
44721
44911
|
const origin = await exec("git", ["remote", "get-url", "origin"], target);
|
|
44722
44912
|
if (!origin.toLowerCase().endsWith(`/${repo.toLowerCase()}.git`) && !origin.toLowerCase().endsWith(`/${repo.toLowerCase()}`))
|
|
44723
44913
|
throw new Error(`${repo}: suite checkout origin mismatch at ${target}; stop`);
|
|
@@ -45106,8 +45296,8 @@ async function collectWaveStatus(deps) {
|
|
|
45106
45296
|
init_github_client();
|
|
45107
45297
|
init_cli_shared();
|
|
45108
45298
|
var import_node_fs46 = require("node:fs");
|
|
45109
|
-
var
|
|
45110
|
-
var
|
|
45299
|
+
var import_node_os21 = require("node:os");
|
|
45300
|
+
var import_node_path44 = require("node:path");
|
|
45111
45301
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
45112
45302
|
async function collectStatus() {
|
|
45113
45303
|
const repo = await resolveRepo();
|
|
@@ -45296,10 +45486,10 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
45296
45486
|
else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
|
|
45297
45487
|
else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
|
|
45298
45488
|
}
|
|
45299
|
-
const home = (0,
|
|
45489
|
+
const home = (0, import_node_os21.homedir)();
|
|
45300
45490
|
const plugin = onboardPluginGate({
|
|
45301
|
-
readKnown: () => readFileSyncSafe((0,
|
|
45302
|
-
readSettings: () => readFileSyncSafe((0,
|
|
45491
|
+
readKnown: () => readFileSyncSafe((0, import_node_path44.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs46.readFileSync),
|
|
45492
|
+
readSettings: () => readFileSyncSafe((0, import_node_path44.join)(home, ".claude", "settings.json"), import_node_fs46.readFileSync)
|
|
45303
45493
|
});
|
|
45304
45494
|
return {
|
|
45305
45495
|
guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
|
|
@@ -48413,7 +48603,7 @@ function registerSessionReport(program3) {
|
|
|
48413
48603
|
|
|
48414
48604
|
// src/stage-commands.ts
|
|
48415
48605
|
var import_node_fs47 = require("node:fs");
|
|
48416
|
-
var
|
|
48606
|
+
var import_node_path45 = require("node:path");
|
|
48417
48607
|
init_cli_shared();
|
|
48418
48608
|
init_clean_exit();
|
|
48419
48609
|
|
|
@@ -48659,8 +48849,8 @@ function registerStageCommands(program3) {
|
|
|
48659
48849
|
return {
|
|
48660
48850
|
resolution: decideStage({
|
|
48661
48851
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
48662
|
-
hasCompose: (0, import_node_fs47.existsSync)((0,
|
|
48663
|
-
hasEnvExample: (0, import_node_fs47.existsSync)((0,
|
|
48852
|
+
hasCompose: (0, import_node_fs47.existsSync)((0, import_node_path45.join)(process.cwd(), "docker-compose.yml")),
|
|
48853
|
+
hasEnvExample: (0, import_node_fs47.existsSync)((0, import_node_path45.join)(process.cwd(), ".env.example")),
|
|
48664
48854
|
repo
|
|
48665
48855
|
}),
|
|
48666
48856
|
project: project2,
|
|
@@ -48944,20 +49134,20 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
|
|
|
48944
49134
|
}
|
|
48945
49135
|
|
|
48946
49136
|
// src/tenant-artifact.ts
|
|
48947
|
-
var
|
|
49137
|
+
var import_node_crypto14 = require("node:crypto");
|
|
48948
49138
|
var import_node_fs48 = require("node:fs");
|
|
48949
49139
|
var import_promises10 = require("node:fs/promises");
|
|
48950
|
-
var
|
|
49140
|
+
var import_node_path46 = require("node:path");
|
|
48951
49141
|
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}$/;
|
|
48952
49142
|
var MAX_BYTES = 5 * 1024 * 1024 * 1024;
|
|
48953
49143
|
async function sha256File(path2) {
|
|
48954
|
-
const hash = (0,
|
|
49144
|
+
const hash = (0, import_node_crypto14.createHash)("sha256");
|
|
48955
49145
|
for await (const chunk of (0, import_node_fs48.createReadStream)(path2)) hash.update(chunk);
|
|
48956
49146
|
return hash.digest("hex");
|
|
48957
49147
|
}
|
|
48958
49148
|
async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
48959
49149
|
if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
|
|
48960
|
-
const path2 = (0,
|
|
49150
|
+
const path2 = (0, import_node_path46.resolve)(inputPath);
|
|
48961
49151
|
const info = await (0, import_promises10.stat)(path2);
|
|
48962
49152
|
if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
|
|
48963
49153
|
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`);
|
|
@@ -49147,7 +49337,7 @@ async function resolveWhoami(deps) {
|
|
|
49147
49337
|
}
|
|
49148
49338
|
|
|
49149
49339
|
// src/command-register-developer.ts
|
|
49150
|
-
var
|
|
49340
|
+
var import_node_path51 = require("node:path");
|
|
49151
49341
|
init_house_map();
|
|
49152
49342
|
init_hub_url();
|
|
49153
49343
|
|
|
@@ -49180,27 +49370,27 @@ async function executeWaveLand(plan, deps) {
|
|
|
49180
49370
|
}
|
|
49181
49371
|
|
|
49182
49372
|
// src/dist-drift.ts
|
|
49183
|
-
var
|
|
49184
|
-
var
|
|
49373
|
+
var import_node_child_process18 = require("node:child_process");
|
|
49374
|
+
var import_node_crypto16 = require("node:crypto");
|
|
49185
49375
|
var import_node_fs50 = require("node:fs");
|
|
49186
|
-
var
|
|
49187
|
-
var
|
|
49376
|
+
var import_node_os22 = require("node:os");
|
|
49377
|
+
var import_node_path48 = require("node:path");
|
|
49188
49378
|
|
|
49189
49379
|
// ../scripts/distribution-digest.mjs
|
|
49190
|
-
var
|
|
49380
|
+
var import_node_crypto15 = require("node:crypto");
|
|
49191
49381
|
var import_node_fs49 = require("node:fs");
|
|
49192
|
-
var
|
|
49382
|
+
var import_node_path47 = require("node:path");
|
|
49193
49383
|
var slash = (value) => value.replaceAll("\\", "/");
|
|
49194
49384
|
function repoPath(root, declaredPath, label) {
|
|
49195
|
-
const absoluteRoot = (0,
|
|
49196
|
-
const target = (0,
|
|
49197
|
-
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${
|
|
49385
|
+
const absoluteRoot = (0, import_node_path47.resolve)(root);
|
|
49386
|
+
const target = (0, import_node_path47.resolve)(root, declaredPath);
|
|
49387
|
+
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path47.sep}`)) {
|
|
49198
49388
|
throw new Error(`${label} ${declaredPath} escapes the repository root`);
|
|
49199
49389
|
}
|
|
49200
49390
|
return target;
|
|
49201
49391
|
}
|
|
49202
49392
|
function digestFiles(files) {
|
|
49203
|
-
const hash = (0,
|
|
49393
|
+
const hash = (0, import_node_crypto15.createHash)("sha256");
|
|
49204
49394
|
for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
|
|
49205
49395
|
const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs49.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs49.readFileSync)(file.absolute);
|
|
49206
49396
|
hash.update(file.relative, "utf8");
|
|
@@ -49230,7 +49420,7 @@ var DIST_ARTIFACTS = [
|
|
|
49230
49420
|
];
|
|
49231
49421
|
var BOM_DIST_TREE_ID = "mmi-cli-dist";
|
|
49232
49422
|
var ABSENT = "absent";
|
|
49233
|
-
var sha256 = (bytes) => `sha256:${(0,
|
|
49423
|
+
var sha256 = (bytes) => `sha256:${(0, import_node_crypto16.createHash)("sha256").update(bytes).digest("hex")}`;
|
|
49234
49424
|
function artifactDrift(path2, committedBytes, rebuiltBytes) {
|
|
49235
49425
|
const committed = committedBytes ? sha256(committedBytes) : ABSENT;
|
|
49236
49426
|
const rebuiltExpected = rebuiltBytes ? sha256(rebuiltBytes) : ABSENT;
|
|
@@ -49308,7 +49498,7 @@ function walkFiles(root) {
|
|
|
49308
49498
|
const files = [];
|
|
49309
49499
|
const walk2 = (directory) => {
|
|
49310
49500
|
for (const entry of (0, import_node_fs50.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
49311
|
-
const child2 = (0,
|
|
49501
|
+
const child2 = (0, import_node_path48.join)(directory, entry.name);
|
|
49312
49502
|
if (entry.isDirectory()) walk2(child2);
|
|
49313
49503
|
else files.push(child2);
|
|
49314
49504
|
}
|
|
@@ -49318,14 +49508,14 @@ function walkFiles(root) {
|
|
|
49318
49508
|
}
|
|
49319
49509
|
function bomPathFor(root) {
|
|
49320
49510
|
try {
|
|
49321
|
-
const registry2 = JSON.parse((0, import_node_fs50.readFileSync)((0,
|
|
49322
|
-
return (0,
|
|
49511
|
+
const registry2 = JSON.parse((0, import_node_fs50.readFileSync)((0, import_node_path48.join)(root, "surfaces.json"), "utf8"));
|
|
49512
|
+
return (0, import_node_path48.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
|
|
49323
49513
|
} catch {
|
|
49324
|
-
return (0,
|
|
49514
|
+
return (0, import_node_path48.join)(root, "distribution-bom.json");
|
|
49325
49515
|
}
|
|
49326
49516
|
}
|
|
49327
49517
|
function rebuildTo(packageRoot, outDir) {
|
|
49328
|
-
(0,
|
|
49518
|
+
(0, import_node_child_process18.execFileSync)(process.execPath, ["build.mjs"], {
|
|
49329
49519
|
cwd: packageRoot,
|
|
49330
49520
|
env: { ...process.env, MMI_DIST_OUTDIR: outDir },
|
|
49331
49521
|
windowsHide: true,
|
|
@@ -49334,28 +49524,28 @@ function rebuildTo(packageRoot, outDir) {
|
|
|
49334
49524
|
});
|
|
49335
49525
|
}
|
|
49336
49526
|
function runDistStatus(root) {
|
|
49337
|
-
const stage = (0, import_node_fs50.mkdtempSync)((0,
|
|
49527
|
+
const stage = (0, import_node_fs50.mkdtempSync)((0, import_node_path48.join)((0, import_node_os22.tmpdir)(), "mmi-dist-drift-"));
|
|
49338
49528
|
let overlayCount = 0;
|
|
49339
49529
|
try {
|
|
49340
|
-
const cliOut = (0,
|
|
49341
|
-
const hubOut = (0,
|
|
49342
|
-
rebuildTo((0,
|
|
49343
|
-
rebuildTo((0,
|
|
49530
|
+
const cliOut = (0, import_node_path48.join)(stage, "cli-dist");
|
|
49531
|
+
const hubOut = (0, import_node_path48.join)(stage, "hub-dist");
|
|
49532
|
+
rebuildTo((0, import_node_path48.join)(root, "cli"), cliOut);
|
|
49533
|
+
rebuildTo((0, import_node_path48.join)(root, "updater"), hubOut);
|
|
49344
49534
|
const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
|
|
49345
49535
|
const rebuilt = (path2) => {
|
|
49346
49536
|
const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
|
|
49347
|
-
return spec ? readOrNull((0,
|
|
49537
|
+
return spec ? readOrNull((0, import_node_path48.join)(outDirFor(spec.packageDir), spec.output)) : null;
|
|
49348
49538
|
};
|
|
49349
|
-
const committed = (path2) => readOrNull((0,
|
|
49350
|
-
const tree = (path2) => readOrNull((0,
|
|
49351
|
-
const distRoot = (0,
|
|
49352
|
-
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0,
|
|
49539
|
+
const committed = (path2) => readOrNull((0, import_node_path48.join)(root, path2));
|
|
49540
|
+
const tree = (path2) => readOrNull((0, import_node_path48.join)(root, path2));
|
|
49541
|
+
const distRoot = (0, import_node_path48.join)(root, "cli", "dist");
|
|
49542
|
+
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path48.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
|
|
49353
49543
|
const bom = JSON.parse((0, import_node_fs50.readFileSync)(bomPathFor(root), "utf8"));
|
|
49354
49544
|
const digest = (entries) => {
|
|
49355
|
-
const overlay = (0,
|
|
49545
|
+
const overlay = (0, import_node_path48.join)(stage, `overlay-${overlayCount++}`);
|
|
49356
49546
|
for (const entry of entries) {
|
|
49357
|
-
const target = (0,
|
|
49358
|
-
(0, import_node_fs50.mkdirSync)((0,
|
|
49547
|
+
const target = (0, import_node_path48.join)(overlay, entry.path);
|
|
49548
|
+
(0, import_node_fs50.mkdirSync)((0, import_node_path48.dirname)(target), { recursive: true });
|
|
49359
49549
|
(0, import_node_fs50.writeFileSync)(target, entry.bytes);
|
|
49360
49550
|
}
|
|
49361
49551
|
return digestPackedFiles(overlay, entries.map((entry) => entry.path));
|
|
@@ -49441,7 +49631,7 @@ init_hub_auth();
|
|
|
49441
49631
|
|
|
49442
49632
|
// src/schedules-lift-command.ts
|
|
49443
49633
|
var import_promises11 = require("node:fs/promises");
|
|
49444
|
-
var
|
|
49634
|
+
var import_node_path49 = require("node:path");
|
|
49445
49635
|
init_clean_exit();
|
|
49446
49636
|
init_cli_shared();
|
|
49447
49637
|
var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
|
|
@@ -49470,7 +49660,7 @@ async function readWorkflowFiles(dir) {
|
|
|
49470
49660
|
const files = [];
|
|
49471
49661
|
for (const name of names.sort()) {
|
|
49472
49662
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
49473
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises11.readFile)((0,
|
|
49663
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises11.readFile)((0, import_node_path49.join)(dir, name), "utf8") });
|
|
49474
49664
|
}
|
|
49475
49665
|
return files;
|
|
49476
49666
|
}
|
|
@@ -49552,9 +49742,9 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
|
|
|
49552
49742
|
}
|
|
49553
49743
|
|
|
49554
49744
|
// src/spawn-policy-core.ts
|
|
49555
|
-
var
|
|
49745
|
+
var import_node_child_process19 = require("node:child_process");
|
|
49556
49746
|
var import_node_fs51 = require("node:fs");
|
|
49557
|
-
var
|
|
49747
|
+
var import_node_path50 = require("node:path");
|
|
49558
49748
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
49559
49749
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
49560
49750
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -49623,7 +49813,7 @@ function findViolationsInSource(raw) {
|
|
|
49623
49813
|
return found;
|
|
49624
49814
|
}
|
|
49625
49815
|
function policedFiles(root) {
|
|
49626
|
-
const r = (0,
|
|
49816
|
+
const r = (0, import_node_child_process19.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
49627
49817
|
cwd: root,
|
|
49628
49818
|
encoding: "utf8",
|
|
49629
49819
|
windowsHide: true,
|
|
@@ -49640,7 +49830,7 @@ function runSpawnPolicy(root) {
|
|
|
49640
49830
|
for (const file of files) {
|
|
49641
49831
|
let raw;
|
|
49642
49832
|
try {
|
|
49643
|
-
raw = (0, import_node_fs51.readFileSync)((0,
|
|
49833
|
+
raw = (0, import_node_fs51.readFileSync)((0, import_node_path50.join)(root, file), "utf8");
|
|
49644
49834
|
} catch {
|
|
49645
49835
|
continue;
|
|
49646
49836
|
}
|
|
@@ -49660,7 +49850,7 @@ function runSpawnPolicy(root) {
|
|
|
49660
49850
|
function registerDeveloperCommands(program3) {
|
|
49661
49851
|
const rules = program3.command("rules").description("org-managed .gitignore delivery");
|
|
49662
49852
|
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) => {
|
|
49663
|
-
const path2 = (0,
|
|
49853
|
+
const path2 = (0, import_node_path51.join)(process.cwd(), ".gitignore");
|
|
49664
49854
|
const current = (0, import_node_fs52.existsSync)(path2) ? (0, import_node_fs52.readFileSync)(path2, "utf8") : null;
|
|
49665
49855
|
const plan = planManagedGitignore(current);
|
|
49666
49856
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
@@ -49935,12 +50125,12 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
49935
50125
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
49936
50126
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
49937
50127
|
function envHealLockPath(home) {
|
|
49938
|
-
return (0,
|
|
50128
|
+
return (0, import_node_path52.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
49939
50129
|
}
|
|
49940
50130
|
async function withEnvHealLock(what, run) {
|
|
49941
50131
|
try {
|
|
49942
50132
|
return await withFileLock(
|
|
49943
|
-
envHealLockPath((0,
|
|
50133
|
+
envHealLockPath((0, import_node_os23.homedir)()),
|
|
49944
50134
|
{ staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
|
|
49945
50135
|
run
|
|
49946
50136
|
);
|
|
@@ -50037,7 +50227,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
50037
50227
|
const configRoot = surfaceConfigRoot(surface);
|
|
50038
50228
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
50039
50229
|
const plan = buildPluginCachePlan(
|
|
50040
|
-
(0,
|
|
50230
|
+
(0, import_node_os23.homedir)(),
|
|
50041
50231
|
running,
|
|
50042
50232
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
50043
50233
|
{ configRoot, includeStaging: surface !== "codex" }
|
|
@@ -50065,7 +50255,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
50065
50255
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
50066
50256
|
const installed = installedActivePluginVersion(surface);
|
|
50067
50257
|
const plan = buildPluginCachePlan(
|
|
50068
|
-
(0,
|
|
50258
|
+
(0, import_node_os23.homedir)(),
|
|
50069
50259
|
running,
|
|
50070
50260
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
50071
50261
|
{ configRoot, includeStaging: surface !== "codex", installedVersion: installed }
|
|
@@ -50107,7 +50297,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
50107
50297
|
// has no generated routing index would
|
|
50108
50298
|
// get a permanent — demanding an artifact it never asked for.
|
|
50109
50299
|
docsIndexState: (root) => {
|
|
50110
|
-
if (!(0, import_node_fs53.existsSync)((0,
|
|
50300
|
+
if (!(0, import_node_fs53.existsSync)((0, import_node_path52.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
50111
50301
|
const real = createDocsIndexDeps(root);
|
|
50112
50302
|
let docs;
|
|
50113
50303
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -50116,7 +50306,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
50116
50306
|
},
|
|
50117
50307
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
50118
50308
|
healDocsIndex: (root) => {
|
|
50119
|
-
if (!(0, import_node_fs53.existsSync)((0,
|
|
50309
|
+
if (!(0, import_node_fs53.existsSync)((0, import_node_path52.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
50120
50310
|
const real = createDocsIndexDeps(root);
|
|
50121
50311
|
let docs;
|
|
50122
50312
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -51203,16 +51393,16 @@ function ciAuditDeps2() {
|
|
|
51203
51393
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
51204
51394
|
readSeedFile: (path2) => {
|
|
51205
51395
|
if (!root) return null;
|
|
51206
|
-
const fullPath = (0,
|
|
51396
|
+
const fullPath = (0, import_node_path52.join)(root, path2);
|
|
51207
51397
|
return (0, import_node_fs53.existsSync)(fullPath) ? (0, import_node_fs53.readFileSync)(fullPath, "utf8") : null;
|
|
51208
51398
|
}
|
|
51209
51399
|
};
|
|
51210
51400
|
}
|
|
51211
51401
|
function hubRoot2() {
|
|
51212
|
-
const fromPkg = (0,
|
|
51402
|
+
const fromPkg = (0, import_node_path52.join)(__dirname, "..", "..");
|
|
51213
51403
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
51214
|
-
if ((0, import_node_fs53.existsSync)((0,
|
|
51215
|
-
if ((0, import_node_fs53.existsSync)((0,
|
|
51404
|
+
if ((0, import_node_fs53.existsSync)((0, import_node_path52.join)(fromPkg, marker))) return fromPkg;
|
|
51405
|
+
if ((0, import_node_fs53.existsSync)((0, import_node_path52.join)(process.cwd(), marker))) return process.cwd();
|
|
51216
51406
|
return null;
|
|
51217
51407
|
}
|
|
51218
51408
|
registerQueryCommands(program2);
|
|
@@ -51353,7 +51543,7 @@ function directoryBytes(path2) {
|
|
|
51353
51543
|
return 0;
|
|
51354
51544
|
}
|
|
51355
51545
|
for (const entry of entries) {
|
|
51356
|
-
const child2 = (0,
|
|
51546
|
+
const child2 = (0, import_node_path52.join)(path2, entry.name);
|
|
51357
51547
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
51358
51548
|
else {
|
|
51359
51549
|
try {
|
|
@@ -51383,7 +51573,7 @@ function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
|
51383
51573
|
dirBytes,
|
|
51384
51574
|
listStagingDirs: (root) => (0, import_node_fs53.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
51385
51575
|
try {
|
|
51386
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
51576
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path52.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs53.statSync)(p).mtimeMs) };
|
|
51387
51577
|
} catch {
|
|
51388
51578
|
return { name: d.name, mtimeMs: Date.now() };
|
|
51389
51579
|
}
|
|
@@ -51397,7 +51587,7 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
51397
51587
|
return {
|
|
51398
51588
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
51399
51589
|
mtimeMs: (name) => {
|
|
51400
|
-
const p = (0,
|
|
51590
|
+
const p = (0, import_node_path52.join)(stagingRoot, name);
|
|
51401
51591
|
if (!(0, import_node_fs53.existsSync)(p)) return null;
|
|
51402
51592
|
try {
|
|
51403
51593
|
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs53.statSync)(q).mtimeMs);
|
|
@@ -51420,7 +51610,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
51420
51610
|
return;
|
|
51421
51611
|
}
|
|
51422
51612
|
const plan = buildPluginCachePlan(
|
|
51423
|
-
(0,
|
|
51613
|
+
(0, import_node_os23.homedir)(),
|
|
51424
51614
|
running,
|
|
51425
51615
|
pluginCacheFsDeps(configRoot, directoryBytes),
|
|
51426
51616
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|