@mutmutco/codex-plugin 4.5.104 → 4.5.105
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/cli/dist/main.cjs
CHANGED
|
@@ -4747,7 +4747,7 @@ function useColor() {
|
|
|
4747
4747
|
var program = new Command();
|
|
4748
4748
|
|
|
4749
4749
|
// src/command-composition.ts
|
|
4750
|
-
var
|
|
4750
|
+
var import_node_fs55 = require("node:fs");
|
|
4751
4751
|
init_clean_exit();
|
|
4752
4752
|
init_cli_shared();
|
|
4753
4753
|
|
|
@@ -17699,10 +17699,10 @@ var rollout_plan_default = {
|
|
|
17699
17699
|
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)."
|
|
17700
17700
|
},
|
|
17701
17701
|
baseline: {
|
|
17702
|
-
version: "4.5.
|
|
17703
|
-
tag: "v4.5.
|
|
17704
|
-
commit: "
|
|
17705
|
-
npm: "@mutmutco/cli@4.5.
|
|
17702
|
+
version: "4.5.105",
|
|
17703
|
+
tag: "v4.5.105",
|
|
17704
|
+
commit: "2edfdb644608",
|
|
17705
|
+
npm: "@mutmutco/cli@4.5.105"
|
|
17706
17706
|
},
|
|
17707
17707
|
exitCriterion: "fleet-n-of-n",
|
|
17708
17708
|
hubOnlyShortcut: "forbidden",
|
|
@@ -17719,14 +17719,14 @@ var rollout_plan_default = {
|
|
|
17719
17719
|
repo: "mutmutco/mmi-hub",
|
|
17720
17720
|
role: "canary",
|
|
17721
17721
|
schedule: "train",
|
|
17722
|
-
v3Target: "v4.5.
|
|
17722
|
+
v3Target: "v4.5.105"
|
|
17723
17723
|
}
|
|
17724
17724
|
],
|
|
17725
17725
|
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.",
|
|
17726
17726
|
rollback: {
|
|
17727
17727
|
independent: true,
|
|
17728
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
17729
|
-
v3Target: "v4.5.
|
|
17728
|
+
mechanism: "npm dist-tag latest -> 4.5.105 and redeploy the Hub Lambda from tag v4.5.105 (2edfdb644608); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
17729
|
+
v3Target: "v4.5.105 (@mutmutco/cli@4.5.105, tag commit 2edfdb644608 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
17730
17730
|
}
|
|
17731
17731
|
},
|
|
17732
17732
|
{
|
|
@@ -21992,7 +21992,7 @@ function renderTenantControl(r) {
|
|
|
21992
21992
|
}
|
|
21993
21993
|
|
|
21994
21994
|
// src/train-apply-phases.ts
|
|
21995
|
-
var
|
|
21995
|
+
var import_node_fs25 = require("node:fs");
|
|
21996
21996
|
var import_promises4 = require("node:fs/promises");
|
|
21997
21997
|
var import_node_path24 = require("node:path");
|
|
21998
21998
|
|
|
@@ -25765,6 +25765,7 @@ function formatTrainDoctor(v) {
|
|
|
25765
25765
|
|
|
25766
25766
|
// src/release-plain-words.ts
|
|
25767
25767
|
var import_promises3 = require("node:fs/promises");
|
|
25768
|
+
var import_node_fs24 = require("node:fs");
|
|
25768
25769
|
function releaseCutByNotes(cutBy) {
|
|
25769
25770
|
return `## Internal
|
|
25770
25771
|
Cut by ${formatSessionIdentity(cutBy)}
|
|
@@ -25779,8 +25780,44 @@ function buildPlainWordsSection(lines2, big, heading = PLAIN_WORDS_HEADING) {
|
|
|
25779
25780
|
if (big) body.push(PLAIN_WORDS_MARKER);
|
|
25780
25781
|
return body.join("\n");
|
|
25781
25782
|
}
|
|
25782
|
-
|
|
25783
|
-
|
|
25783
|
+
var SUMMARY_GUIDE = "https://github.com/mutmutco/MMI-Hub/blob/development/docs/Guides/train-troubleshooting.md#announce-summary-missing";
|
|
25784
|
+
function validatePlainWordsSummary(content) {
|
|
25785
|
+
const lines2 = content.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
|
|
25786
|
+
if (lines2[0]?.startsWith("No changes to this module")) throw new Error(`requires a real MM-Work title, not the no-change placeholder \u2014 see ${SUMMARY_GUIDE}`);
|
|
25787
|
+
if (lines2.length < 3 || lines2.length > 6) throw new Error(`requires 3\u20136 curated non-empty lines (title + 2\u20135 notes; found ${lines2.length}) \u2014 see ${SUMMARY_GUIDE}`);
|
|
25788
|
+
}
|
|
25789
|
+
function validateSuiteReleaseSummary(path2) {
|
|
25790
|
+
if (!path2) throw new Error(`requires --announce-summary-file <path> before promoting any member \u2014 see ${SUMMARY_GUIDE}`);
|
|
25791
|
+
let content;
|
|
25792
|
+
try {
|
|
25793
|
+
content = (0, import_node_fs24.readFileSync)(path2, "utf8");
|
|
25794
|
+
} catch (e) {
|
|
25795
|
+
throw new Error(`could not read summary file ${path2}: ${e.message} \u2014 see ${SUMMARY_GUIDE}`);
|
|
25796
|
+
}
|
|
25797
|
+
validatePlainWordsSummary(content);
|
|
25798
|
+
}
|
|
25799
|
+
async function validateReleaseSummaries(releases, files, releaseExists) {
|
|
25800
|
+
const refusals = [];
|
|
25801
|
+
for (const { repo, tag } of releases) {
|
|
25802
|
+
if (await releaseExists(repo, tag)) continue;
|
|
25803
|
+
try {
|
|
25804
|
+
validateSuiteReleaseSummary(files[repo]);
|
|
25805
|
+
} catch (e) {
|
|
25806
|
+
refusals.push(`${repo}: ${e.message}`);
|
|
25807
|
+
}
|
|
25808
|
+
}
|
|
25809
|
+
if (refusals.length) throw new Error(`suite release summaries refused; no member was released:
|
|
25810
|
+
${refusals.join("\n")}`);
|
|
25811
|
+
}
|
|
25812
|
+
async function requireSuiteReleaseSummary(deps, path2) {
|
|
25813
|
+
if (!path2) throw new Error(`a new mm-work suite Release requires --announce-summary-file <path> with a title + 2\u20135 notes \u2014 see ${SUMMARY_GUIDE}`);
|
|
25814
|
+
let content;
|
|
25815
|
+
try {
|
|
25816
|
+
content = await (deps.readFile ?? ((p) => (0, import_promises3.readFile)(p, "utf8")))(path2);
|
|
25817
|
+
} catch (e) {
|
|
25818
|
+
throw new Error(`could not read --announce-summary-file ${path2}: ${e.message} \u2014 see ${SUMMARY_GUIDE}`);
|
|
25819
|
+
}
|
|
25820
|
+
validatePlainWordsSummary(content);
|
|
25784
25821
|
}
|
|
25785
25822
|
function upsertPlainWordsSection(body, section) {
|
|
25786
25823
|
let rest = body;
|
|
@@ -25810,7 +25847,8 @@ function plainWordsSummaryLines(content) {
|
|
|
25810
25847
|
async function writePlainWordsIfSuiteMember(deps, args) {
|
|
25811
25848
|
if (args.meta?.suite !== "mm-work") return;
|
|
25812
25849
|
try {
|
|
25813
|
-
|
|
25850
|
+
if (!args.summaryLines?.length) return;
|
|
25851
|
+
const lines2 = args.summaryLines;
|
|
25814
25852
|
const section = [
|
|
25815
25853
|
buildPlainWordsSection(lines2, args.big === true),
|
|
25816
25854
|
...args.summaryLinesTr?.length ? [buildPlainWordsSection(args.summaryLinesTr, false, PLAIN_WORDS_HEADING_TR)] : []
|
|
@@ -25962,14 +26000,14 @@ function readLocalGateWorkflows() {
|
|
|
25962
26000
|
const dir = (0, import_node_path24.join)(".github", "workflows");
|
|
25963
26001
|
let names;
|
|
25964
26002
|
try {
|
|
25965
|
-
names = (0,
|
|
26003
|
+
names = (0, import_node_fs25.readdirSync)(dir);
|
|
25966
26004
|
} catch {
|
|
25967
26005
|
return null;
|
|
25968
26006
|
}
|
|
25969
26007
|
const files = [];
|
|
25970
26008
|
for (const name of names.filter(isGateWorkflowPath)) {
|
|
25971
26009
|
try {
|
|
25972
|
-
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0,
|
|
26010
|
+
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs25.readFileSync)((0, import_node_path24.join)(dir, name), "utf8") });
|
|
25973
26011
|
} catch {
|
|
25974
26012
|
}
|
|
25975
26013
|
}
|
|
@@ -25979,14 +26017,14 @@ function readLocalWorkflows() {
|
|
|
25979
26017
|
const dir = (0, import_node_path24.join)(".github", "workflows");
|
|
25980
26018
|
let names;
|
|
25981
26019
|
try {
|
|
25982
|
-
names = (0,
|
|
26020
|
+
names = (0, import_node_fs25.readdirSync)(dir);
|
|
25983
26021
|
} catch {
|
|
25984
26022
|
return null;
|
|
25985
26023
|
}
|
|
25986
26024
|
const files = [];
|
|
25987
26025
|
for (const name of names.filter((n) => /\.ya?ml$/i.test(n))) {
|
|
25988
26026
|
try {
|
|
25989
|
-
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0,
|
|
26027
|
+
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs25.readFileSync)((0, import_node_path24.join)(dir, name), "utf8") });
|
|
25990
26028
|
} catch {
|
|
25991
26029
|
}
|
|
25992
26030
|
}
|
|
@@ -26868,7 +26906,7 @@ async function activeRcandLedgerCandidate(deps, cwd) {
|
|
|
26868
26906
|
}
|
|
26869
26907
|
let prior;
|
|
26870
26908
|
try {
|
|
26871
|
-
prior = parseReleaseLedger((0,
|
|
26909
|
+
prior = parseReleaseLedger((0, import_node_fs25.readFileSync)(path2, "utf8"));
|
|
26872
26910
|
} catch {
|
|
26873
26911
|
return void 0;
|
|
26874
26912
|
}
|
|
@@ -27157,6 +27195,7 @@ Nothing was written. Inspect ${ledger.path} (or clear it only after proving the
|
|
|
27157
27195
|
}
|
|
27158
27196
|
const ledgerMode = persisted ? "strict" : "migration";
|
|
27159
27197
|
const partialRelease = await isStrayUnreleasedTag(deps, tag, tagSha, ctx.repo);
|
|
27198
|
+
if (partialRelease && meta.suite === "mm-work") await requireSuiteReleaseSummary(deps, options.announceSummaryFile);
|
|
27160
27199
|
if (!partialRelease) {
|
|
27161
27200
|
try {
|
|
27162
27201
|
await verifyPublishedRelease(deps, ctx.repo, tag, "main", tagSha);
|
|
@@ -28031,6 +28070,7 @@ async function runTrainApplyLocked(command, deps, options = {}) {
|
|
|
28031
28070
|
const bumpIntent = await resolveTrainBumpIntent(deps, process.env, void 0, preOneZeroInForce(meta));
|
|
28032
28071
|
const branchHints = await loadReleaseTrackBranchHints(deps);
|
|
28033
28072
|
const directTrack = isHubControlRepo(ctx.repo) || resolveReleaseTrack(meta, branchHints) === "direct";
|
|
28073
|
+
if (command === "release" && meta.suite === "mm-work") await requireSuiteReleaseSummary(deps, options.announceSummaryFile);
|
|
28034
28074
|
const pipelineInput = { deps, ctx, command, meta, branchHints, watch, options, bumpIntent };
|
|
28035
28075
|
if (command === "rcand") {
|
|
28036
28076
|
if (directTrack) {
|
|
@@ -30741,7 +30781,7 @@ function registerBoardCommands(program3) {
|
|
|
30741
30781
|
}
|
|
30742
30782
|
|
|
30743
30783
|
// src/bootstrap-commands.ts
|
|
30744
|
-
var
|
|
30784
|
+
var import_node_fs29 = require("node:fs");
|
|
30745
30785
|
var import_node_os17 = require("node:os");
|
|
30746
30786
|
var import_node_path28 = require("node:path");
|
|
30747
30787
|
init_cli_shared();
|
|
@@ -30927,7 +30967,7 @@ init_github_client();
|
|
|
30927
30967
|
init_cli_shared();
|
|
30928
30968
|
|
|
30929
30969
|
// src/port-registry.ts
|
|
30930
|
-
var
|
|
30970
|
+
var import_node_fs26 = require("node:fs");
|
|
30931
30971
|
var import_node_path25 = require("node:path");
|
|
30932
30972
|
|
|
30933
30973
|
// ../infra/port-geometry.mjs
|
|
@@ -30942,8 +30982,8 @@ function nextPortBlock(registry2) {
|
|
|
30942
30982
|
return [base, base + PORT_SPAN];
|
|
30943
30983
|
}
|
|
30944
30984
|
function loadPortRegistry(path2) {
|
|
30945
|
-
if (!(0,
|
|
30946
|
-
const raw = JSON.parse((0,
|
|
30985
|
+
if (!(0, import_node_fs26.existsSync)(path2)) return {};
|
|
30986
|
+
const raw = JSON.parse((0, import_node_fs26.readFileSync)(path2, "utf8"));
|
|
30947
30987
|
const out = {};
|
|
30948
30988
|
for (const [key, value] of Object.entries(raw)) {
|
|
30949
30989
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -30973,7 +31013,7 @@ function existingPortRange(repo, registry2) {
|
|
|
30973
31013
|
function portRangeInfraAt(root, source) {
|
|
30974
31014
|
const registryPath = (0, import_node_path25.join)(root, "infra", "port-ranges.json");
|
|
30975
31015
|
const ddbScriptPath = (0, import_node_path25.join)(root, "infra", "port-ddb.mjs");
|
|
30976
|
-
if (!(0,
|
|
31016
|
+
if (!(0, import_node_fs26.existsSync)(registryPath) || !(0, import_node_fs26.existsSync)(ddbScriptPath)) return null;
|
|
30977
31017
|
return { root, source, registryPath, ddbScriptPath };
|
|
30978
31018
|
}
|
|
30979
31019
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
@@ -31055,7 +31095,7 @@ async function assignPersistedPortRange(repo, slug, reg, opts) {
|
|
|
31055
31095
|
}
|
|
31056
31096
|
|
|
31057
31097
|
// src/box-commands.ts
|
|
31058
|
-
var
|
|
31098
|
+
var import_node_fs28 = require("node:fs");
|
|
31059
31099
|
var import_node_child_process10 = require("node:child_process");
|
|
31060
31100
|
var import_node_os16 = require("node:os");
|
|
31061
31101
|
var import_node_path27 = require("node:path");
|
|
@@ -31187,7 +31227,7 @@ function applyRemoteScript(items, date) {
|
|
|
31187
31227
|
init_clean_exit();
|
|
31188
31228
|
|
|
31189
31229
|
// src/secrets-commands.ts
|
|
31190
|
-
var
|
|
31230
|
+
var import_node_fs27 = require("node:fs");
|
|
31191
31231
|
var import_node_path26 = require("node:path");
|
|
31192
31232
|
var import_node_os15 = require("node:os");
|
|
31193
31233
|
init_cli_shared();
|
|
@@ -32349,8 +32389,8 @@ async function decryptRailsCredentials(input) {
|
|
|
32349
32389
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
32350
32390
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
32351
32391
|
};
|
|
32352
|
-
if ((0,
|
|
32353
|
-
env.RAILS_MASTER_KEY = (0,
|
|
32392
|
+
if ((0, import_node_fs27.existsSync)(masterKeyPath)) {
|
|
32393
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs27.readFileSync)(masterKeyPath, "utf8").trim();
|
|
32354
32394
|
}
|
|
32355
32395
|
const script = [
|
|
32356
32396
|
'require "json"',
|
|
@@ -32360,9 +32400,9 @@ async function decryptRailsCredentials(input) {
|
|
|
32360
32400
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
32361
32401
|
"puts JSON.generate(config.config)"
|
|
32362
32402
|
].join("\n");
|
|
32363
|
-
const scriptDir = (0,
|
|
32403
|
+
const scriptDir = (0, import_node_fs27.mkdtempSync)((0, import_node_path26.join)((0, import_node_os15.tmpdir)(), "mmi-rails-decrypt-"));
|
|
32364
32404
|
const scriptPath = (0, import_node_path26.join)(scriptDir, "decrypt.rb");
|
|
32365
|
-
(0,
|
|
32405
|
+
(0, import_node_fs27.writeFileSync)(scriptPath, script, "utf8");
|
|
32366
32406
|
try {
|
|
32367
32407
|
const args = ["exec", "ruby", scriptPath];
|
|
32368
32408
|
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
@@ -32374,7 +32414,7 @@ async function decryptRailsCredentials(input) {
|
|
|
32374
32414
|
});
|
|
32375
32415
|
return JSON.parse(stdout);
|
|
32376
32416
|
} finally {
|
|
32377
|
-
(0,
|
|
32417
|
+
(0, import_node_fs27.rmSync)(scriptDir, { recursive: true, force: true });
|
|
32378
32418
|
}
|
|
32379
32419
|
}
|
|
32380
32420
|
async function readSecretStdin() {
|
|
@@ -32464,7 +32504,7 @@ function registerSecretsCommands(program3) {
|
|
|
32464
32504
|
let body;
|
|
32465
32505
|
if (o.file) {
|
|
32466
32506
|
try {
|
|
32467
|
-
body = (0,
|
|
32507
|
+
body = (0, import_node_fs27.readFileSync)((0, import_node_path26.resolve)(o.file), "utf8");
|
|
32468
32508
|
} catch (e) {
|
|
32469
32509
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
32470
32510
|
}
|
|
@@ -32600,7 +32640,7 @@ function registerSecretsCommands(program3) {
|
|
|
32600
32640
|
{
|
|
32601
32641
|
...d,
|
|
32602
32642
|
decryptRailsCredentials,
|
|
32603
|
-
removeFile: (path2) => (0,
|
|
32643
|
+
removeFile: (path2) => (0, import_node_fs27.unlinkSync)((0, import_node_path26.resolve)(o.appDir ?? process.cwd(), path2))
|
|
32604
32644
|
},
|
|
32605
32645
|
{
|
|
32606
32646
|
repo: o.repo,
|
|
@@ -32648,8 +32688,6 @@ function registerSecretsCommands(program3) {
|
|
|
32648
32688
|
var BOX_KEYS = {
|
|
32649
32689
|
/** Opens every MM box INCLUDING the CI runner (`mmi-runner`). The general-purpose MM key. */
|
|
32650
32690
|
mmMaster: "HETZNER_MASTER_SSH_PRIVATE_KEY",
|
|
32651
|
-
/** Opens the OGUZ project's boxes. */
|
|
32652
|
-
oguzMaster: "HETZNER_OGUZ_MASTER_SSH_PRIVATE_KEY",
|
|
32653
32691
|
/** The deploy key. Opens MM prod/tenant boxes but NOT `mmi-runner` — a real trap: reaching for this key
|
|
32654
32692
|
* to get into the CI runner fails, and the failure reads like a broken box rather than a wrong key. */
|
|
32655
32693
|
mmDeploy: "HETZNER_SSH_PRIVATE_KEY"
|
|
@@ -32658,11 +32696,10 @@ var HETZNER_VAULT_SLUG = "_org";
|
|
|
32658
32696
|
var HETZNER_NS = "hetzner";
|
|
32659
32697
|
var nsKey = (key) => `${HETZNER_NS}/${key}`;
|
|
32660
32698
|
var PROJECT_API_TOKEN = {
|
|
32661
|
-
MM: nsKey("HETZNER_MM_API_TOKEN")
|
|
32662
|
-
OGUZ: nsKey("HETZNER_OGUZ_API_TOKEN")
|
|
32699
|
+
MM: nsKey("HETZNER_MM_API_TOKEN")
|
|
32663
32700
|
};
|
|
32664
|
-
function sshKeyForProject(
|
|
32665
|
-
return
|
|
32701
|
+
function sshKeyForProject(_project) {
|
|
32702
|
+
return BOX_KEYS.mmMaster;
|
|
32666
32703
|
}
|
|
32667
32704
|
function vaultPath(key) {
|
|
32668
32705
|
return `/mmi-future/${HETZNER_VAULT_SLUG}/${HETZNER_NS}/${key}`;
|
|
@@ -32765,7 +32802,7 @@ var HETZNER_API = "https://api.hetzner.cloud/v1/servers";
|
|
|
32765
32802
|
var HETZNER_TIMEOUT_MS = 15e3;
|
|
32766
32803
|
var PER_PAGE = 50;
|
|
32767
32804
|
var MAX_PAGES = 20;
|
|
32768
|
-
var PROJECTS = ["MM"
|
|
32805
|
+
var PROJECTS = ["MM"];
|
|
32769
32806
|
async function fetchProjectBoxes(project2, token) {
|
|
32770
32807
|
const boxes = [];
|
|
32771
32808
|
let page = 1;
|
|
@@ -32859,7 +32896,7 @@ function registerBoxCommands(program3) {
|
|
|
32859
32896
|
}
|
|
32860
32897
|
const remote = command && command.length ? command.join(" ") : void 0;
|
|
32861
32898
|
const wroteScript = o.ssh && o.script ? o.script : null;
|
|
32862
|
-
if (wroteScript) (0,
|
|
32899
|
+
if (wroteScript) (0, import_node_fs28.writeFileSync)(wroteScript, sshRecipeScript(found, remote), "utf8");
|
|
32863
32900
|
if (o.json) {
|
|
32864
32901
|
console.log(JSON.stringify({
|
|
32865
32902
|
box: found,
|
|
@@ -32933,17 +32970,17 @@ function hubGitShow(ref) {
|
|
|
32933
32970
|
return (path2) => git3(["show", `${ref}:${path2}`]);
|
|
32934
32971
|
}
|
|
32935
32972
|
function runOnBox(found, script) {
|
|
32936
|
-
const dir = (0,
|
|
32973
|
+
const dir = (0, import_node_fs28.mkdtempSync)((0, import_node_path27.join)((0, import_node_os16.tmpdir)(), "mmi-box-control-"));
|
|
32937
32974
|
try {
|
|
32938
32975
|
const recipe = (0, import_node_path27.join)(dir, "recipe.sh");
|
|
32939
|
-
(0,
|
|
32976
|
+
(0, import_node_fs28.writeFileSync)(recipe, sshRecipeScript(found, "sh -s"), "utf8");
|
|
32940
32977
|
const r = (0, import_node_child_process10.spawnSync)("bash", [recipe], { input: script, encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 18e4, windowsHide: true });
|
|
32941
32978
|
if (r.status !== 0) {
|
|
32942
32979
|
throw new UnreadableError(`box control: the ssh session to ${found.name} failed (exit ${r.status ?? r.signal}): ${(r.stderr || "").trim().split("\n").slice(-3).join(" | ")}`);
|
|
32943
32980
|
}
|
|
32944
32981
|
return r.stdout;
|
|
32945
32982
|
} finally {
|
|
32946
|
-
(0,
|
|
32983
|
+
(0, import_node_fs28.rmSync)(dir, { recursive: true, force: true });
|
|
32947
32984
|
}
|
|
32948
32985
|
}
|
|
32949
32986
|
function formatControl(mode, box, rows) {
|
|
@@ -34578,12 +34615,12 @@ async function verifyTargetedSeedPr(repo, number, plan, target, expected, client
|
|
|
34578
34615
|
// src/bootstrap-commands.ts
|
|
34579
34616
|
var execGitForSeedSource = async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
34580
34617
|
async function gateRenderDiff(current, rendered) {
|
|
34581
|
-
const dir = (0,
|
|
34618
|
+
const dir = (0, import_node_fs29.mkdtempSync)((0, import_node_path28.join)((0, import_node_os17.tmpdir)(), "mmi-gate-diff-"));
|
|
34582
34619
|
const before = (0, import_node_path28.join)(dir, "committed-gate.yml");
|
|
34583
34620
|
const after = (0, import_node_path28.join)(dir, "registry-render.yml");
|
|
34584
34621
|
try {
|
|
34585
|
-
(0,
|
|
34586
|
-
(0,
|
|
34622
|
+
(0, import_node_fs29.writeFileSync)(before, current);
|
|
34623
|
+
(0, import_node_fs29.writeFileSync)(after, rendered);
|
|
34587
34624
|
try {
|
|
34588
34625
|
return (await execFileP("git", ["diff", "--no-index", "--", before, after], { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
34589
34626
|
} catch (e) {
|
|
@@ -34592,7 +34629,7 @@ async function gateRenderDiff(current, rendered) {
|
|
|
34592
34629
|
throw e;
|
|
34593
34630
|
}
|
|
34594
34631
|
} finally {
|
|
34595
|
-
(0,
|
|
34632
|
+
(0, import_node_fs29.rmSync)(dir, { recursive: true, force: true });
|
|
34596
34633
|
}
|
|
34597
34634
|
}
|
|
34598
34635
|
function bootstrapTrainDoctorDeps() {
|
|
@@ -34652,13 +34689,13 @@ function registerBootstrapCommands(program3) {
|
|
|
34652
34689
|
})),
|
|
34653
34690
|
pushAllowlistOwners: verifyOwners,
|
|
34654
34691
|
pushAllowlistOwnersRead: verifyOwnersRead,
|
|
34655
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
34692
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs29.existsSync)(path2) ? (0, import_node_fs29.readFileSync)(path2, "utf8") : null,
|
|
34656
34693
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
34657
34694
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
34658
34695
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
34659
34696
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
34660
34697
|
// sanction, which is the pre-#3664 behaviour.
|
|
34661
|
-
sanctionedAdmins: (0,
|
|
34698
|
+
sanctionedAdmins: (0, import_node_fs29.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs29.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
34662
34699
|
requiredGcpApis: (() => {
|
|
34663
34700
|
const v = meta?.requiredGcpApis;
|
|
34664
34701
|
if (Array.isArray(v)) return v;
|
|
@@ -34736,14 +34773,14 @@ function registerBootstrapCommands(program3) {
|
|
|
34736
34773
|
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 () => {
|
|
34737
34774
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
34738
34775
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
34739
|
-
if (!(0,
|
|
34776
|
+
if (!(0, import_node_fs29.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`);
|
|
34740
34777
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
34741
34778
|
if (!seedSource.ok) return fail(`bootstrap drift: ${seedSource.reason}`);
|
|
34742
|
-
const manifest = loadBootstrapSeeds((0,
|
|
34779
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs29.readFileSync)(manifestPath, "utf8"));
|
|
34743
34780
|
const hubContents = /* @__PURE__ */ new Map();
|
|
34744
34781
|
for (const s of manifest.seeds) {
|
|
34745
34782
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
34746
|
-
hubContents.set(s.target, (0,
|
|
34783
|
+
hubContents.set(s.target, (0, import_node_fs29.existsSync)(s.target) ? (0, import_node_fs29.readFileSync)(s.target, "utf8") : null);
|
|
34747
34784
|
}
|
|
34748
34785
|
let targets;
|
|
34749
34786
|
let classOf = (_repo) => "deployable";
|
|
@@ -34951,24 +34988,24 @@ function registerBootstrapCommands(program3) {
|
|
|
34951
34988
|
return;
|
|
34952
34989
|
}
|
|
34953
34990
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
34954
|
-
if (!(0,
|
|
34991
|
+
if (!(0, import_node_fs29.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`);
|
|
34955
34992
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
34956
34993
|
if (!seedSource.ok) return fail(`bootstrap apply: ${seedSource.reason}`);
|
|
34957
|
-
const manifest = loadBootstrapSeeds((0,
|
|
34994
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs29.readFileSync)(manifestPath, "utf8"));
|
|
34958
34995
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
34959
34996
|
const slug = parsedRepo.slug;
|
|
34960
34997
|
const onlyTarget = o.fromConfig && o.only.trim() === "gate.yml" ? ".github/workflows/gate.yml" : o.only.trim();
|
|
34961
34998
|
const gh2 = async (args) => execFileP("gh", args, { timeout: 2e4 });
|
|
34962
|
-
const readFile10 = (p) => (0,
|
|
34999
|
+
const readFile10 = (p) => (0, import_node_fs29.existsSync)(p) ? (0, import_node_fs29.readFileSync)(p, "utf8") : null;
|
|
34963
35000
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
34964
35001
|
const putSeed = async (target, content, ref, sha) => {
|
|
34965
35002
|
const tmp = (0, import_node_path28.join)((0, import_node_os17.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
34966
|
-
(0,
|
|
35003
|
+
(0, import_node_fs29.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
34967
35004
|
try {
|
|
34968
35005
|
await gh2(contentPutInputArgs(repo, target, tmp));
|
|
34969
35006
|
} finally {
|
|
34970
35007
|
try {
|
|
34971
|
-
(0,
|
|
35008
|
+
(0, import_node_fs29.unlinkSync)(tmp);
|
|
34972
35009
|
} catch {
|
|
34973
35010
|
}
|
|
34974
35011
|
}
|
|
@@ -35604,10 +35641,10 @@ LIVE apply to ${repo}:
|
|
|
35604
35641
|
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 () => {
|
|
35605
35642
|
const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
|
|
35606
35643
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
35607
|
-
if (!(0,
|
|
35644
|
+
if (!(0, import_node_fs29.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`);
|
|
35608
35645
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
35609
35646
|
if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
|
|
35610
|
-
const manifest = loadBootstrapSeeds((0,
|
|
35647
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs29.readFileSync)(manifestPath, "utf8"));
|
|
35611
35648
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
35612
35649
|
if (!o.target) {
|
|
35613
35650
|
return fail(`bootstrap propagate: --target <path> is required \u2014 one of:
|
|
@@ -35616,9 +35653,9 @@ LIVE apply to ${repo}:
|
|
|
35616
35653
|
const seed = propagatable.find((s) => s.target === o.target);
|
|
35617
35654
|
if (!seed) return fail(`bootstrap propagate: --target '${o.target}' names no centrally propagatable seed in ${manifestPath}. Propagatable targets:
|
|
35618
35655
|
${propagatable.map((s) => s.target).join("\n ")}`);
|
|
35619
|
-
if (!seed.managedBlock && !(0,
|
|
35620
|
-
const hubContent = seed.managedBlock ? null : (0,
|
|
35621
|
-
const readSeedFile2 = (path2) => (0,
|
|
35656
|
+
if (!seed.managedBlock && !(0, import_node_fs29.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
|
|
35657
|
+
const hubContent = seed.managedBlock ? null : (0, import_node_fs29.readFileSync)(seed.target, "utf8");
|
|
35658
|
+
const readSeedFile2 = (path2) => (0, import_node_fs29.existsSync)(path2) ? (0, import_node_fs29.readFileSync)(path2, "utf8") : null;
|
|
35622
35659
|
const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
|
|
35623
35660
|
const cfg = await loadConfig();
|
|
35624
35661
|
const projects = await fetchProjectsList(registryClientDeps(cfg));
|
|
@@ -35627,9 +35664,9 @@ LIVE apply to ${repo}:
|
|
|
35627
35664
|
}
|
|
35628
35665
|
const rosterRepos = collectRegistryRepos(projects).filter((r) => r.toLowerCase() !== "mutmutco/mmi-hub");
|
|
35629
35666
|
let independentCount = rosterRepos.length;
|
|
35630
|
-
if ((0,
|
|
35667
|
+
if ((0, import_node_fs29.existsSync)("projects.json")) {
|
|
35631
35668
|
try {
|
|
35632
|
-
const local = JSON.parse((0,
|
|
35669
|
+
const local = JSON.parse((0, import_node_fs29.readFileSync)("projects.json", "utf8"));
|
|
35633
35670
|
const localRepos = /* @__PURE__ */ new Set();
|
|
35634
35671
|
for (const p of local.projects ?? []) for (const r of p.repos ?? []) {
|
|
35635
35672
|
const full = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
|
|
@@ -35802,12 +35839,12 @@ LIVE apply to ${repo}:
|
|
|
35802
35839
|
const tmp = (0, import_node_path28.join)((0, import_node_os17.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
35803
35840
|
const desiredContent = desiredByRepo.get(rec.repo);
|
|
35804
35841
|
if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
|
|
35805
|
-
(0,
|
|
35842
|
+
(0, import_node_fs29.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha)), "utf8");
|
|
35806
35843
|
try {
|
|
35807
35844
|
await gh2(contentPutInputArgs(rec.repo, seed.target, tmp));
|
|
35808
35845
|
} finally {
|
|
35809
35846
|
try {
|
|
35810
|
-
(0,
|
|
35847
|
+
(0, import_node_fs29.unlinkSync)(tmp);
|
|
35811
35848
|
} catch {
|
|
35812
35849
|
}
|
|
35813
35850
|
}
|
|
@@ -35876,10 +35913,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
35876
35913
|
return fail(`bootstrap rollback: ${e.message}`);
|
|
35877
35914
|
}
|
|
35878
35915
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
35879
|
-
if (!(0,
|
|
35916
|
+
if (!(0, import_node_fs29.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)`);
|
|
35880
35917
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
35881
35918
|
if (!seedSource.ok) return fail(`bootstrap rollback: ${seedSource.reason}`);
|
|
35882
|
-
const manifest = loadBootstrapSeeds((0,
|
|
35919
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs29.readFileSync)(manifestPath, "utf8"));
|
|
35883
35920
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
35884
35921
|
if (!o.target) {
|
|
35885
35922
|
return fail(`bootstrap rollback: --target <path> is required \u2014 one of:
|
|
@@ -35896,10 +35933,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
35896
35933
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
35897
35934
|
let candidates;
|
|
35898
35935
|
if (o.record) {
|
|
35899
|
-
if (!(0,
|
|
35936
|
+
if (!(0, import_node_fs29.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
|
|
35900
35937
|
let parsed;
|
|
35901
35938
|
try {
|
|
35902
|
-
parsed = JSON.parse((0,
|
|
35939
|
+
parsed = JSON.parse((0, import_node_fs29.readFileSync)(o.record, "utf8"));
|
|
35903
35940
|
} catch (e) {
|
|
35904
35941
|
return fail(`bootstrap rollback: --record '${o.record}' is not valid JSON: ${e.message}`);
|
|
35905
35942
|
}
|
|
@@ -35977,12 +36014,12 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
35977
36014
|
existingSha = void 0;
|
|
35978
36015
|
}
|
|
35979
36016
|
const tmp = (0, import_node_path28.join)((0, import_node_os17.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
35980
|
-
(0,
|
|
36017
|
+
(0, import_node_fs29.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
|
|
35981
36018
|
try {
|
|
35982
36019
|
await gh2(contentPutInputArgs(repo, seed.target, tmp));
|
|
35983
36020
|
} finally {
|
|
35984
36021
|
try {
|
|
35985
|
-
(0,
|
|
36022
|
+
(0, import_node_fs29.unlinkSync)(tmp);
|
|
35986
36023
|
} catch {
|
|
35987
36024
|
}
|
|
35988
36025
|
}
|
|
@@ -36438,7 +36475,7 @@ async function waitSuiteDev(deps, opts) {
|
|
|
36438
36475
|
}
|
|
36439
36476
|
|
|
36440
36477
|
// src/suite-train.ts
|
|
36441
|
-
var
|
|
36478
|
+
var import_node_fs30 = require("node:fs");
|
|
36442
36479
|
var import_node_path29 = require("node:path");
|
|
36443
36480
|
var import_node_crypto14 = require("node:crypto");
|
|
36444
36481
|
|
|
@@ -36468,6 +36505,17 @@ var SCHEMA = "mmi-suite-cut/v1";
|
|
|
36468
36505
|
var SHA2 = /^[a-f0-9]{40}$/i;
|
|
36469
36506
|
var RC_TAG = /^v\d+\.\d+\.\d+-rc\.\d+$/;
|
|
36470
36507
|
var RELEASE_TAG = /^v\d+\.\d+\.\d+$/;
|
|
36508
|
+
async function reapplyRulesets(deps, record, after) {
|
|
36509
|
+
for (const member of record.members) {
|
|
36510
|
+
try {
|
|
36511
|
+
member.ruleset = { after, ok: true, lines: await deps.productRuleset(member.repo) };
|
|
36512
|
+
} catch (e) {
|
|
36513
|
+
member.ruleset = { after, ok: false, lines: [e.message] };
|
|
36514
|
+
console.warn(`${member.repo}: product ruleset not re-applied (${e.message}); run mmi-cli devops bootstrap apply ${member.repo} --only product-ruleset --execute from the MMI-Hub checkout`);
|
|
36515
|
+
}
|
|
36516
|
+
writeSuiteCut(deps.recordPath, record);
|
|
36517
|
+
}
|
|
36518
|
+
}
|
|
36471
36519
|
async function kitCheck(deps, shellSha, bundles, where) {
|
|
36472
36520
|
const shell2 = shellSha ? await deps.shellKit(shellSha) : void 0;
|
|
36473
36521
|
const kits = await Promise.all(bundles.map(async (b) => ({ id: b.id, kit: b.url ? await deps.bundleKit(b.url) : void 0 })));
|
|
@@ -36514,7 +36562,7 @@ function suiteReleaseTag(rc) {
|
|
|
36514
36562
|
function readSuiteCut(path2) {
|
|
36515
36563
|
let raw;
|
|
36516
36564
|
try {
|
|
36517
|
-
raw = (0,
|
|
36565
|
+
raw = (0, import_node_fs30.readFileSync)(path2, "utf8");
|
|
36518
36566
|
} catch (e) {
|
|
36519
36567
|
if (e.code === "ENOENT") return void 0;
|
|
36520
36568
|
throw e;
|
|
@@ -36525,16 +36573,16 @@ function readSuiteCut(path2) {
|
|
|
36525
36573
|
} catch {
|
|
36526
36574
|
throw new Error(`suite cut record unreadable at ${path2}; repair it, never recut`);
|
|
36527
36575
|
}
|
|
36528
|
-
if (record.schema !== SCHEMA || record.lead !== SUITE_REPO || !SHA2.test(record.leadDevelopment) || !SHA2.test(record.leadPreviousRc) || !Array.isArray(record.members) || record.members.length === 0 || record.pinPr !== void 0 && (typeof record.pinPr !== "string" || !record.pinPr.startsWith("https://github.com/mutmutco/MM-Work/pull/")) || record.pinsUnchanged !== void 0 && (record.pinsUnchanged !== true || record.pinPr !== void 0) || record.pinMerged !== void 0 && (record.pinMerged !== true && record.pinMerged !== false || !record.pinPr && !record.pinsUnchanged) || record.leadRcTag !== void 0 && (!RC_TAG.test(record.leadRcTag) || !SHA2.test(record.leadRcSha ?? "") || !record.pinMerged) || record.leadReleased !== void 0 && (record.leadReleased !== true || !record.leadRcTag) || record.announcement !== void 0 && (!["pending", "sent", "skipped"].includes(record.announcement) || !record.leadReleased) || record.heldMembers !== void 0 && (!Array.isArray(record.heldMembers) || record.heldMembers.some((h) => !h.id || !h.repo || typeof h.reason !== "string" || !h.reason.trim() || h.since !== void 0 && typeof h.since !== "string" || record.members.some((m) => m.id === h.id))) || record.members.some((m) => !m.id || !m.repo || !m.backend || !m.host || !SHA2.test(m.development) || !SHA2.test(m.previousRc) || m.rcWasAbsent !== void 0 && m.rcWasAbsent !== true || m.tag !== void 0 && (!RC_TAG.test(m.tag) || !SHA2.test(m.sha ?? "")) || m.published !== void 0 && (m.published !== true || !m.tag) || m.released !== void 0 && (m.released !== true || !m.published) || m.mainPublished !== void 0 && (m.mainPublished !== true || !m.released || !!m.carried) || m.backendAfterRelease !== void 0 && (!m.released || typeof m.backendAfterRelease !== "string") || m.carried !== void 0 && (m.carried !== true || m.tag !== void 0 || m.published !== void 0 || !isPin(m.rcPin) || !isPin(m.mainPin)) || m.carried === void 0 && (m.rcPin !== void 0 || m.mainPin !== void 0)))
|
|
36576
|
+
if (record.schema !== SCHEMA || record.lead !== SUITE_REPO || !SHA2.test(record.leadDevelopment) || !SHA2.test(record.leadPreviousRc) || !Array.isArray(record.members) || record.members.length === 0 || record.pinPr !== void 0 && (typeof record.pinPr !== "string" || !record.pinPr.startsWith("https://github.com/mutmutco/MM-Work/pull/")) || record.pinsUnchanged !== void 0 && (record.pinsUnchanged !== true || record.pinPr !== void 0) || record.pinMerged !== void 0 && (record.pinMerged !== true && record.pinMerged !== false || !record.pinPr && !record.pinsUnchanged) || record.leadRcTag !== void 0 && (!RC_TAG.test(record.leadRcTag) || !SHA2.test(record.leadRcSha ?? "") || !record.pinMerged) || record.leadReleased !== void 0 && (record.leadReleased !== true || !record.leadRcTag) || record.announcement !== void 0 && (!["pending", "sent", "skipped"].includes(record.announcement) || !record.leadReleased) || record.heldMembers !== void 0 && (!Array.isArray(record.heldMembers) || record.heldMembers.some((h) => !h.id || !h.repo || typeof h.reason !== "string" || !h.reason.trim() || h.since !== void 0 && typeof h.since !== "string" || record.members.some((m) => m.id === h.id))) || record.members.some((m) => !m.id || !m.repo || !m.backend || !m.host || !SHA2.test(m.development) || !SHA2.test(m.previousRc) || m.rcWasAbsent !== void 0 && m.rcWasAbsent !== true || m.tag !== void 0 && (!RC_TAG.test(m.tag) || !SHA2.test(m.sha ?? "")) || m.published !== void 0 && (m.published !== true || !m.tag) || m.released !== void 0 && (m.released !== true || !m.published) || m.mainPublished !== void 0 && (m.mainPublished !== true || !m.released || !!m.carried) || m.backendAfterRelease !== void 0 && (!m.released || typeof m.backendAfterRelease !== "string") || m.carried !== void 0 && (m.carried !== true || m.tag !== void 0 || m.published !== void 0 || !isPin(m.rcPin) || !isPin(m.mainPin)) || m.carried === void 0 && (m.rcPin !== void 0 || m.mainPin !== void 0) || m.ruleset !== void 0 && (!["rc", "main"].includes(m.ruleset.after) || typeof m.ruleset.ok !== "boolean" || !Array.isArray(m.ruleset.lines))))
|
|
36529
36577
|
throw new Error(`suite cut record has unknown or invalid state at ${path2}; repair it, never recut`);
|
|
36530
36578
|
return record;
|
|
36531
36579
|
}
|
|
36532
36580
|
function writeSuiteCut(path2, record) {
|
|
36533
|
-
(0,
|
|
36581
|
+
(0, import_node_fs30.mkdirSync)((0, import_node_path29.dirname)(path2), { recursive: true });
|
|
36534
36582
|
const temp = `${path2}.${(0, import_node_crypto14.randomBytes)(8).toString("hex")}.tmp`;
|
|
36535
36583
|
try {
|
|
36536
|
-
(0,
|
|
36537
|
-
(0,
|
|
36584
|
+
(0, import_node_fs30.writeFileSync)(temp, JSON.stringify(record, null, 2), { flag: "wx" });
|
|
36585
|
+
(0, import_node_fs30.renameSync)(temp, path2);
|
|
36538
36586
|
} catch (e) {
|
|
36539
36587
|
throw new Error(`suite cut record write failed at ${path2}: ${e.message}; stop, verify the file before retrying`);
|
|
36540
36588
|
}
|
|
@@ -36583,25 +36631,25 @@ async function suiteMarkAbandoned(deps, ownerGo = false) {
|
|
|
36583
36631
|
return tags.map((t) => `${t.repo}@${t.tag}`);
|
|
36584
36632
|
}
|
|
36585
36633
|
async function retireFailedLeadRcLedger(deps, ownerGo) {
|
|
36586
|
-
if (!(0,
|
|
36634
|
+
if (!(0, import_node_fs30.existsSync)(deps.ledgerPath)) return void 0;
|
|
36587
36635
|
return withLedgerLock(deps.ledgerPath, async (fence) => {
|
|
36588
|
-
const ledger = parseReleaseLedger((0,
|
|
36636
|
+
const ledger = parseReleaseLedger((0, import_node_fs30.readFileSync)(deps.ledgerPath, "utf8"));
|
|
36589
36637
|
const states = Object.values(ledger.phases).map((p) => p.state);
|
|
36590
36638
|
if (ledger.repo !== SUITE_REPO || ledger.lane !== "rcand" || ledger.phases.promotion.state === "complete" || !states.includes("failed")) return void 0;
|
|
36591
36639
|
if (!ownerGo) throw new Error(`the lead's failed rcand ledger for ${ledger.tag} blocks every train; retire it with suite abandon --owner-go`);
|
|
36592
36640
|
if ((await deps.rcHead())?.toLowerCase() === ledger.tagSha.toLowerCase()) throw new Error(`origin rc already points at ${ledger.tag}; it was promoted, so its ledger is not retired`);
|
|
36593
36641
|
const archive = (0, import_node_path29.join)((0, import_node_path29.dirname)(deps.ledgerPath), `phases.archive-${ledger.tag}.json`);
|
|
36594
|
-
if ((0,
|
|
36642
|
+
if ((0, import_node_fs30.existsSync)(archive)) throw new Error(`ledger archive already exists at ${archive}; inspect before retiring again`);
|
|
36595
36643
|
await deps.markAbandoned(SUITE_REPO, ledger.tag, ledger.tagSha);
|
|
36596
|
-
await fence(() => (0,
|
|
36644
|
+
await fence(() => (0, import_node_fs30.renameSync)(deps.ledgerPath, archive));
|
|
36597
36645
|
return `${ledger.tag} (ledger archived to ${archive})`;
|
|
36598
36646
|
});
|
|
36599
36647
|
}
|
|
36600
36648
|
function suiteAbandon(recordPath, ownerGo = false, now = /* @__PURE__ */ new Date()) {
|
|
36601
36649
|
const record = abandonableCut(recordPath, ownerGo);
|
|
36602
36650
|
const archive = `${recordPath}.abandoned-${record.leadDevelopment.slice(0, 12)}-${now.toISOString().replace(/[:.]/g, "")}.json`;
|
|
36603
|
-
if ((0,
|
|
36604
|
-
(0,
|
|
36651
|
+
if ((0, import_node_fs30.existsSync)(archive)) throw new Error(`abandoned-cut archive already exists at ${archive}; inspect before abandoning again`);
|
|
36652
|
+
(0, import_node_fs30.renameSync)(recordPath, archive);
|
|
36605
36653
|
return { archive, orphanTags: record.members.filter((m) => m.tag).map((m) => `${m.repo}@${m.tag}`) };
|
|
36606
36654
|
}
|
|
36607
36655
|
async function suiteRestoreMainPins(deps) {
|
|
@@ -36667,12 +36715,12 @@ async function suiteRcand(deps, opts) {
|
|
|
36667
36715
|
}
|
|
36668
36716
|
if ((record?.announcement === "sent" || record?.announcement === "skipped") && record.leadReleased) {
|
|
36669
36717
|
const archive = `${deps.recordPath}.${record.leadRcTag}.json`;
|
|
36670
|
-
if ((0,
|
|
36671
|
-
(0,
|
|
36718
|
+
if ((0, import_node_fs30.existsSync)(archive)) throw new Error(`suite cut archive already exists at ${archive}; inspect before a new cut`);
|
|
36719
|
+
(0, import_node_fs30.renameSync)(deps.recordPath, archive);
|
|
36672
36720
|
record = void 0;
|
|
36673
36721
|
}
|
|
36674
36722
|
if (record && !record.pinPr && !record.leadRcTag && record.members.every((m) => !m.tag)) {
|
|
36675
|
-
(0,
|
|
36723
|
+
(0, import_node_fs30.rmSync)(deps.recordPath, { force: true });
|
|
36676
36724
|
record = void 0;
|
|
36677
36725
|
}
|
|
36678
36726
|
if (!record) {
|
|
@@ -36726,7 +36774,7 @@ async function suiteRcand(deps, opts) {
|
|
|
36726
36774
|
quiet.push(member);
|
|
36727
36775
|
}
|
|
36728
36776
|
if (record.members.every((m) => m.carried || quiet.includes(m)) && await deps.contains(SUITE_REPO, record.leadDevelopment, record.leadPreviousRc)) {
|
|
36729
|
-
(0,
|
|
36777
|
+
(0, import_node_fs30.rmSync)(deps.recordPath, { force: true });
|
|
36730
36778
|
throw new Error("nothing to promote: neither MM-Work nor any module has new development commits since its rc; no suite cut started");
|
|
36731
36779
|
}
|
|
36732
36780
|
for (const member of quiet) {
|
|
@@ -36744,7 +36792,7 @@ async function suiteRcand(deps, opts) {
|
|
|
36744
36792
|
}
|
|
36745
36793
|
if (refusals.length) {
|
|
36746
36794
|
const untouched = record.members.every((m) => !m.tag) && !record.pinPr;
|
|
36747
|
-
if (untouched) (0,
|
|
36795
|
+
if (untouched) (0, import_node_fs30.rmSync)(deps.recordPath, { force: true });
|
|
36748
36796
|
throw new Error(`module rc serving is not ready; ${untouched ? "no suite cut started" : "no new tag was cut"}:
|
|
36749
36797
|
${refusals.join("\n")}
|
|
36750
36798
|
Each module must ship a live release that bind-mounts <deployPath>/data read-only and serves <data>/rc/module.js at /rc/module.js (docs/Guides/suite-trains.md).`);
|
|
@@ -36807,6 +36855,7 @@ Each module must ship a live release that bind-mounts <deployPath>/data read-onl
|
|
|
36807
36855
|
record.leadRcSha = leadCut.sha;
|
|
36808
36856
|
writeSuiteCut(deps.recordPath, record);
|
|
36809
36857
|
}
|
|
36858
|
+
await reapplyRulesets(deps, record, "rc");
|
|
36810
36859
|
return { plan, record };
|
|
36811
36860
|
}
|
|
36812
36861
|
async function suiteRelease(deps, ownerGo, big = false) {
|
|
@@ -36842,6 +36891,10 @@ async function suiteRelease(deps, ownerGo, big = false) {
|
|
|
36842
36891
|
throw new Error(`${member.repo}: live backend changed after the cut (${member.backend} \u2192 ${live.version}); release refused`);
|
|
36843
36892
|
}
|
|
36844
36893
|
if ((await deps.tagSha(SUITE_REPO, record.leadRcTag))?.toLowerCase() !== record.leadRcSha?.toLowerCase()) throw new Error("MM-Work rc tag changed after the cut; release refused");
|
|
36894
|
+
await deps.validateReleaseSummaries([
|
|
36895
|
+
...record.members.filter((m) => !m.carried && !m.released && !m.hotfixedTo).map((m) => ({ repo: m.repo, tag: m.releasedAs ?? suiteReleaseTag(m.tag) })),
|
|
36896
|
+
...!record.leadReleased ? [{ repo: SUITE_REPO, tag: suiteReleaseTag(record.leadRcTag) }] : []
|
|
36897
|
+
]);
|
|
36845
36898
|
const publishesMain = !record.leadReleased;
|
|
36846
36899
|
const refusals = [];
|
|
36847
36900
|
for (const member of record.members) {
|
|
@@ -36873,6 +36926,7 @@ Each member's live release must serve <data>/main/module.js at its main pin path
|
|
|
36873
36926
|
writeSuiteCut(deps.recordPath, record);
|
|
36874
36927
|
}
|
|
36875
36928
|
}
|
|
36929
|
+
await reapplyRulesets(deps, record, "main");
|
|
36876
36930
|
if (!record.leadReleased) {
|
|
36877
36931
|
await deps.release(SUITE_REPO, suiteReleaseTag(record.leadRcTag), record.leadRcSha, { big });
|
|
36878
36932
|
record.leadReleased = true;
|
|
@@ -36899,7 +36953,7 @@ var hotfixRecordPath = (deps) => (0, import_node_path29.join)((0, import_node_pa
|
|
|
36899
36953
|
function readSuiteHotfix(path2) {
|
|
36900
36954
|
let raw;
|
|
36901
36955
|
try {
|
|
36902
|
-
raw = (0,
|
|
36956
|
+
raw = (0, import_node_fs30.readFileSync)(path2, "utf8");
|
|
36903
36957
|
} catch (e) {
|
|
36904
36958
|
if (e.code === "ENOENT") return void 0;
|
|
36905
36959
|
throw e;
|
|
@@ -36938,11 +36992,11 @@ async function suiteHotfix(deps, opts) {
|
|
|
36938
36992
|
if (cut && !cut.leadReleased) throw new Error(`an unfinished suite cut (${cut.leadRcTag ?? "no lead tag"}) is in flight; finish it with suite release or retire it with suite abandon first`);
|
|
36939
36993
|
const path2 = hotfixRecordPath(deps);
|
|
36940
36994
|
let record = readSuiteHotfix(path2);
|
|
36941
|
-
|
|
36942
|
-
|
|
36943
|
-
record = void 0;
|
|
36944
|
-
}
|
|
36995
|
+
const completed = record?.mapped ? record : void 0;
|
|
36996
|
+
if (completed) record = void 0;
|
|
36945
36997
|
if (record && record.repo !== found.repo) throw new Error(`an unfinished suite hotfix for ${record.repo} is recorded; finish it (rerun with that member) first`);
|
|
36998
|
+
if (!record?.tag) await deps.validateHotfixSummary();
|
|
36999
|
+
if (completed) (0, import_node_fs30.renameSync)(path2, `${path2}.${completed.leadTag}.json`);
|
|
36946
37000
|
const rec = record ?? { schema: "mmi-suite-hotfix/v1", member: found.id, repo: found.repo, from: opts.from, ...opts.ack?.length ? { ack: opts.ack } : {} };
|
|
36947
37001
|
const save = () => writeSuiteHotfix(path2, rec);
|
|
36948
37002
|
save();
|
|
@@ -37209,7 +37263,7 @@ async function runRcDataSwitch(deps, opts) {
|
|
|
37209
37263
|
}
|
|
37210
37264
|
|
|
37211
37265
|
// src/suite-train-io.ts
|
|
37212
|
-
var
|
|
37266
|
+
var import_node_fs44 = require("node:fs");
|
|
37213
37267
|
var import_node_path43 = require("node:path");
|
|
37214
37268
|
init_cli_shared();
|
|
37215
37269
|
|
|
@@ -38271,6 +38325,7 @@ async function runHotfixReleaseLocked(deps, versionInput, options, doctor) {
|
|
|
38271
38325
|
await assertConsumerBomLineage(deps, mergedSha, tag);
|
|
38272
38326
|
}
|
|
38273
38327
|
const releaseExists = await hotfixReleaseExists(deps, ctx, tag);
|
|
38328
|
+
if (!releaseExists && meta.suite === "mm-work") await requireSuiteReleaseSummary(deps, options.announceSummaryFile);
|
|
38274
38329
|
if (!releaseExists && isHubControlRepo(ctx.repo)) {
|
|
38275
38330
|
if (!options.announceSummaryFile) {
|
|
38276
38331
|
throw new Error("a new MMI-Hub hotfix Release requires --announce-summary-file <path> with 3-6 short LLM-curated lines; create the summary file, then rerun the same hotfix release command \u2014 see https://github.com/mutmutco/MMI-Hub/blob/development/docs/Guides/train-troubleshooting.md#announce-summary-missing");
|
|
@@ -38886,7 +38941,7 @@ function checkHotfixCarries(options) {
|
|
|
38886
38941
|
}
|
|
38887
38942
|
|
|
38888
38943
|
// src/merge-cleanup.ts
|
|
38889
|
-
var
|
|
38944
|
+
var import_node_fs32 = require("node:fs");
|
|
38890
38945
|
var import_node_path31 = require("node:path");
|
|
38891
38946
|
var import_node_os18 = require("node:os");
|
|
38892
38947
|
init_cli_shared();
|
|
@@ -39202,7 +39257,7 @@ async function runPrLand(prNumber, options, deps) {
|
|
|
39202
39257
|
|
|
39203
39258
|
// src/test-policy-core.ts
|
|
39204
39259
|
var import_node_child_process13 = require("node:child_process");
|
|
39205
|
-
var
|
|
39260
|
+
var import_node_fs31 = require("node:fs");
|
|
39206
39261
|
var import_node_path30 = require("node:path");
|
|
39207
39262
|
|
|
39208
39263
|
// src/test-command-policy-shared.mjs
|
|
@@ -39756,7 +39811,7 @@ function loadPolicySource(root, explicitRef, diffBase) {
|
|
|
39756
39811
|
}
|
|
39757
39812
|
function readFileOrNull2(path2) {
|
|
39758
39813
|
try {
|
|
39759
|
-
return (0,
|
|
39814
|
+
return (0, import_node_fs31.readFileSync)(path2, "utf8");
|
|
39760
39815
|
} catch {
|
|
39761
39816
|
return null;
|
|
39762
39817
|
}
|
|
@@ -39784,10 +39839,10 @@ function classify(changed, policy, present = () => false) {
|
|
|
39784
39839
|
const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
|
|
39785
39840
|
return { mandatoryHits, untestedHits, testChanges, meaningfulTestChanges, addedTests, removedProtected };
|
|
39786
39841
|
}
|
|
39787
|
-
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0,
|
|
39842
|
+
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs31.existsSync)(path2)) {
|
|
39788
39843
|
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path30.join)(root, p)));
|
|
39789
39844
|
}
|
|
39790
|
-
function unresolvedSatisfiers(policy, root, exists = (path2) => (0,
|
|
39845
|
+
function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs31.existsSync)(path2)) {
|
|
39791
39846
|
const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
|
|
39792
39847
|
return [...new Set(declared)].filter((p) => !exists((0, import_node_path30.join)(root, p)));
|
|
39793
39848
|
}
|
|
@@ -39928,7 +39983,7 @@ function runTestPolicy(root, deps = {}) {
|
|
|
39928
39983
|
}
|
|
39929
39984
|
const loaded = deps.policy ? { policy: deps.policy, source: { ref: null, sha: null, path: POLICY_FILE } } : loadPolicySource(root, deps.policyRef, deps.base);
|
|
39930
39985
|
const { policy, source: policySource } = loaded;
|
|
39931
|
-
const exists = deps.exists ?? ((path2) => (0,
|
|
39986
|
+
const exists = deps.exists ?? ((path2) => (0, import_node_fs31.existsSync)(path2));
|
|
39932
39987
|
const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
|
|
39933
39988
|
const base = deps.changed ? "(injected)" : deps.policyRef ? resolveHotfixBase(root) : resolveBase(root, deps.base);
|
|
39934
39989
|
const refusal = deps.changed ? null : untrustworthyRange(root, base);
|
|
@@ -40226,13 +40281,13 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
|
|
|
40226
40281
|
return rewritten === base ? null : rewritten;
|
|
40227
40282
|
}
|
|
40228
40283
|
function writeSquashBodyFile(body) {
|
|
40229
|
-
const dir = (0,
|
|
40284
|
+
const dir = (0, import_node_fs32.mkdtempSync)((0, import_node_path31.join)((0, import_node_os18.tmpdir)(), "mmi-squash-body-"));
|
|
40230
40285
|
const path2 = (0, import_node_path31.join)(dir, "body.txt");
|
|
40231
|
-
(0,
|
|
40286
|
+
(0, import_node_fs32.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
|
|
40232
40287
|
`, "utf8");
|
|
40233
40288
|
return { path: path2, cleanup: () => {
|
|
40234
40289
|
try {
|
|
40235
|
-
(0,
|
|
40290
|
+
(0, import_node_fs32.rmSync)(dir, { recursive: true, force: true });
|
|
40236
40291
|
} catch {
|
|
40237
40292
|
}
|
|
40238
40293
|
} };
|
|
@@ -40439,7 +40494,7 @@ async function deleteMergedRemoteBranch(options) {
|
|
|
40439
40494
|
}
|
|
40440
40495
|
|
|
40441
40496
|
// src/project-info-sync.ts
|
|
40442
|
-
var
|
|
40497
|
+
var import_node_fs33 = require("node:fs");
|
|
40443
40498
|
var import_node_path32 = require("node:path");
|
|
40444
40499
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
40445
40500
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
@@ -40488,13 +40543,13 @@ function sharedName(entries, fallback) {
|
|
|
40488
40543
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
40489
40544
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
40490
40545
|
const readmePath = (0, import_node_path32.join)(repoRoot2, "README.md");
|
|
40491
|
-
if (!(0,
|
|
40546
|
+
if (!(0, import_node_fs33.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
40492
40547
|
const entries = entriesFor(project2, projects);
|
|
40493
40548
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
40494
40549
|
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
40495
40550
|
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
40496
40551
|
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
40497
|
-
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0,
|
|
40552
|
+
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs33.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
|
|
40498
40553
|
const lines2 = [
|
|
40499
40554
|
`# ${projectName}`,
|
|
40500
40555
|
"",
|
|
@@ -40513,8 +40568,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
40513
40568
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
40514
40569
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
40515
40570
|
const orgDocs = [
|
|
40516
|
-
(0,
|
|
40517
|
-
(0,
|
|
40571
|
+
(0, import_node_fs33.existsSync)((0, import_node_path32.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
|
|
40572
|
+
(0, import_node_fs33.existsSync)((0, import_node_path32.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
|
|
40518
40573
|
].filter(Boolean);
|
|
40519
40574
|
if (orgDocs.length) lines2.push("", "## Organisation docs", "", ...orgDocs);
|
|
40520
40575
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines2.join("\n")}
|
|
@@ -40536,7 +40591,7 @@ async function syncProjectInfo(plan, client, apply) {
|
|
|
40536
40591
|
}
|
|
40537
40592
|
|
|
40538
40593
|
// src/train-commands.ts
|
|
40539
|
-
var
|
|
40594
|
+
var import_node_fs34 = require("node:fs");
|
|
40540
40595
|
var import_node_path33 = require("node:path");
|
|
40541
40596
|
init_cli_shared();
|
|
40542
40597
|
init_clean_exit();
|
|
@@ -40552,7 +40607,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
40552
40607
|
}
|
|
40553
40608
|
function readRepoVersion() {
|
|
40554
40609
|
try {
|
|
40555
|
-
return JSON.parse((0,
|
|
40610
|
+
return JSON.parse((0, import_node_fs34.readFileSync)((0, import_node_path33.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
40556
40611
|
} catch {
|
|
40557
40612
|
return void 0;
|
|
40558
40613
|
}
|
|
@@ -40917,7 +40972,7 @@ async function openSuitePinPr(deps, input) {
|
|
|
40917
40972
|
|
|
40918
40973
|
// src/command-register-collaboration.ts
|
|
40919
40974
|
var import_node_child_process17 = require("node:child_process");
|
|
40920
|
-
var
|
|
40975
|
+
var import_node_fs43 = require("node:fs");
|
|
40921
40976
|
var import_promises8 = require("node:fs/promises");
|
|
40922
40977
|
init_clean_exit();
|
|
40923
40978
|
init_cli_shared();
|
|
@@ -41051,7 +41106,7 @@ init_github_client();
|
|
|
41051
41106
|
init_github_quota();
|
|
41052
41107
|
|
|
41053
41108
|
// src/issue-commands.ts
|
|
41054
|
-
var
|
|
41109
|
+
var import_node_fs35 = require("node:fs");
|
|
41055
41110
|
var import_node_crypto15 = require("node:crypto");
|
|
41056
41111
|
init_cli_shared();
|
|
41057
41112
|
init_clean_exit();
|
|
@@ -41244,7 +41299,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
41244
41299
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
41245
41300
|
const patch = {};
|
|
41246
41301
|
let bodyChanged = false;
|
|
41247
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
41302
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs35.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
41248
41303
|
if (options.titleFile !== void 0) {
|
|
41249
41304
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
41250
41305
|
} else if (options.title !== void 0) {
|
|
@@ -41907,7 +41962,7 @@ function extendCreateCommand(issue, batchAttach) {
|
|
|
41907
41962
|
if (opts.batch) {
|
|
41908
41963
|
let specs;
|
|
41909
41964
|
try {
|
|
41910
|
-
const raw = (0,
|
|
41965
|
+
const raw = (0, import_node_fs35.readFileSync)(opts.batch, "utf8");
|
|
41911
41966
|
specs = JSON.parse(raw);
|
|
41912
41967
|
if (!Array.isArray(specs)) {
|
|
41913
41968
|
const top = specs === null ? "null" : typeof specs === "object" ? `an object with keys: ${Object.keys(specs).join(", ") || "(none)"}` : `a ${typeof specs}`;
|
|
@@ -41984,7 +42039,7 @@ ${lines2}`, {
|
|
|
41984
42039
|
}
|
|
41985
42040
|
|
|
41986
42041
|
// src/post-merge-recon.ts
|
|
41987
|
-
var
|
|
42042
|
+
var import_node_fs36 = require("node:fs");
|
|
41988
42043
|
var import_node_path35 = require("node:path");
|
|
41989
42044
|
|
|
41990
42045
|
// src/cross-repo-filing-issue.ts
|
|
@@ -42154,16 +42209,16 @@ function buildPostMergeReconRecovery(input) {
|
|
|
42154
42209
|
}
|
|
42155
42210
|
function writePostMergeReconRecovery(cwd, recovery) {
|
|
42156
42211
|
const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
|
|
42157
|
-
(0,
|
|
42158
|
-
(0,
|
|
42212
|
+
(0, import_node_fs36.mkdirSync)((0, import_node_path35.dirname)(path2), { recursive: true });
|
|
42213
|
+
(0, import_node_fs36.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
|
|
42159
42214
|
`, "utf8");
|
|
42160
42215
|
return path2;
|
|
42161
42216
|
}
|
|
42162
42217
|
function clearPostMergeReconRecovery(cwd, repo, pr) {
|
|
42163
42218
|
const path2 = postMergeReconStatePath(cwd, repo, pr);
|
|
42164
|
-
if (!(0,
|
|
42219
|
+
if (!(0, import_node_fs36.existsSync)(path2)) return;
|
|
42165
42220
|
try {
|
|
42166
|
-
(0,
|
|
42221
|
+
(0, import_node_fs36.unlinkSync)(path2);
|
|
42167
42222
|
} catch {
|
|
42168
42223
|
}
|
|
42169
42224
|
}
|
|
@@ -42639,7 +42694,7 @@ function registerPrLifecycleCommands(program3) {
|
|
|
42639
42694
|
|
|
42640
42695
|
// src/review-verdict.ts
|
|
42641
42696
|
var import_node_child_process14 = require("node:child_process");
|
|
42642
|
-
var
|
|
42697
|
+
var import_node_fs37 = require("node:fs");
|
|
42643
42698
|
var import_node_os20 = require("node:os");
|
|
42644
42699
|
var import_node_path36 = require("node:path");
|
|
42645
42700
|
init_cli_shared();
|
|
@@ -42806,15 +42861,15 @@ async function readPrIssueComments(number, repo) {
|
|
|
42806
42861
|
return comments;
|
|
42807
42862
|
}
|
|
42808
42863
|
async function postPrCommentFromFile(number, repo, body) {
|
|
42809
|
-
const dir = (0,
|
|
42864
|
+
const dir = (0, import_node_fs37.mkdtempSync)((0, import_node_path36.join)((0, import_node_os20.tmpdir)(), "mmi-review-verdict-"));
|
|
42810
42865
|
const path2 = (0, import_node_path36.join)(dir, "body.md");
|
|
42811
42866
|
try {
|
|
42812
|
-
(0,
|
|
42867
|
+
(0, import_node_fs37.writeFileSync)(path2, body, "utf8");
|
|
42813
42868
|
const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
42814
42869
|
return stdout.trim();
|
|
42815
42870
|
} finally {
|
|
42816
42871
|
try {
|
|
42817
|
-
(0,
|
|
42872
|
+
(0, import_node_fs37.rmSync)(dir, { recursive: true, force: true });
|
|
42818
42873
|
} catch {
|
|
42819
42874
|
}
|
|
42820
42875
|
}
|
|
@@ -43048,17 +43103,17 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
|
|
|
43048
43103
|
}
|
|
43049
43104
|
|
|
43050
43105
|
// src/worktree-residue-sweep.ts
|
|
43051
|
-
var
|
|
43106
|
+
var import_node_fs42 = require("node:fs");
|
|
43052
43107
|
var import_node_path41 = require("node:path");
|
|
43053
43108
|
|
|
43054
43109
|
// src/worktree-merge-cleanup.ts
|
|
43055
|
-
var
|
|
43110
|
+
var import_node_fs41 = require("node:fs");
|
|
43056
43111
|
var import_node_path40 = require("node:path");
|
|
43057
43112
|
init_cli_shared();
|
|
43058
43113
|
|
|
43059
43114
|
// src/stage-runner.ts
|
|
43060
43115
|
var import_node_child_process16 = require("node:child_process");
|
|
43061
|
-
var
|
|
43116
|
+
var import_node_fs39 = require("node:fs");
|
|
43062
43117
|
var import_node_path38 = require("node:path");
|
|
43063
43118
|
var import_node_net = require("node:net");
|
|
43064
43119
|
var import_node_util5 = require("node:util");
|
|
@@ -43069,7 +43124,7 @@ function normalizeEol2(s) {
|
|
|
43069
43124
|
}
|
|
43070
43125
|
|
|
43071
43126
|
// src/env-detection.ts
|
|
43072
|
-
var
|
|
43127
|
+
var import_node_fs38 = require("node:fs");
|
|
43073
43128
|
var import_node_path37 = require("node:path");
|
|
43074
43129
|
var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".jerv", ".pi", ".venv", "venv"]);
|
|
43075
43130
|
function isEnvBaseName(name) {
|
|
@@ -43080,7 +43135,7 @@ function findEnvFiles(root) {
|
|
|
43080
43135
|
const walk2 = (dir, rel) => {
|
|
43081
43136
|
let entries;
|
|
43082
43137
|
try {
|
|
43083
|
-
entries = (0,
|
|
43138
|
+
entries = (0, import_node_fs38.readdirSync)(dir, { withFileTypes: true });
|
|
43084
43139
|
} catch {
|
|
43085
43140
|
return;
|
|
43086
43141
|
}
|
|
@@ -43094,7 +43149,7 @@ function findEnvFiles(root) {
|
|
|
43094
43149
|
if (isEnvBaseName(entry.name)) {
|
|
43095
43150
|
let isDir = false;
|
|
43096
43151
|
try {
|
|
43097
|
-
isDir = (0,
|
|
43152
|
+
isDir = (0, import_node_fs38.lstatSync)((0, import_node_path37.join)(dir, entry.name)).isDirectory();
|
|
43098
43153
|
} catch {
|
|
43099
43154
|
}
|
|
43100
43155
|
if (!isDir) found.push(relPath);
|
|
@@ -43207,12 +43262,12 @@ var DOCKER_TIMEOUT_MS = 15e3;
|
|
|
43207
43262
|
function stageBash() {
|
|
43208
43263
|
if (process.platform !== "win32") return "bash";
|
|
43209
43264
|
const configured = process.env.SHELL;
|
|
43210
|
-
if (configured && (0, import_node_path38.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0,
|
|
43265
|
+
if (configured && (0, import_node_path38.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs39.existsSync)(configured)) return configured;
|
|
43211
43266
|
const candidates = [
|
|
43212
43267
|
(0, import_node_path38.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
|
|
43213
43268
|
...process.env.LOCALAPPDATA ? [(0, import_node_path38.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
|
|
43214
43269
|
];
|
|
43215
|
-
const bash = candidates.find((path2) => (0,
|
|
43270
|
+
const bash = candidates.find((path2) => (0, import_node_fs39.existsSync)(path2));
|
|
43216
43271
|
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.");
|
|
43217
43272
|
return bash;
|
|
43218
43273
|
}
|
|
@@ -43568,7 +43623,7 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
43568
43623
|
function composeResolvesPort(cwd) {
|
|
43569
43624
|
if (process.env.PORT) return true;
|
|
43570
43625
|
const envFile = (0, import_node_path38.join)(cwd, ".env");
|
|
43571
|
-
return (0,
|
|
43626
|
+
return (0, import_node_fs39.existsSync)(envFile) && envFileKeys((0, import_node_fs39.readFileSync)(envFile, "utf8")).has("PORT");
|
|
43572
43627
|
}
|
|
43573
43628
|
function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
43574
43629
|
const composeProject = config.env?.COMPOSE_PROJECT_NAME ?? vaultEnvMerge?.COMPOSE_PROJECT_NAME ?? stageComposeProjectName(cwd);
|
|
@@ -43583,13 +43638,13 @@ function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
|
43583
43638
|
function stageComposeFiles(cwd) {
|
|
43584
43639
|
const files = ["docker-compose.yml"];
|
|
43585
43640
|
for (const conventional of ["docker-compose.override.yml", "docker-compose.override.yaml"]) {
|
|
43586
|
-
if ((0,
|
|
43641
|
+
if ((0, import_node_fs39.existsSync)((0, import_node_path38.join)(cwd, conventional))) files.push(conventional);
|
|
43587
43642
|
}
|
|
43588
43643
|
return [...files, RUNTIME_ENV_OVERRIDE_RELPATH];
|
|
43589
43644
|
}
|
|
43590
43645
|
async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
43591
43646
|
const keys = Object.keys(vaultEnvMerge ?? {});
|
|
43592
|
-
if (!keys.length || !(0,
|
|
43647
|
+
if (!keys.length || !(0, import_node_fs39.existsSync)((0, import_node_path38.join)(cwd, "docker-compose.yml"))) return {};
|
|
43593
43648
|
let configJson;
|
|
43594
43649
|
try {
|
|
43595
43650
|
const { stdout } = await execFileP3("docker", ["compose", "config", "--format", "json"], {
|
|
@@ -43606,20 +43661,20 @@ async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
|
43606
43661
|
const body = renderRuntimeEnvOverride(runtimeEnvMarkedServices(configJson), keys, runtimeEnvExcludes(configJson));
|
|
43607
43662
|
if (!body) return {};
|
|
43608
43663
|
const overridePath = (0, import_node_path38.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
|
|
43609
|
-
(0,
|
|
43610
|
-
(0,
|
|
43664
|
+
(0, import_node_fs39.mkdirSync)((0, import_node_path38.join)(cwd, "tmp", "stage"), { recursive: true });
|
|
43665
|
+
(0, import_node_fs39.writeFileSync)(overridePath, body, "utf8");
|
|
43611
43666
|
return stageComposeFileEnv(stageComposeFiles(cwd));
|
|
43612
43667
|
}
|
|
43613
43668
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
43614
43669
|
if (!config.ensureEnv) return;
|
|
43615
43670
|
const target = (0, import_node_path38.join)(cwd, config.ensureEnv.target);
|
|
43616
43671
|
const example = (0, import_node_path38.join)(cwd, config.ensureEnv.example);
|
|
43617
|
-
if (!(0,
|
|
43618
|
-
(0,
|
|
43619
|
-
} else if ((0,
|
|
43620
|
-
const stale = detectStaleEnvFile((0,
|
|
43621
|
-
exampleMtimeMs: (0,
|
|
43622
|
-
targetMtimeMs: (0,
|
|
43672
|
+
if (!(0, import_node_fs39.existsSync)(target) && (0, import_node_fs39.existsSync)(example)) {
|
|
43673
|
+
(0, import_node_fs39.copyFileSync)(example, target);
|
|
43674
|
+
} else if ((0, import_node_fs39.existsSync)(target) && (0, import_node_fs39.existsSync)(example)) {
|
|
43675
|
+
const stale = detectStaleEnvFile((0, import_node_fs39.readFileSync)(example, "utf8"), (0, import_node_fs39.readFileSync)(target, "utf8"), {
|
|
43676
|
+
exampleMtimeMs: (0, import_node_fs39.statSync)(example).mtimeMs,
|
|
43677
|
+
targetMtimeMs: (0, import_node_fs39.statSync)(target).mtimeMs
|
|
43623
43678
|
});
|
|
43624
43679
|
if (stale) {
|
|
43625
43680
|
const msg = `stale ${config.ensureEnv.target} (${stale}) \u2014 delete it or refresh from ${config.ensureEnv.example} before re-running /stage`;
|
|
@@ -43627,8 +43682,8 @@ async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
|
43627
43682
|
console.error(`mmi-cli stage: ${msg} (allowed via --allow-stale-env)`);
|
|
43628
43683
|
}
|
|
43629
43684
|
}
|
|
43630
|
-
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0,
|
|
43631
|
-
(0,
|
|
43685
|
+
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0, import_node_fs39.existsSync)(target)) {
|
|
43686
|
+
(0, import_node_fs39.writeFileSync)(target, mergeEnvSecretsIntoFile((0, import_node_fs39.readFileSync)(target, "utf8"), opts.vaultEnvMerge), "utf8");
|
|
43632
43687
|
}
|
|
43633
43688
|
}
|
|
43634
43689
|
async function gitText(cwd, args) {
|
|
@@ -43658,20 +43713,20 @@ async function resolveGlobalStatePath(cwd, explicit) {
|
|
|
43658
43713
|
return void 0;
|
|
43659
43714
|
}
|
|
43660
43715
|
function readState(path2) {
|
|
43661
|
-
if (!(0,
|
|
43716
|
+
if (!(0, import_node_fs39.existsSync)(path2)) return null;
|
|
43662
43717
|
try {
|
|
43663
|
-
return JSON.parse((0,
|
|
43718
|
+
return JSON.parse((0, import_node_fs39.readFileSync)(path2, "utf8"));
|
|
43664
43719
|
} catch {
|
|
43665
43720
|
return null;
|
|
43666
43721
|
}
|
|
43667
43722
|
}
|
|
43668
43723
|
function mkdirFor(path2) {
|
|
43669
43724
|
const dir = path2.slice(0, Math.max(path2.lastIndexOf("/"), path2.lastIndexOf("\\")));
|
|
43670
|
-
(0,
|
|
43725
|
+
(0, import_node_fs39.mkdirSync)(dir, { recursive: true });
|
|
43671
43726
|
}
|
|
43672
43727
|
function writeState(path2, state) {
|
|
43673
43728
|
mkdirFor(path2);
|
|
43674
|
-
(0,
|
|
43729
|
+
(0, import_node_fs39.writeFileSync)(path2, JSON.stringify(state, null, 2), "utf8");
|
|
43675
43730
|
}
|
|
43676
43731
|
function writeStagePortReservation(port, cwd, statePath, globalStatePath, now) {
|
|
43677
43732
|
const reservation = {
|
|
@@ -43700,7 +43755,7 @@ async function cleanupStageState(state, paths, timeoutMs, fallbackCwd, currentEn
|
|
|
43700
43755
|
);
|
|
43701
43756
|
}
|
|
43702
43757
|
for (const path2 of [...new Set(paths.filter((p) => Boolean(p)))]) {
|
|
43703
|
-
(0,
|
|
43758
|
+
(0, import_node_fs39.rmSync)(path2, { force: true });
|
|
43704
43759
|
}
|
|
43705
43760
|
}
|
|
43706
43761
|
async function killTree(pid) {
|
|
@@ -43947,8 +44002,8 @@ async function runStage(config = {}, opts = {}) {
|
|
|
43947
44002
|
});
|
|
43948
44003
|
}
|
|
43949
44004
|
} catch (e) {
|
|
43950
|
-
(0,
|
|
43951
|
-
if (globalStatePath && globalStatePath !== statePath) (0,
|
|
44005
|
+
(0, import_node_fs39.rmSync)(statePath, { force: true });
|
|
44006
|
+
if (globalStatePath && globalStatePath !== statePath) (0, import_node_fs39.rmSync)(globalStatePath, { force: true });
|
|
43952
44007
|
throw e;
|
|
43953
44008
|
}
|
|
43954
44009
|
const started = await startStage(config, {
|
|
@@ -43963,7 +44018,7 @@ async function runStage(config = {}, opts = {}) {
|
|
|
43963
44018
|
}
|
|
43964
44019
|
|
|
43965
44020
|
// src/worktree-evidence-archive.ts
|
|
43966
|
-
var
|
|
44021
|
+
var import_node_fs40 = require("node:fs");
|
|
43967
44022
|
var import_node_path39 = require("node:path");
|
|
43968
44023
|
var JERV_ARTIFACT_RUN_SCOPE_ENV_VARS = ["JERV_RUN_ID", ...SESSION_ID_ENV_VARS];
|
|
43969
44024
|
function sanitizeArchiveSegment(value, max = 80) {
|
|
@@ -43979,17 +44034,17 @@ function resolveArtifactRunScope(env = process.env) {
|
|
|
43979
44034
|
}
|
|
43980
44035
|
function defaultIsDirectory(path2) {
|
|
43981
44036
|
try {
|
|
43982
|
-
return (0,
|
|
44037
|
+
return (0, import_node_fs40.lstatSync)(path2).isDirectory();
|
|
43983
44038
|
} catch {
|
|
43984
44039
|
return false;
|
|
43985
44040
|
}
|
|
43986
44041
|
}
|
|
43987
44042
|
function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
43988
|
-
const exists = deps.exists ??
|
|
44043
|
+
const exists = deps.exists ?? import_node_fs40.existsSync;
|
|
43989
44044
|
const isDirectory = deps.isDirectory ?? defaultIsDirectory;
|
|
43990
|
-
const copyDir = deps.copyDir ?? ((from, to) => (0,
|
|
44045
|
+
const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs40.cpSync)(from, to, { recursive: true, force: true }));
|
|
43991
44046
|
const mkdirp = deps.mkdirp ?? ((path2) => {
|
|
43992
|
-
(0,
|
|
44047
|
+
(0, import_node_fs40.mkdirSync)(path2, { recursive: true });
|
|
43993
44048
|
});
|
|
43994
44049
|
const env = deps.env ?? process.env;
|
|
43995
44050
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
@@ -44013,13 +44068,13 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
44013
44068
|
}
|
|
44014
44069
|
}
|
|
44015
44070
|
function defaultStat(path2) {
|
|
44016
|
-
const st = (0,
|
|
44071
|
+
const st = (0, import_node_fs40.statSync)(path2);
|
|
44017
44072
|
return { mtimeMs: st.mtimeMs, size: st.size, isDirectory: () => st.isDirectory() };
|
|
44018
44073
|
}
|
|
44019
44074
|
function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
44020
|
-
const exists = deps.exists ??
|
|
44075
|
+
const exists = deps.exists ?? import_node_fs40.existsSync;
|
|
44021
44076
|
const stat4 = deps.stat ?? defaultStat;
|
|
44022
|
-
const readdir2 = deps.readdir ??
|
|
44077
|
+
const readdir2 = deps.readdir ?? import_node_fs40.readdirSync;
|
|
44023
44078
|
const tmpRoot = (0, import_node_path39.join)(worktreePath, "tmp");
|
|
44024
44079
|
if (!exists(tmpRoot)) return [];
|
|
44025
44080
|
const entries = [];
|
|
@@ -44052,10 +44107,10 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
|
44052
44107
|
return entries;
|
|
44053
44108
|
}
|
|
44054
44109
|
function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
44055
|
-
const exists = deps.exists ??
|
|
44056
|
-
const copyDir = deps.copyDir ?? ((from, to) => (0,
|
|
44110
|
+
const exists = deps.exists ?? import_node_fs40.existsSync;
|
|
44111
|
+
const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs40.cpSync)(from, to, { recursive: true, force: true }));
|
|
44057
44112
|
const mkdirp = deps.mkdirp ?? ((path2) => {
|
|
44058
|
-
(0,
|
|
44113
|
+
(0, import_node_fs40.mkdirSync)(path2, { recursive: true });
|
|
44059
44114
|
});
|
|
44060
44115
|
const env = deps.env ?? process.env;
|
|
44061
44116
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
@@ -44142,7 +44197,7 @@ function normPath2(p) {
|
|
|
44142
44197
|
function unlinkNodeModulesJunction(wtPath) {
|
|
44143
44198
|
const nm = (0, import_node_path40.join)(wtPath, "node_modules");
|
|
44144
44199
|
try {
|
|
44145
|
-
if ((0,
|
|
44200
|
+
if ((0, import_node_fs41.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs41.rmdirSync)(nm);
|
|
44146
44201
|
return { ok: true };
|
|
44147
44202
|
} catch (e) {
|
|
44148
44203
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -44153,8 +44208,8 @@ function defaultSleep2(ms) {
|
|
|
44153
44208
|
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
44154
44209
|
}
|
|
44155
44210
|
async function removeResidueDirectory(wtPath, options = {}) {
|
|
44156
|
-
const { sleep: sleep2 = defaultSleep2, removeEmptyDir =
|
|
44157
|
-
const removeRecursive = options.removeRecursive ?? ((path2) => (0,
|
|
44211
|
+
const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs41.rmdirSync, exists = import_node_fs41.existsSync } = options;
|
|
44212
|
+
const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs41.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
|
|
44158
44213
|
const junction = unlinkNodeModulesJunction(wtPath);
|
|
44159
44214
|
if (!junction.ok) return junction;
|
|
44160
44215
|
try {
|
|
@@ -44280,21 +44335,21 @@ async function listNestedIgnoredNodeModules(wtPath, execGit) {
|
|
|
44280
44335
|
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path40.join)(wtPath, line.slice(0, -1)));
|
|
44281
44336
|
}
|
|
44282
44337
|
function safeRemoveTree(path2) {
|
|
44283
|
-
const stat4 = (0,
|
|
44338
|
+
const stat4 = (0, import_node_fs41.lstatSync)(path2);
|
|
44284
44339
|
if (stat4.isSymbolicLink()) {
|
|
44285
44340
|
try {
|
|
44286
|
-
(0,
|
|
44341
|
+
(0, import_node_fs41.rmdirSync)(path2);
|
|
44287
44342
|
} catch {
|
|
44288
|
-
(0,
|
|
44343
|
+
(0, import_node_fs41.unlinkSync)(path2);
|
|
44289
44344
|
}
|
|
44290
44345
|
return;
|
|
44291
44346
|
}
|
|
44292
44347
|
if (stat4.isDirectory()) {
|
|
44293
|
-
for (const entry of (0,
|
|
44294
|
-
(0,
|
|
44348
|
+
for (const entry of (0, import_node_fs41.readdirSync)(path2)) safeRemoveTree((0, import_node_path40.join)(path2, entry));
|
|
44349
|
+
(0, import_node_fs41.rmdirSync)(path2);
|
|
44295
44350
|
return;
|
|
44296
44351
|
}
|
|
44297
|
-
(0,
|
|
44352
|
+
(0, import_node_fs41.unlinkSync)(path2);
|
|
44298
44353
|
}
|
|
44299
44354
|
async function removeWorktreeNodeModules(wtPath) {
|
|
44300
44355
|
try {
|
|
@@ -44310,7 +44365,7 @@ function errorMessage(e) {
|
|
|
44310
44365
|
}
|
|
44311
44366
|
function resolvedOrRaw(path2) {
|
|
44312
44367
|
try {
|
|
44313
|
-
return normPath2((0,
|
|
44368
|
+
return normPath2((0, import_node_fs41.realpathSync)(path2));
|
|
44314
44369
|
} catch {
|
|
44315
44370
|
return normPath2(path2);
|
|
44316
44371
|
}
|
|
@@ -44318,10 +44373,10 @@ function resolvedOrRaw(path2) {
|
|
|
44318
44373
|
function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
44319
44374
|
let realRoot;
|
|
44320
44375
|
try {
|
|
44321
|
-
if ((0,
|
|
44376
|
+
if ((0, import_node_fs41.lstatSync)(root).isSymbolicLink()) {
|
|
44322
44377
|
return { ok: false, error: `delete root ${normPath2(root)} is a reparse point resolving to ${resolvedOrRaw(root)}` };
|
|
44323
44378
|
}
|
|
44324
|
-
realRoot = normPath2((0,
|
|
44379
|
+
realRoot = normPath2((0, import_node_fs41.realpathSync)(root));
|
|
44325
44380
|
} catch (e) {
|
|
44326
44381
|
if (e.code === "ENOENT") return { ok: true, unlinked: [] };
|
|
44327
44382
|
return { ok: false, error: `cannot resolve delete root ${normPath2(root)}: ${errorMessage(e)}` };
|
|
@@ -44337,7 +44392,7 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
|
44337
44392
|
const dir = stack.pop();
|
|
44338
44393
|
let entries;
|
|
44339
44394
|
try {
|
|
44340
|
-
entries = (0,
|
|
44395
|
+
entries = (0, import_node_fs41.readdirSync)(dir, { withFileTypes: true });
|
|
44341
44396
|
} catch (e) {
|
|
44342
44397
|
if (e.code === "ENOENT") continue;
|
|
44343
44398
|
return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
|
|
@@ -44347,7 +44402,7 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
|
44347
44402
|
if (entry.isSymbolicLink()) {
|
|
44348
44403
|
let target = "";
|
|
44349
44404
|
try {
|
|
44350
|
-
target = normPath2((0,
|
|
44405
|
+
target = normPath2((0, import_node_fs41.realpathSync)(child2));
|
|
44351
44406
|
} catch {
|
|
44352
44407
|
target = "";
|
|
44353
44408
|
}
|
|
@@ -44530,7 +44585,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
44530
44585
|
return report;
|
|
44531
44586
|
}
|
|
44532
44587
|
const execGit = options.execGit ?? (async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
44533
|
-
const pathExists = options.pathExists ??
|
|
44588
|
+
const pathExists = options.pathExists ?? import_node_fs41.existsSync;
|
|
44534
44589
|
let afterWorktrees = [];
|
|
44535
44590
|
try {
|
|
44536
44591
|
afterWorktrees = parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"]));
|
|
@@ -44817,7 +44872,7 @@ function residueLedgerPath(gitCommonDir) {
|
|
|
44817
44872
|
}
|
|
44818
44873
|
function readPaths(file, deps) {
|
|
44819
44874
|
try {
|
|
44820
|
-
const parsed = JSON.parse((deps.readLedger ?? ((f) => (0,
|
|
44875
|
+
const parsed = JSON.parse((deps.readLedger ?? ((f) => (0, import_node_fs42.readFileSync)(f, "utf8")))(file));
|
|
44821
44876
|
return Array.isArray(parsed) ? parsed.filter((p) => typeof p === "string") : [];
|
|
44822
44877
|
} catch {
|
|
44823
44878
|
return [];
|
|
@@ -44826,8 +44881,8 @@ function readPaths(file, deps) {
|
|
|
44826
44881
|
function writePaths(file, paths, deps) {
|
|
44827
44882
|
try {
|
|
44828
44883
|
const write = deps.writeLedger ?? ((f, t) => {
|
|
44829
|
-
(0,
|
|
44830
|
-
(0,
|
|
44884
|
+
(0, import_node_fs42.mkdirSync)((0, import_node_path41.dirname)(f), { recursive: true });
|
|
44885
|
+
(0, import_node_fs42.writeFileSync)(f, t, "utf8");
|
|
44831
44886
|
});
|
|
44832
44887
|
write(file, `${JSON.stringify([...new Set(paths)], null, 1)}
|
|
44833
44888
|
`);
|
|
@@ -44841,17 +44896,17 @@ function recordResidue(gitCommonDir, path2, deps = {}) {
|
|
|
44841
44896
|
writePaths(file, paths, deps);
|
|
44842
44897
|
}
|
|
44843
44898
|
async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
44844
|
-
const exists = deps.exists ??
|
|
44845
|
-
const readDir = deps.readDir ?? ((p) => (0,
|
|
44899
|
+
const exists = deps.exists ?? import_node_fs42.existsSync;
|
|
44900
|
+
const readDir = deps.readDir ?? ((p) => (0, import_node_fs42.readdirSync)(p));
|
|
44846
44901
|
const isDir = deps.isDir ?? ((p) => {
|
|
44847
44902
|
try {
|
|
44848
|
-
return (0,
|
|
44903
|
+
return (0, import_node_fs42.statSync)(p).isDirectory();
|
|
44849
44904
|
} catch {
|
|
44850
44905
|
return false;
|
|
44851
44906
|
}
|
|
44852
44907
|
});
|
|
44853
44908
|
const removeRecorded = deps.removeRecorded ?? ((p) => removeResidueDirectory(p));
|
|
44854
|
-
const removeEmpty = deps.removeEmpty ?? ((p) => (0,
|
|
44909
|
+
const removeEmpty = deps.removeEmpty ?? ((p) => (0, import_node_fs42.rmdirSync)(p));
|
|
44855
44910
|
const report = { swept: [], kept: [], dropped: [], emptyRemoved: [], unrecorded: [] };
|
|
44856
44911
|
const live = new Set(registered.map(norm));
|
|
44857
44912
|
const primary = registered[0] ? norm(registered[0]) : void 0;
|
|
@@ -44931,8 +44986,8 @@ function argvWantsJson2() {
|
|
|
44931
44986
|
function hubRoot() {
|
|
44932
44987
|
const fromPkg = (0, import_node_path42.join)(__dirname, "..", "..");
|
|
44933
44988
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
44934
|
-
if ((0,
|
|
44935
|
-
if ((0,
|
|
44989
|
+
if ((0, import_node_fs43.existsSync)((0, import_node_path42.join)(fromPkg, marker))) return fromPkg;
|
|
44990
|
+
if ((0, import_node_fs43.existsSync)((0, import_node_path42.join)(process.cwd(), marker))) return process.cwd();
|
|
44936
44991
|
return null;
|
|
44937
44992
|
}
|
|
44938
44993
|
function ciAuditDeps() {
|
|
@@ -44945,7 +45000,7 @@ function ciAuditDeps() {
|
|
|
44945
45000
|
readSeedFile: (path2) => {
|
|
44946
45001
|
if (!root) return null;
|
|
44947
45002
|
const fullPath = (0, import_node_path42.join)(root, path2);
|
|
44948
|
-
return (0,
|
|
45003
|
+
return (0, import_node_fs43.existsSync)(fullPath) ? (0, import_node_fs43.readFileSync)(fullPath, "utf8") : null;
|
|
44949
45004
|
}
|
|
44950
45005
|
};
|
|
44951
45006
|
}
|
|
@@ -45709,10 +45764,10 @@ ${list}`);
|
|
|
45709
45764
|
});
|
|
45710
45765
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
45711
45766
|
const wfDir = (0, import_node_path42.join)(cwd, ".github", "workflows");
|
|
45712
|
-
if (!(0,
|
|
45713
|
-
return (0,
|
|
45767
|
+
if (!(0, import_node_fs43.existsSync)(wfDir)) return [];
|
|
45768
|
+
return (0, import_node_fs43.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
45714
45769
|
try {
|
|
45715
|
-
return workflowReportsPrChecks((0,
|
|
45770
|
+
return workflowReportsPrChecks((0, import_node_fs43.readFileSync)((0, import_node_path42.join)(wfDir, name), "utf8"));
|
|
45716
45771
|
} catch {
|
|
45717
45772
|
return true;
|
|
45718
45773
|
}
|
|
@@ -45917,7 +45972,7 @@ ${list}`);
|
|
|
45917
45972
|
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) => {
|
|
45918
45973
|
if (!isReviewVerdict(o.verdict)) return fail(`pr review-verdict: --verdict must be one of ${REVIEW_VERDICTS.join("|")} (got ${o.verdict})`);
|
|
45919
45974
|
const verdict = o.verdict;
|
|
45920
|
-
const findings = o.findingsFile ? (0,
|
|
45975
|
+
const findings = o.findingsFile ? (0, import_node_fs43.readFileSync)(o.findingsFile, "utf8") : void 0;
|
|
45921
45976
|
let patch;
|
|
45922
45977
|
let head;
|
|
45923
45978
|
try {
|
|
@@ -46192,7 +46247,7 @@ ${list}`);
|
|
|
46192
46247
|
const mergeSquashBody = squashBodyTextForMerge(
|
|
46193
46248
|
closingGuardInput,
|
|
46194
46249
|
method === "--squash",
|
|
46195
|
-
o.squashBodyFile ? (0,
|
|
46250
|
+
o.squashBodyFile ? (0, import_node_fs43.readFileSync)(o.squashBodyFile, "utf8") : void 0
|
|
46196
46251
|
);
|
|
46197
46252
|
if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
|
|
46198
46253
|
const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
|
|
@@ -46322,7 +46377,7 @@ ${list}`);
|
|
|
46322
46377
|
}
|
|
46323
46378
|
if (!repoForPostCleanup) throw e;
|
|
46324
46379
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
|
|
46325
|
-
const commitMessage = bodyFile ? (0,
|
|
46380
|
+
const commitMessage = bodyFile ? (0, import_node_fs43.readFileSync)(bodyFile, "utf8") : void 0;
|
|
46326
46381
|
await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
|
|
46327
46382
|
body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
|
|
46328
46383
|
timeoutMs: GH_MUTATION_TIMEOUT_MS
|
|
@@ -46454,7 +46509,7 @@ ${list}`);
|
|
|
46454
46509
|
preserveWorktree: o.preserveWorktree,
|
|
46455
46510
|
gcAcknowledged: o.gc,
|
|
46456
46511
|
expectedHeadOid: headRefOid,
|
|
46457
|
-
pathExists: (p) => (0,
|
|
46512
|
+
pathExists: (p) => (0, import_node_fs43.existsSync)(p),
|
|
46458
46513
|
// #5899: pin cleanup git calls to the main checkout — the task worktree this process may be
|
|
46459
46514
|
// standing in is removed mid-cleanup, so a cwd-relative invocation fails with
|
|
46460
46515
|
// 'fatal: not a git repository' and leaves a spurious partial-cleanup exit.
|
|
@@ -47363,6 +47418,7 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
|
|
|
47363
47418
|
const slug = applyingRepo ? slugOf(applyingRepo) : "";
|
|
47364
47419
|
const isHub = applyingRepo?.toLowerCase() === ANNOUNCE_REPO.toLowerCase();
|
|
47365
47420
|
let announcesToChannel = isHub;
|
|
47421
|
+
let isWorkSuite = false;
|
|
47366
47422
|
let releaseLanguage2 = "en";
|
|
47367
47423
|
if (!isHub && slug) {
|
|
47368
47424
|
let read;
|
|
@@ -47376,9 +47432,17 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
|
|
|
47376
47432
|
`release: Hub registry read failed for ${slug} (${read.error}) \u2014 whether ${slug} announces to a project release channel is UNREAD, so the --announce-summary-file requirement is unproven. Likely transient (cold start, network, or auth blip); retry shortly.`
|
|
47377
47433
|
);
|
|
47378
47434
|
}
|
|
47435
|
+
isWorkSuite = read.project?.suite === "mm-work";
|
|
47379
47436
|
announcesToChannel = Boolean(read.project?.releaseChannel?.trim());
|
|
47380
47437
|
releaseLanguage2 = read.project?.releaseLanguage?.trim() || "en";
|
|
47381
47438
|
}
|
|
47439
|
+
if (isWorkSuite) {
|
|
47440
|
+
try {
|
|
47441
|
+
await requireSuiteReleaseSummary({}, o.announceSummaryFile);
|
|
47442
|
+
} catch (e) {
|
|
47443
|
+
return fail(`release: ${e.message}`);
|
|
47444
|
+
}
|
|
47445
|
+
}
|
|
47382
47446
|
if (announcesToChannel) {
|
|
47383
47447
|
if (!o.announceSummaryFile) {
|
|
47384
47448
|
return fail(
|
|
@@ -47682,11 +47746,11 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
47682
47746
|
const checkout = async (repo) => {
|
|
47683
47747
|
if (repo === SUITE_REPO) return root;
|
|
47684
47748
|
const target = (0, import_node_path43.join)(base, repo.split("/")[1]);
|
|
47685
|
-
if (!(0,
|
|
47686
|
-
(0,
|
|
47749
|
+
if (!(0, import_node_fs44.existsSync)(target)) {
|
|
47750
|
+
(0, import_node_fs44.mkdirSync)(base, { recursive: true });
|
|
47687
47751
|
await exec("git", ["clone", "--quiet", "--branch", "development", `https://github.com/${repo}.git`, target], root, WATCH_TIMEOUT);
|
|
47688
47752
|
}
|
|
47689
|
-
if (!(0,
|
|
47753
|
+
if (!(0, import_node_fs44.existsSync)((0, import_node_path43.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
|
|
47690
47754
|
const origin = await exec("git", ["remote", "get-url", "origin"], target);
|
|
47691
47755
|
if (!origin.toLowerCase().endsWith(`/${repo.toLowerCase()}.git`) && !origin.toLowerCase().endsWith(`/${repo.toLowerCase()}`))
|
|
47692
47756
|
throw new Error(`${repo}: suite checkout origin mismatch at ${target}; stop`);
|
|
@@ -47890,6 +47954,20 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
47890
47954
|
};
|
|
47891
47955
|
return {
|
|
47892
47956
|
recordPath,
|
|
47957
|
+
validateHotfixSummary: () => requireSuiteReleaseSummary({}, summaryFile),
|
|
47958
|
+
validateReleaseSummaries: (releases) => validateReleaseSummaries(
|
|
47959
|
+
releases,
|
|
47960
|
+
{ ...memberSummaryFiles, [SUITE_REPO]: summaryFile },
|
|
47961
|
+
async (repo, tag) => {
|
|
47962
|
+
try {
|
|
47963
|
+
await gh(["release", "view", tag, "--repo", repo, "--json", "tagName"]);
|
|
47964
|
+
return true;
|
|
47965
|
+
} catch (e) {
|
|
47966
|
+
if (/release not found|HTTP 404|Not Found|\(404\)/i.test(e.message)) return false;
|
|
47967
|
+
throw e;
|
|
47968
|
+
}
|
|
47969
|
+
}
|
|
47970
|
+
),
|
|
47893
47971
|
registry: registry2,
|
|
47894
47972
|
backend,
|
|
47895
47973
|
hold,
|
|
@@ -47970,10 +48048,11 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
47970
48048
|
},
|
|
47971
48049
|
// #7738: a member hotfixes as suite-solo; the lead hotfixes under its suite-lead capability (inTrain sets it).
|
|
47972
48050
|
hotfixStart: (repo, o) => inTrain(repo, async (deps) => {
|
|
48051
|
+
await requireSuiteReleaseSummary(deps, summaryFile);
|
|
47973
48052
|
const r = await runHotfixStart(deps, { from: o.from, ack: o.ack, suiteSolo: repo !== SUITE_REPO });
|
|
47974
48053
|
return { tag: r.tag };
|
|
47975
48054
|
}, repo !== SUITE_REPO),
|
|
47976
|
-
hotfixRelease: (repo, tag) => inTrain(repo, async (deps) => ({ tag: (await runHotfixRelease(deps, tag, { suiteSolo: repo !== SUITE_REPO })).tag }), repo !== SUITE_REPO),
|
|
48055
|
+
hotfixRelease: (repo, tag) => inTrain(repo, async (deps) => ({ tag: (await runHotfixRelease(deps, tag, { suiteSolo: repo !== SUITE_REPO, announceSummaryFile: summaryFile, announceSummaryFileTr: summaryFileTr })).tag }), repo !== SUITE_REPO),
|
|
47977
48056
|
// #7738: the #7714 solo pin leg, polled until the auto-merge lands (30 s x 40 = 20 min), then the caller decides.
|
|
47978
48057
|
soloPin: async (repo, tag) => {
|
|
47979
48058
|
for (let i = 0; ; i++) {
|
|
@@ -47994,13 +48073,27 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
47994
48073
|
const body = await gh(["release", "view", leadTag, "--repo", SUITE_REPO, "--json", "body", "--jq", ".body"]);
|
|
47995
48074
|
if (parseSuiteMapping(body)) return;
|
|
47996
48075
|
const dir = suiteStateDir(root);
|
|
47997
|
-
(0,
|
|
48076
|
+
(0, import_node_fs44.mkdirSync)(dir, { recursive: true });
|
|
47998
48077
|
const file = (0, import_node_path43.join)(dir, `notes-${leadTag}.md`);
|
|
47999
|
-
(0,
|
|
48078
|
+
(0, import_node_fs44.writeFileSync)(file, `${body}
|
|
48000
48079
|
|
|
48001
48080
|
${suiteMappingBlock(leadTag, mapping)}`);
|
|
48002
48081
|
await gh(["release", "edit", leadTag, "--repo", SUITE_REPO, "--notes-file", file]);
|
|
48003
48082
|
},
|
|
48083
|
+
productRuleset: async (repo) => {
|
|
48084
|
+
const read = await fetchProjectBySlugChecked(slugOf(repo), registryClientDeps(await loadConfig()));
|
|
48085
|
+
if (!read.ok) throw new Error(`registry META unreadable (${read.error})`);
|
|
48086
|
+
if (!read.project?.requiredChecks?.length) return ["skip: no registry META requiredChecks; the seeded ruleset stays as it is"];
|
|
48087
|
+
const result = await applyCiReconcileRepo(repo, {
|
|
48088
|
+
client: defaultGitHubClient(),
|
|
48089
|
+
listProjects: async () => [],
|
|
48090
|
+
getProjectMeta: async () => read.project
|
|
48091
|
+
}, { productRulesetOnly: true });
|
|
48092
|
+
const lines2 = [...result.applied, ...result.skipped.map((s) => `skip: ${s}`)];
|
|
48093
|
+
lines2.forEach((line) => console.log(`${repo}: product ruleset ${line}`));
|
|
48094
|
+
if (result.errors.length) throw new Error(result.errors.join("; "));
|
|
48095
|
+
return lines2;
|
|
48096
|
+
},
|
|
48004
48097
|
release,
|
|
48005
48098
|
shippedTag,
|
|
48006
48099
|
sameContent: sameContent2,
|
|
@@ -48023,7 +48116,7 @@ ${suiteMappingBlock(leadTag, mapping)}`);
|
|
|
48023
48116
|
|
|
48024
48117
|
// src/suite-commands.ts
|
|
48025
48118
|
init_cli_shared();
|
|
48026
|
-
var
|
|
48119
|
+
var import_node_fs45 = require("node:fs");
|
|
48027
48120
|
function parseMemberSummaryFiles(entries) {
|
|
48028
48121
|
const memberSummaryFiles = {};
|
|
48029
48122
|
for (const entry of entries) {
|
|
@@ -48033,12 +48126,6 @@ function parseMemberSummaryFiles(entries) {
|
|
|
48033
48126
|
}
|
|
48034
48127
|
return memberSummaryFiles;
|
|
48035
48128
|
}
|
|
48036
|
-
function validateSuiteReleaseSummary(path2) {
|
|
48037
|
-
if (!path2) throw new Error("suite release requires --announce-summary-file before promoting any member");
|
|
48038
|
-
const lines2 = (0, import_node_fs44.readFileSync)(path2, "utf8").split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
|
|
48039
|
-
if (lines2[0]?.startsWith("No changes to this module")) throw new Error("suite release requires a real MM-Work title, not the no-change placeholder");
|
|
48040
|
-
if (lines2.length < 3 || lines2.length > 6) throw new Error("suite release announcement needs 3\u20136 curated lines before promoting any member");
|
|
48041
|
-
}
|
|
48042
48129
|
function suiteRcDataProdDeps() {
|
|
48043
48130
|
return {
|
|
48044
48131
|
fetch: async (url, init) => {
|
|
@@ -48111,7 +48198,7 @@ function registerSuiteCommands(program3) {
|
|
|
48111
48198
|
try {
|
|
48112
48199
|
const io = suiteTrainIo(root);
|
|
48113
48200
|
retired = await retireFailedLeadRcLedger({ ledgerPath: releaseLedgerPath(root), rcHead: () => io.head(SUITE_REPO, "rc").then((h) => h ?? void 0), markAbandoned: io.markAbandoned }, o.ownerGo === true);
|
|
48114
|
-
if (retired && !(0,
|
|
48201
|
+
if (retired && !(0, import_node_fs45.existsSync)(io.recordPath)) {
|
|
48115
48202
|
console.log(o.json ? JSON.stringify({ retiredLeadRc: retired }, null, 2) : `suite abandon: retired the lead's failed, never-promoted rc candidate ${retired}; marked abandoned on origin (#7854)`);
|
|
48116
48203
|
return;
|
|
48117
48204
|
}
|
|
@@ -48133,10 +48220,9 @@ function registerSuiteCommands(program3) {
|
|
|
48133
48220
|
return fail(`suite abandon: ${e.message}`);
|
|
48134
48221
|
}
|
|
48135
48222
|
});
|
|
48136
|
-
suite.command("release").description("verify the cut and unchanged live backends, ship members then MM-Work, announce once").option("--owner-go", "explicit owner approval to release the tested candidate").option("--announce-summary-file <path>", "curated suite announcement, posted once after the train; also this cut's plain-words summary for MM-Work itself").option("--member-summary-file <owner/repo=path>", "repeatable:
|
|
48223
|
+
suite.command("release").description("verify the cut and unchanged live backends, ship members then MM-Work, announce once").option("--owner-go", "explicit owner approval to release the tested candidate").option("--announce-summary-file <path>", "curated suite announcement, posted once after the train; also this cut's plain-words summary for MM-Work itself").option("--member-summary-file <owner/repo=path>", "repeatable: required title + 2\u20135 notes for each member being released; carried members need none", (val, prev = []) => [...prev, val], []).option("--announce-summary-file-tr <path>", "Turkish plain-words summary for MM-Work itself").option("--member-summary-file-tr <owner/repo=path>", "repeatable: a member's own Turkish plain-words summary", (val, prev = []) => [...prev, val], []).option("--big", "marks MM-Work's own release big for the plain-words section (#7510); a member is never marked big through this flag \u2014 it is big only via the read route's automatic minor/major detection").option("--json", "structured receipt").action(async (o) => {
|
|
48137
48224
|
try {
|
|
48138
48225
|
if (!o.ownerGo) throw new Error("suite release requires --owner-go; nothing was written");
|
|
48139
|
-
validateSuiteReleaseSummary(o.announceSummaryFile);
|
|
48140
48226
|
const memberSummaryFiles = parseMemberSummaryFiles(o.memberSummaryFile ?? []);
|
|
48141
48227
|
const memberSummaryFilesTr = parseMemberSummaryFiles(o.memberSummaryFileTr ?? []);
|
|
48142
48228
|
const root = await leadRoot();
|
|
@@ -48157,14 +48243,14 @@ function registerSuiteCommands(program3) {
|
|
|
48157
48243
|
return fail(`suite release: ${e.message}`);
|
|
48158
48244
|
}
|
|
48159
48245
|
});
|
|
48160
|
-
suite.command("hotfix <member>").description("member hotfix, main pin PR, MM-Work patch release, and the recorded suite version -> member versions mapping (#7738)").requiredOption("--from <pr#|sha[,pr#|sha...]>", "merged development PR(s) or SHA(s) the member hotfix carries").option("--ack <shas>", "dev shas verified as not needed on main (see hotfix start --ack)").option("--owner-go", "explicit owner approval: ships the member and MM-Work to production (not needed for --dry-run)").option("--dry-run", "read-only: print the ordered plan; writes nothing").option("--json", "structured receipt").action(async (member, o) => {
|
|
48246
|
+
suite.command("hotfix <member>").description("member hotfix, main pin PR, MM-Work patch release, and the recorded suite version -> member versions mapping (#7738)").requiredOption("--from <pr#|sha[,pr#|sha...]>", "merged development PR(s) or SHA(s) the member hotfix carries").option("--ack <shas>", "dev shas verified as not needed on main (see hotfix start --ack)").option("--announce-summary-file <path>", "required title + 2\u20135 notes for the member and lead hotfix Releases").option("--announce-summary-file-tr <path>", "optional Turkish plain-words notes").option("--owner-go", "explicit owner approval: ships the member and MM-Work to production (not needed for --dry-run)").option("--dry-run", "read-only: print the ordered plan; writes nothing").option("--json", "structured receipt").action(async (member, o) => {
|
|
48161
48247
|
try {
|
|
48162
48248
|
if (!o.ownerGo && !o.dryRun) throw new Error("suite hotfix requires --owner-go (it ships to production); nothing was written. --dry-run prints the plan");
|
|
48163
48249
|
const root = await leadRoot();
|
|
48164
48250
|
const unlock = o.dryRun ? async () => void 0 : await acquireTrainWriterLock(root, "suite hotfix");
|
|
48165
48251
|
let result;
|
|
48166
48252
|
try {
|
|
48167
|
-
result = await suiteHotfix(suiteTrainIo(root), { member, from: o.from, ack: o.ack?.split(",").map((s) => s.trim()).filter(Boolean), ownerGo: o.ownerGo, dryRun: o.dryRun });
|
|
48253
|
+
result = await suiteHotfix(suiteTrainIo(root, o.announceSummaryFile, {}, o.announceSummaryFileTr), { member, from: o.from, ack: o.ack?.split(",").map((s) => s.trim()).filter(Boolean), ownerGo: o.ownerGo, dryRun: o.dryRun });
|
|
48168
48254
|
} finally {
|
|
48169
48255
|
await unlock();
|
|
48170
48256
|
}
|
|
@@ -48301,7 +48387,7 @@ function suiteStatusDeps(github, registry2) {
|
|
|
48301
48387
|
|
|
48302
48388
|
// src/packages-adopt-export.ts
|
|
48303
48389
|
var import_node_child_process18 = require("node:child_process");
|
|
48304
|
-
var
|
|
48390
|
+
var import_node_fs46 = require("node:fs");
|
|
48305
48391
|
var import_node_os21 = require("node:os");
|
|
48306
48392
|
var import_node_path44 = require("node:path");
|
|
48307
48393
|
var import_node_util6 = require("node:util");
|
|
@@ -48925,36 +49011,36 @@ async function adoptExport(opts, deps) {
|
|
|
48925
49011
|
if (!/^sha256:[0-9a-f]{64}$/.test(opts.digest)) throw new Error("--digest must be sha256:<64 hex>, from the export run summary");
|
|
48926
49012
|
const packagePath = (0, import_node_path44.join)(opts.dir, "package.json");
|
|
48927
49013
|
const lockPath = (0, import_node_path44.join)(opts.dir, "package-lock.json");
|
|
48928
|
-
const packageText = (0,
|
|
48929
|
-
const lockText = (0,
|
|
48930
|
-
const work = (0,
|
|
49014
|
+
const packageText = (0, import_node_fs46.readFileSync)(packagePath, "utf8");
|
|
49015
|
+
const lockText = (0, import_node_fs46.readFileSync)(lockPath, "utf8");
|
|
49016
|
+
const work = (0, import_node_fs46.mkdtempSync)((0, import_node_path44.join)((0, import_node_os21.tmpdir)(), "mmi-adopt-export-"));
|
|
48931
49017
|
try {
|
|
48932
49018
|
const run = JSON.parse((await deps.ghApi(`repos/${EXPORT_REPO}/actions/runs/${opts.run}`)).toString("utf8"));
|
|
48933
|
-
(0,
|
|
48934
|
-
(0,
|
|
48935
|
-
(0,
|
|
49019
|
+
(0, import_node_fs46.writeFileSync)((0, import_node_path44.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
|
|
49020
|
+
(0, import_node_fs46.writeFileSync)((0, import_node_path44.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
|
|
49021
|
+
(0, import_node_fs46.writeFileSync)((0, import_node_path44.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
|
|
48936
49022
|
const script = (0, import_node_path44.join)(work, "package_export.py");
|
|
48937
|
-
(0,
|
|
49023
|
+
(0, import_node_fs46.writeFileSync)(script, package_export_default);
|
|
48938
49024
|
await deps.python(script, ["verify", work, opts.ds, opts.screens]);
|
|
48939
49025
|
const verified = (0, import_node_path44.join)(work, "verified");
|
|
48940
|
-
const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0,
|
|
49026
|
+
const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs46.readFileSync)((0, import_node_path44.join)(verified, `${short3}.metadata.json`), "utf8"))]));
|
|
48941
49027
|
const plan = planAdoption(packageText, lockText, metadata);
|
|
48942
49028
|
for (const { name } of plan.result.adopted) {
|
|
48943
49029
|
const short3 = PACKAGES.find((p) => p.name === name).short;
|
|
48944
49030
|
await deps.npm(["cache", "add", (0, import_node_path44.join)(verified, `${short3}.tgz`)], opts.dir);
|
|
48945
49031
|
}
|
|
48946
|
-
(0,
|
|
48947
|
-
(0,
|
|
49032
|
+
(0, import_node_fs46.writeFileSync)(packagePath, plan.packageText);
|
|
49033
|
+
(0, import_node_fs46.writeFileSync)(lockPath, plan.lockText);
|
|
48948
49034
|
try {
|
|
48949
49035
|
await deps.npm(["ci", "--offline", "--ignore-scripts", "--no-audit", "--no-fund"], opts.dir);
|
|
48950
49036
|
} catch (error) {
|
|
48951
|
-
(0,
|
|
48952
|
-
(0,
|
|
49037
|
+
(0, import_node_fs46.writeFileSync)(packagePath, packageText);
|
|
49038
|
+
(0, import_node_fs46.writeFileSync)(lockPath, lockText);
|
|
48953
49039
|
throw new Error(`npm ci --offline failed, package.json and package-lock.json restored: ${error.message}`);
|
|
48954
49040
|
}
|
|
48955
49041
|
return plan.result;
|
|
48956
49042
|
} finally {
|
|
48957
|
-
(0,
|
|
49043
|
+
(0, import_node_fs46.rmSync)(work, { recursive: true, force: true });
|
|
48958
49044
|
}
|
|
48959
49045
|
}
|
|
48960
49046
|
var execFileBuffer = (0, import_node_util6.promisify)(import_node_child_process18.execFile);
|
|
@@ -49019,12 +49105,12 @@ function registerPackagesCommands(program3) {
|
|
|
49019
49105
|
init_cli_shared();
|
|
49020
49106
|
|
|
49021
49107
|
// src/wave-status.ts
|
|
49022
|
-
var
|
|
49108
|
+
var import_node_fs47 = require("node:fs");
|
|
49023
49109
|
function readStageSummary(worktreePath) {
|
|
49024
49110
|
const statePath = stageStatePath(worktreePath);
|
|
49025
|
-
if (!(0,
|
|
49111
|
+
if (!(0, import_node_fs47.existsSync)(statePath)) return void 0;
|
|
49026
49112
|
try {
|
|
49027
|
-
const state = JSON.parse((0,
|
|
49113
|
+
const state = JSON.parse((0, import_node_fs47.readFileSync)(statePath, "utf8"));
|
|
49028
49114
|
const port = typeof state.port === "number" ? state.port : void 0;
|
|
49029
49115
|
if (port == null || !Number.isInteger(port) || port <= 0) return void 0;
|
|
49030
49116
|
return { port, url: typeof state.url === "string" ? state.url : void 0 };
|
|
@@ -49081,7 +49167,7 @@ async function collectWaveStatus(deps) {
|
|
|
49081
49167
|
// src/discovery-commands.ts
|
|
49082
49168
|
init_github_client();
|
|
49083
49169
|
init_cli_shared();
|
|
49084
|
-
var
|
|
49170
|
+
var import_node_fs48 = require("node:fs");
|
|
49085
49171
|
var import_node_os22 = require("node:os");
|
|
49086
49172
|
var import_node_path45 = require("node:path");
|
|
49087
49173
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
@@ -49274,8 +49360,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
49274
49360
|
}
|
|
49275
49361
|
const home = (0, import_node_os22.homedir)();
|
|
49276
49362
|
const plugin = onboardPluginGate({
|
|
49277
|
-
readKnown: () => readFileSyncSafe((0, import_node_path45.join)(home, ...KNOWN_MARKETPLACES_RELATIVE),
|
|
49278
|
-
readSettings: () => readFileSyncSafe((0, import_node_path45.join)(home, ".claude", "settings.json"),
|
|
49363
|
+
readKnown: () => readFileSyncSafe((0, import_node_path45.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs48.readFileSync),
|
|
49364
|
+
readSettings: () => readFileSyncSafe((0, import_node_path45.join)(home, ".claude", "settings.json"), import_node_fs48.readFileSync)
|
|
49279
49365
|
});
|
|
49280
49366
|
return {
|
|
49281
49367
|
guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
|
|
@@ -52379,7 +52465,7 @@ function registerSessionReport(program3) {
|
|
|
52379
52465
|
}
|
|
52380
52466
|
|
|
52381
52467
|
// src/stage-commands.ts
|
|
52382
|
-
var
|
|
52468
|
+
var import_node_fs49 = require("node:fs");
|
|
52383
52469
|
var import_node_path46 = require("node:path");
|
|
52384
52470
|
init_cli_shared();
|
|
52385
52471
|
init_clean_exit();
|
|
@@ -52628,8 +52714,8 @@ function registerStageCommands(program3) {
|
|
|
52628
52714
|
const repo = Array.isArray(project2?.repos) && typeof project2.repos[0] === "string" ? project2.repos[0] : slug;
|
|
52629
52715
|
const resolution = decideStage({
|
|
52630
52716
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
52631
|
-
hasCompose: (0,
|
|
52632
|
-
hasEnvExample: (0,
|
|
52717
|
+
hasCompose: (0, import_node_fs49.existsSync)((0, import_node_path46.join)(process.cwd(), "docker-compose.yml")),
|
|
52718
|
+
hasEnvExample: (0, import_node_fs49.existsSync)((0, import_node_path46.join)(process.cwd(), ".env.example")),
|
|
52633
52719
|
repo
|
|
52634
52720
|
});
|
|
52635
52721
|
if (resolution.source === "derived" && resolution.derived) {
|
|
@@ -52922,14 +53008,14 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
|
|
|
52922
53008
|
|
|
52923
53009
|
// src/tenant-artifact.ts
|
|
52924
53010
|
var import_node_crypto16 = require("node:crypto");
|
|
52925
|
-
var
|
|
53011
|
+
var import_node_fs50 = require("node:fs");
|
|
52926
53012
|
var import_promises11 = require("node:fs/promises");
|
|
52927
53013
|
var import_node_path47 = require("node:path");
|
|
52928
53014
|
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}$/;
|
|
52929
53015
|
var MAX_BYTES = 5 * 1024 * 1024 * 1024;
|
|
52930
53016
|
async function sha256File(path2) {
|
|
52931
53017
|
const hash = (0, import_node_crypto16.createHash)("sha256");
|
|
52932
|
-
for await (const chunk of (0,
|
|
53018
|
+
for await (const chunk of (0, import_node_fs50.createReadStream)(path2)) hash.update(chunk);
|
|
52933
53019
|
return hash.digest("hex");
|
|
52934
53020
|
}
|
|
52935
53021
|
async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
@@ -52952,7 +53038,7 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
|
52952
53038
|
return [key, value];
|
|
52953
53039
|
}));
|
|
52954
53040
|
headers["content-length"] = String(info.size);
|
|
52955
|
-
const stream = (0,
|
|
53041
|
+
const stream = (0, import_node_fs50.createReadStream)(path2);
|
|
52956
53042
|
let uploaded;
|
|
52957
53043
|
try {
|
|
52958
53044
|
uploaded = await fetch(body.uploadUrl, {
|
|
@@ -53091,7 +53177,7 @@ function renderVerifySecrets(body) {
|
|
|
53091
53177
|
}
|
|
53092
53178
|
|
|
53093
53179
|
// src/command-register-developer.ts
|
|
53094
|
-
var
|
|
53180
|
+
var import_node_fs54 = require("node:fs");
|
|
53095
53181
|
init_clean_exit();
|
|
53096
53182
|
init_cli_shared();
|
|
53097
53183
|
init_error_codes();
|
|
@@ -53159,13 +53245,13 @@ async function executeWaveLand(plan, deps) {
|
|
|
53159
53245
|
// src/dist-drift.ts
|
|
53160
53246
|
var import_node_child_process19 = require("node:child_process");
|
|
53161
53247
|
var import_node_crypto18 = require("node:crypto");
|
|
53162
|
-
var
|
|
53248
|
+
var import_node_fs52 = require("node:fs");
|
|
53163
53249
|
var import_node_os23 = require("node:os");
|
|
53164
53250
|
var import_node_path49 = require("node:path");
|
|
53165
53251
|
|
|
53166
53252
|
// ../scripts/distribution-digest.mjs
|
|
53167
53253
|
var import_node_crypto17 = require("node:crypto");
|
|
53168
|
-
var
|
|
53254
|
+
var import_node_fs51 = require("node:fs");
|
|
53169
53255
|
var import_node_path48 = require("node:path");
|
|
53170
53256
|
var slash = (value) => value.replaceAll("\\", "/");
|
|
53171
53257
|
function repoPath(root, declaredPath, label) {
|
|
@@ -53179,7 +53265,7 @@ function repoPath(root, declaredPath, label) {
|
|
|
53179
53265
|
function digestFiles(files) {
|
|
53180
53266
|
const hash = (0, import_node_crypto17.createHash)("sha256");
|
|
53181
53267
|
for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
|
|
53182
|
-
const content = file.stat.isSymbolicLink() ? Buffer.from((0,
|
|
53268
|
+
const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs51.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs51.readFileSync)(file.absolute);
|
|
53183
53269
|
hash.update(file.relative, "utf8");
|
|
53184
53270
|
hash.update("\0");
|
|
53185
53271
|
hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
|
|
@@ -53194,8 +53280,8 @@ function digestFiles(files) {
|
|
|
53194
53280
|
function digestPackedFiles(packageRoot, packedFiles) {
|
|
53195
53281
|
return digestFiles(packedFiles.map((path2) => {
|
|
53196
53282
|
const absolute = repoPath(packageRoot, path2, "packed artifact identity path");
|
|
53197
|
-
if (!(0,
|
|
53198
|
-
return { absolute, relative: slash(path2), stat: (0,
|
|
53283
|
+
if (!(0, import_node_fs51.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
|
|
53284
|
+
return { absolute, relative: slash(path2), stat: (0, import_node_fs51.lstatSync)(absolute) };
|
|
53199
53285
|
}));
|
|
53200
53286
|
}
|
|
53201
53287
|
|
|
@@ -53279,12 +53365,12 @@ function renderDistDriftReceipt(receipt) {
|
|
|
53279
53365
|
return lines2;
|
|
53280
53366
|
}
|
|
53281
53367
|
function readOrNull(path2) {
|
|
53282
|
-
return (0,
|
|
53368
|
+
return (0, import_node_fs52.existsSync)(path2) ? (0, import_node_fs52.readFileSync)(path2) : null;
|
|
53283
53369
|
}
|
|
53284
53370
|
function walkFiles(root) {
|
|
53285
53371
|
const files = [];
|
|
53286
53372
|
const walk2 = (directory) => {
|
|
53287
|
-
for (const entry of (0,
|
|
53373
|
+
for (const entry of (0, import_node_fs52.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
53288
53374
|
const child2 = (0, import_node_path49.join)(directory, entry.name);
|
|
53289
53375
|
if (entry.isDirectory()) walk2(child2);
|
|
53290
53376
|
else files.push(child2);
|
|
@@ -53295,7 +53381,7 @@ function walkFiles(root) {
|
|
|
53295
53381
|
}
|
|
53296
53382
|
function bomPathFor(root) {
|
|
53297
53383
|
try {
|
|
53298
|
-
const registry2 = JSON.parse((0,
|
|
53384
|
+
const registry2 = JSON.parse((0, import_node_fs52.readFileSync)((0, import_node_path49.join)(root, "surfaces.json"), "utf8"));
|
|
53299
53385
|
return (0, import_node_path49.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
|
|
53300
53386
|
} catch {
|
|
53301
53387
|
return (0, import_node_path49.join)(root, "distribution-bom.json");
|
|
@@ -53311,7 +53397,7 @@ function rebuildTo(packageRoot, outDir) {
|
|
|
53311
53397
|
});
|
|
53312
53398
|
}
|
|
53313
53399
|
function runDistStatus(root) {
|
|
53314
|
-
const stage = (0,
|
|
53400
|
+
const stage = (0, import_node_fs52.mkdtempSync)((0, import_node_path49.join)((0, import_node_os23.tmpdir)(), "mmi-dist-drift-"));
|
|
53315
53401
|
let overlayCount = 0;
|
|
53316
53402
|
try {
|
|
53317
53403
|
const cliOut = (0, import_node_path49.join)(stage, "cli-dist");
|
|
@@ -53327,19 +53413,19 @@ function runDistStatus(root) {
|
|
|
53327
53413
|
const tree = (path2) => readOrNull((0, import_node_path49.join)(root, path2));
|
|
53328
53414
|
const distRoot = (0, import_node_path49.join)(root, "cli", "dist");
|
|
53329
53415
|
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path49.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
|
|
53330
|
-
const bom = JSON.parse((0,
|
|
53416
|
+
const bom = JSON.parse((0, import_node_fs52.readFileSync)(bomPathFor(root), "utf8"));
|
|
53331
53417
|
const digest = (entries) => {
|
|
53332
53418
|
const overlay = (0, import_node_path49.join)(stage, `overlay-${overlayCount++}`);
|
|
53333
53419
|
for (const entry of entries) {
|
|
53334
53420
|
const target = (0, import_node_path49.join)(overlay, entry.path);
|
|
53335
|
-
(0,
|
|
53336
|
-
(0,
|
|
53421
|
+
(0, import_node_fs52.mkdirSync)((0, import_node_path49.dirname)(target), { recursive: true });
|
|
53422
|
+
(0, import_node_fs52.writeFileSync)(target, entry.bytes);
|
|
53337
53423
|
}
|
|
53338
53424
|
return digestPackedFiles(overlay, entries.map((entry) => entry.path));
|
|
53339
53425
|
};
|
|
53340
53426
|
return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
|
|
53341
53427
|
} finally {
|
|
53342
|
-
(0,
|
|
53428
|
+
(0, import_node_fs52.rmSync)(stage, { recursive: true, force: true });
|
|
53343
53429
|
}
|
|
53344
53430
|
}
|
|
53345
53431
|
|
|
@@ -53552,7 +53638,7 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
|
|
|
53552
53638
|
|
|
53553
53639
|
// src/spawn-policy-core.ts
|
|
53554
53640
|
var import_node_child_process20 = require("node:child_process");
|
|
53555
|
-
var
|
|
53641
|
+
var import_node_fs53 = require("node:fs");
|
|
53556
53642
|
var import_node_path51 = require("node:path");
|
|
53557
53643
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
53558
53644
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
@@ -53639,7 +53725,7 @@ function runSpawnPolicy(root) {
|
|
|
53639
53725
|
for (const file of files) {
|
|
53640
53726
|
let raw;
|
|
53641
53727
|
try {
|
|
53642
|
-
raw = (0,
|
|
53728
|
+
raw = (0, import_node_fs53.readFileSync)((0, import_node_path51.join)(root, file), "utf8");
|
|
53643
53729
|
} catch {
|
|
53644
53730
|
continue;
|
|
53645
53731
|
}
|
|
@@ -53660,18 +53746,18 @@ function registerDeveloperCommands(program3) {
|
|
|
53660
53746
|
const rules = program3.command("rules").description("org-managed .gitignore delivery");
|
|
53661
53747
|
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) => {
|
|
53662
53748
|
const path2 = (0, import_node_path52.join)(process.cwd(), ".gitignore");
|
|
53663
|
-
const current = (0,
|
|
53749
|
+
const current = (0, import_node_fs54.existsSync)(path2) ? (0, import_node_fs54.readFileSync)(path2, "utf8") : null;
|
|
53664
53750
|
const plan = planManagedGitignore(current);
|
|
53665
53751
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
53666
53752
|
if (opts.json) {
|
|
53667
|
-
if (opts.write && plan.changed) (0,
|
|
53753
|
+
if (opts.write && plan.changed) (0, import_node_fs54.writeFileSync)(path2, plan.content, "utf8");
|
|
53668
53754
|
console.log(JSON.stringify(plan, null, 2));
|
|
53669
53755
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
53670
53756
|
return;
|
|
53671
53757
|
}
|
|
53672
53758
|
if (opts.write) {
|
|
53673
53759
|
if (plan.changed) {
|
|
53674
|
-
(0,
|
|
53760
|
+
(0, import_node_fs54.writeFileSync)(path2, plan.content, "utf8");
|
|
53675
53761
|
console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
|
|
53676
53762
|
} else {
|
|
53677
53763
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
@@ -54071,7 +54157,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
54071
54157
|
);
|
|
54072
54158
|
const result = applyPluginCachePlan(
|
|
54073
54159
|
plan,
|
|
54074
|
-
(p) => (0,
|
|
54160
|
+
(p) => (0, import_node_fs55.rmSync)(p, { recursive: true }),
|
|
54075
54161
|
stagingApplyFsGuard(configRoot)
|
|
54076
54162
|
);
|
|
54077
54163
|
return {
|
|
@@ -54106,7 +54192,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
54106
54192
|
// has no generated routing index would
|
|
54107
54193
|
// get a permanent — demanding an artifact it never asked for.
|
|
54108
54194
|
docsIndexState: (root) => {
|
|
54109
|
-
if (!(0,
|
|
54195
|
+
if (!(0, import_node_fs55.existsSync)((0, import_node_path53.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
54110
54196
|
const real = createDocsIndexDeps(root);
|
|
54111
54197
|
let docs;
|
|
54112
54198
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -54115,7 +54201,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
54115
54201
|
},
|
|
54116
54202
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
54117
54203
|
healDocsIndex: (root) => {
|
|
54118
|
-
if (!(0,
|
|
54204
|
+
if (!(0, import_node_fs55.existsSync)((0, import_node_path53.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
54119
54205
|
const real = createDocsIndexDeps(root);
|
|
54120
54206
|
let docs;
|
|
54121
54207
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -54829,7 +54915,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
54829
54915
|
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`);
|
|
54830
54916
|
if (o.secretsFile) {
|
|
54831
54917
|
try {
|
|
54832
|
-
vars.push(`secrets=${(0,
|
|
54918
|
+
vars.push(`secrets=${(0, import_node_fs55.readFileSync)(o.secretsFile, "utf8")}`);
|
|
54833
54919
|
} catch (e) {
|
|
54834
54920
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
54835
54921
|
}
|
|
@@ -55218,15 +55304,15 @@ function ciAuditDeps2() {
|
|
|
55218
55304
|
readSeedFile: (path2) => {
|
|
55219
55305
|
if (!root) return null;
|
|
55220
55306
|
const fullPath = (0, import_node_path53.join)(root, path2);
|
|
55221
|
-
return (0,
|
|
55307
|
+
return (0, import_node_fs55.existsSync)(fullPath) ? (0, import_node_fs55.readFileSync)(fullPath, "utf8") : null;
|
|
55222
55308
|
}
|
|
55223
55309
|
};
|
|
55224
55310
|
}
|
|
55225
55311
|
function hubRoot2() {
|
|
55226
55312
|
const fromPkg = (0, import_node_path53.join)(__dirname, "..", "..");
|
|
55227
55313
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
55228
|
-
if ((0,
|
|
55229
|
-
if ((0,
|
|
55314
|
+
if ((0, import_node_fs55.existsSync)((0, import_node_path53.join)(fromPkg, marker))) return fromPkg;
|
|
55315
|
+
if ((0, import_node_fs55.existsSync)((0, import_node_path53.join)(process.cwd(), marker))) return process.cwd();
|
|
55230
55316
|
return null;
|
|
55231
55317
|
}
|
|
55232
55318
|
registerQueryCommands(program2);
|
|
@@ -55352,10 +55438,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
55352
55438
|
targets = resolution.targets;
|
|
55353
55439
|
}
|
|
55354
55440
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
55355
|
-
const fileMatrix = (0,
|
|
55441
|
+
const fileMatrix = (0, import_node_fs55.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs55.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
55356
55442
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
55357
55443
|
const dataAccess = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
55358
|
-
const sanctioned = (0,
|
|
55444
|
+
const sanctioned = (0, import_node_fs55.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs55.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
55359
55445
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
55360
55446
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
55361
55447
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -55386,7 +55472,7 @@ function directoryBytes(path2) {
|
|
|
55386
55472
|
let total = 0;
|
|
55387
55473
|
let entries;
|
|
55388
55474
|
try {
|
|
55389
|
-
entries = (0,
|
|
55475
|
+
entries = (0, import_node_fs55.readdirSync)(path2, { withFileTypes: true });
|
|
55390
55476
|
} catch {
|
|
55391
55477
|
return 0;
|
|
55392
55478
|
}
|
|
@@ -55395,7 +55481,7 @@ function directoryBytes(path2) {
|
|
|
55395
55481
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
55396
55482
|
else {
|
|
55397
55483
|
try {
|
|
55398
|
-
total += (0,
|
|
55484
|
+
total += (0, import_node_fs55.statSync)(child2).size;
|
|
55399
55485
|
} catch {
|
|
55400
55486
|
}
|
|
55401
55487
|
}
|
|
@@ -55403,25 +55489,25 @@ function directoryBytes(path2) {
|
|
|
55403
55489
|
return total;
|
|
55404
55490
|
}
|
|
55405
55491
|
function listDirEntries(dir) {
|
|
55406
|
-
return (0,
|
|
55492
|
+
return (0, import_node_fs55.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
55407
55493
|
}
|
|
55408
55494
|
function readInstalledPluginRefs(configRoot) {
|
|
55409
55495
|
const p = installedPluginsPathForConfig(configRoot);
|
|
55410
|
-
if (!(0,
|
|
55496
|
+
if (!(0, import_node_fs55.existsSync)(p)) return [];
|
|
55411
55497
|
try {
|
|
55412
|
-
return installedPluginPaths((0,
|
|
55498
|
+
return installedPluginPaths((0, import_node_fs55.readFileSync)(p, "utf8"));
|
|
55413
55499
|
} catch {
|
|
55414
55500
|
return null;
|
|
55415
55501
|
}
|
|
55416
55502
|
}
|
|
55417
55503
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
55418
55504
|
return {
|
|
55419
|
-
exists: (p) => (0,
|
|
55420
|
-
listVersionDirs: (root) => (0,
|
|
55505
|
+
exists: (p) => (0, import_node_fs55.existsSync)(p),
|
|
55506
|
+
listVersionDirs: (root) => (0, import_node_fs55.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
55421
55507
|
dirBytes,
|
|
55422
|
-
listStagingDirs: (root) => (0,
|
|
55508
|
+
listStagingDirs: (root) => (0, import_node_fs55.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
55423
55509
|
try {
|
|
55424
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path53.join)(root, d.name), listDirEntries, (p) => (0,
|
|
55510
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path53.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs55.statSync)(p).mtimeMs) };
|
|
55425
55511
|
} catch {
|
|
55426
55512
|
return { name: d.name, mtimeMs: Date.now() };
|
|
55427
55513
|
}
|
|
@@ -55436,9 +55522,9 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
55436
55522
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
55437
55523
|
mtimeMs: (name) => {
|
|
55438
55524
|
const p = (0, import_node_path53.join)(stagingRoot, name);
|
|
55439
|
-
if (!(0,
|
|
55525
|
+
if (!(0, import_node_fs55.existsSync)(p)) return null;
|
|
55440
55526
|
try {
|
|
55441
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
55527
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs55.statSync)(q).mtimeMs);
|
|
55442
55528
|
} catch {
|
|
55443
55529
|
return null;
|
|
55444
55530
|
}
|
|
@@ -55464,7 +55550,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
55464
55550
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
55465
55551
|
);
|
|
55466
55552
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
55467
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
55553
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs55.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
55468
55554
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
55469
55555
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
55470
55556
|
else console.log(renderPluginCachePlan(plan, result));
|