@mutmutco/cli 4.6.36 → 4.6.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.cjs +650 -351
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -1229,8 +1229,6 @@ var init_house_map = __esm({
|
|
|
1229
1229
|
// --- harbour — declared lanes -------------------------------------------------------------------
|
|
1230
1230
|
"org schedules": "harbour",
|
|
1231
1231
|
// register/run/park schedule rows under the one lane contract
|
|
1232
|
-
intercom: "harbour",
|
|
1233
|
-
// #9184: the guest coordination lane to the MM Coordinator — a bounded lane, not a claim on org truth
|
|
1234
1232
|
// --- devops — shipping --------------------------------------------------------------------------
|
|
1235
1233
|
pr: "devops",
|
|
1236
1234
|
read: "devops",
|
|
@@ -1279,6 +1277,11 @@ var init_house_map = __esm({
|
|
|
1279
1277
|
pickup: "learning",
|
|
1280
1278
|
// cloud-agent adoption gate (#5708)
|
|
1281
1279
|
// --- core — the front door itself ---------------------------------------------------------------
|
|
1280
|
+
// #9993: `intercom` is the guest's own coordination door, not a declared org lane — it reads and
|
|
1281
|
+
// writes nothing of this org's truth (no schedules, no registry, no board), it talks to the Jerv
|
|
1282
|
+
// Gateway as this caller's own GitHub identity. So it is core: `mmi-cli intercom guest …`, unprefixed.
|
|
1283
|
+
// The old `harbour intercom guest …` spelling still runs through HOUSE_ALIASES below.
|
|
1284
|
+
intercom: "core",
|
|
1282
1285
|
commands: "core",
|
|
1283
1286
|
whoami: "core",
|
|
1284
1287
|
spawn: "core",
|
|
@@ -1303,7 +1306,9 @@ var init_house_map = __esm({
|
|
|
1303
1306
|
HOUSE_ALIASES = {
|
|
1304
1307
|
issue: ["devops"],
|
|
1305
1308
|
// filing/managing issues is oracle, but reads as shipping work
|
|
1306
|
-
"issue create": ["devops"]
|
|
1309
|
+
"issue create": ["devops"],
|
|
1310
|
+
intercom: ["harbour"]
|
|
1311
|
+
// #9993: `intercom` moved out of harbour to core; the old spelling still routes
|
|
1307
1312
|
};
|
|
1308
1313
|
}
|
|
1309
1314
|
});
|
|
@@ -13690,7 +13695,7 @@ function isAgentScratchPath(path2) {
|
|
|
13690
13695
|
return /^tmp_[^/]+$/.test(normalized);
|
|
13691
13696
|
}
|
|
13692
13697
|
var PUSH_WALL_AGENT_CONFIG_DIRS = [".cursor/rules/", ".codex/", ".agents/"];
|
|
13693
|
-
var LIVE_SESSION_DIRS = [".jerv/", ".pi/"];
|
|
13698
|
+
var LIVE_SESSION_DIRS = [".jerv/", ".pi/", "review-state/"];
|
|
13694
13699
|
function pathUnderDirPrefix(path2, dir) {
|
|
13695
13700
|
return path2 === dir || path2 === dir.slice(0, -1) || path2.startsWith(dir) || path2.includes(`/${dir}`);
|
|
13696
13701
|
}
|
|
@@ -14549,7 +14554,7 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
|
14549
14554
|
function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
14550
14555
|
const copyFile = deps.copyFile ?? import_node_fs12.copyFileSync;
|
|
14551
14556
|
const fileSize = deps.fileSize ?? ((path2) => (0, import_node_fs12.statSync)(path2).size);
|
|
14552
|
-
const
|
|
14557
|
+
const writeFile5 = deps.writeFile ?? ((path2, text) => (0, import_node_fs12.writeFileSync)(path2, text));
|
|
14553
14558
|
const mkdirp = deps.mkdirp ?? ((path2) => {
|
|
14554
14559
|
(0, import_node_fs12.mkdirSync)(path2, { recursive: true });
|
|
14555
14560
|
});
|
|
@@ -14576,7 +14581,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
|
14576
14581
|
bytes += copied;
|
|
14577
14582
|
}
|
|
14578
14583
|
const manifest = (0, import_node_path9.join)((0, import_node_path9.dirname)(dest), "tmp-manifest.json");
|
|
14579
|
-
|
|
14584
|
+
writeFile5(manifest, `${JSON.stringify({ files: scan.entries.map(({ relPath, bytes: b }) => ({ relPath, bytes: b })), bytes, excluded: scan.excluded, skippedLinks: scan.links }, null, 2)}
|
|
14580
14585
|
`);
|
|
14581
14586
|
return { status: "archived", path: dest, runScope, entries: scan.entries.length, bytes, manifest, excluded: scan.excluded, skippedLinks: scan.links };
|
|
14582
14587
|
} catch (e) {
|
|
@@ -15261,8 +15266,8 @@ function resolveVerbAliasShim(argv) {
|
|
|
15261
15266
|
}
|
|
15262
15267
|
|
|
15263
15268
|
// src/command-composition.ts
|
|
15264
|
-
var
|
|
15265
|
-
var
|
|
15269
|
+
var import_node_os29 = require("node:os");
|
|
15270
|
+
var import_node_path65 = require("node:path");
|
|
15266
15271
|
|
|
15267
15272
|
// src/board-read.ts
|
|
15268
15273
|
var import_node_fs18 = require("node:fs");
|
|
@@ -15686,6 +15691,9 @@ var APP_SUBPROCESS_ROUTED_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
15686
15691
|
// #9871: queue enqueue + auto-merge fallback ride ghFilingAppEnv (#9490/#9528; receipt credential app_installation).
|
|
15687
15692
|
"pr land"
|
|
15688
15693
|
]);
|
|
15694
|
+
function ineligibleActorNote(commandPath4) {
|
|
15695
|
+
return commandPath4 === "board move" ? "app actor: board move retains the existing process identity; status-write credential is selected separately and reported in its receipt" : `app actor: note, not a failure \u2014 '${commandPath4}' is personal-only (viewer/@me/authorship), so it ran on the personal token despite ${APP_ACTOR_ENV}=app`;
|
|
15696
|
+
}
|
|
15689
15697
|
async function activateAppActor(commandPath4, env, mint, note = (line) => process.stderr.write(`${line}
|
|
15690
15698
|
`)) {
|
|
15691
15699
|
activeQuotaScope = void 0;
|
|
@@ -15698,7 +15706,7 @@ async function activateAppActor(commandPath4, env, mint, note = (line) => proces
|
|
|
15698
15706
|
if (!APP_ELIGIBLE_COMMANDS.has(commandPath4) && !APP_WRITE_COMMANDS.has(commandPath4)) {
|
|
15699
15707
|
if (requested === "app" && !notedIneligibleCommand && !APP_SUBPROCESS_ROUTED_COMMANDS.has(commandPath4)) {
|
|
15700
15708
|
notedIneligibleCommand = true;
|
|
15701
|
-
note(
|
|
15709
|
+
note(ineligibleActorNote(commandPath4));
|
|
15702
15710
|
}
|
|
15703
15711
|
return "personal";
|
|
15704
15712
|
}
|
|
@@ -22762,23 +22770,8 @@ var BOARD_WRITE_REFUSAL_TYPES = /* @__PURE__ */ new Set(["INSUFFICIENT_SCOPES",
|
|
|
22762
22770
|
async function updateItemSingleSelect(client, projectId, itemId, fieldId, optionId) {
|
|
22763
22771
|
if (process.env.MMI_ACTOR?.trim() === "app") {
|
|
22764
22772
|
const { writeHubBoardField: writeHubBoardField2 } = await Promise.resolve().then(() => (init_board_write(), board_write_exports));
|
|
22765
|
-
|
|
22766
|
-
|
|
22767
|
-
return { credential: "app_installation" };
|
|
22768
|
-
} catch (error) {
|
|
22769
|
-
const refusal = error;
|
|
22770
|
-
if (refusal?.rateLimited !== true || refusal.status !== 503 && refusal.status !== 429) throw error;
|
|
22771
|
-
try {
|
|
22772
|
-
const identity = await githubKeyringTokenRead();
|
|
22773
|
-
if (identity.state !== "ok") throw error;
|
|
22774
|
-
const owner = createGitHubClient({ token: async () => identity.token });
|
|
22775
|
-
await owner.graphql(UPDATE_ITEM_FIELD_MUTATION, { projectId, itemId, fieldId, optionId });
|
|
22776
|
-
} catch {
|
|
22777
|
-
throw error;
|
|
22778
|
-
}
|
|
22779
|
-
process.stderr.write("board write: Hub App allowance exhausted \u2014 wrote with your own GitHub login (#9053)\n");
|
|
22780
|
-
return { credential: "user" };
|
|
22781
|
-
}
|
|
22773
|
+
await writeHubBoardField2({ projectId, itemId, fieldId, optionId });
|
|
22774
|
+
return { credential: "app_installation" };
|
|
22782
22775
|
}
|
|
22783
22776
|
try {
|
|
22784
22777
|
await client.graphql(UPDATE_ITEM_FIELD_MUTATION, { projectId, itemId, fieldId, optionId });
|
|
@@ -22923,19 +22916,20 @@ function renderBoardItem(item) {
|
|
|
22923
22916
|
}
|
|
22924
22917
|
return lines2.join("\n");
|
|
22925
22918
|
}
|
|
22919
|
+
var CACHED_CONFIRM_CLAUSE = " \xB7 items closed/created after the build may still differ \u2014 confirm with --direct";
|
|
22926
22920
|
function renderCachedBoardSource(report) {
|
|
22927
22921
|
if (typeof report.stale?.ageMs === "number") {
|
|
22928
22922
|
const ageSeconds = Math.floor(report.stale.ageMs / 1e3);
|
|
22929
|
-
return `source: cache \xB7 built ${report.builtAt ? `${report.builtAt} (${ageSeconds}s old)` : `${ageSeconds}s ago`} \xB7 stale`;
|
|
22923
|
+
return `source: cache \xB7 built ${report.builtAt ? `${report.builtAt} (${ageSeconds}s old)` : `${ageSeconds}s ago`} \xB7 stale${CACHED_CONFIRM_CLAUSE}`;
|
|
22930
22924
|
}
|
|
22931
22925
|
if (report.builtAt) {
|
|
22932
22926
|
const builtMs = Date.parse(report.builtAt);
|
|
22933
22927
|
if (Number.isFinite(builtMs)) {
|
|
22934
|
-
return `source: cache \xB7 built ${report.builtAt} (${Math.max(0, Math.floor((Date.now() - builtMs) / 1e3))}s old) \xB7 fresh`;
|
|
22928
|
+
return `source: cache \xB7 built ${report.builtAt} (${Math.max(0, Math.floor((Date.now() - builtMs) / 1e3))}s old) \xB7 fresh${CACHED_CONFIRM_CLAUSE}`;
|
|
22935
22929
|
}
|
|
22936
|
-
return `source: cache \xB7 built ${report.builtAt} \xB7 fresh`;
|
|
22930
|
+
return `source: cache \xB7 built ${report.builtAt} \xB7 fresh${CACHED_CONFIRM_CLAUSE}`;
|
|
22937
22931
|
}
|
|
22938
|
-
return
|
|
22932
|
+
return `source: cache${CACHED_CONFIRM_CLAUSE}`;
|
|
22939
22933
|
}
|
|
22940
22934
|
function renderBoardReport(report) {
|
|
22941
22935
|
const lines2 = [`Board \xB7 ${report.project.title} \xB7 @${report.viewer}`, report.source === "cache" ? renderCachedBoardSource(report) : renderBoardSource()];
|
|
@@ -25230,6 +25224,20 @@ function upsertManagedSeedBlock(current, desired, begin, end) {
|
|
|
25230
25224
|
action: "replace"
|
|
25231
25225
|
};
|
|
25232
25226
|
}
|
|
25227
|
+
async function headStrictlyBehind(execGit, head, remoteSha, defaultBranch) {
|
|
25228
|
+
try {
|
|
25229
|
+
await execGit(["fetch", "--no-tags", "--quiet", "origin", `refs/heads/${defaultBranch}`]);
|
|
25230
|
+
} catch (e) {
|
|
25231
|
+
return { superseded: false, unknown: `could not fetch origin/${defaultBranch} to check ancestry (${e.message})` };
|
|
25232
|
+
}
|
|
25233
|
+
try {
|
|
25234
|
+
await execGit(["merge-base", "--is-ancestor", head, remoteSha]);
|
|
25235
|
+
return { superseded: true };
|
|
25236
|
+
} catch (e) {
|
|
25237
|
+
if (e.code === 1) return { superseded: false };
|
|
25238
|
+
return { superseded: false, unknown: `could not prove whether this checkout is an ancestor of origin/${defaultBranch} (${e.message}) \u2014 a shallow checkout cannot prove ancestry, so fetch full history` };
|
|
25239
|
+
}
|
|
25240
|
+
}
|
|
25233
25241
|
async function resolveHubSeedSource(execGit, defaultBranch = "development") {
|
|
25234
25242
|
let status;
|
|
25235
25243
|
try {
|
|
@@ -25257,9 +25265,19 @@ remedy (non-destructive): run this command from a clean dedicated worktree \u201
|
|
|
25257
25265
|
return { ok: false, reason: `\`git ls-remote origin refs/heads/${defaultBranch}\` returned nothing \u2014 refusing to read seeds without a known upstream revision` };
|
|
25258
25266
|
}
|
|
25259
25267
|
if (head !== remoteSha) {
|
|
25268
|
+
const behind = await headStrictlyBehind(execGit, head, remoteSha, defaultBranch);
|
|
25269
|
+
if (behind.superseded) {
|
|
25270
|
+
return {
|
|
25271
|
+
ok: false,
|
|
25272
|
+
superseded: true,
|
|
25273
|
+
head,
|
|
25274
|
+
remoteSha,
|
|
25275
|
+
reason: `this checkout (${head}) is behind origin/${defaultBranch} (${remoteSha}) \u2014 a newer commit overtook this run, whose own queued run owns the tick; seeding nothing and exiting green`
|
|
25276
|
+
};
|
|
25277
|
+
}
|
|
25260
25278
|
return {
|
|
25261
25279
|
ok: false,
|
|
25262
|
-
reason: `this checkout (${head}) is not origin/${defaultBranch} (${remoteSha}) \u2014 fast-forward to origin/${defaultBranch} first; a stale or ahead checkout must not seed the fleet`
|
|
25280
|
+
reason: `this checkout (${head}) is not origin/${defaultBranch} (${remoteSha}) \u2014 fast-forward to origin/${defaultBranch} first; a stale or ahead checkout must not seed the fleet${behind.unknown ? ` (${behind.unknown})` : ""}`
|
|
25263
25281
|
};
|
|
25264
25282
|
}
|
|
25265
25283
|
return { ok: true, sha: head };
|
|
@@ -25302,6 +25320,8 @@ var MANAGED_GITIGNORE_LINES = [
|
|
|
25302
25320
|
// (measured on the v3.120.0 Hub train), with no legal resolution: committing another host's runtime
|
|
25303
25321
|
// state is not an option, so the operator's only move was to park the directory outside the repo.
|
|
25304
25322
|
".pi/",
|
|
25323
|
+
// #10003: reviewer worktrees — never committable, regenerate during review (same runtime class as `.pi/`).
|
|
25324
|
+
"/review-state/",
|
|
25305
25325
|
".aws-sam/",
|
|
25306
25326
|
// jerv-cli lease markers are ephemeral runtime state and never tracked. Hub#2586.
|
|
25307
25327
|
".jerv-lease.json",
|
|
@@ -25427,7 +25447,7 @@ function extractForwardRefs(markdown) {
|
|
|
25427
25447
|
});
|
|
25428
25448
|
return refs;
|
|
25429
25449
|
}
|
|
25430
|
-
function checkPins(root,
|
|
25450
|
+
function checkPins(root, readFile11, docs) {
|
|
25431
25451
|
const findings = [];
|
|
25432
25452
|
for (const [doc, markdown] of Object.entries(docs)) {
|
|
25433
25453
|
for (const pin of extractPins(markdown)) {
|
|
@@ -25435,7 +25455,7 @@ function checkPins(root, readFile10, docs) {
|
|
|
25435
25455
|
findings.push({ kind: "malformed-pin", doc, line: pin.line, detail: pin.text });
|
|
25436
25456
|
continue;
|
|
25437
25457
|
}
|
|
25438
|
-
const source =
|
|
25458
|
+
const source = readFile11((0, import_node_path16.join)(root, pin.file));
|
|
25439
25459
|
if (source == null) {
|
|
25440
25460
|
findings.push({ kind: "missing-test", doc, line: pin.line, detail: pin.file });
|
|
25441
25461
|
continue;
|
|
@@ -25731,15 +25751,15 @@ function checkRunbookCoverage(docs, base, git3) {
|
|
|
25731
25751
|
}
|
|
25732
25752
|
return findings;
|
|
25733
25753
|
}
|
|
25734
|
-
function checkSeedRunbookReview(root, base,
|
|
25735
|
-
const manifestJson =
|
|
25754
|
+
function checkSeedRunbookReview(root, base, readFile11, git3) {
|
|
25755
|
+
const manifestJson = readFile11((0, import_node_path16.join)(root, "skills/bootstrap/seeds/manifest.json"));
|
|
25736
25756
|
if (!manifestJson) return [];
|
|
25737
25757
|
const changed = new Set(git3(["diff", "--name-only", `${base}...HEAD`]).split(/\r?\n/).filter(Boolean));
|
|
25738
25758
|
const touched = loadBootstrapSeeds(manifestJson).seeds.filter((seed) => isPropagatableSeed(seed) && changed.has(seedSourcePath(seed)));
|
|
25739
25759
|
if (!touched.length) return [];
|
|
25740
25760
|
let declarations = null;
|
|
25741
25761
|
try {
|
|
25742
|
-
declarations = JSON.parse(
|
|
25762
|
+
declarations = JSON.parse(readFile11((0, import_node_path16.join)(root, RUNBOOK_UNAFFECTED_PATH)) ?? "null");
|
|
25743
25763
|
} catch {
|
|
25744
25764
|
declarations = null;
|
|
25745
25765
|
}
|
|
@@ -25826,12 +25846,12 @@ function defaultTrackedFiles(root, firstSegments, exec3 = import_node_child_proc
|
|
|
25826
25846
|
throw error;
|
|
25827
25847
|
}
|
|
25828
25848
|
}
|
|
25829
|
-
function mergeGroupRunbookMessages(root, deps,
|
|
25849
|
+
function mergeGroupRunbookMessages(root, deps, readFile11, warnings) {
|
|
25830
25850
|
const env = deps.env ?? process.env;
|
|
25831
25851
|
if (env.GITHUB_EVENT_NAME !== "merge_group") return "";
|
|
25832
25852
|
let source = "merge_group event payload";
|
|
25833
25853
|
try {
|
|
25834
|
-
const event = JSON.parse(
|
|
25854
|
+
const event = JSON.parse(readFile11(env.GITHUB_EVENT_PATH ?? "") ?? "");
|
|
25835
25855
|
const match = /^refs\/heads\/gh-readonly-queue\/.+\/pr-([1-9]\d*)-[a-f0-9]+$/.exec(event.merge_group?.head_ref ?? "");
|
|
25836
25856
|
const repo = env.GITHUB_REPOSITORY;
|
|
25837
25857
|
if (!match || !repo || !/^[\w.-]+\/[\w.-]+$/.test(repo)) throw new Error("invalid queue source");
|
|
@@ -25854,7 +25874,7 @@ function mergeGroupRunbookMessages(root, deps, readFile10, warnings) {
|
|
|
25854
25874
|
}
|
|
25855
25875
|
}
|
|
25856
25876
|
function runDocRefs(root, deps = {}) {
|
|
25857
|
-
const
|
|
25877
|
+
const readFile11 = deps.readFile ?? readFileOrNull;
|
|
25858
25878
|
const exists = deps.exists ?? import_node_fs19.existsSync;
|
|
25859
25879
|
const listDocs = deps.listDocs ?? defaultListDocs;
|
|
25860
25880
|
const isIgnored = deps.isIgnored ?? ((paths) => defaultIsIgnored(root, paths));
|
|
@@ -25868,11 +25888,11 @@ function runDocRefs(root, deps = {}) {
|
|
|
25868
25888
|
const walked = listDocs(root);
|
|
25869
25889
|
const ignoredDocs = walked.length ? isIgnored(walked) : /* @__PURE__ */ new Set();
|
|
25870
25890
|
const docs = Object.fromEntries(
|
|
25871
|
-
walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel,
|
|
25891
|
+
walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile11((0, import_node_path16.join)(root, rel))]).filter(([rel, body]) => body != null && (!deps.runbooksOnly || isRunbook(rel, body)))
|
|
25872
25892
|
);
|
|
25873
25893
|
const refResult = checkRefs(root, { exists, isIgnored, trackedFirstSegments }, docs);
|
|
25874
25894
|
const findings = [
|
|
25875
|
-
...checkPins(root,
|
|
25895
|
+
...checkPins(root, readFile11, docs).findings,
|
|
25876
25896
|
...refResult.findings
|
|
25877
25897
|
];
|
|
25878
25898
|
if (commandPaths == null) {
|
|
@@ -25886,7 +25906,7 @@ function runDocRefs(root, deps = {}) {
|
|
|
25886
25906
|
findings.push(...checkCommands(docs, commandPaths).findings);
|
|
25887
25907
|
}
|
|
25888
25908
|
const runbooks = Object.fromEntries(Object.entries(docs).filter(([doc, body]) => isRunbook(doc, body)));
|
|
25889
|
-
findings.push(...checkRunbookClaims(root, runbooks, { readFile:
|
|
25909
|
+
findings.push(...checkRunbookClaims(root, runbooks, { readFile: readFile11, exists, isIgnored, trackedFirstSegments, commandPaths, commandOptions: deps.commandOptions, commandSource: deps.commandSource }));
|
|
25890
25910
|
const warnings = [...refResult.warnings];
|
|
25891
25911
|
if (deps.runbooksOnly && !Object.keys(runbooks).length) {
|
|
25892
25912
|
warnings.push({ kind: "runbook-missing", doc: "docs/Guides/operator.md", line: 0, detail: "no operator runbook or <!-- runbook --> document exists" });
|
|
@@ -25894,9 +25914,9 @@ function runDocRefs(root, deps = {}) {
|
|
|
25894
25914
|
if (deps.base) {
|
|
25895
25915
|
const git3 = deps.git ?? ((args) => (0, import_node_child_process6.execFileSync)("git", args, { cwd: root, encoding: "utf8", maxBuffer: CHECK_IGNORE_MAX_BUFFER, windowsHide: true }));
|
|
25896
25916
|
const stale = checkRunbookCoverage(runbooks, deps.base, git3);
|
|
25897
|
-
const seedStale = checkSeedRunbookReview(root, deps.base,
|
|
25917
|
+
const seedStale = checkSeedRunbookReview(root, deps.base, readFile11, git3);
|
|
25898
25918
|
const messages = git3(["log", "--format=%B", `${deps.base}..HEAD`]);
|
|
25899
|
-
const sourceMessages = stale.length || seedStale.length ? mergeGroupRunbookMessages(root, deps,
|
|
25919
|
+
const sourceMessages = stale.length || seedStale.length ? mergeGroupRunbookMessages(root, deps, readFile11, warnings) : "";
|
|
25900
25920
|
const allMessages = `${messages}
|
|
25901
25921
|
${sourceMessages}`;
|
|
25902
25922
|
const override = /^Runbook-Unaffected:[ \t]*\S.*$/m.test(allMessages);
|
|
@@ -26855,22 +26875,22 @@ function renderBootstrapAncillaryPlan(labels, environments, exceptions = []) {
|
|
|
26855
26875
|
...exceptions.map(({ name, detail }) => ` OK environment ${name} (${detail})`)
|
|
26856
26876
|
].join("\n");
|
|
26857
26877
|
}
|
|
26858
|
-
function resolveSeedContent(seed, vars,
|
|
26859
|
-
if (seed.source === "self") return
|
|
26878
|
+
function resolveSeedContent(seed, vars, readFile11) {
|
|
26879
|
+
if (seed.source === "self") return readFile11(seed.target);
|
|
26860
26880
|
if (seed.source.startsWith("seed:")) {
|
|
26861
|
-
const tmpl =
|
|
26881
|
+
const tmpl = readFile11(`skills/bootstrap/seeds/${seed.source.slice("seed:".length)}`);
|
|
26862
26882
|
return tmpl == null ? null : renderSeed(tmpl, vars);
|
|
26863
26883
|
}
|
|
26864
26884
|
return null;
|
|
26865
26885
|
}
|
|
26866
|
-
function resolveSeedWriteContent(seed, vars,
|
|
26886
|
+
function resolveSeedWriteContent(seed, vars, readFile11, remoteContent) {
|
|
26867
26887
|
if (!seed.managedBlock) {
|
|
26868
|
-
return { ok: true, content: resolveSeedContent(seed, vars,
|
|
26888
|
+
return { ok: true, content: resolveSeedContent(seed, vars, readFile11), managed: seed.source === "managed-block" };
|
|
26869
26889
|
}
|
|
26870
|
-
const base = remoteContent ?? resolveSeedContent(seed, vars,
|
|
26890
|
+
const base = remoteContent ?? resolveSeedContent(seed, vars, readFile11);
|
|
26871
26891
|
if (base == null) return { ok: true, content: null, managed: true };
|
|
26872
26892
|
const blockSeed = { ...seed, source: seed.managedBlock.source, managedBlock: void 0 };
|
|
26873
|
-
const desired = resolveSeedContent(blockSeed, vars,
|
|
26893
|
+
const desired = resolveSeedContent(blockSeed, vars, readFile11);
|
|
26874
26894
|
if (desired == null) return { ok: true, content: null, managed: true };
|
|
26875
26895
|
const result = upsertManagedSeedBlock(base, desired, seed.managedBlock.begin, seed.managedBlock.end);
|
|
26876
26896
|
return result.ok ? { ok: true, content: result.content, managed: true } : { ok: false, reason: result.reason, managed: true };
|
|
@@ -28385,10 +28405,10 @@ var rollout_plan_default = {
|
|
|
28385
28405
|
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)."
|
|
28386
28406
|
},
|
|
28387
28407
|
baseline: {
|
|
28388
|
-
version: "4.6.
|
|
28389
|
-
tag: "v4.6.
|
|
28390
|
-
commit: "
|
|
28391
|
-
npm: "@mutmutco/cli@4.6.
|
|
28408
|
+
version: "4.6.38",
|
|
28409
|
+
tag: "v4.6.38",
|
|
28410
|
+
commit: "975581f0f6ce",
|
|
28411
|
+
npm: "@mutmutco/cli@4.6.38"
|
|
28392
28412
|
},
|
|
28393
28413
|
exitCriterion: "fleet-n-of-n",
|
|
28394
28414
|
hubOnlyShortcut: "forbidden",
|
|
@@ -28405,14 +28425,14 @@ var rollout_plan_default = {
|
|
|
28405
28425
|
repo: "mutmutco/mmi-hub",
|
|
28406
28426
|
role: "canary",
|
|
28407
28427
|
schedule: "train",
|
|
28408
|
-
v3Target: "v4.6.
|
|
28428
|
+
v3Target: "v4.6.38"
|
|
28409
28429
|
}
|
|
28410
28430
|
],
|
|
28411
28431
|
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.",
|
|
28412
28432
|
rollback: {
|
|
28413
28433
|
independent: true,
|
|
28414
|
-
mechanism: "npm dist-tag latest -> 4.6.
|
|
28415
|
-
v3Target: "v4.6.
|
|
28434
|
+
mechanism: "npm dist-tag latest -> 4.6.38 and redeploy the Hub Lambda from tag v4.6.38 (975581f0f6ce); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
28435
|
+
v3Target: "v4.6.38 (@mutmutco/cli@4.6.38, tag commit 975581f0f6ce \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
28416
28436
|
}
|
|
28417
28437
|
},
|
|
28418
28438
|
{
|
|
@@ -43435,8 +43455,8 @@ function annotateChangeMeaning(changed, policy, read) {
|
|
|
43435
43455
|
function isMeaningfulRow(file) {
|
|
43436
43456
|
return file.meaningful !== false;
|
|
43437
43457
|
}
|
|
43438
|
-
function loadPolicy(root,
|
|
43439
|
-
const raw =
|
|
43458
|
+
function loadPolicy(root, readFile11 = readFileOrNull2) {
|
|
43459
|
+
const raw = readFile11((0, import_node_path30.join)(root, POLICY_FILE));
|
|
43440
43460
|
if (raw == null) return { mandatory: [], declared: false };
|
|
43441
43461
|
return parsePolicy(raw, POLICY_FILE);
|
|
43442
43462
|
}
|
|
@@ -44173,12 +44193,16 @@ async function submitQueueAdmission(pr, repo, head, read, write, options, resume
|
|
|
44173
44193
|
receipt = { ...receipt, state: "pending", uuid: details.uuid };
|
|
44174
44194
|
}
|
|
44175
44195
|
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
44176
|
-
|
|
44177
|
-
|
|
44196
|
+
const confirmDelays = [0, 1e3, 2e3, 4e3, 8e3, 15e3, 15e3, 15e3];
|
|
44197
|
+
let readFailure;
|
|
44198
|
+
for (const delay of confirmDelays) {
|
|
44199
|
+
if (delay) await sleep2(delay);
|
|
44200
|
+
readFailure = void 0;
|
|
44178
44201
|
try {
|
|
44179
44202
|
response = admissionResponse(await read([`repos/${repo}/pulls/${pr}/merge-async/${receipt.uuid}`, "--include", ...ASYNC_HEADERS]));
|
|
44180
44203
|
} catch {
|
|
44181
|
-
|
|
44204
|
+
readFailure = "the saved operation cannot be read (including expired results)";
|
|
44205
|
+
continue;
|
|
44182
44206
|
}
|
|
44183
44207
|
if (response.code !== 200) return reconcile(`the saved operation returned HTTP ${response.code}`);
|
|
44184
44208
|
const { status, details } = response.body;
|
|
@@ -44220,6 +44244,7 @@ async function submitQueueAdmission(pr, repo, head, read, write, options, resume
|
|
|
44220
44244
|
if (receipt.head !== head) return reconcile("the previous head completed admission; inspect current head and membership before continuing");
|
|
44221
44245
|
return reconcile(`operation ${receipt.uuid} reported ${status}, but matching-head membership or merged state is unconfirmed`, status === "enqueued");
|
|
44222
44246
|
}
|
|
44247
|
+
if (readFailure) return reconcile(readFailure);
|
|
44223
44248
|
return markReenqueued({
|
|
44224
44249
|
mergeStatus: "queue-admission-pending",
|
|
44225
44250
|
retryable: false,
|
|
@@ -44442,8 +44467,8 @@ async function abandonQueueAdmission(pr, repo, options = {}) {
|
|
|
44442
44467
|
}
|
|
44443
44468
|
const receipt = journal.receipt;
|
|
44444
44469
|
if (!receipt) return refuse("no queue-admission receipt exists for this PR; nothing to abandon");
|
|
44445
|
-
if (!["pending", "enqueued"].includes(receipt.state)) {
|
|
44446
|
-
return refuse(`the saved admission is '${receipt.state}', not a stuck non-terminal 'pending'/'enqueued' receipt; nothing to abandon`);
|
|
44470
|
+
if (!["pending", "enqueued"].includes(receipt.state) && !(receipt.state === "dispatching" && !receipt.uuid)) {
|
|
44471
|
+
return refuse(`the saved admission is '${receipt.state}'${receipt.uuid ? ` with operation ${receipt.uuid}` : ""}, not a stuck non-terminal 'pending'/'enqueued' receipt; nothing to abandon`);
|
|
44447
44472
|
}
|
|
44448
44473
|
let snapshot;
|
|
44449
44474
|
try {
|
|
@@ -45777,20 +45802,20 @@ function buildPrLifecycle(state, manifest, joins) {
|
|
|
45777
45802
|
if (member.pr !== state.number) continue;
|
|
45778
45803
|
if (!member.admission_event_id) continue;
|
|
45779
45804
|
const ep = episodes.find((x) => x.episode_id === member.admission_event_id);
|
|
45780
|
-
const
|
|
45781
|
-
if (!
|
|
45805
|
+
const join55 = joins.find((j) => j.run_id === run.run_id && j.run_attempt === run.run_attempt && j.pr === member.pr && j.admission_event_id === member.admission_event_id);
|
|
45806
|
+
if (!join55) continue;
|
|
45782
45807
|
if (!ep) {
|
|
45783
45808
|
if (state.status === "complete") {
|
|
45784
|
-
|
|
45785
|
-
|
|
45809
|
+
join55.status = "unresolved";
|
|
45810
|
+
join55.reason = "admission-event-not-in-history";
|
|
45786
45811
|
} else {
|
|
45787
|
-
|
|
45788
|
-
|
|
45812
|
+
join55.status = "unresolved";
|
|
45813
|
+
join55.reason = "history-incomplete";
|
|
45789
45814
|
}
|
|
45790
45815
|
continue;
|
|
45791
45816
|
}
|
|
45792
|
-
|
|
45793
|
-
delete
|
|
45817
|
+
join55.status = "joined";
|
|
45818
|
+
delete join55.reason;
|
|
45794
45819
|
ep.runs.push({ run_id: run.run_id, run_attempt: run.run_attempt, head_sha: run.head_sha });
|
|
45795
45820
|
}
|
|
45796
45821
|
}
|
|
@@ -46434,7 +46459,7 @@ function refuseNotClaimable(e, json) {
|
|
|
46434
46459
|
process.exitCode = 1;
|
|
46435
46460
|
return true;
|
|
46436
46461
|
}
|
|
46437
|
-
var BOARD_READ_HELP = "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--direct skips the Hub snapshot and uses the existing direct GitHub GraphQL read immediately.\n--allow-partial applies to the paginated path and detail reads.\n\nWhen using Windows PowerShell 5.1, avoid shell redirects: PowerShell 5.1's `> file.json` is Out-File,\nwhich writes UTF-16LE with a BOM, and Node reading it as 'utf8' then fails JSON.parse at position 1\n(#5802). Use --out instead \u2014 the CLI writes the file itself as UTF-8:\n mmi-cli oracle board read --json --out .jerv/tmp/board.json\n\nThe --json report is an OBJECT \u2014 never an array:\n {project, viewer, repo, primary, secondary, items, warnings, partial[, source, builtAt, eventCursor, ...]}\n`primary` is this repo and `secondary` the other repos in scope; each is a bucket object\n {userOwned, claimable, taken, unownedInFlight}\nand each bucket is an array of items. Filter the BUCKET for ownership context:\n mmi-cli oracle board read --json --out .jerv/tmp/board.json\n node -e \"const b=require('./.jerv/tmp/board.json');console.log(b.primary.claimable.length)\"\n\n`items` (#9974) is that same flat row array, already on the full report: {ref,number,title,status,assignees,labels,bucket,claim,openPullRequests}\n node -e \"const b=require('./.jerv/tmp/board.json');for(const i of b.items)console.log(i.number,i.status,i.title)\"\nIt is built from the same flattener as --flat, so `const rows = j.items || j` is now safe on either form.\n\nAdd --flat with --json for that array as the WHOLE output (no project/viewer/warnings envelope):\n(#9595: claim is the parsed mmi-claim evidence object, null when unclaimed; openPullRequests lists the OPEN\n PRs whose closing-issue body links name the item. On the FULL JSON object that list lives at\n `report.pullRequests` \u2014 there is no top-level `openPullRequests`, and #9596 hand-written projections\n that assume one crash on `.filter`.)\n\nNever sniff the envelope \u2014 read `report.items` (or `primary`/`secondary`) by name. `j.items ?? j.rows ?? j`,\n`Object.values(j)` and any generic \"find the list\" wrapper are still guesses: before `items` existed they\nthrew `items is not iterable` before any row was read (#9956), and `j.rows` has never been a key. For a\nnumber|status|claim|title projection across non-Done rows, read --json --flat --out .jerv/tmp/board.json\nand filter that array locally (identical rows to `report.items`):\n mmi-cli oracle board read --json --flat --out .jerv/tmp/board.json\n\nA cached snapshot read renders `source: cache \xB7 built <timestamp> \xB7 fresh|stale` (#9584) and carries\n`builtAt` under --json, so a same-turn re-read of the same builtAt buys nothing new.\n\nUnder --json, parse stdout ONLY and check the exit code first. A failure still prints a\nhuman-readable reason on stderr, so never pipe 2>&1 into a JSON parser (#9151) \u2014 the merged\ntext is not JSON and hides the real board failure.\n\nA zero-byte or empty .jerv/tmp/*.json means the read FAILED \u2014 `Unexpected end of JSON input` is\nthe symptom of shell-redirected empty stdout, not malformed JSON \u2014 re-run with --out (which writes\nthe file only on success) and check the exit code before parsing.\n\nChanging an existing item's Status is a move, never a hand-written `gh api graphql`\n`updateProjectV2ItemFieldValue` mutation with copied `PVT_`/`PVTI_` node ids and a manual Status\nfield-id lookup:\n mmi-cli oracle board move <status> <ref>\n";
|
|
46462
|
+
var BOARD_READ_HELP = "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--direct skips the Hub snapshot and uses the existing direct GitHub GraphQL read immediately.\n--allow-partial applies to the paginated path and detail reads.\n\nWhen using Windows PowerShell 5.1, avoid shell redirects: PowerShell 5.1's `> file.json` is Out-File,\nwhich writes UTF-16LE with a BOM, and Node reading it as 'utf8' then fails JSON.parse at position 1\n(#5802). Use --out instead \u2014 the CLI writes the file itself as UTF-8:\n mmi-cli oracle board read --json --out .jerv/tmp/board.json\n\nThe --json report is an OBJECT \u2014 never an array:\n {project, viewer, repo, primary, secondary, items, warnings, partial[, source, builtAt, eventCursor, ...]}\n`primary` is this repo and `secondary` the other repos in scope; each is a bucket object\n {userOwned, claimable, taken, unownedInFlight}\nand each bucket is an array of items. Filter the BUCKET for ownership context:\n mmi-cli oracle board read --json --out .jerv/tmp/board.json\n node -e \"const b=require('./.jerv/tmp/board.json');console.log(b.primary.claimable.length)\"\n\n`items` (#9974) is that same flat row array, already on the full report: {ref,number,title,status,assignees,labels,bucket,claim,openPullRequests}\n node -e \"const b=require('./.jerv/tmp/board.json');for(const i of b.items)console.log(i.number,i.status,i.title)\"\nIt is built from the same flattener as --flat, so `const rows = j.items || j` is now safe on either form.\n\nAdd --flat with --json for that array as the WHOLE output (no project/viewer/warnings envelope):\n(#9595: claim is the parsed mmi-claim evidence object, null when unclaimed; openPullRequests lists the OPEN\n PRs whose closing-issue body links name the item. On the FULL JSON object that list lives at\n `report.pullRequests` \u2014 there is no top-level `openPullRequests`, and #9596 hand-written projections\n that assume one crash on `.filter`.)\n\nNever sniff the envelope \u2014 read `report.items` (or `primary`/`secondary`) by name. `j.items ?? j.rows ?? j`,\n`Object.values(j)` and any generic \"find the list\" wrapper are still guesses: before `items` existed they\nthrew `items is not iterable` before any row was read (#9956), and `j.rows` has never been a key. For a\nnumber|status|claim|title projection across non-Done rows, read --json --flat --out .jerv/tmp/board.json\nand filter that array locally (identical rows to `report.items`):\n mmi-cli oracle board read --json --flat --out .jerv/tmp/board.json\n\nA cached snapshot read renders `source: cache \xB7 built <timestamp> \xB7 fresh|stale` (#9584) and carries\n`builtAt` under --json, so a same-turn re-read of the same builtAt buys nothing new. That age grades the\nCOPY, not its content: the line ends `\xB7 items closed/created after the build may still differ \u2014 confirm\nwith --direct` (#10028), so re-read with --direct before treating a row as still open or still present.\n\nUnder --json, parse stdout ONLY and check the exit code first. A failure still prints a\nhuman-readable reason on stderr, so never pipe 2>&1 into a JSON parser (#9151) \u2014 the merged\ntext is not JSON and hides the real board failure.\n\nA zero-byte or empty .jerv/tmp/*.json means the read FAILED \u2014 `Unexpected end of JSON input` is\nthe symptom of shell-redirected empty stdout, not malformed JSON \u2014 re-run with --out (which writes\nthe file only on success) and check the exit code before parsing.\n\nChanging an existing item's Status is a move, never a hand-written `gh api graphql`\n`updateProjectV2ItemFieldValue` mutation with copied `PVT_`/`PVTI_` node ids and a manual Status\nfield-id lookup:\n mmi-cli oracle board move <status> <ref>\n";
|
|
46438
46463
|
function flattenBoardReport(report) {
|
|
46439
46464
|
const closingByRef = /* @__PURE__ */ new Map();
|
|
46440
46465
|
for (const pull of report.pullRequests ?? []) {
|
|
@@ -51136,6 +51161,13 @@ var declaredProjectAdmins = (meta) => {
|
|
|
51136
51161
|
return Array.isArray(list) ? list.filter((entry) => typeof entry === "string") : [];
|
|
51137
51162
|
};
|
|
51138
51163
|
var execGitForSeedSource = async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
51164
|
+
function reportSuperseded(prefix, source, json) {
|
|
51165
|
+
const head = source.head.slice(0, 8);
|
|
51166
|
+
const remote = source.remoteSha.slice(0, 8);
|
|
51167
|
+
const summary = `${prefix}: superseded \u2014 this checkout (${head}) is behind origin/development (${remote}); a newer run owns this tick, nothing seeded`;
|
|
51168
|
+
if (json) console.log(JSON.stringify({ status: "superseded", checkoutSha: source.head, remoteSha: source.remoteSha, summary }, null, 2));
|
|
51169
|
+
else console.log(summary);
|
|
51170
|
+
}
|
|
51139
51171
|
async function gateRenderDiff(current, rendered) {
|
|
51140
51172
|
const dir = (0, import_node_fs40.mkdtempSync)((0, import_node_path37.join)((0, import_node_os21.tmpdir)(), "mmi-gate-diff-"));
|
|
51141
51173
|
const before = (0, import_node_path37.join)(dir, "committed-gate.yml");
|
|
@@ -51223,7 +51255,10 @@ async function propagationScope(classes, what) {
|
|
|
51223
51255
|
}
|
|
51224
51256
|
async function propagatePins(apply, json) {
|
|
51225
51257
|
const source = await resolveHubSeedSource(execGitForSeedSource);
|
|
51226
|
-
if (apply && !source.ok)
|
|
51258
|
+
if (apply && !source.ok) {
|
|
51259
|
+
if (source.superseded) return reportSuperseded("bootstrap propagate --pins --apply", source, json);
|
|
51260
|
+
return fail(`bootstrap propagate --pins --apply: ${source.reason}`);
|
|
51261
|
+
}
|
|
51227
51262
|
const { classOf, inClassRepos, canarySlug, sourcePilot } = await propagationScope(null, "hub action pins");
|
|
51228
51263
|
const gh2 = async (args) => execFileP("gh", args, { timeout: 2e4 });
|
|
51229
51264
|
const repos = [];
|
|
@@ -51654,7 +51689,7 @@ function registerBootstrapCommands(program3) {
|
|
|
51654
51689
|
const slug = parsedRepo.slug;
|
|
51655
51690
|
const onlyTarget = o.fromConfig && o.only.trim() === "gate.yml" ? ".github/workflows/gate.yml" : o.only.trim();
|
|
51656
51691
|
const gh2 = async (args) => execFileP("gh", args, { timeout: 2e4 });
|
|
51657
|
-
const
|
|
51692
|
+
const readFile11 = (p) => (0, import_node_fs40.existsSync)(p) ? (0, import_node_fs40.readFileSync)(p, "utf8") : null;
|
|
51658
51693
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
51659
51694
|
const putSeed = async (target, content, ref, sha) => {
|
|
51660
51695
|
const tmp = (0, import_node_path37.join)((0, import_node_os21.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
@@ -51826,7 +51861,7 @@ function registerBootstrapCommands(program3) {
|
|
|
51826
51861
|
registryMeta?.requiredCheckBranches,
|
|
51827
51862
|
registryMeta?.requiredChecks
|
|
51828
51863
|
);
|
|
51829
|
-
const rendered = resolveSeedContent(gateSeed, gateVars,
|
|
51864
|
+
const rendered = resolveSeedContent(gateSeed, gateVars, readFile11);
|
|
51830
51865
|
if (rendered === null || missingPlaceholders(rendered).length) return failGraceful("bootstrap apply: registry gate template is unreadable or has unfilled placeholders");
|
|
51831
51866
|
const base = (await readTargetedSeedFiles(repo, scopedPlan, onlyTarget, scopedPlan.baseSha, controlClient)).base?.content ?? null;
|
|
51832
51867
|
configGateAction = planGateFromConfig(base, rendered);
|
|
@@ -51930,7 +51965,7 @@ function registerBootstrapCommands(program3) {
|
|
|
51930
51965
|
...vars,
|
|
51931
51966
|
...registryMeta?.requiredChecks?.length ? { GATE_RULESET_CONTEXTS_JSON: rulesetVars.GATE_RULESET_CONTEXTS_JSON } : {},
|
|
51932
51967
|
...registryMeta?.requiredCheckBranches?.length ? { GATE_RULESET_BRANCH_REFS_JSON: rulesetVars.GATE_RULESET_BRANCH_REFS_JSON } : {}
|
|
51933
|
-
} : vars,
|
|
51968
|
+
} : vars, readFile11, preservedContent);
|
|
51934
51969
|
if (!writeContent.ok) {
|
|
51935
51970
|
return fail(`bootstrap apply: ${resolved.target}: ${writeContent.reason} \u2014 refusing to overwrite repo-owned content`);
|
|
51936
51971
|
}
|
|
@@ -52076,7 +52111,7 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
|
|
|
52076
52111
|
}
|
|
52077
52112
|
const rulesetSeed = repo.toLowerCase() === "mutmutco/mmi-hub" ? void 0 : manifest.seeds.find((s) => s.target === ".github/rulesets/mmi-product-required-checks.json");
|
|
52078
52113
|
if (rulesetSeed) {
|
|
52079
|
-
const rulesetContent = resolveSeedContent({ ...rulesetSeed, target: rulesetSeed.target.replace("{{REPO_SLUG}}", slug) }, vars,
|
|
52114
|
+
const rulesetContent = resolveSeedContent({ ...rulesetSeed, target: rulesetSeed.target.replace("{{REPO_SLUG}}", slug) }, vars, readFile11);
|
|
52080
52115
|
if (rulesetContent) {
|
|
52081
52116
|
try {
|
|
52082
52117
|
const client = controlClient;
|
|
@@ -52348,7 +52383,10 @@ LIVE apply to ${repo}:
|
|
|
52348
52383
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
52349
52384
|
if (!(0, import_node_fs40.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`);
|
|
52350
52385
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
52351
|
-
if (!seedSource.ok)
|
|
52386
|
+
if (!seedSource.ok) {
|
|
52387
|
+
if (seedSource.superseded) return reportSuperseded("bootstrap propagate", seedSource, o.json);
|
|
52388
|
+
return fail(`bootstrap propagate: ${seedSource.reason}`);
|
|
52389
|
+
}
|
|
52352
52390
|
const manifest = loadBootstrapSeeds((0, import_node_fs40.readFileSync)(manifestPath, "utf8"));
|
|
52353
52391
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
52354
52392
|
if (!o.target) {
|
|
@@ -52868,32 +52906,183 @@ function registerDeployCommands(program3) {
|
|
|
52868
52906
|
}
|
|
52869
52907
|
|
|
52870
52908
|
// src/intercom-guest.ts
|
|
52909
|
+
var import_node_crypto19 = require("node:crypto");
|
|
52910
|
+
var import_promises5 = require("node:fs/promises");
|
|
52911
|
+
var import_node_os22 = require("node:os");
|
|
52912
|
+
var import_node_path38 = require("node:path");
|
|
52913
|
+
init_cli_shared();
|
|
52871
52914
|
init_github_client();
|
|
52872
|
-
var CANONICAL_COMMAND = "mmi-cli
|
|
52915
|
+
var CANONICAL_COMMAND = "mmi-cli intercom guest";
|
|
52873
52916
|
var INTERCOM_GUEST_VERBS = ["register", "read", "send", "join", "leave"];
|
|
52917
|
+
var DELIVERIES_LIMIT_MAX = 100;
|
|
52918
|
+
var DELIVERIES_LIMIT_DEFAULT = 20;
|
|
52919
|
+
function deliveriesLimit(limit) {
|
|
52920
|
+
if (limit === void 0 || !Number.isFinite(limit) || limit < 1) return DELIVERIES_LIMIT_DEFAULT;
|
|
52921
|
+
return Math.min(Math.floor(limit), DELIVERIES_LIMIT_MAX);
|
|
52922
|
+
}
|
|
52874
52923
|
var INTERCOM_GUEST_CONTRACT = {
|
|
52875
|
-
source: "mutmutco/Jerv-Hub#
|
|
52924
|
+
source: "mutmutco/Jerv-Hub#2727 published guest wire contract (gateway/v1: contract/operations.js, contract/authorization.json, src/http/handler.js)",
|
|
52925
|
+
contractVersion: "gateway/v1",
|
|
52926
|
+
baseUrl: "https://jerv-central.tail4ce549.ts.net",
|
|
52876
52927
|
baseUrlEnv: "MMI_INTERCOM_GATEWAY_URL",
|
|
52877
|
-
|
|
52928
|
+
discovery: {
|
|
52929
|
+
operation: "intercom.directory",
|
|
52930
|
+
method: "POST",
|
|
52931
|
+
path: "/v1/intercom/directory",
|
|
52932
|
+
mutation: false,
|
|
52933
|
+
credential: "required",
|
|
52934
|
+
// Idle endpoints are included on purpose: the Coordinator is reachable while it is idle, and the
|
|
52935
|
+
// contract says to match its display name and take the id from the row, never to remember one.
|
|
52936
|
+
body: () => ({ limit: 200, includeIdle: true })
|
|
52937
|
+
},
|
|
52938
|
+
operations: {
|
|
52939
|
+
// #9993 — the guest's first contact. `displayName` is the gh login; the Gateway appends 8 hex of the
|
|
52940
|
+
// endpoint id only when the name collides. HostKind/project are optional and omitted (the Gateway
|
|
52941
|
+
// stores `remote`). With a stored credential this same call rotates and resumes the endpoint.
|
|
52942
|
+
register: {
|
|
52943
|
+
operation: "intercom.register",
|
|
52944
|
+
method: "POST",
|
|
52945
|
+
path: "/v1/intercom/register",
|
|
52946
|
+
mutation: true,
|
|
52947
|
+
credential: "optional",
|
|
52948
|
+
needsLogin: true,
|
|
52949
|
+
body: (_request, context) => ({ displayName: context.login ?? "" })
|
|
52950
|
+
},
|
|
52951
|
+
// #9993 — `read` is the inbox: pending deliveries. Accepting them (intercom.accept) or acking them
|
|
52952
|
+
// (intercom.ack) is a separate, explicit call the contract documents under `cliVerbs.read.then`.
|
|
52953
|
+
read: {
|
|
52954
|
+
operation: "intercom.deliveries",
|
|
52955
|
+
method: "POST",
|
|
52956
|
+
path: "/v1/intercom/deliveries",
|
|
52957
|
+
mutation: false,
|
|
52958
|
+
credential: "required",
|
|
52959
|
+
body: (request) => ({ state: "pending", limit: deliveriesLimit(request.limit) })
|
|
52960
|
+
},
|
|
52961
|
+
// #9993 — the wire field is `text`. A body field named `message` is `unknown_field` at the Gateway.
|
|
52962
|
+
send: {
|
|
52963
|
+
operation: "intercom.send",
|
|
52964
|
+
method: "POST",
|
|
52965
|
+
path: "/v1/intercom/send",
|
|
52966
|
+
mutation: true,
|
|
52967
|
+
credential: "required",
|
|
52968
|
+
body: (request) => ({ to: request.to, text: request.message })
|
|
52969
|
+
},
|
|
52970
|
+
// #9993 — no Gateway operation admits a guest to a suite (Jerv-Hub#2524): a join is an ordinary
|
|
52971
|
+
// `send` to the Coordinator carrying the suite message convention. The suite name travels inside the
|
|
52972
|
+
// text because the convention carries no suite field.
|
|
52973
|
+
join: {
|
|
52974
|
+
operation: "intercom.send",
|
|
52975
|
+
method: "POST",
|
|
52976
|
+
path: "/v1/intercom/send",
|
|
52977
|
+
mutation: true,
|
|
52978
|
+
credential: "required",
|
|
52979
|
+
needsCoordinator: true,
|
|
52980
|
+
body: (request, context) => ({
|
|
52981
|
+
to: context.coordinator,
|
|
52982
|
+
text: `Suite join: ${context.endpointId ?? ""}, ${context.displayName ?? context.login ?? ""} guest, repo ${context.repo}, host ${context.host}, suite ${request.suite ?? ""}`
|
|
52983
|
+
})
|
|
52984
|
+
},
|
|
52985
|
+
// #9993 — leaving is the same shape, and the Coordinator marks the seat left.
|
|
52986
|
+
leave: {
|
|
52987
|
+
operation: "intercom.send",
|
|
52988
|
+
method: "POST",
|
|
52989
|
+
path: "/v1/intercom/send",
|
|
52990
|
+
mutation: true,
|
|
52991
|
+
credential: "required",
|
|
52992
|
+
needsCoordinator: true,
|
|
52993
|
+
body: (_request, context) => ({
|
|
52994
|
+
to: context.coordinator,
|
|
52995
|
+
text: `Suite leave: ${context.endpointId ?? ""}`
|
|
52996
|
+
})
|
|
52997
|
+
}
|
|
52998
|
+
}
|
|
52878
52999
|
};
|
|
52879
53000
|
var INTERCOM_GUEST_CONTRACT_MISSING = "intercom-guest-contract-missing";
|
|
52880
53001
|
var INTERCOM_GUEST_BASE_URL_MISSING = "intercom-guest-base-url-missing";
|
|
52881
53002
|
var INTERCOM_GUEST_NO_IDENTITY = "intercom-guest-no-identity";
|
|
53003
|
+
var INTERCOM_GUEST_INPUT_MISSING = "intercom-guest-input-missing";
|
|
53004
|
+
var INTERCOM_GUEST_NO_CREDENTIAL = "intercom-guest-no-credential";
|
|
53005
|
+
var INTERCOM_GUEST_COORDINATOR_MISSING = "intercom-guest-coordinator-missing";
|
|
53006
|
+
function intercomGuestStorePath(env = process.env) {
|
|
53007
|
+
const home = env.MMI_INTERCOM_HOME?.trim();
|
|
53008
|
+
return (0, import_node_path38.join)(home && home.length > 0 ? home : (0, import_node_path38.join)((0, import_node_os22.homedir)(), ".mmi", "intercom"), "guest.json");
|
|
53009
|
+
}
|
|
52882
53010
|
function scrubToken(text, token) {
|
|
52883
53011
|
if (!token) return text;
|
|
52884
53012
|
return text.split(token).join("***");
|
|
52885
53013
|
}
|
|
53014
|
+
function scrubSecrets(text, secrets) {
|
|
53015
|
+
return secrets.reduce((acc, secret) => scrubToken(acc, secret), text);
|
|
53016
|
+
}
|
|
52886
53017
|
function missingContractRefusal(verb) {
|
|
52887
53018
|
return [
|
|
52888
|
-
`${CANONICAL_COMMAND} ${verb}: not sent \u2014
|
|
52889
|
-
|
|
52890
|
-
"
|
|
52891
|
-
"
|
|
52892
|
-
|
|
52893
|
-
"`INTERCOM_GUEST_CONTRACT.operations` in cli/src/intercom-guest.ts
|
|
53019
|
+
`${CANONICAL_COMMAND} ${verb}: not sent \u2014 this CLI build carries no Jerv Gateway operation for the '${verb}' verb (${INTERCOM_GUEST_CONTRACT.source}).`,
|
|
53020
|
+
"The published guest contract publishes the method, path and field shape for every verb this client knows",
|
|
53021
|
+
`(${INTERCOM_GUEST_VERBS.join(", ")}), so an empty entry means the contract moved and this build did not follow it \u2014`,
|
|
53022
|
+
"this client refuses instead of guessing an endpoint: a guessed call would come back 404/403 and read as a permission",
|
|
53023
|
+
"or allowlist answer when the real answer is that the operation is unknown here.",
|
|
53024
|
+
"To fix it: check the published contract against `INTERCOM_GUEST_CONTRACT.operations` in cli/src/intercom-guest.ts",
|
|
53025
|
+
"\u2014 that object is the one place the wire shape lives.",
|
|
52894
53026
|
"Nothing was read and nothing was sent: no token was touched and no request was made."
|
|
52895
53027
|
].join(" ");
|
|
52896
53028
|
}
|
|
53029
|
+
function missingInput(request) {
|
|
53030
|
+
if (request.verb === "send") {
|
|
53031
|
+
if (!request.to?.trim()) return "`--to <address>` is required";
|
|
53032
|
+
if (!request.message?.trim()) return "`--message <text>` is required";
|
|
53033
|
+
}
|
|
53034
|
+
if (request.verb === "join" && !request.suite?.trim()) return "`--suite <name>` is required";
|
|
53035
|
+
return void 0;
|
|
53036
|
+
}
|
|
53037
|
+
function coordinatorEndpointId(directoryBody) {
|
|
53038
|
+
let parsed;
|
|
53039
|
+
try {
|
|
53040
|
+
parsed = JSON.parse(directoryBody);
|
|
53041
|
+
} catch {
|
|
53042
|
+
return void 0;
|
|
53043
|
+
}
|
|
53044
|
+
const data = parsed?.data ?? parsed;
|
|
53045
|
+
const items = data?.items;
|
|
53046
|
+
if (!Array.isArray(items)) return void 0;
|
|
53047
|
+
const candidates = items.filter((item) => Boolean(item) && typeof item === "object");
|
|
53048
|
+
const named = candidates.filter((item) => typeof item.displayName === "string" && /^Coordinator(\s|$)/.test(item.displayName) && item.self !== true);
|
|
53049
|
+
const exact = named.find((item) => item.displayName === "Coordinator") ?? named[0];
|
|
53050
|
+
return typeof exact?.endpointId === "string" && exact.endpointId.length > 0 ? exact.endpointId : void 0;
|
|
53051
|
+
}
|
|
53052
|
+
async function readStore(path2) {
|
|
53053
|
+
try {
|
|
53054
|
+
const parsed = JSON.parse(await (0, import_promises5.readFile)(path2, "utf8"));
|
|
53055
|
+
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
53056
|
+
} catch {
|
|
53057
|
+
return void 0;
|
|
53058
|
+
}
|
|
53059
|
+
}
|
|
53060
|
+
async function writeStore(path2, entry) {
|
|
53061
|
+
await (0, import_promises5.mkdir)((0, import_node_path38.dirname)(path2), { recursive: true, mode: 448 });
|
|
53062
|
+
await (0, import_promises5.writeFile)(path2, `${JSON.stringify(entry, null, 2)}
|
|
53063
|
+
`, { encoding: "utf8", mode: 384 });
|
|
53064
|
+
await (0, import_promises5.chmod)(path2, 384).catch(() => {
|
|
53065
|
+
});
|
|
53066
|
+
}
|
|
53067
|
+
function registrationEntry(gateway, body, previous) {
|
|
53068
|
+
let parsed;
|
|
53069
|
+
try {
|
|
53070
|
+
parsed = JSON.parse(body);
|
|
53071
|
+
} catch {
|
|
53072
|
+
return void 0;
|
|
53073
|
+
}
|
|
53074
|
+
const data = parsed?.data ?? parsed;
|
|
53075
|
+
const credential = typeof data?.credential === "string" ? data.credential : previous?.credential;
|
|
53076
|
+
if (!credential) return void 0;
|
|
53077
|
+
return {
|
|
53078
|
+
gateway,
|
|
53079
|
+
endpointId: typeof data?.endpointId === "string" ? data.endpointId : previous?.endpointId,
|
|
53080
|
+
deviceId: typeof data?.deviceId === "string" ? data.deviceId : previous?.deviceId,
|
|
53081
|
+
credential,
|
|
53082
|
+
displayName: typeof data?.displayName === "string" ? data.displayName : previous?.displayName,
|
|
53083
|
+
epoch: typeof data?.epoch === "number" ? data.epoch : previous?.epoch
|
|
53084
|
+
};
|
|
53085
|
+
}
|
|
52897
53086
|
async function runIntercomGuest(request, deps = {}) {
|
|
52898
53087
|
const verb = request.verb;
|
|
52899
53088
|
const contract = deps.contract ?? INTERCOM_GUEST_CONTRACT;
|
|
@@ -52901,61 +53090,156 @@ async function runIntercomGuest(request, deps = {}) {
|
|
|
52901
53090
|
if (!operation) {
|
|
52902
53091
|
return { ok: false, verb, code: INTERCOM_GUEST_CONTRACT_MISSING, message: missingContractRefusal(verb), tokenRead: false };
|
|
52903
53092
|
}
|
|
53093
|
+
const inputProblem = missingInput(request);
|
|
53094
|
+
if (inputProblem) {
|
|
53095
|
+
return {
|
|
53096
|
+
ok: false,
|
|
53097
|
+
verb,
|
|
53098
|
+
code: INTERCOM_GUEST_INPUT_MISSING,
|
|
53099
|
+
message: `${CANONICAL_COMMAND} ${verb}: not sent \u2014 ${inputProblem}. Nothing was read and no request was made.`,
|
|
53100
|
+
tokenRead: false
|
|
53101
|
+
};
|
|
53102
|
+
}
|
|
52904
53103
|
const env = deps.env ?? process.env;
|
|
52905
|
-
const baseUrl = env[contract.baseUrlEnv]?.trim();
|
|
53104
|
+
const baseUrl = (env[contract.baseUrlEnv]?.trim() || contract.baseUrl).replace(/\/+$/, "");
|
|
52906
53105
|
if (!baseUrl) {
|
|
52907
53106
|
return {
|
|
52908
53107
|
ok: false,
|
|
52909
53108
|
verb,
|
|
52910
53109
|
code: INTERCOM_GUEST_BASE_URL_MISSING,
|
|
52911
|
-
message: `${CANONICAL_COMMAND} ${verb}: not sent \u2014 ${contract.baseUrlEnv} is
|
|
53110
|
+
message: `${CANONICAL_COMMAND} ${verb}: not sent \u2014 ${contract.baseUrlEnv} is empty and the published contract names no base URL either (${contract.source}). Nothing was read and no request was made.`,
|
|
52912
53111
|
tokenRead: false
|
|
52913
53112
|
};
|
|
52914
53113
|
}
|
|
53114
|
+
const storePath = deps.storePath ?? intercomGuestStorePath(env);
|
|
53115
|
+
const store = await readStore(storePath);
|
|
52915
53116
|
const identity = await (deps.readToken ?? githubKeyringTokenRead)();
|
|
52916
53117
|
if (identity.state !== "ok") {
|
|
52917
53118
|
return {
|
|
52918
53119
|
ok: false,
|
|
52919
53120
|
verb,
|
|
52920
53121
|
code: INTERCOM_GUEST_NO_IDENTITY,
|
|
52921
|
-
message:
|
|
52922
|
-
`${CANONICAL_COMMAND} ${verb}: not sent \u2014 the guest identity is the caller's own \`gh\` login and it could not be read (${identity.detail}). Run \`gh auth login\`, then retry. No request was made
|
|
53122
|
+
message: scrubSecrets(
|
|
53123
|
+
`${CANONICAL_COMMAND} ${verb}: not sent \u2014 the guest identity is the caller's own \`gh\` login and it could not be read (${identity.detail}). Run \`gh auth login\`, then retry. No request was made.`,
|
|
53124
|
+
[]
|
|
52923
53125
|
),
|
|
52924
53126
|
tokenRead: false
|
|
52925
53127
|
};
|
|
52926
53128
|
}
|
|
52927
53129
|
const token = identity.token;
|
|
52928
|
-
|
|
52929
|
-
|
|
52930
|
-
|
|
52931
|
-
|
|
52932
|
-
|
|
52933
|
-
|
|
53130
|
+
let login;
|
|
53131
|
+
if (operation.needsLogin) {
|
|
53132
|
+
login = await (deps.readLogin ?? githubLogin)();
|
|
53133
|
+
if (!login) {
|
|
53134
|
+
return {
|
|
53135
|
+
ok: false,
|
|
53136
|
+
verb,
|
|
53137
|
+
code: INTERCOM_GUEST_NO_IDENTITY,
|
|
53138
|
+
message: scrubSecrets(
|
|
53139
|
+
`${CANONICAL_COMMAND} ${verb}: not sent \u2014 ${operation.operation} needs your GitHub login as \`displayName\` and \`gh api user\` returned none. Run \`gh auth login\`, then retry. No request was made.`,
|
|
53140
|
+
[token]
|
|
53141
|
+
),
|
|
53142
|
+
tokenRead: true
|
|
53143
|
+
};
|
|
53144
|
+
}
|
|
52934
53145
|
}
|
|
52935
|
-
|
|
52936
|
-
|
|
52937
|
-
|
|
52938
|
-
|
|
52939
|
-
|
|
52940
|
-
|
|
53146
|
+
const credential = store?.credential;
|
|
53147
|
+
if (operation.credential === "required" && !credential) {
|
|
53148
|
+
return {
|
|
53149
|
+
ok: false,
|
|
53150
|
+
verb,
|
|
53151
|
+
code: INTERCOM_GUEST_NO_CREDENTIAL,
|
|
53152
|
+
message: scrubSecrets(
|
|
53153
|
+
`${CANONICAL_COMMAND} ${verb}: not sent \u2014 no intercom credential is stored for this machine (${storePath}). ${operation.operation} requires the \`Jerv-Intercom-Credential\` the Gateway returns exactly once, so run \`${CANONICAL_COMMAND} register\` first. Nothing was read and no request was made.`,
|
|
53154
|
+
[token]
|
|
53155
|
+
),
|
|
53156
|
+
tokenRead: true
|
|
53157
|
+
};
|
|
52941
53158
|
}
|
|
52942
|
-
const
|
|
52943
|
-
|
|
53159
|
+
const fetchImpl = deps.fetchImpl ?? ((url, init) => fetch(url, init));
|
|
53160
|
+
const newKey = deps.newIdempotencyKey ?? (() => (0, import_node_crypto19.randomUUID)());
|
|
53161
|
+
const secrets = [token, credential].filter((secret) => Boolean(secret));
|
|
53162
|
+
const callOne = async (op, body2) => {
|
|
53163
|
+
const headers = {
|
|
53164
|
+
accept: "application/json",
|
|
53165
|
+
"content-type": "application/json",
|
|
53166
|
+
"jerv-contract-version": contract.contractVersion,
|
|
53167
|
+
"jerv-guest-github-token": token
|
|
53168
|
+
};
|
|
53169
|
+
if (credential && op.credential !== "never") headers["jerv-intercom-credential"] = credential;
|
|
53170
|
+
if (op.mutation) headers["idempotency-key"] = newKey();
|
|
53171
|
+
let response;
|
|
53172
|
+
try {
|
|
53173
|
+
response = await fetchImpl(new URL(op.path, baseUrl).toString(), { method: op.method, headers, body: JSON.stringify(body2) });
|
|
53174
|
+
} catch (error) {
|
|
53175
|
+
return { transport: error instanceof Error ? error.message : String(error) };
|
|
53176
|
+
}
|
|
53177
|
+
const text = await response.text().catch(() => "");
|
|
53178
|
+
return { status: response.status, ok: response.ok, text };
|
|
53179
|
+
};
|
|
53180
|
+
let context = {
|
|
53181
|
+
login,
|
|
53182
|
+
credential,
|
|
53183
|
+
endpointId: store?.endpointId,
|
|
53184
|
+
displayName: store?.displayName,
|
|
53185
|
+
repo: env.GITHUB_REPOSITORY?.trim() || "unknown",
|
|
53186
|
+
host: (0, import_node_os22.hostname)()
|
|
53187
|
+
};
|
|
53188
|
+
if (operation.needsCoordinator) {
|
|
53189
|
+
const directory = await callOne(contract.discovery, contract.discovery.body?.(request, context) ?? {});
|
|
53190
|
+
if ("transport" in directory) {
|
|
53191
|
+
return { ok: false, verb, code: "intercom-guest-transport-failed", message: scrubSecrets(`${CANONICAL_COMMAND} ${verb}: the Gateway call failed in transport (${directory.transport}).`, secrets), tokenRead: true };
|
|
53192
|
+
}
|
|
53193
|
+
if (!directory.ok) {
|
|
53194
|
+
return {
|
|
53195
|
+
ok: false,
|
|
53196
|
+
verb,
|
|
53197
|
+
code: `intercom-guest-http-${directory.status}`,
|
|
53198
|
+
message: scrubSecrets(`${CANONICAL_COMMAND} ${verb}: ${contract.discovery.operation} answered HTTP ${directory.status}${directory.text ? ` \u2014 ${directory.text.slice(0, 600)}` : ""}`, secrets),
|
|
53199
|
+
tokenRead: true
|
|
53200
|
+
};
|
|
53201
|
+
}
|
|
53202
|
+
const coordinator = coordinatorEndpointId(directory.text);
|
|
53203
|
+
if (!coordinator) {
|
|
53204
|
+
return {
|
|
53205
|
+
ok: false,
|
|
53206
|
+
verb,
|
|
53207
|
+
code: INTERCOM_GUEST_COORDINATOR_MISSING,
|
|
53208
|
+
message: scrubSecrets(
|
|
53209
|
+
`${CANONICAL_COMMAND} ${verb}: ${contract.discovery.operation} listed no live endpoint named \`Coordinator\`, so there is nobody to send to and nothing was sent. The Coordinator's endpoint id comes from that directory, never from this client.`,
|
|
53210
|
+
secrets
|
|
53211
|
+
),
|
|
53212
|
+
tokenRead: true
|
|
53213
|
+
};
|
|
53214
|
+
}
|
|
53215
|
+
context = { ...context, coordinator };
|
|
53216
|
+
}
|
|
53217
|
+
const body = operation.body ? operation.body(request, context) : {};
|
|
53218
|
+
const result = await callOne(operation, body);
|
|
53219
|
+
if ("transport" in result) {
|
|
53220
|
+
return { ok: false, verb, code: "intercom-guest-transport-failed", message: scrubSecrets(`${CANONICAL_COMMAND} ${verb}: the Gateway call failed in transport (${result.transport}).`, secrets), tokenRead: true };
|
|
53221
|
+
}
|
|
53222
|
+
if (!result.ok) {
|
|
52944
53223
|
return {
|
|
52945
53224
|
ok: false,
|
|
52946
53225
|
verb,
|
|
52947
|
-
code: `intercom-guest-http-${
|
|
52948
|
-
message:
|
|
53226
|
+
code: `intercom-guest-http-${result.status}`,
|
|
53227
|
+
message: scrubSecrets(`${CANONICAL_COMMAND} ${verb}: the Gateway answered HTTP ${result.status}${result.text ? ` \u2014 ${result.text.slice(0, 600)}` : ""}`, secrets),
|
|
52949
53228
|
tokenRead: true
|
|
52950
53229
|
};
|
|
52951
53230
|
}
|
|
52952
|
-
|
|
53231
|
+
if (operation.operation === "intercom.register") {
|
|
53232
|
+
const entry = registrationEntry(baseUrl, result.text, store);
|
|
53233
|
+
if (entry) await writeStore(storePath, entry).catch(() => {
|
|
53234
|
+
});
|
|
53235
|
+
}
|
|
53236
|
+
return { ok: true, verb, status: result.status, body: scrubSecrets(result.text, secrets) };
|
|
52953
53237
|
}
|
|
52954
53238
|
function renderIntercomGuestResult(result) {
|
|
52955
53239
|
return result.ok ? result.body : result.message;
|
|
52956
53240
|
}
|
|
52957
53241
|
function registerIntercomGuestCommands(program3, io = {}) {
|
|
52958
|
-
const guest = program3.command("intercom").description("Coordinate with the MM Coordinator through the Jerv Gateway intercom (guest identity; Jerv-Hub#
|
|
53242
|
+
const guest = program3.command("intercom").description("Coordinate with the MM Coordinator through the Jerv Gateway intercom (guest identity; Jerv-Hub#2727)").command("guest").description("Guest coordination verbs: register, read, send, join, leave \u2014 coordination only, never orders");
|
|
52959
53243
|
const out = io.out ?? ((line) => process.stdout.write(`${line}
|
|
52960
53244
|
`));
|
|
52961
53245
|
const err = io.err ?? ((line) => process.stderr.write(`${line}
|
|
@@ -52970,19 +53254,21 @@ function registerIntercomGuestCommands(program3, io = {}) {
|
|
|
52970
53254
|
return;
|
|
52971
53255
|
}
|
|
52972
53256
|
err(renderIntercomGuestResult(result));
|
|
52973
|
-
setExitCode(
|
|
53257
|
+
setExitCode(
|
|
53258
|
+
result.code === INTERCOM_GUEST_CONTRACT_MISSING || result.code === INTERCOM_GUEST_BASE_URL_MISSING || result.code === INTERCOM_GUEST_NO_CREDENTIAL ? 2 : 1
|
|
53259
|
+
);
|
|
52974
53260
|
};
|
|
52975
|
-
guest.command("register").description("Register this guest endpoint with the Gateway using your `gh
|
|
53261
|
+
guest.command("register").description("Register this guest endpoint with the Gateway, using your `gh` login as the display name").action(async () => {
|
|
52976
53262
|
await run({ verb: "register" });
|
|
52977
53263
|
});
|
|
52978
|
-
guest.command("read").description(
|
|
52979
|
-
const limit = Number(options.limit ??
|
|
52980
|
-
await run({ verb: "read", limit:
|
|
53264
|
+
guest.command("read").description(`Read the deliveries waiting for this guest endpoint (default ${DELIVERIES_LIMIT_DEFAULT}, at most ${DELIVERIES_LIMIT_MAX})`).option("--limit <n>", `how many deliveries to look at (1-${DELIVERIES_LIMIT_MAX})`, String(DELIVERIES_LIMIT_DEFAULT)).action(async (options) => {
|
|
53265
|
+
const limit = Number(options.limit ?? DELIVERIES_LIMIT_DEFAULT);
|
|
53266
|
+
await run({ verb: "read", limit: deliveriesLimit(limit) });
|
|
52981
53267
|
});
|
|
52982
|
-
guest.command("send").description("Send a coordination message to
|
|
53268
|
+
guest.command("send").description("Send a coordination message to the MM Coordinator").requiredOption("--to <address>", "peer endpoint id or display name (the MM Coordinator)").requiredOption("--message <text>", "the coordination message").action(async (options) => {
|
|
52983
53269
|
await run({ verb: "send", to: options.to, message: options.message });
|
|
52984
53270
|
});
|
|
52985
|
-
guest.command("join").description("Request to join a suite").requiredOption("--suite <name>", "suite name").action(async (options) => {
|
|
53271
|
+
guest.command("join").description("Request to join a suite (a message to the MM Coordinator, who grants it)").requiredOption("--suite <name>", "suite name").action(async (options) => {
|
|
52986
53272
|
await run({ verb: "join", suite: options.suite });
|
|
52987
53273
|
});
|
|
52988
53274
|
guest.command("leave").description("Leave the suite and withdraw this guest endpoint").action(async () => {
|
|
@@ -52997,7 +53283,7 @@ init_github_client();
|
|
|
52997
53283
|
init_cli_shared();
|
|
52998
53284
|
|
|
52999
53285
|
// src/suite-status.ts
|
|
53000
|
-
var
|
|
53286
|
+
var import_node_crypto20 = require("node:crypto");
|
|
53001
53287
|
var SUITE_REPO = "mutmutco/MM-Work";
|
|
53002
53288
|
var REPOS = {
|
|
53003
53289
|
"mmi-scout": "mutmutco/MMI-Scout",
|
|
@@ -53025,7 +53311,7 @@ function parseSuiteMapping(body) {
|
|
|
53025
53311
|
return void 0;
|
|
53026
53312
|
}
|
|
53027
53313
|
}
|
|
53028
|
-
var sha2562 = (b) => (0,
|
|
53314
|
+
var sha2562 = (b) => (0, import_node_crypto20.createHash)("sha256").update(b).digest("hex");
|
|
53029
53315
|
async function proveBundle(url, tag, fetchUrl) {
|
|
53030
53316
|
const get = async (u) => {
|
|
53031
53317
|
try {
|
|
@@ -53099,7 +53385,7 @@ async function stageStatus(member, stage, deps) {
|
|
|
53099
53385
|
if (pin) {
|
|
53100
53386
|
served = pin;
|
|
53101
53387
|
source = "registry-pin";
|
|
53102
|
-
} else
|
|
53388
|
+
} else {
|
|
53103
53389
|
try {
|
|
53104
53390
|
const status = await deps.deploy(member.repo, stage);
|
|
53105
53391
|
deployStatus = status;
|
|
@@ -53245,10 +53531,12 @@ async function newestGreen(deps, repo) {
|
|
|
53245
53531
|
async function readSuiteDev(deps) {
|
|
53246
53532
|
const report = await deps.status();
|
|
53247
53533
|
return Promise.all(report.members.map(async (m) => {
|
|
53248
|
-
const
|
|
53534
|
+
const track = deps.releaseTrack ? await deps.releaseTrack(m.repo).catch(() => void 0) : void 0;
|
|
53535
|
+
const stage = track === "single" ? "main" : "dev";
|
|
53536
|
+
const st = m.stages.find((s) => s.stage === stage);
|
|
53249
53537
|
const row = { repo: m.repo, head: st.head, served: st.served };
|
|
53250
|
-
if (st.source === "n/a") return { ...row, state: "converged", note:
|
|
53251
|
-
if (st.lag === false) return { ...row, state: "converged", note:
|
|
53538
|
+
if (st.source === "n/a") return { ...row, state: "converged", note: `no ${stage} stage` };
|
|
53539
|
+
if (st.lag === false) return { ...row, state: "converged", note: `${stage} serves the development head` };
|
|
53252
53540
|
let green;
|
|
53253
53541
|
try {
|
|
53254
53542
|
green = await newestGreen(deps, m.repo);
|
|
@@ -53256,10 +53544,10 @@ async function readSuiteDev(deps) {
|
|
|
53256
53544
|
return { ...row, state: "unknown", note: `checks unreadable (${e.message})` };
|
|
53257
53545
|
}
|
|
53258
53546
|
const last = st.deployStatus?.lastRun;
|
|
53259
|
-
if (st.served === "unknown") return { ...row, green, state: "unknown", note:
|
|
53260
|
-
if (same(st.served, green)) return { ...row, green, state: "converged", note:
|
|
53547
|
+
if (st.served === "unknown") return { ...row, green, state: "unknown", note: `${stage} version unproven (health probe failed or no deploy record)${last?.url ? `; last deploy ${last.conclusion ?? last.status}: ${last.url}` : ""}` };
|
|
53548
|
+
if (same(st.served, green)) return { ...row, green, state: "converged", note: `${stage} serves the newest green SHA; head ${short2(st.head)} is not green yet` };
|
|
53261
53549
|
if (!green) return { ...row, state: "blocked", note: `no green SHA in the last ${WALK} development commits` };
|
|
53262
|
-
if (last?.conclusion === "failure") return { ...row, green, state: "blocked", note: `last
|
|
53550
|
+
if (last?.conclusion === "failure") return { ...row, green, state: "blocked", note: `last ${stage} deploy failed: ${last.url ?? "no run url"}` };
|
|
53263
53551
|
return { ...row, green, state: "lagging", note: `newest green ${short2(green)} not deployed yet; the dev auto-deploy sweep (every 15 min) picks it up` };
|
|
53264
53552
|
}));
|
|
53265
53553
|
}
|
|
@@ -53281,8 +53569,8 @@ async function waitSuiteDev(deps, opts) {
|
|
|
53281
53569
|
|
|
53282
53570
|
// src/suite-train.ts
|
|
53283
53571
|
var import_node_fs41 = require("node:fs");
|
|
53284
|
-
var
|
|
53285
|
-
var
|
|
53572
|
+
var import_node_path39 = require("node:path");
|
|
53573
|
+
var import_node_crypto21 = require("node:crypto");
|
|
53286
53574
|
|
|
53287
53575
|
// src/kit-version.ts
|
|
53288
53576
|
var parse = (v) => /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(typeof v === "string" ? v.trim() : "");
|
|
@@ -53383,8 +53671,8 @@ function readSuiteCut(path2) {
|
|
|
53383
53671
|
return record2;
|
|
53384
53672
|
}
|
|
53385
53673
|
function writeSuiteCut(path2, record2) {
|
|
53386
|
-
(0, import_node_fs41.mkdirSync)((0,
|
|
53387
|
-
const temp = `${path2}.${(0,
|
|
53674
|
+
(0, import_node_fs41.mkdirSync)((0, import_node_path39.dirname)(path2), { recursive: true });
|
|
53675
|
+
const temp = `${path2}.${(0, import_node_crypto21.randomBytes)(8).toString("hex")}.tmp`;
|
|
53388
53676
|
try {
|
|
53389
53677
|
(0, import_node_fs41.writeFileSync)(temp, JSON.stringify(record2, null, 2), { flag: "wx" });
|
|
53390
53678
|
(0, import_node_fs41.renameSync)(temp, path2);
|
|
@@ -53443,7 +53731,7 @@ async function retireFailedLeadRcLedger(deps, ownerGo) {
|
|
|
53443
53731
|
if (ledger.repo !== SUITE_REPO || ledger.lane !== "rcand" || ledger.phases.promotion.state === "complete" || !states.includes("failed")) return void 0;
|
|
53444
53732
|
if (!ownerGo) throw new Error(`the lead's failed rcand ledger for ${ledger.tag} blocks every train; retire it with suite abandon --owner-go`);
|
|
53445
53733
|
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`);
|
|
53446
|
-
const archive = (0,
|
|
53734
|
+
const archive = (0, import_node_path39.join)((0, import_node_path39.dirname)(deps.ledgerPath), `phases.archive-${ledger.tag}.json`);
|
|
53447
53735
|
if ((0, import_node_fs41.existsSync)(archive)) throw new Error(`ledger archive already exists at ${archive}; inspect before retiring again`);
|
|
53448
53736
|
await deps.markAbandoned(SUITE_REPO, ledger.tag, ledger.tagSha);
|
|
53449
53737
|
await fence(() => (0, import_node_fs41.renameSync)(deps.ledgerPath, archive));
|
|
@@ -53810,7 +54098,7 @@ Each member's live release must serve <data>/main/module.js at its main pin path
|
|
|
53810
54098
|
}
|
|
53811
54099
|
return record2;
|
|
53812
54100
|
}
|
|
53813
|
-
var hotfixRecordPath = (deps) => (0,
|
|
54101
|
+
var hotfixRecordPath = (deps) => (0, import_node_path39.join)((0, import_node_path39.dirname)(deps.recordPath), "hotfix.json");
|
|
53814
54102
|
function readSuiteHotfix(path2) {
|
|
53815
54103
|
let raw;
|
|
53816
54104
|
try {
|
|
@@ -54125,20 +54413,20 @@ async function runRcDataSwitch(deps, opts) {
|
|
|
54125
54413
|
|
|
54126
54414
|
// src/suite-train-io.ts
|
|
54127
54415
|
var import_node_child_process19 = require("node:child_process");
|
|
54128
|
-
var import_node_path51 = require("node:path");
|
|
54129
|
-
var import_node_fs55 = require("node:fs");
|
|
54130
54416
|
var import_node_path52 = require("node:path");
|
|
54417
|
+
var import_node_fs55 = require("node:fs");
|
|
54418
|
+
var import_node_path53 = require("node:path");
|
|
54131
54419
|
init_cli_shared();
|
|
54132
54420
|
|
|
54133
54421
|
// src/command-register-train-operations.ts
|
|
54134
|
-
var
|
|
54422
|
+
var import_promises10 = require("node:fs/promises");
|
|
54135
54423
|
init_clean_exit();
|
|
54136
54424
|
init_cli_shared();
|
|
54137
54425
|
init_client_version();
|
|
54138
54426
|
init_house_map();
|
|
54139
54427
|
|
|
54140
54428
|
// src/hotfix-apply.ts
|
|
54141
|
-
var
|
|
54429
|
+
var import_promises6 = require("node:fs/promises");
|
|
54142
54430
|
|
|
54143
54431
|
// src/release-announce.ts
|
|
54144
54432
|
var ANNOUNCE_REPO = "mutmutco/MMI-Hub";
|
|
@@ -54481,8 +54769,8 @@ async function preflightHotfixBatchPicks(deps, sources, pickTolerated) {
|
|
|
54481
54769
|
if (sources.length === 0) return { failures: [], skipped: [] };
|
|
54482
54770
|
const { mkdtemp: mkdtemp2, rm: rm2 } = await import("node:fs/promises");
|
|
54483
54771
|
const { tmpdir: tmpdir13 } = await import("node:os");
|
|
54484
|
-
const { join:
|
|
54485
|
-
const dir = await mkdtemp2(
|
|
54772
|
+
const { join: join55 } = await import("node:path");
|
|
54773
|
+
const dir = await mkdtemp2(join55(tmpdir13(), "mmi-hotfix-preflight-"));
|
|
54486
54774
|
const wt = { run: (file, args) => deps.run(file, file === "git" ? ["-C", dir, ...args] : args) };
|
|
54487
54775
|
const failures = [];
|
|
54488
54776
|
const skipped = [];
|
|
@@ -55194,7 +55482,7 @@ async function runHotfixReleaseLocked(deps, versionInput, options, doctor) {
|
|
|
55194
55482
|
}
|
|
55195
55483
|
let lines2;
|
|
55196
55484
|
try {
|
|
55197
|
-
lines2 = summaryFileLines(await (deps.readFile ?? ((p) => (0,
|
|
55485
|
+
lines2 = summaryFileLines(await (deps.readFile ?? ((p) => (0, import_promises6.readFile)(p, "utf8")))(options.announceSummaryFile));
|
|
55198
55486
|
} catch (e) {
|
|
55199
55487
|
throw new Error(`could not read --announce-summary-file ${options.announceSummaryFile}: ${e.message} \u2014 see https://github.com/mutmutco/MMI-Hub/blob/development/docs/Guides/train-troubleshooting.md#announce-summary-missing`);
|
|
55200
55488
|
}
|
|
@@ -55820,8 +56108,8 @@ function checkHotfixCarries(options) {
|
|
|
55820
56108
|
|
|
55821
56109
|
// src/merge-cleanup.ts
|
|
55822
56110
|
var import_node_fs42 = require("node:fs");
|
|
55823
|
-
var
|
|
55824
|
-
var
|
|
56111
|
+
var import_node_path40 = require("node:path");
|
|
56112
|
+
var import_node_os23 = require("node:os");
|
|
55825
56113
|
init_cli_shared();
|
|
55826
56114
|
init_github_client();
|
|
55827
56115
|
|
|
@@ -56228,8 +56516,8 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
|
|
|
56228
56516
|
return rewritten === base ? null : rewritten;
|
|
56229
56517
|
}
|
|
56230
56518
|
function writeSquashBodyFile(body) {
|
|
56231
|
-
const dir = (0, import_node_fs42.mkdtempSync)((0,
|
|
56232
|
-
const path2 = (0,
|
|
56519
|
+
const dir = (0, import_node_fs42.mkdtempSync)((0, import_node_path40.join)((0, import_node_os23.tmpdir)(), "mmi-squash-body-"));
|
|
56520
|
+
const path2 = (0, import_node_path40.join)(dir, "body.txt");
|
|
56233
56521
|
(0, import_node_fs42.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
|
|
56234
56522
|
`, "utf8");
|
|
56235
56523
|
return { path: path2, cleanup: () => {
|
|
@@ -56485,9 +56773,9 @@ async function deleteMergedRemoteBranch(options) {
|
|
|
56485
56773
|
init_github_client();
|
|
56486
56774
|
|
|
56487
56775
|
// src/release-custody.ts
|
|
56488
|
-
var
|
|
56776
|
+
var import_node_crypto22 = require("node:crypto");
|
|
56489
56777
|
var import_node_fs43 = require("node:fs");
|
|
56490
|
-
var
|
|
56778
|
+
var import_node_path41 = require("node:path");
|
|
56491
56779
|
init_github_client();
|
|
56492
56780
|
|
|
56493
56781
|
// src/release-custody-bootstrap.ts
|
|
@@ -56709,7 +56997,7 @@ var ReleaseCustodyError = class extends Error {
|
|
|
56709
56997
|
this.name = "ReleaseCustodyError";
|
|
56710
56998
|
}
|
|
56711
56999
|
};
|
|
56712
|
-
var custodyDigest = (value) => (0,
|
|
57000
|
+
var custodyDigest = (value) => (0, import_node_crypto22.createHash)("sha256").update(value).digest("hex");
|
|
56713
57001
|
function parseCustody(raw, repo) {
|
|
56714
57002
|
const s = JSON.parse(raw);
|
|
56715
57003
|
if (s.schema !== "mmi-release-custody/v1" || s.repo !== repo || !s.precutId || !Number.isSafeInteger(s.revision) || s.revision < 1 || !s.controller || !Number.isSafeInteger(s.controller.runId) || s.controller.runId <= 0 || !Number.isSafeInteger(s.controller.runAttempt) || s.controller.runAttempt <= 0 || !s.controller.token || !(s.ledger === null || typeof s.ledger === "string") || !s.archives || !s.effects) {
|
|
@@ -56810,7 +57098,7 @@ var ReleaseCustodySession = class _ReleaseCustodySession {
|
|
|
56810
57098
|
}
|
|
56811
57099
|
}
|
|
56812
57100
|
let state = {
|
|
56813
|
-
...found.state ?? { schema: "mmi-release-custody/v1", repo, precutId: (0,
|
|
57101
|
+
...found.state ?? { schema: "mmi-release-custody/v1", repo, precutId: (0, import_node_crypto22.randomUUID)(), effects: {}, ...await initialize() },
|
|
56814
57102
|
revision: (found.state?.revision ?? 0) + 1,
|
|
56815
57103
|
controller
|
|
56816
57104
|
};
|
|
@@ -56871,7 +57159,7 @@ var ReleaseCustodySession = class _ReleaseCustodySession {
|
|
|
56871
57159
|
proof: { kind: "ledger-lifecycle", controller: state.controller }
|
|
56872
57160
|
};
|
|
56873
57161
|
}
|
|
56874
|
-
return { ...state, ledger: raw, archives, precutId: state.ledger && !raw ? (0,
|
|
57162
|
+
return { ...state, ledger: raw, archives, precutId: state.ledger && !raw ? (0, import_node_crypto22.randomUUID)() : state.precutId };
|
|
56875
57163
|
});
|
|
56876
57164
|
}
|
|
56877
57165
|
async preserveLocal(raw) {
|
|
@@ -56987,10 +57275,10 @@ function localRaw(path2) {
|
|
|
56987
57275
|
function hydrateLocal(path2, raw) {
|
|
56988
57276
|
const previous = localRaw(path2);
|
|
56989
57277
|
if (previous === raw) return;
|
|
56990
|
-
(0, import_node_fs43.mkdirSync)((0,
|
|
57278
|
+
(0, import_node_fs43.mkdirSync)((0, import_node_path41.dirname)(path2), { recursive: true });
|
|
56991
57279
|
if (previous !== null) {
|
|
56992
57280
|
const ledger = parseReleaseLedger(previous);
|
|
56993
|
-
const archive = (0,
|
|
57281
|
+
const archive = (0, import_node_path41.join)((0, import_node_path41.dirname)(path2), `phases.local-${ledger.tagSha}-${custodyDigest(previous)}.json`);
|
|
56994
57282
|
if (!(0, import_node_fs43.existsSync)(archive)) (0, import_node_fs43.writeFileSync)(archive, previous, { flag: "wx" });
|
|
56995
57283
|
if (raw === null) (0, import_node_fs43.renameSync)(path2, `${path2}.retired-${custodyDigest(previous)}`);
|
|
56996
57284
|
}
|
|
@@ -57105,7 +57393,7 @@ function withHubReleaseCustody(deps) {
|
|
|
57105
57393
|
if (!authority.ok || !authority.train || authority.authenticatedDelegation !== true) {
|
|
57106
57394
|
throw new ReleaseCustodyError("repository custody requires the server-verified owner-triggered train session");
|
|
57107
57395
|
}
|
|
57108
|
-
const controller = { ...runIdentity, token: (0,
|
|
57396
|
+
const controller = { ...runIdentity, token: (0, import_node_crypto22.randomUUID)(), ...process.env.GITHUB_JOB ? { job: process.env.GITHUB_JOB } : {} };
|
|
57109
57397
|
const raw = localRaw(path2);
|
|
57110
57398
|
const requiredContexts = () => discoverRequiredCheckContexts(
|
|
57111
57399
|
deps,
|
|
@@ -57186,7 +57474,7 @@ function withHubReleaseCustody(deps) {
|
|
|
57186
57474
|
|
|
57187
57475
|
// src/project-info-sync.ts
|
|
57188
57476
|
var import_node_fs44 = require("node:fs");
|
|
57189
|
-
var
|
|
57477
|
+
var import_node_path42 = require("node:path");
|
|
57190
57478
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
57191
57479
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
57192
57480
|
projectV2 { id }
|
|
@@ -57233,7 +57521,7 @@ function sharedName(entries, fallback) {
|
|
|
57233
57521
|
}
|
|
57234
57522
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
57235
57523
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
57236
|
-
const readmePath = (0,
|
|
57524
|
+
const readmePath = (0, import_node_path42.join)(repoRoot2, "README.md");
|
|
57237
57525
|
if (!(0, import_node_fs44.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
57238
57526
|
const entries = entriesFor(project2, projects);
|
|
57239
57527
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
@@ -57259,8 +57547,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
57259
57547
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
57260
57548
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
57261
57549
|
const orgDocs = [
|
|
57262
|
-
(0, import_node_fs44.existsSync)((0,
|
|
57263
|
-
(0, import_node_fs44.existsSync)((0,
|
|
57550
|
+
(0, import_node_fs44.existsSync)((0, import_node_path42.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
|
|
57551
|
+
(0, import_node_fs44.existsSync)((0, import_node_path42.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
|
|
57264
57552
|
].filter(Boolean);
|
|
57265
57553
|
if (orgDocs.length) lines2.push("", "## Organisation docs", "", ...orgDocs);
|
|
57266
57554
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines2.join("\n")}
|
|
@@ -57283,7 +57571,7 @@ async function syncProjectInfo(plan, client, apply) {
|
|
|
57283
57571
|
|
|
57284
57572
|
// src/train-commands.ts
|
|
57285
57573
|
var import_node_fs45 = require("node:fs");
|
|
57286
|
-
var
|
|
57574
|
+
var import_node_path43 = require("node:path");
|
|
57287
57575
|
init_cli_shared();
|
|
57288
57576
|
init_clean_exit();
|
|
57289
57577
|
init_client_version();
|
|
@@ -57298,7 +57586,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
57298
57586
|
}
|
|
57299
57587
|
function readRepoVersion() {
|
|
57300
57588
|
try {
|
|
57301
|
-
return JSON.parse((0, import_node_fs45.readFileSync)((0,
|
|
57589
|
+
return JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path43.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
57302
57590
|
} catch {
|
|
57303
57591
|
return void 0;
|
|
57304
57592
|
}
|
|
@@ -57408,8 +57696,8 @@ function releaseProgressVerdict(status, subject = "Release") {
|
|
|
57408
57696
|
}
|
|
57409
57697
|
|
|
57410
57698
|
// src/suite-pin.ts
|
|
57411
|
-
var
|
|
57412
|
-
var
|
|
57699
|
+
var import_promises7 = require("node:fs/promises");
|
|
57700
|
+
var import_node_path44 = require("node:path");
|
|
57413
57701
|
var SuitePinError = class _SuitePinError extends Error {
|
|
57414
57702
|
diagnostic;
|
|
57415
57703
|
constructor(repo, tag, error) {
|
|
@@ -57477,8 +57765,8 @@ async function openSuiteCutPinPr(deps, input) {
|
|
|
57477
57765
|
if (input.carriedOnly) return { status: "unchanged" };
|
|
57478
57766
|
throw new Error("pins already match without a merged cut PR \u2014 provenance unverified");
|
|
57479
57767
|
}
|
|
57480
|
-
await (0,
|
|
57481
|
-
scratch = await (0,
|
|
57768
|
+
await (0, import_promises7.mkdir)((0, import_node_path44.join)(deps.root, "tmp"), { recursive: true });
|
|
57769
|
+
scratch = await (0, import_promises7.mkdtemp)((0, import_node_path44.join)(deps.root, "tmp", "suite-pin-"));
|
|
57482
57770
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
57483
57771
|
await run("git", ["clone", "--quiet", "--branch", prs.length ? branch : "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
57484
57772
|
if (prs.length) {
|
|
@@ -57490,8 +57778,8 @@ async function openSuiteCutPinPr(deps, input) {
|
|
|
57490
57778
|
await run("git", ["switch", "-c", branch]);
|
|
57491
57779
|
}
|
|
57492
57780
|
for (const stage of ["rc", "main"]) {
|
|
57493
|
-
const path2 = (0,
|
|
57494
|
-
await (0,
|
|
57781
|
+
const path2 = (0, import_node_path44.join)(scratch, `${stage}-pins.json`);
|
|
57782
|
+
await (0, import_promises7.writeFile)(path2, JSON.stringify(input.pins.map((p) => ({ module: p.module, url: p[stage] }))));
|
|
57495
57783
|
await run("node", ["scripts/pin-modules.mjs", "--stage", stage, "--pins", path2]);
|
|
57496
57784
|
}
|
|
57497
57785
|
await run("git", ["add", "seed/modules.json"]);
|
|
@@ -57521,7 +57809,7 @@ async function openSuiteCutPinPr(deps, input) {
|
|
|
57521
57809
|
} catch (e) {
|
|
57522
57810
|
throw new Error(`suite cut pin PR FAILED: ${e.message}; merge/verify the single ${SUITE_REPO} development pin PR, then rerun the cut (never recut member tags)`);
|
|
57523
57811
|
} finally {
|
|
57524
|
-
if (scratch) await (0,
|
|
57812
|
+
if (scratch) await (0, import_promises7.rm)(scratch, { recursive: true, force: true });
|
|
57525
57813
|
}
|
|
57526
57814
|
}
|
|
57527
57815
|
async function openSuiteRestorePinPr(deps, input) {
|
|
@@ -57543,13 +57831,13 @@ async function openSuiteRestorePinPr(deps, input) {
|
|
|
57543
57831
|
}
|
|
57544
57832
|
});
|
|
57545
57833
|
if (!todo.length) return void 0;
|
|
57546
|
-
await (0,
|
|
57547
|
-
scratch = await (0,
|
|
57834
|
+
await (0, import_promises7.mkdir)((0, import_node_path44.join)(deps.root, "tmp"), { recursive: true });
|
|
57835
|
+
scratch = await (0, import_promises7.mkdtemp)((0, import_node_path44.join)(deps.root, "tmp", "suite-pin-"));
|
|
57548
57836
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
57549
57837
|
await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
57550
57838
|
await run("git", ["switch", "-c", branch]);
|
|
57551
|
-
const path2 = (0,
|
|
57552
|
-
await (0,
|
|
57839
|
+
const path2 = (0, import_node_path44.join)(scratch, "main-pins.json");
|
|
57840
|
+
await (0, import_promises7.writeFile)(path2, JSON.stringify(todo.map((p) => ({ module: p.module, url: p.prior }))));
|
|
57553
57841
|
await run("node", ["scripts/pin-modules.mjs", "--stage", "main", "--pins", path2]);
|
|
57554
57842
|
await run("git", ["add", "seed/modules.json"]);
|
|
57555
57843
|
await run("git", ["commit", "-m", `Restore main pins of abandoned suite cut ${input.cut.slice(0, 12)} (#7758)`]);
|
|
@@ -57574,7 +57862,7 @@ async function openSuiteRestorePinPr(deps, input) {
|
|
|
57574
57862
|
} catch (e) {
|
|
57575
57863
|
throw new Error(`suite restore pin PR FAILED: ${e.message}; rerun suite abandon --owner-go (the record is kept until the restore PR exists)`);
|
|
57576
57864
|
} finally {
|
|
57577
|
-
if (scratch) await (0,
|
|
57865
|
+
if (scratch) await (0, import_promises7.rm)(scratch, { recursive: true, force: true });
|
|
57578
57866
|
}
|
|
57579
57867
|
}
|
|
57580
57868
|
async function openSuitePinPr(deps, input) {
|
|
@@ -57622,8 +57910,8 @@ async function openSuitePinPr(deps, input) {
|
|
|
57622
57910
|
if (newer.length) return { status: "superseded", pr: newer[0].url, url: pin };
|
|
57623
57911
|
const open2 = onBranch.find((p) => p.state === "OPEN");
|
|
57624
57912
|
if (open2 && tagOf(open2.title ?? "") === tag) return { status: "pending", pr: open2.url, url: pin };
|
|
57625
|
-
await (0,
|
|
57626
|
-
scratch = await (0,
|
|
57913
|
+
await (0, import_promises7.mkdir)((0, import_node_path44.join)(deps.root, "tmp"), { recursive: true });
|
|
57914
|
+
scratch = await (0, import_promises7.mkdtemp)((0, import_node_path44.join)(deps.root, "tmp", "suite-pin-"));
|
|
57627
57915
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
57628
57916
|
await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
57629
57917
|
await run("git", ["switch", "-c", branch]);
|
|
@@ -57650,7 +57938,7 @@ async function openSuitePinPr(deps, input) {
|
|
|
57650
57938
|
} finally {
|
|
57651
57939
|
if (scratch) {
|
|
57652
57940
|
try {
|
|
57653
|
-
await (0,
|
|
57941
|
+
await (0, import_promises7.rm)(scratch, { recursive: true, force: true });
|
|
57654
57942
|
} catch (e) {
|
|
57655
57943
|
const cleanupFailure = failure ?? new SuitePinError(repo, tag, e);
|
|
57656
57944
|
cleanupFailure.diagnostic.cleanupFailed = true;
|
|
@@ -57676,7 +57964,7 @@ function prTitleRefusal(title) {
|
|
|
57676
57964
|
// src/command-register-collaboration.ts
|
|
57677
57965
|
var import_node_child_process18 = require("node:child_process");
|
|
57678
57966
|
var import_node_fs54 = require("node:fs");
|
|
57679
|
-
var
|
|
57967
|
+
var import_promises9 = require("node:fs/promises");
|
|
57680
57968
|
init_clean_exit();
|
|
57681
57969
|
init_cli_shared();
|
|
57682
57970
|
init_error_codes();
|
|
@@ -57699,11 +57987,11 @@ function spawnDetachedSelf(args, deps, opts = {}) {
|
|
|
57699
57987
|
}
|
|
57700
57988
|
|
|
57701
57989
|
// src/command-register-collaboration.ts
|
|
57702
|
-
var
|
|
57990
|
+
var import_node_path51 = require("node:path");
|
|
57703
57991
|
init_house_map();
|
|
57704
57992
|
|
|
57705
57993
|
// src/issue-body.ts
|
|
57706
|
-
var
|
|
57994
|
+
var import_node_os24 = require("node:os");
|
|
57707
57995
|
init_error_codes();
|
|
57708
57996
|
var TextArgError = class extends Error {
|
|
57709
57997
|
constructor(message2, code, offendingFlag) {
|
|
@@ -57716,7 +58004,7 @@ var TextArgError = class extends Error {
|
|
|
57716
58004
|
offendingFlag;
|
|
57717
58005
|
};
|
|
57718
58006
|
function emptyStdinMessage(fileFlag) {
|
|
57719
|
-
if ((0,
|
|
58007
|
+
if ((0, import_node_os24.platform)() === "win32") {
|
|
57720
58008
|
return `${fileFlag} - read empty stdin (on Windows, ${fileFlag} - is unreliable through the npm .cmd shim \u2014 use ${fileFlag} <path>, or pipe to \`node cli/dist/index.cjs\` directly)`;
|
|
57721
58009
|
}
|
|
57722
58010
|
return `${fileFlag} - read empty stdin (nothing piped \u2014 pass a heredoc/pipe, or ${fileFlag} <path>)`;
|
|
@@ -57817,7 +58105,7 @@ init_github_quota();
|
|
|
57817
58105
|
|
|
57818
58106
|
// src/issue-commands.ts
|
|
57819
58107
|
var import_node_fs46 = require("node:fs");
|
|
57820
|
-
var
|
|
58108
|
+
var import_node_crypto23 = require("node:crypto");
|
|
57821
58109
|
init_cli_shared();
|
|
57822
58110
|
init_clean_exit();
|
|
57823
58111
|
init_error_codes();
|
|
@@ -58294,7 +58582,7 @@ function rowIdempotencyKey(batchKey, spec) {
|
|
|
58294
58582
|
const identity = `${spec.type}
|
|
58295
58583
|
${spec.title.trim()}
|
|
58296
58584
|
${spec.body ?? ""}`;
|
|
58297
|
-
const hash = (0,
|
|
58585
|
+
const hash = (0, import_node_crypto23.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
|
|
58298
58586
|
return `${batchKey}:${hash}`;
|
|
58299
58587
|
}
|
|
58300
58588
|
var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
|
|
@@ -58478,6 +58766,11 @@ ${lines2}`);
|
|
|
58478
58766
|
if (deps.attach) {
|
|
58479
58767
|
const attached = await deps.attach(row.number, rowRepo(spec), rowPriority);
|
|
58480
58768
|
row.onBoard = attached.onBoard;
|
|
58769
|
+
if (attached.prioritySet === false) {
|
|
58770
|
+
row.status = "partial";
|
|
58771
|
+
row.prioritySet = false;
|
|
58772
|
+
row.priorityError = attached.priorityError;
|
|
58773
|
+
}
|
|
58481
58774
|
if (attached.projectItemId) row.projectItemId = attached.projectItemId;
|
|
58482
58775
|
if (attached.boardAttach) {
|
|
58483
58776
|
row.boardAttach = attached.boardAttach;
|
|
@@ -58754,7 +59047,7 @@ ${lines2}`, {
|
|
|
58754
59047
|
|
|
58755
59048
|
// src/post-merge-recon.ts
|
|
58756
59049
|
var import_node_fs47 = require("node:fs");
|
|
58757
|
-
var
|
|
59050
|
+
var import_node_path45 = require("node:path");
|
|
58758
59051
|
|
|
58759
59052
|
// src/cross-repo-filing-issue.ts
|
|
58760
59053
|
init_github_client();
|
|
@@ -58924,7 +59217,7 @@ function buildPostMergeReconRecovery(input) {
|
|
|
58924
59217
|
}
|
|
58925
59218
|
function writePostMergeReconRecovery(cwd, recovery) {
|
|
58926
59219
|
const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
|
|
58927
|
-
(0, import_node_fs47.mkdirSync)((0,
|
|
59220
|
+
(0, import_node_fs47.mkdirSync)((0, import_node_path45.dirname)(path2), { recursive: true });
|
|
58928
59221
|
(0, import_node_fs47.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
|
|
58929
59222
|
`, "utf8");
|
|
58930
59223
|
return path2;
|
|
@@ -58964,7 +59257,7 @@ function postMergeReconWarnings(input) {
|
|
|
58964
59257
|
}
|
|
58965
59258
|
|
|
58966
59259
|
// src/pr-commands.ts
|
|
58967
|
-
var
|
|
59260
|
+
var import_promises8 = require("node:fs/promises");
|
|
58968
59261
|
init_cli_shared();
|
|
58969
59262
|
|
|
58970
59263
|
// src/api-read.ts
|
|
@@ -60457,10 +60750,10 @@ function registerPrLifecycleCommands(program3) {
|
|
|
60457
60750
|
let body;
|
|
60458
60751
|
try {
|
|
60459
60752
|
if (o.title || o.titleFile) {
|
|
60460
|
-
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile:
|
|
60753
|
+
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises8.readFile, readStdin });
|
|
60461
60754
|
}
|
|
60462
60755
|
if (o.body || o.bodyFile) {
|
|
60463
|
-
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
60756
|
+
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises8.readFile, readStdin });
|
|
60464
60757
|
}
|
|
60465
60758
|
} catch (e) {
|
|
60466
60759
|
return fail(`pr edit: ${e.message}`);
|
|
@@ -60491,7 +60784,7 @@ function registerPrLifecycleCommands(program3) {
|
|
|
60491
60784
|
}
|
|
60492
60785
|
let body;
|
|
60493
60786
|
try {
|
|
60494
|
-
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
60787
|
+
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises8.readFile, readStdin });
|
|
60495
60788
|
} catch (e) {
|
|
60496
60789
|
return fail(`pr comment: ${e.message}`);
|
|
60497
60790
|
}
|
|
@@ -60776,8 +61069,8 @@ function registerPrLifecycleCommands(program3) {
|
|
|
60776
61069
|
// src/review-verdict.ts
|
|
60777
61070
|
var import_node_child_process15 = require("node:child_process");
|
|
60778
61071
|
var import_node_fs49 = require("node:fs");
|
|
60779
|
-
var
|
|
60780
|
-
var
|
|
61072
|
+
var import_node_os25 = require("node:os");
|
|
61073
|
+
var import_node_path46 = require("node:path");
|
|
60781
61074
|
init_cli_shared();
|
|
60782
61075
|
var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
|
|
60783
61076
|
var REVIEW_VERDICTS = ["PROCEED", "CORRECT", "ESCALATE"];
|
|
@@ -60942,8 +61235,8 @@ async function readPrIssueComments(number, repo) {
|
|
|
60942
61235
|
return comments;
|
|
60943
61236
|
}
|
|
60944
61237
|
async function postPrCommentFromFile(number, repo, body) {
|
|
60945
|
-
const dir = (0, import_node_fs49.mkdtempSync)((0,
|
|
60946
|
-
const path2 = (0,
|
|
61238
|
+
const dir = (0, import_node_fs49.mkdtempSync)((0, import_node_path46.join)((0, import_node_os25.tmpdir)(), "mmi-review-verdict-"));
|
|
61239
|
+
const path2 = (0, import_node_path46.join)(dir, "body.md");
|
|
60947
61240
|
try {
|
|
60948
61241
|
(0, import_node_fs49.writeFileSync)(path2, body, "utf8");
|
|
60949
61242
|
const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
@@ -61439,17 +61732,17 @@ function createPreparationDeps(input, io = {}) {
|
|
|
61439
61732
|
|
|
61440
61733
|
// src/worktree-residue-sweep.ts
|
|
61441
61734
|
var import_node_fs53 = require("node:fs");
|
|
61442
|
-
var
|
|
61735
|
+
var import_node_path50 = require("node:path");
|
|
61443
61736
|
|
|
61444
61737
|
// src/worktree-merge-cleanup.ts
|
|
61445
61738
|
var import_node_fs52 = require("node:fs");
|
|
61446
|
-
var
|
|
61739
|
+
var import_node_path49 = require("node:path");
|
|
61447
61740
|
init_cli_shared();
|
|
61448
61741
|
|
|
61449
61742
|
// src/stage-runner.ts
|
|
61450
61743
|
var import_node_child_process17 = require("node:child_process");
|
|
61451
61744
|
var import_node_fs51 = require("node:fs");
|
|
61452
|
-
var
|
|
61745
|
+
var import_node_path48 = require("node:path");
|
|
61453
61746
|
var import_node_net = require("node:net");
|
|
61454
61747
|
var import_node_util5 = require("node:util");
|
|
61455
61748
|
|
|
@@ -61460,7 +61753,7 @@ function normalizeEol2(s) {
|
|
|
61460
61753
|
|
|
61461
61754
|
// src/env-detection.ts
|
|
61462
61755
|
var import_node_fs50 = require("node:fs");
|
|
61463
|
-
var
|
|
61756
|
+
var import_node_path47 = require("node:path");
|
|
61464
61757
|
var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".jerv", ".pi", ".venv", "venv"]);
|
|
61465
61758
|
function isEnvBaseName(name) {
|
|
61466
61759
|
return name.toLowerCase() === ".env";
|
|
@@ -61478,13 +61771,13 @@ function findEnvFiles(root) {
|
|
|
61478
61771
|
const relPath = rel ? `${rel}/${entry.name}` : entry.name;
|
|
61479
61772
|
if (entry.isDirectory()) {
|
|
61480
61773
|
if (SKIPPED_DIRS.has(entry.name)) continue;
|
|
61481
|
-
walk2((0,
|
|
61774
|
+
walk2((0, import_node_path47.join)(dir, entry.name), relPath);
|
|
61482
61775
|
continue;
|
|
61483
61776
|
}
|
|
61484
61777
|
if (isEnvBaseName(entry.name)) {
|
|
61485
61778
|
let isDir = false;
|
|
61486
61779
|
try {
|
|
61487
|
-
isDir = (0, import_node_fs50.lstatSync)((0,
|
|
61780
|
+
isDir = (0, import_node_fs50.lstatSync)((0, import_node_path47.join)(dir, entry.name)).isDirectory();
|
|
61488
61781
|
} catch {
|
|
61489
61782
|
}
|
|
61490
61783
|
if (!isDir) found.push(relPath);
|
|
@@ -61623,10 +61916,10 @@ var DOCKER_TIMEOUT_MS = 15e3;
|
|
|
61623
61916
|
function stageBash() {
|
|
61624
61917
|
if (process.platform !== "win32") return "bash";
|
|
61625
61918
|
const configured = process.env.SHELL;
|
|
61626
|
-
if (configured && (0,
|
|
61919
|
+
if (configured && (0, import_node_path48.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs51.existsSync)(configured)) return configured;
|
|
61627
61920
|
const candidates = [
|
|
61628
|
-
(0,
|
|
61629
|
-
...process.env.LOCALAPPDATA ? [(0,
|
|
61921
|
+
(0, import_node_path48.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
|
|
61922
|
+
...process.env.LOCALAPPDATA ? [(0, import_node_path48.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
|
|
61630
61923
|
];
|
|
61631
61924
|
const bash = candidates.find((path2) => (0, import_node_fs51.existsSync)(path2));
|
|
61632
61925
|
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.");
|
|
@@ -61766,7 +62059,7 @@ function appendForceRecreate(up) {
|
|
|
61766
62059
|
return `${up.trimEnd()} --force-recreate`;
|
|
61767
62060
|
}
|
|
61768
62061
|
function stageStatePath(cwd = process.cwd()) {
|
|
61769
|
-
return (0,
|
|
62062
|
+
return (0, import_node_path48.join)(cwd, "tmp", "stage", "state.json");
|
|
61770
62063
|
}
|
|
61771
62064
|
function normPath(path2) {
|
|
61772
62065
|
return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
@@ -61983,7 +62276,7 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
61983
62276
|
}
|
|
61984
62277
|
function composeResolvesPort(cwd) {
|
|
61985
62278
|
if (process.env.PORT) return true;
|
|
61986
|
-
const envFile = (0,
|
|
62279
|
+
const envFile = (0, import_node_path48.join)(cwd, ".env");
|
|
61987
62280
|
return (0, import_node_fs51.existsSync)(envFile) && envFileKeys((0, import_node_fs51.readFileSync)(envFile, "utf8")).has("PORT");
|
|
61988
62281
|
}
|
|
61989
62282
|
function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
@@ -61999,13 +62292,13 @@ function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
|
61999
62292
|
function stageComposeFiles(cwd) {
|
|
62000
62293
|
const files = ["docker-compose.yml"];
|
|
62001
62294
|
for (const conventional of ["docker-compose.override.yml", "docker-compose.override.yaml"]) {
|
|
62002
|
-
if ((0, import_node_fs51.existsSync)((0,
|
|
62295
|
+
if ((0, import_node_fs51.existsSync)((0, import_node_path48.join)(cwd, conventional))) files.push(conventional);
|
|
62003
62296
|
}
|
|
62004
62297
|
return [...files, RUNTIME_ENV_OVERRIDE_RELPATH];
|
|
62005
62298
|
}
|
|
62006
62299
|
async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
62007
62300
|
const keys = Object.keys(vaultEnvMerge ?? {});
|
|
62008
|
-
if (!keys.length || !(0, import_node_fs51.existsSync)((0,
|
|
62301
|
+
if (!keys.length || !(0, import_node_fs51.existsSync)((0, import_node_path48.join)(cwd, "docker-compose.yml"))) return {};
|
|
62009
62302
|
let configJson;
|
|
62010
62303
|
try {
|
|
62011
62304
|
const { stdout } = await execFileP3("docker", ["compose", "config", "--format", "json"], {
|
|
@@ -62021,15 +62314,15 @@ async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
|
62021
62314
|
}
|
|
62022
62315
|
const body = renderRuntimeEnvOverride(runtimeEnvMarkedServices(configJson), keys, runtimeEnvExcludes(configJson), runtimeEnvSelects(configJson));
|
|
62023
62316
|
if (!body) return {};
|
|
62024
|
-
const overridePath = (0,
|
|
62025
|
-
(0, import_node_fs51.mkdirSync)((0,
|
|
62317
|
+
const overridePath = (0, import_node_path48.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
|
|
62318
|
+
(0, import_node_fs51.mkdirSync)((0, import_node_path48.join)(cwd, "tmp", "stage"), { recursive: true });
|
|
62026
62319
|
(0, import_node_fs51.writeFileSync)(overridePath, body, "utf8");
|
|
62027
62320
|
return stageComposeFileEnv(stageComposeFiles(cwd));
|
|
62028
62321
|
}
|
|
62029
62322
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
62030
62323
|
if (!config.ensureEnv) return;
|
|
62031
|
-
const target = (0,
|
|
62032
|
-
const example = (0,
|
|
62324
|
+
const target = (0, import_node_path48.join)(cwd, config.ensureEnv.target);
|
|
62325
|
+
const example = (0, import_node_path48.join)(cwd, config.ensureEnv.example);
|
|
62033
62326
|
if (!(0, import_node_fs51.existsSync)(target) && (0, import_node_fs51.existsSync)(example)) {
|
|
62034
62327
|
(0, import_node_fs51.copyFileSync)(example, target);
|
|
62035
62328
|
} else if ((0, import_node_fs51.existsSync)(target) && (0, import_node_fs51.existsSync)(example)) {
|
|
@@ -62383,7 +62676,7 @@ function normPath2(p) {
|
|
|
62383
62676
|
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
62384
62677
|
}
|
|
62385
62678
|
function unlinkNodeModulesJunction(wtPath) {
|
|
62386
|
-
const nm = (0,
|
|
62679
|
+
const nm = (0, import_node_path49.join)(wtPath, "node_modules");
|
|
62387
62680
|
try {
|
|
62388
62681
|
if ((0, import_node_fs52.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs52.rmdirSync)(nm);
|
|
62389
62682
|
return { ok: true };
|
|
@@ -62520,7 +62813,7 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
|
|
|
62520
62813
|
}
|
|
62521
62814
|
async function listNestedIgnoredNodeModules(wtPath, execGit) {
|
|
62522
62815
|
const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
|
|
62523
|
-
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0,
|
|
62816
|
+
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path49.join)(wtPath, line.slice(0, -1)));
|
|
62524
62817
|
}
|
|
62525
62818
|
function safeRemoveTree(path2) {
|
|
62526
62819
|
const stat4 = (0, import_node_fs52.lstatSync)(path2);
|
|
@@ -62533,7 +62826,7 @@ function safeRemoveTree(path2) {
|
|
|
62533
62826
|
return;
|
|
62534
62827
|
}
|
|
62535
62828
|
if (stat4.isDirectory()) {
|
|
62536
|
-
for (const entry of (0, import_node_fs52.readdirSync)(path2)) safeRemoveTree((0,
|
|
62829
|
+
for (const entry of (0, import_node_fs52.readdirSync)(path2)) safeRemoveTree((0, import_node_path49.join)(path2, entry));
|
|
62537
62830
|
(0, import_node_fs52.rmdirSync)(path2);
|
|
62538
62831
|
return;
|
|
62539
62832
|
}
|
|
@@ -62541,7 +62834,7 @@ function safeRemoveTree(path2) {
|
|
|
62541
62834
|
}
|
|
62542
62835
|
async function removeWorktreeNodeModules(wtPath) {
|
|
62543
62836
|
try {
|
|
62544
|
-
safeRemoveTree((0,
|
|
62837
|
+
safeRemoveTree((0, import_node_path49.join)(wtPath, "node_modules"));
|
|
62545
62838
|
return { ok: true };
|
|
62546
62839
|
} catch (e) {
|
|
62547
62840
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -62586,7 +62879,7 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
|
62586
62879
|
return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
|
|
62587
62880
|
}
|
|
62588
62881
|
for (const entry of entries) {
|
|
62589
|
-
const child2 = (0,
|
|
62882
|
+
const child2 = (0, import_node_path49.join)(dir, entry.name);
|
|
62590
62883
|
if (entry.isSymbolicLink()) {
|
|
62591
62884
|
let target = "";
|
|
62592
62885
|
try {
|
|
@@ -62913,7 +63206,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
62913
63206
|
const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
|
|
62914
63207
|
if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
|
|
62915
63208
|
unlinkedReparsePoints.push(...preHelperGuard.unlinked);
|
|
62916
|
-
if (pathExists((0,
|
|
63209
|
+
if (pathExists((0, import_node_path49.join)(wtPath, "node_modules"))) {
|
|
62917
63210
|
const nmRemoved = await (options.removeRealNodeModules ?? removeWorktreeNodeModules)(wtPath);
|
|
62918
63211
|
if (!nmRemoved.ok) {
|
|
62919
63212
|
report.worktree = {
|
|
@@ -63049,35 +63342,35 @@ function renderPrMergeCleanupLines(cleanup) {
|
|
|
63049
63342
|
// src/worktree-residue-sweep.ts
|
|
63050
63343
|
var RESIDUE_LEDGER_FILE = "mmi-worktree-residue.json";
|
|
63051
63344
|
function canonicalWorktreeRoot(primary) {
|
|
63052
|
-
return (0,
|
|
63345
|
+
return (0, import_node_path50.join)((0, import_node_path50.dirname)(primary), "mmi-worktrees", (0, import_node_path50.basename)(primary));
|
|
63053
63346
|
}
|
|
63054
63347
|
function lastSegment(p) {
|
|
63055
|
-
return (0,
|
|
63348
|
+
return (0, import_node_path50.basename)(p.split(/[\\/]+/).filter(Boolean).pop() ?? p);
|
|
63056
63349
|
}
|
|
63057
63350
|
function readProvenance(folder, gitCommonDir, exists) {
|
|
63058
63351
|
const found = {
|
|
63059
63352
|
path: folder,
|
|
63060
63353
|
gitdirPresent: false,
|
|
63061
|
-
registrationId: (0,
|
|
63354
|
+
registrationId: (0, import_node_path50.basename)(folder),
|
|
63062
63355
|
registrationPresent: false
|
|
63063
63356
|
};
|
|
63064
63357
|
try {
|
|
63065
|
-
const pointer = /^\s*gitdir:\s*(.+?)\s*$/m.exec((0, import_node_fs53.readFileSync)((0,
|
|
63358
|
+
const pointer = /^\s*gitdir:\s*(.+?)\s*$/m.exec((0, import_node_fs53.readFileSync)((0, import_node_path50.join)(folder, ".git"), "utf8"));
|
|
63066
63359
|
if (pointer) {
|
|
63067
63360
|
const target = pointer[1];
|
|
63068
|
-
found.gitdir = (0,
|
|
63361
|
+
found.gitdir = (0, import_node_path50.isAbsolute)(target) ? target : (0, import_node_path50.join)(folder, target);
|
|
63069
63362
|
found.gitdirPresent = exists(found.gitdir);
|
|
63070
63363
|
found.registrationId = lastSegment(found.gitdir);
|
|
63071
63364
|
}
|
|
63072
63365
|
} catch {
|
|
63073
63366
|
}
|
|
63074
63367
|
try {
|
|
63075
|
-
found.registrationPresent = exists((0,
|
|
63368
|
+
found.registrationPresent = exists((0, import_node_path50.join)(gitCommonDir, "worktrees", found.registrationId));
|
|
63076
63369
|
} catch {
|
|
63077
63370
|
}
|
|
63078
63371
|
if (found.gitdir && found.gitdirPresent) {
|
|
63079
63372
|
try {
|
|
63080
|
-
const head = (0, import_node_fs53.readFileSync)((0,
|
|
63373
|
+
const head = (0, import_node_fs53.readFileSync)((0, import_node_path50.join)(found.gitdir, "HEAD"), "utf8").trim();
|
|
63081
63374
|
const branch = /^ref:\s*(.+?)\s*$/.exec(head);
|
|
63082
63375
|
if (branch) found.head = branch[1].startsWith("refs/heads/") ? branch[1].slice("refs/heads/".length) : `ref ${branch[1]}`;
|
|
63083
63376
|
else if (/^[0-9a-f]{7,64}$/i.test(head)) found.head = `detached ${head.slice(0, 12)}`;
|
|
@@ -63096,7 +63389,7 @@ function norm2(p) {
|
|
|
63096
63389
|
return process.platform === "win32" ? s.toLowerCase() : s;
|
|
63097
63390
|
}
|
|
63098
63391
|
function residueLedgerPath(gitCommonDir) {
|
|
63099
|
-
return (0,
|
|
63392
|
+
return (0, import_node_path50.join)(gitCommonDir, RESIDUE_LEDGER_FILE);
|
|
63100
63393
|
}
|
|
63101
63394
|
function readPaths(file, deps) {
|
|
63102
63395
|
try {
|
|
@@ -63109,7 +63402,7 @@ function readPaths(file, deps) {
|
|
|
63109
63402
|
function writePaths(file, paths, deps) {
|
|
63110
63403
|
try {
|
|
63111
63404
|
const write = deps.writeLedger ?? ((f, t) => {
|
|
63112
|
-
(0, import_node_fs53.mkdirSync)((0,
|
|
63405
|
+
(0, import_node_fs53.mkdirSync)((0, import_node_path50.dirname)(f), { recursive: true });
|
|
63113
63406
|
(0, import_node_fs53.writeFileSync)(f, t, "utf8");
|
|
63114
63407
|
});
|
|
63115
63408
|
write(file, `${JSON.stringify([...new Set(paths)], null, 1)}
|
|
@@ -63169,7 +63462,7 @@ async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
|
63169
63462
|
} catch {
|
|
63170
63463
|
}
|
|
63171
63464
|
for (const name of children) {
|
|
63172
|
-
const child2 = (0,
|
|
63465
|
+
const child2 = (0, import_node_path50.join)(root, name);
|
|
63173
63466
|
const key = norm2(child2);
|
|
63174
63467
|
if (live.has(key) || key === primary || keptKeys.has(key) || !isDir(child2)) continue;
|
|
63175
63468
|
let entries;
|
|
@@ -63221,10 +63514,10 @@ function argvWantsJson2() {
|
|
|
63221
63514
|
return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
|
|
63222
63515
|
}
|
|
63223
63516
|
function hubRoot() {
|
|
63224
|
-
const fromPkg = (0,
|
|
63517
|
+
const fromPkg = (0, import_node_path51.join)(__dirname, "..", "..");
|
|
63225
63518
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
63226
|
-
if ((0, import_node_fs54.existsSync)((0,
|
|
63227
|
-
if ((0, import_node_fs54.existsSync)((0,
|
|
63519
|
+
if ((0, import_node_fs54.existsSync)((0, import_node_path51.join)(fromPkg, marker))) return fromPkg;
|
|
63520
|
+
if ((0, import_node_fs54.existsSync)((0, import_node_path51.join)(process.cwd(), marker))) return process.cwd();
|
|
63228
63521
|
return null;
|
|
63229
63522
|
}
|
|
63230
63523
|
function ciAuditDeps() {
|
|
@@ -63236,7 +63529,7 @@ function ciAuditDeps() {
|
|
|
63236
63529
|
getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
|
|
63237
63530
|
readSeedFile: (path2) => {
|
|
63238
63531
|
if (!root) return null;
|
|
63239
|
-
const fullPath = (0,
|
|
63532
|
+
const fullPath = (0, import_node_path51.join)(root, path2);
|
|
63240
63533
|
return (0, import_node_fs54.existsSync)(fullPath) ? (0, import_node_fs54.readFileSync)(fullPath, "utf8") : null;
|
|
63241
63534
|
}
|
|
63242
63535
|
};
|
|
@@ -63276,8 +63569,10 @@ async function attachToProject(issueNumber, repo, priority) {
|
|
|
63276
63569
|
await setBoardItemPriority(defaultGitHubClient(), cfg, projectItemId2, priority);
|
|
63277
63570
|
} catch (e) {
|
|
63278
63571
|
const err = e;
|
|
63279
|
-
|
|
63572
|
+
const priorityError = (err.stderr || err.message || String(e)).trim();
|
|
63573
|
+
process.stderr.write(`warning: issue #${issueNumber} board Priority not set: ${priorityError}; issue and board attachment already exist; do not recreate the issue
|
|
63280
63574
|
`);
|
|
63575
|
+
return { projectItemId: projectItemId2, onBoard: true, credential: "app_installation", prioritySet: false, priorityError };
|
|
63281
63576
|
}
|
|
63282
63577
|
}
|
|
63283
63578
|
return { projectItemId: projectItemId2, onBoard: true, credential: "app_installation" };
|
|
@@ -63461,7 +63756,7 @@ function registerCollaborationCommands(program3) {
|
|
|
63461
63756
|
try {
|
|
63462
63757
|
title = await resolveIssueTitle(
|
|
63463
63758
|
{ title: opts.title, titleFile: opts.titleFile },
|
|
63464
|
-
{ readFile:
|
|
63759
|
+
{ readFile: import_promises9.readFile, readStdin }
|
|
63465
63760
|
);
|
|
63466
63761
|
} catch (e) {
|
|
63467
63762
|
return fail(
|
|
@@ -63515,8 +63810,8 @@ function registerCollaborationCommands(program3) {
|
|
|
63515
63810
|
let surfaceInferred;
|
|
63516
63811
|
try {
|
|
63517
63812
|
issueType = resolveCreateType(o.type, "issue create", o.label);
|
|
63518
|
-
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile:
|
|
63519
|
-
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
63813
|
+
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises9.readFile, readStdin });
|
|
63814
|
+
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises9.readFile, readStdin });
|
|
63520
63815
|
if (o.idempotencyKey) body = appendIdempotencyMarker(body, o.idempotencyKey);
|
|
63521
63816
|
priority = resolveCreatePriority(o.priority, "issue create");
|
|
63522
63817
|
extraLabels = [...o.label ?? []];
|
|
@@ -63627,12 +63922,13 @@ function registerCollaborationCommands(program3) {
|
|
|
63627
63922
|
`);
|
|
63628
63923
|
const linkRecovery = parentRecoveryRef !== void 0 && !parent ? `mmi-cli oracle issue link-child ${parentRecoveryRef} ${childRef}` : void 0;
|
|
63629
63924
|
console.log(JSON.stringify({
|
|
63630
|
-
status: boardRecovery || linkRecovery ? "partial" : "created",
|
|
63925
|
+
status: boardRecovery || linkRecovery || attached.prioritySet === false ? "partial" : "created",
|
|
63631
63926
|
...created,
|
|
63632
63927
|
label: issueType,
|
|
63633
63928
|
priority,
|
|
63634
63929
|
projectItemId,
|
|
63635
63930
|
onBoard,
|
|
63931
|
+
...attached.prioritySet === false ? { prioritySet: false, priorityError: attached.priorityError } : {},
|
|
63636
63932
|
...attachCredential ? { credential: attachCredential } : {},
|
|
63637
63933
|
// #5489: partial receipt when the GitHub issue landed but Project v2 attach hit GraphQL quota.
|
|
63638
63934
|
...!onBoard ? {
|
|
@@ -63831,7 +64127,7 @@ function registerCollaborationCommands(program3) {
|
|
|
63831
64127
|
}
|
|
63832
64128
|
let body;
|
|
63833
64129
|
try {
|
|
63834
|
-
body = o.edit !== void 0 ? "" : await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
64130
|
+
body = o.edit !== void 0 ? "" : await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises9.readFile, readStdin });
|
|
63835
64131
|
} catch (e) {
|
|
63836
64132
|
return fail(`issue comment: ${e.message}`);
|
|
63837
64133
|
}
|
|
@@ -63910,8 +64206,8 @@ ${list}`);
|
|
|
63910
64206
|
let title;
|
|
63911
64207
|
const sourceRepo = o.repo ?? await resolveRepo(void 0);
|
|
63912
64208
|
try {
|
|
63913
|
-
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile:
|
|
63914
|
-
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
64209
|
+
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises9.readFile, readStdin });
|
|
64210
|
+
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises9.readFile, readStdin });
|
|
63915
64211
|
priority = resolveCreatePriority(o.priority, "report");
|
|
63916
64212
|
if (!ISSUE_TYPES.includes(o.type)) {
|
|
63917
64213
|
throw new Error(`unknown issue type "${o.type}" \u2014 expected one of: ${ISSUE_TYPES.join(", ")}`);
|
|
@@ -63965,8 +64261,8 @@ ${list}`);
|
|
|
63965
64261
|
let args;
|
|
63966
64262
|
try {
|
|
63967
64263
|
skill = assertSkillName(o.skill);
|
|
63968
|
-
rawBody = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
63969
|
-
const rawTitle = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile:
|
|
64264
|
+
rawBody = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises9.readFile, readStdin });
|
|
64265
|
+
const rawTitle = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises9.readFile, readStdin });
|
|
63970
64266
|
title = buildSkillLessonTitle(skill, rawTitle);
|
|
63971
64267
|
priority = resolveCreatePriority(o.priority, "skill-lesson");
|
|
63972
64268
|
body = buildSkillLessonBody(rawBody, sourceRepo, pluginSha);
|
|
@@ -64019,16 +64315,17 @@ ${list}`);
|
|
|
64019
64315
|
}
|
|
64020
64316
|
}
|
|
64021
64317
|
const created = requireGhCreateOk(await ghCreate(args), "skill-lesson");
|
|
64022
|
-
const
|
|
64023
|
-
|
|
64318
|
+
const attached = await attachToProject(created.number, targetRepo2, priority);
|
|
64319
|
+
const { projectItemId, onBoard } = attached;
|
|
64320
|
+
console.log(JSON.stringify({ ...created, projectItemId, onBoard, ...attached.prioritySet === false ? { status: "partial", prioritySet: false, priorityError: attached.priorityError } : {} }));
|
|
64024
64321
|
});
|
|
64025
64322
|
const pr = program3.command("pr").description("pull requests \u2014 reliable create with structured output");
|
|
64026
64323
|
withExamples(pr.command("create").description("create a PR and print {number,url} JSON").option("--title <title>", "PR title").option("--title-file <path|->", "read the PR title from a UTF-8 file, or from stdin with -").option("--body <body>", "PR body (markdown)").option("--body-file <path|->", "read PR body from a UTF-8 file, or from stdin with -").option("--base <branch>", "base branch (defaults to the repo default)").option("--head <branch>", "head branch (defaults to the current branch)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--draft", "open the PR in draft state (#2667)").option("--no-zeroci", "compatibility option; pr create does not mint receipts").option("--json", "machine-readable output (default; accepted for parity)").action(async (o) => {
|
|
64027
64324
|
let body;
|
|
64028
64325
|
let title;
|
|
64029
64326
|
try {
|
|
64030
|
-
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile:
|
|
64031
|
-
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
64327
|
+
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises9.readFile, readStdin });
|
|
64328
|
+
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises9.readFile, readStdin });
|
|
64032
64329
|
} catch (e) {
|
|
64033
64330
|
return fail(`pr create: ${e.message}`, e instanceof TextArgError ? { code: e.code, offending_flag: e.offendingFlag } : void 0);
|
|
64034
64331
|
}
|
|
@@ -64122,11 +64419,11 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
64122
64419
|
}
|
|
64123
64420
|
});
|
|
64124
64421
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
64125
|
-
const wfDir = (0,
|
|
64422
|
+
const wfDir = (0, import_node_path51.join)(cwd, ".github", "workflows");
|
|
64126
64423
|
if (!(0, import_node_fs54.existsSync)(wfDir)) return [];
|
|
64127
64424
|
return (0, import_node_fs54.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
64128
64425
|
try {
|
|
64129
|
-
return workflowReportsPrChecks((0, import_node_fs54.readFileSync)((0,
|
|
64426
|
+
return workflowReportsPrChecks((0, import_node_fs54.readFileSync)((0, import_node_path51.join)(wfDir, name), "utf8"));
|
|
64130
64427
|
} catch {
|
|
64131
64428
|
return true;
|
|
64132
64429
|
}
|
|
@@ -64381,7 +64678,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
64381
64678
|
}
|
|
64382
64679
|
class PrHeadBehindBaseError extends Error {
|
|
64383
64680
|
}
|
|
64384
|
-
pr.command("land <numbers...>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 poll enqueued \u2014 development PRs only. Several PR numbers land together (#8322): all are enqueued, waited on together, a queue-ejected PR is updated and re-enqueued once, a queue merge is followed by worktree/branch cleanup and board Done, and one table (or --json) reports each outcome").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--enqueue-only", "return once queue membership is confirmed; without a merge queue, land normally").option("--abandon-admission", "one stuck-receipt recovery, one PR per call: journal the saved non-terminal (pending/enqueued) queue-admission receipt terminal ONLY after positively observing the PR OPEN at the receipt head and absent from the queue; any unprovable observation refuses and changes nothing (#9951). The next pr land then submits a new intent with no new head").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--force", "silence the leftover-scratch warning (scratch never blocks, #8785) and acknowledge a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").option("--without-review <reason>", "compatibility option; shared merge helpers do not require a review verdict").option("--timeout <minutes>", `merge-queue wait per PR (default ${PR_LAND_QUEUE_TIMEOUT_MS / 6e4} minutes, one window per ${PR_LAND_QUEUE_BATCH_SIZE} PRs \u2014 per-repo ruleset check_response_timeout_minutes is 15 default with 30/45/60 overrides in .github/rulesets/mmi-development-merge-queue.json; agents never edit the ruleset to shorten a queue wait)`).option("--preserve-worktree", "after a queue merge keep the local worktree and branch (skips the post-merge cleanup)").option("--expect-head <sha>", "pin the PR head to this full 40-hex SHA (#8456): checked before landing, after checks and by GitHub at enqueue (--match-head-commit); never updates a BEHIND head and never retries an ejection \u2014 it stops with pinnedHead.refused instead. One PR only").action(async (numbers, opts) => {
|
|
64681
|
+
pr.command("land <numbers...>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 poll enqueued \u2014 development PRs only. Several PR numbers land together (#8322): all are enqueued, waited on together, a queue-ejected PR is updated and re-enqueued once, a queue merge is followed by worktree/branch cleanup and board Done, and one table (or --json) reports each outcome").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--enqueue-only", "return once queue membership is confirmed; without a merge queue, land normally").option("--abandon-admission", "one stuck-receipt recovery, one PR per call: journal the saved non-terminal (pending/enqueued, or a dispatching send that saved no operation UUID after its sender died \u2014 #9995) queue-admission receipt terminal ONLY after positively observing the PR OPEN at the receipt head and absent from the queue; any unprovable observation refuses and changes nothing (#9951). The next pr land then submits a new intent with no new head").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--force", "silence the leftover-scratch warning (scratch never blocks, #8785) and acknowledge a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").option("--without-review <reason>", "compatibility option; shared merge helpers do not require a review verdict").option("--timeout <minutes>", `merge-queue wait per PR (default ${PR_LAND_QUEUE_TIMEOUT_MS / 6e4} minutes, one window per ${PR_LAND_QUEUE_BATCH_SIZE} PRs \u2014 per-repo ruleset check_response_timeout_minutes is 15 default with 30/45/60 overrides in .github/rulesets/mmi-development-merge-queue.json; agents never edit the ruleset to shorten a queue wait)`).option("--preserve-worktree", "after a queue merge keep the local worktree and branch (skips the post-merge cleanup)").option("--expect-head <sha>", "pin the PR head to this full 40-hex SHA (#8456): checked before landing, after checks and by GitHub at enqueue (--match-head-commit); never updates a BEHIND head and never retries an ejection \u2014 it stops with pinnedHead.refused instead. One PR only").action(async (numbers, opts) => {
|
|
64385
64682
|
const timeoutMinutes = opts.timeout === void 0 ? void 0 : Number(opts.timeout);
|
|
64386
64683
|
if (timeoutMinutes !== void 0 && !(timeoutMinutes > 0)) return fail("pr land: --timeout must be a positive number of minutes");
|
|
64387
64684
|
if (opts.abandonAdmission) {
|
|
@@ -64722,7 +65019,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
64722
65019
|
const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
|
|
64723
65020
|
let foreignCheckout;
|
|
64724
65021
|
if (foreignCwd) {
|
|
64725
|
-
const sibling = (0,
|
|
65022
|
+
const sibling = (0, import_node_path51.join)((0, import_node_path51.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
|
|
64726
65023
|
const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
|
|
64727
65024
|
if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
|
|
64728
65025
|
const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
|
|
@@ -64956,7 +65253,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
64956
65253
|
let residueSweep;
|
|
64957
65254
|
if (!(foreignCwd && !foreignCheckout)) {
|
|
64958
65255
|
try {
|
|
64959
|
-
gitCommonDir = (0,
|
|
65256
|
+
gitCommonDir = (0, import_node_path51.resolve)(primaryRoot, (await execFileP("git", ["-C", primaryRoot, "rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim());
|
|
64960
65257
|
residueSweep = await sweepWorktreeResidue(gitCommonDir, beforeWorktrees.map((w) => w.path));
|
|
64961
65258
|
} catch {
|
|
64962
65259
|
}
|
|
@@ -65176,7 +65473,7 @@ function trainGitArgs(args) {
|
|
|
65176
65473
|
async function announceSummaryLineCountRefusal(path2) {
|
|
65177
65474
|
let content;
|
|
65178
65475
|
try {
|
|
65179
|
-
content = await (0,
|
|
65476
|
+
content = await (0, import_promises10.readFile)(path2, "utf8");
|
|
65180
65477
|
} catch (e) {
|
|
65181
65478
|
return `release: could not read --announce-summary-file ${path2}: ${e.message} \u2014 see https://github.com/mutmutco/MMI-Hub/blob/development/docs/Guides/train-troubleshooting.md#announce-summary-missing`;
|
|
65182
65479
|
}
|
|
@@ -65188,7 +65485,7 @@ async function announceSummaryLineCountRefusal(path2) {
|
|
|
65188
65485
|
async function announceSummaryRefusal(path2, isHub = false, isWorkSuite = false) {
|
|
65189
65486
|
let lines2;
|
|
65190
65487
|
try {
|
|
65191
|
-
lines2 = summaryFileLines(await (0,
|
|
65488
|
+
lines2 = summaryFileLines(await (0, import_promises10.readFile)(path2, "utf8"));
|
|
65192
65489
|
} catch (e) {
|
|
65193
65490
|
return `release: could not read --announce-summary-file ${path2}: ${e.message} \u2014 see https://github.com/mutmutco/MMI-Hub/blob/development/docs/Guides/train-troubleshooting.md#announce-summary-missing`;
|
|
65194
65491
|
}
|
|
@@ -65326,8 +65623,8 @@ function trainApplyDeps() {
|
|
|
65326
65623
|
}
|
|
65327
65624
|
return body.status;
|
|
65328
65625
|
},
|
|
65329
|
-
readFile: (path2) => (0,
|
|
65330
|
-
removeFile: (path2) => (0,
|
|
65626
|
+
readFile: (path2) => (0, import_promises10.readFile)(path2, "utf8"),
|
|
65627
|
+
removeFile: (path2) => (0, import_promises10.unlink)(path2),
|
|
65331
65628
|
// #6521/#6523: the project's own release channel AND releaseLanguage, read from registry META in ONE
|
|
65332
65629
|
// checked read. Same shape as fetchEdgeDomains below: a FAILED read refuses by name so the announce
|
|
65333
65630
|
// note reports a failed read, never a project that configured nothing.
|
|
@@ -66255,7 +66552,7 @@ function rcRunCauseFromLog(log) {
|
|
|
66255
66552
|
function memberAnnounceSummaryFile(repo, summaryFile, memberSummaryFiles) {
|
|
66256
66553
|
return repo === SUITE_REPO ? summaryFile : memberSummaryFiles[repo];
|
|
66257
66554
|
}
|
|
66258
|
-
var suiteStateDir = (root) => (0,
|
|
66555
|
+
var suiteStateDir = (root) => (0, import_node_path53.join)(resolvePrimaryGitDir(root), "mmi-runtime", "suite");
|
|
66259
66556
|
async function runSuiteMemberRcand(repo, previousRc, rcWasAbsent) {
|
|
66260
66557
|
const deps = trainApplyDeps();
|
|
66261
66558
|
deps.suiteCut = suiteCutContext(repo, SUITE_REPO);
|
|
@@ -66302,16 +66599,16 @@ async function rcandInCheckout(repo, deps, previousRc, rcWasAbsent) {
|
|
|
66302
66599
|
}
|
|
66303
66600
|
}
|
|
66304
66601
|
function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr, memberSummaryFilesTr = {}) {
|
|
66305
|
-
const recordPath = (0,
|
|
66306
|
-
const base = (0,
|
|
66602
|
+
const recordPath = (0, import_node_path53.join)(suiteStateDir(root), "cut.json");
|
|
66603
|
+
const base = (0, import_node_path53.join)(suiteStateDir(root), "checkouts");
|
|
66307
66604
|
const checkout = async (repo) => {
|
|
66308
66605
|
if (repo === SUITE_REPO) return root;
|
|
66309
|
-
const target = (0,
|
|
66606
|
+
const target = (0, import_node_path53.join)(base, repo.split("/")[1]);
|
|
66310
66607
|
if (!(0, import_node_fs55.existsSync)(target)) {
|
|
66311
66608
|
(0, import_node_fs55.mkdirSync)(base, { recursive: true });
|
|
66312
66609
|
await exec("git", ["clone", "--quiet", "--branch", "development", `https://github.com/${repo}.git`, target], root, WATCH_TIMEOUT);
|
|
66313
66610
|
}
|
|
66314
|
-
if (!(0, import_node_fs55.existsSync)((0,
|
|
66611
|
+
if (!(0, import_node_fs55.existsSync)((0, import_node_path53.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
|
|
66315
66612
|
const origin = await exec("git", ["remote", "get-url", "origin"], target);
|
|
66316
66613
|
if (!origin.toLowerCase().endsWith(`/${repo.toLowerCase()}.git`) && !origin.toLowerCase().endsWith(`/${repo.toLowerCase()}`))
|
|
66317
66614
|
throw new Error(`${repo}: suite checkout origin mismatch at ${target}; stop`);
|
|
@@ -66375,7 +66672,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
66375
66672
|
if (repo === SUITE_REPO) return inTrain(repo, (deps) => rcandInCheckout(repo, deps, previousRc, rcWasAbsent));
|
|
66376
66673
|
const target = await checkout(repo);
|
|
66377
66674
|
return new Promise((resolveResult, reject2) => {
|
|
66378
|
-
const child2 = (0, import_node_child_process19.fork)((0,
|
|
66675
|
+
const child2 = (0, import_node_child_process19.fork)((0, import_node_path52.resolve)(process.argv[1]), ["devops", "suite", "member-rcand-internal", repo, previousRc ?? "", ...rcWasAbsent ? ["--rc-was-absent"] : []], {
|
|
66379
66676
|
cwd: target,
|
|
66380
66677
|
stdio: ["ignore", "inherit", "inherit", "ipc"],
|
|
66381
66678
|
execArgv: []
|
|
@@ -66619,7 +66916,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
66619
66916
|
if (parseSuiteMapping(body)) return;
|
|
66620
66917
|
const dir = suiteStateDir(root);
|
|
66621
66918
|
(0, import_node_fs55.mkdirSync)(dir, { recursive: true });
|
|
66622
|
-
const file = (0,
|
|
66919
|
+
const file = (0, import_node_path53.join)(dir, `notes-${leadTag}.md`);
|
|
66623
66920
|
(0, import_node_fs55.writeFileSync)(file, `${body}
|
|
66624
66921
|
|
|
66625
66922
|
${suiteMappingBlock(leadTag, mapping)}`);
|
|
@@ -66863,6 +67160,8 @@ function registerSuiteCommands(program3) {
|
|
|
66863
67160
|
const status = suiteStatusDeps(github, registry2);
|
|
66864
67161
|
const deps = {
|
|
66865
67162
|
status: () => readSuiteStatus(status),
|
|
67163
|
+
// #10020: a single-track member's one live stage is `main`; judge that row instead of the dev one.
|
|
67164
|
+
releaseTrack: status.releaseTrack,
|
|
66866
67165
|
commits: async (repo, limit) => (await github.rest("GET", `repos/${repo}/commits?sha=development&per_page=${limit}`) ?? []).map((c) => c.sha),
|
|
66867
67166
|
checkRuns: async (repo, sha) => (await github.rest("GET", `repos/${repo}/commits/${sha}/check-runs?per_page=100`))?.check_runs ?? [],
|
|
66868
67167
|
requiredChecks: async (repo) => {
|
|
@@ -66996,8 +67295,8 @@ function registerJevCommands(program3) {
|
|
|
66996
67295
|
// src/packages-adopt-export.ts
|
|
66997
67296
|
var import_node_child_process20 = require("node:child_process");
|
|
66998
67297
|
var import_node_fs57 = require("node:fs");
|
|
66999
|
-
var
|
|
67000
|
-
var
|
|
67298
|
+
var import_node_os26 = require("node:os");
|
|
67299
|
+
var import_node_path54 = require("node:path");
|
|
67001
67300
|
var import_node_util6 = require("node:util");
|
|
67002
67301
|
init_cli_shared();
|
|
67003
67302
|
|
|
@@ -67617,25 +67916,25 @@ function expectedFromRun(run, opts) {
|
|
|
67617
67916
|
}
|
|
67618
67917
|
async function adoptExport(opts, deps) {
|
|
67619
67918
|
if (!/^sha256:[0-9a-f]{64}$/.test(opts.digest)) throw new Error("--digest must be sha256:<64 hex>, from the export run summary");
|
|
67620
|
-
const packagePath = (0,
|
|
67621
|
-
const lockPath = (0,
|
|
67919
|
+
const packagePath = (0, import_node_path54.join)(opts.dir, "package.json");
|
|
67920
|
+
const lockPath = (0, import_node_path54.join)(opts.dir, "package-lock.json");
|
|
67622
67921
|
const packageText = (0, import_node_fs57.readFileSync)(packagePath, "utf8");
|
|
67623
67922
|
const lockText = (0, import_node_fs57.readFileSync)(lockPath, "utf8");
|
|
67624
|
-
const work = (0, import_node_fs57.mkdtempSync)((0,
|
|
67923
|
+
const work = (0, import_node_fs57.mkdtempSync)((0, import_node_path54.join)((0, import_node_os26.tmpdir)(), "mmi-adopt-export-"));
|
|
67625
67924
|
try {
|
|
67626
67925
|
const run = JSON.parse((await deps.ghApi(`repos/${EXPORT_REPO}/actions/runs/${opts.run}`)).toString("utf8"));
|
|
67627
|
-
(0, import_node_fs57.writeFileSync)((0,
|
|
67628
|
-
(0, import_node_fs57.writeFileSync)((0,
|
|
67629
|
-
(0, import_node_fs57.writeFileSync)((0,
|
|
67630
|
-
const script = (0,
|
|
67926
|
+
(0, import_node_fs57.writeFileSync)((0, import_node_path54.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
|
|
67927
|
+
(0, import_node_fs57.writeFileSync)((0, import_node_path54.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
|
|
67928
|
+
(0, import_node_fs57.writeFileSync)((0, import_node_path54.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
|
|
67929
|
+
const script = (0, import_node_path54.join)(work, "package_export.py");
|
|
67631
67930
|
(0, import_node_fs57.writeFileSync)(script, package_export_default);
|
|
67632
67931
|
await deps.python(script, ["verify", work, opts.ds, opts.screens]);
|
|
67633
|
-
const verified = (0,
|
|
67634
|
-
const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs57.readFileSync)((0,
|
|
67932
|
+
const verified = (0, import_node_path54.join)(work, "verified");
|
|
67933
|
+
const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs57.readFileSync)((0, import_node_path54.join)(verified, `${short3}.metadata.json`), "utf8"))]));
|
|
67635
67934
|
const plan = planAdoption(packageText, lockText, metadata);
|
|
67636
67935
|
for (const { name } of plan.result.adopted) {
|
|
67637
67936
|
const short3 = PACKAGES.find((p) => p.name === name).short;
|
|
67638
|
-
await deps.npm(["cache", "add", (0,
|
|
67937
|
+
await deps.npm(["cache", "add", (0, import_node_path54.join)(verified, `${short3}.tgz`)], opts.dir);
|
|
67639
67938
|
}
|
|
67640
67939
|
(0, import_node_fs57.writeFileSync)(packagePath, plan.packageText);
|
|
67641
67940
|
(0, import_node_fs57.writeFileSync)(lockPath, plan.lockText);
|
|
@@ -67825,8 +68124,8 @@ async function collectWaveStatus(deps) {
|
|
|
67825
68124
|
init_github_client();
|
|
67826
68125
|
init_cli_shared();
|
|
67827
68126
|
var import_node_fs59 = require("node:fs");
|
|
67828
|
-
var
|
|
67829
|
-
var
|
|
68127
|
+
var import_node_os27 = require("node:os");
|
|
68128
|
+
var import_node_path55 = require("node:path");
|
|
67830
68129
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
67831
68130
|
var defaultStatusDeps = {
|
|
67832
68131
|
repo: resolveRepo,
|
|
@@ -68045,10 +68344,10 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
68045
68344
|
else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
|
|
68046
68345
|
else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
|
|
68047
68346
|
}
|
|
68048
|
-
const home = (0,
|
|
68347
|
+
const home = (0, import_node_os27.homedir)();
|
|
68049
68348
|
const plugin = onboardPluginGate({
|
|
68050
|
-
readKnown: () => readFileSyncSafe((0,
|
|
68051
|
-
readSettings: () => readFileSyncSafe((0,
|
|
68349
|
+
readKnown: () => readFileSyncSafe((0, import_node_path55.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs59.readFileSync),
|
|
68350
|
+
readSettings: () => readFileSyncSafe((0, import_node_path55.join)(home, ".claude", "settings.json"), import_node_fs59.readFileSync)
|
|
68052
68351
|
});
|
|
68053
68352
|
return {
|
|
68054
68353
|
guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
|
|
@@ -70685,7 +70984,7 @@ function registerConnectionsOauth(program3, deps) {
|
|
|
70685
70984
|
}
|
|
70686
70985
|
|
|
70687
70986
|
// src/schedules-commands.ts
|
|
70688
|
-
var
|
|
70987
|
+
var import_promises11 = require("node:fs/promises");
|
|
70689
70988
|
init_clean_exit();
|
|
70690
70989
|
init_github_client();
|
|
70691
70990
|
init_cli_shared();
|
|
@@ -70865,9 +71164,9 @@ function registerSchedulesCommands(program3) {
|
|
|
70865
71164
|
await failGraceful("org schedules --doc: refusing to regenerate the doc from an incomplete read (see warnings above).");
|
|
70866
71165
|
return;
|
|
70867
71166
|
}
|
|
70868
|
-
const docText = await (0,
|
|
71167
|
+
const docText = await (0, import_promises11.readFile)(o.doc, "utf8");
|
|
70869
71168
|
const spliced = spliceDoc(docText, renderDocSection(entries, now.toISOString()));
|
|
70870
|
-
await (0,
|
|
71169
|
+
await (0, import_promises11.writeFile)(o.doc, spliced, "utf8");
|
|
70871
71170
|
reportParked(parkedLines);
|
|
70872
71171
|
reportDrift(drift);
|
|
70873
71172
|
console.log(`org schedules: wrote ${entries.length} entries into ${o.doc}`);
|
|
@@ -71180,7 +71479,7 @@ function registerSessionReport(program3) {
|
|
|
71180
71479
|
|
|
71181
71480
|
// src/stage-commands.ts
|
|
71182
71481
|
var import_node_fs60 = require("node:fs");
|
|
71183
|
-
var
|
|
71482
|
+
var import_node_path56 = require("node:path");
|
|
71184
71483
|
init_cli_shared();
|
|
71185
71484
|
init_clean_exit();
|
|
71186
71485
|
|
|
@@ -71428,8 +71727,8 @@ function registerStageCommands(program3) {
|
|
|
71428
71727
|
const repo = Array.isArray(project2?.repos) && typeof project2.repos[0] === "string" ? project2.repos[0] : slug;
|
|
71429
71728
|
const resolution = decideStage({
|
|
71430
71729
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
71431
|
-
hasCompose: (0, import_node_fs60.existsSync)((0,
|
|
71432
|
-
hasEnvExample: (0, import_node_fs60.existsSync)((0,
|
|
71730
|
+
hasCompose: (0, import_node_fs60.existsSync)((0, import_node_path56.join)(process.cwd(), "docker-compose.yml")),
|
|
71731
|
+
hasEnvExample: (0, import_node_fs60.existsSync)((0, import_node_path56.join)(process.cwd(), ".env.example")),
|
|
71433
71732
|
repo
|
|
71434
71733
|
});
|
|
71435
71734
|
if (resolution.source === "derived" && resolution.derived) {
|
|
@@ -71721,21 +72020,21 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
|
|
|
71721
72020
|
}
|
|
71722
72021
|
|
|
71723
72022
|
// src/tenant-artifact.ts
|
|
71724
|
-
var
|
|
72023
|
+
var import_node_crypto24 = require("node:crypto");
|
|
71725
72024
|
var import_node_fs61 = require("node:fs");
|
|
71726
|
-
var
|
|
71727
|
-
var
|
|
72025
|
+
var import_promises12 = require("node:fs/promises");
|
|
72026
|
+
var import_node_path57 = require("node:path");
|
|
71728
72027
|
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}$/;
|
|
71729
72028
|
var MAX_BYTES2 = 5 * 1024 * 1024 * 1024;
|
|
71730
72029
|
async function sha256File(path2) {
|
|
71731
|
-
const hash = (0,
|
|
72030
|
+
const hash = (0, import_node_crypto24.createHash)("sha256");
|
|
71732
72031
|
for await (const chunk of (0, import_node_fs61.createReadStream)(path2)) hash.update(chunk);
|
|
71733
72032
|
return hash.digest("hex");
|
|
71734
72033
|
}
|
|
71735
72034
|
async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
71736
72035
|
if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
|
|
71737
|
-
const path2 = (0,
|
|
71738
|
-
const info = await (0,
|
|
72036
|
+
const path2 = (0, import_node_path57.resolve)(inputPath);
|
|
72037
|
+
const info = await (0, import_promises12.stat)(path2);
|
|
71739
72038
|
if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
|
|
71740
72039
|
if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size > MAX_BYTES2) throw new Error(`tenant artifact put: file must be 1..${MAX_BYTES2} bytes`);
|
|
71741
72040
|
const sha2564 = await sha256File(path2);
|
|
@@ -72214,12 +72513,12 @@ async function resolveWhoami(deps) {
|
|
|
72214
72513
|
}
|
|
72215
72514
|
|
|
72216
72515
|
// src/command-register-developer.ts
|
|
72217
|
-
var
|
|
72516
|
+
var import_node_path64 = require("node:path");
|
|
72218
72517
|
|
|
72219
72518
|
// src/doc-refs-command-source.ts
|
|
72220
72519
|
var import_node_child_process21 = require("node:child_process");
|
|
72221
72520
|
var import_node_fs62 = require("node:fs");
|
|
72222
|
-
var
|
|
72521
|
+
var import_node_path58 = require("node:path");
|
|
72223
72522
|
var IN_PROCESS_SOURCE = "in-process";
|
|
72224
72523
|
var BUNDLE_MANIFEST_TIMEOUT_MS = 12e4;
|
|
72225
72524
|
var BUNDLE_MANIFEST_MAX_BUFFER = 64 * 1024 * 1024;
|
|
@@ -72241,7 +72540,7 @@ function inventoryFromManifest(manifest, source) {
|
|
|
72241
72540
|
function readBundleManifest(bundle, exec3 = import_node_child_process21.execFileSync) {
|
|
72242
72541
|
try {
|
|
72243
72542
|
const text = exec3(process.execPath, [bundle, "commands", "--all", "--json"], {
|
|
72244
|
-
cwd: (0,
|
|
72543
|
+
cwd: (0, import_node_path58.dirname)(bundle),
|
|
72245
72544
|
encoding: "utf8",
|
|
72246
72545
|
windowsHide: true,
|
|
72247
72546
|
timeout: BUNDLE_MANIFEST_TIMEOUT_MS,
|
|
@@ -72257,16 +72556,16 @@ function readBundleManifest(bundle, exec3 = import_node_child_process21.execFile
|
|
|
72257
72556
|
function isRunningBundle(bundle, argv1 = process.argv[1], platform2 = process.platform) {
|
|
72258
72557
|
if (!argv1) return false;
|
|
72259
72558
|
const norm3 = (path2) => {
|
|
72260
|
-
const absolute = (0,
|
|
72559
|
+
const absolute = (0, import_node_path58.resolve)(path2);
|
|
72261
72560
|
return platform2 === "win32" ? absolute.toLowerCase() : absolute;
|
|
72262
72561
|
};
|
|
72263
72562
|
const running = norm3(argv1);
|
|
72264
|
-
if (!["main.cjs", "index.cjs"].includes((0,
|
|
72265
|
-
return (0,
|
|
72563
|
+
if (!["main.cjs", "index.cjs"].includes((0, import_node_path58.basename)(running))) return false;
|
|
72564
|
+
return (0, import_node_path58.dirname)(running) === (0, import_node_path58.dirname)(norm3(bundle));
|
|
72266
72565
|
}
|
|
72267
72566
|
function nameUnknownCommandSources(findings, source, root) {
|
|
72268
|
-
const checkoutBundle = (0,
|
|
72269
|
-
const recovery = source !== IN_PROCESS_SOURCE && (0,
|
|
72567
|
+
const checkoutBundle = (0, import_node_path58.resolve)(root, "cli", "dist", "main.cjs");
|
|
72568
|
+
const recovery = source !== IN_PROCESS_SOURCE && (0, import_node_path58.resolve)(source) === checkoutBundle ? "; rebuild with `npm --prefix cli run build` and retry, or pass `--commands-from <bundle>`" : "";
|
|
72270
72569
|
for (const finding of findings) {
|
|
72271
72570
|
if (finding.kind !== "unknown-command") continue;
|
|
72272
72571
|
finding.commandSource = source;
|
|
@@ -72278,7 +72577,7 @@ function resolveCommandInventory(deps) {
|
|
|
72278
72577
|
const read = deps.read ?? ((bundle) => readBundleManifest(bundle));
|
|
72279
72578
|
const warnings = [];
|
|
72280
72579
|
if (deps.from) {
|
|
72281
|
-
const bundle = (0,
|
|
72580
|
+
const bundle = (0, import_node_path58.isAbsolute)(deps.from) ? deps.from : (0, import_node_path58.resolve)(deps.root, deps.from);
|
|
72282
72581
|
if (!exists(bundle)) {
|
|
72283
72582
|
throw new Error(`docs refs --commands-from: ${bundle} does not exist \u2014 name the CLI bundle to read commands from (usually cli/dist/main.cjs)`);
|
|
72284
72583
|
}
|
|
@@ -72288,7 +72587,7 @@ function resolveCommandInventory(deps) {
|
|
|
72288
72587
|
}
|
|
72289
72588
|
return { inventory: inventory2, warnings };
|
|
72290
72589
|
}
|
|
72291
|
-
const treeBundle = (0,
|
|
72590
|
+
const treeBundle = (0, import_node_path58.join)(deps.root, "cli", "dist", "main.cjs");
|
|
72292
72591
|
if (!exists(treeBundle)) {
|
|
72293
72592
|
return { inventory: deps.inProcess(), warnings };
|
|
72294
72593
|
}
|
|
@@ -72408,26 +72707,26 @@ async function executeWaveLand(plan, deps) {
|
|
|
72408
72707
|
|
|
72409
72708
|
// src/dist-drift.ts
|
|
72410
72709
|
var import_node_child_process22 = require("node:child_process");
|
|
72411
|
-
var
|
|
72710
|
+
var import_node_crypto26 = require("node:crypto");
|
|
72412
72711
|
var import_node_fs64 = require("node:fs");
|
|
72413
|
-
var
|
|
72414
|
-
var
|
|
72712
|
+
var import_node_os28 = require("node:os");
|
|
72713
|
+
var import_node_path60 = require("node:path");
|
|
72415
72714
|
|
|
72416
72715
|
// ../scripts/distribution-digest.mjs
|
|
72417
|
-
var
|
|
72716
|
+
var import_node_crypto25 = require("node:crypto");
|
|
72418
72717
|
var import_node_fs63 = require("node:fs");
|
|
72419
|
-
var
|
|
72718
|
+
var import_node_path59 = require("node:path");
|
|
72420
72719
|
var slash = (value) => value.replaceAll("\\", "/");
|
|
72421
72720
|
function repoPath(root, declaredPath, label) {
|
|
72422
|
-
const absoluteRoot = (0,
|
|
72423
|
-
const target = (0,
|
|
72424
|
-
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${
|
|
72721
|
+
const absoluteRoot = (0, import_node_path59.resolve)(root);
|
|
72722
|
+
const target = (0, import_node_path59.resolve)(root, declaredPath);
|
|
72723
|
+
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path59.sep}`)) {
|
|
72425
72724
|
throw new Error(`${label} ${declaredPath} escapes the repository root`);
|
|
72426
72725
|
}
|
|
72427
72726
|
return target;
|
|
72428
72727
|
}
|
|
72429
72728
|
function digestFiles(files) {
|
|
72430
|
-
const hash = (0,
|
|
72729
|
+
const hash = (0, import_node_crypto25.createHash)("sha256");
|
|
72431
72730
|
for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
|
|
72432
72731
|
const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs63.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs63.readFileSync)(file.absolute);
|
|
72433
72732
|
hash.update(file.relative, "utf8");
|
|
@@ -72458,7 +72757,7 @@ var DIST_ARTIFACTS = [
|
|
|
72458
72757
|
];
|
|
72459
72758
|
var BOM_DIST_TREE_ID = "mmi-cli-dist";
|
|
72460
72759
|
var ABSENT = "absent";
|
|
72461
|
-
var sha2563 = (bytes) => `sha256:${(0,
|
|
72760
|
+
var sha2563 = (bytes) => `sha256:${(0, import_node_crypto26.createHash)("sha256").update(bytes).digest("hex")}`;
|
|
72462
72761
|
function artifactDrift(path2, committedBytes, rebuiltBytes) {
|
|
72463
72762
|
const committed = committedBytes ? sha2563(committedBytes) : ABSENT;
|
|
72464
72763
|
const rebuiltExpected = rebuiltBytes ? sha2563(rebuiltBytes) : ABSENT;
|
|
@@ -72536,7 +72835,7 @@ function walkFiles(root) {
|
|
|
72536
72835
|
const files = [];
|
|
72537
72836
|
const walk2 = (directory) => {
|
|
72538
72837
|
for (const entry of (0, import_node_fs64.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
72539
|
-
const child2 = (0,
|
|
72838
|
+
const child2 = (0, import_node_path60.join)(directory, entry.name);
|
|
72540
72839
|
if (entry.isDirectory()) walk2(child2);
|
|
72541
72840
|
else files.push(child2);
|
|
72542
72841
|
}
|
|
@@ -72546,10 +72845,10 @@ function walkFiles(root) {
|
|
|
72546
72845
|
}
|
|
72547
72846
|
function bomPathFor(root) {
|
|
72548
72847
|
try {
|
|
72549
|
-
const registry2 = JSON.parse((0, import_node_fs64.readFileSync)((0,
|
|
72550
|
-
return (0,
|
|
72848
|
+
const registry2 = JSON.parse((0, import_node_fs64.readFileSync)((0, import_node_path60.join)(root, "surfaces.json"), "utf8"));
|
|
72849
|
+
return (0, import_node_path60.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
|
|
72551
72850
|
} catch {
|
|
72552
|
-
return (0,
|
|
72851
|
+
return (0, import_node_path60.join)(root, "distribution-bom.json");
|
|
72553
72852
|
}
|
|
72554
72853
|
}
|
|
72555
72854
|
function rebuildTo(packageRoot, outDir) {
|
|
@@ -72562,28 +72861,28 @@ function rebuildTo(packageRoot, outDir) {
|
|
|
72562
72861
|
});
|
|
72563
72862
|
}
|
|
72564
72863
|
function runDistStatus(root) {
|
|
72565
|
-
const stage = (0, import_node_fs64.mkdtempSync)((0,
|
|
72864
|
+
const stage = (0, import_node_fs64.mkdtempSync)((0, import_node_path60.join)((0, import_node_os28.tmpdir)(), "mmi-dist-drift-"));
|
|
72566
72865
|
let overlayCount = 0;
|
|
72567
72866
|
try {
|
|
72568
|
-
const cliOut = (0,
|
|
72569
|
-
const hubOut = (0,
|
|
72570
|
-
rebuildTo((0,
|
|
72571
|
-
rebuildTo((0,
|
|
72867
|
+
const cliOut = (0, import_node_path60.join)(stage, "cli-dist");
|
|
72868
|
+
const hubOut = (0, import_node_path60.join)(stage, "hub-dist");
|
|
72869
|
+
rebuildTo((0, import_node_path60.join)(root, "cli"), cliOut);
|
|
72870
|
+
rebuildTo((0, import_node_path60.join)(root, "updater"), hubOut);
|
|
72572
72871
|
const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
|
|
72573
72872
|
const rebuilt = (path2) => {
|
|
72574
72873
|
const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
|
|
72575
|
-
return spec ? readOrNull((0,
|
|
72874
|
+
return spec ? readOrNull((0, import_node_path60.join)(outDirFor(spec.packageDir), spec.output)) : null;
|
|
72576
72875
|
};
|
|
72577
|
-
const committed = (path2) => readOrNull((0,
|
|
72578
|
-
const tree = (path2) => readOrNull((0,
|
|
72579
|
-
const distRoot = (0,
|
|
72580
|
-
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0,
|
|
72876
|
+
const committed = (path2) => readOrNull((0, import_node_path60.join)(root, path2));
|
|
72877
|
+
const tree = (path2) => readOrNull((0, import_node_path60.join)(root, path2));
|
|
72878
|
+
const distRoot = (0, import_node_path60.join)(root, "cli", "dist");
|
|
72879
|
+
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path60.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
|
|
72581
72880
|
const bom = JSON.parse((0, import_node_fs64.readFileSync)(bomPathFor(root), "utf8"));
|
|
72582
72881
|
const digest = (entries) => {
|
|
72583
|
-
const overlay = (0,
|
|
72882
|
+
const overlay = (0, import_node_path60.join)(stage, `overlay-${overlayCount++}`);
|
|
72584
72883
|
for (const entry of entries) {
|
|
72585
|
-
const target = (0,
|
|
72586
|
-
(0, import_node_fs64.mkdirSync)((0,
|
|
72884
|
+
const target = (0, import_node_path60.join)(overlay, entry.path);
|
|
72885
|
+
(0, import_node_fs64.mkdirSync)((0, import_node_path60.dirname)(target), { recursive: true });
|
|
72587
72886
|
(0, import_node_fs64.writeFileSync)(target, entry.bytes);
|
|
72588
72887
|
}
|
|
72589
72888
|
return digestPackedFiles(overlay, entries.map((entry) => entry.path));
|
|
@@ -72595,7 +72894,7 @@ function runDistStatus(root) {
|
|
|
72595
72894
|
}
|
|
72596
72895
|
|
|
72597
72896
|
// src/doc-refs-live.ts
|
|
72598
|
-
var
|
|
72897
|
+
var import_node_path61 = require("node:path");
|
|
72599
72898
|
var concrete = (value) => !!value && !/[<>{}$]/.test(value);
|
|
72600
72899
|
var liveOption = (tokens2, flag) => {
|
|
72601
72900
|
const index = tokens2.indexOf(flag);
|
|
@@ -72668,7 +72967,7 @@ async function runLiveDocRefs(root, deps, live) {
|
|
|
72668
72967
|
const offline = runDocRefs(root, { ...deps, runbooksOnly: true });
|
|
72669
72968
|
const read = deps.readFile ?? readFileOrNull;
|
|
72670
72969
|
const docs = Object.fromEntries((deps.listDocs ?? defaultListDocs)(root).flatMap((doc) => {
|
|
72671
|
-
const body = read((0,
|
|
72970
|
+
const body = read((0, import_node_path61.join)(root, doc));
|
|
72672
72971
|
return body !== null && isRunbook(doc, body) && !deps.isIgnored?.([doc]).has(doc) ? [[doc, body]] : [];
|
|
72673
72972
|
}));
|
|
72674
72973
|
const remote = await checkRunbookLive(docs, live);
|
|
@@ -72717,13 +73016,13 @@ init_cli_shared();
|
|
|
72717
73016
|
// src/edge-tunnel.ts
|
|
72718
73017
|
var HOSTNAME_RE = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/;
|
|
72719
73018
|
var UPSTREAM_RE = /^https?:\/\/[^/\s]+(?::\d+)?(?:\/.*)?$/;
|
|
72720
|
-
function tunnelNameFromHostname(
|
|
72721
|
-
return
|
|
73019
|
+
function tunnelNameFromHostname(hostname5) {
|
|
73020
|
+
return hostname5.replace(/\./g, "-").slice(0, 63);
|
|
72722
73021
|
}
|
|
72723
|
-
function planInfraTunnel(
|
|
72724
|
-
const host =
|
|
73022
|
+
function planInfraTunnel(hostname5, upstream) {
|
|
73023
|
+
const host = hostname5.trim().toLowerCase();
|
|
72725
73024
|
const origin = upstream.trim();
|
|
72726
|
-
if (!HOSTNAME_RE.test(host)) throw new Error(`invalid hostname ${JSON.stringify(
|
|
73025
|
+
if (!HOSTNAME_RE.test(host)) throw new Error(`invalid hostname ${JSON.stringify(hostname5)}`);
|
|
72727
73026
|
if (!UPSTREAM_RE.test(origin)) throw new Error(`invalid upstream ${JSON.stringify(upstream)} \u2014 expected http(s)://host:port`);
|
|
72728
73027
|
const tunnelName = tunnelNameFromHostname(host);
|
|
72729
73028
|
const configYaml = [
|
|
@@ -72878,8 +73177,8 @@ async function diagnoseHub(deps) {
|
|
|
72878
73177
|
}
|
|
72879
73178
|
|
|
72880
73179
|
// src/schedules-lift-command.ts
|
|
72881
|
-
var
|
|
72882
|
-
var
|
|
73180
|
+
var import_promises13 = require("node:fs/promises");
|
|
73181
|
+
var import_node_path62 = require("node:path");
|
|
72883
73182
|
init_clean_exit();
|
|
72884
73183
|
init_cli_shared();
|
|
72885
73184
|
var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
|
|
@@ -72901,14 +73200,14 @@ var RegistryUnreachableError = class extends Error {
|
|
|
72901
73200
|
async function readWorkflowFiles(dir) {
|
|
72902
73201
|
let names;
|
|
72903
73202
|
try {
|
|
72904
|
-
names = await (0,
|
|
73203
|
+
names = await (0, import_promises13.readdir)(dir);
|
|
72905
73204
|
} catch {
|
|
72906
73205
|
return [];
|
|
72907
73206
|
}
|
|
72908
73207
|
const files = [];
|
|
72909
73208
|
for (const name of names.sort()) {
|
|
72910
73209
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
72911
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0,
|
|
73210
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises13.readFile)((0, import_node_path62.join)(dir, name), "utf8") });
|
|
72912
73211
|
}
|
|
72913
73212
|
return files;
|
|
72914
73213
|
}
|
|
@@ -72992,7 +73291,7 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
|
|
|
72992
73291
|
// src/spawn-policy-core.ts
|
|
72993
73292
|
var import_node_child_process24 = require("node:child_process");
|
|
72994
73293
|
var import_node_fs65 = require("node:fs");
|
|
72995
|
-
var
|
|
73294
|
+
var import_node_path63 = require("node:path");
|
|
72996
73295
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
72997
73296
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
72998
73297
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -73078,7 +73377,7 @@ function runSpawnPolicy(root) {
|
|
|
73078
73377
|
for (const file of files) {
|
|
73079
73378
|
let raw;
|
|
73080
73379
|
try {
|
|
73081
|
-
raw = (0, import_node_fs65.readFileSync)((0,
|
|
73380
|
+
raw = (0, import_node_fs65.readFileSync)((0, import_node_path63.join)(root, file), "utf8");
|
|
73082
73381
|
} catch {
|
|
73083
73382
|
continue;
|
|
73084
73383
|
}
|
|
@@ -73098,7 +73397,7 @@ function runSpawnPolicy(root) {
|
|
|
73098
73397
|
function registerDeveloperCommands(program3) {
|
|
73099
73398
|
const rules = program3.command("rules").description("org-managed .gitignore delivery");
|
|
73100
73399
|
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) => {
|
|
73101
|
-
const path2 = (0,
|
|
73400
|
+
const path2 = (0, import_node_path64.join)(process.cwd(), ".gitignore");
|
|
73102
73401
|
const current = (0, import_node_fs66.existsSync)(path2) ? (0, import_node_fs66.readFileSync)(path2, "utf8") : null;
|
|
73103
73402
|
const plan = planManagedGitignore(current);
|
|
73104
73403
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
@@ -73428,12 +73727,12 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
73428
73727
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
73429
73728
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
73430
73729
|
function envHealLockPath(home) {
|
|
73431
|
-
return (0,
|
|
73730
|
+
return (0, import_node_path65.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
73432
73731
|
}
|
|
73433
73732
|
async function withEnvHealLock(what, run) {
|
|
73434
73733
|
try {
|
|
73435
73734
|
return await withFileLock(
|
|
73436
|
-
envHealLockPath((0,
|
|
73735
|
+
envHealLockPath((0, import_node_os29.homedir)()),
|
|
73437
73736
|
{ staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
|
|
73438
73737
|
run
|
|
73439
73738
|
);
|
|
@@ -73531,7 +73830,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
73531
73830
|
const configRoot = surfaceConfigRoot(surface);
|
|
73532
73831
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
73533
73832
|
const plan = buildPluginCachePlan(
|
|
73534
|
-
(0,
|
|
73833
|
+
(0, import_node_os29.homedir)(),
|
|
73535
73834
|
running,
|
|
73536
73835
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
73537
73836
|
{ configRoot, includeStaging: surface !== "codex" }
|
|
@@ -73559,7 +73858,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
73559
73858
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
73560
73859
|
const installed = installedActivePluginVersion(surface);
|
|
73561
73860
|
const plan = buildPluginCachePlan(
|
|
73562
|
-
(0,
|
|
73861
|
+
(0, import_node_os29.homedir)(),
|
|
73563
73862
|
running,
|
|
73564
73863
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
73565
73864
|
{ configRoot, includeStaging: surface !== "codex", installedVersion: installed }
|
|
@@ -73601,7 +73900,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
73601
73900
|
// has no generated routing index would
|
|
73602
73901
|
// get a permanent — demanding an artifact it never asked for.
|
|
73603
73902
|
docsIndexState: (root) => {
|
|
73604
|
-
if (!(0, import_node_fs67.existsSync)((0,
|
|
73903
|
+
if (!(0, import_node_fs67.existsSync)((0, import_node_path65.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
73605
73904
|
const real = createDocsIndexDeps(root);
|
|
73606
73905
|
let docs;
|
|
73607
73906
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -73610,7 +73909,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
73610
73909
|
},
|
|
73611
73910
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
73612
73911
|
healDocsIndex: (root) => {
|
|
73613
|
-
if (!(0, import_node_fs67.existsSync)((0,
|
|
73912
|
+
if (!(0, import_node_fs67.existsSync)((0, import_node_path65.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
73614
73913
|
const real = createDocsIndexDeps(root);
|
|
73615
73914
|
let docs;
|
|
73616
73915
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -74841,16 +75140,16 @@ function ciAuditDeps2() {
|
|
|
74841
75140
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
74842
75141
|
readSeedFile: (path2) => {
|
|
74843
75142
|
if (!root) return null;
|
|
74844
|
-
const fullPath = (0,
|
|
75143
|
+
const fullPath = (0, import_node_path65.join)(root, path2);
|
|
74845
75144
|
return (0, import_node_fs67.existsSync)(fullPath) ? (0, import_node_fs67.readFileSync)(fullPath, "utf8") : null;
|
|
74846
75145
|
}
|
|
74847
75146
|
};
|
|
74848
75147
|
}
|
|
74849
75148
|
function hubRoot2() {
|
|
74850
|
-
const fromPkg = (0,
|
|
75149
|
+
const fromPkg = (0, import_node_path65.join)(__dirname, "..", "..");
|
|
74851
75150
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
74852
|
-
if ((0, import_node_fs67.existsSync)((0,
|
|
74853
|
-
if ((0, import_node_fs67.existsSync)((0,
|
|
75151
|
+
if ((0, import_node_fs67.existsSync)((0, import_node_path65.join)(fromPkg, marker))) return fromPkg;
|
|
75152
|
+
if ((0, import_node_fs67.existsSync)((0, import_node_path65.join)(process.cwd(), marker))) return process.cwd();
|
|
74854
75153
|
return null;
|
|
74855
75154
|
}
|
|
74856
75155
|
registerQueryCommands(program2);
|
|
@@ -75064,7 +75363,7 @@ function directoryBytes(path2) {
|
|
|
75064
75363
|
return 0;
|
|
75065
75364
|
}
|
|
75066
75365
|
for (const entry of entries) {
|
|
75067
|
-
const child2 = (0,
|
|
75366
|
+
const child2 = (0, import_node_path65.join)(path2, entry.name);
|
|
75068
75367
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
75069
75368
|
else {
|
|
75070
75369
|
try {
|
|
@@ -75094,7 +75393,7 @@ function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
|
75094
75393
|
dirBytes,
|
|
75095
75394
|
listStagingDirs: (root) => (0, import_node_fs67.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
75096
75395
|
try {
|
|
75097
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
75396
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path65.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs67.statSync)(p).mtimeMs) };
|
|
75098
75397
|
} catch {
|
|
75099
75398
|
return { name: d.name, mtimeMs: Date.now() };
|
|
75100
75399
|
}
|
|
@@ -75108,7 +75407,7 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
75108
75407
|
return {
|
|
75109
75408
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
75110
75409
|
mtimeMs: (name) => {
|
|
75111
|
-
const p = (0,
|
|
75410
|
+
const p = (0, import_node_path65.join)(stagingRoot, name);
|
|
75112
75411
|
if (!(0, import_node_fs67.existsSync)(p)) return null;
|
|
75113
75412
|
try {
|
|
75114
75413
|
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs67.statSync)(q).mtimeMs);
|
|
@@ -75131,7 +75430,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
75131
75430
|
return;
|
|
75132
75431
|
}
|
|
75133
75432
|
const plan = buildPluginCachePlan(
|
|
75134
|
-
(0,
|
|
75433
|
+
(0, import_node_os29.homedir)(),
|
|
75135
75434
|
running,
|
|
75136
75435
|
pluginCacheFsDeps(configRoot, directoryBytes),
|
|
75137
75436
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
@@ -75147,7 +75446,7 @@ program2.command("archive-prune").description("inventory worktree evidence archi
|
|
|
75147
75446
|
let primary = o.repoRoot;
|
|
75148
75447
|
if (!primary) {
|
|
75149
75448
|
const common = await gitOut(["rev-parse", "--path-format=absolute", "--git-common-dir"]);
|
|
75150
|
-
primary = common ? (0,
|
|
75449
|
+
primary = common ? (0, import_node_path65.dirname)(common) : "";
|
|
75151
75450
|
}
|
|
75152
75451
|
if (!primary) {
|
|
75153
75452
|
const detail = "archive-prune: not inside a git checkout (pass --repo-root <primary checkout>)";
|