@mutmutco/cli 4.4.8 → 4.4.9
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 +605 -393
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -4534,7 +4534,7 @@ function useColor() {
|
|
|
4534
4534
|
var program = new Command();
|
|
4535
4535
|
|
|
4536
4536
|
// src/command-composition.ts
|
|
4537
|
-
var
|
|
4537
|
+
var import_node_fs49 = require("node:fs");
|
|
4538
4538
|
init_clean_exit();
|
|
4539
4539
|
init_cli_shared();
|
|
4540
4540
|
|
|
@@ -5437,7 +5437,7 @@ function resolveVerbAliasShim(argv) {
|
|
|
5437
5437
|
|
|
5438
5438
|
// src/command-composition.ts
|
|
5439
5439
|
var import_node_os21 = require("node:os");
|
|
5440
|
-
var
|
|
5440
|
+
var import_node_path47 = require("node:path");
|
|
5441
5441
|
|
|
5442
5442
|
// src/board-read.ts
|
|
5443
5443
|
var import_node_fs13 = require("node:fs");
|
|
@@ -15938,10 +15938,10 @@ var rollout_plan_default = {
|
|
|
15938
15938
|
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)."
|
|
15939
15939
|
},
|
|
15940
15940
|
baseline: {
|
|
15941
|
-
version: "4.4.
|
|
15942
|
-
tag: "v4.4.
|
|
15943
|
-
commit: "
|
|
15944
|
-
npm: "@mutmutco/cli@4.4.
|
|
15941
|
+
version: "4.4.9",
|
|
15942
|
+
tag: "v4.4.9",
|
|
15943
|
+
commit: "da79ce082e9b",
|
|
15944
|
+
npm: "@mutmutco/cli@4.4.9"
|
|
15945
15945
|
},
|
|
15946
15946
|
exitCriterion: "fleet-n-of-n",
|
|
15947
15947
|
hubOnlyShortcut: "forbidden",
|
|
@@ -15958,14 +15958,14 @@ var rollout_plan_default = {
|
|
|
15958
15958
|
repo: "mutmutco/mmi-hub",
|
|
15959
15959
|
role: "canary",
|
|
15960
15960
|
schedule: "train",
|
|
15961
|
-
v3Target: "v4.4.
|
|
15961
|
+
v3Target: "v4.4.9"
|
|
15962
15962
|
}
|
|
15963
15963
|
],
|
|
15964
15964
|
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.",
|
|
15965
15965
|
rollback: {
|
|
15966
15966
|
independent: true,
|
|
15967
|
-
mechanism: "npm dist-tag latest -> 4.4.
|
|
15968
|
-
v3Target: "v4.4.
|
|
15967
|
+
mechanism: "npm dist-tag latest -> 4.4.9 and redeploy the Hub Lambda from tag v4.4.9 (da79ce082e9b); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
15968
|
+
v3Target: "v4.4.9 (@mutmutco/cli@4.4.9, tag commit da79ce082e9b \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
15969
15969
|
}
|
|
15970
15970
|
},
|
|
15971
15971
|
{
|
|
@@ -18259,6 +18259,9 @@ function correlateTenantRun(deps, since, titleIncludes) {
|
|
|
18259
18259
|
function correlatePublishRun(deps, since, titleIncludes) {
|
|
18260
18260
|
return correlateRun(deps, { workflow: "tenant-publish.yml", since, mode: "dispatch", titleIncludes });
|
|
18261
18261
|
}
|
|
18262
|
+
function correlateGetPublishRun(deps, since, product) {
|
|
18263
|
+
return correlateRun(deps, { workflow: "get-publish.yml", since, mode: "dispatch", titleIncludes: [product] });
|
|
18264
|
+
}
|
|
18262
18265
|
function correlateControlRun(deps, since, titleIncludes) {
|
|
18263
18266
|
return correlateRun(deps, { workflow: "tenant-control.yml", since, mode: "dispatch", titleIncludes });
|
|
18264
18267
|
}
|
|
@@ -18801,6 +18804,10 @@ function isReleaseShaDeployEnumerationCandidate(row, nonDeployEvents) {
|
|
|
18801
18804
|
return true;
|
|
18802
18805
|
}
|
|
18803
18806
|
|
|
18807
|
+
// src/train-apply-deploy.ts
|
|
18808
|
+
var import_node_fs19 = require("node:fs");
|
|
18809
|
+
var import_node_path17 = require("node:path");
|
|
18810
|
+
|
|
18804
18811
|
// src/vercel-deployments.ts
|
|
18805
18812
|
var TERMINAL = /* @__PURE__ */ new Set(["success", "failure", "error", "inactive"]);
|
|
18806
18813
|
function isTerminalVercelState(state) {
|
|
@@ -19009,6 +19016,114 @@ function appendPublishDispatch(deploy, publish) {
|
|
|
19009
19016
|
deployStatus: deploy.deployStatus === "failure" || publish.deployStatus === "failure" ? "failure" : deploy.deployStatus === "pending" || publish.deployStatus === "pending" ? "pending" : "success"
|
|
19010
19017
|
};
|
|
19011
19018
|
}
|
|
19019
|
+
var HUB_SERVED_PRODUCT = "mmi";
|
|
19020
|
+
function getPublishDispatchCommand(repo, product = HUB_SERVED_PRODUCT) {
|
|
19021
|
+
return `gh workflow run get-publish.yml --repo ${repo} --ref main -f product=${product}`;
|
|
19022
|
+
}
|
|
19023
|
+
function readLocalLauncherVersion(root = process.cwd()) {
|
|
19024
|
+
try {
|
|
19025
|
+
const raw = (0, import_node_fs19.readFileSync)((0, import_node_path17.join)(root, "installer", "launcher", "package.json"), "utf8");
|
|
19026
|
+
const version = JSON.parse(raw).version;
|
|
19027
|
+
return typeof version === "string" && version.trim() ? version.trim() : null;
|
|
19028
|
+
} catch {
|
|
19029
|
+
return null;
|
|
19030
|
+
}
|
|
19031
|
+
}
|
|
19032
|
+
function readServedChannelUrl(product = HUB_SERVED_PRODUCT, root = process.cwd()) {
|
|
19033
|
+
try {
|
|
19034
|
+
const raw = (0, import_node_fs19.readFileSync)((0, import_node_path17.join)(root, "config", `product.${product}.json`), "utf8");
|
|
19035
|
+
const host = JSON.parse(raw).host;
|
|
19036
|
+
if (typeof host !== "string" || !host.trim()) return null;
|
|
19037
|
+
return `${host.trim().replace(/\/+$/, "")}/release/version`;
|
|
19038
|
+
} catch {
|
|
19039
|
+
return null;
|
|
19040
|
+
}
|
|
19041
|
+
}
|
|
19042
|
+
async function fetchServedChannelVersion(url, fetchImpl = fetch) {
|
|
19043
|
+
try {
|
|
19044
|
+
const res = await fetchImpl(url, { method: "GET", redirect: "follow", signal: AbortSignal.timeout(15e3) });
|
|
19045
|
+
if (res.status !== 200) return { ok: false, status: res.status };
|
|
19046
|
+
const body = (await res.text()).slice(0, 4096);
|
|
19047
|
+
const version = JSON.parse(body).version;
|
|
19048
|
+
if (typeof version !== "string" || !version.trim()) return { ok: false, status: res.status, error: "pointer carried no version" };
|
|
19049
|
+
return { ok: true, version: version.trim(), status: res.status };
|
|
19050
|
+
} catch (e) {
|
|
19051
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
19052
|
+
}
|
|
19053
|
+
}
|
|
19054
|
+
async function appendServedChannelPublish(deps, input, dispatch) {
|
|
19055
|
+
if (!isHubControlRepo(input.repo)) return dispatch;
|
|
19056
|
+
const channelUrl = input.channelUrl ?? readServedChannelUrl();
|
|
19057
|
+
const remedy = getPublishDispatchCommand(input.repo);
|
|
19058
|
+
const withNote = (note, deployStatus) => ({
|
|
19059
|
+
...dispatch,
|
|
19060
|
+
note: `${dispatch.note}; ${note}`,
|
|
19061
|
+
...deployStatus ? { deployStatus } : {}
|
|
19062
|
+
});
|
|
19063
|
+
if (input.publishStatus !== "success") {
|
|
19064
|
+
return withNote(`served channel NOT moved (publish did not succeed) \u2014 after the publish is green, run \`${remedy}\``);
|
|
19065
|
+
}
|
|
19066
|
+
const since = (deps.now ?? Date.now)();
|
|
19067
|
+
try {
|
|
19068
|
+
await deps.run("gh", ["workflow", "run", "get-publish.yml", "--repo", input.repo, "--ref", "main", "-f", `product=${HUB_SERVED_PRODUCT}`]);
|
|
19069
|
+
} catch (e) {
|
|
19070
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
19071
|
+
return withNote(`get-publish dispatch FAILED: ${msg}. The release itself landed, but the SERVED CHANNEL IS UNCHANGED \u2014 recover with \`${remedy}\``, "failure");
|
|
19072
|
+
}
|
|
19073
|
+
const correlated = await correlateGetPublishRun(deps, since, HUB_SERVED_PRODUCT);
|
|
19074
|
+
if (correlated.read === "failed") {
|
|
19075
|
+
return withNote(`get-publish dispatched but its run could not be READ: ${correlated.error}. Check the Actions tab; if it never ran, recover with \`${remedy}\``, "failure");
|
|
19076
|
+
}
|
|
19077
|
+
const { runId, runUrl } = correlated;
|
|
19078
|
+
const runRow = { workflow: "get-publish.yml", ...runId ? { runId } : {}, ...runUrl ? { runUrl } : {} };
|
|
19079
|
+
if (!input.watch) {
|
|
19080
|
+
return {
|
|
19081
|
+
...dispatch,
|
|
19082
|
+
note: `${dispatch.note}; dispatched get-publish.yml (product=${HUB_SERVED_PRODUCT}) \u2014 NOT watched, so the served channel is not yet proven moved`,
|
|
19083
|
+
workflowRuns: [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: "pending" }]
|
|
19084
|
+
};
|
|
19085
|
+
}
|
|
19086
|
+
const runStatus = await watchTenantRun(deps, runId, input.repo);
|
|
19087
|
+
if (runStatus !== "success") {
|
|
19088
|
+
return {
|
|
19089
|
+
...dispatch,
|
|
19090
|
+
note: `${dispatch.note}; get-publish.yml ${runStatus} \u2014 the SERVED CHANNEL IS UNCHANGED; recover with \`${remedy}\``,
|
|
19091
|
+
workflowRuns: [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: runStatus }],
|
|
19092
|
+
deployStatus: "failure"
|
|
19093
|
+
};
|
|
19094
|
+
}
|
|
19095
|
+
const expected = (deps.readLauncherVersion ?? readLocalLauncherVersion)();
|
|
19096
|
+
const runs = [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: "success" }];
|
|
19097
|
+
if (!channelUrl) {
|
|
19098
|
+
return {
|
|
19099
|
+
...dispatch,
|
|
19100
|
+
note: `${dispatch.note}; get-publish.yml succeeded but the channel URL could not be read from the product config \u2014 the move is unverified`,
|
|
19101
|
+
workflowRuns: runs
|
|
19102
|
+
};
|
|
19103
|
+
}
|
|
19104
|
+
const probe = await (deps.readServedChannelVersion ?? fetchServedChannelVersion)(channelUrl);
|
|
19105
|
+
if (!probe.ok) {
|
|
19106
|
+
const why = probe.error ?? `HTTP ${probe.status ?? "unknown"}`;
|
|
19107
|
+
return {
|
|
19108
|
+
...dispatch,
|
|
19109
|
+
note: `${dispatch.note}; get-publish.yml succeeded but the served channel version could NOT be read at ${channelUrl} (${why}) \u2014 the move is unverified`,
|
|
19110
|
+
workflowRuns: runs
|
|
19111
|
+
};
|
|
19112
|
+
}
|
|
19113
|
+
if (expected && probe.version !== expected) {
|
|
19114
|
+
return {
|
|
19115
|
+
...dispatch,
|
|
19116
|
+
note: `${dispatch.note}; get-publish.yml succeeded but the served channel still reports ${probe.version} (expected launcher ${expected}) \u2014 THE CHANNEL DID NOT MOVE; recover with \`${remedy}\``,
|
|
19117
|
+
workflowRuns: runs,
|
|
19118
|
+
deployStatus: "failure"
|
|
19119
|
+
};
|
|
19120
|
+
}
|
|
19121
|
+
return {
|
|
19122
|
+
...dispatch,
|
|
19123
|
+
note: `${dispatch.note}; served channel moved \u2014 ${channelUrl} now reports ${probe.version}${expected ? "" : " (no local launcher version to compare against)"}`,
|
|
19124
|
+
workflowRuns: runs
|
|
19125
|
+
};
|
|
19126
|
+
}
|
|
19012
19127
|
var JERV_GATEWAY_RUN_URL_NOTE = "no run URL emitted \u2014 host health leg runs outside GitHub Actions";
|
|
19013
19128
|
async function appendJervGatewayReleaseDeploy(deps, repo, tag, tagSha, dispatch) {
|
|
19014
19129
|
if (!isJervHubRepo(repo)) return dispatch;
|
|
@@ -19523,9 +19638,9 @@ function renderTenantControl(r) {
|
|
|
19523
19638
|
}
|
|
19524
19639
|
|
|
19525
19640
|
// src/train-apply-phases.ts
|
|
19526
|
-
var
|
|
19641
|
+
var import_node_fs24 = require("node:fs");
|
|
19527
19642
|
var import_promises3 = require("node:fs/promises");
|
|
19528
|
-
var
|
|
19643
|
+
var import_node_path24 = require("node:path");
|
|
19529
19644
|
|
|
19530
19645
|
// src/actions-billing-preflight.ts
|
|
19531
19646
|
var CANARY_WORKFLOW = "actions-job-start-canary.yml";
|
|
@@ -19778,14 +19893,14 @@ Resume it: ${resumeCommand}
|
|
|
19778
19893
|
|
|
19779
19894
|
// src/train-doctor.ts
|
|
19780
19895
|
var import_node_child_process9 = require("node:child_process");
|
|
19781
|
-
var
|
|
19896
|
+
var import_node_fs23 = require("node:fs");
|
|
19782
19897
|
var import_node_os13 = require("node:os");
|
|
19783
|
-
var
|
|
19898
|
+
var import_node_path23 = require("node:path");
|
|
19784
19899
|
|
|
19785
19900
|
// src/doctor-io.ts
|
|
19786
|
-
var
|
|
19901
|
+
var import_node_fs20 = require("node:fs");
|
|
19787
19902
|
var import_node_os10 = require("node:os");
|
|
19788
|
-
var
|
|
19903
|
+
var import_node_path18 = require("node:path");
|
|
19789
19904
|
var import_node_child_process7 = require("node:child_process");
|
|
19790
19905
|
var import_node_util4 = require("node:util");
|
|
19791
19906
|
|
|
@@ -19798,8 +19913,8 @@ function nodeDiscardSinkPath(platform2 = process.platform) {
|
|
|
19798
19913
|
var execFileP2 = (0, import_node_util4.promisify)(import_node_child_process7.execFile);
|
|
19799
19914
|
function execFileCapture(file, args, opts = {}) {
|
|
19800
19915
|
const sink = nodeDiscardSinkPath();
|
|
19801
|
-
const inFd = (0,
|
|
19802
|
-
const errFd = (0,
|
|
19916
|
+
const inFd = (0, import_node_fs20.openSync)(sink, "r");
|
|
19917
|
+
const errFd = (0, import_node_fs20.openSync)(sink, "w");
|
|
19803
19918
|
try {
|
|
19804
19919
|
return (0, import_node_child_process7.execFileSync)(file, args, {
|
|
19805
19920
|
...opts,
|
|
@@ -19807,17 +19922,17 @@ function execFileCapture(file, args, opts = {}) {
|
|
|
19807
19922
|
stdio: [inFd, "pipe", errFd]
|
|
19808
19923
|
});
|
|
19809
19924
|
} finally {
|
|
19810
|
-
(0,
|
|
19811
|
-
(0,
|
|
19925
|
+
(0, import_node_fs20.closeSync)(inFd);
|
|
19926
|
+
(0, import_node_fs20.closeSync)(errFd);
|
|
19812
19927
|
}
|
|
19813
19928
|
}
|
|
19814
19929
|
var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
19815
19930
|
function stagedClaudePluginVersion() {
|
|
19816
19931
|
try {
|
|
19817
|
-
const stagingRoot = (0,
|
|
19818
|
-
const record = JSON.parse((0,
|
|
19932
|
+
const stagingRoot = (0, import_node_path18.join)((0, import_node_os10.homedir)(), ".claude", "plugins", "staging");
|
|
19933
|
+
const record = JSON.parse((0, import_node_fs20.readFileSync)((0, import_node_path18.join)(stagingRoot, "launch-target.json"), "utf8"));
|
|
19819
19934
|
if (typeof record.target !== "string" || typeof record.incoming !== "string") return void 0;
|
|
19820
|
-
const manifest = JSON.parse((0,
|
|
19935
|
+
const manifest = JSON.parse((0, import_node_fs20.readFileSync)((0, import_node_path18.join)(record.incoming, ".claude-plugin", "plugin.json"), "utf8"));
|
|
19821
19936
|
if (typeof manifest.version !== "string") return void 0;
|
|
19822
19937
|
return manifest.version === record.target ? record.target : void 0;
|
|
19823
19938
|
} catch {
|
|
@@ -19827,7 +19942,7 @@ function stagedClaudePluginVersion() {
|
|
|
19827
19942
|
function installedClaudePluginVersion() {
|
|
19828
19943
|
try {
|
|
19829
19944
|
const file = JSON.parse(
|
|
19830
|
-
(0,
|
|
19945
|
+
(0, import_node_fs20.readFileSync)((0, import_node_path18.join)((0, import_node_os10.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
19831
19946
|
);
|
|
19832
19947
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
19833
19948
|
if (versions.length === 0) return void 0;
|
|
@@ -19838,7 +19953,7 @@ function installedClaudePluginVersion() {
|
|
|
19838
19953
|
}
|
|
19839
19954
|
function manifestVersion(path2) {
|
|
19840
19955
|
try {
|
|
19841
|
-
const manifest = JSON.parse((0,
|
|
19956
|
+
const manifest = JSON.parse((0, import_node_fs20.readFileSync)(path2, "utf8"));
|
|
19842
19957
|
return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
|
|
19843
19958
|
} catch {
|
|
19844
19959
|
return void 0;
|
|
@@ -19847,12 +19962,12 @@ function manifestVersion(path2) {
|
|
|
19847
19962
|
function readHermesPluginEvidence(env = process.env) {
|
|
19848
19963
|
const host = hermesConfigRoot(env);
|
|
19849
19964
|
const root = hermesPluginRoot(env);
|
|
19850
|
-
const installRecordPresent = (0,
|
|
19851
|
-
const manifestPath = (0,
|
|
19965
|
+
const installRecordPresent = (0, import_node_fs20.existsSync)(root);
|
|
19966
|
+
const manifestPath = (0, import_node_path18.join)(root, "plugin.yaml");
|
|
19852
19967
|
let installedVersion;
|
|
19853
19968
|
let manifest = "missing";
|
|
19854
19969
|
try {
|
|
19855
|
-
const text = (0,
|
|
19970
|
+
const text = (0, import_node_fs20.readFileSync)(manifestPath, "utf8");
|
|
19856
19971
|
let version;
|
|
19857
19972
|
try {
|
|
19858
19973
|
const parsed = JSON.parse(text).version;
|
|
@@ -19866,25 +19981,25 @@ function readHermesPluginEvidence(env = process.env) {
|
|
|
19866
19981
|
if (version) {
|
|
19867
19982
|
installedVersion = version;
|
|
19868
19983
|
manifest = "valid";
|
|
19869
|
-
} else if ((0,
|
|
19984
|
+
} else if ((0, import_node_fs20.existsSync)(manifestPath)) manifest = "invalid";
|
|
19870
19985
|
} catch {
|
|
19871
|
-
if ((0,
|
|
19986
|
+
if ((0, import_node_fs20.existsSync)(manifestPath)) manifest = "invalid";
|
|
19872
19987
|
}
|
|
19873
19988
|
let skills = false;
|
|
19874
19989
|
try {
|
|
19875
|
-
skills = (0,
|
|
19990
|
+
skills = (0, import_node_fs20.existsSync)((0, import_node_path18.join)(root, "skills")) && (0, import_node_fs20.statSync)((0, import_node_path18.join)(root, "skills")).isDirectory();
|
|
19876
19991
|
} catch {
|
|
19877
19992
|
}
|
|
19878
19993
|
let provisioned = false;
|
|
19879
|
-
const provisionedRoot = (0,
|
|
19994
|
+
const provisionedRoot = (0, import_node_path18.join)(host, "skills", "mmi");
|
|
19880
19995
|
try {
|
|
19881
|
-
provisioned = (0,
|
|
19996
|
+
provisioned = (0, import_node_fs20.existsSync)(provisionedRoot) && (0, import_node_fs20.statSync)(provisionedRoot).isDirectory();
|
|
19882
19997
|
} catch {
|
|
19883
19998
|
}
|
|
19884
|
-
const treeOk = (0,
|
|
19999
|
+
const treeOk = (0, import_node_fs20.existsSync)((0, import_node_path18.join)(root, "__init__.py")) && skills && manifest === "valid";
|
|
19885
20000
|
const receipt = treeOk && !provisioned ? `hermes-skills-unprovisioned:${provisionedRoot}` : void 0;
|
|
19886
20001
|
return {
|
|
19887
|
-
hostPresent: (0,
|
|
20002
|
+
hostPresent: (0, import_node_fs20.existsSync)(host),
|
|
19888
20003
|
installRecordPresent,
|
|
19889
20004
|
manifest,
|
|
19890
20005
|
payloadPresent: treeOk && provisioned,
|
|
@@ -19896,7 +20011,7 @@ function installedSurfacePluginVersion(surface) {
|
|
|
19896
20011
|
const token = surfaceToken(surface);
|
|
19897
20012
|
if (token === "hermes") return readHermesPluginEvidence().installedVersion;
|
|
19898
20013
|
if (token === "cursor") {
|
|
19899
|
-
return manifestVersion((0,
|
|
20014
|
+
return manifestVersion((0, import_node_path18.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
19900
20015
|
}
|
|
19901
20016
|
if (token === "jervcode") {
|
|
19902
20017
|
return installedJervCodePackageVersion();
|
|
@@ -19933,13 +20048,13 @@ function worktreeRootSync() {
|
|
|
19933
20048
|
}
|
|
19934
20049
|
var gitignorePath = () => {
|
|
19935
20050
|
const root = worktreeRootSync();
|
|
19936
|
-
return root === null ? null : (0,
|
|
20051
|
+
return root === null ? null : (0, import_node_path18.join)(root, ".gitignore");
|
|
19937
20052
|
};
|
|
19938
20053
|
function readGitignore() {
|
|
19939
20054
|
const path2 = gitignorePath();
|
|
19940
20055
|
if (path2 === null) return null;
|
|
19941
20056
|
try {
|
|
19942
|
-
return (0,
|
|
20057
|
+
return (0, import_node_fs20.readFileSync)(path2, "utf8");
|
|
19943
20058
|
} catch {
|
|
19944
20059
|
return null;
|
|
19945
20060
|
}
|
|
@@ -19948,14 +20063,14 @@ function writeGitignore(content) {
|
|
|
19948
20063
|
const path2 = gitignorePath();
|
|
19949
20064
|
if (path2 === null) return false;
|
|
19950
20065
|
try {
|
|
19951
|
-
(0,
|
|
20066
|
+
(0, import_node_fs20.writeFileSync)(path2, content, "utf8");
|
|
19952
20067
|
return true;
|
|
19953
20068
|
} catch {
|
|
19954
20069
|
return false;
|
|
19955
20070
|
}
|
|
19956
20071
|
}
|
|
19957
20072
|
function lineEndingState(root) {
|
|
19958
|
-
const attributesPresent = (0,
|
|
20073
|
+
const attributesPresent = (0, import_node_fs20.existsSync)((0, import_node_path18.join)(root, ".gitattributes"));
|
|
19959
20074
|
try {
|
|
19960
20075
|
const output = execFileCapture("git", ["-C", root, "ls-files", "--eol", "--", ":(glob)**/*.sh"], {
|
|
19961
20076
|
windowsHide: true
|
|
@@ -19998,18 +20113,18 @@ function compatHolds(range, version) {
|
|
|
19998
20113
|
}
|
|
19999
20114
|
|
|
20000
20115
|
// src/claude-binary-doctor.ts
|
|
20001
|
-
var
|
|
20116
|
+
var import_node_fs21 = require("node:fs");
|
|
20002
20117
|
var import_node_os12 = require("node:os");
|
|
20003
|
-
var
|
|
20118
|
+
var import_node_path20 = require("node:path");
|
|
20004
20119
|
|
|
20005
20120
|
// src/jerv-cli-spawn.ts
|
|
20006
20121
|
var import_node_os11 = require("node:os");
|
|
20007
|
-
var
|
|
20122
|
+
var import_node_path19 = require("node:path");
|
|
20008
20123
|
init_cli_shared();
|
|
20009
|
-
var JERV_CLI_ENTRY = (0,
|
|
20124
|
+
var JERV_CLI_ENTRY = (0, import_node_path19.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
|
|
20010
20125
|
function pathEnvEntries(pathEnv, platform2 = process.platform) {
|
|
20011
20126
|
if (platform2 !== "win32") {
|
|
20012
|
-
return pathEnv.split(
|
|
20127
|
+
return pathEnv.split(import_node_path19.delimiter).map((e) => e.trim()).filter(Boolean);
|
|
20013
20128
|
}
|
|
20014
20129
|
if (pathEnv.includes(";")) {
|
|
20015
20130
|
return pathEnv.split(";").map((e) => e.trim()).filter(Boolean);
|
|
@@ -20042,10 +20157,10 @@ function jervCliCandidateDirs(env = process.env, home = (0, import_node_os11.hom
|
|
|
20042
20157
|
push(normalizeSpawnPathEntry(entry, platform2));
|
|
20043
20158
|
}
|
|
20044
20159
|
if (platform2 === "win32") {
|
|
20045
|
-
if (env.APPDATA) push((0,
|
|
20046
|
-
if (env.LOCALAPPDATA) push((0,
|
|
20160
|
+
if (env.APPDATA) push((0, import_node_path19.join)(env.APPDATA, "npm"));
|
|
20161
|
+
if (env.LOCALAPPDATA) push((0, import_node_path19.join)(env.LOCALAPPDATA, "npm"));
|
|
20047
20162
|
} else {
|
|
20048
|
-
push((0,
|
|
20163
|
+
push((0, import_node_path19.join)(home, ".local", "bin"));
|
|
20049
20164
|
}
|
|
20050
20165
|
return out;
|
|
20051
20166
|
}
|
|
@@ -20098,26 +20213,26 @@ function globalNodeModulesRoots(host) {
|
|
|
20098
20213
|
out.push(dir);
|
|
20099
20214
|
};
|
|
20100
20215
|
const prefix = env.npm_config_prefix?.trim();
|
|
20101
|
-
if (prefix) push(platform2 === "win32" ? (0,
|
|
20216
|
+
if (prefix) push(platform2 === "win32" ? (0, import_node_path20.join)(prefix, "node_modules") : (0, import_node_path20.join)(prefix, "lib", "node_modules"));
|
|
20102
20217
|
for (const dir of jervCliCandidateDirs(env, host.home ?? (0, import_node_os12.homedir)(), platform2)) {
|
|
20103
|
-
push((0,
|
|
20104
|
-
push((0,
|
|
20218
|
+
push((0, import_node_path20.join)(dir, "node_modules"));
|
|
20219
|
+
push((0, import_node_path20.join)((0, import_node_path20.dirname)(dir), "lib", "node_modules"));
|
|
20105
20220
|
}
|
|
20106
20221
|
return out;
|
|
20107
20222
|
}
|
|
20108
20223
|
function readHead(path2) {
|
|
20109
20224
|
let fd;
|
|
20110
20225
|
try {
|
|
20111
|
-
fd = (0,
|
|
20226
|
+
fd = (0, import_node_fs21.openSync)(path2, "r");
|
|
20112
20227
|
const buffer = new Uint8Array(MAGIC_HEAD_BYTES);
|
|
20113
|
-
const read = (0,
|
|
20228
|
+
const read = (0, import_node_fs21.readSync)(fd, buffer, 0, MAGIC_HEAD_BYTES, 0);
|
|
20114
20229
|
return buffer.subarray(0, read);
|
|
20115
20230
|
} catch {
|
|
20116
20231
|
return void 0;
|
|
20117
20232
|
} finally {
|
|
20118
20233
|
if (fd !== void 0) {
|
|
20119
20234
|
try {
|
|
20120
|
-
(0,
|
|
20235
|
+
(0, import_node_fs21.closeSync)(fd);
|
|
20121
20236
|
} catch {
|
|
20122
20237
|
}
|
|
20123
20238
|
}
|
|
@@ -20125,7 +20240,7 @@ function readHead(path2) {
|
|
|
20125
20240
|
}
|
|
20126
20241
|
function fileBytes(path2) {
|
|
20127
20242
|
try {
|
|
20128
|
-
return (0,
|
|
20243
|
+
return (0, import_node_fs21.statSync)(path2).size;
|
|
20129
20244
|
} catch {
|
|
20130
20245
|
return void 0;
|
|
20131
20246
|
}
|
|
@@ -20139,17 +20254,17 @@ function readClaudeBinaryState(host = {}) {
|
|
|
20139
20254
|
const arch = host.arch ?? process.arch;
|
|
20140
20255
|
const magic = EXECUTABLE_MAGIC[platform2];
|
|
20141
20256
|
if (!magic) return void 0;
|
|
20142
|
-
const packageRoot = globalNodeModulesRoots(host).map((root) => (0,
|
|
20257
|
+
const packageRoot = globalNodeModulesRoots(host).map((root) => (0, import_node_path20.join)(root, ...PACKAGE.split("/"))).find((dir) => (0, import_node_fs21.existsSync)((0, import_node_path20.join)(dir, "package.json")));
|
|
20143
20258
|
if (!packageRoot) return void 0;
|
|
20144
20259
|
const keys = platformPackageKeys(platform2, arch);
|
|
20145
20260
|
const fallbackPackage = `${PACKAGE}-${keys[0]}`;
|
|
20146
20261
|
let manifest;
|
|
20147
20262
|
try {
|
|
20148
|
-
manifest = JSON.parse((0,
|
|
20263
|
+
manifest = JSON.parse((0, import_node_fs21.readFileSync)((0, import_node_path20.join)(packageRoot, "package.json"), "utf8"));
|
|
20149
20264
|
} catch (e) {
|
|
20150
20265
|
return {
|
|
20151
20266
|
state: "unreadable",
|
|
20152
|
-
binPath: (0,
|
|
20267
|
+
binPath: (0, import_node_path20.join)(packageRoot, "package.json"),
|
|
20153
20268
|
expectedMagic: magic.name,
|
|
20154
20269
|
platformPackage: fallbackPackage,
|
|
20155
20270
|
error: `package.json could not be read \u2014 ${e.message}`
|
|
@@ -20166,17 +20281,17 @@ function readClaudeBinaryState(host = {}) {
|
|
|
20166
20281
|
error: "package.json declares no `bin` entry \u2014 the executed file cannot be resolved"
|
|
20167
20282
|
};
|
|
20168
20283
|
}
|
|
20169
|
-
const binPath = (0,
|
|
20170
|
-
const binName = (0,
|
|
20284
|
+
const binPath = (0, import_node_path20.join)(packageRoot, binRelative);
|
|
20285
|
+
const binName = (0, import_node_path20.basename)(binRelative);
|
|
20171
20286
|
const optional = manifest.optionalDependencies;
|
|
20172
20287
|
const publishes = (name) => typeof optional === "object" && optional !== null && Object.prototype.hasOwnProperty.call(optional, name);
|
|
20173
20288
|
const published = keys.map((key) => `${PACKAGE}-${key}`).filter(publishes);
|
|
20174
20289
|
if (published.length === 0) return void 0;
|
|
20175
20290
|
const binIn = (name) => [
|
|
20176
|
-
(0,
|
|
20177
|
-
(0,
|
|
20291
|
+
(0, import_node_path20.join)(packageRoot, "node_modules", ...name.split("/"), binName),
|
|
20292
|
+
(0, import_node_path20.join)((0, import_node_path20.dirname)((0, import_node_path20.dirname)(packageRoot)), ...name.split("/"), binName)
|
|
20178
20293
|
];
|
|
20179
|
-
const found = published.map((name) => ({ name, path: binIn(name).find((file) => (0,
|
|
20294
|
+
const found = published.map((name) => ({ name, path: binIn(name).find((file) => (0, import_node_fs21.existsSync)(file)) })).find((c) => c.path);
|
|
20180
20295
|
const platformPackage = found?.name ?? published[0];
|
|
20181
20296
|
let source;
|
|
20182
20297
|
let sourceProblem;
|
|
@@ -20187,7 +20302,7 @@ function readClaudeBinaryState(host = {}) {
|
|
|
20187
20302
|
} else {
|
|
20188
20303
|
source = { path: found.path, bytes: fileBytes(found.path) ?? 0 };
|
|
20189
20304
|
}
|
|
20190
|
-
if (!(0,
|
|
20305
|
+
if (!(0, import_node_fs21.existsSync)(binPath)) {
|
|
20191
20306
|
return { state: "missing", binPath, expectedMagic: magic.name, platformPackage, ...source ? { source } : {}, ...sourceProblem ? { sourceProblem } : {} };
|
|
20192
20307
|
}
|
|
20193
20308
|
const head = readHead(binPath);
|
|
@@ -20230,9 +20345,9 @@ function healClaudeBinary(host = {}, onStep) {
|
|
|
20230
20345
|
const platform2 = host.platform ?? process.platform;
|
|
20231
20346
|
const aside = `${probe.binPath}.stub-${Date.now()}`;
|
|
20232
20347
|
let renamed = false;
|
|
20233
|
-
if ((0,
|
|
20348
|
+
if ((0, import_node_fs21.existsSync)(probe.binPath)) {
|
|
20234
20349
|
try {
|
|
20235
|
-
(0,
|
|
20350
|
+
(0, import_node_fs21.renameSync)(probe.binPath, aside);
|
|
20236
20351
|
renamed = true;
|
|
20237
20352
|
onStep?.(`renamed the stub aside: ${aside}`);
|
|
20238
20353
|
} catch (e) {
|
|
@@ -20241,12 +20356,12 @@ function healClaudeBinary(host = {}, onStep) {
|
|
|
20241
20356
|
}
|
|
20242
20357
|
try {
|
|
20243
20358
|
onStep?.(`copying ${probe.source.path} \u2192 ${probe.binPath} (${(probe.source.bytes / 1e6).toFixed(0)} MB)`);
|
|
20244
|
-
(0,
|
|
20245
|
-
if (platform2 !== "win32") (0,
|
|
20359
|
+
(0, import_node_fs21.copyFileSync)(probe.source.path, probe.binPath);
|
|
20360
|
+
if (platform2 !== "win32") (0, import_node_fs21.chmodSync)(probe.binPath, 493);
|
|
20246
20361
|
} catch (e) {
|
|
20247
20362
|
if (renamed) {
|
|
20248
20363
|
try {
|
|
20249
|
-
(0,
|
|
20364
|
+
(0, import_node_fs21.renameSync)(aside, probe.binPath);
|
|
20250
20365
|
} catch {
|
|
20251
20366
|
return { ok: false, detail: `copy failed (${e.message}) and the stub could not be restored \u2014 the original is at ${aside}` };
|
|
20252
20367
|
}
|
|
@@ -20260,7 +20375,7 @@ function healClaudeBinary(host = {}, onStep) {
|
|
|
20260
20375
|
let kept = false;
|
|
20261
20376
|
if (renamed) {
|
|
20262
20377
|
try {
|
|
20263
|
-
(0,
|
|
20378
|
+
(0, import_node_fs21.rmSync)(aside);
|
|
20264
20379
|
} catch {
|
|
20265
20380
|
kept = true;
|
|
20266
20381
|
}
|
|
@@ -20619,13 +20734,13 @@ function checkLineEndings(probe) {
|
|
|
20619
20734
|
}
|
|
20620
20735
|
|
|
20621
20736
|
// src/release-ledger.ts
|
|
20622
|
-
var
|
|
20623
|
-
var
|
|
20737
|
+
var import_node_fs22 = require("node:fs");
|
|
20738
|
+
var import_node_path22 = require("node:path");
|
|
20624
20739
|
var import_node_crypto5 = require("node:crypto");
|
|
20625
20740
|
|
|
20626
20741
|
// src/file-lock.ts
|
|
20627
20742
|
var import_promises2 = require("node:fs/promises");
|
|
20628
|
-
var
|
|
20743
|
+
var import_node_path21 = require("node:path");
|
|
20629
20744
|
var sleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
20630
20745
|
var IMMEDIATE_RETRY_BUDGET = 3;
|
|
20631
20746
|
var FileLockBusyError = class extends Error {
|
|
@@ -20710,7 +20825,7 @@ async function releaseFileLock(lockPath, guard) {
|
|
|
20710
20825
|
}
|
|
20711
20826
|
async function withFileLock(lockPath, opts, fn) {
|
|
20712
20827
|
const resolved = resolveFileLockOpts(opts);
|
|
20713
|
-
await (0, import_promises2.mkdir)((0,
|
|
20828
|
+
await (0, import_promises2.mkdir)((0, import_node_path21.dirname)(lockPath), { recursive: true }).catch(() => void 0);
|
|
20714
20829
|
const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
|
|
20715
20830
|
try {
|
|
20716
20831
|
return await fn();
|
|
@@ -20879,37 +20994,37 @@ function resolvePrimaryGitDir(cwd) {
|
|
|
20879
20994
|
}
|
|
20880
20995
|
let common;
|
|
20881
20996
|
try {
|
|
20882
|
-
common = (0,
|
|
20997
|
+
common = (0, import_node_fs22.readFileSync)((0, import_node_path22.join)(gitDir, "commondir"), "utf8").trim();
|
|
20883
20998
|
} catch (e) {
|
|
20884
20999
|
if (e.code !== "ENOENT") {
|
|
20885
21000
|
throw new ReleaseLedgerError("unreadable", `release ledger: the worktree gitdir's commondir pointer could not be READ (${e instanceof Error ? e.message : String(e)}) \u2014 a failed read is unverified, not primary (#5987/#4713)`);
|
|
20886
21001
|
}
|
|
20887
21002
|
}
|
|
20888
21003
|
if (!common) return gitDir;
|
|
20889
|
-
const commonDir = (0,
|
|
21004
|
+
const commonDir = (0, import_node_path22.isAbsolute)(common) ? common : (0, import_node_path22.resolve)(gitDir, common);
|
|
20890
21005
|
try {
|
|
20891
|
-
(0,
|
|
21006
|
+
(0, import_node_fs22.readFileSync)((0, import_node_path22.join)(commonDir, "HEAD"), "utf8");
|
|
20892
21007
|
} catch (e) {
|
|
20893
21008
|
throw new ReleaseLedgerError("no-checkout", `release ledger: the worktree's commondir pointer names ${commonDir}, which has no readable HEAD (${e instanceof Error ? e.message : String(e)}) \u2014 the primary checkout may have been removed; refusing to write the ledger into a doomed location`);
|
|
20894
21009
|
}
|
|
20895
21010
|
return commonDir;
|
|
20896
21011
|
}
|
|
20897
21012
|
function releaseLedgerPath(cwd) {
|
|
20898
|
-
return (0,
|
|
21013
|
+
return (0, import_node_path22.join)(resolvePrimaryGitDir(cwd), "mmi-runtime", "release", "phases.json");
|
|
20899
21014
|
}
|
|
20900
21015
|
function atomicWriteJson(path2, ledger) {
|
|
20901
|
-
(0,
|
|
21016
|
+
(0, import_node_fs22.mkdirSync)((0, import_node_path22.dirname)(path2), { recursive: true });
|
|
20902
21017
|
const tmp = `${path2}.tmp-${process.pid}-${(0, import_node_crypto5.randomBytes)(4).toString("hex")}`;
|
|
20903
|
-
(0,
|
|
21018
|
+
(0, import_node_fs22.writeFileSync)(tmp, `${JSON.stringify(ledger, null, 2)}
|
|
20904
21019
|
`, "utf8");
|
|
20905
|
-
(0,
|
|
21020
|
+
(0, import_node_fs22.renameSync)(tmp, path2);
|
|
20906
21021
|
}
|
|
20907
21022
|
async function withReleaseLedger(path2, expected, fn, opts = {}) {
|
|
20908
21023
|
return withFileLock(`${path2}.lock`, { label: "release ledger", maxWaitMs: opts.maxWaitMs ?? 5e3 }, async () => {
|
|
20909
21024
|
let current;
|
|
20910
21025
|
let raw;
|
|
20911
21026
|
try {
|
|
20912
|
-
raw = (0,
|
|
21027
|
+
raw = (0, import_node_fs22.readFileSync)(path2, "utf8");
|
|
20913
21028
|
} catch (e) {
|
|
20914
21029
|
if (e.code !== "ENOENT") {
|
|
20915
21030
|
throw new ReleaseLedgerError("unreadable", `release ledger at ${path2} could not be READ (${e instanceof Error ? e.message : String(e)}) \u2014 a failed read is unverified, not absent (#5987)`);
|
|
@@ -21543,7 +21658,7 @@ async function clearLedgerFile(path2, anchors, maxWaitMs) {
|
|
|
21543
21658
|
return withFileLock(`${path2}.lock`, { label: "release ledger", maxWaitMs }, async () => {
|
|
21544
21659
|
let raw;
|
|
21545
21660
|
try {
|
|
21546
|
-
raw = (0,
|
|
21661
|
+
raw = (0, import_node_fs22.readFileSync)(path2, "utf8");
|
|
21547
21662
|
} catch (e) {
|
|
21548
21663
|
if (e.code === "ENOENT") return { cleared: false };
|
|
21549
21664
|
return { cleared: false, note: `release ledger at ${path2} could not be read for clearing (${e instanceof Error ? e.message : String(e)}) \u2014 left in place` };
|
|
@@ -21559,7 +21674,7 @@ async function clearLedgerFile(path2, anchors, maxWaitMs) {
|
|
|
21559
21674
|
} catch (e) {
|
|
21560
21675
|
return { cleared: false, note: `release ledger at ${path2} belongs to ${ledger.tag} @ ${ledger.tagSha.slice(0, 12)} \u2014 not this release; left in place (${e instanceof Error ? e.message : String(e)})` };
|
|
21561
21676
|
}
|
|
21562
|
-
(0,
|
|
21677
|
+
(0, import_node_fs22.unlinkSync)(path2);
|
|
21563
21678
|
return { cleared: true, note: `cleared the release ledger at ${path2} for the live-proven aborted ${anchors.tag}` };
|
|
21564
21679
|
});
|
|
21565
21680
|
}
|
|
@@ -21662,7 +21777,7 @@ async function healStaleAlignmentLeg(deps, cwd, expected) {
|
|
|
21662
21777
|
return withFileLock(`${path2}.lock`, { label: "release ledger" }, async () => {
|
|
21663
21778
|
let prior;
|
|
21664
21779
|
try {
|
|
21665
|
-
prior = parseReleaseLedger((0,
|
|
21780
|
+
prior = parseReleaseLedger((0, import_node_fs22.readFileSync)(path2, "utf8"));
|
|
21666
21781
|
} catch {
|
|
21667
21782
|
return void 0;
|
|
21668
21783
|
}
|
|
@@ -21687,7 +21802,7 @@ async function supersedeContentCausedDeployLeg(deps, cwd) {
|
|
|
21687
21802
|
return withFileLock(`${path2}.lock`, { label: "release ledger" }, async () => {
|
|
21688
21803
|
let prior;
|
|
21689
21804
|
try {
|
|
21690
|
-
prior = parseReleaseLedger((0,
|
|
21805
|
+
prior = parseReleaseLedger((0, import_node_fs22.readFileSync)(path2, "utf8"));
|
|
21691
21806
|
} catch (e) {
|
|
21692
21807
|
throw new ReleaseLedgerError(
|
|
21693
21808
|
"unreadable",
|
|
@@ -21783,11 +21898,11 @@ async function proveOriginPromotionOrArchived(deps, prior, path2, label, ref) {
|
|
|
21783
21898
|
if (!releaseAbsent) {
|
|
21784
21899
|
throw new Error(`${label}: origin no longer has ${prior.tag} but its GitHub Release still exists \u2014 resolve the mixed external state before closing the ledger; nothing was written (${ref})`);
|
|
21785
21900
|
}
|
|
21786
|
-
const archivePath = (0,
|
|
21787
|
-
if ((0,
|
|
21901
|
+
const archivePath = (0, import_node_path22.join)((0, import_node_path22.dirname)(path2), `phases.archive-${prior.tag}.json`);
|
|
21902
|
+
if ((0, import_node_fs22.existsSync)(archivePath)) {
|
|
21788
21903
|
throw new Error(`${label}: archive already exists at ${archivePath} \u2014 preserving both ledger records; resolve the collision by hand; nothing was written (${ref})`);
|
|
21789
21904
|
}
|
|
21790
|
-
(0,
|
|
21905
|
+
(0, import_node_fs22.renameSync)(path2, archivePath);
|
|
21791
21906
|
return "deleted";
|
|
21792
21907
|
}
|
|
21793
21908
|
async function supersedeContentCausedPublishLeg(deps, cwd) {
|
|
@@ -21795,7 +21910,7 @@ async function supersedeContentCausedPublishLeg(deps, cwd) {
|
|
|
21795
21910
|
return withFileLock(`${path2}.lock`, { label: "release ledger" }, async () => {
|
|
21796
21911
|
let prior;
|
|
21797
21912
|
try {
|
|
21798
|
-
prior = parseReleaseLedger((0,
|
|
21913
|
+
prior = parseReleaseLedger((0, import_node_fs22.readFileSync)(path2, "utf8"));
|
|
21799
21914
|
} catch (e) {
|
|
21800
21915
|
throw new ReleaseLedgerError(
|
|
21801
21916
|
"unreadable",
|
|
@@ -21877,7 +21992,7 @@ async function supersedeContentCausedDeployAndPublishLegs(deps, cwd) {
|
|
|
21877
21992
|
return withFileLock(`${path2}.lock`, { label: "release ledger" }, async () => {
|
|
21878
21993
|
let prior;
|
|
21879
21994
|
try {
|
|
21880
|
-
prior = parseReleaseLedger((0,
|
|
21995
|
+
prior = parseReleaseLedger((0, import_node_fs22.readFileSync)(path2, "utf8"));
|
|
21881
21996
|
} catch (e) {
|
|
21882
21997
|
throw new ReleaseLedgerError(
|
|
21883
21998
|
"unreadable",
|
|
@@ -21982,7 +22097,7 @@ async function supersedeContentCausedDeployAndPublishLegs(deps, cwd) {
|
|
|
21982
22097
|
async function reverifyPriorRunLegs(deps, path2, targetTag) {
|
|
21983
22098
|
let prior;
|
|
21984
22099
|
try {
|
|
21985
|
-
prior = parseReleaseLedger((0,
|
|
22100
|
+
prior = parseReleaseLedger((0, import_node_fs22.readFileSync)(path2, "utf8"));
|
|
21986
22101
|
} catch {
|
|
21987
22102
|
return;
|
|
21988
22103
|
}
|
|
@@ -22002,7 +22117,7 @@ async function beginReleaseLedger(deps, cwd, targetTag) {
|
|
|
22002
22117
|
return withFileLock(`${path2}.lock`, { label: "release ledger" }, async () => {
|
|
22003
22118
|
let raw;
|
|
22004
22119
|
try {
|
|
22005
|
-
raw = (0,
|
|
22120
|
+
raw = (0, import_node_fs22.readFileSync)(path2, "utf8");
|
|
22006
22121
|
} catch (e) {
|
|
22007
22122
|
if (e.code === "ENOENT") return { ok: true };
|
|
22008
22123
|
return { ok: false, error: `the release ledger at ${path2} could not be READ (${e instanceof Error ? e.message : String(e)}) \u2014 a failed read is unverified, not absent (#5987/#4713)` };
|
|
@@ -22039,15 +22154,15 @@ async function beginReleaseLedger(deps, cwd, targetTag) {
|
|
|
22039
22154
|
if (liveSha.toLowerCase() !== prior.tagSha.toLowerCase()) {
|
|
22040
22155
|
return { ok: false, error: `the completed ${prior.tag} ledger says phases-green but origin reports ${liveSha.slice(0, 12) || "no tag"} \u2260 ${prior.tagSha.slice(0, 12)} \u2014 refusing to archive on unproven state; resolve by hand (#5987)` };
|
|
22041
22156
|
}
|
|
22042
|
-
const archivePath = (0,
|
|
22157
|
+
const archivePath = (0, import_node_path22.join)((0, import_node_path22.dirname)(path2), `phases.archive-${prior.tag}.json`);
|
|
22043
22158
|
try {
|
|
22044
|
-
(0,
|
|
22159
|
+
(0, import_node_fs22.unlinkSync)(archivePath);
|
|
22045
22160
|
} catch (e) {
|
|
22046
22161
|
if (e.code !== "ENOENT") {
|
|
22047
22162
|
return { ok: false, error: `could not clear the stale archive at ${archivePath} (${e instanceof Error ? e.message : String(e)}) \u2014 the ledger for ${prior.tag} stays active (#5987)` };
|
|
22048
22163
|
}
|
|
22049
22164
|
}
|
|
22050
|
-
(0,
|
|
22165
|
+
(0, import_node_fs22.renameSync)(path2, archivePath);
|
|
22051
22166
|
return { ok: true, note: `archived the completed release ledger ${prior.tag} to ${archivePath} (live-proven at ${prior.tagSha.slice(0, 12)})` };
|
|
22052
22167
|
});
|
|
22053
22168
|
}
|
|
@@ -22148,10 +22263,10 @@ function readOriginWorkflowsDefault(root, ref) {
|
|
|
22148
22263
|
}
|
|
22149
22264
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
22150
22265
|
async function probeRegistryWhoami(train) {
|
|
22151
|
-
const dir = (0,
|
|
22152
|
-
const rc = (0,
|
|
22266
|
+
const dir = (0, import_node_fs23.mkdtempSync)((0, import_node_path23.join)((0, import_node_os13.tmpdir)(), "mmi-npmrc-"));
|
|
22267
|
+
const rc = (0, import_node_path23.join)(dir, "npmrc");
|
|
22153
22268
|
try {
|
|
22154
|
-
(0,
|
|
22269
|
+
(0, import_node_fs23.writeFileSync)(rc, "//registry.npmjs.org/:_authToken=${NPM_TOKEN}\n");
|
|
22155
22270
|
await train.runSelf(["secrets", "use", "npm/NPM_TOKEN", "--slug", "_org", "--", "npm", "whoami", "--registry", NPM_REGISTRY, "--userconfig", rc]);
|
|
22156
22271
|
return { kind: "ok", detail: "the registry answered a login" };
|
|
22157
22272
|
} catch (e) {
|
|
@@ -22162,7 +22277,7 @@ async function probeRegistryWhoami(train) {
|
|
|
22162
22277
|
if (/\bE401\b|401 Unauthorized/.test(stderr)) return { kind: "rejected", detail: "npm whoami answered 401 Unauthorized" };
|
|
22163
22278
|
return { kind: "unverified", detail: first };
|
|
22164
22279
|
} finally {
|
|
22165
|
-
(0,
|
|
22280
|
+
(0, import_node_fs23.rmSync)(dir, { recursive: true, force: true });
|
|
22166
22281
|
}
|
|
22167
22282
|
}
|
|
22168
22283
|
var REGISTRY_TOKEN_ENV = /\b(NODE_AUTH_TOKEN|NPM_TOKEN)\b/;
|
|
@@ -22180,7 +22295,7 @@ var defaultDeps2 = {
|
|
|
22180
22295
|
ledgerPath: releaseLedgerPath,
|
|
22181
22296
|
readLedgerRaw: (path2) => {
|
|
22182
22297
|
try {
|
|
22183
|
-
return (0,
|
|
22298
|
+
return (0, import_node_fs23.readFileSync)(path2, "utf8");
|
|
22184
22299
|
} catch (e) {
|
|
22185
22300
|
if (e.code === "ENOENT") return void 0;
|
|
22186
22301
|
throw e;
|
|
@@ -22189,7 +22304,7 @@ var defaultDeps2 = {
|
|
|
22189
22304
|
healAlignmentLeg: healStaleAlignmentLeg,
|
|
22190
22305
|
readSurfacesRaw: (cwd) => {
|
|
22191
22306
|
try {
|
|
22192
|
-
return (0,
|
|
22307
|
+
return (0, import_node_fs23.readFileSync)((0, import_node_path23.join)(cwd, "surfaces.json"), "utf8");
|
|
22193
22308
|
} catch {
|
|
22194
22309
|
return void 0;
|
|
22195
22310
|
}
|
|
@@ -22594,6 +22709,53 @@ async function runTrainDoctor(input) {
|
|
|
22594
22709
|
regenerate the projections (\`node scripts/check-skill-payload.mjs\`) and commit the canonical fix; never hand-edit a projection` });
|
|
22595
22710
|
}
|
|
22596
22711
|
}
|
|
22712
|
+
if (isHubControlRepo(repo)) {
|
|
22713
|
+
const verifyDeps = async () => {
|
|
22714
|
+
try {
|
|
22715
|
+
await train.run("node", [`${cwd}/scripts/release-distribution.mjs`, "verify-deps"]);
|
|
22716
|
+
return null;
|
|
22717
|
+
} catch (e) {
|
|
22718
|
+
return message(e);
|
|
22719
|
+
}
|
|
22720
|
+
};
|
|
22721
|
+
const failure = await verifyDeps();
|
|
22722
|
+
if (failure) {
|
|
22723
|
+
const firstDir = /in (cli|updater)\/node_modules/u.exec(failure)?.[1] ?? null;
|
|
22724
|
+
if (!heal || !firstDir) {
|
|
22725
|
+
add({
|
|
22726
|
+
code: "hub-build-deps",
|
|
22727
|
+
severity: "blocker",
|
|
22728
|
+
source: "local",
|
|
22729
|
+
title: `the Hub distribution build dependencies do not resolve${firstDir ? ` in ${firstDir}/node_modules` : ""} \u2014 the release refuses at preflight`,
|
|
22730
|
+
remedy: firstDir ? `run \`mmi-cli devops train doctor --heal\` (installs ${firstDir} from its lockfile), or \`npm --prefix ${firstDir} ci\`` : "run `npm --prefix cli ci` and `npm --prefix updater ci`, then rerun"
|
|
22731
|
+
});
|
|
22732
|
+
} else {
|
|
22733
|
+
const installed = [];
|
|
22734
|
+
let pending = firstDir;
|
|
22735
|
+
let outstanding = failure;
|
|
22736
|
+
let failed = null;
|
|
22737
|
+
while (pending && !installed.includes(pending)) {
|
|
22738
|
+
const dir = pending;
|
|
22739
|
+
try {
|
|
22740
|
+
await train.run("npm", ["--prefix", dir, "ci"]);
|
|
22741
|
+
} catch (e) {
|
|
22742
|
+
failed = { dir, detail: `\`npm --prefix ${dir} ci\` failed: ${message(e)}` };
|
|
22743
|
+
break;
|
|
22744
|
+
}
|
|
22745
|
+
installed.push(dir);
|
|
22746
|
+
outstanding = await verifyDeps();
|
|
22747
|
+
pending = outstanding ? /in (cli|updater)\/node_modules/u.exec(outstanding)?.[1] ?? null : null;
|
|
22748
|
+
}
|
|
22749
|
+
if (failed) {
|
|
22750
|
+
add({ code: "hub-build-deps", severity: "blocker", source: "local", title: failed.detail, remedy: `repair ${failed.dir}/node_modules by hand, then rerun` });
|
|
22751
|
+
} else if (outstanding) {
|
|
22752
|
+
add({ code: "hub-build-deps", severity: "blocker", source: "local", title: `installing ${installed.join(", ")} from ${installed.length === 1 ? "its lockfile" : "their lockfiles"} did not resolve the build dependencies`, remedy: outstanding });
|
|
22753
|
+
} else {
|
|
22754
|
+
add({ code: "hub-build-deps", severity: "healed", source: "local", title: `installed ${installed.join(", ")} from ${installed.length === 1 ? "its lockfile" : "their lockfiles"} \u2014 the Hub distribution build dependencies resolve`, remedy: "nothing \u2014 the release preflight passes" });
|
|
22755
|
+
}
|
|
22756
|
+
}
|
|
22757
|
+
}
|
|
22758
|
+
}
|
|
22597
22759
|
const branches = TRAIN_BRANCHES.filter((b) => b !== "rc" || track === "full");
|
|
22598
22760
|
if (heal) {
|
|
22599
22761
|
const sync = await syncLocalTrainBranches(train.run, { branches, fetch: false });
|
|
@@ -22689,34 +22851,34 @@ function formatTrainDoctor(v) {
|
|
|
22689
22851
|
// src/train-apply-phases.ts
|
|
22690
22852
|
var TRAIN_BUMP_INTENTS = ["major", "minor", "patch"];
|
|
22691
22853
|
function readLocalGateWorkflows() {
|
|
22692
|
-
const dir = (0,
|
|
22854
|
+
const dir = (0, import_node_path24.join)(".github", "workflows");
|
|
22693
22855
|
let names;
|
|
22694
22856
|
try {
|
|
22695
|
-
names = (0,
|
|
22857
|
+
names = (0, import_node_fs24.readdirSync)(dir);
|
|
22696
22858
|
} catch {
|
|
22697
22859
|
return null;
|
|
22698
22860
|
}
|
|
22699
22861
|
const files = [];
|
|
22700
22862
|
for (const name of names.filter(isGateWorkflowPath)) {
|
|
22701
22863
|
try {
|
|
22702
|
-
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0,
|
|
22864
|
+
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs24.readFileSync)((0, import_node_path24.join)(dir, name), "utf8") });
|
|
22703
22865
|
} catch {
|
|
22704
22866
|
}
|
|
22705
22867
|
}
|
|
22706
22868
|
return files;
|
|
22707
22869
|
}
|
|
22708
22870
|
function readLocalWorkflows() {
|
|
22709
|
-
const dir = (0,
|
|
22871
|
+
const dir = (0, import_node_path24.join)(".github", "workflows");
|
|
22710
22872
|
let names;
|
|
22711
22873
|
try {
|
|
22712
|
-
names = (0,
|
|
22874
|
+
names = (0, import_node_fs24.readdirSync)(dir);
|
|
22713
22875
|
} catch {
|
|
22714
22876
|
return null;
|
|
22715
22877
|
}
|
|
22716
22878
|
const files = [];
|
|
22717
22879
|
for (const name of names.filter((n) => /\.ya?ml$/i.test(n))) {
|
|
22718
22880
|
try {
|
|
22719
|
-
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0,
|
|
22881
|
+
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs24.readFileSync)((0, import_node_path24.join)(dir, name), "utf8") });
|
|
22720
22882
|
} catch {
|
|
22721
22883
|
}
|
|
22722
22884
|
}
|
|
@@ -23506,6 +23668,11 @@ ${recovery.note}`), recoveryInput);
|
|
|
23506
23668
|
});
|
|
23507
23669
|
let dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
|
|
23508
23670
|
if (publishSkipNote) dispatch = { ...dispatch, note: `${dispatch.note}; tenant-publish.yml skipped (${publishSkipNote})` };
|
|
23671
|
+
dispatch = await appendServedChannelPublish(
|
|
23672
|
+
deps,
|
|
23673
|
+
{ repo: ctx.repo, publishStatus: publishLeg.state === "complete" ? "success" : "failure", watch },
|
|
23674
|
+
dispatch
|
|
23675
|
+
);
|
|
23509
23676
|
const announcing = announceShippedRelease(
|
|
23510
23677
|
deps,
|
|
23511
23678
|
{ repo: ctx.repo, tag, summaryFile: options.announceSummaryFile },
|
|
@@ -23562,7 +23729,7 @@ async function activeRcandLedgerCandidate(deps, cwd) {
|
|
|
23562
23729
|
}
|
|
23563
23730
|
let prior;
|
|
23564
23731
|
try {
|
|
23565
|
-
prior = parseReleaseLedger((0,
|
|
23732
|
+
prior = parseReleaseLedger((0, import_node_fs24.readFileSync)(path2, "utf8"));
|
|
23566
23733
|
} catch {
|
|
23567
23734
|
return void 0;
|
|
23568
23735
|
}
|
|
@@ -26988,9 +27155,9 @@ function registerBoardCommands(program3) {
|
|
|
26988
27155
|
}
|
|
26989
27156
|
|
|
26990
27157
|
// src/bootstrap-commands.ts
|
|
26991
|
-
var
|
|
27158
|
+
var import_node_fs26 = require("node:fs");
|
|
26992
27159
|
var import_node_os14 = require("node:os");
|
|
26993
|
-
var
|
|
27160
|
+
var import_node_path26 = require("node:path");
|
|
26994
27161
|
init_cli_shared();
|
|
26995
27162
|
init_clean_exit();
|
|
26996
27163
|
|
|
@@ -27170,8 +27337,8 @@ init_github_client();
|
|
|
27170
27337
|
init_cli_shared();
|
|
27171
27338
|
|
|
27172
27339
|
// src/port-registry.ts
|
|
27173
|
-
var
|
|
27174
|
-
var
|
|
27340
|
+
var import_node_fs25 = require("node:fs");
|
|
27341
|
+
var import_node_path25 = require("node:path");
|
|
27175
27342
|
|
|
27176
27343
|
// ../infra/port-geometry.mjs
|
|
27177
27344
|
var PORT_BLOCK = 100;
|
|
@@ -27185,8 +27352,8 @@ function nextPortBlock(registry2) {
|
|
|
27185
27352
|
return [base, base + PORT_SPAN];
|
|
27186
27353
|
}
|
|
27187
27354
|
function loadPortRegistry(path2) {
|
|
27188
|
-
if (!(0,
|
|
27189
|
-
const raw = JSON.parse((0,
|
|
27355
|
+
if (!(0, import_node_fs25.existsSync)(path2)) return {};
|
|
27356
|
+
const raw = JSON.parse((0, import_node_fs25.readFileSync)(path2, "utf8"));
|
|
27190
27357
|
const out = {};
|
|
27191
27358
|
for (const [key, value] of Object.entries(raw)) {
|
|
27192
27359
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -27200,9 +27367,9 @@ function ensurePortRange(repo, path2) {
|
|
|
27200
27367
|
const existing = registry2[repo];
|
|
27201
27368
|
if (existing) return existing;
|
|
27202
27369
|
const range = nextPortBlock(registry2);
|
|
27203
|
-
const raw = (0,
|
|
27370
|
+
const raw = (0, import_node_fs25.existsSync)(path2) ? JSON.parse((0, import_node_fs25.readFileSync)(path2, "utf8")) : {};
|
|
27204
27371
|
raw[repo] = range;
|
|
27205
|
-
(0,
|
|
27372
|
+
(0, import_node_fs25.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
|
|
27206
27373
|
return range;
|
|
27207
27374
|
}
|
|
27208
27375
|
function portCursorSeed(registry2) {
|
|
@@ -27224,22 +27391,22 @@ function existingPortRange(repo, registry2) {
|
|
|
27224
27391
|
return registry2[repo] ?? null;
|
|
27225
27392
|
}
|
|
27226
27393
|
function portRangeInfraAt(root, source) {
|
|
27227
|
-
const registryPath = (0,
|
|
27228
|
-
const ddbScriptPath = (0,
|
|
27229
|
-
if (!(0,
|
|
27394
|
+
const registryPath = (0, import_node_path25.join)(root, "infra", "port-ranges.json");
|
|
27395
|
+
const ddbScriptPath = (0, import_node_path25.join)(root, "infra", "port-ddb.mjs");
|
|
27396
|
+
if (!(0, import_node_fs25.existsSync)(registryPath) || !(0, import_node_fs25.existsSync)(ddbScriptPath)) return null;
|
|
27230
27397
|
return { root, source, registryPath, ddbScriptPath };
|
|
27231
27398
|
}
|
|
27232
27399
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
27233
27400
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
27234
27401
|
if (direct) return direct;
|
|
27235
|
-
for (let dir = cwd; ; dir = (0,
|
|
27236
|
-
const sibling = portRangeInfraAt((0,
|
|
27402
|
+
for (let dir = cwd; ; dir = (0, import_node_path25.dirname)(dir)) {
|
|
27403
|
+
const sibling = portRangeInfraAt((0, import_node_path25.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
27237
27404
|
if (sibling) return sibling;
|
|
27238
|
-
const parent = (0,
|
|
27405
|
+
const parent = (0, import_node_path25.dirname)(dir);
|
|
27239
27406
|
if (parent === dir) break;
|
|
27240
27407
|
}
|
|
27241
27408
|
if (packageDir) {
|
|
27242
|
-
const pkgRoot = (0,
|
|
27409
|
+
const pkgRoot = (0, import_node_path25.join)(packageDir, "..", "..");
|
|
27243
27410
|
const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
|
|
27244
27411
|
if (pkgFrom) return pkgFrom;
|
|
27245
27412
|
}
|
|
@@ -28725,13 +28892,13 @@ function registerBootstrapCommands(program3) {
|
|
|
28725
28892
|
deployFactsRead: deployFacts !== null,
|
|
28726
28893
|
pushAllowlistOwners: verifyOwners,
|
|
28727
28894
|
pushAllowlistOwnersRead: verifyOwnersRead,
|
|
28728
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
28895
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs26.existsSync)(path2) ? (0, import_node_fs26.readFileSync)(path2, "utf8") : null,
|
|
28729
28896
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
28730
28897
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
28731
28898
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
28732
28899
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
28733
28900
|
// sanction, which is the pre-#3664 behaviour.
|
|
28734
|
-
sanctionedAdmins: (0,
|
|
28901
|
+
sanctionedAdmins: (0, import_node_fs26.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs26.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
28735
28902
|
requiredGcpApis: (() => {
|
|
28736
28903
|
const v = meta?.requiredGcpApis;
|
|
28737
28904
|
if (Array.isArray(v)) return v;
|
|
@@ -28809,14 +28976,14 @@ function registerBootstrapCommands(program3) {
|
|
|
28809
28976
|
bootstrap.command("drift").description("#3818: compare every org-owned whole-file seed against MMI-Hub's copy across the registry roster; read-only").option("--repo <owner/repo>", "audit one repo instead of the roster (never a fleet verdict)").option("--json", "machine-readable output").action(async () => {
|
|
28810
28977
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
28811
28978
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
28812
|
-
if (!(0,
|
|
28979
|
+
if (!(0, import_node_fs26.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
|
|
28813
28980
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
28814
28981
|
if (!seedSource.ok) return fail(`bootstrap drift: ${seedSource.reason}`);
|
|
28815
|
-
const manifest = loadBootstrapSeeds((0,
|
|
28982
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
|
|
28816
28983
|
const hubContents = /* @__PURE__ */ new Map();
|
|
28817
28984
|
for (const s of manifest.seeds) {
|
|
28818
28985
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
28819
|
-
hubContents.set(s.target, (0,
|
|
28986
|
+
hubContents.set(s.target, (0, import_node_fs26.existsSync)(s.target) ? (0, import_node_fs26.readFileSync)(s.target, "utf8") : null);
|
|
28820
28987
|
}
|
|
28821
28988
|
let targets;
|
|
28822
28989
|
let classOf = (_repo) => "deployable";
|
|
@@ -28979,10 +29146,10 @@ function registerBootstrapCommands(program3) {
|
|
|
28979
29146
|
return;
|
|
28980
29147
|
}
|
|
28981
29148
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
28982
|
-
if (!(0,
|
|
29149
|
+
if (!(0, import_node_fs26.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
|
|
28983
29150
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
28984
29151
|
if (!seedSource.ok) return fail(`bootstrap apply: ${seedSource.reason}`);
|
|
28985
|
-
const manifest = loadBootstrapSeeds((0,
|
|
29152
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
|
|
28986
29153
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
28987
29154
|
const slug = parsedRepo.slug;
|
|
28988
29155
|
const onlyTarget = o.only.trim();
|
|
@@ -28994,16 +29161,16 @@ function registerBootstrapCommands(program3) {
|
|
|
28994
29161
|
}
|
|
28995
29162
|
const onlyManagedBlock = onlyTarget ? seedsToApply[0]?.managedBlock != null : false;
|
|
28996
29163
|
const gh = async (args) => execFileP("gh", args, { timeout: 2e4 });
|
|
28997
|
-
const readFile9 = (p) => (0,
|
|
29164
|
+
const readFile9 = (p) => (0, import_node_fs26.existsSync)(p) ? (0, import_node_fs26.readFileSync)(p, "utf8") : null;
|
|
28998
29165
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
28999
29166
|
const putSeed = async (target, content, ref, sha) => {
|
|
29000
|
-
const tmp = (0,
|
|
29001
|
-
(0,
|
|
29167
|
+
const tmp = (0, import_node_path26.join)((0, import_node_os14.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
29168
|
+
(0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
29002
29169
|
try {
|
|
29003
29170
|
await gh(contentPutInputArgs(repo, target, tmp));
|
|
29004
29171
|
} finally {
|
|
29005
29172
|
try {
|
|
29006
|
-
(0,
|
|
29173
|
+
(0, import_node_fs26.unlinkSync)(tmp);
|
|
29007
29174
|
} catch {
|
|
29008
29175
|
}
|
|
29009
29176
|
}
|
|
@@ -29452,10 +29619,10 @@ LIVE apply to ${repo}:
|
|
|
29452
29619
|
bootstrap.command("propagate").description("#4238: re-entrant canary\u2192wave tick \u2014 plan (or, with --execute, open) per-repo PRs fanning an org-owned seed out to the fleet").option("--target <path>", "the manifest target to propagate (an org-owned whole file or declared Hub-managed block)").option("--execute", "LIVE tick via gh (master-gated) \u2014 opens/reuses per-repo seed-propagate PRs; dry-run prints the plan only").option("--json", "machine-readable output").action(async () => {
|
|
29453
29620
|
const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
|
|
29454
29621
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
29455
|
-
if (!(0,
|
|
29622
|
+
if (!(0, import_node_fs26.existsSync)(manifestPath)) return fail(`bootstrap propagate: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the desired state this tick propagates`);
|
|
29456
29623
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
29457
29624
|
if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
|
|
29458
|
-
const manifest = loadBootstrapSeeds((0,
|
|
29625
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
|
|
29459
29626
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
29460
29627
|
if (!o.target) {
|
|
29461
29628
|
return fail(`bootstrap propagate: --target <path> is required \u2014 one of:
|
|
@@ -29464,9 +29631,9 @@ LIVE apply to ${repo}:
|
|
|
29464
29631
|
const seed = propagatable.find((s) => s.target === o.target);
|
|
29465
29632
|
if (!seed) return fail(`bootstrap propagate: --target '${o.target}' names no centrally propagatable seed in ${manifestPath}. Propagatable targets:
|
|
29466
29633
|
${propagatable.map((s) => s.target).join("\n ")}`);
|
|
29467
|
-
if (!seed.managedBlock && !(0,
|
|
29468
|
-
const hubContent = seed.managedBlock ? null : (0,
|
|
29469
|
-
const readSeedFile2 = (path2) => (0,
|
|
29634
|
+
if (!seed.managedBlock && !(0, import_node_fs26.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
|
|
29635
|
+
const hubContent = seed.managedBlock ? null : (0, import_node_fs26.readFileSync)(seed.target, "utf8");
|
|
29636
|
+
const readSeedFile2 = (path2) => (0, import_node_fs26.existsSync)(path2) ? (0, import_node_fs26.readFileSync)(path2, "utf8") : null;
|
|
29470
29637
|
const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
|
|
29471
29638
|
const cfg = await loadConfig();
|
|
29472
29639
|
const projects = await fetchProjectsList(registryClientDeps(cfg));
|
|
@@ -29475,9 +29642,9 @@ LIVE apply to ${repo}:
|
|
|
29475
29642
|
}
|
|
29476
29643
|
const rosterRepos = collectRegistryRepos(projects).filter((r) => r.toLowerCase() !== "mutmutco/mmi-hub");
|
|
29477
29644
|
let independentCount = rosterRepos.length;
|
|
29478
|
-
if ((0,
|
|
29645
|
+
if ((0, import_node_fs26.existsSync)("projects.json")) {
|
|
29479
29646
|
try {
|
|
29480
|
-
const local = JSON.parse((0,
|
|
29647
|
+
const local = JSON.parse((0, import_node_fs26.readFileSync)("projects.json", "utf8"));
|
|
29481
29648
|
const localRepos = /* @__PURE__ */ new Set();
|
|
29482
29649
|
for (const p of local.projects ?? []) for (const r of p.repos ?? []) {
|
|
29483
29650
|
const full = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
|
|
@@ -29638,15 +29805,15 @@ LIVE apply to ${repo}:
|
|
|
29638
29805
|
} catch {
|
|
29639
29806
|
existingSha = void 0;
|
|
29640
29807
|
}
|
|
29641
|
-
const tmp = (0,
|
|
29808
|
+
const tmp = (0, import_node_path26.join)((0, import_node_os14.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
29642
29809
|
const desiredContent = desiredByRepo.get(rec.repo);
|
|
29643
29810
|
if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
|
|
29644
|
-
(0,
|
|
29811
|
+
(0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha)), "utf8");
|
|
29645
29812
|
try {
|
|
29646
29813
|
await gh(contentPutInputArgs(rec.repo, seed.target, tmp));
|
|
29647
29814
|
} finally {
|
|
29648
29815
|
try {
|
|
29649
|
-
(0,
|
|
29816
|
+
(0, import_node_fs26.unlinkSync)(tmp);
|
|
29650
29817
|
} catch {
|
|
29651
29818
|
}
|
|
29652
29819
|
}
|
|
@@ -29713,10 +29880,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
29713
29880
|
return fail(`bootstrap rollback: ${e.message}`);
|
|
29714
29881
|
}
|
|
29715
29882
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
29716
|
-
if (!(0,
|
|
29883
|
+
if (!(0, import_node_fs26.existsSync)(manifestPath)) return fail(`bootstrap rollback: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the manifest names which targets are org-owned and therefore propagated (and rollback-able)`);
|
|
29717
29884
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
29718
29885
|
if (!seedSource.ok) return fail(`bootstrap rollback: ${seedSource.reason}`);
|
|
29719
|
-
const manifest = loadBootstrapSeeds((0,
|
|
29886
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
|
|
29720
29887
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
29721
29888
|
if (!o.target) {
|
|
29722
29889
|
return fail(`bootstrap rollback: --target <path> is required \u2014 one of:
|
|
@@ -29733,10 +29900,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
29733
29900
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
29734
29901
|
let candidates;
|
|
29735
29902
|
if (o.record) {
|
|
29736
|
-
if (!(0,
|
|
29903
|
+
if (!(0, import_node_fs26.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
|
|
29737
29904
|
let parsed;
|
|
29738
29905
|
try {
|
|
29739
|
-
parsed = JSON.parse((0,
|
|
29906
|
+
parsed = JSON.parse((0, import_node_fs26.readFileSync)(o.record, "utf8"));
|
|
29740
29907
|
} catch (e) {
|
|
29741
29908
|
return fail(`bootstrap rollback: --record '${o.record}' is not valid JSON: ${e.message}`);
|
|
29742
29909
|
}
|
|
@@ -29813,13 +29980,13 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
29813
29980
|
} catch {
|
|
29814
29981
|
existingSha = void 0;
|
|
29815
29982
|
}
|
|
29816
|
-
const tmp = (0,
|
|
29817
|
-
(0,
|
|
29983
|
+
const tmp = (0, import_node_path26.join)((0, import_node_os14.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
29984
|
+
(0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
|
|
29818
29985
|
try {
|
|
29819
29986
|
await gh(contentPutInputArgs(repo, seed.target, tmp));
|
|
29820
29987
|
} finally {
|
|
29821
29988
|
try {
|
|
29822
|
-
(0,
|
|
29989
|
+
(0, import_node_fs26.unlinkSync)(tmp);
|
|
29823
29990
|
} catch {
|
|
29824
29991
|
}
|
|
29825
29992
|
}
|
|
@@ -30886,7 +31053,7 @@ function renderDeployPortDoctor(report) {
|
|
|
30886
31053
|
init_cli_shared();
|
|
30887
31054
|
|
|
30888
31055
|
// src/wave-status.ts
|
|
30889
|
-
var
|
|
31056
|
+
var import_node_fs29 = require("node:fs");
|
|
30890
31057
|
|
|
30891
31058
|
// src/git-worktree-observation.ts
|
|
30892
31059
|
function parseGitWorktreePorcelain(text) {
|
|
@@ -30917,8 +31084,8 @@ function deriveComposeProjectName(worktreePath) {
|
|
|
30917
31084
|
|
|
30918
31085
|
// src/stage-runner.ts
|
|
30919
31086
|
var import_node_child_process10 = require("node:child_process");
|
|
30920
|
-
var
|
|
30921
|
-
var
|
|
31087
|
+
var import_node_fs28 = require("node:fs");
|
|
31088
|
+
var import_node_path28 = require("node:path");
|
|
30922
31089
|
var import_node_net = require("node:net");
|
|
30923
31090
|
var import_node_util5 = require("node:util");
|
|
30924
31091
|
|
|
@@ -30928,8 +31095,8 @@ function normalizeEol2(s) {
|
|
|
30928
31095
|
}
|
|
30929
31096
|
|
|
30930
31097
|
// src/env-detection.ts
|
|
30931
|
-
var
|
|
30932
|
-
var
|
|
31098
|
+
var import_node_fs27 = require("node:fs");
|
|
31099
|
+
var import_node_path27 = require("node:path");
|
|
30933
31100
|
var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".jerv", ".pi", ".venv", "venv"]);
|
|
30934
31101
|
function isEnvBaseName(name) {
|
|
30935
31102
|
return name.toLowerCase() === ".env";
|
|
@@ -30939,7 +31106,7 @@ function findEnvFiles(root) {
|
|
|
30939
31106
|
const walk2 = (dir, rel) => {
|
|
30940
31107
|
let entries;
|
|
30941
31108
|
try {
|
|
30942
|
-
entries = (0,
|
|
31109
|
+
entries = (0, import_node_fs27.readdirSync)(dir, { withFileTypes: true });
|
|
30943
31110
|
} catch {
|
|
30944
31111
|
return;
|
|
30945
31112
|
}
|
|
@@ -30947,13 +31114,13 @@ function findEnvFiles(root) {
|
|
|
30947
31114
|
const relPath = rel ? `${rel}/${entry.name}` : entry.name;
|
|
30948
31115
|
if (entry.isDirectory()) {
|
|
30949
31116
|
if (SKIPPED_DIRS.has(entry.name)) continue;
|
|
30950
|
-
walk2((0,
|
|
31117
|
+
walk2((0, import_node_path27.join)(dir, entry.name), relPath);
|
|
30951
31118
|
continue;
|
|
30952
31119
|
}
|
|
30953
31120
|
if (isEnvBaseName(entry.name)) {
|
|
30954
31121
|
let isDir = false;
|
|
30955
31122
|
try {
|
|
30956
|
-
isDir = (0,
|
|
31123
|
+
isDir = (0, import_node_fs27.lstatSync)((0, import_node_path27.join)(dir, entry.name)).isDirectory();
|
|
30957
31124
|
} catch {
|
|
30958
31125
|
}
|
|
30959
31126
|
if (!isDir) found.push(relPath);
|
|
@@ -31015,12 +31182,12 @@ var DOCKER_TIMEOUT_MS = 15e3;
|
|
|
31015
31182
|
function stageBash() {
|
|
31016
31183
|
if (process.platform !== "win32") return "bash";
|
|
31017
31184
|
const configured = process.env.SHELL;
|
|
31018
|
-
if (configured && (0,
|
|
31185
|
+
if (configured && (0, import_node_path28.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs28.existsSync)(configured)) return configured;
|
|
31019
31186
|
const candidates = [
|
|
31020
|
-
(0,
|
|
31021
|
-
...process.env.LOCALAPPDATA ? [(0,
|
|
31187
|
+
(0, import_node_path28.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
|
|
31188
|
+
...process.env.LOCALAPPDATA ? [(0, import_node_path28.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
|
|
31022
31189
|
];
|
|
31023
|
-
const bash = candidates.find((path2) => (0,
|
|
31190
|
+
const bash = candidates.find((path2) => (0, import_node_fs28.existsSync)(path2));
|
|
31024
31191
|
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.");
|
|
31025
31192
|
return bash;
|
|
31026
31193
|
}
|
|
@@ -31151,7 +31318,7 @@ function appendForceRecreate(up) {
|
|
|
31151
31318
|
return `${up.trimEnd()} --force-recreate`;
|
|
31152
31319
|
}
|
|
31153
31320
|
function stageStatePath(cwd = process.cwd()) {
|
|
31154
|
-
return (0,
|
|
31321
|
+
return (0, import_node_path28.join)(cwd, "tmp", "stage", "state.json");
|
|
31155
31322
|
}
|
|
31156
31323
|
function normPath(path2) {
|
|
31157
31324
|
return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
@@ -31368,8 +31535,8 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
31368
31535
|
}
|
|
31369
31536
|
function composeResolvesPort(cwd) {
|
|
31370
31537
|
if (process.env.PORT) return true;
|
|
31371
|
-
const envFile = (0,
|
|
31372
|
-
return (0,
|
|
31538
|
+
const envFile = (0, import_node_path28.join)(cwd, ".env");
|
|
31539
|
+
return (0, import_node_fs28.existsSync)(envFile) && envFileKeys((0, import_node_fs28.readFileSync)(envFile, "utf8")).has("PORT");
|
|
31373
31540
|
}
|
|
31374
31541
|
function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
31375
31542
|
return {
|
|
@@ -31382,13 +31549,13 @@ function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
|
31382
31549
|
function stageComposeFiles(cwd) {
|
|
31383
31550
|
const files = ["docker-compose.yml"];
|
|
31384
31551
|
for (const conventional of ["docker-compose.override.yml", "docker-compose.override.yaml"]) {
|
|
31385
|
-
if ((0,
|
|
31552
|
+
if ((0, import_node_fs28.existsSync)((0, import_node_path28.join)(cwd, conventional))) files.push(conventional);
|
|
31386
31553
|
}
|
|
31387
31554
|
return [...files, RUNTIME_ENV_OVERRIDE_RELPATH];
|
|
31388
31555
|
}
|
|
31389
31556
|
async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
31390
31557
|
const keys = Object.keys(vaultEnvMerge ?? {});
|
|
31391
|
-
if (!keys.length || !(0,
|
|
31558
|
+
if (!keys.length || !(0, import_node_fs28.existsSync)((0, import_node_path28.join)(cwd, "docker-compose.yml"))) return {};
|
|
31392
31559
|
let configJson;
|
|
31393
31560
|
try {
|
|
31394
31561
|
const { stdout } = await execFileP3("docker", ["compose", "config", "--format", "json"], {
|
|
@@ -31404,21 +31571,21 @@ async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
|
31404
31571
|
}
|
|
31405
31572
|
const body = renderRuntimeEnvOverride(runtimeEnvMarkedServices(configJson), keys);
|
|
31406
31573
|
if (!body) return {};
|
|
31407
|
-
const overridePath = (0,
|
|
31408
|
-
(0,
|
|
31409
|
-
(0,
|
|
31574
|
+
const overridePath = (0, import_node_path28.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
|
|
31575
|
+
(0, import_node_fs28.mkdirSync)((0, import_node_path28.join)(cwd, "tmp", "stage"), { recursive: true });
|
|
31576
|
+
(0, import_node_fs28.writeFileSync)(overridePath, body, "utf8");
|
|
31410
31577
|
return stageComposeFileEnv(stageComposeFiles(cwd));
|
|
31411
31578
|
}
|
|
31412
31579
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
31413
31580
|
if (!config.ensureEnv) return;
|
|
31414
|
-
const target = (0,
|
|
31415
|
-
const example = (0,
|
|
31416
|
-
if (!(0,
|
|
31417
|
-
(0,
|
|
31418
|
-
} else if ((0,
|
|
31419
|
-
const stale = detectStaleEnvFile((0,
|
|
31420
|
-
exampleMtimeMs: (0,
|
|
31421
|
-
targetMtimeMs: (0,
|
|
31581
|
+
const target = (0, import_node_path28.join)(cwd, config.ensureEnv.target);
|
|
31582
|
+
const example = (0, import_node_path28.join)(cwd, config.ensureEnv.example);
|
|
31583
|
+
if (!(0, import_node_fs28.existsSync)(target) && (0, import_node_fs28.existsSync)(example)) {
|
|
31584
|
+
(0, import_node_fs28.copyFileSync)(example, target);
|
|
31585
|
+
} else if ((0, import_node_fs28.existsSync)(target) && (0, import_node_fs28.existsSync)(example)) {
|
|
31586
|
+
const stale = detectStaleEnvFile((0, import_node_fs28.readFileSync)(example, "utf8"), (0, import_node_fs28.readFileSync)(target, "utf8"), {
|
|
31587
|
+
exampleMtimeMs: (0, import_node_fs28.statSync)(example).mtimeMs,
|
|
31588
|
+
targetMtimeMs: (0, import_node_fs28.statSync)(target).mtimeMs
|
|
31422
31589
|
});
|
|
31423
31590
|
if (stale) {
|
|
31424
31591
|
const msg = `stale ${config.ensureEnv.target} (${stale}) \u2014 delete it or refresh from ${config.ensureEnv.example} before re-running /stage`;
|
|
@@ -31426,8 +31593,8 @@ async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
|
31426
31593
|
console.error(`mmi-cli stage: ${msg} (allowed via --allow-stale-env)`);
|
|
31427
31594
|
}
|
|
31428
31595
|
}
|
|
31429
|
-
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0,
|
|
31430
|
-
(0,
|
|
31596
|
+
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0, import_node_fs28.existsSync)(target)) {
|
|
31597
|
+
(0, import_node_fs28.writeFileSync)(target, mergeEnvSecretsIntoFile((0, import_node_fs28.readFileSync)(target, "utf8"), opts.vaultEnvMerge), "utf8");
|
|
31431
31598
|
}
|
|
31432
31599
|
}
|
|
31433
31600
|
async function gitText(cwd, args) {
|
|
@@ -31457,20 +31624,20 @@ async function resolveGlobalStatePath(cwd, explicit) {
|
|
|
31457
31624
|
return void 0;
|
|
31458
31625
|
}
|
|
31459
31626
|
function readState(path2) {
|
|
31460
|
-
if (!(0,
|
|
31627
|
+
if (!(0, import_node_fs28.existsSync)(path2)) return null;
|
|
31461
31628
|
try {
|
|
31462
|
-
return JSON.parse((0,
|
|
31629
|
+
return JSON.parse((0, import_node_fs28.readFileSync)(path2, "utf8"));
|
|
31463
31630
|
} catch {
|
|
31464
31631
|
return null;
|
|
31465
31632
|
}
|
|
31466
31633
|
}
|
|
31467
31634
|
function mkdirFor(path2) {
|
|
31468
31635
|
const dir = path2.slice(0, Math.max(path2.lastIndexOf("/"), path2.lastIndexOf("\\")));
|
|
31469
|
-
(0,
|
|
31636
|
+
(0, import_node_fs28.mkdirSync)(dir, { recursive: true });
|
|
31470
31637
|
}
|
|
31471
31638
|
function writeState(path2, state) {
|
|
31472
31639
|
mkdirFor(path2);
|
|
31473
|
-
(0,
|
|
31640
|
+
(0, import_node_fs28.writeFileSync)(path2, JSON.stringify(state, null, 2), "utf8");
|
|
31474
31641
|
}
|
|
31475
31642
|
function writeStagePortReservation(port, cwd, statePath, globalStatePath, now) {
|
|
31476
31643
|
const reservation = {
|
|
@@ -31499,7 +31666,7 @@ async function cleanupStageState(state, paths, timeoutMs, fallbackCwd, currentEn
|
|
|
31499
31666
|
);
|
|
31500
31667
|
}
|
|
31501
31668
|
for (const path2 of [...new Set(paths.filter((p) => Boolean(p)))]) {
|
|
31502
|
-
(0,
|
|
31669
|
+
(0, import_node_fs28.rmSync)(path2, { force: true });
|
|
31503
31670
|
}
|
|
31504
31671
|
}
|
|
31505
31672
|
async function killTree(pid) {
|
|
@@ -31686,8 +31853,8 @@ async function runStage(config = {}, opts = {}) {
|
|
|
31686
31853
|
});
|
|
31687
31854
|
}
|
|
31688
31855
|
} catch (e) {
|
|
31689
|
-
(0,
|
|
31690
|
-
if (globalStatePath && globalStatePath !== statePath) (0,
|
|
31856
|
+
(0, import_node_fs28.rmSync)(statePath, { force: true });
|
|
31857
|
+
if (globalStatePath && globalStatePath !== statePath) (0, import_node_fs28.rmSync)(globalStatePath, { force: true });
|
|
31691
31858
|
throw e;
|
|
31692
31859
|
}
|
|
31693
31860
|
const started = await startStage(config, {
|
|
@@ -31704,9 +31871,9 @@ async function runStage(config = {}, opts = {}) {
|
|
|
31704
31871
|
// src/wave-status.ts
|
|
31705
31872
|
function readStageSummary(worktreePath) {
|
|
31706
31873
|
const statePath = stageStatePath(worktreePath);
|
|
31707
|
-
if (!(0,
|
|
31874
|
+
if (!(0, import_node_fs29.existsSync)(statePath)) return void 0;
|
|
31708
31875
|
try {
|
|
31709
|
-
const state = JSON.parse((0,
|
|
31876
|
+
const state = JSON.parse((0, import_node_fs29.readFileSync)(statePath, "utf8"));
|
|
31710
31877
|
const port = typeof state.port === "number" ? state.port : void 0;
|
|
31711
31878
|
if (port == null || !Number.isInteger(port) || port <= 0) return void 0;
|
|
31712
31879
|
return { port, url: typeof state.url === "string" ? state.url : void 0 };
|
|
@@ -31763,9 +31930,9 @@ async function collectWaveStatus(deps) {
|
|
|
31763
31930
|
// src/discovery-commands.ts
|
|
31764
31931
|
init_github_client();
|
|
31765
31932
|
init_cli_shared();
|
|
31766
|
-
var
|
|
31933
|
+
var import_node_fs30 = require("node:fs");
|
|
31767
31934
|
var import_node_os15 = require("node:os");
|
|
31768
|
-
var
|
|
31935
|
+
var import_node_path29 = require("node:path");
|
|
31769
31936
|
var GC_GH_TIMEOUT_MS = 2e4;
|
|
31770
31937
|
async function collectStatus() {
|
|
31771
31938
|
const repo = await resolveRepo();
|
|
@@ -31955,8 +32122,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
31955
32122
|
}
|
|
31956
32123
|
const home = (0, import_node_os15.homedir)();
|
|
31957
32124
|
const plugin = onboardPluginGate({
|
|
31958
|
-
readKnown: () => readFileSyncSafe((0,
|
|
31959
|
-
readSettings: () => readFileSyncSafe((0,
|
|
32125
|
+
readKnown: () => readFileSyncSafe((0, import_node_path29.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs30.readFileSync),
|
|
32126
|
+
readSettings: () => readFileSyncSafe((0, import_node_path29.join)(home, ".claude", "settings.json"), import_node_fs30.readFileSync)
|
|
31960
32127
|
});
|
|
31961
32128
|
return {
|
|
31962
32129
|
guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
|
|
@@ -33973,7 +34140,7 @@ function parseOriginRepo(remoteUrl) {
|
|
|
33973
34140
|
init_github_client();
|
|
33974
34141
|
|
|
33975
34142
|
// src/issue-commands.ts
|
|
33976
|
-
var
|
|
34143
|
+
var import_node_fs31 = require("node:fs");
|
|
33977
34144
|
var import_node_crypto10 = require("node:crypto");
|
|
33978
34145
|
init_cli_shared();
|
|
33979
34146
|
init_clean_exit();
|
|
@@ -34230,7 +34397,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
34230
34397
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
34231
34398
|
const patch = {};
|
|
34232
34399
|
let bodyChanged = false;
|
|
34233
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
34400
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs31.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
34234
34401
|
if (options.titleFile !== void 0) {
|
|
34235
34402
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
34236
34403
|
} else if (options.title !== void 0) {
|
|
@@ -34886,7 +35053,7 @@ function extendCreateCommand(issue, batchAttach) {
|
|
|
34886
35053
|
if (opts.batch) {
|
|
34887
35054
|
let specs;
|
|
34888
35055
|
try {
|
|
34889
|
-
const raw = (0,
|
|
35056
|
+
const raw = (0, import_node_fs31.readFileSync)(opts.batch, "utf8");
|
|
34890
35057
|
specs = JSON.parse(raw);
|
|
34891
35058
|
if (!Array.isArray(specs)) {
|
|
34892
35059
|
const top = specs === null ? "null" : typeof specs === "object" ? `an object with keys: ${Object.keys(specs).join(", ") || "(none)"}` : `a ${typeof specs}`;
|
|
@@ -36050,8 +36217,8 @@ function registerPrLifecycleCommands(program3) {
|
|
|
36050
36217
|
}
|
|
36051
36218
|
|
|
36052
36219
|
// src/project-info-sync.ts
|
|
36053
|
-
var
|
|
36054
|
-
var
|
|
36220
|
+
var import_node_fs32 = require("node:fs");
|
|
36221
|
+
var import_node_path30 = require("node:path");
|
|
36055
36222
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
36056
36223
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
36057
36224
|
projectV2 { id }
|
|
@@ -36098,14 +36265,14 @@ function sharedName(entries, fallback) {
|
|
|
36098
36265
|
}
|
|
36099
36266
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
36100
36267
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
36101
|
-
const readmePath = (0,
|
|
36102
|
-
if (!(0,
|
|
36268
|
+
const readmePath = (0, import_node_path30.join)(repoRoot2, "README.md");
|
|
36269
|
+
if (!(0, import_node_fs32.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
36103
36270
|
const entries = entriesFor(project2, projects);
|
|
36104
36271
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
36105
36272
|
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
36106
36273
|
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
36107
36274
|
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
36108
|
-
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0,
|
|
36275
|
+
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs32.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
|
|
36109
36276
|
const lines2 = [
|
|
36110
36277
|
`# ${projectName}`,
|
|
36111
36278
|
"",
|
|
@@ -36124,8 +36291,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
36124
36291
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
36125
36292
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
36126
36293
|
const orgDocs = [
|
|
36127
|
-
(0,
|
|
36128
|
-
(0,
|
|
36294
|
+
(0, import_node_fs32.existsSync)((0, import_node_path30.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
|
|
36295
|
+
(0, import_node_fs32.existsSync)((0, import_node_path30.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
|
|
36129
36296
|
].filter(Boolean);
|
|
36130
36297
|
if (orgDocs.length) lines2.push("", "## Organisation docs", "", ...orgDocs);
|
|
36131
36298
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines2.join("\n")}
|
|
@@ -36507,8 +36674,8 @@ function registerSchedulesCommands(program3) {
|
|
|
36507
36674
|
}
|
|
36508
36675
|
|
|
36509
36676
|
// src/secrets-commands.ts
|
|
36510
|
-
var
|
|
36511
|
-
var
|
|
36677
|
+
var import_node_fs33 = require("node:fs");
|
|
36678
|
+
var import_node_path31 = require("node:path");
|
|
36512
36679
|
var import_node_os17 = require("node:os");
|
|
36513
36680
|
init_cli_shared();
|
|
36514
36681
|
init_hub_auth();
|
|
@@ -36614,18 +36781,18 @@ function collectMap(value, previous = []) {
|
|
|
36614
36781
|
return [...previous, value];
|
|
36615
36782
|
}
|
|
36616
36783
|
async function decryptRailsCredentials(input) {
|
|
36617
|
-
const appDir = (0,
|
|
36784
|
+
const appDir = (0, import_node_path31.resolve)(input.appDir ?? process.cwd());
|
|
36618
36785
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
36619
36786
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
36620
|
-
const credentialsPath = (0,
|
|
36621
|
-
const masterKeyPath = (0,
|
|
36787
|
+
const credentialsPath = (0, import_node_path31.resolve)(appDir, credentialsFile);
|
|
36788
|
+
const masterKeyPath = (0, import_node_path31.resolve)(appDir, masterKeyFile);
|
|
36622
36789
|
const env = {
|
|
36623
36790
|
...process.env,
|
|
36624
36791
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
36625
36792
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
36626
36793
|
};
|
|
36627
|
-
if ((0,
|
|
36628
|
-
env.RAILS_MASTER_KEY = (0,
|
|
36794
|
+
if ((0, import_node_fs33.existsSync)(masterKeyPath)) {
|
|
36795
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs33.readFileSync)(masterKeyPath, "utf8").trim();
|
|
36629
36796
|
}
|
|
36630
36797
|
const script = [
|
|
36631
36798
|
'require "json"',
|
|
@@ -36635,9 +36802,9 @@ async function decryptRailsCredentials(input) {
|
|
|
36635
36802
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
36636
36803
|
"puts JSON.generate(config.config)"
|
|
36637
36804
|
].join("\n");
|
|
36638
|
-
const scriptDir = (0,
|
|
36639
|
-
const scriptPath = (0,
|
|
36640
|
-
(0,
|
|
36805
|
+
const scriptDir = (0, import_node_fs33.mkdtempSync)((0, import_node_path31.join)((0, import_node_os17.tmpdir)(), "mmi-rails-decrypt-"));
|
|
36806
|
+
const scriptPath = (0, import_node_path31.join)(scriptDir, "decrypt.rb");
|
|
36807
|
+
(0, import_node_fs33.writeFileSync)(scriptPath, script, "utf8");
|
|
36641
36808
|
try {
|
|
36642
36809
|
const args = ["exec", "ruby", scriptPath];
|
|
36643
36810
|
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
@@ -36649,7 +36816,7 @@ async function decryptRailsCredentials(input) {
|
|
|
36649
36816
|
});
|
|
36650
36817
|
return JSON.parse(stdout);
|
|
36651
36818
|
} finally {
|
|
36652
|
-
(0,
|
|
36819
|
+
(0, import_node_fs33.rmSync)(scriptDir, { recursive: true, force: true });
|
|
36653
36820
|
}
|
|
36654
36821
|
}
|
|
36655
36822
|
async function readSecretStdin() {
|
|
@@ -36739,7 +36906,7 @@ function registerSecretsCommands(program3) {
|
|
|
36739
36906
|
let body;
|
|
36740
36907
|
if (o.file) {
|
|
36741
36908
|
try {
|
|
36742
|
-
body = (0,
|
|
36909
|
+
body = (0, import_node_fs33.readFileSync)((0, import_node_path31.resolve)(o.file), "utf8");
|
|
36743
36910
|
} catch (e) {
|
|
36744
36911
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
36745
36912
|
}
|
|
@@ -36871,7 +37038,7 @@ function registerSecretsCommands(program3) {
|
|
|
36871
37038
|
{
|
|
36872
37039
|
...d,
|
|
36873
37040
|
decryptRailsCredentials,
|
|
36874
|
-
removeFile: (path2) => (0,
|
|
37041
|
+
removeFile: (path2) => (0, import_node_fs33.unlinkSync)((0, import_node_path31.resolve)(o.appDir ?? process.cwd(), path2))
|
|
36875
37042
|
},
|
|
36876
37043
|
{
|
|
36877
37044
|
repo: o.repo,
|
|
@@ -37034,8 +37201,8 @@ function registerSessionReport(program3) {
|
|
|
37034
37201
|
}
|
|
37035
37202
|
|
|
37036
37203
|
// src/stage-commands.ts
|
|
37037
|
-
var
|
|
37038
|
-
var
|
|
37204
|
+
var import_node_fs34 = require("node:fs");
|
|
37205
|
+
var import_node_path32 = require("node:path");
|
|
37039
37206
|
init_cli_shared();
|
|
37040
37207
|
init_clean_exit();
|
|
37041
37208
|
|
|
@@ -37281,8 +37448,8 @@ function registerStageCommands(program3) {
|
|
|
37281
37448
|
return {
|
|
37282
37449
|
resolution: decideStage({
|
|
37283
37450
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
37284
|
-
hasCompose: (0,
|
|
37285
|
-
hasEnvExample: (0,
|
|
37451
|
+
hasCompose: (0, import_node_fs34.existsSync)((0, import_node_path32.join)(process.cwd(), "docker-compose.yml")),
|
|
37452
|
+
hasEnvExample: (0, import_node_fs34.existsSync)((0, import_node_path32.join)(process.cwd(), ".env.example")),
|
|
37286
37453
|
repo
|
|
37287
37454
|
}),
|
|
37288
37455
|
project: project2,
|
|
@@ -37566,19 +37733,19 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
|
|
|
37566
37733
|
|
|
37567
37734
|
// src/tenant-artifact.ts
|
|
37568
37735
|
var import_node_crypto11 = require("node:crypto");
|
|
37569
|
-
var
|
|
37736
|
+
var import_node_fs35 = require("node:fs");
|
|
37570
37737
|
var import_promises6 = require("node:fs/promises");
|
|
37571
|
-
var
|
|
37738
|
+
var import_node_path33 = require("node:path");
|
|
37572
37739
|
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}$/;
|
|
37573
37740
|
var MAX_BYTES = 5 * 1024 * 1024 * 1024;
|
|
37574
37741
|
async function sha256File(path2) {
|
|
37575
37742
|
const hash = (0, import_node_crypto11.createHash)("sha256");
|
|
37576
|
-
for await (const chunk of (0,
|
|
37743
|
+
for await (const chunk of (0, import_node_fs35.createReadStream)(path2)) hash.update(chunk);
|
|
37577
37744
|
return hash.digest("hex");
|
|
37578
37745
|
}
|
|
37579
37746
|
async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
37580
37747
|
if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
|
|
37581
|
-
const path2 = (0,
|
|
37748
|
+
const path2 = (0, import_node_path33.resolve)(inputPath);
|
|
37582
37749
|
const info = await (0, import_promises6.stat)(path2);
|
|
37583
37750
|
if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
|
|
37584
37751
|
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`);
|
|
@@ -37596,7 +37763,7 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
|
37596
37763
|
return [key, value];
|
|
37597
37764
|
}));
|
|
37598
37765
|
headers["content-length"] = String(info.size);
|
|
37599
|
-
const stream = (0,
|
|
37766
|
+
const stream = (0, import_node_fs35.createReadStream)(path2);
|
|
37600
37767
|
let uploaded;
|
|
37601
37768
|
try {
|
|
37602
37769
|
uploaded = await fetch(body.uploadUrl, {
|
|
@@ -37736,7 +37903,7 @@ function renderVerifySecrets(body) {
|
|
|
37736
37903
|
|
|
37737
37904
|
// src/command-register-collaboration.ts
|
|
37738
37905
|
var import_node_child_process15 = require("node:child_process");
|
|
37739
|
-
var
|
|
37906
|
+
var import_node_fs42 = require("node:fs");
|
|
37740
37907
|
var import_promises7 = require("node:fs/promises");
|
|
37741
37908
|
init_clean_exit();
|
|
37742
37909
|
init_cli_shared();
|
|
@@ -37760,7 +37927,7 @@ function spawnDetachedSelf(args, deps, opts = {}) {
|
|
|
37760
37927
|
}
|
|
37761
37928
|
|
|
37762
37929
|
// src/command-register-collaboration.ts
|
|
37763
|
-
var
|
|
37930
|
+
var import_node_path40 = require("node:path");
|
|
37764
37931
|
|
|
37765
37932
|
// src/attach-to-project.ts
|
|
37766
37933
|
async function attachViaHubApp(cfg, contentNodeId) {
|
|
@@ -37851,6 +38018,9 @@ function prHeadBehindBase(input) {
|
|
|
37851
38018
|
function prHeadUpdateRemedy(head, base) {
|
|
37852
38019
|
return `git fetch origin ${base} && git merge origin/${base} (on ${head}), resolve the conflicts, push, then rerun \u2014 docs/Guides/train-troubleshooting.md#head-behind-base`;
|
|
37853
38020
|
}
|
|
38021
|
+
function prLandedBranchFinality(prNumber, base) {
|
|
38022
|
+
return `PR #${prNumber} is merged, and that is final \u2014 stopping a local waiter or monitor does not unmerge it. The head branch is now landed; further commits pushed to it reach nothing. If more work is needed, run \`git fetch origin ${base} && git switch -c <new-branch> origin/${base}\`, then cherry-pick anything already committed \u2014 docs/Guides/train-troubleshooting.md#merged-pr-head-reuse`;
|
|
38023
|
+
}
|
|
37854
38024
|
var PROTECTED_PR_HEAD_BRANCHES = ["main", "master", "development", "rc"];
|
|
37855
38025
|
function isProtectedPrHead(head) {
|
|
37856
38026
|
return PROTECTED_PR_HEAD_BRANCHES.includes(head);
|
|
@@ -37946,8 +38116,9 @@ async function readGhPrStateWithRetry(fetchState, options) {
|
|
|
37946
38116
|
return { ok: false, error: lastError };
|
|
37947
38117
|
}
|
|
37948
38118
|
async function runPrLand(prNumber, options, deps) {
|
|
37949
|
-
const
|
|
37950
|
-
const
|
|
38119
|
+
const resolved = await deps.resolveRepo(prNumber, options.repo);
|
|
38120
|
+
const { repo } = resolved;
|
|
38121
|
+
const base = { status: "failed", repo, pr: prNumber, base: resolved.base };
|
|
37951
38122
|
if (options.requireTrain !== false) {
|
|
37952
38123
|
const verdict = await deps.fetchTrainAuthority(repo);
|
|
37953
38124
|
if (!verdict.ok) {
|
|
@@ -37999,12 +38170,21 @@ async function runPrLand(prNumber, options, deps) {
|
|
|
37999
38170
|
return { ...base, error: merge.error ?? "merge failed" };
|
|
38000
38171
|
}
|
|
38001
38172
|
if (merge.mergeStatus === "merged") {
|
|
38002
|
-
return {
|
|
38173
|
+
return {
|
|
38174
|
+
...base,
|
|
38175
|
+
status: "merged",
|
|
38176
|
+
branchFinality: prLandedBranchFinality(prNumber, resolved.base)
|
|
38177
|
+
};
|
|
38003
38178
|
}
|
|
38004
38179
|
const now = deps.now ?? (() => Date.now());
|
|
38005
38180
|
const merged = await deps.pollMerged(prNumber, repo, now() + PR_LAND_ENQUEUE_TIMEOUT_MS);
|
|
38006
38181
|
if (merged) {
|
|
38007
|
-
return {
|
|
38182
|
+
return {
|
|
38183
|
+
...base,
|
|
38184
|
+
status: "auto-merge-enqueued-then-merged",
|
|
38185
|
+
mergeStatus: "merged",
|
|
38186
|
+
branchFinality: prLandedBranchFinality(prNumber, resolved.base)
|
|
38187
|
+
};
|
|
38008
38188
|
}
|
|
38009
38189
|
return {
|
|
38010
38190
|
...base,
|
|
@@ -38016,8 +38196,8 @@ async function runPrLand(prNumber, options, deps) {
|
|
|
38016
38196
|
init_github_client();
|
|
38017
38197
|
|
|
38018
38198
|
// src/merge-cleanup.ts
|
|
38019
|
-
var
|
|
38020
|
-
var
|
|
38199
|
+
var import_node_fs37 = require("node:fs");
|
|
38200
|
+
var import_node_path35 = require("node:path");
|
|
38021
38201
|
var import_node_os18 = require("node:os");
|
|
38022
38202
|
init_cli_shared();
|
|
38023
38203
|
init_github_client();
|
|
@@ -38126,8 +38306,8 @@ function boardAdvanceFailureMessage(result) {
|
|
|
38126
38306
|
|
|
38127
38307
|
// src/test-policy-core.ts
|
|
38128
38308
|
var import_node_child_process12 = require("node:child_process");
|
|
38129
|
-
var
|
|
38130
|
-
var
|
|
38309
|
+
var import_node_fs36 = require("node:fs");
|
|
38310
|
+
var import_node_path34 = require("node:path");
|
|
38131
38311
|
|
|
38132
38312
|
// src/test-command-policy-shared.mjs
|
|
38133
38313
|
var import_node_child_process11 = require("node:child_process");
|
|
@@ -38603,7 +38783,7 @@ function isMeaningfulRow(file) {
|
|
|
38603
38783
|
return file.meaningful !== false;
|
|
38604
38784
|
}
|
|
38605
38785
|
function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
38606
|
-
const raw = readFile9((0,
|
|
38786
|
+
const raw = readFile9((0, import_node_path34.join)(root, POLICY_FILE));
|
|
38607
38787
|
if (raw == null) return { mandatory: [], declared: false };
|
|
38608
38788
|
return parsePolicy(raw, POLICY_FILE);
|
|
38609
38789
|
}
|
|
@@ -38680,7 +38860,7 @@ function loadPolicySource(root, explicitRef, diffBase) {
|
|
|
38680
38860
|
}
|
|
38681
38861
|
function readFileOrNull2(path2) {
|
|
38682
38862
|
try {
|
|
38683
|
-
return (0,
|
|
38863
|
+
return (0, import_node_fs36.readFileSync)(path2, "utf8");
|
|
38684
38864
|
} catch {
|
|
38685
38865
|
return null;
|
|
38686
38866
|
}
|
|
@@ -38708,12 +38888,12 @@ function classify(changed, policy, present = () => false) {
|
|
|
38708
38888
|
const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
|
|
38709
38889
|
return { mandatoryHits, untestedHits, testChanges, meaningfulTestChanges, addedTests, removedProtected };
|
|
38710
38890
|
}
|
|
38711
|
-
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0,
|
|
38712
|
-
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0,
|
|
38891
|
+
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs36.existsSync)(path2)) {
|
|
38892
|
+
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path34.join)(root, p)));
|
|
38713
38893
|
}
|
|
38714
|
-
function unresolvedSatisfiers(policy, root, exists = (path2) => (0,
|
|
38894
|
+
function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs36.existsSync)(path2)) {
|
|
38715
38895
|
const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
|
|
38716
|
-
return [...new Set(declared)].filter((p) => !exists((0,
|
|
38896
|
+
return [...new Set(declared)].filter((p) => !exists((0, import_node_path34.join)(root, p)));
|
|
38717
38897
|
}
|
|
38718
38898
|
function evaluate(changed, policy, present = () => false) {
|
|
38719
38899
|
const { mandatoryHits, untestedHits, meaningfulTestChanges, addedTests, removedProtected } = classify(changed, policy, present);
|
|
@@ -38852,14 +39032,14 @@ function runTestPolicy(root, deps = {}) {
|
|
|
38852
39032
|
}
|
|
38853
39033
|
const loaded = deps.policy ? { policy: deps.policy, source: { ref: null, sha: null, path: POLICY_FILE } } : loadPolicySource(root, deps.policyRef, deps.base);
|
|
38854
39034
|
const { policy, source: policySource } = loaded;
|
|
38855
|
-
const exists = deps.exists ?? ((path2) => (0,
|
|
39035
|
+
const exists = deps.exists ?? ((path2) => (0, import_node_fs36.existsSync)(path2));
|
|
38856
39036
|
const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
|
|
38857
39037
|
const base = deps.changed ? "(injected)" : deps.policyRef ? resolveHotfixBase(root) : resolveBase(root, deps.base);
|
|
38858
39038
|
const refusal = deps.changed ? null : untrustworthyRange(root, base);
|
|
38859
39039
|
const raw = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
|
|
38860
39040
|
const changed = deps.changed ? raw : annotateChangeMeaning(raw, policy, (path2) => ({
|
|
38861
39041
|
before: blobAt(base, path2, root),
|
|
38862
|
-
after: readFileOrNull2((0,
|
|
39042
|
+
after: readFileOrNull2((0, import_node_path34.join)(root, path2))
|
|
38863
39043
|
}));
|
|
38864
39044
|
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride2(base, root) : { override: null, refusals: [] };
|
|
38865
39045
|
const policyRefusals = policy.declared === false ? [{
|
|
@@ -38869,8 +39049,8 @@ function runTestPolicy(root, deps = {}) {
|
|
|
38869
39049
|
A gate with no policy cannot decide which test commands are required or permitted.
|
|
38870
39050
|
` + (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.`)
|
|
38871
39051
|
}] : [];
|
|
38872
|
-
const present = (path2) => exists((0,
|
|
38873
|
-
const policyTree = policySource.sha ? new Set(git2(["ls-tree", "-r", "-z", "--name-only", policySource.sha], root).split("\0").filter(Boolean).map((p) => (0,
|
|
39052
|
+
const present = (path2) => exists((0, import_node_path34.join)(root, path2));
|
|
39053
|
+
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_path34.join)(root, p))) : null;
|
|
38874
39054
|
const policyTreeExists = policyTree ? (abs) => policyTree.has(abs) : exists;
|
|
38875
39055
|
const where = policySource.sha ? `${policySource.ref}@${policySource.sha}` : "this worktree";
|
|
38876
39056
|
const removedByThisDiff = removedPaths(changed);
|
|
@@ -39088,13 +39268,13 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
|
|
|
39088
39268
|
return rewritten === base ? null : rewritten;
|
|
39089
39269
|
}
|
|
39090
39270
|
function writeSquashBodyFile(body) {
|
|
39091
|
-
const dir = (0,
|
|
39092
|
-
const path2 = (0,
|
|
39093
|
-
(0,
|
|
39271
|
+
const dir = (0, import_node_fs37.mkdtempSync)((0, import_node_path35.join)((0, import_node_os18.tmpdir)(), "mmi-squash-body-"));
|
|
39272
|
+
const path2 = (0, import_node_path35.join)(dir, "body.txt");
|
|
39273
|
+
(0, import_node_fs37.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
|
|
39094
39274
|
`, "utf8");
|
|
39095
39275
|
return { path: path2, cleanup: () => {
|
|
39096
39276
|
try {
|
|
39097
|
-
(0,
|
|
39277
|
+
(0, import_node_fs37.rmSync)(dir, { recursive: true, force: true });
|
|
39098
39278
|
} catch {
|
|
39099
39279
|
}
|
|
39100
39280
|
} };
|
|
@@ -39301,8 +39481,8 @@ async function deleteMergedRemoteBranch(options) {
|
|
|
39301
39481
|
}
|
|
39302
39482
|
|
|
39303
39483
|
// src/post-merge-recon.ts
|
|
39304
|
-
var
|
|
39305
|
-
var
|
|
39484
|
+
var import_node_fs38 = require("node:fs");
|
|
39485
|
+
var import_node_path36 = require("node:path");
|
|
39306
39486
|
|
|
39307
39487
|
// src/cross-repo-filing-issue.ts
|
|
39308
39488
|
init_github_client();
|
|
@@ -39469,16 +39649,16 @@ function buildPostMergeReconRecovery(input) {
|
|
|
39469
39649
|
}
|
|
39470
39650
|
function writePostMergeReconRecovery(cwd, recovery) {
|
|
39471
39651
|
const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
|
|
39472
|
-
(0,
|
|
39473
|
-
(0,
|
|
39652
|
+
(0, import_node_fs38.mkdirSync)((0, import_node_path36.dirname)(path2), { recursive: true });
|
|
39653
|
+
(0, import_node_fs38.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
|
|
39474
39654
|
`, "utf8");
|
|
39475
39655
|
return path2;
|
|
39476
39656
|
}
|
|
39477
39657
|
function clearPostMergeReconRecovery(cwd, repo, pr) {
|
|
39478
39658
|
const path2 = postMergeReconStatePath(cwd, repo, pr);
|
|
39479
|
-
if (!(0,
|
|
39659
|
+
if (!(0, import_node_fs38.existsSync)(path2)) return;
|
|
39480
39660
|
try {
|
|
39481
|
-
(0,
|
|
39661
|
+
(0, import_node_fs38.unlinkSync)(path2);
|
|
39482
39662
|
} catch {
|
|
39483
39663
|
}
|
|
39484
39664
|
}
|
|
@@ -39510,9 +39690,9 @@ function postMergeReconWarnings(input) {
|
|
|
39510
39690
|
|
|
39511
39691
|
// src/review-verdict.ts
|
|
39512
39692
|
var import_node_child_process13 = require("node:child_process");
|
|
39513
|
-
var
|
|
39693
|
+
var import_node_fs39 = require("node:fs");
|
|
39514
39694
|
var import_node_os19 = require("node:os");
|
|
39515
|
-
var
|
|
39695
|
+
var import_node_path37 = require("node:path");
|
|
39516
39696
|
init_cli_shared();
|
|
39517
39697
|
var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
|
|
39518
39698
|
var REVIEW_VERDICTS = ["PROCEED", "CORRECT", "ESCALATE"];
|
|
@@ -39677,15 +39857,15 @@ async function readPrIssueComments(number, repo) {
|
|
|
39677
39857
|
return comments;
|
|
39678
39858
|
}
|
|
39679
39859
|
async function postPrCommentFromFile(number, repo, body) {
|
|
39680
|
-
const dir = (0,
|
|
39681
|
-
const path2 = (0,
|
|
39860
|
+
const dir = (0, import_node_fs39.mkdtempSync)((0, import_node_path37.join)((0, import_node_os19.tmpdir)(), "mmi-review-verdict-"));
|
|
39861
|
+
const path2 = (0, import_node_path37.join)(dir, "body.md");
|
|
39682
39862
|
try {
|
|
39683
|
-
(0,
|
|
39863
|
+
(0, import_node_fs39.writeFileSync)(path2, body, "utf8");
|
|
39684
39864
|
const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
39685
39865
|
return stdout.trim();
|
|
39686
39866
|
} finally {
|
|
39687
39867
|
try {
|
|
39688
|
-
(0,
|
|
39868
|
+
(0, import_node_fs39.rmSync)(dir, { recursive: true, force: true });
|
|
39689
39869
|
} catch {
|
|
39690
39870
|
}
|
|
39691
39871
|
}
|
|
@@ -39839,6 +40019,32 @@ function withRateLimitRetry(client, seams = {}) {
|
|
|
39839
40019
|
throttledPaths
|
|
39840
40020
|
};
|
|
39841
40021
|
}
|
|
40022
|
+
async function prCreateMergedHeadWarning(input, deps = {}) {
|
|
40023
|
+
const guide = "docs/Guides/train-troubleshooting.md#merged-pr-head-reuse";
|
|
40024
|
+
if (!input.head) {
|
|
40025
|
+
return `pr create: WARNING \u2014 could not check whether the current head branch already landed (the current branch could not be resolved); continuing with PR creation \u2014 ${guide}`;
|
|
40026
|
+
}
|
|
40027
|
+
try {
|
|
40028
|
+
const repo = await requireRepo(input.repo);
|
|
40029
|
+
const owner = repo.split("/")[0];
|
|
40030
|
+
if (!owner) throw new Error(`invalid repository '${repo}'`);
|
|
40031
|
+
const qualifiedHead = input.head.includes(":") ? input.head : `${owner}:${input.head}`;
|
|
40032
|
+
const query = new URLSearchParams({ state: "closed", head: qualifiedHead, sort: "updated", direction: "desc" });
|
|
40033
|
+
const pulls = await (deps.client ?? defaultGitHubClient()).restPaginate(
|
|
40034
|
+
`repos/${repo}/pulls?${query.toString()}`
|
|
40035
|
+
);
|
|
40036
|
+
const merged = pulls.find((pull) => typeof pull.merged_at === "string" && pull.merged_at.length > 0);
|
|
40037
|
+
if (!merged) return void 0;
|
|
40038
|
+
if (typeof merged.number !== "number" || typeof merged.base?.ref !== "string" || !merged.base.ref) {
|
|
40039
|
+
throw new Error("GitHub returned a merged PR without its number or base branch");
|
|
40040
|
+
}
|
|
40041
|
+
const base = merged.base.ref;
|
|
40042
|
+
return `pr create: WARNING \u2014 head branch '${input.head}' already landed in merged PR #${merged.number}. If you meant to continue that landed work, cut a fresh branch from origin/${base}: \`git fetch origin ${base} && git switch -c <new-branch> origin/${base}\`, then cherry-pick anything already committed. Creating another PR from a deliberately reused branch name remains allowed \u2014 ${guide}`;
|
|
40043
|
+
} catch (e) {
|
|
40044
|
+
const detail = String(e.message || e).replace(/\s+/g, " ").slice(0, 240);
|
|
40045
|
+
return `pr create: WARNING \u2014 could not check whether head branch '${input.head}' already landed (${detail}); continuing with PR creation \u2014 ${guide}`;
|
|
40046
|
+
}
|
|
40047
|
+
}
|
|
39842
40048
|
async function prCreateClaimRefusal(body, repoOption, deps = {}) {
|
|
39843
40049
|
const issues = [...new Set(findClosingMentions(body).map((mention) => mention.issue))];
|
|
39844
40050
|
if (!issues.length) return void 0;
|
|
@@ -39871,13 +40077,13 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
|
|
|
39871
40077
|
}
|
|
39872
40078
|
|
|
39873
40079
|
// src/worktree-merge-cleanup.ts
|
|
39874
|
-
var
|
|
39875
|
-
var
|
|
40080
|
+
var import_node_fs41 = require("node:fs");
|
|
40081
|
+
var import_node_path39 = require("node:path");
|
|
39876
40082
|
init_cli_shared();
|
|
39877
40083
|
|
|
39878
40084
|
// src/worktree-evidence-archive.ts
|
|
39879
|
-
var
|
|
39880
|
-
var
|
|
40085
|
+
var import_node_fs40 = require("node:fs");
|
|
40086
|
+
var import_node_path38 = require("node:path");
|
|
39881
40087
|
var JERV_ARTIFACT_RUN_SCOPE_ENV_VARS = ["JERV_RUN_ID", ...SESSION_ID_ENV_VARS];
|
|
39882
40088
|
function sanitizeArchiveSegment(value, max = 80) {
|
|
39883
40089
|
const scrubbed = value.replace(/[^A-Za-z0-9._@+-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -39892,24 +40098,24 @@ function resolveArtifactRunScope(env = process.env) {
|
|
|
39892
40098
|
}
|
|
39893
40099
|
function defaultIsDirectory(path2) {
|
|
39894
40100
|
try {
|
|
39895
|
-
return (0,
|
|
40101
|
+
return (0, import_node_fs40.lstatSync)(path2).isDirectory();
|
|
39896
40102
|
} catch {
|
|
39897
40103
|
return false;
|
|
39898
40104
|
}
|
|
39899
40105
|
}
|
|
39900
40106
|
function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
39901
|
-
const exists = deps.exists ??
|
|
40107
|
+
const exists = deps.exists ?? import_node_fs40.existsSync;
|
|
39902
40108
|
const isDirectory = deps.isDirectory ?? defaultIsDirectory;
|
|
39903
|
-
const copyDir = deps.copyDir ?? ((from, to) => (0,
|
|
40109
|
+
const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs40.cpSync)(from, to, { recursive: true, force: true }));
|
|
39904
40110
|
const mkdirp = deps.mkdirp ?? ((path2) => {
|
|
39905
|
-
(0,
|
|
40111
|
+
(0, import_node_fs40.mkdirSync)(path2, { recursive: true });
|
|
39906
40112
|
});
|
|
39907
40113
|
const env = deps.env ?? process.env;
|
|
39908
40114
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
39909
40115
|
const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
|
|
39910
40116
|
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
39911
40117
|
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
39912
|
-
const source = (0,
|
|
40118
|
+
const source = (0, import_node_path38.join)(args.worktreePath, ".jerv");
|
|
39913
40119
|
if (!exists(source)) return { status: "absent" };
|
|
39914
40120
|
if (!isDirectory(source)) return { status: "skipped", reason: "jerv-not-a-directory" };
|
|
39915
40121
|
const runScope = resolveArtifactRunScope(env);
|
|
@@ -39917,7 +40123,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
39917
40123
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
39918
40124
|
const dest = resolveRoot(args.primaryRoot, "jerv-artifacts", runScope, branchSlug, stamp, ".jerv");
|
|
39919
40125
|
try {
|
|
39920
|
-
mkdirp((0,
|
|
40126
|
+
mkdirp((0, import_node_path38.dirname)(dest));
|
|
39921
40127
|
copyDir(source, dest);
|
|
39922
40128
|
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
39923
40129
|
return { status: "archived", path: dest, runScope };
|
|
@@ -39926,14 +40132,14 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
39926
40132
|
}
|
|
39927
40133
|
}
|
|
39928
40134
|
function defaultStat(path2) {
|
|
39929
|
-
const st = (0,
|
|
40135
|
+
const st = (0, import_node_fs40.statSync)(path2);
|
|
39930
40136
|
return { mtimeMs: st.mtimeMs, size: st.size, isDirectory: () => st.isDirectory() };
|
|
39931
40137
|
}
|
|
39932
40138
|
function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
39933
|
-
const exists = deps.exists ??
|
|
40139
|
+
const exists = deps.exists ?? import_node_fs40.existsSync;
|
|
39934
40140
|
const stat4 = deps.stat ?? defaultStat;
|
|
39935
|
-
const readdir2 = deps.readdir ??
|
|
39936
|
-
const tmpRoot = (0,
|
|
40141
|
+
const readdir2 = deps.readdir ?? import_node_fs40.readdirSync;
|
|
40142
|
+
const tmpRoot = (0, import_node_path38.join)(worktreePath, "tmp");
|
|
39937
40143
|
if (!exists(tmpRoot)) return [];
|
|
39938
40144
|
const entries = [];
|
|
39939
40145
|
const walk2 = (dir) => {
|
|
@@ -39944,14 +40150,14 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
|
39944
40150
|
return;
|
|
39945
40151
|
}
|
|
39946
40152
|
for (const name of names) {
|
|
39947
|
-
const full = (0,
|
|
40153
|
+
const full = (0, import_node_path38.join)(dir, name);
|
|
39948
40154
|
let st;
|
|
39949
40155
|
try {
|
|
39950
40156
|
st = stat4(full);
|
|
39951
40157
|
} catch {
|
|
39952
40158
|
continue;
|
|
39953
40159
|
}
|
|
39954
|
-
const relPath = (0,
|
|
40160
|
+
const relPath = (0, import_node_path38.relative)(worktreePath, full).replace(/\\/g, "/");
|
|
39955
40161
|
if (st.isDirectory()) {
|
|
39956
40162
|
if (st.mtimeMs > newerThanMs) entries.push({ relPath, bytes: 0, mtimeMs: st.mtimeMs });
|
|
39957
40163
|
walk2(full);
|
|
@@ -39965,10 +40171,10 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
|
39965
40171
|
return entries;
|
|
39966
40172
|
}
|
|
39967
40173
|
function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
39968
|
-
const exists = deps.exists ??
|
|
39969
|
-
const copyDir = deps.copyDir ?? ((from, to) => (0,
|
|
40174
|
+
const exists = deps.exists ?? import_node_fs40.existsSync;
|
|
40175
|
+
const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs40.cpSync)(from, to, { recursive: true, force: true }));
|
|
39970
40176
|
const mkdirp = deps.mkdirp ?? ((path2) => {
|
|
39971
|
-
(0,
|
|
40177
|
+
(0, import_node_fs40.mkdirSync)(path2, { recursive: true });
|
|
39972
40178
|
});
|
|
39973
40179
|
const env = deps.env ?? process.env;
|
|
39974
40180
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
@@ -39976,7 +40182,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
|
39976
40182
|
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
39977
40183
|
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
39978
40184
|
const scanned = scanWorktreeTmpEvidence(args.worktreePath, args.newerThanMs, deps);
|
|
39979
|
-
const source = (0,
|
|
40185
|
+
const source = (0, import_node_path38.join)(args.worktreePath, "tmp");
|
|
39980
40186
|
if (!scanned.length) return { status: "absent" };
|
|
39981
40187
|
if (!exists(source)) return { status: "absent" };
|
|
39982
40188
|
const runScope = resolveArtifactRunScope(env);
|
|
@@ -39984,7 +40190,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
|
39984
40190
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
39985
40191
|
const dest = resolveRoot(args.primaryRoot, "worktree-artifacts", runScope, branchSlug, stamp, "tmp");
|
|
39986
40192
|
try {
|
|
39987
|
-
mkdirp((0,
|
|
40193
|
+
mkdirp((0, import_node_path38.dirname)(dest));
|
|
39988
40194
|
copyDir(source, dest);
|
|
39989
40195
|
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
39990
40196
|
const bytes = scanned.reduce((sum, e) => sum + e.bytes, 0);
|
|
@@ -40053,9 +40259,9 @@ function normPath2(p) {
|
|
|
40053
40259
|
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
40054
40260
|
}
|
|
40055
40261
|
function unlinkNodeModulesJunction(wtPath) {
|
|
40056
|
-
const nm = (0,
|
|
40262
|
+
const nm = (0, import_node_path39.join)(wtPath, "node_modules");
|
|
40057
40263
|
try {
|
|
40058
|
-
if ((0,
|
|
40264
|
+
if ((0, import_node_fs41.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs41.rmdirSync)(nm);
|
|
40059
40265
|
return { ok: true };
|
|
40060
40266
|
} catch (e) {
|
|
40061
40267
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -40066,8 +40272,8 @@ function defaultSleep2(ms) {
|
|
|
40066
40272
|
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
40067
40273
|
}
|
|
40068
40274
|
async function removeResidueDirectory(wtPath, options = {}) {
|
|
40069
|
-
const { sleep: sleep2 = defaultSleep2, removeEmptyDir =
|
|
40070
|
-
const removeRecursive = options.removeRecursive ?? ((path2) => (0,
|
|
40275
|
+
const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs41.rmdirSync, exists = import_node_fs41.existsSync } = options;
|
|
40276
|
+
const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs41.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
|
|
40071
40277
|
const junction = unlinkNodeModulesJunction(wtPath);
|
|
40072
40278
|
if (!junction.ok) return junction;
|
|
40073
40279
|
try {
|
|
@@ -40190,28 +40396,28 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
|
|
|
40190
40396
|
}
|
|
40191
40397
|
async function listNestedIgnoredNodeModules(wtPath, execGit) {
|
|
40192
40398
|
const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
|
|
40193
|
-
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0,
|
|
40399
|
+
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path39.join)(wtPath, line.slice(0, -1)));
|
|
40194
40400
|
}
|
|
40195
40401
|
function safeRemoveTree(path2) {
|
|
40196
|
-
const stat4 = (0,
|
|
40402
|
+
const stat4 = (0, import_node_fs41.lstatSync)(path2);
|
|
40197
40403
|
if (stat4.isSymbolicLink()) {
|
|
40198
40404
|
try {
|
|
40199
|
-
(0,
|
|
40405
|
+
(0, import_node_fs41.rmdirSync)(path2);
|
|
40200
40406
|
} catch {
|
|
40201
|
-
(0,
|
|
40407
|
+
(0, import_node_fs41.unlinkSync)(path2);
|
|
40202
40408
|
}
|
|
40203
40409
|
return;
|
|
40204
40410
|
}
|
|
40205
40411
|
if (stat4.isDirectory()) {
|
|
40206
|
-
for (const entry of (0,
|
|
40207
|
-
(0,
|
|
40412
|
+
for (const entry of (0, import_node_fs41.readdirSync)(path2)) safeRemoveTree((0, import_node_path39.join)(path2, entry));
|
|
40413
|
+
(0, import_node_fs41.rmdirSync)(path2);
|
|
40208
40414
|
return;
|
|
40209
40415
|
}
|
|
40210
|
-
(0,
|
|
40416
|
+
(0, import_node_fs41.unlinkSync)(path2);
|
|
40211
40417
|
}
|
|
40212
40418
|
async function removeWorktreeNodeModules(wtPath) {
|
|
40213
40419
|
try {
|
|
40214
|
-
safeRemoveTree((0,
|
|
40420
|
+
safeRemoveTree((0, import_node_path39.join)(wtPath, "node_modules"));
|
|
40215
40421
|
return { ok: true };
|
|
40216
40422
|
} catch (e) {
|
|
40217
40423
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -40223,7 +40429,7 @@ function errorMessage(e) {
|
|
|
40223
40429
|
}
|
|
40224
40430
|
function resolvedOrRaw(path2) {
|
|
40225
40431
|
try {
|
|
40226
|
-
return normPath2((0,
|
|
40432
|
+
return normPath2((0, import_node_fs41.realpathSync)(path2));
|
|
40227
40433
|
} catch {
|
|
40228
40434
|
return normPath2(path2);
|
|
40229
40435
|
}
|
|
@@ -40231,10 +40437,10 @@ function resolvedOrRaw(path2) {
|
|
|
40231
40437
|
function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
40232
40438
|
let realRoot;
|
|
40233
40439
|
try {
|
|
40234
|
-
if ((0,
|
|
40440
|
+
if ((0, import_node_fs41.lstatSync)(root).isSymbolicLink()) {
|
|
40235
40441
|
return { ok: false, error: `delete root ${normPath2(root)} is a reparse point resolving to ${resolvedOrRaw(root)}` };
|
|
40236
40442
|
}
|
|
40237
|
-
realRoot = normPath2((0,
|
|
40443
|
+
realRoot = normPath2((0, import_node_fs41.realpathSync)(root));
|
|
40238
40444
|
} catch (e) {
|
|
40239
40445
|
if (e.code === "ENOENT") return { ok: true, unlinked: [] };
|
|
40240
40446
|
return { ok: false, error: `cannot resolve delete root ${normPath2(root)}: ${errorMessage(e)}` };
|
|
@@ -40250,16 +40456,16 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
|
40250
40456
|
const dir = stack.pop();
|
|
40251
40457
|
let entries;
|
|
40252
40458
|
try {
|
|
40253
|
-
entries = (0,
|
|
40459
|
+
entries = (0, import_node_fs41.readdirSync)(dir, { withFileTypes: true });
|
|
40254
40460
|
} catch (e) {
|
|
40255
40461
|
return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
|
|
40256
40462
|
}
|
|
40257
40463
|
for (const entry of entries) {
|
|
40258
|
-
const child2 = (0,
|
|
40464
|
+
const child2 = (0, import_node_path39.join)(dir, entry.name);
|
|
40259
40465
|
if (entry.isSymbolicLink()) {
|
|
40260
40466
|
let target = "";
|
|
40261
40467
|
try {
|
|
40262
|
-
target = normPath2((0,
|
|
40468
|
+
target = normPath2((0, import_node_fs41.realpathSync)(child2));
|
|
40263
40469
|
} catch {
|
|
40264
40470
|
target = "";
|
|
40265
40471
|
}
|
|
@@ -40442,7 +40648,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
40442
40648
|
return report;
|
|
40443
40649
|
}
|
|
40444
40650
|
const execGit = options.execGit ?? (async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
40445
|
-
const pathExists = options.pathExists ??
|
|
40651
|
+
const pathExists = options.pathExists ?? import_node_fs41.existsSync;
|
|
40446
40652
|
let afterWorktrees = [];
|
|
40447
40653
|
try {
|
|
40448
40654
|
afterWorktrees = parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"]));
|
|
@@ -40582,7 +40788,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
40582
40788
|
const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
|
|
40583
40789
|
if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
|
|
40584
40790
|
unlinkedReparsePoints.push(...preHelperGuard.unlinked);
|
|
40585
|
-
if (pathExists((0,
|
|
40791
|
+
if (pathExists((0, import_node_path39.join)(wtPath, "node_modules"))) {
|
|
40586
40792
|
const nmRemoved = await (options.removeRealNodeModules ?? removeWorktreeNodeModules)(wtPath);
|
|
40587
40793
|
if (!nmRemoved.ok) {
|
|
40588
40794
|
report.worktree = {
|
|
@@ -40749,10 +40955,10 @@ function argvWantsJson2() {
|
|
|
40749
40955
|
return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
|
|
40750
40956
|
}
|
|
40751
40957
|
function hubRoot() {
|
|
40752
|
-
const fromPkg = (0,
|
|
40958
|
+
const fromPkg = (0, import_node_path40.join)(__dirname, "..", "..");
|
|
40753
40959
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
40754
|
-
if ((0,
|
|
40755
|
-
if ((0,
|
|
40960
|
+
if ((0, import_node_fs42.existsSync)((0, import_node_path40.join)(fromPkg, marker))) return fromPkg;
|
|
40961
|
+
if ((0, import_node_fs42.existsSync)((0, import_node_path40.join)(process.cwd(), marker))) return process.cwd();
|
|
40756
40962
|
return null;
|
|
40757
40963
|
}
|
|
40758
40964
|
function ciAuditDeps() {
|
|
@@ -40764,8 +40970,8 @@ function ciAuditDeps() {
|
|
|
40764
40970
|
getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
|
|
40765
40971
|
readSeedFile: (path2) => {
|
|
40766
40972
|
if (!root) return null;
|
|
40767
|
-
const fullPath = (0,
|
|
40768
|
-
return (0,
|
|
40973
|
+
const fullPath = (0, import_node_path40.join)(root, path2);
|
|
40974
|
+
return (0, import_node_fs42.existsSync)(fullPath) ? (0, import_node_fs42.readFileSync)(fullPath, "utf8") : null;
|
|
40769
40975
|
}
|
|
40770
40976
|
};
|
|
40771
40977
|
}
|
|
@@ -41443,6 +41649,10 @@ ${list}`);
|
|
|
41443
41649
|
}
|
|
41444
41650
|
const claimRefusal = await prCreateClaimRefusal(body, o.repo);
|
|
41445
41651
|
if (claimRefusal) return fail(claimRefusal);
|
|
41652
|
+
const createHead = o.head ?? await gitOut(["symbolic-ref", "--quiet", "--short", "HEAD"]).catch(() => "");
|
|
41653
|
+
const mergedHeadWarning = await prCreateMergedHeadWarning({ head: createHead || void 0, repo: o.repo });
|
|
41654
|
+
if (mergedHeadWarning) process.stderr.write(`${mergedHeadWarning}
|
|
41655
|
+
`);
|
|
41446
41656
|
const created = await ghCreate(buildPrArgs({ title, body, base: o.base, head: o.head, repo: o.repo, draft: o.draft }));
|
|
41447
41657
|
if (isGhCreateRateLimited(created)) {
|
|
41448
41658
|
console.log(JSON.stringify(created));
|
|
@@ -41486,11 +41696,11 @@ ${list}`);
|
|
|
41486
41696
|
}
|
|
41487
41697
|
});
|
|
41488
41698
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
41489
|
-
const wfDir = (0,
|
|
41490
|
-
if (!(0,
|
|
41491
|
-
return (0,
|
|
41699
|
+
const wfDir = (0, import_node_path40.join)(cwd, ".github", "workflows");
|
|
41700
|
+
if (!(0, import_node_fs42.existsSync)(wfDir)) return [];
|
|
41701
|
+
return (0, import_node_fs42.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
41492
41702
|
try {
|
|
41493
|
-
return workflowReportsPrChecks((0,
|
|
41703
|
+
return workflowReportsPrChecks((0, import_node_fs42.readFileSync)((0, import_node_path40.join)(wfDir, name), "utf8"));
|
|
41494
41704
|
} catch {
|
|
41495
41705
|
return true;
|
|
41496
41706
|
}
|
|
@@ -41686,7 +41896,7 @@ ${list}`);
|
|
|
41686
41896
|
jsonParity(pr.command("review-verdict <number>").description("post an explicit review verdict comment: computes the PR diff patch-id and head sha, renders the v1 contract body, and posts it as a PR comment").requiredOption("--repo <owner/repo>", "target repo").requiredOption("--verdict <verdict>", `one of ${REVIEW_VERDICTS.join("|")}`).requiredOption("--scope <text>", "one line: what was reviewed").requiredOption("--risk <text>", "one line: the residual risk").option("--unverified <text>", "a claim the reviewer could not verify (repeatable)", (v, acc) => [...acc, v], []).requiredOption("--reviewer <id>", "reviewer seat or model id").option("--findings-file <path>", "Markdown findings appended after the JSON block")).action(async (number, o) => {
|
|
41687
41897
|
if (!isReviewVerdict(o.verdict)) return fail(`pr review-verdict: --verdict must be one of ${REVIEW_VERDICTS.join("|")} (got ${o.verdict})`);
|
|
41688
41898
|
const verdict = o.verdict;
|
|
41689
|
-
const findings = o.findingsFile ? (0,
|
|
41899
|
+
const findings = o.findingsFile ? (0, import_node_fs42.readFileSync)(o.findingsFile, "utf8") : void 0;
|
|
41690
41900
|
let patch;
|
|
41691
41901
|
let head;
|
|
41692
41902
|
try {
|
|
@@ -41801,7 +42011,7 @@ ${list}`);
|
|
|
41801
42011
|
const shape = track ? `${track} track` : "unresolved track \u2014 strict reading";
|
|
41802
42012
|
throw new Error(`pr land: base branch ${base} is a promotion target (${shape}) \u2014 promotion merges stay human-only`);
|
|
41803
42013
|
}
|
|
41804
|
-
return repo;
|
|
42014
|
+
return { repo, base };
|
|
41805
42015
|
},
|
|
41806
42016
|
fetchTrainAuthority: async (repo) => fetchTrainAuthority(repo, registryClientDeps(await loadConfig())),
|
|
41807
42017
|
resolveCiPolicy: (repo) => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
|
|
@@ -41916,7 +42126,9 @@ ${list}`);
|
|
|
41916
42126
|
}
|
|
41917
42127
|
const credential = await pollToken().catch(() => void 0) ? "app_installation" : "user";
|
|
41918
42128
|
if (o.json) printLine(JSON.stringify({ ...result, credential }));
|
|
41919
|
-
else printLine(
|
|
42129
|
+
else printLine(
|
|
42130
|
+
`pr land: ${result.status}${result.error ? ` \u2014 ${result.error}` : ""} (credential: ${credential})` + (result.branchFinality ? ` \u2014 ${result.branchFinality}` : "")
|
|
42131
|
+
);
|
|
41920
42132
|
if (result.status === "failed") process.exitCode = 1;
|
|
41921
42133
|
});
|
|
41922
42134
|
jsonParity(pr.command("merge <number>").description("merge a PR (squash by default); archives gitignored tmp/** before worktree teardown; on no-ci repos run pr ci-policy / checks-wait first (#1432, #5679)").option("--squash", "squash merge (default)").option("--merge", "create a merge commit").option("--rebase", "rebase merge").option("--repo <owner/repo>", "target repo (defaults to the current repo); from a foreign checkout the remote probe/delete address this repo and local cleanup runs only in the verified sibling checkout ../<repo>, else localBranch reports skipped-foreign-cwd and the receipt carries foreignCwd: true (#6148)").option("--auto", "enable auto-merge \u2014 merge once the base-branch policy is satisfied (use for policy-gated repos)").addOption(new Option("--disable-auto", "disable a queued auto-merge without merging").conflicts(["auto", "wait", "squash", "merge", "rebase", "preserveWorktree", "gc", "squashBodyFile", "force"])).option("--wait", `wait for checks to reach a terminal passing verdict before merging (default budget ${PR_CHECKS_TIMEOUT_MS / 6e4}m) \u2014 run as a background/monitor task or under a shell timeout above that budget; a short foreground timeout (e.g. 120s) kills it after checks pass and leaves the PR open (#6027)`).option("--preserve-worktree", "after merge, keep the local PR worktree/branch for an active batch (#1888); also the merge settings-proof bypass when delete_branch_on_merge cannot be proven (#6210)").option("--gc", "acknowledge deleting unarchived gitignored tmp/** evidence newer than the branch base (#5679)").option("--squash-body-file <path>", "squash commit body (overrides GitHub COMMIT_MESSAGES); use when a pushed commit mentions close/fix/resolve + #N that must not close (#5723)").option("--force", "acknowledge and merge past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").option("--without-review <reason>", "compatibility option; shared merge helpers do not require a review verdict")).action(async (number, o) => {
|
|
@@ -41958,7 +42170,7 @@ ${list}`);
|
|
|
41958
42170
|
const mergeSquashBody = squashBodyTextForMerge(
|
|
41959
42171
|
closingGuardInput,
|
|
41960
42172
|
method === "--squash",
|
|
41961
|
-
o.squashBodyFile ? (0,
|
|
42173
|
+
o.squashBodyFile ? (0, import_node_fs42.readFileSync)(o.squashBodyFile, "utf8") : void 0
|
|
41962
42174
|
);
|
|
41963
42175
|
if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
|
|
41964
42176
|
const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
|
|
@@ -41990,7 +42202,7 @@ ${list}`);
|
|
|
41990
42202
|
const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
|
|
41991
42203
|
let foreignCheckout;
|
|
41992
42204
|
if (foreignCwd) {
|
|
41993
|
-
const sibling = (0,
|
|
42205
|
+
const sibling = (0, import_node_path40.join)((0, import_node_path40.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
|
|
41994
42206
|
const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
|
|
41995
42207
|
if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
|
|
41996
42208
|
const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
|
|
@@ -42088,7 +42300,7 @@ ${list}`);
|
|
|
42088
42300
|
}
|
|
42089
42301
|
if (!repoForPostCleanup) throw e;
|
|
42090
42302
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
|
|
42091
|
-
const commitMessage = bodyFile ? (0,
|
|
42303
|
+
const commitMessage = bodyFile ? (0, import_node_fs42.readFileSync)(bodyFile, "utf8") : void 0;
|
|
42092
42304
|
await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
|
|
42093
42305
|
body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
|
|
42094
42306
|
timeoutMs: GH_MUTATION_TIMEOUT_MS
|
|
@@ -42208,7 +42420,7 @@ ${list}`);
|
|
|
42208
42420
|
preserveWorktree: o.preserveWorktree,
|
|
42209
42421
|
gcAcknowledged: o.gc,
|
|
42210
42422
|
expectedHeadOid: headRefOid,
|
|
42211
|
-
pathExists: (p) => (0,
|
|
42423
|
+
pathExists: (p) => (0, import_node_fs42.existsSync)(p),
|
|
42212
42424
|
// #5899: pin cleanup git calls to the main checkout — the task worktree this process may be
|
|
42213
42425
|
// standing in is removed mid-cleanup, so a cwd-relative invocation fails with
|
|
42214
42426
|
// 'fatal: not a git repository' and leaves a spurious partial-cleanup exit.
|
|
@@ -42329,7 +42541,7 @@ ${list}`);
|
|
|
42329
42541
|
}
|
|
42330
42542
|
|
|
42331
42543
|
// src/command-register-developer.ts
|
|
42332
|
-
var
|
|
42544
|
+
var import_node_fs47 = require("node:fs");
|
|
42333
42545
|
init_clean_exit();
|
|
42334
42546
|
init_cli_shared();
|
|
42335
42547
|
init_error_codes();
|
|
@@ -42362,7 +42574,7 @@ async function resolveWhoami(deps) {
|
|
|
42362
42574
|
}
|
|
42363
42575
|
|
|
42364
42576
|
// src/command-register-developer.ts
|
|
42365
|
-
var
|
|
42577
|
+
var import_node_path45 = require("node:path");
|
|
42366
42578
|
init_house_map();
|
|
42367
42579
|
init_hub_url();
|
|
42368
42580
|
|
|
@@ -42395,7 +42607,7 @@ async function executeWaveLand(plan, deps) {
|
|
|
42395
42607
|
}
|
|
42396
42608
|
|
|
42397
42609
|
// src/box-commands.ts
|
|
42398
|
-
var
|
|
42610
|
+
var import_node_fs43 = require("node:fs");
|
|
42399
42611
|
init_clean_exit();
|
|
42400
42612
|
|
|
42401
42613
|
// src/box.ts
|
|
@@ -42607,7 +42819,7 @@ function registerBoxCommands(program3) {
|
|
|
42607
42819
|
}
|
|
42608
42820
|
const remote = command && command.length ? command.join(" ") : void 0;
|
|
42609
42821
|
const wroteScript = o.ssh && o.script ? o.script : null;
|
|
42610
|
-
if (wroteScript) (0,
|
|
42822
|
+
if (wroteScript) (0, import_node_fs43.writeFileSync)(wroteScript, sshRecipeScript(found, remote), "utf8");
|
|
42611
42823
|
if (o.json) {
|
|
42612
42824
|
console.log(JSON.stringify({
|
|
42613
42825
|
box: found,
|
|
@@ -42629,19 +42841,19 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
42629
42841
|
// src/dist-drift.ts
|
|
42630
42842
|
var import_node_child_process16 = require("node:child_process");
|
|
42631
42843
|
var import_node_crypto13 = require("node:crypto");
|
|
42632
|
-
var
|
|
42844
|
+
var import_node_fs45 = require("node:fs");
|
|
42633
42845
|
var import_node_os20 = require("node:os");
|
|
42634
|
-
var
|
|
42846
|
+
var import_node_path42 = require("node:path");
|
|
42635
42847
|
|
|
42636
42848
|
// ../scripts/distribution-digest.mjs
|
|
42637
42849
|
var import_node_crypto12 = require("node:crypto");
|
|
42638
|
-
var
|
|
42639
|
-
var
|
|
42850
|
+
var import_node_fs44 = require("node:fs");
|
|
42851
|
+
var import_node_path41 = require("node:path");
|
|
42640
42852
|
var slash = (value) => value.replaceAll("\\", "/");
|
|
42641
42853
|
function repoPath(root, declaredPath, label) {
|
|
42642
|
-
const absoluteRoot = (0,
|
|
42643
|
-
const target = (0,
|
|
42644
|
-
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${
|
|
42854
|
+
const absoluteRoot = (0, import_node_path41.resolve)(root);
|
|
42855
|
+
const target = (0, import_node_path41.resolve)(root, declaredPath);
|
|
42856
|
+
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path41.sep}`)) {
|
|
42645
42857
|
throw new Error(`${label} ${declaredPath} escapes the repository root`);
|
|
42646
42858
|
}
|
|
42647
42859
|
return target;
|
|
@@ -42649,7 +42861,7 @@ function repoPath(root, declaredPath, label) {
|
|
|
42649
42861
|
function digestFiles(files) {
|
|
42650
42862
|
const hash = (0, import_node_crypto12.createHash)("sha256");
|
|
42651
42863
|
for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
|
|
42652
|
-
const content = file.stat.isSymbolicLink() ? Buffer.from((0,
|
|
42864
|
+
const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs44.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs44.readFileSync)(file.absolute);
|
|
42653
42865
|
hash.update(file.relative, "utf8");
|
|
42654
42866
|
hash.update("\0");
|
|
42655
42867
|
hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
|
|
@@ -42664,8 +42876,8 @@ function digestFiles(files) {
|
|
|
42664
42876
|
function digestPackedFiles(packageRoot, packedFiles) {
|
|
42665
42877
|
return digestFiles(packedFiles.map((path2) => {
|
|
42666
42878
|
const absolute = repoPath(packageRoot, path2, "packed artifact identity path");
|
|
42667
|
-
if (!(0,
|
|
42668
|
-
return { absolute, relative: slash(path2), stat: (0,
|
|
42879
|
+
if (!(0, import_node_fs44.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
|
|
42880
|
+
return { absolute, relative: slash(path2), stat: (0, import_node_fs44.lstatSync)(absolute) };
|
|
42669
42881
|
}));
|
|
42670
42882
|
}
|
|
42671
42883
|
|
|
@@ -42749,13 +42961,13 @@ function renderDistDriftReceipt(receipt) {
|
|
|
42749
42961
|
return lines2;
|
|
42750
42962
|
}
|
|
42751
42963
|
function readOrNull(path2) {
|
|
42752
|
-
return (0,
|
|
42964
|
+
return (0, import_node_fs45.existsSync)(path2) ? (0, import_node_fs45.readFileSync)(path2) : null;
|
|
42753
42965
|
}
|
|
42754
42966
|
function walkFiles(root) {
|
|
42755
42967
|
const files = [];
|
|
42756
42968
|
const walk2 = (directory) => {
|
|
42757
|
-
for (const entry of (0,
|
|
42758
|
-
const child2 = (0,
|
|
42969
|
+
for (const entry of (0, import_node_fs45.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
42970
|
+
const child2 = (0, import_node_path42.join)(directory, entry.name);
|
|
42759
42971
|
if (entry.isDirectory()) walk2(child2);
|
|
42760
42972
|
else files.push(child2);
|
|
42761
42973
|
}
|
|
@@ -42765,10 +42977,10 @@ function walkFiles(root) {
|
|
|
42765
42977
|
}
|
|
42766
42978
|
function bomPathFor(root) {
|
|
42767
42979
|
try {
|
|
42768
|
-
const registry2 = JSON.parse((0,
|
|
42769
|
-
return (0,
|
|
42980
|
+
const registry2 = JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path42.join)(root, "surfaces.json"), "utf8"));
|
|
42981
|
+
return (0, import_node_path42.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
|
|
42770
42982
|
} catch {
|
|
42771
|
-
return (0,
|
|
42983
|
+
return (0, import_node_path42.join)(root, "distribution-bom.json");
|
|
42772
42984
|
}
|
|
42773
42985
|
}
|
|
42774
42986
|
function rebuildTo(packageRoot, outDir) {
|
|
@@ -42781,35 +42993,35 @@ function rebuildTo(packageRoot, outDir) {
|
|
|
42781
42993
|
});
|
|
42782
42994
|
}
|
|
42783
42995
|
function runDistStatus(root) {
|
|
42784
|
-
const stage = (0,
|
|
42996
|
+
const stage = (0, import_node_fs45.mkdtempSync)((0, import_node_path42.join)((0, import_node_os20.tmpdir)(), "mmi-dist-drift-"));
|
|
42785
42997
|
let overlayCount = 0;
|
|
42786
42998
|
try {
|
|
42787
|
-
const cliOut = (0,
|
|
42788
|
-
const hubOut = (0,
|
|
42789
|
-
rebuildTo((0,
|
|
42790
|
-
rebuildTo((0,
|
|
42999
|
+
const cliOut = (0, import_node_path42.join)(stage, "cli-dist");
|
|
43000
|
+
const hubOut = (0, import_node_path42.join)(stage, "hub-dist");
|
|
43001
|
+
rebuildTo((0, import_node_path42.join)(root, "cli"), cliOut);
|
|
43002
|
+
rebuildTo((0, import_node_path42.join)(root, "updater"), hubOut);
|
|
42791
43003
|
const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
|
|
42792
43004
|
const rebuilt = (path2) => {
|
|
42793
43005
|
const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
|
|
42794
|
-
return spec ? readOrNull((0,
|
|
43006
|
+
return spec ? readOrNull((0, import_node_path42.join)(outDirFor(spec.packageDir), spec.output)) : null;
|
|
42795
43007
|
};
|
|
42796
|
-
const committed = (path2) => readOrNull((0,
|
|
42797
|
-
const tree = (path2) => readOrNull((0,
|
|
42798
|
-
const distRoot = (0,
|
|
42799
|
-
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0,
|
|
42800
|
-
const bom = JSON.parse((0,
|
|
43008
|
+
const committed = (path2) => readOrNull((0, import_node_path42.join)(root, path2));
|
|
43009
|
+
const tree = (path2) => readOrNull((0, import_node_path42.join)(root, path2));
|
|
43010
|
+
const distRoot = (0, import_node_path42.join)(root, "cli", "dist");
|
|
43011
|
+
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path42.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
|
|
43012
|
+
const bom = JSON.parse((0, import_node_fs45.readFileSync)(bomPathFor(root), "utf8"));
|
|
42801
43013
|
const digest = (entries) => {
|
|
42802
|
-
const overlay = (0,
|
|
43014
|
+
const overlay = (0, import_node_path42.join)(stage, `overlay-${overlayCount++}`);
|
|
42803
43015
|
for (const entry of entries) {
|
|
42804
|
-
const target = (0,
|
|
42805
|
-
(0,
|
|
42806
|
-
(0,
|
|
43016
|
+
const target = (0, import_node_path42.join)(overlay, entry.path);
|
|
43017
|
+
(0, import_node_fs45.mkdirSync)((0, import_node_path42.dirname)(target), { recursive: true });
|
|
43018
|
+
(0, import_node_fs45.writeFileSync)(target, entry.bytes);
|
|
42807
43019
|
}
|
|
42808
43020
|
return digestPackedFiles(overlay, entries.map((entry) => entry.path));
|
|
42809
43021
|
};
|
|
42810
43022
|
return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
|
|
42811
43023
|
} finally {
|
|
42812
|
-
(0,
|
|
43024
|
+
(0, import_node_fs45.rmSync)(stage, { recursive: true, force: true });
|
|
42813
43025
|
}
|
|
42814
43026
|
}
|
|
42815
43027
|
|
|
@@ -42888,7 +43100,7 @@ init_hub_auth();
|
|
|
42888
43100
|
|
|
42889
43101
|
// src/schedules-lift-command.ts
|
|
42890
43102
|
var import_promises8 = require("node:fs/promises");
|
|
42891
|
-
var
|
|
43103
|
+
var import_node_path43 = require("node:path");
|
|
42892
43104
|
init_clean_exit();
|
|
42893
43105
|
init_cli_shared();
|
|
42894
43106
|
var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
|
|
@@ -42917,7 +43129,7 @@ async function readWorkflowFiles(dir) {
|
|
|
42917
43129
|
const files = [];
|
|
42918
43130
|
for (const name of names.sort()) {
|
|
42919
43131
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
42920
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises8.readFile)((0,
|
|
43132
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises8.readFile)((0, import_node_path43.join)(dir, name), "utf8") });
|
|
42921
43133
|
}
|
|
42922
43134
|
return files;
|
|
42923
43135
|
}
|
|
@@ -43000,8 +43212,8 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
|
|
|
43000
43212
|
|
|
43001
43213
|
// src/spawn-policy-core.ts
|
|
43002
43214
|
var import_node_child_process17 = require("node:child_process");
|
|
43003
|
-
var
|
|
43004
|
-
var
|
|
43215
|
+
var import_node_fs46 = require("node:fs");
|
|
43216
|
+
var import_node_path44 = require("node:path");
|
|
43005
43217
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
43006
43218
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
43007
43219
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -43087,7 +43299,7 @@ function runSpawnPolicy(root) {
|
|
|
43087
43299
|
for (const file of files) {
|
|
43088
43300
|
let raw;
|
|
43089
43301
|
try {
|
|
43090
|
-
raw = (0,
|
|
43302
|
+
raw = (0, import_node_fs46.readFileSync)((0, import_node_path44.join)(root, file), "utf8");
|
|
43091
43303
|
} catch {
|
|
43092
43304
|
continue;
|
|
43093
43305
|
}
|
|
@@ -43107,19 +43319,19 @@ function runSpawnPolicy(root) {
|
|
|
43107
43319
|
function registerDeveloperCommands(program3) {
|
|
43108
43320
|
const rules = program3.command("rules").description("org-managed .gitignore delivery");
|
|
43109
43321
|
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) => {
|
|
43110
|
-
const path2 = (0,
|
|
43111
|
-
const current = (0,
|
|
43322
|
+
const path2 = (0, import_node_path45.join)(process.cwd(), ".gitignore");
|
|
43323
|
+
const current = (0, import_node_fs47.existsSync)(path2) ? (0, import_node_fs47.readFileSync)(path2, "utf8") : null;
|
|
43112
43324
|
const plan = planManagedGitignore(current);
|
|
43113
43325
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
43114
43326
|
if (opts.json) {
|
|
43115
|
-
if (opts.write && plan.changed) (0,
|
|
43327
|
+
if (opts.write && plan.changed) (0, import_node_fs47.writeFileSync)(path2, plan.content, "utf8");
|
|
43116
43328
|
console.log(JSON.stringify(plan, null, 2));
|
|
43117
43329
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
43118
43330
|
return;
|
|
43119
43331
|
}
|
|
43120
43332
|
if (opts.write) {
|
|
43121
43333
|
if (plan.changed) {
|
|
43122
|
-
(0,
|
|
43334
|
+
(0, import_node_fs47.writeFileSync)(path2, plan.content, "utf8");
|
|
43123
43335
|
console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
|
|
43124
43336
|
} else {
|
|
43125
43337
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
@@ -44753,8 +44965,8 @@ function checkHotfixCarries(options) {
|
|
|
44753
44965
|
}
|
|
44754
44966
|
|
|
44755
44967
|
// src/train-commands.ts
|
|
44756
|
-
var
|
|
44757
|
-
var
|
|
44968
|
+
var import_node_fs48 = require("node:fs");
|
|
44969
|
+
var import_node_path46 = require("node:path");
|
|
44758
44970
|
init_cli_shared();
|
|
44759
44971
|
init_clean_exit();
|
|
44760
44972
|
init_client_version();
|
|
@@ -44769,7 +44981,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
44769
44981
|
}
|
|
44770
44982
|
function readRepoVersion() {
|
|
44771
44983
|
try {
|
|
44772
|
-
return JSON.parse((0,
|
|
44984
|
+
return JSON.parse((0, import_node_fs48.readFileSync)((0, import_node_path46.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
44773
44985
|
} catch {
|
|
44774
44986
|
return void 0;
|
|
44775
44987
|
}
|
|
@@ -45735,7 +45947,7 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
45735
45947
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
45736
45948
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
45737
45949
|
function envHealLockPath(home) {
|
|
45738
|
-
return (0,
|
|
45950
|
+
return (0, import_node_path47.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
45739
45951
|
}
|
|
45740
45952
|
async function withEnvHealLock(what, run) {
|
|
45741
45953
|
try {
|
|
@@ -45872,7 +46084,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
45872
46084
|
);
|
|
45873
46085
|
const result = applyPluginCachePlan(
|
|
45874
46086
|
plan,
|
|
45875
|
-
(p) => (0,
|
|
46087
|
+
(p) => (0, import_node_fs49.rmSync)(p, { recursive: true }),
|
|
45876
46088
|
stagingApplyFsGuard(configRoot)
|
|
45877
46089
|
);
|
|
45878
46090
|
return {
|
|
@@ -45907,7 +46119,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
45907
46119
|
// has no generated routing index would
|
|
45908
46120
|
// get a permanent — demanding an artifact it never asked for.
|
|
45909
46121
|
docsIndexState: (root) => {
|
|
45910
|
-
if (!(0,
|
|
46122
|
+
if (!(0, import_node_fs49.existsSync)((0, import_node_path47.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
45911
46123
|
const real = createDocsIndexDeps(root);
|
|
45912
46124
|
let docs;
|
|
45913
46125
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -45916,7 +46128,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
45916
46128
|
},
|
|
45917
46129
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
45918
46130
|
healDocsIndex: (root) => {
|
|
45919
|
-
if (!(0,
|
|
46131
|
+
if (!(0, import_node_fs49.existsSync)((0, import_node_path47.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
45920
46132
|
const real = createDocsIndexDeps(root);
|
|
45921
46133
|
let docs;
|
|
45922
46134
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -46542,7 +46754,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
46542
46754
|
if (dupe) return fail(`org project set: KEY "${dupe}" was passed to both --var and --set; --set is an alias of --var, so pass each KEY once`);
|
|
46543
46755
|
if (o.secretsFile) {
|
|
46544
46756
|
try {
|
|
46545
|
-
vars.push(`secrets=${(0,
|
|
46757
|
+
vars.push(`secrets=${(0, import_node_fs49.readFileSync)(o.secretsFile, "utf8")}`);
|
|
46546
46758
|
} catch (e) {
|
|
46547
46759
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
46548
46760
|
}
|
|
@@ -46875,16 +47087,16 @@ function ciAuditDeps2() {
|
|
|
46875
47087
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
46876
47088
|
readSeedFile: (path2) => {
|
|
46877
47089
|
if (!root) return null;
|
|
46878
|
-
const fullPath = (0,
|
|
46879
|
-
return (0,
|
|
47090
|
+
const fullPath = (0, import_node_path47.join)(root, path2);
|
|
47091
|
+
return (0, import_node_fs49.existsSync)(fullPath) ? (0, import_node_fs49.readFileSync)(fullPath, "utf8") : null;
|
|
46880
47092
|
}
|
|
46881
47093
|
};
|
|
46882
47094
|
}
|
|
46883
47095
|
function hubRoot2() {
|
|
46884
|
-
const fromPkg = (0,
|
|
47096
|
+
const fromPkg = (0, import_node_path47.join)(__dirname, "..", "..");
|
|
46885
47097
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
46886
|
-
if ((0,
|
|
46887
|
-
if ((0,
|
|
47098
|
+
if ((0, import_node_fs49.existsSync)((0, import_node_path47.join)(fromPkg, marker))) return fromPkg;
|
|
47099
|
+
if ((0, import_node_fs49.existsSync)((0, import_node_path47.join)(process.cwd(), marker))) return process.cwd();
|
|
46888
47100
|
return null;
|
|
46889
47101
|
}
|
|
46890
47102
|
registerQueryCommands(program2);
|
|
@@ -46984,10 +47196,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
46984
47196
|
targets = resolution.targets;
|
|
46985
47197
|
}
|
|
46986
47198
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
46987
|
-
const fileMatrix = (0,
|
|
47199
|
+
const fileMatrix = (0, import_node_fs49.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs49.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
46988
47200
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
46989
47201
|
const dataAccess = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
46990
|
-
const sanctioned = (0,
|
|
47202
|
+
const sanctioned = (0, import_node_fs49.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs49.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
46991
47203
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
46992
47204
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
46993
47205
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -47018,16 +47230,16 @@ function directoryBytes(path2) {
|
|
|
47018
47230
|
let total = 0;
|
|
47019
47231
|
let entries;
|
|
47020
47232
|
try {
|
|
47021
|
-
entries = (0,
|
|
47233
|
+
entries = (0, import_node_fs49.readdirSync)(path2, { withFileTypes: true });
|
|
47022
47234
|
} catch {
|
|
47023
47235
|
return 0;
|
|
47024
47236
|
}
|
|
47025
47237
|
for (const entry of entries) {
|
|
47026
|
-
const child2 = (0,
|
|
47238
|
+
const child2 = (0, import_node_path47.join)(path2, entry.name);
|
|
47027
47239
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
47028
47240
|
else {
|
|
47029
47241
|
try {
|
|
47030
|
-
total += (0,
|
|
47242
|
+
total += (0, import_node_fs49.statSync)(child2).size;
|
|
47031
47243
|
} catch {
|
|
47032
47244
|
}
|
|
47033
47245
|
}
|
|
@@ -47035,25 +47247,25 @@ function directoryBytes(path2) {
|
|
|
47035
47247
|
return total;
|
|
47036
47248
|
}
|
|
47037
47249
|
function listDirEntries(dir) {
|
|
47038
|
-
return (0,
|
|
47250
|
+
return (0, import_node_fs49.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
47039
47251
|
}
|
|
47040
47252
|
function readInstalledPluginRefs(configRoot) {
|
|
47041
47253
|
const p = installedPluginsPathForConfig(configRoot);
|
|
47042
|
-
if (!(0,
|
|
47254
|
+
if (!(0, import_node_fs49.existsSync)(p)) return [];
|
|
47043
47255
|
try {
|
|
47044
|
-
return installedPluginPaths((0,
|
|
47256
|
+
return installedPluginPaths((0, import_node_fs49.readFileSync)(p, "utf8"));
|
|
47045
47257
|
} catch {
|
|
47046
47258
|
return null;
|
|
47047
47259
|
}
|
|
47048
47260
|
}
|
|
47049
47261
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
47050
47262
|
return {
|
|
47051
|
-
exists: (p) => (0,
|
|
47052
|
-
listVersionDirs: (root) => (0,
|
|
47263
|
+
exists: (p) => (0, import_node_fs49.existsSync)(p),
|
|
47264
|
+
listVersionDirs: (root) => (0, import_node_fs49.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
47053
47265
|
dirBytes,
|
|
47054
|
-
listStagingDirs: (root) => (0,
|
|
47266
|
+
listStagingDirs: (root) => (0, import_node_fs49.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
47055
47267
|
try {
|
|
47056
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
47268
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path47.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs49.statSync)(p).mtimeMs) };
|
|
47057
47269
|
} catch {
|
|
47058
47270
|
return { name: d.name, mtimeMs: Date.now() };
|
|
47059
47271
|
}
|
|
@@ -47067,10 +47279,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
47067
47279
|
return {
|
|
47068
47280
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
47069
47281
|
mtimeMs: (name) => {
|
|
47070
|
-
const p = (0,
|
|
47071
|
-
if (!(0,
|
|
47282
|
+
const p = (0, import_node_path47.join)(stagingRoot, name);
|
|
47283
|
+
if (!(0, import_node_fs49.existsSync)(p)) return null;
|
|
47072
47284
|
try {
|
|
47073
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
47285
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs49.statSync)(q).mtimeMs);
|
|
47074
47286
|
} catch {
|
|
47075
47287
|
return null;
|
|
47076
47288
|
}
|
|
@@ -47096,7 +47308,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
47096
47308
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
47097
47309
|
);
|
|
47098
47310
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
47099
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
47311
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs49.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
47100
47312
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
47101
47313
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
47102
47314
|
else console.log(renderPluginCachePlan(plan, result));
|