@mutmutco/cli 4.5.117 → 4.5.119
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 +936 -732
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -1178,7 +1178,9 @@ function commandFromFailMessage(msg) {
|
|
|
1178
1178
|
const head = msg.split(":", 1)[0].trim();
|
|
1179
1179
|
if (!head || head.startsWith("-")) return "mmi-cli";
|
|
1180
1180
|
if (!/^[a-z][a-z0-9-]*(?: [a-z][a-z0-9-]*){0,5}$/.test(head)) return "mmi-cli";
|
|
1181
|
-
|
|
1181
|
+
const canonical = canonicalPathFor(head);
|
|
1182
|
+
if (canonical) return canonical;
|
|
1183
|
+
return HOUSE_ROOTS.includes(head.split(" ")[0]) ? head : "mmi-cli";
|
|
1182
1184
|
}
|
|
1183
1185
|
function canonicalFailMessage(msg) {
|
|
1184
1186
|
const colon = msg.indexOf(":");
|
|
@@ -4751,7 +4753,7 @@ function useColor() {
|
|
|
4751
4753
|
var program = new Command();
|
|
4752
4754
|
|
|
4753
4755
|
// src/command-composition.ts
|
|
4754
|
-
var
|
|
4756
|
+
var import_node_fs57 = require("node:fs");
|
|
4755
4757
|
init_clean_exit();
|
|
4756
4758
|
init_cli_shared();
|
|
4757
4759
|
|
|
@@ -5653,13 +5655,13 @@ function resolveVerbAliasShim(argv) {
|
|
|
5653
5655
|
}
|
|
5654
5656
|
|
|
5655
5657
|
// src/command-composition.ts
|
|
5656
|
-
var
|
|
5657
|
-
var
|
|
5658
|
+
var import_node_os25 = require("node:os");
|
|
5659
|
+
var import_node_path54 = require("node:path");
|
|
5658
5660
|
|
|
5659
5661
|
// src/board-read.ts
|
|
5660
|
-
var
|
|
5661
|
-
var
|
|
5662
|
-
var
|
|
5662
|
+
var import_node_fs14 = require("node:fs");
|
|
5663
|
+
var import_node_os10 = require("node:os");
|
|
5664
|
+
var import_node_path12 = require("node:path");
|
|
5663
5665
|
init_cli_shared();
|
|
5664
5666
|
init_github_client();
|
|
5665
5667
|
|
|
@@ -5667,21 +5669,35 @@ init_github_client();
|
|
|
5667
5669
|
init_fetch_retry();
|
|
5668
5670
|
init_client_version();
|
|
5669
5671
|
var BOARD_SNAPSHOT_TIMEOUT_MS = 25e3;
|
|
5672
|
+
var BOARD_SNAPSHOT_EXIT_CODES = {
|
|
5673
|
+
"hub-unreachable": 20,
|
|
5674
|
+
"app-quota-exhausted": 21,
|
|
5675
|
+
refused: 22
|
|
5676
|
+
};
|
|
5677
|
+
var BoardSnapshotFailure = class extends Error {
|
|
5678
|
+
constructor(receipt) {
|
|
5679
|
+
super(receipt.reason);
|
|
5680
|
+
this.receipt = receipt;
|
|
5681
|
+
this.exitCode = BOARD_SNAPSHOT_EXIT_CODES[receipt.outcome];
|
|
5682
|
+
}
|
|
5683
|
+
receipt;
|
|
5684
|
+
exitCode;
|
|
5685
|
+
};
|
|
5670
5686
|
function isSnapshotShape(body) {
|
|
5671
5687
|
const b = body;
|
|
5672
5688
|
const rate = b?.github?.rateLimit;
|
|
5673
5689
|
const staleValid = b?.stale === void 0 || typeof b.stale === "object" && b.stale !== null && Number.isFinite(b.stale.ageMs) && b.stale.ageMs >= 0;
|
|
5674
|
-
const githubValid = b?.github === void 0 || b.github.credential === "app_installation" && (rate === void 0 || Number.isFinite(rate?.limit) && Number.isFinite(rate?.remaining) && Number.isFinite(rate?.cost) && typeof rate?.resetAt === "string");
|
|
5690
|
+
const githubValid = b?.github === void 0 || b.github !== null && typeof b.github === "object" && b.github.credential === "app_installation" && (rate === void 0 || Number.isFinite(rate?.limit) && Number.isFinite(rate?.remaining) && Number.isFinite(rate?.cost) && typeof rate?.resetAt === "string");
|
|
5675
5691
|
return Boolean(
|
|
5676
5692
|
b && typeof b === "object" && typeof b.project?.id === "string" && typeof b.project?.title === "string" && typeof b.viewer === "string" && b.viewer.length > 0 && Array.isArray(b.nodes) && Array.isArray(b.writableRepos) && Array.isArray(b.unreadableRepos) && Array.isArray(b.pullRequests) && Array.isArray(b.warnings) && typeof b.partial === "boolean" && (b.source === void 0 || b.source === "live" || b.source === "cache") && (b.builtAt === void 0 || typeof b.builtAt === "string" && Number.isFinite(Date.parse(b.builtAt))) && (b.eventCursor === void 0 || Number.isInteger(b.eventCursor) && b.eventCursor >= 0) && githubValid && staleValid
|
|
5677
5693
|
);
|
|
5678
5694
|
}
|
|
5679
5695
|
async function fetchHubBoardSnapshot(request, deps) {
|
|
5680
5696
|
if (!deps.baseUrl) return { state: "unavailable", reason: "no Hub API URL (set MMI_HUB_URL or use a current MMI CLI/plugin build)" };
|
|
5681
|
-
const token = await deps.token();
|
|
5682
|
-
if (!token) return { state: "unavailable", reason: "no Hub session token (run `gh auth login`)" };
|
|
5683
5697
|
let res;
|
|
5684
5698
|
try {
|
|
5699
|
+
const token = await deps.token();
|
|
5700
|
+
if (!token) return { state: "unavailable", reason: "no Hub session token (run `gh auth login`)" };
|
|
5685
5701
|
res = await fetchWithRetry(
|
|
5686
5702
|
deps.fetch ?? fetch,
|
|
5687
5703
|
`${deps.baseUrl.replace(/\/$/, "")}/board/snapshot`,
|
|
@@ -5715,10 +5731,26 @@ async function fetchHubBoardSnapshot(request, deps) {
|
|
|
5715
5731
|
return {
|
|
5716
5732
|
state: "unavailable",
|
|
5717
5733
|
reason: `GitHub App quota exhausted${reset}`,
|
|
5734
|
+
appQuotaExhausted: true,
|
|
5735
|
+
pool: "graphql",
|
|
5736
|
+
...typeof body2.resetAt === "string" ? { resetAt: body2.resetAt } : {},
|
|
5718
5737
|
...body2.github?.credential === "app_installation" && body2.github.resource === "graphql" && /^github-app-v1:[a-f0-9]{64}$/.test(body2.github.quotaScope ?? "") ? { quota: { status: "rate_limited", ...typeof body2.resetAt === "string" ? { resetAt: body2.resetAt } : {}, github: { credential: "app_installation", resource: "graphql", quotaScope: body2.github.quotaScope } } } : {}
|
|
5719
5738
|
};
|
|
5720
5739
|
}
|
|
5721
5740
|
}
|
|
5741
|
+
if (res.status === 503) {
|
|
5742
|
+
const body2 = await res.json().catch(() => null);
|
|
5743
|
+
if (body2?.code === "github-rate-limited") {
|
|
5744
|
+
const reset = typeof body2.resetAt === "string" ? `; resets ${body2.resetAt}` : "; reset time unavailable";
|
|
5745
|
+
return {
|
|
5746
|
+
state: "unavailable",
|
|
5747
|
+
reason: `GitHub App allowance exhausted${reset}`,
|
|
5748
|
+
appQuotaExhausted: true,
|
|
5749
|
+
pool: "rest",
|
|
5750
|
+
...typeof body2.resetAt === "string" ? { resetAt: body2.resetAt } : {}
|
|
5751
|
+
};
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5722
5754
|
if (!res.ok) return { state: "unavailable", reason: `board snapshot HTTP ${res.status}` };
|
|
5723
5755
|
const body = await res.json().catch(() => null);
|
|
5724
5756
|
if (!isSnapshotShape(body)) return { state: "unavailable", reason: "malformed board snapshot response" };
|
|
@@ -5816,6 +5848,22 @@ var activeQuotaScope;
|
|
|
5816
5848
|
function activeAppQuotaScope() {
|
|
5817
5849
|
return activeQuotaScope;
|
|
5818
5850
|
}
|
|
5851
|
+
var activeToken;
|
|
5852
|
+
function activeAppToken() {
|
|
5853
|
+
return activeToken;
|
|
5854
|
+
}
|
|
5855
|
+
async function readAppVaultSecret(read) {
|
|
5856
|
+
try {
|
|
5857
|
+
return await read();
|
|
5858
|
+
} catch (e) {
|
|
5859
|
+
const reason = e.message;
|
|
5860
|
+
if (/^(Hub GitHub App allowance used up|rate limited)/.test(reason)) {
|
|
5861
|
+
throw new AppActorError(`app actor: the vault could not read the App credentials because GitHub is rate-limited, not a denial: ${reason}`, "mint-failed");
|
|
5862
|
+
}
|
|
5863
|
+
if (/^authority refused|\(HTTP 404\b/.test(reason)) return null;
|
|
5864
|
+
throw new AppActorError(`app actor: the vault read of the App credentials failed, not a denial: ${reason}`, "mint-failed");
|
|
5865
|
+
}
|
|
5866
|
+
}
|
|
5819
5867
|
async function mintInstallationToken(deps) {
|
|
5820
5868
|
const [appId, installationId, privateKey] = await Promise.all(APP_VAULT_KEYS.map((k) => deps.fetchSecret(k)));
|
|
5821
5869
|
if (!appId || !installationId || !privateKey) {
|
|
@@ -5875,6 +5923,7 @@ var notedIneligibleCommand = false;
|
|
|
5875
5923
|
async function activateAppActor(commandPath3, env, mint, note = (line) => process.stderr.write(`${line}
|
|
5876
5924
|
`)) {
|
|
5877
5925
|
activeQuotaScope = void 0;
|
|
5926
|
+
activeToken = void 0;
|
|
5878
5927
|
const requested = (env[APP_ACTOR_ENV] ?? "").trim();
|
|
5879
5928
|
if (!requested) return "personal";
|
|
5880
5929
|
if (requested !== "app") {
|
|
@@ -5892,6 +5941,7 @@ async function activateAppActor(commandPath3, env, mint, note = (line) => proces
|
|
|
5892
5941
|
const minted = await mint();
|
|
5893
5942
|
env.GH_TOKEN = minted.token;
|
|
5894
5943
|
activeQuotaScope = minted.quotaScope;
|
|
5944
|
+
activeToken = minted.token;
|
|
5895
5945
|
return "app";
|
|
5896
5946
|
}
|
|
5897
5947
|
|
|
@@ -6298,6 +6348,93 @@ function isReleaseShaDeployEnumerationCandidate(row, nonDeployEvents) {
|
|
|
6298
6348
|
|
|
6299
6349
|
// src/pr-checks-rest.ts
|
|
6300
6350
|
init_github_quota();
|
|
6351
|
+
|
|
6352
|
+
// src/rest-etag-cache.ts
|
|
6353
|
+
var import_node_crypto5 = require("node:crypto");
|
|
6354
|
+
var import_node_fs11 = require("node:fs");
|
|
6355
|
+
var import_node_os4 = require("node:os");
|
|
6356
|
+
var import_node_path9 = require("node:path");
|
|
6357
|
+
var MAX_AGE_MS = 24 * 60 * 6e4;
|
|
6358
|
+
var MAX_ENTRIES = 256;
|
|
6359
|
+
var MAX_BYTES = 16 * 1024 * 1024;
|
|
6360
|
+
var MAX_ENTRY_BYTES = 1024 * 1024;
|
|
6361
|
+
function restEtagCacheDir() {
|
|
6362
|
+
if (process.env.MMI_REST_ETAG_CACHE_DIR) return process.env.MMI_REST_ETAG_CACHE_DIR;
|
|
6363
|
+
return process.platform === "win32" ? (0, import_node_path9.join)(process.env.LOCALAPPDATA || (0, import_node_path9.join)((0, import_node_os4.homedir)(), "AppData", "Local"), "MMI Future", "mmi-cli", "rest-etags") : (0, import_node_path9.join)(process.env.XDG_CACHE_HOME || (0, import_node_path9.join)((0, import_node_os4.homedir)(), ".cache"), "mmi-cli", "rest-etags");
|
|
6364
|
+
}
|
|
6365
|
+
function entryPath(dir, key) {
|
|
6366
|
+
return (0, import_node_path9.join)(dir, `${(0, import_node_crypto5.createHash)("sha256").update(key).digest("hex")}.json`);
|
|
6367
|
+
}
|
|
6368
|
+
function readRestEtag(key, dir = restEtagCacheDir()) {
|
|
6369
|
+
if (dir === "off") return void 0;
|
|
6370
|
+
try {
|
|
6371
|
+
const path2 = entryPath(dir, key);
|
|
6372
|
+
if ((0, import_node_fs11.statSync)(path2).size > MAX_ENTRY_BYTES) return void 0;
|
|
6373
|
+
const entry = JSON.parse((0, import_node_fs11.readFileSync)(path2, "utf8"));
|
|
6374
|
+
const age = Date.now() - entry.storedAt;
|
|
6375
|
+
if (typeof entry.etag === "string" && entry.etag && !/[\r\n]/.test(entry.etag) && typeof entry.body === "string" && Number.isFinite(entry.storedAt) && age >= 0 && age <= MAX_AGE_MS) return entry;
|
|
6376
|
+
} catch {
|
|
6377
|
+
}
|
|
6378
|
+
return void 0;
|
|
6379
|
+
}
|
|
6380
|
+
function deleteRestEtag(key, dir = restEtagCacheDir()) {
|
|
6381
|
+
if (dir === "off") return;
|
|
6382
|
+
try {
|
|
6383
|
+
(0, import_node_fs11.rmSync)(entryPath(dir, key), { force: true });
|
|
6384
|
+
} catch {
|
|
6385
|
+
}
|
|
6386
|
+
}
|
|
6387
|
+
function prune(dir) {
|
|
6388
|
+
const now = Date.now();
|
|
6389
|
+
const files = (0, import_node_fs11.readdirSync)(dir).filter((name) => /^[a-f0-9]{64}\.json(?:\.tmp-.+)?$/.test(name)).flatMap((name) => {
|
|
6390
|
+
try {
|
|
6391
|
+
const path2 = (0, import_node_path9.join)(dir, name);
|
|
6392
|
+
const stat4 = (0, import_node_fs11.statSync)(path2);
|
|
6393
|
+
if (now - stat4.mtimeMs > MAX_AGE_MS) {
|
|
6394
|
+
(0, import_node_fs11.rmSync)(path2, { force: true });
|
|
6395
|
+
return [];
|
|
6396
|
+
}
|
|
6397
|
+
return name.endsWith(".json") ? [{ path: path2, size: stat4.size, at: stat4.mtimeMs }] : [];
|
|
6398
|
+
} catch {
|
|
6399
|
+
return [];
|
|
6400
|
+
}
|
|
6401
|
+
}).sort((a, b) => a.at - b.at);
|
|
6402
|
+
let bytes = files.reduce((sum, f) => sum + f.size, 0);
|
|
6403
|
+
let count = files.length;
|
|
6404
|
+
for (const file of files) {
|
|
6405
|
+
if (count <= MAX_ENTRIES && bytes <= MAX_BYTES) break;
|
|
6406
|
+
try {
|
|
6407
|
+
(0, import_node_fs11.rmSync)(file.path, { force: true });
|
|
6408
|
+
bytes -= file.size;
|
|
6409
|
+
count--;
|
|
6410
|
+
} catch {
|
|
6411
|
+
}
|
|
6412
|
+
}
|
|
6413
|
+
}
|
|
6414
|
+
function writeRestEtag(key, entry, dir = restEtagCacheDir()) {
|
|
6415
|
+
if (dir === "off") return;
|
|
6416
|
+
const path2 = entryPath(dir, key);
|
|
6417
|
+
const tmp = `${path2}.tmp-${process.pid}-${(0, import_node_crypto5.randomUUID)()}`;
|
|
6418
|
+
try {
|
|
6419
|
+
const data = JSON.stringify({ etag: entry.etag, body: entry.body, storedAt: Date.now() });
|
|
6420
|
+
if (Buffer.byteLength(data) > MAX_ENTRY_BYTES) {
|
|
6421
|
+
deleteRestEtag(key, dir);
|
|
6422
|
+
return;
|
|
6423
|
+
}
|
|
6424
|
+
(0, import_node_fs11.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
6425
|
+
(0, import_node_fs11.writeFileSync)(tmp, data, { mode: 384, flag: "wx" });
|
|
6426
|
+
(0, import_node_fs11.renameSync)(tmp, path2);
|
|
6427
|
+
prune(dir);
|
|
6428
|
+
} catch {
|
|
6429
|
+
} finally {
|
|
6430
|
+
try {
|
|
6431
|
+
(0, import_node_fs11.rmSync)(tmp, { force: true });
|
|
6432
|
+
} catch {
|
|
6433
|
+
}
|
|
6434
|
+
}
|
|
6435
|
+
}
|
|
6436
|
+
|
|
6437
|
+
// src/pr-checks-rest.ts
|
|
6301
6438
|
var REST_GH_TIMEOUT_MS = 2e4;
|
|
6302
6439
|
var pollTokenProvider;
|
|
6303
6440
|
var pollTokenOnce;
|
|
@@ -6382,8 +6519,9 @@ function observedCorePool(source, nowMs) {
|
|
|
6382
6519
|
async function defaultGhApi(args, env = process.env) {
|
|
6383
6520
|
const appToken = await pollToken();
|
|
6384
6521
|
const source = appToken ? "app" : "user";
|
|
6385
|
-
const key = `${source}\0${args.join("\0")}`;
|
|
6386
|
-
const cached = etagCache.get(key);
|
|
6522
|
+
const key = `${source}\0${env.GH_HOST || "github.com"}\0${args.join("\0")}`;
|
|
6523
|
+
const cached = etagCache.get(key) ?? readRestEtag(key);
|
|
6524
|
+
if (cached) etagCache.set(key, cached);
|
|
6387
6525
|
let stdout;
|
|
6388
6526
|
try {
|
|
6389
6527
|
({ stdout } = await execFileP("gh", ["api", "--include", ...cached ? ["-H", `If-None-Match: ${cached.etag}`] : [], ...args], {
|
|
@@ -6397,31 +6535,37 @@ async function defaultGhApi(args, env = process.env) {
|
|
|
6397
6535
|
} catch (e) {
|
|
6398
6536
|
const out = e.stdout;
|
|
6399
6537
|
const errOut = typeof out === "string" ? out : void 0;
|
|
6400
|
-
const
|
|
6538
|
+
const status2 = errOut !== void 0 ? parseIncludedOutput(errOut).status : void 0;
|
|
6401
6539
|
if (errOut !== void 0) notePoolHeaders(source, errOut, Date.now());
|
|
6402
|
-
if (cached &&
|
|
6540
|
+
if (cached && status2 === 304) {
|
|
6403
6541
|
lastPollSource = source;
|
|
6404
6542
|
return cached.body;
|
|
6405
6543
|
}
|
|
6406
|
-
if (
|
|
6544
|
+
if (status2 === 403 || status2 === 429) {
|
|
6407
6545
|
const path2 = args.filter((a) => !a.startsWith("-")).join(" ") || args.join(" ");
|
|
6408
6546
|
const refusal = errOut !== void 0 ? describeRefusal(errOut) : "no rate-limit headers or message";
|
|
6409
|
-
if (
|
|
6547
|
+
if (status2 === 403 && /Resource not accessible by integration/i.test(refusal)) {
|
|
6410
6548
|
throw new Error(
|
|
6411
6549
|
`poll read refused: App token lacks permission for ${path2} (HTTP 403, response: ${refusal}) \u2014 reads never fall back to the owner's token (#8030); add the permission to APP_POLL_PERMISSIONS / the App installation`
|
|
6412
6550
|
);
|
|
6413
6551
|
}
|
|
6414
6552
|
throw Object.assign(new Error(
|
|
6415
|
-
`poll read refused on the ${source} pool (HTTP ${
|
|
6416
|
-
), { status, stdout: errOut, stderr: e.stderr });
|
|
6553
|
+
`poll read refused on the ${source} pool (HTTP ${status2}, response: ${refusal}) \u2014 reads stay on the ${source === "app" ? "App" : "ambient identity"} (#8030); the wait re-arms after reset`
|
|
6554
|
+
), { status: status2, stdout: errOut, stderr: e.stderr });
|
|
6417
6555
|
}
|
|
6418
6556
|
throw e;
|
|
6419
6557
|
}
|
|
6420
|
-
const { body, etag, pages } = parseIncludedOutput(stdout);
|
|
6558
|
+
const { body, status, etag, pages } = parseIncludedOutput(stdout);
|
|
6421
6559
|
notePoolHeaders(source, stdout, Date.now());
|
|
6422
6560
|
lastPollSource = source;
|
|
6423
|
-
if (
|
|
6424
|
-
|
|
6561
|
+
if (cached && status === 304) return cached.body;
|
|
6562
|
+
if (status === 200 && etag && pages === 1) {
|
|
6563
|
+
etagCache.set(key, { etag, body });
|
|
6564
|
+
writeRestEtag(key, { etag, body });
|
|
6565
|
+
} else {
|
|
6566
|
+
etagCache.delete(key);
|
|
6567
|
+
deleteRestEtag(key);
|
|
6568
|
+
}
|
|
6425
6569
|
return body;
|
|
6426
6570
|
}
|
|
6427
6571
|
function classifyCheckRun(run) {
|
|
@@ -8526,12 +8670,12 @@ async function fetchSchedulesList(deps) {
|
|
|
8526
8670
|
return null;
|
|
8527
8671
|
}
|
|
8528
8672
|
}
|
|
8529
|
-
async function fetchOrgConfig(deps) {
|
|
8673
|
+
async function fetchOrgConfig(deps, spendMinutes) {
|
|
8530
8674
|
if (!deps.baseUrl) return null;
|
|
8531
8675
|
const token = await deps.token();
|
|
8532
8676
|
if (!token) return null;
|
|
8533
8677
|
try {
|
|
8534
|
-
const res = await retriedFetch(deps, `${deps.baseUrl.replace(/\/$/, "")}${ORG_CONFIG_PATH}`, {
|
|
8678
|
+
const res = await retriedFetch(deps, `${deps.baseUrl.replace(/\/$/, "")}${ORG_CONFIG_PATH}${spendMinutes === void 0 ? "" : `?github-spend-minutes=${spendMinutes}`}`, {
|
|
8535
8679
|
method: "GET",
|
|
8536
8680
|
headers: { Authorization: `Bearer ${token}` }
|
|
8537
8681
|
});
|
|
@@ -9765,14 +9909,14 @@ async function secretsPreflight(deps, opts) {
|
|
|
9765
9909
|
|
|
9766
9910
|
// src/secrets-execution.ts
|
|
9767
9911
|
var import_node_child_process4 = require("node:child_process");
|
|
9768
|
-
var
|
|
9912
|
+
var import_node_os6 = require("node:os");
|
|
9769
9913
|
init_clean_exit();
|
|
9770
9914
|
|
|
9771
9915
|
// src/gh-create.ts
|
|
9772
9916
|
var import_promises = require("node:fs/promises");
|
|
9773
|
-
var
|
|
9774
|
-
var
|
|
9775
|
-
var
|
|
9917
|
+
var import_node_os5 = require("node:os");
|
|
9918
|
+
var import_node_path10 = require("node:path");
|
|
9919
|
+
var import_node_crypto6 = require("node:crypto");
|
|
9776
9920
|
|
|
9777
9921
|
// src/board-priority.ts
|
|
9778
9922
|
var BOARD_PRIORITY_NAMES = ["Urgent", "High", "Medium", "Low"];
|
|
@@ -9858,9 +10002,9 @@ async function bodyArgsViaFile(args, deps = {}) {
|
|
|
9858
10002
|
const write = deps.write ?? import_promises.writeFile;
|
|
9859
10003
|
const remove2 = deps.remove ?? import_promises.unlink;
|
|
9860
10004
|
const ensureDir = deps.ensureDir ?? import_promises.mkdir;
|
|
9861
|
-
const dir = deps.dir ?? (0,
|
|
9862
|
-
const file = (0,
|
|
9863
|
-
await ensureDir((0,
|
|
10005
|
+
const dir = deps.dir ?? (0, import_node_os5.tmpdir)();
|
|
10006
|
+
const file = (0, import_node_path10.join)(dir, `mmi-gh-body-${process.pid}-${(0, import_node_crypto6.randomBytes)(4).toString("hex")}.md`);
|
|
10007
|
+
await ensureDir((0, import_node_path10.dirname)(file), { recursive: true }).catch(() => {
|
|
9864
10008
|
});
|
|
9865
10009
|
await write(file, args[i + 1], "utf8");
|
|
9866
10010
|
return {
|
|
@@ -10188,8 +10332,8 @@ async function defaultRestCreatePr(exec2, input, deps = {}) {
|
|
|
10188
10332
|
const write = deps.write ?? import_promises.writeFile;
|
|
10189
10333
|
const remove2 = deps.remove ?? import_promises.unlink;
|
|
10190
10334
|
const ensureDir = deps.ensureDir ?? import_promises.mkdir;
|
|
10191
|
-
const dir = deps.dir ?? (0,
|
|
10192
|
-
const file = (0,
|
|
10335
|
+
const dir = deps.dir ?? (0, import_node_os5.tmpdir)();
|
|
10336
|
+
const file = (0, import_node_path10.join)(dir, `mmi-rest-pr-${process.pid}-${(0, import_node_crypto6.randomBytes)(4).toString("hex")}.json`);
|
|
10193
10337
|
const payload = {
|
|
10194
10338
|
title: input.title,
|
|
10195
10339
|
head: input.head,
|
|
@@ -10197,7 +10341,7 @@ async function defaultRestCreatePr(exec2, input, deps = {}) {
|
|
|
10197
10341
|
};
|
|
10198
10342
|
if (input.base) payload.base = input.base;
|
|
10199
10343
|
if (input.draft) payload.draft = true;
|
|
10200
|
-
await ensureDir((0,
|
|
10344
|
+
await ensureDir((0, import_node_path10.dirname)(file), { recursive: true }).catch(() => {
|
|
10201
10345
|
});
|
|
10202
10346
|
await write(file, JSON.stringify(payload), "utf8");
|
|
10203
10347
|
const args = [
|
|
@@ -11133,7 +11277,7 @@ function resolveSpawnTarget(command, args, platform2 = process.platform) {
|
|
|
11133
11277
|
}
|
|
11134
11278
|
function spawnExitCode(status, signal) {
|
|
11135
11279
|
if (status != null) return status;
|
|
11136
|
-
const signo = signal ?
|
|
11280
|
+
const signo = signal ? import_node_os6.constants.signals[signal] : void 0;
|
|
11137
11281
|
return signo ? 128 + signo : 1;
|
|
11138
11282
|
}
|
|
11139
11283
|
function defaultSpawn(command, args, env) {
|
|
@@ -11589,14 +11733,14 @@ function findDuplicateLesson(source, openLessons) {
|
|
|
11589
11733
|
}
|
|
11590
11734
|
|
|
11591
11735
|
// src/session-identity.ts
|
|
11592
|
-
var
|
|
11593
|
-
var
|
|
11736
|
+
var import_node_crypto7 = require("node:crypto");
|
|
11737
|
+
var import_node_os9 = require("node:os");
|
|
11594
11738
|
|
|
11595
11739
|
// src/plugin-guard-io.ts
|
|
11596
|
-
var
|
|
11740
|
+
var import_node_fs13 = require("node:fs");
|
|
11597
11741
|
var import_node_child_process5 = require("node:child_process");
|
|
11598
|
-
var
|
|
11599
|
-
var
|
|
11742
|
+
var import_node_path11 = require("node:path");
|
|
11743
|
+
var import_node_os8 = require("node:os");
|
|
11600
11744
|
|
|
11601
11745
|
// src/marketplace-autoupdate.ts
|
|
11602
11746
|
var KNOWN_MARKETPLACES_RELATIVE = [".claude", "plugins", "known_marketplaces.json"];
|
|
@@ -11740,8 +11884,8 @@ function buildPluginGuardLine(state, opts = {}) {
|
|
|
11740
11884
|
}
|
|
11741
11885
|
|
|
11742
11886
|
// src/host-doc-lock.ts
|
|
11743
|
-
var
|
|
11744
|
-
var
|
|
11887
|
+
var import_node_fs12 = require("node:fs");
|
|
11888
|
+
var import_node_os7 = require("node:os");
|
|
11745
11889
|
var SHARED_DOC_LOCK_STALE_MS = 3e4;
|
|
11746
11890
|
var SHARED_DOC_LOCK_ATTEMPTS = 50;
|
|
11747
11891
|
var SHARED_DOC_LOCK_INTERVAL_MS = 100;
|
|
@@ -11754,7 +11898,7 @@ function lockContent(options) {
|
|
|
11754
11898
|
}
|
|
11755
11899
|
function readHolder(lockPath) {
|
|
11756
11900
|
try {
|
|
11757
|
-
const parsed = JSON.parse((0,
|
|
11901
|
+
const parsed = JSON.parse((0, import_node_fs12.readFileSync)(lockPath, "utf8"));
|
|
11758
11902
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
11759
11903
|
return parsed;
|
|
11760
11904
|
} catch {
|
|
@@ -11766,7 +11910,7 @@ function lockAgeMs(lockPath, now) {
|
|
|
11766
11910
|
const declared = typeof holder?.acquiredAt === "string" ? Date.parse(holder.acquiredAt) : Number.NaN;
|
|
11767
11911
|
if (!Number.isNaN(declared)) return Math.max(0, now - declared);
|
|
11768
11912
|
try {
|
|
11769
|
-
return Math.max(0, now - (0,
|
|
11913
|
+
return Math.max(0, now - (0, import_node_fs12.statSync)(lockPath).mtimeMs);
|
|
11770
11914
|
} catch {
|
|
11771
11915
|
return 0;
|
|
11772
11916
|
}
|
|
@@ -11781,7 +11925,7 @@ function acquireSharedDocLock(documentPath, options) {
|
|
|
11781
11925
|
const staleAfterMs = options.staleAfterMs ?? SHARED_DOC_LOCK_STALE_MS;
|
|
11782
11926
|
const attempts = options.attempts ?? SHARED_DOC_LOCK_ATTEMPTS;
|
|
11783
11927
|
const intervalMs = options.intervalMs ?? SHARED_DOC_LOCK_INTERVAL_MS;
|
|
11784
|
-
const identity = { owner: options.owner, pid: process.pid, host: options.host ?? (0,
|
|
11928
|
+
const identity = { owner: options.owner, pid: process.pid, host: options.host ?? (0, import_node_os7.hostname)(), acquiredAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
11785
11929
|
const content = lockContent(identity);
|
|
11786
11930
|
const sleep2 = (ms) => {
|
|
11787
11931
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
@@ -11789,9 +11933,9 @@ function acquireSharedDocLock(documentPath, options) {
|
|
|
11789
11933
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
11790
11934
|
let fd;
|
|
11791
11935
|
try {
|
|
11792
|
-
fd = (0,
|
|
11793
|
-
(0,
|
|
11794
|
-
(0,
|
|
11936
|
+
fd = (0, import_node_fs12.openSync)(lockPath, "wx");
|
|
11937
|
+
(0, import_node_fs12.writeSync)(fd, content);
|
|
11938
|
+
(0, import_node_fs12.closeSync)(fd);
|
|
11795
11939
|
return {
|
|
11796
11940
|
documentPath,
|
|
11797
11941
|
lockPath,
|
|
@@ -11804,11 +11948,11 @@ function acquireSharedDocLock(documentPath, options) {
|
|
|
11804
11948
|
} catch (error) {
|
|
11805
11949
|
if (fd !== void 0) {
|
|
11806
11950
|
try {
|
|
11807
|
-
(0,
|
|
11951
|
+
(0, import_node_fs12.closeSync)(fd);
|
|
11808
11952
|
} catch {
|
|
11809
11953
|
}
|
|
11810
11954
|
try {
|
|
11811
|
-
(0,
|
|
11955
|
+
(0, import_node_fs12.unlinkSync)(lockPath);
|
|
11812
11956
|
} catch {
|
|
11813
11957
|
}
|
|
11814
11958
|
}
|
|
@@ -11817,12 +11961,12 @@ function acquireSharedDocLock(documentPath, options) {
|
|
|
11817
11961
|
if (lockAgeMs(lockPath, Date.now()) >= staleAfterMs) {
|
|
11818
11962
|
const aside = `${lockPath}.stale-${Date.now()}`;
|
|
11819
11963
|
try {
|
|
11820
|
-
(0,
|
|
11964
|
+
(0, import_node_fs12.renameSync)(lockPath, aside);
|
|
11821
11965
|
} catch {
|
|
11822
11966
|
continue;
|
|
11823
11967
|
}
|
|
11824
11968
|
try {
|
|
11825
|
-
(0,
|
|
11969
|
+
(0, import_node_fs12.rmSync)(aside, { force: true });
|
|
11826
11970
|
} catch {
|
|
11827
11971
|
}
|
|
11828
11972
|
continue;
|
|
@@ -11835,7 +11979,7 @@ function releaseSharedDocLock(handle) {
|
|
|
11835
11979
|
try {
|
|
11836
11980
|
const holder = readHolder(handle.lockPath);
|
|
11837
11981
|
if (holder && holder.owner === handle.owner && Number(holder.pid) === handle.pid) {
|
|
11838
|
-
(0,
|
|
11982
|
+
(0, import_node_fs12.unlinkSync)(handle.lockPath);
|
|
11839
11983
|
}
|
|
11840
11984
|
} catch {
|
|
11841
11985
|
}
|
|
@@ -11982,25 +12126,25 @@ function runHostBin(bin, args, opts) {
|
|
|
11982
12126
|
const argv = isWin ? ["/c", bin, ...args] : args;
|
|
11983
12127
|
return step ? execFileHard(file, argv, { ...shared, step }) : execFileP(file, argv, shared);
|
|
11984
12128
|
}
|
|
11985
|
-
function hermesConfigRoot(env = process.env, home = (0,
|
|
12129
|
+
function hermesConfigRoot(env = process.env, home = (0, import_node_os8.homedir)(), platform2 = process.platform) {
|
|
11986
12130
|
if (env.HERMES_HOME?.trim()) return env.HERMES_HOME.trim();
|
|
11987
|
-
if (platform2 === "win32") return (0,
|
|
11988
|
-
return (0,
|
|
12131
|
+
if (platform2 === "win32") return (0, import_node_path11.join)(env.LOCALAPPDATA?.trim() || (0, import_node_path11.join)(home, "AppData", "Local"), "hermes");
|
|
12132
|
+
return (0, import_node_path11.join)(home, ".hermes");
|
|
11989
12133
|
}
|
|
11990
|
-
function surfaceConfigRoot(surface, env = process.env, home = (0,
|
|
11991
|
-
if (surface === "codex") return env.CODEX_HOME?.trim() || (0,
|
|
12134
|
+
function surfaceConfigRoot(surface, env = process.env, home = (0, import_node_os8.homedir)()) {
|
|
12135
|
+
if (surface === "codex") return env.CODEX_HOME?.trim() || (0, import_node_path11.join)(home, ".codex");
|
|
11992
12136
|
if (surface === "hermes") return hermesConfigRoot(env, home);
|
|
11993
12137
|
if (surface === "jervcode") {
|
|
11994
|
-
return env.JERVCODE_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim() || (0,
|
|
12138
|
+
return env.JERVCODE_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim() || (0, import_node_path11.join)(home, ".jerv", "agent");
|
|
11995
12139
|
}
|
|
11996
|
-
return (0,
|
|
12140
|
+
return (0, import_node_path11.join)(home, ".claude");
|
|
11997
12141
|
}
|
|
11998
12142
|
var installedPluginsPath = (surface = detectSurface(process.env)) => {
|
|
11999
|
-
return (0,
|
|
12143
|
+
return (0, import_node_path11.join)(surfaceConfigRoot(surface), "plugins", "installed_plugins.json");
|
|
12000
12144
|
};
|
|
12001
12145
|
function readInstalledPlugins(surface = detectSurface(process.env)) {
|
|
12002
12146
|
try {
|
|
12003
|
-
return JSON.parse((0,
|
|
12147
|
+
return JSON.parse((0, import_node_fs13.readFileSync)(installedPluginsPath(surface), "utf8"));
|
|
12004
12148
|
} catch {
|
|
12005
12149
|
return null;
|
|
12006
12150
|
}
|
|
@@ -12009,15 +12153,15 @@ function marketplaceCloneCandidates(surface, home, env = process.env) {
|
|
|
12009
12153
|
if (surface === "codex") {
|
|
12010
12154
|
const root = surfaceConfigRoot(surface, env, home);
|
|
12011
12155
|
return [
|
|
12012
|
-
(0,
|
|
12013
|
-
(0,
|
|
12156
|
+
(0, import_node_path11.join)(root, ".tmp", "marketplaces", CODEX_MARKETPLACE),
|
|
12157
|
+
(0, import_node_path11.join)(root, "plugins", "marketplaces", CODEX_MARKETPLACE)
|
|
12014
12158
|
];
|
|
12015
12159
|
}
|
|
12016
12160
|
if (surface === "hermes") return [];
|
|
12017
12161
|
if (surface === "jervcode") return [];
|
|
12018
|
-
return [(0,
|
|
12162
|
+
return [(0, import_node_path11.join)(home, ".claude", "plugins", "marketplaces", "mutmutco")];
|
|
12019
12163
|
}
|
|
12020
|
-
function marketplaceClonePresent(surface, home, exists =
|
|
12164
|
+
function marketplaceClonePresent(surface, home, exists = import_node_fs13.existsSync, env = process.env) {
|
|
12021
12165
|
return marketplaceCloneCandidates(surface, home, env).some(exists);
|
|
12022
12166
|
}
|
|
12023
12167
|
function runHostBinSync(bin, args) {
|
|
@@ -12059,26 +12203,26 @@ async function fetchNpmReleasedVersion() {
|
|
|
12059
12203
|
}
|
|
12060
12204
|
}
|
|
12061
12205
|
var JERVCODE_NPM_PACKAGE = "@mutmutco/pi-plugin";
|
|
12062
|
-
function jervcodeAgentDirs(env = process.env, home = (0,
|
|
12206
|
+
function jervcodeAgentDirs(env = process.env, home = (0, import_node_os8.homedir)()) {
|
|
12063
12207
|
const primary = surfaceConfigRoot("jervcode", env, home);
|
|
12064
|
-
const legacy = (0,
|
|
12208
|
+
const legacy = (0, import_node_path11.join)(home, ".pi", "agent");
|
|
12065
12209
|
const dirs = [primary];
|
|
12066
12210
|
if (legacy !== primary) dirs.push(legacy);
|
|
12067
|
-
return dirs.filter((dir) => (0,
|
|
12211
|
+
return dirs.filter((dir) => (0, import_node_fs13.existsSync)(dir));
|
|
12068
12212
|
}
|
|
12069
12213
|
function readPiSettings(path2) {
|
|
12070
|
-
if (!(0,
|
|
12214
|
+
if (!(0, import_node_fs13.existsSync)(path2)) return void 0;
|
|
12071
12215
|
try {
|
|
12072
|
-
const parsed = JSON.parse((0,
|
|
12216
|
+
const parsed = JSON.parse((0, import_node_fs13.readFileSync)(path2, "utf8"));
|
|
12073
12217
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
12074
12218
|
return parsed;
|
|
12075
12219
|
} catch {
|
|
12076
12220
|
return null;
|
|
12077
12221
|
}
|
|
12078
12222
|
}
|
|
12079
|
-
function mmiPiPackageEntry(env = process.env, home = (0,
|
|
12223
|
+
function mmiPiPackageEntry(env = process.env, home = (0, import_node_os8.homedir)()) {
|
|
12080
12224
|
for (const dir of jervcodeAgentDirs(env, home)) {
|
|
12081
|
-
const settings = readPiSettings((0,
|
|
12225
|
+
const settings = readPiSettings((0, import_node_path11.join)(dir, "settings.json"));
|
|
12082
12226
|
const entries = Array.isArray(settings?.packages) ? settings.packages : [];
|
|
12083
12227
|
for (const entry of entries) {
|
|
12084
12228
|
if (typeof entry !== "string") continue;
|
|
@@ -12088,10 +12232,10 @@ function mmiPiPackageEntry(env = process.env, home = (0, import_node_os7.homedir
|
|
|
12088
12232
|
return null;
|
|
12089
12233
|
}
|
|
12090
12234
|
var LEGACY_MMI_PI_PATH = /[/\\]mutmutco[/\\]mmi[/\\]\d+\.\d+\.\d+[/\\]\.pi-plugin[/\\]?$/i;
|
|
12091
|
-
function legacyMmiPiPathEntries(env = process.env, home = (0,
|
|
12235
|
+
function legacyMmiPiPathEntries(env = process.env, home = (0, import_node_os8.homedir)()) {
|
|
12092
12236
|
const found = [];
|
|
12093
12237
|
for (const dir of jervcodeAgentDirs(env, home)) {
|
|
12094
|
-
const settings = readPiSettings((0,
|
|
12238
|
+
const settings = readPiSettings((0, import_node_path11.join)(dir, "settings.json"));
|
|
12095
12239
|
const entries = Array.isArray(settings?.packages) ? settings.packages : [];
|
|
12096
12240
|
for (const entry of entries) {
|
|
12097
12241
|
if (typeof entry === "string" && LEGACY_MMI_PI_PATH.test(entry) && !found.includes(entry)) found.push(entry);
|
|
@@ -12099,37 +12243,37 @@ function legacyMmiPiPathEntries(env = process.env, home = (0, import_node_os7.ho
|
|
|
12099
12243
|
}
|
|
12100
12244
|
return found;
|
|
12101
12245
|
}
|
|
12102
|
-
function mmiPiPackageRoot(env = process.env, home = (0,
|
|
12246
|
+
function mmiPiPackageRoot(env = process.env, home = (0, import_node_os8.homedir)()) {
|
|
12103
12247
|
for (const dir of jervcodeAgentDirs(env, home)) {
|
|
12104
|
-
const root = (0,
|
|
12105
|
-
if ((0,
|
|
12248
|
+
const root = (0, import_node_path11.join)(dir, "npm", "node_modules", ...JERVCODE_NPM_PACKAGE.split("/"));
|
|
12249
|
+
if ((0, import_node_fs13.existsSync)((0, import_node_path11.join)(root, "package.json"))) return root;
|
|
12106
12250
|
}
|
|
12107
12251
|
return null;
|
|
12108
12252
|
}
|
|
12109
|
-
function installedJervCodePackageVersion(env = process.env, home = (0,
|
|
12253
|
+
function installedJervCodePackageVersion(env = process.env, home = (0, import_node_os8.homedir)()) {
|
|
12110
12254
|
const root = mmiPiPackageRoot(env, home);
|
|
12111
12255
|
if (!root) return void 0;
|
|
12112
12256
|
try {
|
|
12113
|
-
const manifest = JSON.parse((0,
|
|
12257
|
+
const manifest = JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path11.join)(root, "package.json"), "utf8"));
|
|
12114
12258
|
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
12115
12259
|
} catch {
|
|
12116
12260
|
return void 0;
|
|
12117
12261
|
}
|
|
12118
12262
|
}
|
|
12119
|
-
function mmiPiPackageHealthy(env = process.env, home = (0,
|
|
12263
|
+
function mmiPiPackageHealthy(env = process.env, home = (0, import_node_os8.homedir)()) {
|
|
12120
12264
|
const root = mmiPiPackageRoot(env, home);
|
|
12121
|
-
return Boolean(root && (0,
|
|
12265
|
+
return Boolean(root && (0, import_node_fs13.existsSync)((0, import_node_path11.join)(root, "skills", "bootstrap", "SKILL.md")));
|
|
12122
12266
|
}
|
|
12123
12267
|
function piDoctorEnv(agentDir) {
|
|
12124
12268
|
return { ...process.env, PI_CODING_AGENT_DIR: agentDir };
|
|
12125
12269
|
}
|
|
12126
|
-
function hermesPluginRoot(env = process.env, home = (0,
|
|
12127
|
-
return (0,
|
|
12270
|
+
function hermesPluginRoot(env = process.env, home = (0, import_node_os8.homedir)()) {
|
|
12271
|
+
return (0, import_node_path11.join)(hermesConfigRoot(env, home), "plugins", "mmi");
|
|
12128
12272
|
}
|
|
12129
|
-
function hermesPluginTreeHealthy(root, exists =
|
|
12130
|
-
if (!["plugin.yaml", "__init__.py"].every((file) => exists((0,
|
|
12273
|
+
function hermesPluginTreeHealthy(root, exists = import_node_fs13.existsSync) {
|
|
12274
|
+
if (!["plugin.yaml", "__init__.py"].every((file) => exists((0, import_node_path11.join)(root, file)))) return false;
|
|
12131
12275
|
try {
|
|
12132
|
-
return (0,
|
|
12276
|
+
return (0, import_node_fs13.statSync)((0, import_node_path11.join)(root, "skills")).isDirectory();
|
|
12133
12277
|
} catch {
|
|
12134
12278
|
return false;
|
|
12135
12279
|
}
|
|
@@ -12144,12 +12288,12 @@ function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRep
|
|
|
12144
12288
|
// Codex's native projection is authoritative; its legacy installed_plugins.json may be empty
|
|
12145
12289
|
// while `plugin list --json` proves the managed plugin installed (#6111).
|
|
12146
12290
|
installRecordPresent: surface === "codex" ? codexStatus?.installed === true : hasUserInstallRecord(installed, MMI_PLUGIN_ID) || hasProjectInstallRecord(installed, MMI_PLUGIN_ID, process.cwd()) || // #4188: jervcode's install record is the settings-file packages[] entry itself.
|
|
12147
|
-
surface === "jervcode" && piEntry !== null || surface === "hermes" && (0,
|
|
12291
|
+
surface === "jervcode" && piEntry !== null || surface === "hermes" && (0, import_node_fs13.existsSync)(hermesPluginRoot()),
|
|
12148
12292
|
// JervCode's settings entry and native local surfaces have no marketplace clone.
|
|
12149
|
-
marketplaceClonePresent: surface === "jervcode" || surface === "hermes" ? true : marketplaceClonePresent(surface, (0,
|
|
12293
|
+
marketplaceClonePresent: surface === "jervcode" || surface === "hermes" ? true : marketplaceClonePresent(surface, (0, import_node_os8.homedir)()),
|
|
12150
12294
|
pluginCachePresent: surface === "jervcode" ? mmiPiPackageHealthy() : surface === "hermes" ? hermesPluginTreeHealthy(hermesPluginRoot()) : surface === "codex" ? Boolean(
|
|
12151
|
-
codexStatus?.installed && codexStatus.enabled && codexStatus.version && (0,
|
|
12152
|
-
) : (0,
|
|
12295
|
+
codexStatus?.installed && codexStatus.enabled && codexStatus.version && (0, import_node_fs13.existsSync)((0, import_node_path11.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", codexStatus.version))
|
|
12296
|
+
) : (0, import_node_fs13.existsSync)((0, import_node_path11.join)(root, "plugins", "cache", "mutmutco", "mmi"))
|
|
12153
12297
|
};
|
|
12154
12298
|
}
|
|
12155
12299
|
async function runPluginCli(bin, args, log) {
|
|
@@ -12196,7 +12340,7 @@ async function runHealSteps(host, tableSteps, deps) {
|
|
|
12196
12340
|
const refSupported = needsRefProbe ? await marketplaceAddRefSupported(host) : true;
|
|
12197
12341
|
const { steps } = adaptHealStepsForRefSupport(tableSteps, refSupported);
|
|
12198
12342
|
if (deps.banner) log(deps.banner(refSupported));
|
|
12199
|
-
const pinsPath = (0,
|
|
12343
|
+
const pinsPath = (0, import_node_path11.join)((0, import_node_os8.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE);
|
|
12200
12344
|
const pins = host === "claude" ? captureMarketplacePins(readKnownMarketplacesFile(pinsPath), [MMI_MARKETPLACE_NAME, JERV_MARKETPLACE_NAME]) : /* @__PURE__ */ new Map();
|
|
12201
12345
|
let failure;
|
|
12202
12346
|
try {
|
|
@@ -12307,7 +12451,7 @@ async function healActivePluginForDoctor(surface = detectSurface(process.env), o
|
|
|
12307
12451
|
}
|
|
12308
12452
|
function readKnownMarketplacesFile(path2) {
|
|
12309
12453
|
try {
|
|
12310
|
-
return (0,
|
|
12454
|
+
return (0, import_node_fs13.existsSync)(path2) ? (0, import_node_fs13.readFileSync)(path2, "utf8") : void 0;
|
|
12311
12455
|
} catch {
|
|
12312
12456
|
return void 0;
|
|
12313
12457
|
}
|
|
@@ -12341,11 +12485,11 @@ function writeMarketplacePinsOnDisk(path2, pins, succeeded, failedVerb, declineW
|
|
|
12341
12485
|
if (declined) return declined;
|
|
12342
12486
|
const tmp = `${path2}.tmp-${process.pid}`;
|
|
12343
12487
|
try {
|
|
12344
|
-
(0,
|
|
12345
|
-
(0,
|
|
12488
|
+
(0, import_node_fs13.writeFileSync)(tmp, next, "utf8");
|
|
12489
|
+
(0, import_node_fs13.renameSync)(tmp, path2);
|
|
12346
12490
|
} catch {
|
|
12347
12491
|
try {
|
|
12348
|
-
(0,
|
|
12492
|
+
(0, import_node_fs13.rmSync)(tmp, { force: true });
|
|
12349
12493
|
} catch {
|
|
12350
12494
|
}
|
|
12351
12495
|
return `could NOT ${failedVerb} ${[...pins.keys()].join(", ")} \u2014 set it by hand`;
|
|
@@ -12473,7 +12617,7 @@ function readSessionIdSource(env) {
|
|
|
12473
12617
|
var synthesizedSessionId;
|
|
12474
12618
|
function fallbackSessionId(surface) {
|
|
12475
12619
|
if (!synthesizedSessionId) {
|
|
12476
|
-
synthesizedSessionId = `${SYNTH_SESSION_PREFIX}${surface}-${process.pid}-${(0,
|
|
12620
|
+
synthesizedSessionId = `${SYNTH_SESSION_PREFIX}${surface}-${process.pid}-${(0, import_node_crypto7.randomBytes)(3).toString("hex")}`;
|
|
12477
12621
|
}
|
|
12478
12622
|
return synthesizedSessionId;
|
|
12479
12623
|
}
|
|
@@ -12482,7 +12626,7 @@ function describeSessionIdentity(env = process.env) {
|
|
|
12482
12626
|
return {
|
|
12483
12627
|
session: readSessionId(env) ?? fallbackSessionId(surface),
|
|
12484
12628
|
surface,
|
|
12485
|
-
host: (0,
|
|
12629
|
+
host: (0, import_node_os9.hostname)()
|
|
12486
12630
|
};
|
|
12487
12631
|
}
|
|
12488
12632
|
function formatSessionIdentity(identity) {
|
|
@@ -13182,7 +13326,7 @@ function renderBoardSource() {
|
|
|
13182
13326
|
return "source: live";
|
|
13183
13327
|
}
|
|
13184
13328
|
async function collectBoardPreferringSnapshot(cfg, options, deps) {
|
|
13185
|
-
if (options.snapshotOnly && !deps.snapshot) throw new
|
|
13329
|
+
if (options.snapshotOnly && !deps.snapshot) throw new BoardSnapshotFailure({ ok: false, outcome: "hub-unreachable", reason: "--snapshot-only needs the Hub snapshot transport (not with --direct)" });
|
|
13186
13330
|
const attempt = deps.snapshot ? await fetchHubBoardSnapshot(
|
|
13187
13331
|
{
|
|
13188
13332
|
owner: cfg.projectOwner,
|
|
@@ -13195,6 +13339,7 @@ async function collectBoardPreferringSnapshot(cfg, options, deps) {
|
|
|
13195
13339
|
deps.snapshot
|
|
13196
13340
|
) : void 0;
|
|
13197
13341
|
if (attempt?.state === "refused") {
|
|
13342
|
+
if (options.snapshotOnly) throw new BoardSnapshotFailure({ ok: false, outcome: "refused", reason: attempt.reason });
|
|
13198
13343
|
throw new Error(attempt.reason);
|
|
13199
13344
|
}
|
|
13200
13345
|
if (attempt?.state === "ok") {
|
|
@@ -13224,7 +13369,16 @@ async function collectBoardPreferringSnapshot(cfg, options, deps) {
|
|
|
13224
13369
|
};
|
|
13225
13370
|
}
|
|
13226
13371
|
if (options.snapshotOnly && attempt?.state === "unavailable") {
|
|
13227
|
-
throw Object.assign(new
|
|
13372
|
+
throw Object.assign(new BoardSnapshotFailure({
|
|
13373
|
+
ok: false,
|
|
13374
|
+
outcome: attempt.appQuotaExhausted ? "app-quota-exhausted" : "hub-unreachable",
|
|
13375
|
+
reason: `Hub board snapshot unavailable (${attempt.reason}) \u2014 --snapshot-only refuses the direct user-auth fallback`,
|
|
13376
|
+
...attempt.resetAt ? { resetAt: attempt.resetAt } : {},
|
|
13377
|
+
...attempt.pool ? { pool: attempt.pool } : {}
|
|
13378
|
+
}), { quota: attempt.quota });
|
|
13379
|
+
}
|
|
13380
|
+
if (attempt?.state === "unavailable" && attempt.appQuotaExhausted) {
|
|
13381
|
+
throw Object.assign(new Error(`Hub board snapshot unavailable (${attempt.reason}) \u2014 the personal-token fallback is refused while the org App allowance is exhausted; retry after the reset`), { quota: attempt.quota });
|
|
13228
13382
|
}
|
|
13229
13383
|
const collected = await collectBoardItems(cfg, { repo: options.repo, allowPartial: options.allowPartial, activeOnly: true }, deps);
|
|
13230
13384
|
if (attempt?.state === "unavailable") {
|
|
@@ -13907,7 +14061,7 @@ var CLAIM_SESSION_ACTIVITY_MS = 30 * 6e4;
|
|
|
13907
14061
|
var CLAIM_SESSION_PROBE_CACHE_MS = 6e4;
|
|
13908
14062
|
var claimSessionProbeCache = /* @__PURE__ */ new Map();
|
|
13909
14063
|
function probeLocalClaimSession(marker, now = Date.now()) {
|
|
13910
|
-
if (!marker.session || !marker.host || marker.host.toLowerCase() !== (0,
|
|
14064
|
+
if (!marker.session || !marker.host || marker.host.toLowerCase() !== (0, import_node_os10.hostname)().toLowerCase()) return void 0;
|
|
13911
14065
|
if (!marker.surface?.toLowerCase().startsWith("claude")) return void 0;
|
|
13912
14066
|
const cacheKey = `${marker.host.toLowerCase()}/${marker.session}`;
|
|
13913
14067
|
const cached = claimSessionProbeCache.get(cacheKey);
|
|
@@ -13916,17 +14070,17 @@ function probeLocalClaimSession(marker, now = Date.now()) {
|
|
|
13916
14070
|
claimSessionProbeCache.set(cacheKey, { checkedAt: now, state });
|
|
13917
14071
|
return state;
|
|
13918
14072
|
};
|
|
13919
|
-
const root = (0,
|
|
14073
|
+
const root = (0, import_node_path12.join)((0, import_node_os10.homedir)(), ".claude", "projects");
|
|
13920
14074
|
try {
|
|
13921
14075
|
const wanted = `${marker.session}.jsonl`.toLowerCase();
|
|
13922
14076
|
const pending = [root];
|
|
13923
14077
|
while (pending.length) {
|
|
13924
14078
|
const dir = pending.pop();
|
|
13925
|
-
for (const entry of (0,
|
|
13926
|
-
const path2 = (0,
|
|
14079
|
+
for (const entry of (0, import_node_fs14.readdirSync)(dir, { withFileTypes: true })) {
|
|
14080
|
+
const path2 = (0, import_node_path12.join)(dir, entry.name);
|
|
13927
14081
|
if (entry.isDirectory()) pending.push(path2);
|
|
13928
14082
|
else if (entry.isFile() && entry.name.toLowerCase() === wanted) {
|
|
13929
|
-
return remember(now - (0,
|
|
14083
|
+
return remember(now - (0, import_node_fs14.statSync)(path2).mtimeMs <= CLAIM_SESSION_ACTIVITY_MS ? "live" : "dead");
|
|
13930
14084
|
}
|
|
13931
14085
|
}
|
|
13932
14086
|
}
|
|
@@ -15179,7 +15333,7 @@ async function propagateMayAutoMerge(repo, repoClass, rest) {
|
|
|
15179
15333
|
}
|
|
15180
15334
|
|
|
15181
15335
|
// src/bootstrap-apply.ts
|
|
15182
|
-
var
|
|
15336
|
+
var import_node_crypto8 = require("node:crypto");
|
|
15183
15337
|
|
|
15184
15338
|
// src/bootstrap-seeds.ts
|
|
15185
15339
|
var PLACEHOLDER_RE = /\{\{([A-Z0-9_]+)\}\}/g;
|
|
@@ -15418,13 +15572,13 @@ function planManagedGitignore(current) {
|
|
|
15418
15572
|
}
|
|
15419
15573
|
|
|
15420
15574
|
// src/docs-index-command.ts
|
|
15421
|
-
var
|
|
15422
|
-
var
|
|
15575
|
+
var import_node_fs16 = require("node:fs");
|
|
15576
|
+
var import_node_path14 = require("node:path");
|
|
15423
15577
|
|
|
15424
15578
|
// src/doc-refs-core.ts
|
|
15425
15579
|
var import_node_child_process6 = require("node:child_process");
|
|
15426
|
-
var
|
|
15427
|
-
var
|
|
15580
|
+
var import_node_fs15 = require("node:fs");
|
|
15581
|
+
var import_node_path13 = require("node:path");
|
|
15428
15582
|
var PIN_RE = /<!--\s*pinned by\s+([^:]+?)\s*:\s*"([^"]+)"[^"]*-->/;
|
|
15429
15583
|
var PIN_MENTION_RE = /<!--\s*pinned by\b/;
|
|
15430
15584
|
var FWD_RE = /<!--\s*forward-ref:\s*(\S+?)\s*-->/;
|
|
@@ -15483,7 +15637,7 @@ function checkPins(root, readFile10, docs) {
|
|
|
15483
15637
|
findings.push({ kind: "malformed-pin", doc, line: pin.line, detail: pin.text });
|
|
15484
15638
|
continue;
|
|
15485
15639
|
}
|
|
15486
|
-
const source = readFile10((0,
|
|
15640
|
+
const source = readFile10((0, import_node_path13.join)(root, pin.file));
|
|
15487
15641
|
if (source == null) {
|
|
15488
15642
|
findings.push({ kind: "missing-test", doc, line: pin.line, detail: pin.file });
|
|
15489
15643
|
continue;
|
|
@@ -15546,11 +15700,11 @@ function checkRefs(root, deps, docs) {
|
|
|
15546
15700
|
for (const { ref } of extractRefs(markdown)) allFirstSegments.add(refFirstSegment(ref));
|
|
15547
15701
|
}
|
|
15548
15702
|
const tracked = allFirstSegments.size ? deps.trackedFirstSegments?.([...allFirstSegments]) ?? null : null;
|
|
15549
|
-
const firstVerifiable = (first) => tracked ? tracked.has(first) : exists((0,
|
|
15703
|
+
const firstVerifiable = (first) => tracked ? tracked.has(first) : exists((0, import_node_path13.join)(root, first));
|
|
15550
15704
|
const candidates = [];
|
|
15551
15705
|
const direct = [];
|
|
15552
15706
|
for (const [doc, markdown] of Object.entries(docs)) {
|
|
15553
|
-
const docDir =
|
|
15707
|
+
const docDir = import_node_path13.posix.dirname(doc);
|
|
15554
15708
|
const base = docDir === "." ? "" : docDir;
|
|
15555
15709
|
const covered = /* @__PURE__ */ new Set();
|
|
15556
15710
|
const markers = [];
|
|
@@ -15559,21 +15713,21 @@ function checkRefs(root, deps, docs) {
|
|
|
15559
15713
|
direct.push({ kind: "malformed-forward-ref", doc, line: fwd.line, detail: fwd.text });
|
|
15560
15714
|
continue;
|
|
15561
15715
|
}
|
|
15562
|
-
const docRel =
|
|
15563
|
-
const rootRel =
|
|
15716
|
+
const docRel = import_node_path13.posix.normalize(import_node_path13.posix.join(base, fwd.target));
|
|
15717
|
+
const rootRel = import_node_path13.posix.normalize(fwd.target.replace(/^\/+/, ""));
|
|
15564
15718
|
markers.push({ target: fwd.target, line: fwd.line, docRel, rootRel });
|
|
15565
15719
|
covered.add(docRel);
|
|
15566
15720
|
covered.add(rootRel);
|
|
15567
15721
|
}
|
|
15568
15722
|
const links = extractLinks(markdown).map(({ target, line }) => {
|
|
15569
|
-
const resolved =
|
|
15570
|
-
return { target, line, resolved, missing: !exists((0,
|
|
15723
|
+
const resolved = import_node_path13.posix.normalize(import_node_path13.posix.join(base, target));
|
|
15724
|
+
return { target, line, resolved, missing: !exists((0, import_node_path13.join)(root, resolved)) };
|
|
15571
15725
|
});
|
|
15572
15726
|
for (const marker of markers) {
|
|
15573
15727
|
const coversMissing = links.some(
|
|
15574
15728
|
(l) => l.missing && (l.resolved === marker.docRel || l.resolved === marker.rootRel)
|
|
15575
15729
|
);
|
|
15576
|
-
if (!coversMissing && (exists((0,
|
|
15730
|
+
if (!coversMissing && (exists((0, import_node_path13.join)(root, marker.docRel)) || exists((0, import_node_path13.join)(root, marker.rootRel)))) {
|
|
15577
15731
|
direct.push({
|
|
15578
15732
|
kind: "stale-forward-ref",
|
|
15579
15733
|
doc,
|
|
@@ -15584,7 +15738,7 @@ function checkRefs(root, deps, docs) {
|
|
|
15584
15738
|
}
|
|
15585
15739
|
for (const { ref, line } of extractRefs(markdown)) {
|
|
15586
15740
|
if (!firstVerifiable(refFirstSegment(ref))) continue;
|
|
15587
|
-
if (!exists((0,
|
|
15741
|
+
if (!exists((0, import_node_path13.join)(root, ref))) candidates.push({ kind: "missing-path", doc, line, detail: ref });
|
|
15588
15742
|
}
|
|
15589
15743
|
for (const { target, line, resolved, missing } of links) {
|
|
15590
15744
|
if (resolved.startsWith("..")) {
|
|
@@ -15632,24 +15786,24 @@ function checkCommands(docs, commandPaths) {
|
|
|
15632
15786
|
return { ok: findings.length === 0, findings, warnings: [] };
|
|
15633
15787
|
}
|
|
15634
15788
|
function readFileOrNull(path2) {
|
|
15635
|
-
return (0,
|
|
15789
|
+
return (0, import_node_fs15.existsSync)(path2) ? (0, import_node_fs15.readFileSync)(path2, "utf8") : null;
|
|
15636
15790
|
}
|
|
15637
15791
|
function walk(dir, root, out) {
|
|
15638
|
-
for (const entry of (0,
|
|
15639
|
-
const full = (0,
|
|
15640
|
-
if ((0,
|
|
15792
|
+
for (const entry of (0, import_node_fs15.readdirSync)(dir)) {
|
|
15793
|
+
const full = (0, import_node_path13.join)(dir, entry);
|
|
15794
|
+
if ((0, import_node_fs15.statSync)(full).isDirectory()) walk(full, root, out);
|
|
15641
15795
|
else if (entry.endsWith(".md")) out.push(full.slice(root.length + 1).replaceAll("\\", "/"));
|
|
15642
15796
|
}
|
|
15643
15797
|
return out;
|
|
15644
15798
|
}
|
|
15645
15799
|
function defaultListDocs(root) {
|
|
15646
15800
|
const docs = ["docs", "skills/org-guide", "skills/mmi-help"].flatMap((path2) => {
|
|
15647
|
-
const directory = (0,
|
|
15648
|
-
return (0,
|
|
15801
|
+
const directory = (0, import_node_path13.join)(root, path2);
|
|
15802
|
+
return (0, import_node_fs15.existsSync)(directory) ? walk(directory, root, []) : [];
|
|
15649
15803
|
}).filter(
|
|
15650
15804
|
(rel) => !SKIP_WALK.some((skip) => rel.startsWith(skip))
|
|
15651
15805
|
);
|
|
15652
|
-
return [...ROOT_DOCS.filter((rel) => (0,
|
|
15806
|
+
return [...ROOT_DOCS.filter((rel) => (0, import_node_fs15.existsSync)((0, import_node_path13.join)(root, rel))), ...docs];
|
|
15653
15807
|
}
|
|
15654
15808
|
var CHECK_IGNORE_MAX_BUFFER = 32 * 1024 * 1024;
|
|
15655
15809
|
function defaultIsIgnored(root, relPaths, exec2 = import_node_child_process6.execFileSync) {
|
|
@@ -15701,7 +15855,7 @@ function defaultTrackedFirstSegments(root, firstSegments, exec2 = import_node_ch
|
|
|
15701
15855
|
}
|
|
15702
15856
|
function runDocRefs(root, deps = {}) {
|
|
15703
15857
|
const readFile10 = deps.readFile ?? readFileOrNull;
|
|
15704
|
-
const exists = deps.exists ??
|
|
15858
|
+
const exists = deps.exists ?? import_node_fs15.existsSync;
|
|
15705
15859
|
const listDocs = deps.listDocs ?? defaultListDocs;
|
|
15706
15860
|
const isIgnored = deps.isIgnored ?? ((paths) => defaultIsIgnored(root, paths));
|
|
15707
15861
|
const trackedFirstSegments = deps.trackedFirstSegments ?? ((segs) => defaultTrackedFirstSegments(root, segs));
|
|
@@ -15709,7 +15863,7 @@ function runDocRefs(root, deps = {}) {
|
|
|
15709
15863
|
const walked = listDocs(root);
|
|
15710
15864
|
const ignoredDocs = walked.length ? isIgnored(walked) : /* @__PURE__ */ new Set();
|
|
15711
15865
|
const docs = Object.fromEntries(
|
|
15712
|
-
walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile10((0,
|
|
15866
|
+
walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile10((0, import_node_path13.join)(root, rel))]).filter(([, body]) => body != null)
|
|
15713
15867
|
);
|
|
15714
15868
|
const refResult = checkRefs(root, { exists, isIgnored, trackedFirstSegments }, docs);
|
|
15715
15869
|
const findings = [
|
|
@@ -15816,31 +15970,31 @@ function walkMarkdown(dir) {
|
|
|
15816
15970
|
const stack = [dir];
|
|
15817
15971
|
while (stack.length) {
|
|
15818
15972
|
const current = stack.pop();
|
|
15819
|
-
for (const entry of (0,
|
|
15820
|
-
const full = (0,
|
|
15973
|
+
for (const entry of (0, import_node_fs16.readdirSync)(current, { withFileTypes: true })) {
|
|
15974
|
+
const full = (0, import_node_path14.join)(current, entry.name);
|
|
15821
15975
|
if (entry.isDirectory()) {
|
|
15822
15976
|
stack.push(full);
|
|
15823
15977
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
15824
|
-
out.push((0,
|
|
15978
|
+
out.push((0, import_node_path14.relative)(dir, full).split(import_node_path14.sep).join("/"));
|
|
15825
15979
|
}
|
|
15826
15980
|
}
|
|
15827
15981
|
}
|
|
15828
15982
|
return out;
|
|
15829
15983
|
}
|
|
15830
15984
|
function createDocsIndexDeps(repoRoot2) {
|
|
15831
|
-
const docsDir = (0,
|
|
15832
|
-
const indexPath = (0,
|
|
15985
|
+
const docsDir = (0, import_node_path14.join)(repoRoot2, "docs");
|
|
15986
|
+
const indexPath = (0, import_node_path14.join)(repoRoot2, DOCS_INDEX_PATH);
|
|
15833
15987
|
return {
|
|
15834
15988
|
listDocs: () => {
|
|
15835
|
-
if (!(0,
|
|
15989
|
+
if (!(0, import_node_fs16.existsSync)(docsDir)) return [];
|
|
15836
15990
|
const walked = walkMarkdown(docsDir).filter(isRoutableDocsPath);
|
|
15837
15991
|
if (!walked.length) return [];
|
|
15838
15992
|
const ignored = defaultIsIgnored(repoRoot2, walked.map((rel) => `docs/${rel}`));
|
|
15839
15993
|
return walked.filter((rel) => !ignored.has(`docs/${rel}`)).sort();
|
|
15840
15994
|
},
|
|
15841
|
-
readDoc: (relPath) => (0,
|
|
15842
|
-
readIndex: () => (0,
|
|
15843
|
-
writeIndex: (content) => (0,
|
|
15995
|
+
readDoc: (relPath) => (0, import_node_fs16.readFileSync)((0, import_node_path14.join)(docsDir, relPath), "utf8"),
|
|
15996
|
+
readIndex: () => (0, import_node_fs16.existsSync)(indexPath) ? (0, import_node_fs16.readFileSync)(indexPath, "utf8") : null,
|
|
15997
|
+
writeIndex: (content) => (0, import_node_fs16.writeFileSync)(indexPath, content, "utf8")
|
|
15844
15998
|
};
|
|
15845
15999
|
}
|
|
15846
16000
|
|
|
@@ -16446,7 +16600,7 @@ function planSeedAction(seed, exists) {
|
|
|
16446
16600
|
return exists ? { target: seed.target, action: "update", ownership: "org", reason: "org-owned, refresh to current" } : { target: seed.target, action: "create", ownership: "org", reason: "org-owned, missing" };
|
|
16447
16601
|
}
|
|
16448
16602
|
var GATE_RENDER_NOTE = "# Rendered from the registry gate config by mmi-cli devops bootstrap apply <repo> --only gate.yml --from-config; edit the config, not this file.\n";
|
|
16449
|
-
var gateHash = (body) => (0,
|
|
16603
|
+
var gateHash = (body) => (0, import_node_crypto8.createHash)("sha256").update(body, "utf8").digest("hex");
|
|
16450
16604
|
function parseGateRenderStamp(content) {
|
|
16451
16605
|
const match = /^# mmi-gate-render: sha256=([a-f0-9]{64})\n/.exec(content);
|
|
16452
16606
|
return match ? { hash: match[1], body: content.slice(match[0].length) } : null;
|
|
@@ -16753,8 +16907,8 @@ function contentPutInputArgs(repo, path2, inputFile) {
|
|
|
16753
16907
|
}
|
|
16754
16908
|
|
|
16755
16909
|
// src/train-apply-contracts.ts
|
|
16756
|
-
var
|
|
16757
|
-
var
|
|
16910
|
+
var import_node_fs17 = require("node:fs");
|
|
16911
|
+
var import_node_path15 = require("node:path");
|
|
16758
16912
|
function reduceFollowUpOutcomes(outcomes) {
|
|
16759
16913
|
if (outcomes.includes("failure")) return "failure";
|
|
16760
16914
|
if (outcomes.includes("unresolved")) return "unresolved";
|
|
@@ -16834,7 +16988,7 @@ async function resolveFoldPaths(deps, model) {
|
|
|
16834
16988
|
if (model === "vercel") return [];
|
|
16835
16989
|
const helper = "scripts/release-distribution.mjs";
|
|
16836
16990
|
if (model === "hub-serverless" || model === "registry-publish") {
|
|
16837
|
-
if ((0,
|
|
16991
|
+
if ((0, import_node_fs17.existsSync)(helper)) {
|
|
16838
16992
|
let out;
|
|
16839
16993
|
try {
|
|
16840
16994
|
out = await deps.run("node", [helper, "changed-files"]);
|
|
@@ -16973,7 +17127,7 @@ async function restoreMainNpmPackIdentities(deps, version) {
|
|
|
16973
17127
|
if (mainBom.version !== version) return void 0;
|
|
16974
17128
|
let localBom;
|
|
16975
17129
|
try {
|
|
16976
|
-
localBom = JSON.parse((0,
|
|
17130
|
+
localBom = JSON.parse((0, import_node_fs17.readFileSync)(bomPath, "utf8"));
|
|
16977
17131
|
} catch (e) {
|
|
16978
17132
|
throw new Error(
|
|
16979
17133
|
`version fold refused: ${bomPath} written by this fold's prepare is unreadable (${describeReadError(e)}) \u2014 cannot restore the published ${version} npm-pack identities from origin/main (#4503/#4713).`
|
|
@@ -16991,14 +17145,14 @@ async function restoreMainNpmPackIdentities(deps, version) {
|
|
|
16991
17145
|
restored += 1;
|
|
16992
17146
|
}
|
|
16993
17147
|
if (restored === 0) return void 0;
|
|
16994
|
-
(0,
|
|
17148
|
+
(0, import_node_fs17.writeFileSync)(bomPath, `${JSON.stringify(localBom, null, 2)}
|
|
16995
17149
|
`);
|
|
16996
17150
|
return `restored ${restored} npm-pack BOM identit${restored === 1 ? "y" : "ies"} from origin/main (#4503)`;
|
|
16997
17151
|
}
|
|
16998
17152
|
function publishVisibilityFor(surfaceId) {
|
|
16999
17153
|
let raw;
|
|
17000
17154
|
try {
|
|
17001
|
-
raw = (0,
|
|
17155
|
+
raw = (0, import_node_fs17.readFileSync)("surfaces.json", "utf8");
|
|
17002
17156
|
} catch (e) {
|
|
17003
17157
|
if (e.code === "ENOENT") return "unknown";
|
|
17004
17158
|
throw trainReadFailure(
|
|
@@ -17019,7 +17173,7 @@ function publishVisibilityFor(surfaceId) {
|
|
|
17019
17173
|
}
|
|
17020
17174
|
function npmPackArtifactName(packagePath) {
|
|
17021
17175
|
try {
|
|
17022
|
-
const pkg = JSON.parse((0,
|
|
17176
|
+
const pkg = JSON.parse((0, import_node_fs17.readFileSync)((0, import_node_path15.join)(packagePath, "package.json"), "utf8"));
|
|
17023
17177
|
return pkg.name || void 0;
|
|
17024
17178
|
} catch {
|
|
17025
17179
|
return void 0;
|
|
@@ -17028,7 +17182,7 @@ function npmPackArtifactName(packagePath) {
|
|
|
17028
17182
|
async function refuseDivergentPublishedNpmPack(deps, version) {
|
|
17029
17183
|
let localBom;
|
|
17030
17184
|
try {
|
|
17031
|
-
localBom = JSON.parse((0,
|
|
17185
|
+
localBom = JSON.parse((0, import_node_fs17.readFileSync)("distribution-bom.json", "utf8"));
|
|
17032
17186
|
} catch (e) {
|
|
17033
17187
|
throw new Error(
|
|
17034
17188
|
`version fold refused: distribution-bom.json written by this fold's prepare is unreadable (${e instanceof Error ? e.message.split("\n")[0] : String(e)}) \u2014 cannot compare the same-PATCH ${version} npm-pack identities against published npm (#4503/#4662).`
|
|
@@ -17173,8 +17327,8 @@ function assertGitBashOnWindowsForJervHubRelease(repo, platform2 = process.platf
|
|
|
17173
17327
|
}
|
|
17174
17328
|
|
|
17175
17329
|
// src/train-apply-correlation.ts
|
|
17176
|
-
var
|
|
17177
|
-
var
|
|
17330
|
+
var import_node_fs19 = require("node:fs");
|
|
17331
|
+
var import_node_path17 = require("node:path");
|
|
17178
17332
|
|
|
17179
17333
|
// src/fleet-lockstep-enforce.ts
|
|
17180
17334
|
init_github_client();
|
|
@@ -17939,10 +18093,10 @@ var rollout_plan_default = {
|
|
|
17939
18093
|
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)."
|
|
17940
18094
|
},
|
|
17941
18095
|
baseline: {
|
|
17942
|
-
version: "4.5.
|
|
17943
|
-
tag: "v4.5.
|
|
17944
|
-
commit: "
|
|
17945
|
-
npm: "@mutmutco/cli@4.5.
|
|
18096
|
+
version: "4.5.119",
|
|
18097
|
+
tag: "v4.5.119",
|
|
18098
|
+
commit: "1682b1bdb4b1",
|
|
18099
|
+
npm: "@mutmutco/cli@4.5.119"
|
|
17946
18100
|
},
|
|
17947
18101
|
exitCriterion: "fleet-n-of-n",
|
|
17948
18102
|
hubOnlyShortcut: "forbidden",
|
|
@@ -17959,14 +18113,14 @@ var rollout_plan_default = {
|
|
|
17959
18113
|
repo: "mutmutco/mmi-hub",
|
|
17960
18114
|
role: "canary",
|
|
17961
18115
|
schedule: "train",
|
|
17962
|
-
v3Target: "v4.5.
|
|
18116
|
+
v3Target: "v4.5.119"
|
|
17963
18117
|
}
|
|
17964
18118
|
],
|
|
17965
18119
|
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.",
|
|
17966
18120
|
rollback: {
|
|
17967
18121
|
independent: true,
|
|
17968
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
17969
|
-
v3Target: "v4.5.
|
|
18122
|
+
mechanism: "npm dist-tag latest -> 4.5.119 and redeploy the Hub Lambda from tag v4.5.119 (1682b1bdb4b1); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
18123
|
+
v3Target: "v4.5.119 (@mutmutco/cli@4.5.119, tag commit 1682b1bdb4b1 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
17970
18124
|
}
|
|
17971
18125
|
},
|
|
17972
18126
|
{
|
|
@@ -18300,13 +18454,13 @@ function resolveIssueViewGhFields(normalized) {
|
|
|
18300
18454
|
}
|
|
18301
18455
|
|
|
18302
18456
|
// src/utf8-receipt.ts
|
|
18303
|
-
var
|
|
18304
|
-
var
|
|
18457
|
+
var import_node_fs18 = require("node:fs");
|
|
18458
|
+
var import_node_path16 = require("node:path");
|
|
18305
18459
|
function writeUtf8Receipt(path2, text) {
|
|
18306
|
-
(0,
|
|
18460
|
+
(0, import_node_fs18.mkdirSync)((0, import_node_path16.dirname)(path2), { recursive: true });
|
|
18307
18461
|
const body = text.endsWith("\n") ? text : `${text}
|
|
18308
18462
|
`;
|
|
18309
|
-
(0,
|
|
18463
|
+
(0, import_node_fs18.writeFileSync)(path2, body, "utf8");
|
|
18310
18464
|
return Buffer.byteLength(body, "utf8");
|
|
18311
18465
|
}
|
|
18312
18466
|
|
|
@@ -20174,7 +20328,7 @@ function resolveReleaseAbortNpmPackage(meta, repo) {
|
|
|
20174
20328
|
if (isHubControlRepo(repo)) return "@mutmutco/cli";
|
|
20175
20329
|
const publishDir = typeof meta.publishDir === "string" && meta.publishDir.trim() ? meta.publishDir.trim() : ".";
|
|
20176
20330
|
try {
|
|
20177
|
-
const pkg = JSON.parse((0,
|
|
20331
|
+
const pkg = JSON.parse((0, import_node_fs19.readFileSync)((0, import_node_path17.join)(publishDir, "package.json"), "utf8"));
|
|
20178
20332
|
const name = pkg.name?.trim();
|
|
20179
20333
|
if (!name) throw new Error("missing name field");
|
|
20180
20334
|
return name;
|
|
@@ -21102,9 +21256,9 @@ function parseVerifyBroker(stdout) {
|
|
|
21102
21256
|
}
|
|
21103
21257
|
|
|
21104
21258
|
// src/train-apply-deploy.ts
|
|
21105
|
-
var
|
|
21106
|
-
var
|
|
21107
|
-
var
|
|
21259
|
+
var import_node_fs20 = require("node:fs");
|
|
21260
|
+
var import_node_os11 = require("node:os");
|
|
21261
|
+
var import_node_path18 = require("node:path");
|
|
21108
21262
|
|
|
21109
21263
|
// src/vercel-deployments.ts
|
|
21110
21264
|
var TERMINAL = /* @__PURE__ */ new Set(["success", "failure", "error", "inactive"]);
|
|
@@ -21243,7 +21397,7 @@ function mmiGetRedeployCommand(repo) {
|
|
|
21243
21397
|
}
|
|
21244
21398
|
function readLocalLauncherVersion(root = process.cwd()) {
|
|
21245
21399
|
try {
|
|
21246
|
-
const raw = (0,
|
|
21400
|
+
const raw = (0, import_node_fs20.readFileSync)((0, import_node_path18.join)(root, "cli", "package.json"), "utf8");
|
|
21247
21401
|
const version = JSON.parse(raw).version;
|
|
21248
21402
|
return typeof version === "string" && version.trim() ? version.trim() : null;
|
|
21249
21403
|
} catch {
|
|
@@ -21252,7 +21406,7 @@ function readLocalLauncherVersion(root = process.cwd()) {
|
|
|
21252
21406
|
}
|
|
21253
21407
|
function readServedChannelUrl(product = HUB_SERVED_PRODUCT, root = process.cwd()) {
|
|
21254
21408
|
try {
|
|
21255
|
-
const raw = (0,
|
|
21409
|
+
const raw = (0, import_node_fs20.readFileSync)((0, import_node_path18.join)(root, "config", `product.${product}.json`), "utf8");
|
|
21256
21410
|
const host = JSON.parse(raw).host;
|
|
21257
21411
|
if (typeof host !== "string" || !host.trim()) return null;
|
|
21258
21412
|
return `${host.trim().replace(/\/+$/, "")}/release/version`;
|
|
@@ -21686,13 +21840,13 @@ async function runTaggedJervGatewayLocalWrapper(deps, tag, tagSha) {
|
|
|
21686
21840
|
const reason = error instanceof Error ? error.message : String(error);
|
|
21687
21841
|
throw new Error(`could not extract ${JERV_GATEWAY_RELEASE_DEPLOY_SCRIPT} from tagged commit ${tagSha.slice(0, 12)}: ${reason}`);
|
|
21688
21842
|
}
|
|
21689
|
-
const dir = (0,
|
|
21690
|
-
const extracted = (0,
|
|
21843
|
+
const dir = (0, import_node_fs20.mkdtempSync)((0, import_node_path18.join)((0, import_node_os11.tmpdir)(), "jerv-gateway-release-deploy-"));
|
|
21844
|
+
const extracted = (0, import_node_path18.join)(dir, "jerv-gateway-release-deploy.sh");
|
|
21691
21845
|
try {
|
|
21692
|
-
(0,
|
|
21846
|
+
(0, import_node_fs20.writeFileSync)(extracted, script, "utf8");
|
|
21693
21847
|
return await deps.run("bash", [extracted, "--tag", tag]);
|
|
21694
21848
|
} finally {
|
|
21695
|
-
(0,
|
|
21849
|
+
(0, import_node_fs20.rmSync)(dir, { recursive: true, force: true });
|
|
21696
21850
|
}
|
|
21697
21851
|
}
|
|
21698
21852
|
async function appendJervGatewayReleaseDeploy(deps, repo, tag, tagSha, dispatch) {
|
|
@@ -22158,28 +22312,46 @@ async function runTenantControl(deps, options) {
|
|
|
22158
22312
|
const watch = options.watch ?? tenantControlWatches(action);
|
|
22159
22313
|
const base = { command: "tenant-control", repo, stage, action };
|
|
22160
22314
|
const since = (deps.now ?? Date.now)();
|
|
22161
|
-
|
|
22162
|
-
|
|
22163
|
-
|
|
22164
|
-
|
|
22165
|
-
|
|
22166
|
-
|
|
22167
|
-
|
|
22168
|
-
|
|
22169
|
-
|
|
22170
|
-
}
|
|
22171
|
-
|
|
22172
|
-
|
|
22173
|
-
|
|
22174
|
-
|
|
22175
|
-
|
|
22176
|
-
|
|
22177
|
-
|
|
22178
|
-
|
|
22179
|
-
|
|
22180
|
-
|
|
22315
|
+
let runId;
|
|
22316
|
+
let runUrl;
|
|
22317
|
+
if (options.resume != null) {
|
|
22318
|
+
if (action !== "run-task") throw new Error("runtime tenant control: --resume is valid only for run-task");
|
|
22319
|
+
let view;
|
|
22320
|
+
try {
|
|
22321
|
+
view = JSON.parse(await deps.run("gh", ["run", "view", String(options.resume), "--repo", HUB_REPO2, "--json", "workflowName,displayTitle,url"]));
|
|
22322
|
+
} catch (e) {
|
|
22323
|
+
throw new Error(`runtime tenant control: run ${options.resume} could not be READ: ${e.message}`);
|
|
22324
|
+
}
|
|
22325
|
+
const f = (view.displayTitle ?? "").split(" ");
|
|
22326
|
+
if (view.workflowName !== "tenant-control" || f[0] !== "tenant-control" || f[1] !== (repo.split("/")[1] ?? "").toLowerCase() || f[2] !== stage || f[3] !== "run-task") {
|
|
22327
|
+
throw new Error(`runtime tenant control: run ${options.resume} is not a tenant-control run-task run for ${repo} ${stage} (title: ${view.displayTitle ?? "unreadable"}) \u2014 refusing to resume it`);
|
|
22328
|
+
}
|
|
22329
|
+
runId = options.resume;
|
|
22330
|
+
runUrl = view.url;
|
|
22331
|
+
} else {
|
|
22332
|
+
const d = await deps.dispatchTenantControl({ repo, stage, action, lines: options.lines, edge: options.edge, since: options.logSince, until: options.logUntil, task: options.task, artifact: options.artifact, confirmDataLoss: options.confirmDataLoss });
|
|
22333
|
+
if (!d.ok) {
|
|
22334
|
+
const transport = d.category === "transport-failed";
|
|
22335
|
+
return {
|
|
22336
|
+
...base,
|
|
22337
|
+
dispatched: false,
|
|
22338
|
+
conclusion: "failure",
|
|
22339
|
+
category: action === "retire" ? transport ? "transport-failed" : "dispatch-rejected" : void 0,
|
|
22340
|
+
note: transport ? `runtime tenant control ${action} dispatch failed (transport) \u2014 safe to retry` : `runtime tenant control ${action} rejected: ${d.error ?? "request rejected by the Hub"}`
|
|
22341
|
+
};
|
|
22342
|
+
}
|
|
22343
|
+
const correlated = await correlateControlRun(deps, since, [stage, action, ...action === "run-task" && options.task ? [options.task] : []]);
|
|
22344
|
+
if (correlated.read === "failed") {
|
|
22345
|
+
return {
|
|
22346
|
+
...base,
|
|
22347
|
+
dispatched: true,
|
|
22348
|
+
conclusion: "failure",
|
|
22349
|
+
category: action === "retire" ? "wait-timeout" : void 0,
|
|
22350
|
+
note: `runtime tenant control ${action} dispatched, but its run could not be READ: ${correlated.error} \u2014 the run's outcome is unverified, not absent; check the Actions tab or retry once the read succeeds (#4713)`
|
|
22351
|
+
};
|
|
22352
|
+
}
|
|
22353
|
+
({ runId, runUrl } = correlated);
|
|
22181
22354
|
}
|
|
22182
|
-
const { runId, runUrl } = correlated;
|
|
22183
22355
|
const conclusion = watch ? await watchTenantRun(deps, runId) : "pending";
|
|
22184
22356
|
const result = { ...base, dispatched: true, runId, runUrl, conclusion, note: "" };
|
|
22185
22357
|
if (action === "retire") {
|
|
@@ -22213,6 +22385,9 @@ async function runTenantControl(deps, options) {
|
|
|
22213
22385
|
}
|
|
22214
22386
|
}
|
|
22215
22387
|
result.note = conclusion === "success" ? `tenant-control ${action} run succeeded` : conclusion === "failure" ? `tenant-control ${action} run failed \u2014 inspect the run` : runId == null ? `dispatched tenant-control.yml (${action}) \u2014 run not correlated; check the Actions tab` : `dispatched tenant-control.yml (${action}) \u2014 not watched`;
|
|
22388
|
+
if (conclusion === "pending" && runId != null && action === "run-task") {
|
|
22389
|
+
result.note += `; run ${runId} \u2014 resume without re-dispatching: mmi-cli devops runtime tenant control ${repo} ${stage} run-task --resume ${runId} --watch`;
|
|
22390
|
+
}
|
|
22216
22391
|
return result;
|
|
22217
22392
|
}
|
|
22218
22393
|
function renderTenantControl(r) {
|
|
@@ -22232,9 +22407,9 @@ function renderTenantControl(r) {
|
|
|
22232
22407
|
}
|
|
22233
22408
|
|
|
22234
22409
|
// src/train-apply-phases.ts
|
|
22235
|
-
var
|
|
22410
|
+
var import_node_fs26 = require("node:fs");
|
|
22236
22411
|
var import_promises4 = require("node:fs/promises");
|
|
22237
|
-
var
|
|
22412
|
+
var import_node_path25 = require("node:path");
|
|
22238
22413
|
|
|
22239
22414
|
// src/actions-billing-preflight.ts
|
|
22240
22415
|
var CANARY_WORKFLOW = "actions-job-start-canary.yml";
|
|
@@ -22542,14 +22717,14 @@ function assertPreOneZero(repo, meta, tag) {
|
|
|
22542
22717
|
|
|
22543
22718
|
// src/train-doctor.ts
|
|
22544
22719
|
var import_node_child_process9 = require("node:child_process");
|
|
22545
|
-
var
|
|
22546
|
-
var
|
|
22547
|
-
var
|
|
22720
|
+
var import_node_fs24 = require("node:fs");
|
|
22721
|
+
var import_node_os15 = require("node:os");
|
|
22722
|
+
var import_node_path24 = require("node:path");
|
|
22548
22723
|
|
|
22549
22724
|
// src/doctor-io.ts
|
|
22550
|
-
var
|
|
22551
|
-
var
|
|
22552
|
-
var
|
|
22725
|
+
var import_node_fs21 = require("node:fs");
|
|
22726
|
+
var import_node_os12 = require("node:os");
|
|
22727
|
+
var import_node_path19 = require("node:path");
|
|
22553
22728
|
var import_node_child_process7 = require("node:child_process");
|
|
22554
22729
|
var import_node_util4 = require("node:util");
|
|
22555
22730
|
|
|
@@ -22562,8 +22737,8 @@ function nodeDiscardSinkPath(platform2 = process.platform) {
|
|
|
22562
22737
|
var execFileP2 = (0, import_node_util4.promisify)(import_node_child_process7.execFile);
|
|
22563
22738
|
function execFileCapture(file, args, opts = {}) {
|
|
22564
22739
|
const sink = nodeDiscardSinkPath();
|
|
22565
|
-
const inFd = (0,
|
|
22566
|
-
const errFd = (0,
|
|
22740
|
+
const inFd = (0, import_node_fs21.openSync)(sink, "r");
|
|
22741
|
+
const errFd = (0, import_node_fs21.openSync)(sink, "w");
|
|
22567
22742
|
try {
|
|
22568
22743
|
return (0, import_node_child_process7.execFileSync)(file, args, {
|
|
22569
22744
|
...opts,
|
|
@@ -22571,17 +22746,17 @@ function execFileCapture(file, args, opts = {}) {
|
|
|
22571
22746
|
stdio: [inFd, "pipe", errFd]
|
|
22572
22747
|
});
|
|
22573
22748
|
} finally {
|
|
22574
|
-
(0,
|
|
22575
|
-
(0,
|
|
22749
|
+
(0, import_node_fs21.closeSync)(inFd);
|
|
22750
|
+
(0, import_node_fs21.closeSync)(errFd);
|
|
22576
22751
|
}
|
|
22577
22752
|
}
|
|
22578
22753
|
var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
22579
22754
|
function stagedClaudePluginVersion() {
|
|
22580
22755
|
try {
|
|
22581
|
-
const stagingRoot = (0,
|
|
22582
|
-
const record = JSON.parse((0,
|
|
22756
|
+
const stagingRoot = (0, import_node_path19.join)((0, import_node_os12.homedir)(), ".claude", "plugins", "staging");
|
|
22757
|
+
const record = JSON.parse((0, import_node_fs21.readFileSync)((0, import_node_path19.join)(stagingRoot, "launch-target.json"), "utf8"));
|
|
22583
22758
|
if (typeof record.target !== "string" || typeof record.incoming !== "string") return void 0;
|
|
22584
|
-
const manifest = JSON.parse((0,
|
|
22759
|
+
const manifest = JSON.parse((0, import_node_fs21.readFileSync)((0, import_node_path19.join)(record.incoming, ".claude-plugin", "plugin.json"), "utf8"));
|
|
22585
22760
|
if (typeof manifest.version !== "string") return void 0;
|
|
22586
22761
|
return manifest.version === record.target ? record.target : void 0;
|
|
22587
22762
|
} catch {
|
|
@@ -22591,7 +22766,7 @@ function stagedClaudePluginVersion() {
|
|
|
22591
22766
|
function installedClaudePluginVersion() {
|
|
22592
22767
|
try {
|
|
22593
22768
|
const file = JSON.parse(
|
|
22594
|
-
(0,
|
|
22769
|
+
(0, import_node_fs21.readFileSync)((0, import_node_path19.join)((0, import_node_os12.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
22595
22770
|
);
|
|
22596
22771
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
22597
22772
|
if (versions.length === 0) return void 0;
|
|
@@ -22603,12 +22778,12 @@ function installedClaudePluginVersion() {
|
|
|
22603
22778
|
function readHermesPluginEvidence(env = process.env) {
|
|
22604
22779
|
const host = hermesConfigRoot(env);
|
|
22605
22780
|
const root = hermesPluginRoot(env);
|
|
22606
|
-
const installRecordPresent = (0,
|
|
22607
|
-
const manifestPath = (0,
|
|
22781
|
+
const installRecordPresent = (0, import_node_fs21.existsSync)(root);
|
|
22782
|
+
const manifestPath = (0, import_node_path19.join)(root, "plugin.yaml");
|
|
22608
22783
|
let installedVersion;
|
|
22609
22784
|
let manifest = "missing";
|
|
22610
22785
|
try {
|
|
22611
|
-
const text = (0,
|
|
22786
|
+
const text = (0, import_node_fs21.readFileSync)(manifestPath, "utf8");
|
|
22612
22787
|
let version;
|
|
22613
22788
|
try {
|
|
22614
22789
|
const parsed = JSON.parse(text).version;
|
|
@@ -22622,25 +22797,25 @@ function readHermesPluginEvidence(env = process.env) {
|
|
|
22622
22797
|
if (version) {
|
|
22623
22798
|
installedVersion = version;
|
|
22624
22799
|
manifest = "valid";
|
|
22625
|
-
} else if ((0,
|
|
22800
|
+
} else if ((0, import_node_fs21.existsSync)(manifestPath)) manifest = "invalid";
|
|
22626
22801
|
} catch {
|
|
22627
|
-
if ((0,
|
|
22802
|
+
if ((0, import_node_fs21.existsSync)(manifestPath)) manifest = "invalid";
|
|
22628
22803
|
}
|
|
22629
22804
|
let skills = false;
|
|
22630
22805
|
try {
|
|
22631
|
-
skills = (0,
|
|
22806
|
+
skills = (0, import_node_fs21.existsSync)((0, import_node_path19.join)(root, "skills")) && (0, import_node_fs21.statSync)((0, import_node_path19.join)(root, "skills")).isDirectory();
|
|
22632
22807
|
} catch {
|
|
22633
22808
|
}
|
|
22634
22809
|
let provisioned = false;
|
|
22635
|
-
const provisionedRoot = (0,
|
|
22810
|
+
const provisionedRoot = (0, import_node_path19.join)(host, "skills", "mmi");
|
|
22636
22811
|
try {
|
|
22637
|
-
provisioned = (0,
|
|
22812
|
+
provisioned = (0, import_node_fs21.existsSync)(provisionedRoot) && (0, import_node_fs21.statSync)(provisionedRoot).isDirectory();
|
|
22638
22813
|
} catch {
|
|
22639
22814
|
}
|
|
22640
|
-
const treeOk = (0,
|
|
22815
|
+
const treeOk = (0, import_node_fs21.existsSync)((0, import_node_path19.join)(root, "__init__.py")) && skills && manifest === "valid";
|
|
22641
22816
|
const receipt = treeOk && !provisioned ? `hermes-skills-unprovisioned:${provisionedRoot}` : void 0;
|
|
22642
22817
|
return {
|
|
22643
|
-
hostPresent: (0,
|
|
22818
|
+
hostPresent: (0, import_node_fs21.existsSync)(host),
|
|
22644
22819
|
installRecordPresent,
|
|
22645
22820
|
manifest,
|
|
22646
22821
|
payloadPresent: treeOk && provisioned,
|
|
@@ -22686,13 +22861,13 @@ function worktreeRootSync() {
|
|
|
22686
22861
|
}
|
|
22687
22862
|
var gitignorePath = () => {
|
|
22688
22863
|
const root = worktreeRootSync();
|
|
22689
|
-
return root === null ? null : (0,
|
|
22864
|
+
return root === null ? null : (0, import_node_path19.join)(root, ".gitignore");
|
|
22690
22865
|
};
|
|
22691
22866
|
function readGitignore() {
|
|
22692
22867
|
const path2 = gitignorePath();
|
|
22693
22868
|
if (path2 === null) return null;
|
|
22694
22869
|
try {
|
|
22695
|
-
return (0,
|
|
22870
|
+
return (0, import_node_fs21.readFileSync)(path2, "utf8");
|
|
22696
22871
|
} catch {
|
|
22697
22872
|
return null;
|
|
22698
22873
|
}
|
|
@@ -22701,14 +22876,14 @@ function writeGitignore(content) {
|
|
|
22701
22876
|
const path2 = gitignorePath();
|
|
22702
22877
|
if (path2 === null) return false;
|
|
22703
22878
|
try {
|
|
22704
|
-
(0,
|
|
22879
|
+
(0, import_node_fs21.writeFileSync)(path2, content, "utf8");
|
|
22705
22880
|
return true;
|
|
22706
22881
|
} catch {
|
|
22707
22882
|
return false;
|
|
22708
22883
|
}
|
|
22709
22884
|
}
|
|
22710
22885
|
function lineEndingState(root) {
|
|
22711
|
-
const attributesPresent = (0,
|
|
22886
|
+
const attributesPresent = (0, import_node_fs21.existsSync)((0, import_node_path19.join)(root, ".gitattributes"));
|
|
22712
22887
|
try {
|
|
22713
22888
|
const output = execFileCapture("git", ["-C", root, "ls-files", "--eol", "--", ":(glob)**/*.sh"], {
|
|
22714
22889
|
windowsHide: true
|
|
@@ -22751,18 +22926,18 @@ function compatHolds(range, version) {
|
|
|
22751
22926
|
}
|
|
22752
22927
|
|
|
22753
22928
|
// src/claude-binary-doctor.ts
|
|
22754
|
-
var
|
|
22755
|
-
var
|
|
22756
|
-
var
|
|
22929
|
+
var import_node_fs22 = require("node:fs");
|
|
22930
|
+
var import_node_os14 = require("node:os");
|
|
22931
|
+
var import_node_path21 = require("node:path");
|
|
22757
22932
|
|
|
22758
22933
|
// src/jerv-cli-spawn.ts
|
|
22759
|
-
var
|
|
22760
|
-
var
|
|
22934
|
+
var import_node_os13 = require("node:os");
|
|
22935
|
+
var import_node_path20 = require("node:path");
|
|
22761
22936
|
init_cli_shared();
|
|
22762
|
-
var JERV_CLI_ENTRY = (0,
|
|
22937
|
+
var JERV_CLI_ENTRY = (0, import_node_path20.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
|
|
22763
22938
|
function pathEnvEntries(pathEnv, platform2 = process.platform) {
|
|
22764
22939
|
if (platform2 !== "win32") {
|
|
22765
|
-
return pathEnv.split(
|
|
22940
|
+
return pathEnv.split(import_node_path20.delimiter).map((e) => e.trim()).filter(Boolean);
|
|
22766
22941
|
}
|
|
22767
22942
|
if (pathEnv.includes(";")) {
|
|
22768
22943
|
return pathEnv.split(";").map((e) => e.trim()).filter(Boolean);
|
|
@@ -22781,7 +22956,7 @@ function normalizeSpawnPathEntry(entry, platform2 = process.platform) {
|
|
|
22781
22956
|
if (msys) return `${msys[1].toUpperCase()}:\\${msys[2].replace(/\//g, "\\")}`;
|
|
22782
22957
|
return trimmed;
|
|
22783
22958
|
}
|
|
22784
|
-
function jervCliCandidateDirs(env = process.env, home = (0,
|
|
22959
|
+
function jervCliCandidateDirs(env = process.env, home = (0, import_node_os13.homedir)(), platform2 = process.platform) {
|
|
22785
22960
|
const seen = /* @__PURE__ */ new Set();
|
|
22786
22961
|
const out = [];
|
|
22787
22962
|
const push = (dir) => {
|
|
@@ -22795,10 +22970,10 @@ function jervCliCandidateDirs(env = process.env, home = (0, import_node_os12.hom
|
|
|
22795
22970
|
push(normalizeSpawnPathEntry(entry, platform2));
|
|
22796
22971
|
}
|
|
22797
22972
|
if (platform2 === "win32") {
|
|
22798
|
-
if (env.APPDATA) push((0,
|
|
22799
|
-
if (env.LOCALAPPDATA) push((0,
|
|
22973
|
+
if (env.APPDATA) push((0, import_node_path20.join)(env.APPDATA, "npm"));
|
|
22974
|
+
if (env.LOCALAPPDATA) push((0, import_node_path20.join)(env.LOCALAPPDATA, "npm"));
|
|
22800
22975
|
} else {
|
|
22801
|
-
push((0,
|
|
22976
|
+
push((0, import_node_path20.join)(home, ".local", "bin"));
|
|
22802
22977
|
}
|
|
22803
22978
|
return out;
|
|
22804
22979
|
}
|
|
@@ -22851,26 +23026,26 @@ function globalNodeModulesRoots(host) {
|
|
|
22851
23026
|
out.push(dir);
|
|
22852
23027
|
};
|
|
22853
23028
|
const prefix = env.npm_config_prefix?.trim();
|
|
22854
|
-
if (prefix) push(platform2 === "win32" ? (0,
|
|
22855
|
-
for (const dir of jervCliCandidateDirs(env, host.home ?? (0,
|
|
22856
|
-
push((0,
|
|
22857
|
-
push((0,
|
|
23029
|
+
if (prefix) push(platform2 === "win32" ? (0, import_node_path21.join)(prefix, "node_modules") : (0, import_node_path21.join)(prefix, "lib", "node_modules"));
|
|
23030
|
+
for (const dir of jervCliCandidateDirs(env, host.home ?? (0, import_node_os14.homedir)(), platform2)) {
|
|
23031
|
+
push((0, import_node_path21.join)(dir, "node_modules"));
|
|
23032
|
+
push((0, import_node_path21.join)((0, import_node_path21.dirname)(dir), "lib", "node_modules"));
|
|
22858
23033
|
}
|
|
22859
23034
|
return out;
|
|
22860
23035
|
}
|
|
22861
23036
|
function readHead(path2) {
|
|
22862
23037
|
let fd;
|
|
22863
23038
|
try {
|
|
22864
|
-
fd = (0,
|
|
23039
|
+
fd = (0, import_node_fs22.openSync)(path2, "r");
|
|
22865
23040
|
const buffer = new Uint8Array(MAGIC_HEAD_BYTES);
|
|
22866
|
-
const read = (0,
|
|
23041
|
+
const read = (0, import_node_fs22.readSync)(fd, buffer, 0, MAGIC_HEAD_BYTES, 0);
|
|
22867
23042
|
return buffer.subarray(0, read);
|
|
22868
23043
|
} catch {
|
|
22869
23044
|
return void 0;
|
|
22870
23045
|
} finally {
|
|
22871
23046
|
if (fd !== void 0) {
|
|
22872
23047
|
try {
|
|
22873
|
-
(0,
|
|
23048
|
+
(0, import_node_fs22.closeSync)(fd);
|
|
22874
23049
|
} catch {
|
|
22875
23050
|
}
|
|
22876
23051
|
}
|
|
@@ -22878,7 +23053,7 @@ function readHead(path2) {
|
|
|
22878
23053
|
}
|
|
22879
23054
|
function fileBytes(path2) {
|
|
22880
23055
|
try {
|
|
22881
|
-
return (0,
|
|
23056
|
+
return (0, import_node_fs22.statSync)(path2).size;
|
|
22882
23057
|
} catch {
|
|
22883
23058
|
return void 0;
|
|
22884
23059
|
}
|
|
@@ -22892,17 +23067,17 @@ function readClaudeBinaryState(host = {}) {
|
|
|
22892
23067
|
const arch = host.arch ?? process.arch;
|
|
22893
23068
|
const magic = EXECUTABLE_MAGIC[platform2];
|
|
22894
23069
|
if (!magic) return void 0;
|
|
22895
|
-
const packageRoot = globalNodeModulesRoots(host).map((root) => (0,
|
|
23070
|
+
const packageRoot = globalNodeModulesRoots(host).map((root) => (0, import_node_path21.join)(root, ...PACKAGE.split("/"))).find((dir) => (0, import_node_fs22.existsSync)((0, import_node_path21.join)(dir, "package.json")));
|
|
22896
23071
|
if (!packageRoot) return void 0;
|
|
22897
23072
|
const keys = platformPackageKeys(platform2, arch);
|
|
22898
23073
|
const fallbackPackage = `${PACKAGE}-${keys[0]}`;
|
|
22899
23074
|
let manifest;
|
|
22900
23075
|
try {
|
|
22901
|
-
manifest = JSON.parse((0,
|
|
23076
|
+
manifest = JSON.parse((0, import_node_fs22.readFileSync)((0, import_node_path21.join)(packageRoot, "package.json"), "utf8"));
|
|
22902
23077
|
} catch (e) {
|
|
22903
23078
|
return {
|
|
22904
23079
|
state: "unreadable",
|
|
22905
|
-
binPath: (0,
|
|
23080
|
+
binPath: (0, import_node_path21.join)(packageRoot, "package.json"),
|
|
22906
23081
|
expectedMagic: magic.name,
|
|
22907
23082
|
platformPackage: fallbackPackage,
|
|
22908
23083
|
error: `package.json could not be read \u2014 ${e.message}`
|
|
@@ -22919,17 +23094,17 @@ function readClaudeBinaryState(host = {}) {
|
|
|
22919
23094
|
error: "package.json declares no `bin` entry \u2014 the executed file cannot be resolved"
|
|
22920
23095
|
};
|
|
22921
23096
|
}
|
|
22922
|
-
const binPath = (0,
|
|
22923
|
-
const binName = (0,
|
|
23097
|
+
const binPath = (0, import_node_path21.join)(packageRoot, binRelative);
|
|
23098
|
+
const binName = (0, import_node_path21.basename)(binRelative);
|
|
22924
23099
|
const optional = manifest.optionalDependencies;
|
|
22925
23100
|
const publishes = (name) => typeof optional === "object" && optional !== null && Object.prototype.hasOwnProperty.call(optional, name);
|
|
22926
23101
|
const published = keys.map((key) => `${PACKAGE}-${key}`).filter(publishes);
|
|
22927
23102
|
if (published.length === 0) return void 0;
|
|
22928
23103
|
const binIn = (name) => [
|
|
22929
|
-
(0,
|
|
22930
|
-
(0,
|
|
23104
|
+
(0, import_node_path21.join)(packageRoot, "node_modules", ...name.split("/"), binName),
|
|
23105
|
+
(0, import_node_path21.join)((0, import_node_path21.dirname)((0, import_node_path21.dirname)(packageRoot)), ...name.split("/"), binName)
|
|
22931
23106
|
];
|
|
22932
|
-
const found = published.map((name) => ({ name, path: binIn(name).find((file) => (0,
|
|
23107
|
+
const found = published.map((name) => ({ name, path: binIn(name).find((file) => (0, import_node_fs22.existsSync)(file)) })).find((c) => c.path);
|
|
22933
23108
|
const platformPackage = found?.name ?? published[0];
|
|
22934
23109
|
let source;
|
|
22935
23110
|
let sourceProblem;
|
|
@@ -22940,7 +23115,7 @@ function readClaudeBinaryState(host = {}) {
|
|
|
22940
23115
|
} else {
|
|
22941
23116
|
source = { path: found.path, bytes: fileBytes(found.path) ?? 0 };
|
|
22942
23117
|
}
|
|
22943
|
-
if (!(0,
|
|
23118
|
+
if (!(0, import_node_fs22.existsSync)(binPath)) {
|
|
22944
23119
|
return { state: "missing", binPath, expectedMagic: magic.name, platformPackage, ...source ? { source } : {}, ...sourceProblem ? { sourceProblem } : {} };
|
|
22945
23120
|
}
|
|
22946
23121
|
const head = readHead(binPath);
|
|
@@ -22983,9 +23158,9 @@ function healClaudeBinary(host = {}, onStep) {
|
|
|
22983
23158
|
const platform2 = host.platform ?? process.platform;
|
|
22984
23159
|
const aside = `${probe.binPath}.stub-${Date.now()}`;
|
|
22985
23160
|
let renamed = false;
|
|
22986
|
-
if ((0,
|
|
23161
|
+
if ((0, import_node_fs22.existsSync)(probe.binPath)) {
|
|
22987
23162
|
try {
|
|
22988
|
-
(0,
|
|
23163
|
+
(0, import_node_fs22.renameSync)(probe.binPath, aside);
|
|
22989
23164
|
renamed = true;
|
|
22990
23165
|
onStep?.(`renamed the stub aside: ${aside}`);
|
|
22991
23166
|
} catch (e) {
|
|
@@ -22994,12 +23169,12 @@ function healClaudeBinary(host = {}, onStep) {
|
|
|
22994
23169
|
}
|
|
22995
23170
|
try {
|
|
22996
23171
|
onStep?.(`copying ${probe.source.path} \u2192 ${probe.binPath} (${(probe.source.bytes / 1e6).toFixed(0)} MB)`);
|
|
22997
|
-
(0,
|
|
22998
|
-
if (platform2 !== "win32") (0,
|
|
23172
|
+
(0, import_node_fs22.copyFileSync)(probe.source.path, probe.binPath);
|
|
23173
|
+
if (platform2 !== "win32") (0, import_node_fs22.chmodSync)(probe.binPath, 493);
|
|
22999
23174
|
} catch (e) {
|
|
23000
23175
|
if (renamed) {
|
|
23001
23176
|
try {
|
|
23002
|
-
(0,
|
|
23177
|
+
(0, import_node_fs22.renameSync)(aside, probe.binPath);
|
|
23003
23178
|
} catch {
|
|
23004
23179
|
return { ok: false, detail: `copy failed (${e.message}) and the stub could not be restored \u2014 the original is at ${aside}` };
|
|
23005
23180
|
}
|
|
@@ -23013,7 +23188,7 @@ function healClaudeBinary(host = {}, onStep) {
|
|
|
23013
23188
|
let kept = false;
|
|
23014
23189
|
if (renamed) {
|
|
23015
23190
|
try {
|
|
23016
|
-
(0,
|
|
23191
|
+
(0, import_node_fs22.rmSync)(aside);
|
|
23017
23192
|
} catch {
|
|
23018
23193
|
kept = true;
|
|
23019
23194
|
}
|
|
@@ -23372,13 +23547,13 @@ function checkLineEndings(probe) {
|
|
|
23372
23547
|
}
|
|
23373
23548
|
|
|
23374
23549
|
// src/release-ledger.ts
|
|
23375
|
-
var
|
|
23376
|
-
var
|
|
23377
|
-
var
|
|
23550
|
+
var import_node_fs23 = require("node:fs");
|
|
23551
|
+
var import_node_path23 = require("node:path");
|
|
23552
|
+
var import_node_crypto9 = require("node:crypto");
|
|
23378
23553
|
|
|
23379
23554
|
// src/file-lock.ts
|
|
23380
23555
|
var import_promises2 = require("node:fs/promises");
|
|
23381
|
-
var
|
|
23556
|
+
var import_node_path22 = require("node:path");
|
|
23382
23557
|
function pidIsDead(pid) {
|
|
23383
23558
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
23384
23559
|
if (pid === process.pid) return false;
|
|
@@ -23496,7 +23671,7 @@ async function withFileLock(lockPath, opts, fn) {
|
|
|
23496
23671
|
}
|
|
23497
23672
|
async function withFencedFileLock(lockPath, opts, fn) {
|
|
23498
23673
|
const resolved = resolveFileLockOpts(opts);
|
|
23499
|
-
await (0, import_promises2.mkdir)((0,
|
|
23674
|
+
await (0, import_promises2.mkdir)((0, import_node_path22.dirname)(lockPath), { recursive: true }).catch(() => void 0);
|
|
23500
23675
|
const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
|
|
23501
23676
|
try {
|
|
23502
23677
|
return await fn(() => fileLockHeldBy(lockPath, guard.token));
|
|
@@ -23547,8 +23722,8 @@ function attachOutputNote(note, suffix) {
|
|
|
23547
23722
|
}
|
|
23548
23723
|
function writeFailingLegOutput(artifactPath, output) {
|
|
23549
23724
|
const fileLines = lastNonEmptyLines(output, LEDGER_OUTPUT_FILE_LINES);
|
|
23550
|
-
(0,
|
|
23551
|
-
(0,
|
|
23725
|
+
(0, import_node_fs23.mkdirSync)((0, import_node_path23.dirname)(artifactPath), { recursive: true });
|
|
23726
|
+
(0, import_node_fs23.writeFileSync)(artifactPath, `${fileLines.join("\n")}
|
|
23552
23727
|
`, "utf8");
|
|
23553
23728
|
const tail = fileLines.slice(-LEDGER_OUTPUT_NOTE_LINES).join("\n");
|
|
23554
23729
|
const noteSuffix = truncate(`failing-leg output: ${artifactPath}
|
|
@@ -23563,7 +23738,7 @@ function stampPhaseRecord(ledgerPath, tag, phase, entry, previousRetries, now) {
|
|
|
23563
23738
|
if (raw) {
|
|
23564
23739
|
try {
|
|
23565
23740
|
const persisted = writeFailingLegOutput(
|
|
23566
|
-
(0,
|
|
23741
|
+
(0, import_node_path23.join)((0, import_node_path23.dirname)((0, import_node_path23.dirname)(ledgerPath)), "ledger-output", `${tag}-${phase}.txt`),
|
|
23567
23742
|
raw
|
|
23568
23743
|
);
|
|
23569
23744
|
note = attachOutputNote(note, persisted.noteSuffix);
|
|
@@ -23716,30 +23891,30 @@ function resolvePrimaryGitDir(cwd) {
|
|
|
23716
23891
|
}
|
|
23717
23892
|
let common;
|
|
23718
23893
|
try {
|
|
23719
|
-
common = (0,
|
|
23894
|
+
common = (0, import_node_fs23.readFileSync)((0, import_node_path23.join)(gitDir, "commondir"), "utf8").trim();
|
|
23720
23895
|
} catch (e) {
|
|
23721
23896
|
if (e.code !== "ENOENT") {
|
|
23722
23897
|
throw new ReleaseLedgerError("unreadable", `release ledger: the worktree gitdir's commondir pointer could not be READ (${e instanceof Error ? e.message : String(e)}) \u2014 a failed read is unverified, not primary (#5987/#4713)`);
|
|
23723
23898
|
}
|
|
23724
23899
|
}
|
|
23725
23900
|
if (!common) return gitDir;
|
|
23726
|
-
const commonDir = (0,
|
|
23901
|
+
const commonDir = (0, import_node_path23.isAbsolute)(common) ? common : (0, import_node_path23.resolve)(gitDir, common);
|
|
23727
23902
|
try {
|
|
23728
|
-
(0,
|
|
23903
|
+
(0, import_node_fs23.readFileSync)((0, import_node_path23.join)(commonDir, "HEAD"), "utf8");
|
|
23729
23904
|
} catch (e) {
|
|
23730
23905
|
throw new ReleaseLedgerError("no-checkout", `release ledger: the worktree's commondir pointer names ${commonDir}, which has no readable HEAD (${e instanceof Error ? e.message : String(e)}) \u2014 the primary checkout may have been removed; refusing to write the ledger into a doomed location`);
|
|
23731
23906
|
}
|
|
23732
23907
|
return commonDir;
|
|
23733
23908
|
}
|
|
23734
23909
|
function releaseLedgerPath(cwd) {
|
|
23735
|
-
return (0,
|
|
23910
|
+
return (0, import_node_path23.join)(resolvePrimaryGitDir(cwd), "mmi-runtime", "release", "phases.json");
|
|
23736
23911
|
}
|
|
23737
23912
|
function atomicWriteJson(path2, ledger) {
|
|
23738
|
-
(0,
|
|
23739
|
-
const tmp = `${path2}.tmp-${process.pid}-${(0,
|
|
23740
|
-
(0,
|
|
23913
|
+
(0, import_node_fs23.mkdirSync)((0, import_node_path23.dirname)(path2), { recursive: true });
|
|
23914
|
+
const tmp = `${path2}.tmp-${process.pid}-${(0, import_node_crypto9.randomBytes)(4).toString("hex")}`;
|
|
23915
|
+
(0, import_node_fs23.writeFileSync)(tmp, `${JSON.stringify(ledger, null, 2)}
|
|
23741
23916
|
`, "utf8");
|
|
23742
|
-
(0,
|
|
23917
|
+
(0, import_node_fs23.renameSync)(tmp, path2);
|
|
23743
23918
|
}
|
|
23744
23919
|
var LEDGER_LOCK_OPTS = { label: "release ledger", staleMs: 18e4, deadHolderSteal: true };
|
|
23745
23920
|
async function withLedgerLock(path2, fn, opts = {}) {
|
|
@@ -23751,16 +23926,16 @@ async function withLedgerLock(path2, fn, opts = {}) {
|
|
|
23751
23926
|
}));
|
|
23752
23927
|
}
|
|
23753
23928
|
function checkoutRootFrom(cwd) {
|
|
23754
|
-
let dir = (0,
|
|
23929
|
+
let dir = (0, import_node_path23.resolve)(cwd);
|
|
23755
23930
|
for (; ; ) {
|
|
23756
23931
|
if (resolveGitDir(dir)) return dir;
|
|
23757
|
-
const parent = (0,
|
|
23932
|
+
const parent = (0, import_node_path23.dirname)(dir);
|
|
23758
23933
|
if (parent === dir) throw new ReleaseLedgerError("no-checkout", `train writer lock: no git checkout found above ${cwd}`);
|
|
23759
23934
|
dir = parent;
|
|
23760
23935
|
}
|
|
23761
23936
|
}
|
|
23762
23937
|
function trainWriterLockPath(cwd) {
|
|
23763
|
-
return (0,
|
|
23938
|
+
return (0, import_node_path23.join)((0, import_node_path23.dirname)(releaseLedgerPath(checkoutRootFrom(cwd))), "train.lock");
|
|
23764
23939
|
}
|
|
23765
23940
|
var TrainWriterBusyError = class extends Error {
|
|
23766
23941
|
lockPath;
|
|
@@ -23786,7 +23961,7 @@ function describeTrainWriterToken(token) {
|
|
|
23786
23961
|
function liveTrainWriterHolder(cwd) {
|
|
23787
23962
|
let token;
|
|
23788
23963
|
try {
|
|
23789
|
-
token = (0,
|
|
23964
|
+
token = (0, import_node_fs23.readFileSync)(trainWriterLockPath(cwd), "utf8");
|
|
23790
23965
|
} catch {
|
|
23791
23966
|
return void 0;
|
|
23792
23967
|
}
|
|
@@ -23808,7 +23983,7 @@ async function acquireTrainWriterLock(cwd, verb) {
|
|
|
23808
23983
|
if (heldTrainWriter) heldTrainWriter.depth -= 1;
|
|
23809
23984
|
};
|
|
23810
23985
|
}
|
|
23811
|
-
(0,
|
|
23986
|
+
(0, import_node_fs23.mkdirSync)((0, import_node_path23.dirname)(lockPath), { recursive: true });
|
|
23812
23987
|
const opts = resolveFileLockOpts({ label: "train writer", staleMs: TRAIN_WRITER_STALE_MS, deadHolderSteal: true, maxWaitMs: 0, holder: verb });
|
|
23813
23988
|
let guard;
|
|
23814
23989
|
try {
|
|
@@ -23817,7 +23992,7 @@ async function acquireTrainWriterLock(cwd, verb) {
|
|
|
23817
23992
|
if (!(e instanceof FileLockBusyError)) throw e;
|
|
23818
23993
|
let token = "";
|
|
23819
23994
|
try {
|
|
23820
|
-
token = (0,
|
|
23995
|
+
token = (0, import_node_fs23.readFileSync)(lockPath, "utf8");
|
|
23821
23996
|
} catch {
|
|
23822
23997
|
token = "";
|
|
23823
23998
|
}
|
|
@@ -23852,7 +24027,7 @@ async function withReleaseLedger(path2, expected, fn, opts = {}) {
|
|
|
23852
24027
|
let current;
|
|
23853
24028
|
let raw;
|
|
23854
24029
|
try {
|
|
23855
|
-
raw = (0,
|
|
24030
|
+
raw = (0, import_node_fs23.readFileSync)(path2, "utf8");
|
|
23856
24031
|
} catch (e) {
|
|
23857
24032
|
if (e.code !== "ENOENT") {
|
|
23858
24033
|
throw new ReleaseLedgerError("unreadable", `release ledger at ${path2} could not be READ (${e instanceof Error ? e.message : String(e)}) \u2014 a failed read is unverified, not absent (#5987)`);
|
|
@@ -24521,7 +24696,7 @@ async function clearLedgerFile(path2, anchors, maxWaitMs) {
|
|
|
24521
24696
|
return withLedgerLock(path2, async (fence) => {
|
|
24522
24697
|
let raw;
|
|
24523
24698
|
try {
|
|
24524
|
-
raw = (0,
|
|
24699
|
+
raw = (0, import_node_fs23.readFileSync)(path2, "utf8");
|
|
24525
24700
|
} catch (e) {
|
|
24526
24701
|
if (e.code === "ENOENT") return { cleared: false };
|
|
24527
24702
|
return { cleared: false, note: `release ledger at ${path2} could not be read for clearing (${e instanceof Error ? e.message : String(e)}) \u2014 left in place` };
|
|
@@ -24537,7 +24712,7 @@ async function clearLedgerFile(path2, anchors, maxWaitMs) {
|
|
|
24537
24712
|
} catch (e) {
|
|
24538
24713
|
return { cleared: false, note: `release ledger at ${path2} belongs to ${ledger.tag} @ ${ledger.tagSha.slice(0, 12)} \u2014 not this release; left in place (${e instanceof Error ? e.message : String(e)})` };
|
|
24539
24714
|
}
|
|
24540
|
-
await fence(() => (0,
|
|
24715
|
+
await fence(() => (0, import_node_fs23.unlinkSync)(path2));
|
|
24541
24716
|
return { cleared: true, note: `cleared the release ledger at ${path2} for the live-proven aborted ${anchors.tag}` };
|
|
24542
24717
|
}, { maxWaitMs });
|
|
24543
24718
|
}
|
|
@@ -24697,7 +24872,7 @@ async function healStaleAlignmentLeg(deps, cwd, expected) {
|
|
|
24697
24872
|
return withLedgerLock(path2, async (fence) => {
|
|
24698
24873
|
let prior;
|
|
24699
24874
|
try {
|
|
24700
|
-
prior = parseReleaseLedger((0,
|
|
24875
|
+
prior = parseReleaseLedger((0, import_node_fs23.readFileSync)(path2, "utf8"));
|
|
24701
24876
|
} catch {
|
|
24702
24877
|
return void 0;
|
|
24703
24878
|
}
|
|
@@ -24722,7 +24897,7 @@ async function healUnfoldedRunLegs(deps, cwd, model) {
|
|
|
24722
24897
|
const path2 = releaseLedgerPath(cwd);
|
|
24723
24898
|
let prior;
|
|
24724
24899
|
try {
|
|
24725
|
-
prior = parseReleaseLedger((0,
|
|
24900
|
+
prior = parseReleaseLedger((0, import_node_fs23.readFileSync)(path2, "utf8"));
|
|
24726
24901
|
} catch {
|
|
24727
24902
|
return void 0;
|
|
24728
24903
|
}
|
|
@@ -24791,7 +24966,7 @@ async function supersedeContentCausedDeployLeg(deps, cwd) {
|
|
|
24791
24966
|
return withLedgerLock(path2, async (fence) => {
|
|
24792
24967
|
let prior;
|
|
24793
24968
|
try {
|
|
24794
|
-
prior = parseReleaseLedger((0,
|
|
24969
|
+
prior = parseReleaseLedger((0, import_node_fs23.readFileSync)(path2, "utf8"));
|
|
24795
24970
|
} catch (e) {
|
|
24796
24971
|
throw new ReleaseLedgerError(
|
|
24797
24972
|
"unreadable",
|
|
@@ -24888,11 +25063,11 @@ async function proveOriginPromotionOrArchived(deps, prior, path2, label, ref, fe
|
|
|
24888
25063
|
if (!releaseAbsent) {
|
|
24889
25064
|
throw new Error(`${label}: origin no longer has ${prior.tag} but its GitHub Release still exists \u2014 resolve the mixed external state before closing the ledger; nothing was written (${ref})`);
|
|
24890
25065
|
}
|
|
24891
|
-
const archivePath = (0,
|
|
24892
|
-
if ((0,
|
|
25066
|
+
const archivePath = (0, import_node_path23.join)((0, import_node_path23.dirname)(path2), `phases.archive-${prior.tag}.json`);
|
|
25067
|
+
if ((0, import_node_fs23.existsSync)(archivePath)) {
|
|
24893
25068
|
throw new Error(`${label}: archive already exists at ${archivePath} \u2014 preserving both ledger records; resolve the collision by hand; nothing was written (${ref})`);
|
|
24894
25069
|
}
|
|
24895
|
-
await fence(() => (0,
|
|
25070
|
+
await fence(() => (0, import_node_fs23.renameSync)(path2, archivePath));
|
|
24896
25071
|
return "deleted";
|
|
24897
25072
|
}
|
|
24898
25073
|
async function supersedeContentCausedPublishLeg(deps, cwd) {
|
|
@@ -24900,7 +25075,7 @@ async function supersedeContentCausedPublishLeg(deps, cwd) {
|
|
|
24900
25075
|
return withLedgerLock(path2, async (fence) => {
|
|
24901
25076
|
let prior;
|
|
24902
25077
|
try {
|
|
24903
|
-
prior = parseReleaseLedger((0,
|
|
25078
|
+
prior = parseReleaseLedger((0, import_node_fs23.readFileSync)(path2, "utf8"));
|
|
24904
25079
|
} catch (e) {
|
|
24905
25080
|
throw new ReleaseLedgerError(
|
|
24906
25081
|
"unreadable",
|
|
@@ -24982,7 +25157,7 @@ async function supersedeContentCausedDeployAndPublishLegs(deps, cwd) {
|
|
|
24982
25157
|
return withLedgerLock(path2, async (fence) => {
|
|
24983
25158
|
let prior;
|
|
24984
25159
|
try {
|
|
24985
|
-
prior = parseReleaseLedger((0,
|
|
25160
|
+
prior = parseReleaseLedger((0, import_node_fs23.readFileSync)(path2, "utf8"));
|
|
24986
25161
|
} catch (e) {
|
|
24987
25162
|
throw new ReleaseLedgerError(
|
|
24988
25163
|
"unreadable",
|
|
@@ -25087,7 +25262,7 @@ async function supersedeContentCausedDeployAndPublishLegs(deps, cwd) {
|
|
|
25087
25262
|
async function reverifyPriorRunLegs(deps, path2, targetTag) {
|
|
25088
25263
|
let prior;
|
|
25089
25264
|
try {
|
|
25090
|
-
prior = parseReleaseLedger((0,
|
|
25265
|
+
prior = parseReleaseLedger((0, import_node_fs23.readFileSync)(path2, "utf8"));
|
|
25091
25266
|
} catch {
|
|
25092
25267
|
return;
|
|
25093
25268
|
}
|
|
@@ -25107,7 +25282,7 @@ async function beginReleaseLedger(deps, cwd, targetTag) {
|
|
|
25107
25282
|
return withLedgerLock(path2, async (fence) => {
|
|
25108
25283
|
let raw;
|
|
25109
25284
|
try {
|
|
25110
|
-
raw = (0,
|
|
25285
|
+
raw = (0, import_node_fs23.readFileSync)(path2, "utf8");
|
|
25111
25286
|
} catch (e) {
|
|
25112
25287
|
if (e.code === "ENOENT") return { ok: true };
|
|
25113
25288
|
return { ok: false, error: `the release ledger at ${path2} could not be READ (${e instanceof Error ? e.message : String(e)}) \u2014 a failed read is unverified, not absent (#5987/#4713)` };
|
|
@@ -25145,15 +25320,15 @@ async function beginReleaseLedger(deps, cwd, targetTag) {
|
|
|
25145
25320
|
if (liveSha.toLowerCase() !== prior.tagSha.toLowerCase()) {
|
|
25146
25321
|
return { ok: false, error: `the completed ${prior.tag} ledger says phases-green but origin reports ${liveSha.slice(0, 12) || "no tag"} \u2260 ${prior.tagSha.slice(0, 12)} \u2014 refusing to archive on unproven state; resolve by hand (#5987)` };
|
|
25147
25322
|
}
|
|
25148
|
-
const archivePath = (0,
|
|
25323
|
+
const archivePath = (0, import_node_path23.join)((0, import_node_path23.dirname)(path2), `phases.archive-${prior.tag}.json`);
|
|
25149
25324
|
try {
|
|
25150
|
-
await fence(() => (0,
|
|
25325
|
+
await fence(() => (0, import_node_fs23.unlinkSync)(archivePath));
|
|
25151
25326
|
} catch (e) {
|
|
25152
25327
|
if (e.code !== "ENOENT") {
|
|
25153
25328
|
return { ok: false, error: `could not clear the stale archive at ${archivePath} (${e instanceof Error ? e.message : String(e)}) \u2014 the ledger for ${prior.tag} stays active (#5987)` };
|
|
25154
25329
|
}
|
|
25155
25330
|
}
|
|
25156
|
-
await fence(() => (0,
|
|
25331
|
+
await fence(() => (0, import_node_fs23.renameSync)(path2, archivePath));
|
|
25157
25332
|
return { ok: true, note: `archived the completed release ledger ${prior.tag} to ${archivePath} (live-proven at ${prior.tagSha.slice(0, 12)})` };
|
|
25158
25333
|
});
|
|
25159
25334
|
}
|
|
@@ -25181,7 +25356,7 @@ async function retryHotfixDeployLegs(deps, cwd, opts = {}) {
|
|
|
25181
25356
|
return withLedgerLock(path2, async (fence) => {
|
|
25182
25357
|
let prior;
|
|
25183
25358
|
try {
|
|
25184
|
-
prior = parseReleaseLedger((0,
|
|
25359
|
+
prior = parseReleaseLedger((0, import_node_fs23.readFileSync)(path2, "utf8"));
|
|
25185
25360
|
} catch (e) {
|
|
25186
25361
|
throw new ReleaseLedgerError(
|
|
25187
25362
|
"unreadable",
|
|
@@ -25372,10 +25547,10 @@ function readOriginWorkflowsDefault(root, ref) {
|
|
|
25372
25547
|
}
|
|
25373
25548
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
25374
25549
|
async function probeRegistryWhoami(train) {
|
|
25375
|
-
const dir = (0,
|
|
25376
|
-
const rc = (0,
|
|
25550
|
+
const dir = (0, import_node_fs24.mkdtempSync)((0, import_node_path24.join)((0, import_node_os15.tmpdir)(), "mmi-npmrc-"));
|
|
25551
|
+
const rc = (0, import_node_path24.join)(dir, "npmrc");
|
|
25377
25552
|
try {
|
|
25378
|
-
(0,
|
|
25553
|
+
(0, import_node_fs24.writeFileSync)(rc, "//registry.npmjs.org/:_authToken=${NPM_TOKEN}\n");
|
|
25379
25554
|
await train.runSelf(["secrets", "use", "npm/NPM_TOKEN", "--slug", "_org", "--", "npm", "whoami", "--registry", NPM_REGISTRY, "--userconfig", rc]);
|
|
25380
25555
|
return { kind: "ok", detail: "the registry answered a login" };
|
|
25381
25556
|
} catch (e) {
|
|
@@ -25386,7 +25561,7 @@ async function probeRegistryWhoami(train) {
|
|
|
25386
25561
|
if (/\bE401\b|401 Unauthorized/.test(stderr)) return { kind: "rejected", detail: "npm whoami answered 401 Unauthorized" };
|
|
25387
25562
|
return { kind: "unverified", detail: first };
|
|
25388
25563
|
} finally {
|
|
25389
|
-
(0,
|
|
25564
|
+
(0, import_node_fs24.rmSync)(dir, { recursive: true, force: true });
|
|
25390
25565
|
}
|
|
25391
25566
|
}
|
|
25392
25567
|
var REGISTRY_TOKEN_ENV = /\b(NODE_AUTH_TOKEN|NPM_TOKEN)\b/;
|
|
@@ -25404,7 +25579,7 @@ var defaultDeps2 = {
|
|
|
25404
25579
|
ledgerPath: releaseLedgerPath,
|
|
25405
25580
|
readLedgerRaw: (path2) => {
|
|
25406
25581
|
try {
|
|
25407
|
-
return (0,
|
|
25582
|
+
return (0, import_node_fs24.readFileSync)(path2, "utf8");
|
|
25408
25583
|
} catch (e) {
|
|
25409
25584
|
if (e.code === "ENOENT") return void 0;
|
|
25410
25585
|
throw e;
|
|
@@ -25415,7 +25590,7 @@ var defaultDeps2 = {
|
|
|
25415
25590
|
trainWriterHolder: liveTrainWriterHolder,
|
|
25416
25591
|
readSurfacesRaw: (cwd) => {
|
|
25417
25592
|
try {
|
|
25418
|
-
return (0,
|
|
25593
|
+
return (0, import_node_fs24.readFileSync)((0, import_node_path24.join)(cwd, "surfaces.json"), "utf8");
|
|
25419
25594
|
} catch {
|
|
25420
25595
|
return void 0;
|
|
25421
25596
|
}
|
|
@@ -26005,7 +26180,7 @@ function formatTrainDoctor(v) {
|
|
|
26005
26180
|
|
|
26006
26181
|
// src/release-plain-words.ts
|
|
26007
26182
|
var import_promises3 = require("node:fs/promises");
|
|
26008
|
-
var
|
|
26183
|
+
var import_node_fs25 = require("node:fs");
|
|
26009
26184
|
function releaseCutByNotes(cutBy) {
|
|
26010
26185
|
return `## Internal
|
|
26011
26186
|
Cut by ${formatSessionIdentity(cutBy)}
|
|
@@ -26030,7 +26205,7 @@ function validateSuiteReleaseSummary(path2) {
|
|
|
26030
26205
|
if (!path2) throw new Error(`requires --announce-summary-file <path> before promoting any member \u2014 see ${SUMMARY_GUIDE}`);
|
|
26031
26206
|
let content;
|
|
26032
26207
|
try {
|
|
26033
|
-
content = (0,
|
|
26208
|
+
content = (0, import_node_fs25.readFileSync)(path2, "utf8");
|
|
26034
26209
|
} catch (e) {
|
|
26035
26210
|
throw new Error(`could not read summary file ${path2}: ${e.message} \u2014 see ${SUMMARY_GUIDE}`);
|
|
26036
26211
|
}
|
|
@@ -26237,34 +26412,34 @@ function vercelVersionEnvDeps(deps) {
|
|
|
26237
26412
|
// src/train-apply-phases.ts
|
|
26238
26413
|
var TRAIN_BUMP_INTENTS = ["major", "minor", "patch"];
|
|
26239
26414
|
function readLocalGateWorkflows() {
|
|
26240
|
-
const dir = (0,
|
|
26415
|
+
const dir = (0, import_node_path25.join)(".github", "workflows");
|
|
26241
26416
|
let names;
|
|
26242
26417
|
try {
|
|
26243
|
-
names = (0,
|
|
26418
|
+
names = (0, import_node_fs26.readdirSync)(dir);
|
|
26244
26419
|
} catch {
|
|
26245
26420
|
return null;
|
|
26246
26421
|
}
|
|
26247
26422
|
const files = [];
|
|
26248
26423
|
for (const name of names.filter(isGateWorkflowPath)) {
|
|
26249
26424
|
try {
|
|
26250
|
-
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0,
|
|
26425
|
+
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs26.readFileSync)((0, import_node_path25.join)(dir, name), "utf8") });
|
|
26251
26426
|
} catch {
|
|
26252
26427
|
}
|
|
26253
26428
|
}
|
|
26254
26429
|
return files;
|
|
26255
26430
|
}
|
|
26256
26431
|
function readLocalWorkflows() {
|
|
26257
|
-
const dir = (0,
|
|
26432
|
+
const dir = (0, import_node_path25.join)(".github", "workflows");
|
|
26258
26433
|
let names;
|
|
26259
26434
|
try {
|
|
26260
|
-
names = (0,
|
|
26435
|
+
names = (0, import_node_fs26.readdirSync)(dir);
|
|
26261
26436
|
} catch {
|
|
26262
26437
|
return null;
|
|
26263
26438
|
}
|
|
26264
26439
|
const files = [];
|
|
26265
26440
|
for (const name of names.filter((n) => /\.ya?ml$/i.test(n))) {
|
|
26266
26441
|
try {
|
|
26267
|
-
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0,
|
|
26442
|
+
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs26.readFileSync)((0, import_node_path25.join)(dir, name), "utf8") });
|
|
26268
26443
|
} catch {
|
|
26269
26444
|
}
|
|
26270
26445
|
}
|
|
@@ -27146,7 +27321,7 @@ async function activeRcandLedgerCandidate(deps, cwd) {
|
|
|
27146
27321
|
}
|
|
27147
27322
|
let prior;
|
|
27148
27323
|
try {
|
|
27149
|
-
prior = parseReleaseLedger((0,
|
|
27324
|
+
prior = parseReleaseLedger((0, import_node_fs26.readFileSync)(path2, "utf8"));
|
|
27150
27325
|
} catch {
|
|
27151
27326
|
return void 0;
|
|
27152
27327
|
}
|
|
@@ -30428,11 +30603,11 @@ function buildPluginCachePlan(home, running, deps, opts = {}) {
|
|
|
30428
30603
|
return absent;
|
|
30429
30604
|
}
|
|
30430
30605
|
const versions = names.filter(isVersionDirName);
|
|
30431
|
-
const
|
|
30432
|
-
const pruneSet = new Set(
|
|
30606
|
+
const prune2 = selectPrunablePluginVersions(versions, running, opts.installedVersion);
|
|
30607
|
+
const pruneSet = new Set(prune2);
|
|
30433
30608
|
const keep = [...versions].sort((a, b) => compareVersions(b, a)).filter((v) => !pruneSet.has(v));
|
|
30434
|
-
const bytes = opts.withBytes ?
|
|
30435
|
-
return { cacheRoot, present: true, keep, prune, bytes, running, ...stagingFields };
|
|
30609
|
+
const bytes = opts.withBytes ? prune2.reduce((sum, v) => sum + deps.dirBytes(`${cacheRoot}/${v}`), 0) : 0;
|
|
30610
|
+
return { cacheRoot, present: true, keep, prune: prune2, bytes, running, ...stagingFields };
|
|
30436
30611
|
}
|
|
30437
30612
|
function applyPluginCachePlan(plan, remove2, stagingGuard) {
|
|
30438
30613
|
const removed = [];
|
|
@@ -30741,6 +30916,12 @@ function registerBoardCommands(program3) {
|
|
|
30741
30916
|
if (!o.out) console.log(output);
|
|
30742
30917
|
else console.log(`Wrote board read to ${o.out} (UTF-8, ${writeUtf8Receipt(o.out, output)} bytes)`);
|
|
30743
30918
|
} catch (e) {
|
|
30919
|
+
if (e instanceof BoardSnapshotFailure) {
|
|
30920
|
+
if (o.json) console.log(JSON.stringify(e.receipt));
|
|
30921
|
+
console.error(`mmi-cli board read failed: ${e.message}`);
|
|
30922
|
+
process.exitCode = e.exitCode;
|
|
30923
|
+
return;
|
|
30924
|
+
}
|
|
30744
30925
|
if (o.json && e && typeof e === "object" && "quota" in e && e.quota) console.log(JSON.stringify(e.quota));
|
|
30745
30926
|
return failGraceful(`board read failed: ${withDiscoverMissDetail(e.message)}`);
|
|
30746
30927
|
}
|
|
@@ -30781,7 +30962,7 @@ function registerBoardCommands(program3) {
|
|
|
30781
30962
|
return `Claimed ${ref} for ${holder} - In Progress${reclaimed}`;
|
|
30782
30963
|
}
|
|
30783
30964
|
const board = program3.command("board").description("read, claim, show, and move Project v2 work items for the current repo");
|
|
30784
|
-
board.command("read", { isDefault: true }).alias("list").description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--direct", "bypass the Hub snapshot and read the live board through direct GitHub GraphQL").option("--snapshot-only", "
|
|
30965
|
+
board.command("read", { isDefault: true }).alias("list").description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--direct", "bypass the Hub snapshot and read the live board through direct GitHub GraphQL").option("--snapshot-only", "never fall back to your personal GraphQL pool; failure exits: 20 hub-unreachable, 21 app-quota-exhausted, 22 refused. With --json, stdout is {ok:false,outcome,reason[,resetAt,pool]} (pool is rest or graphql when known); success is unchanged").option("--min-cursor <n>", "board-event cursor (from `oracle board events`) the Hub snapshot must postdate; a cached snapshot older than that event is rebuilt (#7240)").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").option("--out <path>", "write the output to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5802)").addHelpText("after", "\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").action((o) => runBoardRead(o));
|
|
30785
30966
|
board.command("events").description("content-free board/CI change records after a cursor, from the Hub event feed (no GitHub call) \u2014 for real-time sidebars (#7235)").option("--since <cursor>", "cursor from the previous answer; omit on first read (answers the head cursor with resync:true)").option("--wait <seconds>", "hold up to this long (max 120), polling every 2s, and return on the first event or resync", "0").option("--repo <owner/repo>", "repo whose Hub config to use (defaults to git origin)").option("--json", "machine-readable output (already the default)").addHelpText("after", "\nPrints {cursor, events, resync, more[, hidden]}. Pass the returned cursor as the next --since.\nresync:true means records were missed (retention ~1h, or a gap): re-read the board with\n`mmi-cli oracle board read --snapshot-only --json`.\n").action(async (o) => {
|
|
30786
30967
|
try {
|
|
30787
30968
|
if (o.since !== void 0 && !/^\d{1,15}$/.test(o.since)) throw new Error("--since must be a non-negative integer cursor");
|
|
@@ -31022,9 +31203,9 @@ function registerBoardCommands(program3) {
|
|
|
31022
31203
|
}
|
|
31023
31204
|
|
|
31024
31205
|
// src/bootstrap-commands.ts
|
|
31025
|
-
var
|
|
31026
|
-
var
|
|
31027
|
-
var
|
|
31206
|
+
var import_node_fs30 = require("node:fs");
|
|
31207
|
+
var import_node_os18 = require("node:os");
|
|
31208
|
+
var import_node_path29 = require("node:path");
|
|
31028
31209
|
init_cli_shared();
|
|
31029
31210
|
init_clean_exit();
|
|
31030
31211
|
|
|
@@ -31208,8 +31389,8 @@ init_github_client();
|
|
|
31208
31389
|
init_cli_shared();
|
|
31209
31390
|
|
|
31210
31391
|
// src/port-registry.ts
|
|
31211
|
-
var
|
|
31212
|
-
var
|
|
31392
|
+
var import_node_fs27 = require("node:fs");
|
|
31393
|
+
var import_node_path26 = require("node:path");
|
|
31213
31394
|
|
|
31214
31395
|
// ../infra/port-geometry.mjs
|
|
31215
31396
|
var PORT_BLOCK = 100;
|
|
@@ -31223,8 +31404,8 @@ function nextPortBlock(registry2) {
|
|
|
31223
31404
|
return [base, base + PORT_SPAN];
|
|
31224
31405
|
}
|
|
31225
31406
|
function loadPortRegistry(path2) {
|
|
31226
|
-
if (!(0,
|
|
31227
|
-
const raw = JSON.parse((0,
|
|
31407
|
+
if (!(0, import_node_fs27.existsSync)(path2)) return {};
|
|
31408
|
+
const raw = JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8"));
|
|
31228
31409
|
const out = {};
|
|
31229
31410
|
for (const [key, value] of Object.entries(raw)) {
|
|
31230
31411
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -31252,22 +31433,22 @@ function existingPortRange(repo, registry2) {
|
|
|
31252
31433
|
return registry2[repo] ?? null;
|
|
31253
31434
|
}
|
|
31254
31435
|
function portRangeInfraAt(root, source) {
|
|
31255
|
-
const registryPath = (0,
|
|
31256
|
-
const ddbScriptPath = (0,
|
|
31257
|
-
if (!(0,
|
|
31436
|
+
const registryPath = (0, import_node_path26.join)(root, "infra", "port-ranges.json");
|
|
31437
|
+
const ddbScriptPath = (0, import_node_path26.join)(root, "infra", "port-ddb.mjs");
|
|
31438
|
+
if (!(0, import_node_fs27.existsSync)(registryPath) || !(0, import_node_fs27.existsSync)(ddbScriptPath)) return null;
|
|
31258
31439
|
return { root, source, registryPath, ddbScriptPath };
|
|
31259
31440
|
}
|
|
31260
31441
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
31261
31442
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
31262
31443
|
if (direct) return direct;
|
|
31263
|
-
for (let dir = cwd; ; dir = (0,
|
|
31264
|
-
const sibling = portRangeInfraAt((0,
|
|
31444
|
+
for (let dir = cwd; ; dir = (0, import_node_path26.dirname)(dir)) {
|
|
31445
|
+
const sibling = portRangeInfraAt((0, import_node_path26.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
31265
31446
|
if (sibling) return sibling;
|
|
31266
|
-
const parent = (0,
|
|
31447
|
+
const parent = (0, import_node_path26.dirname)(dir);
|
|
31267
31448
|
if (parent === dir) break;
|
|
31268
31449
|
}
|
|
31269
31450
|
if (packageDir) {
|
|
31270
|
-
const pkgRoot = (0,
|
|
31451
|
+
const pkgRoot = (0, import_node_path26.join)(packageDir, "..", "..");
|
|
31271
31452
|
const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
|
|
31272
31453
|
if (pkgFrom) return pkgFrom;
|
|
31273
31454
|
}
|
|
@@ -31284,7 +31465,7 @@ async function ensurePortRangeAtomic(repo, path2, allocate) {
|
|
|
31284
31465
|
} catch (e) {
|
|
31285
31466
|
const why = e.message;
|
|
31286
31467
|
const missing = /@aws-sdk\/[\w-]+/.exec(why)?.[0];
|
|
31287
|
-
throw new Error(`DDB port allocator failed for ${repo} \u2014 refusing to fall back to the frozen ${path2} (it would hand out a block the cursor already gave away). ` + (missing && /cannot find|not found|ERR_MODULE_NOT_FOUND/i.test(why) ? `Missing dependency ${missing}: run \`npm ci\` in ${(0,
|
|
31468
|
+
throw new Error(`DDB port allocator failed for ${repo} \u2014 refusing to fall back to the frozen ${path2} (it would hand out a block the cursor already gave away). ` + (missing && /cannot find|not found|ERR_MODULE_NOT_FOUND/i.test(why) ? `Missing dependency ${missing}: run \`npm ci\` in ${(0, import_node_path26.dirname)(path2)}, then retry.` : "Check AWS credentials and that the registry table is reachable, then retry.") + ` Cause: ${why}`);
|
|
31288
31469
|
}
|
|
31289
31470
|
}
|
|
31290
31471
|
|
|
@@ -31336,13 +31517,13 @@ async function assignPersistedPortRange(repo, slug, reg, opts) {
|
|
|
31336
31517
|
}
|
|
31337
31518
|
|
|
31338
31519
|
// src/box-commands.ts
|
|
31339
|
-
var
|
|
31520
|
+
var import_node_fs29 = require("node:fs");
|
|
31340
31521
|
var import_node_child_process10 = require("node:child_process");
|
|
31341
|
-
var
|
|
31342
|
-
var
|
|
31522
|
+
var import_node_os17 = require("node:os");
|
|
31523
|
+
var import_node_path28 = require("node:path");
|
|
31343
31524
|
|
|
31344
31525
|
// src/box-control.ts
|
|
31345
|
-
var
|
|
31526
|
+
var import_node_crypto10 = require("node:crypto");
|
|
31346
31527
|
var MANIFEST_PATH = "infra/box-control/manifest.json";
|
|
31347
31528
|
var ALLOWED_POST = /* @__PURE__ */ new Set(["systemctl daemon-reload"]);
|
|
31348
31529
|
function parseManifest(text) {
|
|
@@ -31389,7 +31570,7 @@ function filesForBox(files, box) {
|
|
|
31389
31570
|
return files.flatMap((file) => file.targets.filter((t) => t.box === box).map((t) => ({ file, dest: t.dest })));
|
|
31390
31571
|
}
|
|
31391
31572
|
function md5(content) {
|
|
31392
|
-
return (0,
|
|
31573
|
+
return (0, import_node_crypto10.createHash)("md5").update(content).digest("hex");
|
|
31393
31574
|
}
|
|
31394
31575
|
function planRemoteScript(dests) {
|
|
31395
31576
|
const lines2 = ["set -u"];
|
|
@@ -31468,9 +31649,9 @@ function applyRemoteScript(items, date) {
|
|
|
31468
31649
|
init_clean_exit();
|
|
31469
31650
|
|
|
31470
31651
|
// src/secrets-commands.ts
|
|
31471
|
-
var
|
|
31472
|
-
var
|
|
31473
|
-
var
|
|
31652
|
+
var import_node_fs28 = require("node:fs");
|
|
31653
|
+
var import_node_path27 = require("node:path");
|
|
31654
|
+
var import_node_os16 = require("node:os");
|
|
31474
31655
|
init_cli_shared();
|
|
31475
31656
|
init_hub_auth();
|
|
31476
31657
|
init_github_client();
|
|
@@ -32626,18 +32807,18 @@ function collectMap(value, previous = []) {
|
|
|
32626
32807
|
return [...previous, value];
|
|
32627
32808
|
}
|
|
32628
32809
|
async function decryptRailsCredentials(input) {
|
|
32629
|
-
const appDir = (0,
|
|
32810
|
+
const appDir = (0, import_node_path27.resolve)(input.appDir ?? process.cwd());
|
|
32630
32811
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
32631
32812
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
32632
|
-
const credentialsPath = (0,
|
|
32633
|
-
const masterKeyPath = (0,
|
|
32813
|
+
const credentialsPath = (0, import_node_path27.resolve)(appDir, credentialsFile);
|
|
32814
|
+
const masterKeyPath = (0, import_node_path27.resolve)(appDir, masterKeyFile);
|
|
32634
32815
|
const env = {
|
|
32635
32816
|
...process.env,
|
|
32636
32817
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
32637
32818
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
32638
32819
|
};
|
|
32639
|
-
if ((0,
|
|
32640
|
-
env.RAILS_MASTER_KEY = (0,
|
|
32820
|
+
if ((0, import_node_fs28.existsSync)(masterKeyPath)) {
|
|
32821
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs28.readFileSync)(masterKeyPath, "utf8").trim();
|
|
32641
32822
|
}
|
|
32642
32823
|
const script = [
|
|
32643
32824
|
'require "json"',
|
|
@@ -32647,9 +32828,9 @@ async function decryptRailsCredentials(input) {
|
|
|
32647
32828
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
32648
32829
|
"puts JSON.generate(config.config)"
|
|
32649
32830
|
].join("\n");
|
|
32650
|
-
const scriptDir = (0,
|
|
32651
|
-
const scriptPath = (0,
|
|
32652
|
-
(0,
|
|
32831
|
+
const scriptDir = (0, import_node_fs28.mkdtempSync)((0, import_node_path27.join)((0, import_node_os16.tmpdir)(), "mmi-rails-decrypt-"));
|
|
32832
|
+
const scriptPath = (0, import_node_path27.join)(scriptDir, "decrypt.rb");
|
|
32833
|
+
(0, import_node_fs28.writeFileSync)(scriptPath, script, "utf8");
|
|
32653
32834
|
try {
|
|
32654
32835
|
const args = ["exec", "ruby", scriptPath];
|
|
32655
32836
|
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
@@ -32661,7 +32842,7 @@ async function decryptRailsCredentials(input) {
|
|
|
32661
32842
|
});
|
|
32662
32843
|
return JSON.parse(stdout);
|
|
32663
32844
|
} finally {
|
|
32664
|
-
(0,
|
|
32845
|
+
(0, import_node_fs28.rmSync)(scriptDir, { recursive: true, force: true });
|
|
32665
32846
|
}
|
|
32666
32847
|
}
|
|
32667
32848
|
async function readSecretStdin() {
|
|
@@ -32751,7 +32932,7 @@ function registerSecretsCommands(program3) {
|
|
|
32751
32932
|
let body;
|
|
32752
32933
|
if (o.file) {
|
|
32753
32934
|
try {
|
|
32754
|
-
body = (0,
|
|
32935
|
+
body = (0, import_node_fs28.readFileSync)((0, import_node_path27.resolve)(o.file), "utf8");
|
|
32755
32936
|
} catch (e) {
|
|
32756
32937
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
32757
32938
|
}
|
|
@@ -32887,7 +33068,7 @@ function registerSecretsCommands(program3) {
|
|
|
32887
33068
|
{
|
|
32888
33069
|
...d,
|
|
32889
33070
|
decryptRailsCredentials,
|
|
32890
|
-
removeFile: (path2) => (0,
|
|
33071
|
+
removeFile: (path2) => (0, import_node_fs28.unlinkSync)((0, import_node_path27.resolve)(o.appDir ?? process.cwd(), path2))
|
|
32891
33072
|
},
|
|
32892
33073
|
{
|
|
32893
33074
|
repo: o.repo,
|
|
@@ -33143,7 +33324,7 @@ function registerBoxCommands(program3) {
|
|
|
33143
33324
|
}
|
|
33144
33325
|
const remote = command && command.length ? command.join(" ") : void 0;
|
|
33145
33326
|
const wroteScript = o.ssh && o.script ? o.script : null;
|
|
33146
|
-
if (wroteScript) (0,
|
|
33327
|
+
if (wroteScript) (0, import_node_fs29.writeFileSync)(wroteScript, sshRecipeScript(found, remote), "utf8");
|
|
33147
33328
|
if (o.json) {
|
|
33148
33329
|
console.log(JSON.stringify({
|
|
33149
33330
|
box: found,
|
|
@@ -33217,17 +33398,17 @@ function hubGitShow(ref) {
|
|
|
33217
33398
|
return (path2) => git3(["show", `${ref}:${path2}`]);
|
|
33218
33399
|
}
|
|
33219
33400
|
function runOnBox(found, script) {
|
|
33220
|
-
const dir = (0,
|
|
33401
|
+
const dir = (0, import_node_fs29.mkdtempSync)((0, import_node_path28.join)((0, import_node_os17.tmpdir)(), "mmi-box-control-"));
|
|
33221
33402
|
try {
|
|
33222
|
-
const recipe = (0,
|
|
33223
|
-
(0,
|
|
33403
|
+
const recipe = (0, import_node_path28.join)(dir, "recipe.sh");
|
|
33404
|
+
(0, import_node_fs29.writeFileSync)(recipe, sshRecipeScript(found, "sh -s"), "utf8");
|
|
33224
33405
|
const r = (0, import_node_child_process10.spawnSync)("bash", [recipe], { input: script, encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 18e4, windowsHide: true });
|
|
33225
33406
|
if (r.status !== 0) {
|
|
33226
33407
|
throw new UnreadableError(`box control: the ssh session to ${found.name} failed (exit ${r.status ?? r.signal}): ${(r.stderr || "").trim().split("\n").slice(-3).join(" | ")}`);
|
|
33227
33408
|
}
|
|
33228
33409
|
return r.stdout;
|
|
33229
33410
|
} finally {
|
|
33230
|
-
(0,
|
|
33411
|
+
(0, import_node_fs29.rmSync)(dir, { recursive: true, force: true });
|
|
33231
33412
|
}
|
|
33232
33413
|
}
|
|
33233
33414
|
function formatControl(mode, box, rows) {
|
|
@@ -33355,7 +33536,7 @@ function renderDeployPortDoctor(report) {
|
|
|
33355
33536
|
}
|
|
33356
33537
|
|
|
33357
33538
|
// src/bootstrap-drift.ts
|
|
33358
|
-
var
|
|
33539
|
+
var import_node_crypto11 = require("node:crypto");
|
|
33359
33540
|
function byteComparableSeeds(manifest, cls) {
|
|
33360
33541
|
return manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self" && s.classes.includes(cls));
|
|
33361
33542
|
}
|
|
@@ -33375,7 +33556,7 @@ function compareSeedBytes(hubContent, repoContent) {
|
|
|
33375
33556
|
return normalize(hubContent) === normalize(repoContent) ? "match" : "drift";
|
|
33376
33557
|
}
|
|
33377
33558
|
function seedContentHash(content) {
|
|
33378
|
-
return (0,
|
|
33559
|
+
return (0, import_node_crypto11.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
|
|
33379
33560
|
}
|
|
33380
33561
|
function auditRepoSeedDrift(repo, seeds, hubContents, repoReads) {
|
|
33381
33562
|
const byTarget = new Map(repoReads.map((r) => [r.target, r.content]));
|
|
@@ -33538,7 +33719,7 @@ function renderPropagationReport(plan) {
|
|
|
33538
33719
|
}
|
|
33539
33720
|
|
|
33540
33721
|
// src/bootstrap-propagation-identity.ts
|
|
33541
|
-
var
|
|
33722
|
+
var import_node_crypto12 = require("node:crypto");
|
|
33542
33723
|
var PROPAGATION_BRANCH_PREFIX = "seed-propagate-";
|
|
33543
33724
|
var TARGET_MARKER_NAME = "mmi-bootstrap-propagation-target";
|
|
33544
33725
|
function safeBranchPart(value, maxLength, fallback) {
|
|
@@ -33551,7 +33732,7 @@ function repoSlug2(repo) {
|
|
|
33551
33732
|
function propagationBranch(repo, target) {
|
|
33552
33733
|
const repoPart = safeBranchPart(repoSlug2(repo), 32, "repo");
|
|
33553
33734
|
const targetPart = safeBranchPart(target, 48, "target");
|
|
33554
|
-
const hash = (0,
|
|
33735
|
+
const hash = (0, import_node_crypto12.createHash)("sha256").update(repo.trim().toLowerCase()).update("\0").update(target).digest("hex").slice(0, 12);
|
|
33555
33736
|
return `${PROPAGATION_BRANCH_PREFIX}${repoPart}-${targetPart}-${hash}`;
|
|
33556
33737
|
}
|
|
33557
33738
|
function legacyPropagationBranch(repo) {
|
|
@@ -34763,7 +34944,7 @@ function renderBootstrapVerifyReport(report) {
|
|
|
34763
34944
|
}
|
|
34764
34945
|
|
|
34765
34946
|
// src/bootstrap-seed-delivery.ts
|
|
34766
|
-
var
|
|
34947
|
+
var import_node_crypto13 = require("node:crypto");
|
|
34767
34948
|
init_github_client();
|
|
34768
34949
|
var SHA = /^[a-f0-9]{40}$/;
|
|
34769
34950
|
async function readSeedFile(repo, target, ref, client) {
|
|
@@ -34796,7 +34977,7 @@ async function targetedSeedPlan(repo, slug, target, baseBranch, sourceSha, clien
|
|
|
34796
34977
|
}
|
|
34797
34978
|
const baseSha = base?.object?.sha;
|
|
34798
34979
|
if (!baseSha || !SHA.test(baseSha) || !SHA.test(sourceSha)) throw new Error("bootstrap apply: seed identity is unreadable");
|
|
34799
|
-
const identity = (0,
|
|
34980
|
+
const identity = (0, import_node_crypto13.createHash)("sha256").update(JSON.stringify([repo.toLowerCase(), target, baseSha, sourceSha])).digest("hex").slice(0, 24);
|
|
34800
34981
|
const delivery = planSeedDelivery(rules, slug, baseBranch, `bootstrap-seed-${identity}`);
|
|
34801
34982
|
const scoped = forcePr && delivery.mode === "direct" ? { mode: "pr", ref: `bootstrap-seed-${identity}-${slug}`, branch: `bootstrap-seed-${identity}-${slug}`, reason: "registry gate render requires review" } : delivery;
|
|
34802
34983
|
return { ...scoped, baseSha, baseBranch };
|
|
@@ -34862,12 +35043,12 @@ async function verifyTargetedSeedPr(repo, number, plan, target, expected, client
|
|
|
34862
35043
|
// src/bootstrap-commands.ts
|
|
34863
35044
|
var execGitForSeedSource = async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
34864
35045
|
async function gateRenderDiff(current, rendered) {
|
|
34865
|
-
const dir = (0,
|
|
34866
|
-
const before = (0,
|
|
34867
|
-
const after = (0,
|
|
35046
|
+
const dir = (0, import_node_fs30.mkdtempSync)((0, import_node_path29.join)((0, import_node_os18.tmpdir)(), "mmi-gate-diff-"));
|
|
35047
|
+
const before = (0, import_node_path29.join)(dir, "committed-gate.yml");
|
|
35048
|
+
const after = (0, import_node_path29.join)(dir, "registry-render.yml");
|
|
34868
35049
|
try {
|
|
34869
|
-
(0,
|
|
34870
|
-
(0,
|
|
35050
|
+
(0, import_node_fs30.writeFileSync)(before, current);
|
|
35051
|
+
(0, import_node_fs30.writeFileSync)(after, rendered);
|
|
34871
35052
|
try {
|
|
34872
35053
|
return (await execFileP("git", ["diff", "--no-index", "--", before, after], { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
34873
35054
|
} catch (e) {
|
|
@@ -34876,7 +35057,7 @@ async function gateRenderDiff(current, rendered) {
|
|
|
34876
35057
|
throw e;
|
|
34877
35058
|
}
|
|
34878
35059
|
} finally {
|
|
34879
|
-
(0,
|
|
35060
|
+
(0, import_node_fs30.rmSync)(dir, { recursive: true, force: true });
|
|
34880
35061
|
}
|
|
34881
35062
|
}
|
|
34882
35063
|
function bootstrapTrainDoctorDeps() {
|
|
@@ -34936,13 +35117,13 @@ function registerBootstrapCommands(program3) {
|
|
|
34936
35117
|
})),
|
|
34937
35118
|
pushAllowlistOwners: verifyOwners,
|
|
34938
35119
|
pushAllowlistOwnersRead: verifyOwnersRead,
|
|
34939
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
35120
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs30.existsSync)(path2) ? (0, import_node_fs30.readFileSync)(path2, "utf8") : null,
|
|
34940
35121
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
34941
35122
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
34942
35123
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
34943
35124
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
34944
35125
|
// sanction, which is the pre-#3664 behaviour.
|
|
34945
|
-
sanctionedAdmins: (0,
|
|
35126
|
+
sanctionedAdmins: (0, import_node_fs30.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs30.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
34946
35127
|
requiredGcpApis: (() => {
|
|
34947
35128
|
const v = meta?.requiredGcpApis;
|
|
34948
35129
|
if (Array.isArray(v)) return v;
|
|
@@ -35020,14 +35201,14 @@ function registerBootstrapCommands(program3) {
|
|
|
35020
35201
|
bootstrap.command("drift").description("#3818: compare every org-owned whole-file seed against MMI-Hub's copy across the registry roster; read-only").option("--repo <owner/repo>", "audit one repo instead of the roster (never a fleet verdict)").option("--json", "machine-readable output").action(async () => {
|
|
35021
35202
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
35022
35203
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
35023
|
-
if (!(0,
|
|
35204
|
+
if (!(0, import_node_fs30.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
|
|
35024
35205
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
35025
35206
|
if (!seedSource.ok) return fail(`bootstrap drift: ${seedSource.reason}`);
|
|
35026
|
-
const manifest = loadBootstrapSeeds((0,
|
|
35207
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs30.readFileSync)(manifestPath, "utf8"));
|
|
35027
35208
|
const hubContents = /* @__PURE__ */ new Map();
|
|
35028
35209
|
for (const s of manifest.seeds) {
|
|
35029
35210
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
35030
|
-
hubContents.set(s.target, (0,
|
|
35211
|
+
hubContents.set(s.target, (0, import_node_fs30.existsSync)(s.target) ? (0, import_node_fs30.readFileSync)(s.target, "utf8") : null);
|
|
35031
35212
|
}
|
|
35032
35213
|
let targets;
|
|
35033
35214
|
let classOf = (_repo) => "deployable";
|
|
@@ -35235,24 +35416,24 @@ function registerBootstrapCommands(program3) {
|
|
|
35235
35416
|
return;
|
|
35236
35417
|
}
|
|
35237
35418
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
35238
|
-
if (!(0,
|
|
35419
|
+
if (!(0, import_node_fs30.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
|
|
35239
35420
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
35240
35421
|
if (!seedSource.ok) return fail(`bootstrap apply: ${seedSource.reason}`);
|
|
35241
|
-
const manifest = loadBootstrapSeeds((0,
|
|
35422
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs30.readFileSync)(manifestPath, "utf8"));
|
|
35242
35423
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
35243
35424
|
const slug = parsedRepo.slug;
|
|
35244
35425
|
const onlyTarget = o.fromConfig && o.only.trim() === "gate.yml" ? ".github/workflows/gate.yml" : o.only.trim();
|
|
35245
35426
|
const gh2 = async (args) => execFileP("gh", args, { timeout: 2e4 });
|
|
35246
|
-
const readFile10 = (p) => (0,
|
|
35427
|
+
const readFile10 = (p) => (0, import_node_fs30.existsSync)(p) ? (0, import_node_fs30.readFileSync)(p, "utf8") : null;
|
|
35247
35428
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
35248
35429
|
const putSeed = async (target, content, ref, sha) => {
|
|
35249
|
-
const tmp = (0,
|
|
35250
|
-
(0,
|
|
35430
|
+
const tmp = (0, import_node_path29.join)((0, import_node_os18.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
35431
|
+
(0, import_node_fs30.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
35251
35432
|
try {
|
|
35252
35433
|
await gh2(contentPutInputArgs(repo, target, tmp));
|
|
35253
35434
|
} finally {
|
|
35254
35435
|
try {
|
|
35255
|
-
(0,
|
|
35436
|
+
(0, import_node_fs30.unlinkSync)(tmp);
|
|
35256
35437
|
} catch {
|
|
35257
35438
|
}
|
|
35258
35439
|
}
|
|
@@ -35888,10 +36069,10 @@ LIVE apply to ${repo}:
|
|
|
35888
36069
|
bootstrap.command("propagate").description("#4238: re-entrant canary\u2192wave tick \u2014 plan (or, with --execute, open) per-repo PRs fanning an org-owned seed out to the fleet").option("--target <path>", "the manifest target to propagate (an org-owned whole file or declared Hub-managed block)").option("--execute", "LIVE tick via gh (master-gated) \u2014 opens/reuses per-repo seed-propagate PRs; dry-run prints the plan only").option("--json", "machine-readable output").action(async () => {
|
|
35889
36070
|
const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
|
|
35890
36071
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
35891
|
-
if (!(0,
|
|
36072
|
+
if (!(0, import_node_fs30.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`);
|
|
35892
36073
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
35893
36074
|
if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
|
|
35894
|
-
const manifest = loadBootstrapSeeds((0,
|
|
36075
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs30.readFileSync)(manifestPath, "utf8"));
|
|
35895
36076
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
35896
36077
|
if (!o.target) {
|
|
35897
36078
|
return fail(`bootstrap propagate: --target <path> is required \u2014 one of:
|
|
@@ -35900,9 +36081,9 @@ LIVE apply to ${repo}:
|
|
|
35900
36081
|
const seed = propagatable.find((s) => s.target === o.target);
|
|
35901
36082
|
if (!seed) return fail(`bootstrap propagate: --target '${o.target}' names no centrally propagatable seed in ${manifestPath}. Propagatable targets:
|
|
35902
36083
|
${propagatable.map((s) => s.target).join("\n ")}`);
|
|
35903
|
-
if (!seed.managedBlock && !(0,
|
|
35904
|
-
const hubContent = seed.managedBlock ? null : (0,
|
|
35905
|
-
const readSeedFile2 = (path2) => (0,
|
|
36084
|
+
if (!seed.managedBlock && !(0, import_node_fs30.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
|
|
36085
|
+
const hubContent = seed.managedBlock ? null : (0, import_node_fs30.readFileSync)(seed.target, "utf8");
|
|
36086
|
+
const readSeedFile2 = (path2) => (0, import_node_fs30.existsSync)(path2) ? (0, import_node_fs30.readFileSync)(path2, "utf8") : null;
|
|
35906
36087
|
const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
|
|
35907
36088
|
const cfg = await loadConfig();
|
|
35908
36089
|
const projects = await fetchProjectsList(registryClientDeps(cfg));
|
|
@@ -35911,9 +36092,9 @@ LIVE apply to ${repo}:
|
|
|
35911
36092
|
}
|
|
35912
36093
|
const rosterRepos = collectRegistryRepos(projects).filter((r) => r.toLowerCase() !== "mutmutco/mmi-hub");
|
|
35913
36094
|
let independentCount = rosterRepos.length;
|
|
35914
|
-
if ((0,
|
|
36095
|
+
if ((0, import_node_fs30.existsSync)("projects.json")) {
|
|
35915
36096
|
try {
|
|
35916
|
-
const local = JSON.parse((0,
|
|
36097
|
+
const local = JSON.parse((0, import_node_fs30.readFileSync)("projects.json", "utf8"));
|
|
35917
36098
|
const localRepos = /* @__PURE__ */ new Set();
|
|
35918
36099
|
for (const p of local.projects ?? []) for (const r of p.repos ?? []) {
|
|
35919
36100
|
const full = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
|
|
@@ -36083,15 +36264,15 @@ LIVE apply to ${repo}:
|
|
|
36083
36264
|
} catch {
|
|
36084
36265
|
existingSha = void 0;
|
|
36085
36266
|
}
|
|
36086
|
-
const tmp = (0,
|
|
36267
|
+
const tmp = (0, import_node_path29.join)((0, import_node_os18.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
36087
36268
|
const desiredContent = desiredByRepo.get(rec.repo);
|
|
36088
36269
|
if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
|
|
36089
|
-
(0,
|
|
36270
|
+
(0, import_node_fs30.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha)), "utf8");
|
|
36090
36271
|
try {
|
|
36091
36272
|
await gh2(contentPutInputArgs(rec.repo, seed.target, tmp));
|
|
36092
36273
|
} finally {
|
|
36093
36274
|
try {
|
|
36094
|
-
(0,
|
|
36275
|
+
(0, import_node_fs30.unlinkSync)(tmp);
|
|
36095
36276
|
} catch {
|
|
36096
36277
|
}
|
|
36097
36278
|
}
|
|
@@ -36160,10 +36341,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
36160
36341
|
return fail(`bootstrap rollback: ${e.message}`);
|
|
36161
36342
|
}
|
|
36162
36343
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
36163
|
-
if (!(0,
|
|
36344
|
+
if (!(0, import_node_fs30.existsSync)(manifestPath)) return fail(`bootstrap rollback: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the manifest names which targets are org-owned and therefore propagated (and rollback-able)`);
|
|
36164
36345
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
36165
36346
|
if (!seedSource.ok) return fail(`bootstrap rollback: ${seedSource.reason}`);
|
|
36166
|
-
const manifest = loadBootstrapSeeds((0,
|
|
36347
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs30.readFileSync)(manifestPath, "utf8"));
|
|
36167
36348
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
36168
36349
|
if (!o.target) {
|
|
36169
36350
|
return fail(`bootstrap rollback: --target <path> is required \u2014 one of:
|
|
@@ -36180,10 +36361,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
36180
36361
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
36181
36362
|
let candidates;
|
|
36182
36363
|
if (o.record) {
|
|
36183
|
-
if (!(0,
|
|
36364
|
+
if (!(0, import_node_fs30.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
|
|
36184
36365
|
let parsed;
|
|
36185
36366
|
try {
|
|
36186
|
-
parsed = JSON.parse((0,
|
|
36367
|
+
parsed = JSON.parse((0, import_node_fs30.readFileSync)(o.record, "utf8"));
|
|
36187
36368
|
} catch (e) {
|
|
36188
36369
|
return fail(`bootstrap rollback: --record '${o.record}' is not valid JSON: ${e.message}`);
|
|
36189
36370
|
}
|
|
@@ -36260,13 +36441,13 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
36260
36441
|
} catch {
|
|
36261
36442
|
existingSha = void 0;
|
|
36262
36443
|
}
|
|
36263
|
-
const tmp = (0,
|
|
36264
|
-
(0,
|
|
36444
|
+
const tmp = (0, import_node_path29.join)((0, import_node_os18.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
36445
|
+
(0, import_node_fs30.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
|
|
36265
36446
|
try {
|
|
36266
36447
|
await gh2(contentPutInputArgs(repo, seed.target, tmp));
|
|
36267
36448
|
} finally {
|
|
36268
36449
|
try {
|
|
36269
|
-
(0,
|
|
36450
|
+
(0, import_node_fs30.unlinkSync)(tmp);
|
|
36270
36451
|
} catch {
|
|
36271
36452
|
}
|
|
36272
36453
|
}
|
|
@@ -36442,7 +36623,7 @@ init_github_client();
|
|
|
36442
36623
|
init_cli_shared();
|
|
36443
36624
|
|
|
36444
36625
|
// src/suite-status.ts
|
|
36445
|
-
var
|
|
36626
|
+
var import_node_crypto14 = require("node:crypto");
|
|
36446
36627
|
var SUITE_REPO = "mutmutco/MM-Work";
|
|
36447
36628
|
var REPOS = {
|
|
36448
36629
|
"mmi-scout": "mutmutco/MMI-Scout",
|
|
@@ -36470,7 +36651,7 @@ function parseSuiteMapping(body) {
|
|
|
36470
36651
|
return void 0;
|
|
36471
36652
|
}
|
|
36472
36653
|
}
|
|
36473
|
-
var sha256 = (b) => (0,
|
|
36654
|
+
var sha256 = (b) => (0, import_node_crypto14.createHash)("sha256").update(b).digest("hex");
|
|
36474
36655
|
async function proveBundle(url, tag, fetchUrl) {
|
|
36475
36656
|
const get = async (u) => {
|
|
36476
36657
|
try {
|
|
@@ -36723,9 +36904,9 @@ async function waitSuiteDev(deps, opts) {
|
|
|
36723
36904
|
}
|
|
36724
36905
|
|
|
36725
36906
|
// src/suite-train.ts
|
|
36726
|
-
var
|
|
36727
|
-
var
|
|
36728
|
-
var
|
|
36907
|
+
var import_node_fs31 = require("node:fs");
|
|
36908
|
+
var import_node_path30 = require("node:path");
|
|
36909
|
+
var import_node_crypto15 = require("node:crypto");
|
|
36729
36910
|
|
|
36730
36911
|
// src/kit-version.ts
|
|
36731
36912
|
var parse = (v) => /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(typeof v === "string" ? v.trim() : "");
|
|
@@ -36810,7 +36991,7 @@ function suiteReleaseTag(rc) {
|
|
|
36810
36991
|
function readSuiteCut(path2) {
|
|
36811
36992
|
let raw;
|
|
36812
36993
|
try {
|
|
36813
|
-
raw = (0,
|
|
36994
|
+
raw = (0, import_node_fs31.readFileSync)(path2, "utf8");
|
|
36814
36995
|
} catch (e) {
|
|
36815
36996
|
if (e.code === "ENOENT") return void 0;
|
|
36816
36997
|
throw e;
|
|
@@ -36826,11 +37007,11 @@ function readSuiteCut(path2) {
|
|
|
36826
37007
|
return record;
|
|
36827
37008
|
}
|
|
36828
37009
|
function writeSuiteCut(path2, record) {
|
|
36829
|
-
(0,
|
|
36830
|
-
const temp = `${path2}.${(0,
|
|
37010
|
+
(0, import_node_fs31.mkdirSync)((0, import_node_path30.dirname)(path2), { recursive: true });
|
|
37011
|
+
const temp = `${path2}.${(0, import_node_crypto15.randomBytes)(8).toString("hex")}.tmp`;
|
|
36831
37012
|
try {
|
|
36832
|
-
(0,
|
|
36833
|
-
(0,
|
|
37013
|
+
(0, import_node_fs31.writeFileSync)(temp, JSON.stringify(record, null, 2), { flag: "wx" });
|
|
37014
|
+
(0, import_node_fs31.renameSync)(temp, path2);
|
|
36834
37015
|
} catch (e) {
|
|
36835
37016
|
throw new Error(`suite cut record write failed at ${path2}: ${e.message}; stop, verify the file before retrying`);
|
|
36836
37017
|
}
|
|
@@ -36879,25 +37060,25 @@ async function suiteMarkAbandoned(deps, ownerGo = false) {
|
|
|
36879
37060
|
return tags.map((t) => `${t.repo}@${t.tag}`);
|
|
36880
37061
|
}
|
|
36881
37062
|
async function retireFailedLeadRcLedger(deps, ownerGo) {
|
|
36882
|
-
if (!(0,
|
|
37063
|
+
if (!(0, import_node_fs31.existsSync)(deps.ledgerPath)) return void 0;
|
|
36883
37064
|
return withLedgerLock(deps.ledgerPath, async (fence) => {
|
|
36884
|
-
const ledger = parseReleaseLedger((0,
|
|
37065
|
+
const ledger = parseReleaseLedger((0, import_node_fs31.readFileSync)(deps.ledgerPath, "utf8"));
|
|
36885
37066
|
const states = Object.values(ledger.phases).map((p) => p.state);
|
|
36886
37067
|
if (ledger.repo !== SUITE_REPO || ledger.lane !== "rcand" || ledger.phases.promotion.state === "complete" || !states.includes("failed")) return void 0;
|
|
36887
37068
|
if (!ownerGo) throw new Error(`the lead's failed rcand ledger for ${ledger.tag} blocks every train; retire it with suite abandon --owner-go`);
|
|
36888
37069
|
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`);
|
|
36889
|
-
const archive = (0,
|
|
36890
|
-
if ((0,
|
|
37070
|
+
const archive = (0, import_node_path30.join)((0, import_node_path30.dirname)(deps.ledgerPath), `phases.archive-${ledger.tag}.json`);
|
|
37071
|
+
if ((0, import_node_fs31.existsSync)(archive)) throw new Error(`ledger archive already exists at ${archive}; inspect before retiring again`);
|
|
36891
37072
|
await deps.markAbandoned(SUITE_REPO, ledger.tag, ledger.tagSha);
|
|
36892
|
-
await fence(() => (0,
|
|
37073
|
+
await fence(() => (0, import_node_fs31.renameSync)(deps.ledgerPath, archive));
|
|
36893
37074
|
return `${ledger.tag} (ledger archived to ${archive})`;
|
|
36894
37075
|
});
|
|
36895
37076
|
}
|
|
36896
37077
|
function suiteAbandon(recordPath, ownerGo = false, now = /* @__PURE__ */ new Date()) {
|
|
36897
37078
|
const record = abandonableCut(recordPath, ownerGo);
|
|
36898
37079
|
const archive = `${recordPath}.abandoned-${record.leadDevelopment.slice(0, 12)}-${now.toISOString().replace(/[:.]/g, "")}.json`;
|
|
36899
|
-
if ((0,
|
|
36900
|
-
(0,
|
|
37080
|
+
if ((0, import_node_fs31.existsSync)(archive)) throw new Error(`abandoned-cut archive already exists at ${archive}; inspect before abandoning again`);
|
|
37081
|
+
(0, import_node_fs31.renameSync)(recordPath, archive);
|
|
36901
37082
|
return { archive, orphanTags: record.members.filter((m) => m.tag).map((m) => `${m.repo}@${m.tag}`) };
|
|
36902
37083
|
}
|
|
36903
37084
|
async function suiteRestoreMainPins(deps) {
|
|
@@ -36963,12 +37144,12 @@ async function suiteRcand(deps, opts) {
|
|
|
36963
37144
|
}
|
|
36964
37145
|
if ((record?.announcement === "sent" || record?.announcement === "skipped") && record.leadReleased) {
|
|
36965
37146
|
const archive = `${deps.recordPath}.${record.leadRcTag}.json`;
|
|
36966
|
-
if ((0,
|
|
36967
|
-
(0,
|
|
37147
|
+
if ((0, import_node_fs31.existsSync)(archive)) throw new Error(`suite cut archive already exists at ${archive}; inspect before a new cut`);
|
|
37148
|
+
(0, import_node_fs31.renameSync)(deps.recordPath, archive);
|
|
36968
37149
|
record = void 0;
|
|
36969
37150
|
}
|
|
36970
37151
|
if (record && !record.pinPr && !record.leadRcTag && record.members.every((m) => !m.tag)) {
|
|
36971
|
-
(0,
|
|
37152
|
+
(0, import_node_fs31.rmSync)(deps.recordPath, { force: true });
|
|
36972
37153
|
record = void 0;
|
|
36973
37154
|
}
|
|
36974
37155
|
if (!record) {
|
|
@@ -37022,7 +37203,7 @@ async function suiteRcand(deps, opts) {
|
|
|
37022
37203
|
quiet.push(member);
|
|
37023
37204
|
}
|
|
37024
37205
|
if (record.members.every((m) => m.carried || quiet.includes(m)) && await deps.contains(SUITE_REPO, record.leadDevelopment, record.leadPreviousRc)) {
|
|
37025
|
-
(0,
|
|
37206
|
+
(0, import_node_fs31.rmSync)(deps.recordPath, { force: true });
|
|
37026
37207
|
throw new Error("nothing to promote: neither MM-Work nor any module has new development commits since its rc; no suite cut started");
|
|
37027
37208
|
}
|
|
37028
37209
|
for (const member of quiet) {
|
|
@@ -37040,7 +37221,7 @@ async function suiteRcand(deps, opts) {
|
|
|
37040
37221
|
}
|
|
37041
37222
|
if (refusals.length) {
|
|
37042
37223
|
const untouched = record.members.every((m) => !m.tag) && !record.pinPr;
|
|
37043
|
-
if (untouched) (0,
|
|
37224
|
+
if (untouched) (0, import_node_fs31.rmSync)(deps.recordPath, { force: true });
|
|
37044
37225
|
throw new Error(`module rc serving is not ready; ${untouched ? "no suite cut started" : "no new tag was cut"}:
|
|
37045
37226
|
${refusals.join("\n")}
|
|
37046
37227
|
Each module must ship a live release that bind-mounts <deployPath>/data read-only and serves <data>/rc/module.js at /rc/module.js (docs/Guides/suite-trains.md).`);
|
|
@@ -37197,11 +37378,11 @@ Each member's live release must serve <data>/main/module.js at its main pin path
|
|
|
37197
37378
|
}
|
|
37198
37379
|
return record;
|
|
37199
37380
|
}
|
|
37200
|
-
var hotfixRecordPath = (deps) => (0,
|
|
37381
|
+
var hotfixRecordPath = (deps) => (0, import_node_path30.join)((0, import_node_path30.dirname)(deps.recordPath), "hotfix.json");
|
|
37201
37382
|
function readSuiteHotfix(path2) {
|
|
37202
37383
|
let raw;
|
|
37203
37384
|
try {
|
|
37204
|
-
raw = (0,
|
|
37385
|
+
raw = (0, import_node_fs31.readFileSync)(path2, "utf8");
|
|
37205
37386
|
} catch (e) {
|
|
37206
37387
|
if (e.code === "ENOENT") return void 0;
|
|
37207
37388
|
throw e;
|
|
@@ -37244,7 +37425,7 @@ async function suiteHotfix(deps, opts) {
|
|
|
37244
37425
|
if (completed) record = void 0;
|
|
37245
37426
|
if (record && record.repo !== found.repo) throw new Error(`an unfinished suite hotfix for ${record.repo} is recorded; finish it (rerun with that member) first`);
|
|
37246
37427
|
if (!record?.tag) await deps.validateHotfixSummary();
|
|
37247
|
-
if (completed) (0,
|
|
37428
|
+
if (completed) (0, import_node_fs31.renameSync)(path2, `${path2}.${completed.leadTag}.json`);
|
|
37248
37429
|
const rec = record ?? { schema: "mmi-suite-hotfix/v1", member: found.id, repo: found.repo, from: opts.from, ...opts.ack?.length ? { ack: opts.ack } : {} };
|
|
37249
37430
|
const save = () => writeSuiteHotfix(path2, rec);
|
|
37250
37431
|
save();
|
|
@@ -37511,8 +37692,8 @@ async function runRcDataSwitch(deps, opts) {
|
|
|
37511
37692
|
}
|
|
37512
37693
|
|
|
37513
37694
|
// src/suite-train-io.ts
|
|
37514
|
-
var
|
|
37515
|
-
var
|
|
37695
|
+
var import_node_fs45 = require("node:fs");
|
|
37696
|
+
var import_node_path44 = require("node:path");
|
|
37516
37697
|
init_cli_shared();
|
|
37517
37698
|
|
|
37518
37699
|
// src/command-register-train-operations.ts
|
|
@@ -37867,8 +38048,8 @@ async function preflightHotfixBatchPicks(deps, sources, pickTolerated) {
|
|
|
37867
38048
|
if (sources.length === 0) return { failures: [], skipped: [] };
|
|
37868
38049
|
const { mkdtemp: mkdtemp2, rm: rm2 } = await import("node:fs/promises");
|
|
37869
38050
|
const { tmpdir: tmpdir12 } = await import("node:os");
|
|
37870
|
-
const { join:
|
|
37871
|
-
const dir = await mkdtemp2(
|
|
38051
|
+
const { join: join48 } = await import("node:path");
|
|
38052
|
+
const dir = await mkdtemp2(join48(tmpdir12(), "mmi-hotfix-preflight-"));
|
|
37872
38053
|
const wt = { run: (file, args) => deps.run(file, file === "git" ? ["-C", dir, ...args] : args) };
|
|
37873
38054
|
const failures = [];
|
|
37874
38055
|
const skipped = [];
|
|
@@ -39189,9 +39370,9 @@ function checkHotfixCarries(options) {
|
|
|
39189
39370
|
}
|
|
39190
39371
|
|
|
39191
39372
|
// src/merge-cleanup.ts
|
|
39192
|
-
var
|
|
39193
|
-
var
|
|
39194
|
-
var
|
|
39373
|
+
var import_node_fs33 = require("node:fs");
|
|
39374
|
+
var import_node_path32 = require("node:path");
|
|
39375
|
+
var import_node_os19 = require("node:os");
|
|
39195
39376
|
init_cli_shared();
|
|
39196
39377
|
init_github_client();
|
|
39197
39378
|
|
|
@@ -39505,8 +39686,8 @@ async function runPrLand(prNumber, options, deps) {
|
|
|
39505
39686
|
|
|
39506
39687
|
// src/test-policy-core.ts
|
|
39507
39688
|
var import_node_child_process13 = require("node:child_process");
|
|
39508
|
-
var
|
|
39509
|
-
var
|
|
39689
|
+
var import_node_fs32 = require("node:fs");
|
|
39690
|
+
var import_node_path31 = require("node:path");
|
|
39510
39691
|
|
|
39511
39692
|
// src/test-command-policy-shared.mjs
|
|
39512
39693
|
var import_node_child_process12 = require("node:child_process");
|
|
@@ -39982,7 +40163,7 @@ function isMeaningfulRow(file) {
|
|
|
39982
40163
|
return file.meaningful !== false;
|
|
39983
40164
|
}
|
|
39984
40165
|
function loadPolicy(root, readFile10 = readFileOrNull2) {
|
|
39985
|
-
const raw = readFile10((0,
|
|
40166
|
+
const raw = readFile10((0, import_node_path31.join)(root, POLICY_FILE));
|
|
39986
40167
|
if (raw == null) return { mandatory: [], declared: false };
|
|
39987
40168
|
return parsePolicy(raw, POLICY_FILE);
|
|
39988
40169
|
}
|
|
@@ -40059,7 +40240,7 @@ function loadPolicySource(root, explicitRef, diffBase) {
|
|
|
40059
40240
|
}
|
|
40060
40241
|
function readFileOrNull2(path2) {
|
|
40061
40242
|
try {
|
|
40062
|
-
return (0,
|
|
40243
|
+
return (0, import_node_fs32.readFileSync)(path2, "utf8");
|
|
40063
40244
|
} catch {
|
|
40064
40245
|
return null;
|
|
40065
40246
|
}
|
|
@@ -40087,12 +40268,12 @@ function classify(changed, policy, present = () => false) {
|
|
|
40087
40268
|
const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
|
|
40088
40269
|
return { mandatoryHits, untestedHits, testChanges, meaningfulTestChanges, addedTests, removedProtected };
|
|
40089
40270
|
}
|
|
40090
|
-
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0,
|
|
40091
|
-
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0,
|
|
40271
|
+
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs32.existsSync)(path2)) {
|
|
40272
|
+
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path31.join)(root, p)));
|
|
40092
40273
|
}
|
|
40093
|
-
function unresolvedSatisfiers(policy, root, exists = (path2) => (0,
|
|
40274
|
+
function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs32.existsSync)(path2)) {
|
|
40094
40275
|
const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
|
|
40095
|
-
return [...new Set(declared)].filter((p) => !exists((0,
|
|
40276
|
+
return [...new Set(declared)].filter((p) => !exists((0, import_node_path31.join)(root, p)));
|
|
40096
40277
|
}
|
|
40097
40278
|
function evaluate(changed, policy, present = () => false) {
|
|
40098
40279
|
const { mandatoryHits, untestedHits, meaningfulTestChanges, addedTests, removedProtected } = classify(changed, policy, present);
|
|
@@ -40231,14 +40412,14 @@ function runTestPolicy(root, deps = {}) {
|
|
|
40231
40412
|
}
|
|
40232
40413
|
const loaded = deps.policy ? { policy: deps.policy, source: { ref: null, sha: null, path: POLICY_FILE } } : loadPolicySource(root, deps.policyRef, deps.base);
|
|
40233
40414
|
const { policy, source: policySource } = loaded;
|
|
40234
|
-
const exists = deps.exists ?? ((path2) => (0,
|
|
40415
|
+
const exists = deps.exists ?? ((path2) => (0, import_node_fs32.existsSync)(path2));
|
|
40235
40416
|
const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
|
|
40236
40417
|
const base = deps.changed ? "(injected)" : deps.policyRef ? resolveHotfixBase(root) : resolveBase(root, deps.base);
|
|
40237
40418
|
const refusal = deps.changed ? null : untrustworthyRange(root, base);
|
|
40238
40419
|
const raw = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
|
|
40239
40420
|
const changed = deps.changed ? raw : annotateChangeMeaning(raw, policy, (path2) => ({
|
|
40240
40421
|
before: blobAt(base, path2, root),
|
|
40241
|
-
after: readFileOrNull2((0,
|
|
40422
|
+
after: readFileOrNull2((0, import_node_path31.join)(root, path2))
|
|
40242
40423
|
}));
|
|
40243
40424
|
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride2(base, root) : { override: null, refusals: [] };
|
|
40244
40425
|
const policyRefusals = policy.declared === false ? [{
|
|
@@ -40248,8 +40429,8 @@ function runTestPolicy(root, deps = {}) {
|
|
|
40248
40429
|
A gate with no policy cannot decide which test commands are required or permitted.
|
|
40249
40430
|
` + (deps.base === HOTFIX_DIFF_BASE ? ` Hotfix lane (--base ${HOTFIX_DIFF_BASE}): pass --policy-ref <exact-${HOTFIX_POLICY_REF}-commit> so the current policy is read from ${HOTFIX_POLICY_REF}.` : ` Restore ${POLICY_FILE} on this branch; the policy file belongs on every non-hotfix lane.`)
|
|
40250
40431
|
}] : [];
|
|
40251
|
-
const present = (path2) => exists((0,
|
|
40252
|
-
const policyTree = policySource.sha ? new Set(git2(["ls-tree", "-r", "-z", "--name-only", policySource.sha], root).split("\0").filter(Boolean).map((p) => (0,
|
|
40432
|
+
const present = (path2) => exists((0, import_node_path31.join)(root, path2));
|
|
40433
|
+
const policyTree = policySource.sha ? new Set(git2(["ls-tree", "-r", "-z", "--name-only", policySource.sha], root).split("\0").filter(Boolean).map((p) => (0, import_node_path31.join)(root, p))) : null;
|
|
40253
40434
|
const policyTreeExists = policyTree ? (abs) => policyTree.has(abs) : exists;
|
|
40254
40435
|
const where = policySource.sha ? `${policySource.ref}@${policySource.sha}` : "this worktree";
|
|
40255
40436
|
const removedByThisDiff = removedPaths(changed);
|
|
@@ -40529,13 +40710,13 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
|
|
|
40529
40710
|
return rewritten === base ? null : rewritten;
|
|
40530
40711
|
}
|
|
40531
40712
|
function writeSquashBodyFile(body) {
|
|
40532
|
-
const dir = (0,
|
|
40533
|
-
const path2 = (0,
|
|
40534
|
-
(0,
|
|
40713
|
+
const dir = (0, import_node_fs33.mkdtempSync)((0, import_node_path32.join)((0, import_node_os19.tmpdir)(), "mmi-squash-body-"));
|
|
40714
|
+
const path2 = (0, import_node_path32.join)(dir, "body.txt");
|
|
40715
|
+
(0, import_node_fs33.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
|
|
40535
40716
|
`, "utf8");
|
|
40536
40717
|
return { path: path2, cleanup: () => {
|
|
40537
40718
|
try {
|
|
40538
|
-
(0,
|
|
40719
|
+
(0, import_node_fs33.rmSync)(dir, { recursive: true, force: true });
|
|
40539
40720
|
} catch {
|
|
40540
40721
|
}
|
|
40541
40722
|
} };
|
|
@@ -40742,8 +40923,8 @@ async function deleteMergedRemoteBranch(options) {
|
|
|
40742
40923
|
}
|
|
40743
40924
|
|
|
40744
40925
|
// src/project-info-sync.ts
|
|
40745
|
-
var
|
|
40746
|
-
var
|
|
40926
|
+
var import_node_fs34 = require("node:fs");
|
|
40927
|
+
var import_node_path33 = require("node:path");
|
|
40747
40928
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
40748
40929
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
40749
40930
|
projectV2 { id }
|
|
@@ -40790,14 +40971,14 @@ function sharedName(entries, fallback) {
|
|
|
40790
40971
|
}
|
|
40791
40972
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
40792
40973
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
40793
|
-
const readmePath = (0,
|
|
40794
|
-
if (!(0,
|
|
40974
|
+
const readmePath = (0, import_node_path33.join)(repoRoot2, "README.md");
|
|
40975
|
+
if (!(0, import_node_fs34.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
40795
40976
|
const entries = entriesFor(project2, projects);
|
|
40796
40977
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
40797
40978
|
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
40798
40979
|
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
40799
40980
|
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
40800
|
-
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0,
|
|
40981
|
+
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs34.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
|
|
40801
40982
|
const lines2 = [
|
|
40802
40983
|
`# ${projectName}`,
|
|
40803
40984
|
"",
|
|
@@ -40816,8 +40997,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
40816
40997
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
40817
40998
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
40818
40999
|
const orgDocs = [
|
|
40819
|
-
(0,
|
|
40820
|
-
(0,
|
|
41000
|
+
(0, import_node_fs34.existsSync)((0, import_node_path33.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
|
|
41001
|
+
(0, import_node_fs34.existsSync)((0, import_node_path33.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
|
|
40821
41002
|
].filter(Boolean);
|
|
40822
41003
|
if (orgDocs.length) lines2.push("", "## Organisation docs", "", ...orgDocs);
|
|
40823
41004
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines2.join("\n")}
|
|
@@ -40839,8 +41020,8 @@ async function syncProjectInfo(plan, client, apply) {
|
|
|
40839
41020
|
}
|
|
40840
41021
|
|
|
40841
41022
|
// src/train-commands.ts
|
|
40842
|
-
var
|
|
40843
|
-
var
|
|
41023
|
+
var import_node_fs35 = require("node:fs");
|
|
41024
|
+
var import_node_path34 = require("node:path");
|
|
40844
41025
|
init_cli_shared();
|
|
40845
41026
|
init_clean_exit();
|
|
40846
41027
|
init_client_version();
|
|
@@ -40855,7 +41036,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
40855
41036
|
}
|
|
40856
41037
|
function readRepoVersion() {
|
|
40857
41038
|
try {
|
|
40858
|
-
return JSON.parse((0,
|
|
41039
|
+
return JSON.parse((0, import_node_fs35.readFileSync)((0, import_node_path34.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
40859
41040
|
} catch {
|
|
40860
41041
|
return void 0;
|
|
40861
41042
|
}
|
|
@@ -40966,7 +41147,7 @@ function releaseProgressVerdict(status, subject = "Release") {
|
|
|
40966
41147
|
|
|
40967
41148
|
// src/suite-pin.ts
|
|
40968
41149
|
var import_promises6 = require("node:fs/promises");
|
|
40969
|
-
var
|
|
41150
|
+
var import_node_path35 = require("node:path");
|
|
40970
41151
|
var SuitePinError = class _SuitePinError extends Error {
|
|
40971
41152
|
diagnostic;
|
|
40972
41153
|
constructor(repo, tag, error) {
|
|
@@ -41034,8 +41215,8 @@ async function openSuiteCutPinPr(deps, input) {
|
|
|
41034
41215
|
if (input.carriedOnly) return { status: "unchanged" };
|
|
41035
41216
|
throw new Error("pins already match without a merged cut PR \u2014 provenance unverified");
|
|
41036
41217
|
}
|
|
41037
|
-
await (0, import_promises6.mkdir)((0,
|
|
41038
|
-
scratch = await (0, import_promises6.mkdtemp)((0,
|
|
41218
|
+
await (0, import_promises6.mkdir)((0, import_node_path35.join)(deps.root, "tmp"), { recursive: true });
|
|
41219
|
+
scratch = await (0, import_promises6.mkdtemp)((0, import_node_path35.join)(deps.root, "tmp", "suite-pin-"));
|
|
41039
41220
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
41040
41221
|
await run("git", ["clone", "--quiet", "--branch", prs.length ? branch : "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
41041
41222
|
if (prs.length) {
|
|
@@ -41047,7 +41228,7 @@ async function openSuiteCutPinPr(deps, input) {
|
|
|
41047
41228
|
await run("git", ["switch", "-c", branch]);
|
|
41048
41229
|
}
|
|
41049
41230
|
for (const stage of ["rc", "main"]) {
|
|
41050
|
-
const path2 = (0,
|
|
41231
|
+
const path2 = (0, import_node_path35.join)(scratch, `${stage}-pins.json`);
|
|
41051
41232
|
await (0, import_promises6.writeFile)(path2, JSON.stringify(input.pins.map((p) => ({ module: p.module, url: p[stage] }))));
|
|
41052
41233
|
await run("node", ["scripts/pin-modules.mjs", "--stage", stage, "--pins", path2]);
|
|
41053
41234
|
}
|
|
@@ -41100,12 +41281,12 @@ async function openSuiteRestorePinPr(deps, input) {
|
|
|
41100
41281
|
}
|
|
41101
41282
|
});
|
|
41102
41283
|
if (!todo.length) return void 0;
|
|
41103
|
-
await (0, import_promises6.mkdir)((0,
|
|
41104
|
-
scratch = await (0, import_promises6.mkdtemp)((0,
|
|
41284
|
+
await (0, import_promises6.mkdir)((0, import_node_path35.join)(deps.root, "tmp"), { recursive: true });
|
|
41285
|
+
scratch = await (0, import_promises6.mkdtemp)((0, import_node_path35.join)(deps.root, "tmp", "suite-pin-"));
|
|
41105
41286
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
41106
41287
|
await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
41107
41288
|
await run("git", ["switch", "-c", branch]);
|
|
41108
|
-
const path2 = (0,
|
|
41289
|
+
const path2 = (0, import_node_path35.join)(scratch, "main-pins.json");
|
|
41109
41290
|
await (0, import_promises6.writeFile)(path2, JSON.stringify(todo.map((p) => ({ module: p.module, url: p.prior }))));
|
|
41110
41291
|
await run("node", ["scripts/pin-modules.mjs", "--stage", "main", "--pins", path2]);
|
|
41111
41292
|
await run("git", ["add", "seed/modules.json"]);
|
|
@@ -41179,8 +41360,8 @@ async function openSuitePinPr(deps, input) {
|
|
|
41179
41360
|
if (newer.length) return { status: "superseded", pr: newer[0].url, url: pin };
|
|
41180
41361
|
const open2 = onBranch.find((p) => p.state === "OPEN");
|
|
41181
41362
|
if (open2 && tagOf(open2.title ?? "") === tag) return { status: "pending", pr: open2.url, url: pin };
|
|
41182
|
-
await (0, import_promises6.mkdir)((0,
|
|
41183
|
-
scratch = await (0, import_promises6.mkdtemp)((0,
|
|
41363
|
+
await (0, import_promises6.mkdir)((0, import_node_path35.join)(deps.root, "tmp"), { recursive: true });
|
|
41364
|
+
scratch = await (0, import_promises6.mkdtemp)((0, import_node_path35.join)(deps.root, "tmp", "suite-pin-"));
|
|
41184
41365
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
41185
41366
|
await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
41186
41367
|
await run("git", ["switch", "-c", branch]);
|
|
@@ -41220,7 +41401,7 @@ async function openSuitePinPr(deps, input) {
|
|
|
41220
41401
|
|
|
41221
41402
|
// src/command-register-collaboration.ts
|
|
41222
41403
|
var import_node_child_process17 = require("node:child_process");
|
|
41223
|
-
var
|
|
41404
|
+
var import_node_fs44 = require("node:fs");
|
|
41224
41405
|
var import_promises8 = require("node:fs/promises");
|
|
41225
41406
|
init_clean_exit();
|
|
41226
41407
|
init_cli_shared();
|
|
@@ -41244,11 +41425,11 @@ function spawnDetachedSelf(args, deps, opts = {}) {
|
|
|
41244
41425
|
}
|
|
41245
41426
|
|
|
41246
41427
|
// src/command-register-collaboration.ts
|
|
41247
|
-
var
|
|
41428
|
+
var import_node_path43 = require("node:path");
|
|
41248
41429
|
init_house_map();
|
|
41249
41430
|
|
|
41250
41431
|
// src/issue-body.ts
|
|
41251
|
-
var
|
|
41432
|
+
var import_node_os20 = require("node:os");
|
|
41252
41433
|
init_error_codes();
|
|
41253
41434
|
var TextArgError = class extends Error {
|
|
41254
41435
|
constructor(message2, code, offendingFlag) {
|
|
@@ -41261,7 +41442,7 @@ var TextArgError = class extends Error {
|
|
|
41261
41442
|
offendingFlag;
|
|
41262
41443
|
};
|
|
41263
41444
|
function emptyStdinMessage(fileFlag) {
|
|
41264
|
-
if ((0,
|
|
41445
|
+
if ((0, import_node_os20.platform)() === "win32") {
|
|
41265
41446
|
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)`;
|
|
41266
41447
|
}
|
|
41267
41448
|
return `${fileFlag} - read empty stdin (nothing piped \u2014 pass a heredoc/pipe, or ${fileFlag} <path>)`;
|
|
@@ -41354,8 +41535,8 @@ init_github_client();
|
|
|
41354
41535
|
init_github_quota();
|
|
41355
41536
|
|
|
41356
41537
|
// src/issue-commands.ts
|
|
41357
|
-
var
|
|
41358
|
-
var
|
|
41538
|
+
var import_node_fs36 = require("node:fs");
|
|
41539
|
+
var import_node_crypto16 = require("node:crypto");
|
|
41359
41540
|
init_cli_shared();
|
|
41360
41541
|
init_clean_exit();
|
|
41361
41542
|
init_error_codes();
|
|
@@ -41547,7 +41728,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
41547
41728
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
41548
41729
|
const patch = {};
|
|
41549
41730
|
let bodyChanged = false;
|
|
41550
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
41731
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs36.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
41551
41732
|
if (options.titleFile !== void 0) {
|
|
41552
41733
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
41553
41734
|
} else if (options.title !== void 0) {
|
|
@@ -41828,7 +42009,7 @@ function rowIdempotencyKey(batchKey, spec) {
|
|
|
41828
42009
|
const identity = `${spec.type}
|
|
41829
42010
|
${spec.title.trim()}
|
|
41830
42011
|
${spec.body ?? ""}`;
|
|
41831
|
-
const hash = (0,
|
|
42012
|
+
const hash = (0, import_node_crypto16.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
|
|
41832
42013
|
return `${batchKey}:${hash}`;
|
|
41833
42014
|
}
|
|
41834
42015
|
var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
|
|
@@ -42210,7 +42391,7 @@ function extendCreateCommand(issue, batchAttach) {
|
|
|
42210
42391
|
if (opts.batch) {
|
|
42211
42392
|
let specs;
|
|
42212
42393
|
try {
|
|
42213
|
-
const raw = (0,
|
|
42394
|
+
const raw = (0, import_node_fs36.readFileSync)(opts.batch, "utf8");
|
|
42214
42395
|
specs = JSON.parse(raw);
|
|
42215
42396
|
if (!Array.isArray(specs)) {
|
|
42216
42397
|
const top = specs === null ? "null" : typeof specs === "object" ? `an object with keys: ${Object.keys(specs).join(", ") || "(none)"}` : `a ${typeof specs}`;
|
|
@@ -42287,8 +42468,8 @@ ${lines2}`, {
|
|
|
42287
42468
|
}
|
|
42288
42469
|
|
|
42289
42470
|
// src/post-merge-recon.ts
|
|
42290
|
-
var
|
|
42291
|
-
var
|
|
42471
|
+
var import_node_fs37 = require("node:fs");
|
|
42472
|
+
var import_node_path36 = require("node:path");
|
|
42292
42473
|
|
|
42293
42474
|
// src/cross-repo-filing-issue.ts
|
|
42294
42475
|
init_github_client();
|
|
@@ -42457,16 +42638,16 @@ function buildPostMergeReconRecovery(input) {
|
|
|
42457
42638
|
}
|
|
42458
42639
|
function writePostMergeReconRecovery(cwd, recovery) {
|
|
42459
42640
|
const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
|
|
42460
|
-
(0,
|
|
42461
|
-
(0,
|
|
42641
|
+
(0, import_node_fs37.mkdirSync)((0, import_node_path36.dirname)(path2), { recursive: true });
|
|
42642
|
+
(0, import_node_fs37.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
|
|
42462
42643
|
`, "utf8");
|
|
42463
42644
|
return path2;
|
|
42464
42645
|
}
|
|
42465
42646
|
function clearPostMergeReconRecovery(cwd, repo, pr) {
|
|
42466
42647
|
const path2 = postMergeReconStatePath(cwd, repo, pr);
|
|
42467
|
-
if (!(0,
|
|
42648
|
+
if (!(0, import_node_fs37.existsSync)(path2)) return;
|
|
42468
42649
|
try {
|
|
42469
|
-
(0,
|
|
42650
|
+
(0, import_node_fs37.unlinkSync)(path2);
|
|
42470
42651
|
} catch {
|
|
42471
42652
|
}
|
|
42472
42653
|
}
|
|
@@ -42942,9 +43123,9 @@ function registerPrLifecycleCommands(program3) {
|
|
|
42942
43123
|
|
|
42943
43124
|
// src/review-verdict.ts
|
|
42944
43125
|
var import_node_child_process14 = require("node:child_process");
|
|
42945
|
-
var
|
|
42946
|
-
var
|
|
42947
|
-
var
|
|
43126
|
+
var import_node_fs38 = require("node:fs");
|
|
43127
|
+
var import_node_os21 = require("node:os");
|
|
43128
|
+
var import_node_path37 = require("node:path");
|
|
42948
43129
|
init_cli_shared();
|
|
42949
43130
|
var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
|
|
42950
43131
|
var REVIEW_VERDICTS = ["PROCEED", "CORRECT", "ESCALATE"];
|
|
@@ -43109,15 +43290,15 @@ async function readPrIssueComments(number, repo) {
|
|
|
43109
43290
|
return comments;
|
|
43110
43291
|
}
|
|
43111
43292
|
async function postPrCommentFromFile(number, repo, body) {
|
|
43112
|
-
const dir = (0,
|
|
43113
|
-
const path2 = (0,
|
|
43293
|
+
const dir = (0, import_node_fs38.mkdtempSync)((0, import_node_path37.join)((0, import_node_os21.tmpdir)(), "mmi-review-verdict-"));
|
|
43294
|
+
const path2 = (0, import_node_path37.join)(dir, "body.md");
|
|
43114
43295
|
try {
|
|
43115
|
-
(0,
|
|
43296
|
+
(0, import_node_fs38.writeFileSync)(path2, body, "utf8");
|
|
43116
43297
|
const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
43117
43298
|
return stdout.trim();
|
|
43118
43299
|
} finally {
|
|
43119
43300
|
try {
|
|
43120
|
-
(0,
|
|
43301
|
+
(0, import_node_fs38.rmSync)(dir, { recursive: true, force: true });
|
|
43121
43302
|
} catch {
|
|
43122
43303
|
}
|
|
43123
43304
|
}
|
|
@@ -43351,18 +43532,18 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
|
|
|
43351
43532
|
}
|
|
43352
43533
|
|
|
43353
43534
|
// src/worktree-residue-sweep.ts
|
|
43354
|
-
var
|
|
43355
|
-
var
|
|
43535
|
+
var import_node_fs43 = require("node:fs");
|
|
43536
|
+
var import_node_path42 = require("node:path");
|
|
43356
43537
|
|
|
43357
43538
|
// src/worktree-merge-cleanup.ts
|
|
43358
|
-
var
|
|
43359
|
-
var
|
|
43539
|
+
var import_node_fs42 = require("node:fs");
|
|
43540
|
+
var import_node_path41 = require("node:path");
|
|
43360
43541
|
init_cli_shared();
|
|
43361
43542
|
|
|
43362
43543
|
// src/stage-runner.ts
|
|
43363
43544
|
var import_node_child_process16 = require("node:child_process");
|
|
43364
|
-
var
|
|
43365
|
-
var
|
|
43545
|
+
var import_node_fs40 = require("node:fs");
|
|
43546
|
+
var import_node_path39 = require("node:path");
|
|
43366
43547
|
var import_node_net = require("node:net");
|
|
43367
43548
|
var import_node_util5 = require("node:util");
|
|
43368
43549
|
|
|
@@ -43372,8 +43553,8 @@ function normalizeEol2(s) {
|
|
|
43372
43553
|
}
|
|
43373
43554
|
|
|
43374
43555
|
// src/env-detection.ts
|
|
43375
|
-
var
|
|
43376
|
-
var
|
|
43556
|
+
var import_node_fs39 = require("node:fs");
|
|
43557
|
+
var import_node_path38 = require("node:path");
|
|
43377
43558
|
var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".jerv", ".pi", ".venv", "venv"]);
|
|
43378
43559
|
function isEnvBaseName(name) {
|
|
43379
43560
|
return name.toLowerCase() === ".env";
|
|
@@ -43383,7 +43564,7 @@ function findEnvFiles(root) {
|
|
|
43383
43564
|
const walk2 = (dir, rel) => {
|
|
43384
43565
|
let entries;
|
|
43385
43566
|
try {
|
|
43386
|
-
entries = (0,
|
|
43567
|
+
entries = (0, import_node_fs39.readdirSync)(dir, { withFileTypes: true });
|
|
43387
43568
|
} catch {
|
|
43388
43569
|
return;
|
|
43389
43570
|
}
|
|
@@ -43391,13 +43572,13 @@ function findEnvFiles(root) {
|
|
|
43391
43572
|
const relPath = rel ? `${rel}/${entry.name}` : entry.name;
|
|
43392
43573
|
if (entry.isDirectory()) {
|
|
43393
43574
|
if (SKIPPED_DIRS.has(entry.name)) continue;
|
|
43394
|
-
walk2((0,
|
|
43575
|
+
walk2((0, import_node_path38.join)(dir, entry.name), relPath);
|
|
43395
43576
|
continue;
|
|
43396
43577
|
}
|
|
43397
43578
|
if (isEnvBaseName(entry.name)) {
|
|
43398
43579
|
let isDir = false;
|
|
43399
43580
|
try {
|
|
43400
|
-
isDir = (0,
|
|
43581
|
+
isDir = (0, import_node_fs39.lstatSync)((0, import_node_path38.join)(dir, entry.name)).isDirectory();
|
|
43401
43582
|
} catch {
|
|
43402
43583
|
}
|
|
43403
43584
|
if (!isDir) found.push(relPath);
|
|
@@ -43536,12 +43717,12 @@ var DOCKER_TIMEOUT_MS = 15e3;
|
|
|
43536
43717
|
function stageBash() {
|
|
43537
43718
|
if (process.platform !== "win32") return "bash";
|
|
43538
43719
|
const configured = process.env.SHELL;
|
|
43539
|
-
if (configured && (0,
|
|
43720
|
+
if (configured && (0, import_node_path39.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs40.existsSync)(configured)) return configured;
|
|
43540
43721
|
const candidates = [
|
|
43541
|
-
(0,
|
|
43542
|
-
...process.env.LOCALAPPDATA ? [(0,
|
|
43722
|
+
(0, import_node_path39.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
|
|
43723
|
+
...process.env.LOCALAPPDATA ? [(0, import_node_path39.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
|
|
43543
43724
|
];
|
|
43544
|
-
const bash = candidates.find((path2) => (0,
|
|
43725
|
+
const bash = candidates.find((path2) => (0, import_node_fs40.existsSync)(path2));
|
|
43545
43726
|
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.");
|
|
43546
43727
|
return bash;
|
|
43547
43728
|
}
|
|
@@ -43679,7 +43860,7 @@ function appendForceRecreate(up) {
|
|
|
43679
43860
|
return `${up.trimEnd()} --force-recreate`;
|
|
43680
43861
|
}
|
|
43681
43862
|
function stageStatePath(cwd = process.cwd()) {
|
|
43682
|
-
return (0,
|
|
43863
|
+
return (0, import_node_path39.join)(cwd, "tmp", "stage", "state.json");
|
|
43683
43864
|
}
|
|
43684
43865
|
function normPath(path2) {
|
|
43685
43866
|
return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
@@ -43896,8 +44077,8 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
43896
44077
|
}
|
|
43897
44078
|
function composeResolvesPort(cwd) {
|
|
43898
44079
|
if (process.env.PORT) return true;
|
|
43899
|
-
const envFile = (0,
|
|
43900
|
-
return (0,
|
|
44080
|
+
const envFile = (0, import_node_path39.join)(cwd, ".env");
|
|
44081
|
+
return (0, import_node_fs40.existsSync)(envFile) && envFileKeys((0, import_node_fs40.readFileSync)(envFile, "utf8")).has("PORT");
|
|
43901
44082
|
}
|
|
43902
44083
|
function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
43903
44084
|
const composeProject = config.env?.COMPOSE_PROJECT_NAME ?? vaultEnvMerge?.COMPOSE_PROJECT_NAME ?? stageComposeProjectName(cwd);
|
|
@@ -43912,13 +44093,13 @@ function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
|
43912
44093
|
function stageComposeFiles(cwd) {
|
|
43913
44094
|
const files = ["docker-compose.yml"];
|
|
43914
44095
|
for (const conventional of ["docker-compose.override.yml", "docker-compose.override.yaml"]) {
|
|
43915
|
-
if ((0,
|
|
44096
|
+
if ((0, import_node_fs40.existsSync)((0, import_node_path39.join)(cwd, conventional))) files.push(conventional);
|
|
43916
44097
|
}
|
|
43917
44098
|
return [...files, RUNTIME_ENV_OVERRIDE_RELPATH];
|
|
43918
44099
|
}
|
|
43919
44100
|
async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
43920
44101
|
const keys = Object.keys(vaultEnvMerge ?? {});
|
|
43921
|
-
if (!keys.length || !(0,
|
|
44102
|
+
if (!keys.length || !(0, import_node_fs40.existsSync)((0, import_node_path39.join)(cwd, "docker-compose.yml"))) return {};
|
|
43922
44103
|
let configJson;
|
|
43923
44104
|
try {
|
|
43924
44105
|
const { stdout } = await execFileP3("docker", ["compose", "config", "--format", "json"], {
|
|
@@ -43934,21 +44115,21 @@ async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
|
43934
44115
|
}
|
|
43935
44116
|
const body = renderRuntimeEnvOverride(runtimeEnvMarkedServices(configJson), keys, runtimeEnvExcludes(configJson), runtimeEnvSelects(configJson));
|
|
43936
44117
|
if (!body) return {};
|
|
43937
|
-
const overridePath = (0,
|
|
43938
|
-
(0,
|
|
43939
|
-
(0,
|
|
44118
|
+
const overridePath = (0, import_node_path39.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
|
|
44119
|
+
(0, import_node_fs40.mkdirSync)((0, import_node_path39.join)(cwd, "tmp", "stage"), { recursive: true });
|
|
44120
|
+
(0, import_node_fs40.writeFileSync)(overridePath, body, "utf8");
|
|
43940
44121
|
return stageComposeFileEnv(stageComposeFiles(cwd));
|
|
43941
44122
|
}
|
|
43942
44123
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
43943
44124
|
if (!config.ensureEnv) return;
|
|
43944
|
-
const target = (0,
|
|
43945
|
-
const example = (0,
|
|
43946
|
-
if (!(0,
|
|
43947
|
-
(0,
|
|
43948
|
-
} else if ((0,
|
|
43949
|
-
const stale = detectStaleEnvFile((0,
|
|
43950
|
-
exampleMtimeMs: (0,
|
|
43951
|
-
targetMtimeMs: (0,
|
|
44125
|
+
const target = (0, import_node_path39.join)(cwd, config.ensureEnv.target);
|
|
44126
|
+
const example = (0, import_node_path39.join)(cwd, config.ensureEnv.example);
|
|
44127
|
+
if (!(0, import_node_fs40.existsSync)(target) && (0, import_node_fs40.existsSync)(example)) {
|
|
44128
|
+
(0, import_node_fs40.copyFileSync)(example, target);
|
|
44129
|
+
} else if ((0, import_node_fs40.existsSync)(target) && (0, import_node_fs40.existsSync)(example)) {
|
|
44130
|
+
const stale = detectStaleEnvFile((0, import_node_fs40.readFileSync)(example, "utf8"), (0, import_node_fs40.readFileSync)(target, "utf8"), {
|
|
44131
|
+
exampleMtimeMs: (0, import_node_fs40.statSync)(example).mtimeMs,
|
|
44132
|
+
targetMtimeMs: (0, import_node_fs40.statSync)(target).mtimeMs
|
|
43952
44133
|
});
|
|
43953
44134
|
if (stale) {
|
|
43954
44135
|
const msg = `stale ${config.ensureEnv.target} (${stale}) \u2014 delete it or refresh from ${config.ensureEnv.example} before re-running /stage`;
|
|
@@ -43956,8 +44137,8 @@ async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
|
43956
44137
|
console.error(`mmi-cli stage: ${msg} (allowed via --allow-stale-env)`);
|
|
43957
44138
|
}
|
|
43958
44139
|
}
|
|
43959
|
-
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0,
|
|
43960
|
-
(0,
|
|
44140
|
+
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0, import_node_fs40.existsSync)(target)) {
|
|
44141
|
+
(0, import_node_fs40.writeFileSync)(target, mergeEnvSecretsIntoFile((0, import_node_fs40.readFileSync)(target, "utf8"), opts.vaultEnvMerge), "utf8");
|
|
43961
44142
|
}
|
|
43962
44143
|
}
|
|
43963
44144
|
async function gitText(cwd, args) {
|
|
@@ -43987,20 +44168,20 @@ async function resolveGlobalStatePath(cwd, explicit) {
|
|
|
43987
44168
|
return void 0;
|
|
43988
44169
|
}
|
|
43989
44170
|
function readState(path2) {
|
|
43990
|
-
if (!(0,
|
|
44171
|
+
if (!(0, import_node_fs40.existsSync)(path2)) return null;
|
|
43991
44172
|
try {
|
|
43992
|
-
return JSON.parse((0,
|
|
44173
|
+
return JSON.parse((0, import_node_fs40.readFileSync)(path2, "utf8"));
|
|
43993
44174
|
} catch {
|
|
43994
44175
|
return null;
|
|
43995
44176
|
}
|
|
43996
44177
|
}
|
|
43997
44178
|
function mkdirFor(path2) {
|
|
43998
44179
|
const dir = path2.slice(0, Math.max(path2.lastIndexOf("/"), path2.lastIndexOf("\\")));
|
|
43999
|
-
(0,
|
|
44180
|
+
(0, import_node_fs40.mkdirSync)(dir, { recursive: true });
|
|
44000
44181
|
}
|
|
44001
44182
|
function writeState(path2, state) {
|
|
44002
44183
|
mkdirFor(path2);
|
|
44003
|
-
(0,
|
|
44184
|
+
(0, import_node_fs40.writeFileSync)(path2, JSON.stringify(state, null, 2), "utf8");
|
|
44004
44185
|
}
|
|
44005
44186
|
function writeStagePortReservation(port, cwd, statePath, globalStatePath, now) {
|
|
44006
44187
|
const reservation = {
|
|
@@ -44029,7 +44210,7 @@ async function cleanupStageState(state, paths, timeoutMs, fallbackCwd, currentEn
|
|
|
44029
44210
|
);
|
|
44030
44211
|
}
|
|
44031
44212
|
for (const path2 of [...new Set(paths.filter((p) => Boolean(p)))]) {
|
|
44032
|
-
(0,
|
|
44213
|
+
(0, import_node_fs40.rmSync)(path2, { force: true });
|
|
44033
44214
|
}
|
|
44034
44215
|
}
|
|
44035
44216
|
async function killTree(pid) {
|
|
@@ -44276,8 +44457,8 @@ async function runStage(config = {}, opts = {}) {
|
|
|
44276
44457
|
});
|
|
44277
44458
|
}
|
|
44278
44459
|
} catch (e) {
|
|
44279
|
-
(0,
|
|
44280
|
-
if (globalStatePath && globalStatePath !== statePath) (0,
|
|
44460
|
+
(0, import_node_fs40.rmSync)(statePath, { force: true });
|
|
44461
|
+
if (globalStatePath && globalStatePath !== statePath) (0, import_node_fs40.rmSync)(globalStatePath, { force: true });
|
|
44281
44462
|
throw e;
|
|
44282
44463
|
}
|
|
44283
44464
|
const started = await startStage(config, {
|
|
@@ -44292,8 +44473,8 @@ async function runStage(config = {}, opts = {}) {
|
|
|
44292
44473
|
}
|
|
44293
44474
|
|
|
44294
44475
|
// src/worktree-evidence-archive.ts
|
|
44295
|
-
var
|
|
44296
|
-
var
|
|
44476
|
+
var import_node_fs41 = require("node:fs");
|
|
44477
|
+
var import_node_path40 = require("node:path");
|
|
44297
44478
|
var JERV_ARTIFACT_RUN_SCOPE_ENV_VARS = ["JERV_RUN_ID", ...SESSION_ID_ENV_VARS];
|
|
44298
44479
|
function sanitizeArchiveSegment(value, max = 80) {
|
|
44299
44480
|
const scrubbed = value.replace(/[^A-Za-z0-9._@+-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -44308,24 +44489,24 @@ function resolveArtifactRunScope(env = process.env) {
|
|
|
44308
44489
|
}
|
|
44309
44490
|
function defaultIsDirectory(path2) {
|
|
44310
44491
|
try {
|
|
44311
|
-
return (0,
|
|
44492
|
+
return (0, import_node_fs41.lstatSync)(path2).isDirectory();
|
|
44312
44493
|
} catch {
|
|
44313
44494
|
return false;
|
|
44314
44495
|
}
|
|
44315
44496
|
}
|
|
44316
44497
|
function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
44317
|
-
const exists = deps.exists ??
|
|
44498
|
+
const exists = deps.exists ?? import_node_fs41.existsSync;
|
|
44318
44499
|
const isDirectory = deps.isDirectory ?? defaultIsDirectory;
|
|
44319
|
-
const copyDir = deps.copyDir ?? ((from, to) => (0,
|
|
44500
|
+
const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs41.cpSync)(from, to, { recursive: true, force: true }));
|
|
44320
44501
|
const mkdirp = deps.mkdirp ?? ((path2) => {
|
|
44321
|
-
(0,
|
|
44502
|
+
(0, import_node_fs41.mkdirSync)(path2, { recursive: true });
|
|
44322
44503
|
});
|
|
44323
44504
|
const env = deps.env ?? process.env;
|
|
44324
44505
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
44325
44506
|
const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
|
|
44326
44507
|
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
44327
44508
|
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
44328
|
-
const source = (0,
|
|
44509
|
+
const source = (0, import_node_path40.join)(args.worktreePath, ".jerv");
|
|
44329
44510
|
if (!exists(source)) return { status: "absent" };
|
|
44330
44511
|
if (!isDirectory(source)) return { status: "skipped", reason: "jerv-not-a-directory" };
|
|
44331
44512
|
const runScope = resolveArtifactRunScope(env);
|
|
@@ -44333,7 +44514,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
44333
44514
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
44334
44515
|
const dest = resolveRoot(args.primaryRoot, "jerv-artifacts", runScope, branchSlug, stamp, ".jerv");
|
|
44335
44516
|
try {
|
|
44336
|
-
mkdirp((0,
|
|
44517
|
+
mkdirp((0, import_node_path40.dirname)(dest));
|
|
44337
44518
|
copyDir(source, dest);
|
|
44338
44519
|
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
44339
44520
|
return { status: "archived", path: dest, runScope };
|
|
@@ -44342,14 +44523,14 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
44342
44523
|
}
|
|
44343
44524
|
}
|
|
44344
44525
|
function defaultStat(path2) {
|
|
44345
|
-
const st = (0,
|
|
44526
|
+
const st = (0, import_node_fs41.statSync)(path2);
|
|
44346
44527
|
return { mtimeMs: st.mtimeMs, size: st.size, isDirectory: () => st.isDirectory() };
|
|
44347
44528
|
}
|
|
44348
44529
|
function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
44349
|
-
const exists = deps.exists ??
|
|
44530
|
+
const exists = deps.exists ?? import_node_fs41.existsSync;
|
|
44350
44531
|
const stat4 = deps.stat ?? defaultStat;
|
|
44351
|
-
const readdir2 = deps.readdir ??
|
|
44352
|
-
const tmpRoot = (0,
|
|
44532
|
+
const readdir2 = deps.readdir ?? import_node_fs41.readdirSync;
|
|
44533
|
+
const tmpRoot = (0, import_node_path40.join)(worktreePath, "tmp");
|
|
44353
44534
|
if (!exists(tmpRoot)) return [];
|
|
44354
44535
|
const entries = [];
|
|
44355
44536
|
const walk2 = (dir) => {
|
|
@@ -44360,14 +44541,14 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
|
44360
44541
|
return;
|
|
44361
44542
|
}
|
|
44362
44543
|
for (const name of names) {
|
|
44363
|
-
const full = (0,
|
|
44544
|
+
const full = (0, import_node_path40.join)(dir, name);
|
|
44364
44545
|
let st;
|
|
44365
44546
|
try {
|
|
44366
44547
|
st = stat4(full);
|
|
44367
44548
|
} catch {
|
|
44368
44549
|
continue;
|
|
44369
44550
|
}
|
|
44370
|
-
const relPath = (0,
|
|
44551
|
+
const relPath = (0, import_node_path40.relative)(worktreePath, full).replace(/\\/g, "/");
|
|
44371
44552
|
if (st.isDirectory()) {
|
|
44372
44553
|
if (st.mtimeMs > newerThanMs) entries.push({ relPath, bytes: 0, mtimeMs: st.mtimeMs });
|
|
44373
44554
|
walk2(full);
|
|
@@ -44381,10 +44562,10 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
|
44381
44562
|
return entries;
|
|
44382
44563
|
}
|
|
44383
44564
|
function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
44384
|
-
const exists = deps.exists ??
|
|
44385
|
-
const copyDir = deps.copyDir ?? ((from, to) => (0,
|
|
44565
|
+
const exists = deps.exists ?? import_node_fs41.existsSync;
|
|
44566
|
+
const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs41.cpSync)(from, to, { recursive: true, force: true }));
|
|
44386
44567
|
const mkdirp = deps.mkdirp ?? ((path2) => {
|
|
44387
|
-
(0,
|
|
44568
|
+
(0, import_node_fs41.mkdirSync)(path2, { recursive: true });
|
|
44388
44569
|
});
|
|
44389
44570
|
const env = deps.env ?? process.env;
|
|
44390
44571
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
@@ -44392,7 +44573,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
|
44392
44573
|
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
44393
44574
|
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
44394
44575
|
const scanned = scanWorktreeTmpEvidence(args.worktreePath, args.newerThanMs, deps);
|
|
44395
|
-
const source = (0,
|
|
44576
|
+
const source = (0, import_node_path40.join)(args.worktreePath, "tmp");
|
|
44396
44577
|
if (!scanned.length) return { status: "absent" };
|
|
44397
44578
|
if (!exists(source)) return { status: "absent" };
|
|
44398
44579
|
const runScope = resolveArtifactRunScope(env);
|
|
@@ -44400,7 +44581,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
|
44400
44581
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
44401
44582
|
const dest = resolveRoot(args.primaryRoot, "worktree-artifacts", runScope, branchSlug, stamp, "tmp");
|
|
44402
44583
|
try {
|
|
44403
|
-
mkdirp((0,
|
|
44584
|
+
mkdirp((0, import_node_path40.dirname)(dest));
|
|
44404
44585
|
copyDir(source, dest);
|
|
44405
44586
|
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
44406
44587
|
const bytes = scanned.reduce((sum, e) => sum + e.bytes, 0);
|
|
@@ -44469,9 +44650,9 @@ function normPath2(p) {
|
|
|
44469
44650
|
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
44470
44651
|
}
|
|
44471
44652
|
function unlinkNodeModulesJunction(wtPath) {
|
|
44472
|
-
const nm = (0,
|
|
44653
|
+
const nm = (0, import_node_path41.join)(wtPath, "node_modules");
|
|
44473
44654
|
try {
|
|
44474
|
-
if ((0,
|
|
44655
|
+
if ((0, import_node_fs42.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs42.rmdirSync)(nm);
|
|
44475
44656
|
return { ok: true };
|
|
44476
44657
|
} catch (e) {
|
|
44477
44658
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -44482,8 +44663,8 @@ function defaultSleep2(ms) {
|
|
|
44482
44663
|
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
44483
44664
|
}
|
|
44484
44665
|
async function removeResidueDirectory(wtPath, options = {}) {
|
|
44485
|
-
const { sleep: sleep2 = defaultSleep2, removeEmptyDir =
|
|
44486
|
-
const removeRecursive = options.removeRecursive ?? ((path2) => (0,
|
|
44666
|
+
const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs42.rmdirSync, exists = import_node_fs42.existsSync } = options;
|
|
44667
|
+
const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs42.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
|
|
44487
44668
|
const junction = unlinkNodeModulesJunction(wtPath);
|
|
44488
44669
|
if (!junction.ok) return junction;
|
|
44489
44670
|
try {
|
|
@@ -44606,28 +44787,28 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
|
|
|
44606
44787
|
}
|
|
44607
44788
|
async function listNestedIgnoredNodeModules(wtPath, execGit) {
|
|
44608
44789
|
const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
|
|
44609
|
-
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0,
|
|
44790
|
+
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path41.join)(wtPath, line.slice(0, -1)));
|
|
44610
44791
|
}
|
|
44611
44792
|
function safeRemoveTree(path2) {
|
|
44612
|
-
const stat4 = (0,
|
|
44793
|
+
const stat4 = (0, import_node_fs42.lstatSync)(path2);
|
|
44613
44794
|
if (stat4.isSymbolicLink()) {
|
|
44614
44795
|
try {
|
|
44615
|
-
(0,
|
|
44796
|
+
(0, import_node_fs42.rmdirSync)(path2);
|
|
44616
44797
|
} catch {
|
|
44617
|
-
(0,
|
|
44798
|
+
(0, import_node_fs42.unlinkSync)(path2);
|
|
44618
44799
|
}
|
|
44619
44800
|
return;
|
|
44620
44801
|
}
|
|
44621
44802
|
if (stat4.isDirectory()) {
|
|
44622
|
-
for (const entry of (0,
|
|
44623
|
-
(0,
|
|
44803
|
+
for (const entry of (0, import_node_fs42.readdirSync)(path2)) safeRemoveTree((0, import_node_path41.join)(path2, entry));
|
|
44804
|
+
(0, import_node_fs42.rmdirSync)(path2);
|
|
44624
44805
|
return;
|
|
44625
44806
|
}
|
|
44626
|
-
(0,
|
|
44807
|
+
(0, import_node_fs42.unlinkSync)(path2);
|
|
44627
44808
|
}
|
|
44628
44809
|
async function removeWorktreeNodeModules(wtPath) {
|
|
44629
44810
|
try {
|
|
44630
|
-
safeRemoveTree((0,
|
|
44811
|
+
safeRemoveTree((0, import_node_path41.join)(wtPath, "node_modules"));
|
|
44631
44812
|
return { ok: true };
|
|
44632
44813
|
} catch (e) {
|
|
44633
44814
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -44639,7 +44820,7 @@ function errorMessage(e) {
|
|
|
44639
44820
|
}
|
|
44640
44821
|
function resolvedOrRaw(path2) {
|
|
44641
44822
|
try {
|
|
44642
|
-
return normPath2((0,
|
|
44823
|
+
return normPath2((0, import_node_fs42.realpathSync)(path2));
|
|
44643
44824
|
} catch {
|
|
44644
44825
|
return normPath2(path2);
|
|
44645
44826
|
}
|
|
@@ -44647,10 +44828,10 @@ function resolvedOrRaw(path2) {
|
|
|
44647
44828
|
function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
44648
44829
|
let realRoot;
|
|
44649
44830
|
try {
|
|
44650
|
-
if ((0,
|
|
44831
|
+
if ((0, import_node_fs42.lstatSync)(root).isSymbolicLink()) {
|
|
44651
44832
|
return { ok: false, error: `delete root ${normPath2(root)} is a reparse point resolving to ${resolvedOrRaw(root)}` };
|
|
44652
44833
|
}
|
|
44653
|
-
realRoot = normPath2((0,
|
|
44834
|
+
realRoot = normPath2((0, import_node_fs42.realpathSync)(root));
|
|
44654
44835
|
} catch (e) {
|
|
44655
44836
|
if (e.code === "ENOENT") return { ok: true, unlinked: [] };
|
|
44656
44837
|
return { ok: false, error: `cannot resolve delete root ${normPath2(root)}: ${errorMessage(e)}` };
|
|
@@ -44666,17 +44847,17 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
|
44666
44847
|
const dir = stack.pop();
|
|
44667
44848
|
let entries;
|
|
44668
44849
|
try {
|
|
44669
|
-
entries = (0,
|
|
44850
|
+
entries = (0, import_node_fs42.readdirSync)(dir, { withFileTypes: true });
|
|
44670
44851
|
} catch (e) {
|
|
44671
44852
|
if (e.code === "ENOENT") continue;
|
|
44672
44853
|
return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
|
|
44673
44854
|
}
|
|
44674
44855
|
for (const entry of entries) {
|
|
44675
|
-
const child2 = (0,
|
|
44856
|
+
const child2 = (0, import_node_path41.join)(dir, entry.name);
|
|
44676
44857
|
if (entry.isSymbolicLink()) {
|
|
44677
44858
|
let target = "";
|
|
44678
44859
|
try {
|
|
44679
|
-
target = normPath2((0,
|
|
44860
|
+
target = normPath2((0, import_node_fs42.realpathSync)(child2));
|
|
44680
44861
|
} catch {
|
|
44681
44862
|
target = "";
|
|
44682
44863
|
}
|
|
@@ -44859,7 +45040,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
44859
45040
|
return report;
|
|
44860
45041
|
}
|
|
44861
45042
|
const execGit = options.execGit ?? (async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
44862
|
-
const pathExists = options.pathExists ??
|
|
45043
|
+
const pathExists = options.pathExists ?? import_node_fs42.existsSync;
|
|
44863
45044
|
let afterWorktrees = [];
|
|
44864
45045
|
try {
|
|
44865
45046
|
afterWorktrees = parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"]));
|
|
@@ -44999,7 +45180,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
44999
45180
|
const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
|
|
45000
45181
|
if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
|
|
45001
45182
|
unlinkedReparsePoints.push(...preHelperGuard.unlinked);
|
|
45002
|
-
if (pathExists((0,
|
|
45183
|
+
if (pathExists((0, import_node_path41.join)(wtPath, "node_modules"))) {
|
|
45003
45184
|
const nmRemoved = await (options.removeRealNodeModules ?? removeWorktreeNodeModules)(wtPath);
|
|
45004
45185
|
if (!nmRemoved.ok) {
|
|
45005
45186
|
report.worktree = {
|
|
@@ -45135,18 +45316,18 @@ function renderPrMergeCleanupLines(cleanup) {
|
|
|
45135
45316
|
// src/worktree-residue-sweep.ts
|
|
45136
45317
|
var RESIDUE_LEDGER_FILE = "mmi-worktree-residue.json";
|
|
45137
45318
|
function canonicalWorktreeRoot(primary) {
|
|
45138
|
-
return (0,
|
|
45319
|
+
return (0, import_node_path42.join)((0, import_node_path42.dirname)(primary), "mmi-worktrees", (0, import_node_path42.basename)(primary));
|
|
45139
45320
|
}
|
|
45140
45321
|
function norm(p) {
|
|
45141
45322
|
const s = p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
45142
45323
|
return process.platform === "win32" ? s.toLowerCase() : s;
|
|
45143
45324
|
}
|
|
45144
45325
|
function residueLedgerPath(gitCommonDir) {
|
|
45145
|
-
return (0,
|
|
45326
|
+
return (0, import_node_path42.join)(gitCommonDir, RESIDUE_LEDGER_FILE);
|
|
45146
45327
|
}
|
|
45147
45328
|
function readPaths(file, deps) {
|
|
45148
45329
|
try {
|
|
45149
|
-
const parsed = JSON.parse((deps.readLedger ?? ((f) => (0,
|
|
45330
|
+
const parsed = JSON.parse((deps.readLedger ?? ((f) => (0, import_node_fs43.readFileSync)(f, "utf8")))(file));
|
|
45150
45331
|
return Array.isArray(parsed) ? parsed.filter((p) => typeof p === "string") : [];
|
|
45151
45332
|
} catch {
|
|
45152
45333
|
return [];
|
|
@@ -45155,8 +45336,8 @@ function readPaths(file, deps) {
|
|
|
45155
45336
|
function writePaths(file, paths, deps) {
|
|
45156
45337
|
try {
|
|
45157
45338
|
const write = deps.writeLedger ?? ((f, t) => {
|
|
45158
|
-
(0,
|
|
45159
|
-
(0,
|
|
45339
|
+
(0, import_node_fs43.mkdirSync)((0, import_node_path42.dirname)(f), { recursive: true });
|
|
45340
|
+
(0, import_node_fs43.writeFileSync)(f, t, "utf8");
|
|
45160
45341
|
});
|
|
45161
45342
|
write(file, `${JSON.stringify([...new Set(paths)], null, 1)}
|
|
45162
45343
|
`);
|
|
@@ -45170,17 +45351,17 @@ function recordResidue(gitCommonDir, path2, deps = {}) {
|
|
|
45170
45351
|
writePaths(file, paths, deps);
|
|
45171
45352
|
}
|
|
45172
45353
|
async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
45173
|
-
const exists = deps.exists ??
|
|
45174
|
-
const readDir = deps.readDir ?? ((p) => (0,
|
|
45354
|
+
const exists = deps.exists ?? import_node_fs43.existsSync;
|
|
45355
|
+
const readDir = deps.readDir ?? ((p) => (0, import_node_fs43.readdirSync)(p));
|
|
45175
45356
|
const isDir = deps.isDir ?? ((p) => {
|
|
45176
45357
|
try {
|
|
45177
|
-
return (0,
|
|
45358
|
+
return (0, import_node_fs43.statSync)(p).isDirectory();
|
|
45178
45359
|
} catch {
|
|
45179
45360
|
return false;
|
|
45180
45361
|
}
|
|
45181
45362
|
});
|
|
45182
45363
|
const removeRecorded = deps.removeRecorded ?? ((p) => removeResidueDirectory(p));
|
|
45183
|
-
const removeEmpty = deps.removeEmpty ?? ((p) => (0,
|
|
45364
|
+
const removeEmpty = deps.removeEmpty ?? ((p) => (0, import_node_fs43.rmdirSync)(p));
|
|
45184
45365
|
const report = { swept: [], kept: [], dropped: [], emptyRemoved: [], unrecorded: [] };
|
|
45185
45366
|
const live = new Set(registered.map(norm));
|
|
45186
45367
|
const primary = registered[0] ? norm(registered[0]) : void 0;
|
|
@@ -45215,7 +45396,7 @@ async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
|
45215
45396
|
} catch {
|
|
45216
45397
|
}
|
|
45217
45398
|
for (const name of children) {
|
|
45218
|
-
const child2 = (0,
|
|
45399
|
+
const child2 = (0, import_node_path42.join)(root, name);
|
|
45219
45400
|
const key = norm(child2);
|
|
45220
45401
|
if (live.has(key) || key === primary || keptKeys.has(key) || !isDir(child2)) continue;
|
|
45221
45402
|
let entries;
|
|
@@ -45258,10 +45439,10 @@ function argvWantsJson2() {
|
|
|
45258
45439
|
return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
|
|
45259
45440
|
}
|
|
45260
45441
|
function hubRoot() {
|
|
45261
|
-
const fromPkg = (0,
|
|
45442
|
+
const fromPkg = (0, import_node_path43.join)(__dirname, "..", "..");
|
|
45262
45443
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
45263
|
-
if ((0,
|
|
45264
|
-
if ((0,
|
|
45444
|
+
if ((0, import_node_fs44.existsSync)((0, import_node_path43.join)(fromPkg, marker))) return fromPkg;
|
|
45445
|
+
if ((0, import_node_fs44.existsSync)((0, import_node_path43.join)(process.cwd(), marker))) return process.cwd();
|
|
45265
45446
|
return null;
|
|
45266
45447
|
}
|
|
45267
45448
|
function ciAuditDeps() {
|
|
@@ -45273,8 +45454,8 @@ function ciAuditDeps() {
|
|
|
45273
45454
|
getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
|
|
45274
45455
|
readSeedFile: (path2) => {
|
|
45275
45456
|
if (!root) return null;
|
|
45276
|
-
const fullPath = (0,
|
|
45277
|
-
return (0,
|
|
45457
|
+
const fullPath = (0, import_node_path43.join)(root, path2);
|
|
45458
|
+
return (0, import_node_fs44.existsSync)(fullPath) ? (0, import_node_fs44.readFileSync)(fullPath, "utf8") : null;
|
|
45278
45459
|
}
|
|
45279
45460
|
};
|
|
45280
45461
|
}
|
|
@@ -46037,11 +46218,11 @@ ${list}`);
|
|
|
46037
46218
|
}
|
|
46038
46219
|
});
|
|
46039
46220
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
46040
|
-
const wfDir = (0,
|
|
46041
|
-
if (!(0,
|
|
46042
|
-
return (0,
|
|
46221
|
+
const wfDir = (0, import_node_path43.join)(cwd, ".github", "workflows");
|
|
46222
|
+
if (!(0, import_node_fs44.existsSync)(wfDir)) return [];
|
|
46223
|
+
return (0, import_node_fs44.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
46043
46224
|
try {
|
|
46044
|
-
return workflowReportsPrChecks((0,
|
|
46225
|
+
return workflowReportsPrChecks((0, import_node_fs44.readFileSync)((0, import_node_path43.join)(wfDir, name), "utf8"));
|
|
46045
46226
|
} catch {
|
|
46046
46227
|
return true;
|
|
46047
46228
|
}
|
|
@@ -46246,7 +46427,7 @@ ${list}`);
|
|
|
46246
46427
|
jsonParity(pr.command("review-verdict <number>").description("post an explicit review verdict comment: computes the PR diff patch-id and head sha, renders the v1 contract body, and posts it as a PR comment").requiredOption("--repo <owner/repo>", "target repo").requiredOption("--verdict <verdict>", `one of ${REVIEW_VERDICTS.join("|")}`).requiredOption("--scope <text>", "one line: what was reviewed").requiredOption("--risk <text>", "one line: the residual risk").option("--unverified <text>", "a claim the reviewer could not verify (repeatable)", (v, acc) => [...acc, v], []).requiredOption("--reviewer <id>", "reviewer seat or model id").option("--findings-file <path>", "Markdown findings appended after the JSON block")).action(async (number, o) => {
|
|
46247
46428
|
if (!isReviewVerdict(o.verdict)) return fail(`pr review-verdict: --verdict must be one of ${REVIEW_VERDICTS.join("|")} (got ${o.verdict})`);
|
|
46248
46429
|
const verdict = o.verdict;
|
|
46249
|
-
const findings = o.findingsFile ? (0,
|
|
46430
|
+
const findings = o.findingsFile ? (0, import_node_fs44.readFileSync)(o.findingsFile, "utf8") : void 0;
|
|
46250
46431
|
let patch;
|
|
46251
46432
|
let head;
|
|
46252
46433
|
try {
|
|
@@ -46523,7 +46704,7 @@ ${list}`);
|
|
|
46523
46704
|
const mergeSquashBody = squashBodyTextForMerge(
|
|
46524
46705
|
closingGuardInput,
|
|
46525
46706
|
method === "--squash",
|
|
46526
|
-
o.squashBodyFile ? (0,
|
|
46707
|
+
o.squashBodyFile ? (0, import_node_fs44.readFileSync)(o.squashBodyFile, "utf8") : void 0
|
|
46527
46708
|
);
|
|
46528
46709
|
if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
|
|
46529
46710
|
const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
|
|
@@ -46555,7 +46736,7 @@ ${list}`);
|
|
|
46555
46736
|
const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
|
|
46556
46737
|
let foreignCheckout;
|
|
46557
46738
|
if (foreignCwd) {
|
|
46558
|
-
const sibling = (0,
|
|
46739
|
+
const sibling = (0, import_node_path43.join)((0, import_node_path43.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
|
|
46559
46740
|
const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
|
|
46560
46741
|
if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
|
|
46561
46742
|
const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
|
|
@@ -46653,7 +46834,7 @@ ${list}`);
|
|
|
46653
46834
|
}
|
|
46654
46835
|
if (!repoForPostCleanup) throw e;
|
|
46655
46836
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
|
|
46656
|
-
const commitMessage = bodyFile ? (0,
|
|
46837
|
+
const commitMessage = bodyFile ? (0, import_node_fs44.readFileSync)(bodyFile, "utf8") : void 0;
|
|
46657
46838
|
await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
|
|
46658
46839
|
body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
|
|
46659
46840
|
timeoutMs: GH_MUTATION_TIMEOUT_MS
|
|
@@ -46767,7 +46948,7 @@ ${list}`);
|
|
|
46767
46948
|
let residueSweep;
|
|
46768
46949
|
if (!(foreignCwd && !foreignCheckout)) {
|
|
46769
46950
|
try {
|
|
46770
|
-
gitCommonDir = (0,
|
|
46951
|
+
gitCommonDir = (0, import_node_path43.resolve)(primaryRoot, (await execFileP("git", ["-C", primaryRoot, "rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim());
|
|
46771
46952
|
residueSweep = await sweepWorktreeResidue(gitCommonDir, beforeWorktrees.map((w) => w.path));
|
|
46772
46953
|
} catch {
|
|
46773
46954
|
}
|
|
@@ -46785,7 +46966,7 @@ ${list}`);
|
|
|
46785
46966
|
preserveWorktree: o.preserveWorktree,
|
|
46786
46967
|
gcAcknowledged: o.gc,
|
|
46787
46968
|
expectedHeadOid: headRefOid,
|
|
46788
|
-
pathExists: (p) => (0,
|
|
46969
|
+
pathExists: (p) => (0, import_node_fs44.existsSync)(p),
|
|
46789
46970
|
// #5899: pin cleanup git calls to the main checkout — the task worktree this process may be
|
|
46790
46971
|
// standing in is removed mid-cleanup, so a cwd-relative invocation fails with
|
|
46791
46972
|
// 'fatal: not a git repository' and leaves a spurious partial-cleanup exit.
|
|
@@ -48017,18 +48198,18 @@ function rcRunCauseFromLog(log) {
|
|
|
48017
48198
|
function memberAnnounceSummaryFile(repo, summaryFile, memberSummaryFiles) {
|
|
48018
48199
|
return repo === SUITE_REPO ? summaryFile : memberSummaryFiles[repo];
|
|
48019
48200
|
}
|
|
48020
|
-
var suiteStateDir = (root) => (0,
|
|
48201
|
+
var suiteStateDir = (root) => (0, import_node_path44.join)(resolvePrimaryGitDir(root), "mmi-runtime", "suite");
|
|
48021
48202
|
function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr, memberSummaryFilesTr = {}) {
|
|
48022
|
-
const recordPath = (0,
|
|
48023
|
-
const base = (0,
|
|
48203
|
+
const recordPath = (0, import_node_path44.join)(suiteStateDir(root), "cut.json");
|
|
48204
|
+
const base = (0, import_node_path44.join)(suiteStateDir(root), "checkouts");
|
|
48024
48205
|
const checkout = async (repo) => {
|
|
48025
48206
|
if (repo === SUITE_REPO) return root;
|
|
48026
|
-
const target = (0,
|
|
48027
|
-
if (!(0,
|
|
48028
|
-
(0,
|
|
48207
|
+
const target = (0, import_node_path44.join)(base, repo.split("/")[1]);
|
|
48208
|
+
if (!(0, import_node_fs45.existsSync)(target)) {
|
|
48209
|
+
(0, import_node_fs45.mkdirSync)(base, { recursive: true });
|
|
48029
48210
|
await exec("git", ["clone", "--quiet", "--branch", "development", `https://github.com/${repo}.git`, target], root, WATCH_TIMEOUT);
|
|
48030
48211
|
}
|
|
48031
|
-
if (!(0,
|
|
48212
|
+
if (!(0, import_node_fs45.existsSync)((0, import_node_path44.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
|
|
48032
48213
|
const origin = await exec("git", ["remote", "get-url", "origin"], target);
|
|
48033
48214
|
if (!origin.toLowerCase().endsWith(`/${repo.toLowerCase()}.git`) && !origin.toLowerCase().endsWith(`/${repo.toLowerCase()}`))
|
|
48034
48215
|
throw new Error(`${repo}: suite checkout origin mismatch at ${target}; stop`);
|
|
@@ -48351,9 +48532,9 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
48351
48532
|
const body = await gh(["release", "view", leadTag, "--repo", SUITE_REPO, "--json", "body", "--jq", ".body"]);
|
|
48352
48533
|
if (parseSuiteMapping(body)) return;
|
|
48353
48534
|
const dir = suiteStateDir(root);
|
|
48354
|
-
(0,
|
|
48355
|
-
const file = (0,
|
|
48356
|
-
(0,
|
|
48535
|
+
(0, import_node_fs45.mkdirSync)(dir, { recursive: true });
|
|
48536
|
+
const file = (0, import_node_path44.join)(dir, `notes-${leadTag}.md`);
|
|
48537
|
+
(0, import_node_fs45.writeFileSync)(file, `${body}
|
|
48357
48538
|
|
|
48358
48539
|
${suiteMappingBlock(leadTag, mapping)}`);
|
|
48359
48540
|
await gh(["release", "edit", leadTag, "--repo", SUITE_REPO, "--notes-file", file]);
|
|
@@ -48394,7 +48575,7 @@ ${suiteMappingBlock(leadTag, mapping)}`);
|
|
|
48394
48575
|
|
|
48395
48576
|
// src/suite-commands.ts
|
|
48396
48577
|
init_cli_shared();
|
|
48397
|
-
var
|
|
48578
|
+
var import_node_fs46 = require("node:fs");
|
|
48398
48579
|
function parseMemberSummaryFiles(entries) {
|
|
48399
48580
|
const memberSummaryFiles = {};
|
|
48400
48581
|
for (const entry of entries) {
|
|
@@ -48476,7 +48657,7 @@ function registerSuiteCommands(program3) {
|
|
|
48476
48657
|
try {
|
|
48477
48658
|
const io = suiteTrainIo(root);
|
|
48478
48659
|
retired = await retireFailedLeadRcLedger({ ledgerPath: releaseLedgerPath(root), rcHead: () => io.head(SUITE_REPO, "rc").then((h) => h ?? void 0), markAbandoned: io.markAbandoned }, o.ownerGo === true);
|
|
48479
|
-
if (retired && !(0,
|
|
48660
|
+
if (retired && !(0, import_node_fs46.existsSync)(io.recordPath)) {
|
|
48480
48661
|
console.log(o.json ? JSON.stringify({ retiredLeadRc: retired }, null, 2) : `suite abandon: retired the lead's failed, never-promoted rc candidate ${retired}; marked abandoned on origin (#7854)`);
|
|
48481
48662
|
return;
|
|
48482
48663
|
}
|
|
@@ -48665,9 +48846,9 @@ function suiteStatusDeps(github, registry2) {
|
|
|
48665
48846
|
|
|
48666
48847
|
// src/packages-adopt-export.ts
|
|
48667
48848
|
var import_node_child_process18 = require("node:child_process");
|
|
48668
|
-
var
|
|
48669
|
-
var
|
|
48670
|
-
var
|
|
48849
|
+
var import_node_fs47 = require("node:fs");
|
|
48850
|
+
var import_node_os22 = require("node:os");
|
|
48851
|
+
var import_node_path45 = require("node:path");
|
|
48671
48852
|
var import_node_util6 = require("node:util");
|
|
48672
48853
|
init_cli_shared();
|
|
48673
48854
|
|
|
@@ -49287,38 +49468,38 @@ function expectedFromRun(run, opts) {
|
|
|
49287
49468
|
}
|
|
49288
49469
|
async function adoptExport(opts, deps) {
|
|
49289
49470
|
if (!/^sha256:[0-9a-f]{64}$/.test(opts.digest)) throw new Error("--digest must be sha256:<64 hex>, from the export run summary");
|
|
49290
|
-
const packagePath = (0,
|
|
49291
|
-
const lockPath = (0,
|
|
49292
|
-
const packageText = (0,
|
|
49293
|
-
const lockText = (0,
|
|
49294
|
-
const work = (0,
|
|
49471
|
+
const packagePath = (0, import_node_path45.join)(opts.dir, "package.json");
|
|
49472
|
+
const lockPath = (0, import_node_path45.join)(opts.dir, "package-lock.json");
|
|
49473
|
+
const packageText = (0, import_node_fs47.readFileSync)(packagePath, "utf8");
|
|
49474
|
+
const lockText = (0, import_node_fs47.readFileSync)(lockPath, "utf8");
|
|
49475
|
+
const work = (0, import_node_fs47.mkdtempSync)((0, import_node_path45.join)((0, import_node_os22.tmpdir)(), "mmi-adopt-export-"));
|
|
49295
49476
|
try {
|
|
49296
49477
|
const run = JSON.parse((await deps.ghApi(`repos/${EXPORT_REPO}/actions/runs/${opts.run}`)).toString("utf8"));
|
|
49297
|
-
(0,
|
|
49298
|
-
(0,
|
|
49299
|
-
(0,
|
|
49300
|
-
const script = (0,
|
|
49301
|
-
(0,
|
|
49478
|
+
(0, import_node_fs47.writeFileSync)((0, import_node_path45.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
|
|
49479
|
+
(0, import_node_fs47.writeFileSync)((0, import_node_path45.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
|
|
49480
|
+
(0, import_node_fs47.writeFileSync)((0, import_node_path45.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
|
|
49481
|
+
const script = (0, import_node_path45.join)(work, "package_export.py");
|
|
49482
|
+
(0, import_node_fs47.writeFileSync)(script, package_export_default);
|
|
49302
49483
|
await deps.python(script, ["verify", work, opts.ds, opts.screens]);
|
|
49303
|
-
const verified = (0,
|
|
49304
|
-
const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0,
|
|
49484
|
+
const verified = (0, import_node_path45.join)(work, "verified");
|
|
49485
|
+
const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs47.readFileSync)((0, import_node_path45.join)(verified, `${short3}.metadata.json`), "utf8"))]));
|
|
49305
49486
|
const plan = planAdoption(packageText, lockText, metadata);
|
|
49306
49487
|
for (const { name } of plan.result.adopted) {
|
|
49307
49488
|
const short3 = PACKAGES.find((p) => p.name === name).short;
|
|
49308
|
-
await deps.npm(["cache", "add", (0,
|
|
49489
|
+
await deps.npm(["cache", "add", (0, import_node_path45.join)(verified, `${short3}.tgz`)], opts.dir);
|
|
49309
49490
|
}
|
|
49310
|
-
(0,
|
|
49311
|
-
(0,
|
|
49491
|
+
(0, import_node_fs47.writeFileSync)(packagePath, plan.packageText);
|
|
49492
|
+
(0, import_node_fs47.writeFileSync)(lockPath, plan.lockText);
|
|
49312
49493
|
try {
|
|
49313
49494
|
await deps.npm(["ci", "--offline", "--ignore-scripts", "--no-audit", "--no-fund"], opts.dir);
|
|
49314
49495
|
} catch (error) {
|
|
49315
|
-
(0,
|
|
49316
|
-
(0,
|
|
49496
|
+
(0, import_node_fs47.writeFileSync)(packagePath, packageText);
|
|
49497
|
+
(0, import_node_fs47.writeFileSync)(lockPath, lockText);
|
|
49317
49498
|
throw new Error(`npm ci --offline failed, package.json and package-lock.json restored: ${error.message}`);
|
|
49318
49499
|
}
|
|
49319
49500
|
return plan.result;
|
|
49320
49501
|
} finally {
|
|
49321
|
-
(0,
|
|
49502
|
+
(0, import_node_fs47.rmSync)(work, { recursive: true, force: true });
|
|
49322
49503
|
}
|
|
49323
49504
|
}
|
|
49324
49505
|
var execFileBuffer = (0, import_node_util6.promisify)(import_node_child_process18.execFile);
|
|
@@ -49383,12 +49564,12 @@ function registerPackagesCommands(program3) {
|
|
|
49383
49564
|
init_cli_shared();
|
|
49384
49565
|
|
|
49385
49566
|
// src/wave-status.ts
|
|
49386
|
-
var
|
|
49567
|
+
var import_node_fs48 = require("node:fs");
|
|
49387
49568
|
function readStageSummary(worktreePath) {
|
|
49388
49569
|
const statePath = stageStatePath(worktreePath);
|
|
49389
|
-
if (!(0,
|
|
49570
|
+
if (!(0, import_node_fs48.existsSync)(statePath)) return void 0;
|
|
49390
49571
|
try {
|
|
49391
|
-
const state = JSON.parse((0,
|
|
49572
|
+
const state = JSON.parse((0, import_node_fs48.readFileSync)(statePath, "utf8"));
|
|
49392
49573
|
const port = typeof state.port === "number" ? state.port : void 0;
|
|
49393
49574
|
if (port == null || !Number.isInteger(port) || port <= 0) return void 0;
|
|
49394
49575
|
return { port, url: typeof state.url === "string" ? state.url : void 0 };
|
|
@@ -49445,9 +49626,9 @@ async function collectWaveStatus(deps) {
|
|
|
49445
49626
|
// src/discovery-commands.ts
|
|
49446
49627
|
init_github_client();
|
|
49447
49628
|
init_cli_shared();
|
|
49448
|
-
var
|
|
49449
|
-
var
|
|
49450
|
-
var
|
|
49629
|
+
var import_node_fs49 = require("node:fs");
|
|
49630
|
+
var import_node_os23 = require("node:os");
|
|
49631
|
+
var import_node_path46 = require("node:path");
|
|
49451
49632
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
49452
49633
|
async function collectStatus() {
|
|
49453
49634
|
const repo = await resolveRepo();
|
|
@@ -49636,10 +49817,10 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
49636
49817
|
else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
|
|
49637
49818
|
else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
|
|
49638
49819
|
}
|
|
49639
|
-
const home = (0,
|
|
49820
|
+
const home = (0, import_node_os23.homedir)();
|
|
49640
49821
|
const plugin = onboardPluginGate({
|
|
49641
|
-
readKnown: () => readFileSyncSafe((0,
|
|
49642
|
-
readSettings: () => readFileSyncSafe((0,
|
|
49822
|
+
readKnown: () => readFileSyncSafe((0, import_node_path46.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs49.readFileSync),
|
|
49823
|
+
readSettings: () => readFileSyncSafe((0, import_node_path46.join)(home, ".claude", "settings.json"), import_node_fs49.readFileSync)
|
|
49643
49824
|
});
|
|
49644
49825
|
return {
|
|
49645
49826
|
guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
|
|
@@ -51831,6 +52012,9 @@ function registerOrgHealthQuery(program3, deps = defaultOrgHealthQueryDeps()) {
|
|
|
51831
52012
|
});
|
|
51832
52013
|
}
|
|
51833
52014
|
|
|
52015
|
+
// src/api-read.ts
|
|
52016
|
+
var import_node_fs50 = require("node:fs");
|
|
52017
|
+
|
|
51834
52018
|
// src/app-read-broker.ts
|
|
51835
52019
|
init_cli_shared();
|
|
51836
52020
|
var OWNER_REPO_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
@@ -52200,7 +52384,10 @@ var CONTENTS_QUERY_RE = /^ref=[^&\s]+$/;
|
|
|
52200
52384
|
var ACTIONS_RUNS_RE = new RegExp(`^repos/${NAME}/${NAME}/actions/runs$`);
|
|
52201
52385
|
var ACTIONS_RUN_RE = new RegExp(`^repos/${NAME}/${NAME}/actions/runs/\\d+$`);
|
|
52202
52386
|
var ACTIONS_JOBS_RE = new RegExp(`^repos/${NAME}/${NAME}/actions/runs/\\d+/jobs$`);
|
|
52203
|
-
var
|
|
52387
|
+
var ACTIONS_ARTIFACTS_RE = new RegExp(`^repos/${NAME}/${NAME}/actions/runs/\\d+/artifacts$`);
|
|
52388
|
+
var ACTIONS_ARTIFACT_ZIP_RE = new RegExp(`^repos/${NAME}/${NAME}/actions/artifacts/\\d+/zip$`);
|
|
52389
|
+
var ACTIONS_JOB_LOGS_RE = new RegExp(`^repos/${NAME}/${NAME}/actions/jobs/\\d+/logs$`);
|
|
52390
|
+
var API_GET_ALLOWED_SHAPES = "repos/{owner}/{repo}/pulls/{n}/files, repos/{owner}/{repo}/compare/{a}...{b}, repos/{owner}/{repo}/contents/{path}[?ref=<branch>], repos/{owner}/{repo}/actions/runs, repos/{owner}/{repo}/actions/runs/{id}, repos/{owner}/{repo}/actions/runs/{id}/jobs, repos/{owner}/{repo}/actions/runs/{id}/artifacts, repos/{owner}/{repo}/actions/artifacts/{id}/zip (needs --out <file>), repos/{owner}/{repo}/actions/jobs/{id}/logs (needs --out <file>)";
|
|
52204
52391
|
function parseApiGetPath(rawPath) {
|
|
52205
52392
|
const path2 = rawPath.trim();
|
|
52206
52393
|
const refuse = () => new Error(`api get: refusing '${rawPath}' \u2014 only these GET reads are allowed (#7219): ${API_GET_ALLOWED_SHAPES}`);
|
|
@@ -52210,6 +52397,9 @@ function parseApiGetPath(rawPath) {
|
|
|
52210
52397
|
if (ACTIONS_RUNS_RE.test(path2)) return { kind: "actions-runs", path: path2, paginate: true };
|
|
52211
52398
|
if (ACTIONS_RUN_RE.test(path2)) return { kind: "actions-run", path: path2, paginate: false };
|
|
52212
52399
|
if (ACTIONS_JOBS_RE.test(path2)) return { kind: "actions-jobs", path: path2, paginate: true };
|
|
52400
|
+
if (ACTIONS_ARTIFACTS_RE.test(path2)) return { kind: "actions-artifacts", path: path2, paginate: true };
|
|
52401
|
+
if (ACTIONS_ARTIFACT_ZIP_RE.test(path2)) return { kind: "actions-artifact-zip", path: path2, paginate: false };
|
|
52402
|
+
if (ACTIONS_JOB_LOGS_RE.test(path2)) return { kind: "actions-job-logs", path: path2, paginate: false };
|
|
52213
52403
|
const q = path2.indexOf("?");
|
|
52214
52404
|
const base = q === -1 ? path2 : path2.slice(0, q);
|
|
52215
52405
|
const query = q === -1 ? "" : path2.slice(q + 1);
|
|
@@ -52218,6 +52408,8 @@ function parseApiGetPath(rawPath) {
|
|
|
52218
52408
|
}
|
|
52219
52409
|
throw new Error(`api get: refusing '${rawPath}' \u2014 only these GET reads are allowed (#7219): ${API_GET_ALLOWED_SHAPES}`);
|
|
52220
52410
|
}
|
|
52411
|
+
var ZIP_TIMEOUT_MS = 12e4;
|
|
52412
|
+
var ZIP_MAX_BUFFER = 512 * 1024 * 1024;
|
|
52221
52413
|
function buildApiGetArgs(path2, accept) {
|
|
52222
52414
|
const req = parseApiGetPath(path2);
|
|
52223
52415
|
const args = ["api", req.path];
|
|
@@ -52230,22 +52422,28 @@ async function runApiGet(run, path2, accept) {
|
|
|
52230
52422
|
return run(buildApiGetArgs(path2, accept), GIT_TIMEOUT_MS);
|
|
52231
52423
|
}
|
|
52232
52424
|
function registerApiReadCommands(program3, overrides) {
|
|
52233
|
-
const spawn6 = overrides?.runner ?? (async (args, timeoutMs, env) => (await execFileP("gh", args, { timeout: timeoutMs, ...env ? { env } : {} })).stdout);
|
|
52234
|
-
const api = program3.command("api").description("bounded GitHub REST reads for automation \u2014 GET only, allowlisted paths (pull files, compare, contents, actions runs/jobs)");
|
|
52425
|
+
const spawn6 = overrides?.runner ?? (async (args, timeoutMs, env, binary) => (await execFileP("gh", args, { timeout: timeoutMs, ...binary ? { encoding: "latin1", maxBuffer: ZIP_MAX_BUFFER } : {}, ...env ? { env } : {} })).stdout);
|
|
52426
|
+
const api = program3.command("api").description("bounded GitHub REST reads for automation \u2014 GET only, allowlisted paths (pull files, compare, contents, actions runs/jobs/artifacts)");
|
|
52235
52427
|
api.command("get").description("GET one allowlisted REST path and print the response body to stdout (JSON, or raw file bytes with --accept raw); refuses everything outside the allowlist").option("--accept <type>", "response media type: json (default) or raw (file bytes)", (v) => {
|
|
52236
52428
|
acceptHeaderFor(v);
|
|
52237
52429
|
return v.trim().toLowerCase();
|
|
52238
|
-
}, "json").argument("<path>", "allowlisted GET path (shapes named on refusal)", (v) => parseApiGetPath(v)).action(async (req, o) => {
|
|
52430
|
+
}, "json").option("--out <file>", "write the response bytes to this file instead of stdout (required for, and only valid with, actions/artifacts/{id}/zip)").argument("<path>", "allowlisted GET path (shapes named on refusal)", (v) => parseApiGetPath(v)).action(async (req, o) => {
|
|
52431
|
+
const zip = req.kind === "actions-artifact-zip" || req.kind === "actions-job-logs";
|
|
52432
|
+
if (zip !== (o.out !== void 0)) return fail(`api get: --out <file> is required for, and only valid with, repos/{owner}/{repo}/actions/artifacts/{id}/zip and repos/{owner}/{repo}/actions/jobs/{id}/logs`);
|
|
52239
52433
|
const actionsRoute = req.kind.startsWith("actions-");
|
|
52240
52434
|
if (actionsRoute) requireBrokerApp("api get");
|
|
52241
52435
|
const env = actionsRoute || process.env.MMI_ACTOR === "app" ? brokerChildEnv() : void 0;
|
|
52242
52436
|
let body;
|
|
52243
52437
|
try {
|
|
52244
52438
|
body = await runApiGet(
|
|
52245
|
-
async (args, timeoutMs) => spawn6(args, timeoutMs, env),
|
|
52439
|
+
async (args, timeoutMs) => zip ? spawn6(args, ZIP_TIMEOUT_MS, env, true) : spawn6(args, timeoutMs, env),
|
|
52246
52440
|
req.path,
|
|
52247
52441
|
o.accept
|
|
52248
52442
|
);
|
|
52443
|
+
if (zip) {
|
|
52444
|
+
(0, import_node_fs50.writeFileSync)(o.out, body, "latin1");
|
|
52445
|
+
return;
|
|
52446
|
+
}
|
|
52249
52447
|
} catch (e) {
|
|
52250
52448
|
const err = e;
|
|
52251
52449
|
return fail(`api get: ${(err.stderr || err.message || String(e)).trim()}`);
|
|
@@ -53102,8 +53300,8 @@ function registerSessionReport(program3) {
|
|
|
53102
53300
|
}
|
|
53103
53301
|
|
|
53104
53302
|
// src/stage-commands.ts
|
|
53105
|
-
var
|
|
53106
|
-
var
|
|
53303
|
+
var import_node_fs51 = require("node:fs");
|
|
53304
|
+
var import_node_path47 = require("node:path");
|
|
53107
53305
|
init_cli_shared();
|
|
53108
53306
|
init_clean_exit();
|
|
53109
53307
|
|
|
@@ -53351,8 +53549,8 @@ function registerStageCommands(program3) {
|
|
|
53351
53549
|
const repo = Array.isArray(project2?.repos) && typeof project2.repos[0] === "string" ? project2.repos[0] : slug;
|
|
53352
53550
|
const resolution = decideStage({
|
|
53353
53551
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
53354
|
-
hasCompose: (0,
|
|
53355
|
-
hasEnvExample: (0,
|
|
53552
|
+
hasCompose: (0, import_node_fs51.existsSync)((0, import_node_path47.join)(process.cwd(), "docker-compose.yml")),
|
|
53553
|
+
hasEnvExample: (0, import_node_fs51.existsSync)((0, import_node_path47.join)(process.cwd(), ".env.example")),
|
|
53356
53554
|
repo
|
|
53357
53555
|
});
|
|
53358
53556
|
if (resolution.source === "derived" && resolution.derived) {
|
|
@@ -53644,23 +53842,23 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
|
|
|
53644
53842
|
}
|
|
53645
53843
|
|
|
53646
53844
|
// src/tenant-artifact.ts
|
|
53647
|
-
var
|
|
53648
|
-
var
|
|
53845
|
+
var import_node_crypto17 = require("node:crypto");
|
|
53846
|
+
var import_node_fs52 = require("node:fs");
|
|
53649
53847
|
var import_promises11 = require("node:fs/promises");
|
|
53650
|
-
var
|
|
53848
|
+
var import_node_path48 = require("node:path");
|
|
53651
53849
|
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}$/;
|
|
53652
|
-
var
|
|
53850
|
+
var MAX_BYTES2 = 5 * 1024 * 1024 * 1024;
|
|
53653
53851
|
async function sha256File(path2) {
|
|
53654
|
-
const hash = (0,
|
|
53655
|
-
for await (const chunk of (0,
|
|
53852
|
+
const hash = (0, import_node_crypto17.createHash)("sha256");
|
|
53853
|
+
for await (const chunk of (0, import_node_fs52.createReadStream)(path2)) hash.update(chunk);
|
|
53656
53854
|
return hash.digest("hex");
|
|
53657
53855
|
}
|
|
53658
53856
|
async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
53659
53857
|
if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
|
|
53660
|
-
const path2 = (0,
|
|
53858
|
+
const path2 = (0, import_node_path48.resolve)(inputPath);
|
|
53661
53859
|
const info = await (0, import_promises11.stat)(path2);
|
|
53662
53860
|
if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
|
|
53663
|
-
if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size >
|
|
53861
|
+
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`);
|
|
53664
53862
|
const sha2563 = await sha256File(path2);
|
|
53665
53863
|
const prepared = await tenantArtifactUpload({ repo, stage, size: info.size, sha256: sha2563 }, deps);
|
|
53666
53864
|
if (!prepared.ok) {
|
|
@@ -53675,7 +53873,7 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
|
53675
53873
|
return [key, value];
|
|
53676
53874
|
}));
|
|
53677
53875
|
headers["content-length"] = String(info.size);
|
|
53678
|
-
const stream = (0,
|
|
53876
|
+
const stream = (0, import_node_fs52.createReadStream)(path2);
|
|
53679
53877
|
let uploaded;
|
|
53680
53878
|
try {
|
|
53681
53879
|
uploaded = await fetch(body.uploadUrl, {
|
|
@@ -53814,7 +54012,7 @@ function renderVerifySecrets(body) {
|
|
|
53814
54012
|
}
|
|
53815
54013
|
|
|
53816
54014
|
// src/command-register-developer.ts
|
|
53817
|
-
var
|
|
54015
|
+
var import_node_fs56 = require("node:fs");
|
|
53818
54016
|
init_clean_exit();
|
|
53819
54017
|
init_cli_shared();
|
|
53820
54018
|
init_error_codes();
|
|
@@ -53847,7 +54045,7 @@ async function resolveWhoami(deps) {
|
|
|
53847
54045
|
}
|
|
53848
54046
|
|
|
53849
54047
|
// src/command-register-developer.ts
|
|
53850
|
-
var
|
|
54048
|
+
var import_node_path53 = require("node:path");
|
|
53851
54049
|
init_house_map();
|
|
53852
54050
|
init_hub_url();
|
|
53853
54051
|
|
|
@@ -53881,28 +54079,28 @@ async function executeWaveLand(plan, deps) {
|
|
|
53881
54079
|
|
|
53882
54080
|
// src/dist-drift.ts
|
|
53883
54081
|
var import_node_child_process19 = require("node:child_process");
|
|
53884
|
-
var
|
|
53885
|
-
var
|
|
53886
|
-
var
|
|
53887
|
-
var
|
|
54082
|
+
var import_node_crypto19 = require("node:crypto");
|
|
54083
|
+
var import_node_fs54 = require("node:fs");
|
|
54084
|
+
var import_node_os24 = require("node:os");
|
|
54085
|
+
var import_node_path50 = require("node:path");
|
|
53888
54086
|
|
|
53889
54087
|
// ../scripts/distribution-digest.mjs
|
|
53890
|
-
var
|
|
53891
|
-
var
|
|
53892
|
-
var
|
|
54088
|
+
var import_node_crypto18 = require("node:crypto");
|
|
54089
|
+
var import_node_fs53 = require("node:fs");
|
|
54090
|
+
var import_node_path49 = require("node:path");
|
|
53893
54091
|
var slash = (value) => value.replaceAll("\\", "/");
|
|
53894
54092
|
function repoPath(root, declaredPath, label) {
|
|
53895
|
-
const absoluteRoot = (0,
|
|
53896
|
-
const target = (0,
|
|
53897
|
-
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${
|
|
54093
|
+
const absoluteRoot = (0, import_node_path49.resolve)(root);
|
|
54094
|
+
const target = (0, import_node_path49.resolve)(root, declaredPath);
|
|
54095
|
+
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path49.sep}`)) {
|
|
53898
54096
|
throw new Error(`${label} ${declaredPath} escapes the repository root`);
|
|
53899
54097
|
}
|
|
53900
54098
|
return target;
|
|
53901
54099
|
}
|
|
53902
54100
|
function digestFiles(files) {
|
|
53903
|
-
const hash = (0,
|
|
54101
|
+
const hash = (0, import_node_crypto18.createHash)("sha256");
|
|
53904
54102
|
for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
|
|
53905
|
-
const content = file.stat.isSymbolicLink() ? Buffer.from((0,
|
|
54103
|
+
const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs53.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs53.readFileSync)(file.absolute);
|
|
53906
54104
|
hash.update(file.relative, "utf8");
|
|
53907
54105
|
hash.update("\0");
|
|
53908
54106
|
hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
|
|
@@ -53917,8 +54115,8 @@ function digestFiles(files) {
|
|
|
53917
54115
|
function digestPackedFiles(packageRoot, packedFiles) {
|
|
53918
54116
|
return digestFiles(packedFiles.map((path2) => {
|
|
53919
54117
|
const absolute = repoPath(packageRoot, path2, "packed artifact identity path");
|
|
53920
|
-
if (!(0,
|
|
53921
|
-
return { absolute, relative: slash(path2), stat: (0,
|
|
54118
|
+
if (!(0, import_node_fs53.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
|
|
54119
|
+
return { absolute, relative: slash(path2), stat: (0, import_node_fs53.lstatSync)(absolute) };
|
|
53922
54120
|
}));
|
|
53923
54121
|
}
|
|
53924
54122
|
|
|
@@ -53930,7 +54128,7 @@ var DIST_ARTIFACTS = [
|
|
|
53930
54128
|
];
|
|
53931
54129
|
var BOM_DIST_TREE_ID = "mmi-cli-dist";
|
|
53932
54130
|
var ABSENT = "absent";
|
|
53933
|
-
var sha2562 = (bytes) => `sha256:${(0,
|
|
54131
|
+
var sha2562 = (bytes) => `sha256:${(0, import_node_crypto19.createHash)("sha256").update(bytes).digest("hex")}`;
|
|
53934
54132
|
function artifactDrift(path2, committedBytes, rebuiltBytes) {
|
|
53935
54133
|
const committed = committedBytes ? sha2562(committedBytes) : ABSENT;
|
|
53936
54134
|
const rebuiltExpected = rebuiltBytes ? sha2562(rebuiltBytes) : ABSENT;
|
|
@@ -54002,13 +54200,13 @@ function renderDistDriftReceipt(receipt) {
|
|
|
54002
54200
|
return lines2;
|
|
54003
54201
|
}
|
|
54004
54202
|
function readOrNull(path2) {
|
|
54005
|
-
return (0,
|
|
54203
|
+
return (0, import_node_fs54.existsSync)(path2) ? (0, import_node_fs54.readFileSync)(path2) : null;
|
|
54006
54204
|
}
|
|
54007
54205
|
function walkFiles(root) {
|
|
54008
54206
|
const files = [];
|
|
54009
54207
|
const walk2 = (directory) => {
|
|
54010
|
-
for (const entry of (0,
|
|
54011
|
-
const child2 = (0,
|
|
54208
|
+
for (const entry of (0, import_node_fs54.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
54209
|
+
const child2 = (0, import_node_path50.join)(directory, entry.name);
|
|
54012
54210
|
if (entry.isDirectory()) walk2(child2);
|
|
54013
54211
|
else files.push(child2);
|
|
54014
54212
|
}
|
|
@@ -54018,10 +54216,10 @@ function walkFiles(root) {
|
|
|
54018
54216
|
}
|
|
54019
54217
|
function bomPathFor(root) {
|
|
54020
54218
|
try {
|
|
54021
|
-
const registry2 = JSON.parse((0,
|
|
54022
|
-
return (0,
|
|
54219
|
+
const registry2 = JSON.parse((0, import_node_fs54.readFileSync)((0, import_node_path50.join)(root, "surfaces.json"), "utf8"));
|
|
54220
|
+
return (0, import_node_path50.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
|
|
54023
54221
|
} catch {
|
|
54024
|
-
return (0,
|
|
54222
|
+
return (0, import_node_path50.join)(root, "distribution-bom.json");
|
|
54025
54223
|
}
|
|
54026
54224
|
}
|
|
54027
54225
|
function rebuildTo(packageRoot, outDir) {
|
|
@@ -54034,35 +54232,35 @@ function rebuildTo(packageRoot, outDir) {
|
|
|
54034
54232
|
});
|
|
54035
54233
|
}
|
|
54036
54234
|
function runDistStatus(root) {
|
|
54037
|
-
const stage = (0,
|
|
54235
|
+
const stage = (0, import_node_fs54.mkdtempSync)((0, import_node_path50.join)((0, import_node_os24.tmpdir)(), "mmi-dist-drift-"));
|
|
54038
54236
|
let overlayCount = 0;
|
|
54039
54237
|
try {
|
|
54040
|
-
const cliOut = (0,
|
|
54041
|
-
const hubOut = (0,
|
|
54042
|
-
rebuildTo((0,
|
|
54043
|
-
rebuildTo((0,
|
|
54238
|
+
const cliOut = (0, import_node_path50.join)(stage, "cli-dist");
|
|
54239
|
+
const hubOut = (0, import_node_path50.join)(stage, "hub-dist");
|
|
54240
|
+
rebuildTo((0, import_node_path50.join)(root, "cli"), cliOut);
|
|
54241
|
+
rebuildTo((0, import_node_path50.join)(root, "updater"), hubOut);
|
|
54044
54242
|
const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
|
|
54045
54243
|
const rebuilt = (path2) => {
|
|
54046
54244
|
const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
|
|
54047
|
-
return spec ? readOrNull((0,
|
|
54245
|
+
return spec ? readOrNull((0, import_node_path50.join)(outDirFor(spec.packageDir), spec.output)) : null;
|
|
54048
54246
|
};
|
|
54049
|
-
const committed = (path2) => readOrNull((0,
|
|
54050
|
-
const tree = (path2) => readOrNull((0,
|
|
54051
|
-
const distRoot = (0,
|
|
54052
|
-
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0,
|
|
54053
|
-
const bom = JSON.parse((0,
|
|
54247
|
+
const committed = (path2) => readOrNull((0, import_node_path50.join)(root, path2));
|
|
54248
|
+
const tree = (path2) => readOrNull((0, import_node_path50.join)(root, path2));
|
|
54249
|
+
const distRoot = (0, import_node_path50.join)(root, "cli", "dist");
|
|
54250
|
+
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path50.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
|
|
54251
|
+
const bom = JSON.parse((0, import_node_fs54.readFileSync)(bomPathFor(root), "utf8"));
|
|
54054
54252
|
const digest = (entries) => {
|
|
54055
|
-
const overlay = (0,
|
|
54253
|
+
const overlay = (0, import_node_path50.join)(stage, `overlay-${overlayCount++}`);
|
|
54056
54254
|
for (const entry of entries) {
|
|
54057
|
-
const target = (0,
|
|
54058
|
-
(0,
|
|
54059
|
-
(0,
|
|
54255
|
+
const target = (0, import_node_path50.join)(overlay, entry.path);
|
|
54256
|
+
(0, import_node_fs54.mkdirSync)((0, import_node_path50.dirname)(target), { recursive: true });
|
|
54257
|
+
(0, import_node_fs54.writeFileSync)(target, entry.bytes);
|
|
54060
54258
|
}
|
|
54061
54259
|
return digestPackedFiles(overlay, entries.map((entry) => entry.path));
|
|
54062
54260
|
};
|
|
54063
54261
|
return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
|
|
54064
54262
|
} finally {
|
|
54065
|
-
(0,
|
|
54263
|
+
(0, import_node_fs54.rmSync)(stage, { recursive: true, force: true });
|
|
54066
54264
|
}
|
|
54067
54265
|
}
|
|
54068
54266
|
|
|
@@ -54163,7 +54361,7 @@ init_hub_auth();
|
|
|
54163
54361
|
|
|
54164
54362
|
// src/schedules-lift-command.ts
|
|
54165
54363
|
var import_promises12 = require("node:fs/promises");
|
|
54166
|
-
var
|
|
54364
|
+
var import_node_path51 = require("node:path");
|
|
54167
54365
|
init_clean_exit();
|
|
54168
54366
|
init_cli_shared();
|
|
54169
54367
|
var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
|
|
@@ -54192,7 +54390,7 @@ async function readWorkflowFiles(dir) {
|
|
|
54192
54390
|
const files = [];
|
|
54193
54391
|
for (const name of names.sort()) {
|
|
54194
54392
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
54195
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0,
|
|
54393
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0, import_node_path51.join)(dir, name), "utf8") });
|
|
54196
54394
|
}
|
|
54197
54395
|
return files;
|
|
54198
54396
|
}
|
|
@@ -54275,8 +54473,8 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
|
|
|
54275
54473
|
|
|
54276
54474
|
// src/spawn-policy-core.ts
|
|
54277
54475
|
var import_node_child_process20 = require("node:child_process");
|
|
54278
|
-
var
|
|
54279
|
-
var
|
|
54476
|
+
var import_node_fs55 = require("node:fs");
|
|
54477
|
+
var import_node_path52 = require("node:path");
|
|
54280
54478
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
54281
54479
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
54282
54480
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -54362,7 +54560,7 @@ function runSpawnPolicy(root) {
|
|
|
54362
54560
|
for (const file of files) {
|
|
54363
54561
|
let raw;
|
|
54364
54562
|
try {
|
|
54365
|
-
raw = (0,
|
|
54563
|
+
raw = (0, import_node_fs55.readFileSync)((0, import_node_path52.join)(root, file), "utf8");
|
|
54366
54564
|
} catch {
|
|
54367
54565
|
continue;
|
|
54368
54566
|
}
|
|
@@ -54382,19 +54580,19 @@ function runSpawnPolicy(root) {
|
|
|
54382
54580
|
function registerDeveloperCommands(program3) {
|
|
54383
54581
|
const rules = program3.command("rules").description("org-managed .gitignore delivery");
|
|
54384
54582
|
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) => {
|
|
54385
|
-
const path2 = (0,
|
|
54386
|
-
const current = (0,
|
|
54583
|
+
const path2 = (0, import_node_path53.join)(process.cwd(), ".gitignore");
|
|
54584
|
+
const current = (0, import_node_fs56.existsSync)(path2) ? (0, import_node_fs56.readFileSync)(path2, "utf8") : null;
|
|
54387
54585
|
const plan = planManagedGitignore(current);
|
|
54388
54586
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
54389
54587
|
if (opts.json) {
|
|
54390
|
-
if (opts.write && plan.changed) (0,
|
|
54588
|
+
if (opts.write && plan.changed) (0, import_node_fs56.writeFileSync)(path2, plan.content, "utf8");
|
|
54391
54589
|
console.log(JSON.stringify(plan, null, 2));
|
|
54392
54590
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
54393
54591
|
return;
|
|
54394
54592
|
}
|
|
54395
54593
|
if (opts.write) {
|
|
54396
54594
|
if (plan.changed) {
|
|
54397
|
-
(0,
|
|
54595
|
+
(0, import_node_fs56.writeFileSync)(path2, plan.content, "utf8");
|
|
54398
54596
|
console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
|
|
54399
54597
|
} else {
|
|
54400
54598
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
@@ -54657,12 +54855,12 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
54657
54855
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
54658
54856
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
54659
54857
|
function envHealLockPath(home) {
|
|
54660
|
-
return (0,
|
|
54858
|
+
return (0, import_node_path54.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
54661
54859
|
}
|
|
54662
54860
|
async function withEnvHealLock(what, run) {
|
|
54663
54861
|
try {
|
|
54664
54862
|
return await withFileLock(
|
|
54665
|
-
envHealLockPath((0,
|
|
54863
|
+
envHealLockPath((0, import_node_os25.homedir)()),
|
|
54666
54864
|
{ staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
|
|
54667
54865
|
run
|
|
54668
54866
|
);
|
|
@@ -54759,7 +54957,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
54759
54957
|
const configRoot = surfaceConfigRoot(surface);
|
|
54760
54958
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
54761
54959
|
const plan = buildPluginCachePlan(
|
|
54762
|
-
(0,
|
|
54960
|
+
(0, import_node_os25.homedir)(),
|
|
54763
54961
|
running,
|
|
54764
54962
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
54765
54963
|
{ configRoot, includeStaging: surface !== "codex" }
|
|
@@ -54787,14 +54985,14 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
54787
54985
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
54788
54986
|
const installed = installedActivePluginVersion(surface);
|
|
54789
54987
|
const plan = buildPluginCachePlan(
|
|
54790
|
-
(0,
|
|
54988
|
+
(0, import_node_os25.homedir)(),
|
|
54791
54989
|
running,
|
|
54792
54990
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
54793
54991
|
{ configRoot, includeStaging: surface !== "codex", installedVersion: installed }
|
|
54794
54992
|
);
|
|
54795
54993
|
const result = applyPluginCachePlan(
|
|
54796
54994
|
plan,
|
|
54797
|
-
(p) => (0,
|
|
54995
|
+
(p) => (0, import_node_fs57.rmSync)(p, { recursive: true }),
|
|
54798
54996
|
stagingApplyFsGuard(configRoot)
|
|
54799
54997
|
);
|
|
54800
54998
|
return {
|
|
@@ -54829,7 +55027,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
54829
55027
|
// has no generated routing index would
|
|
54830
55028
|
// get a permanent — demanding an artifact it never asked for.
|
|
54831
55029
|
docsIndexState: (root) => {
|
|
54832
|
-
if (!(0,
|
|
55030
|
+
if (!(0, import_node_fs57.existsSync)((0, import_node_path54.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
54833
55031
|
const real = createDocsIndexDeps(root);
|
|
54834
55032
|
let docs;
|
|
54835
55033
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -54838,7 +55036,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
54838
55036
|
},
|
|
54839
55037
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
54840
55038
|
healDocsIndex: (root) => {
|
|
54841
|
-
if (!(0,
|
|
55039
|
+
if (!(0, import_node_fs57.existsSync)((0, import_node_path54.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
54842
55040
|
const real = createDocsIndexDeps(root);
|
|
54843
55041
|
let docs;
|
|
54844
55042
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -55187,14 +55385,17 @@ program2.addHelpText(
|
|
|
55187
55385
|
var appActorCommand = "poll";
|
|
55188
55386
|
function appActorDeps() {
|
|
55189
55387
|
return {
|
|
55190
|
-
fetchSecret: async (key) =>
|
|
55191
|
-
|
|
55192
|
-
|
|
55193
|
-
|
|
55388
|
+
fetchSecret: async (key) => {
|
|
55389
|
+
const deps = makeSecretsDeps(await loadConfig());
|
|
55390
|
+
return readAppVaultSecret(() => fetchSecretValue(deps, key, {
|
|
55391
|
+
repo: APP_VAULT_REPO,
|
|
55392
|
+
attribution: { command: appActorCommand, via: formatSessionIdentity(describeSessionIdentity()) }
|
|
55393
|
+
}, true));
|
|
55394
|
+
},
|
|
55194
55395
|
fetch: (url, init) => fetch(url, init)
|
|
55195
55396
|
};
|
|
55196
55397
|
}
|
|
55197
|
-
setPollTokenProvider(() => pollTokenOrUndefined(appActorDeps()));
|
|
55398
|
+
setPollTokenProvider(async () => activeAppToken() ?? pollTokenOrUndefined(appActorDeps()));
|
|
55198
55399
|
var registerPreAction = Reflect.get(program2, ["h", "ook"].join(""));
|
|
55199
55400
|
registerPreAction.call(program2, "preAction", async (_thisCommand, actionCommand) => {
|
|
55200
55401
|
const commandPath3 = commandPathOf(actionCommand);
|
|
@@ -55218,8 +55419,9 @@ async function reportWrite(label, res) {
|
|
|
55218
55419
|
return failGraceful(`${label}: HTTP ${res.status}${detail ? ` \u2014 ${detail}` : ""}`);
|
|
55219
55420
|
}
|
|
55220
55421
|
var tenant = program2.command("tenant").description("tenant runtime control through Hub authority");
|
|
55221
|
-
tenant.command("control <owner/repo> <stage> <action>").description("bounded tenant control plus value-free vault/broker verification; dev/main retire needs master and exact data-loss confirmation").option("--watch", "block on the dispatched run and report its conclusion (status/retire/verify-secrets/verify-broker/logs watch by default)").option("--lines <n>", "logs only: trailing lines of the tenant service to return (1-2000, default 200)").option("--since <utc>", "logs only: read the journal from this UTC time, e.g. 2026-10-07T13:10Z (includes replaced containers)").option("--until <utc>", "logs only: read the journal up to this UTC time").option("--edge", "logs only: Caddy journal for this stage\u2019s hosts (default window: last 15 minutes)").option("--task <name>", "run-task only: registry-declared task name (never a command)").option("--artifact <id>", "run-task only: opaque id returned by tenant artifact put").option("--confirm-data-loss <owner/repo/stage>", "dev/main retire only: exact target to decommission; without it show the dry-run plan").option("--json", "machine-readable output").action(async (repo, stage, action, o) => {
|
|
55422
|
+
tenant.command("control <owner/repo> <stage> <action>").description("bounded tenant control plus value-free vault/broker verification; dev/main retire needs master and exact data-loss confirmation").option("--watch", "block on the dispatched run and report its conclusion (status/retire/verify-secrets/verify-broker/logs watch by default)").option("--lines <n>", "logs only: trailing lines of the tenant service to return (1-2000, default 200)").option("--since <utc>", "logs only: read the journal from this UTC time, e.g. 2026-10-07T13:10Z (includes replaced containers)").option("--until <utc>", "logs only: read the journal up to this UTC time").option("--edge", "logs only: Caddy journal for this stage\u2019s hosts (default window: last 15 minutes)").option("--task <name>", "run-task only: registry-declared task name (never a command)").option("--artifact <id>", "run-task only: opaque id returned by tenant artifact put").option("--resume <run-id>", "run-task only: re-attach to an already-dispatched run (read-only, never re-dispatches) and print its receipt").option("--confirm-data-loss <owner/repo/stage>", "dev/main retire only: exact target to decommission; without it show the dry-run plan").option("--json", "machine-readable output").action(async (repo, stage, action, o) => {
|
|
55222
55423
|
try {
|
|
55424
|
+
if (o.resume !== void 0 && (action !== "run-task" || !/^\d+$/.test(o.resume) || o.task || o.artifact)) return fail("runtime tenant control: --resume <run-id> is run-task-only, takes a numeric run id, and cannot be combined with --task/--artifact");
|
|
55223
55425
|
if (o.confirmDataLoss !== void 0 && (action !== "retire" || stage === "rc")) return fail("runtime tenant control: --confirm-data-loss is dev/main retire-only");
|
|
55224
55426
|
if (action === "retire" && (stage === "dev" || stage === "main")) {
|
|
55225
55427
|
if (o.confirmDataLoss !== void 0 && o.confirmDataLoss !== `${repo}/${stage}`) return fail(`runtime tenant control: --confirm-data-loss must match ${repo}/${stage} exactly`);
|
|
@@ -55244,11 +55446,11 @@ Dry run: pass --confirm-data-loss ${repo}/${stage} to decommission.`);
|
|
|
55244
55446
|
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?Z$/.test(value)) return fail(`runtime tenant control: ${flag} must be a UTC time like 2026-10-07T13:10Z`);
|
|
55245
55447
|
}
|
|
55246
55448
|
if (action === "run-task") {
|
|
55247
|
-
if (!o.task) return fail("runtime tenant control: run-task requires --task <declared-name>");
|
|
55449
|
+
if (!o.task && o.resume === void 0) return fail("runtime tenant control: run-task requires --task <declared-name>");
|
|
55248
55450
|
} else if (o.task || o.artifact) {
|
|
55249
55451
|
return fail("runtime tenant control: --task/--artifact are valid only for run-task");
|
|
55250
55452
|
}
|
|
55251
|
-
const result = await runTenantControl(trainApplyDeps(), { repo, stage, action, watch: o.watch, lines: lines2, edge: o.edge, logSince: o.since, logUntil: o.until, task: o.task, artifact: o.artifact, confirmDataLoss: o.confirmDataLoss });
|
|
55453
|
+
const result = await runTenantControl(trainApplyDeps(), { repo, stage, action, watch: o.watch, lines: lines2, edge: o.edge, logSince: o.since, logUntil: o.until, task: o.task, artifact: o.artifact, confirmDataLoss: o.confirmDataLoss, resume: o.resume === void 0 ? void 0 : Number(o.resume) });
|
|
55252
55454
|
if (!o.json && action === "verify-secrets" && result.secrets) {
|
|
55253
55455
|
const body = { ok: result.conclusion === "success", secrets: result.secrets, ssmStatus: result.conclusion === "success" ? "Success" : "Failed", raw: result.secretsRaw };
|
|
55254
55456
|
const { lines: lines3, failure } = renderVerifySecrets(body);
|
|
@@ -55552,7 +55754,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
55552
55754
|
if (dupe) return fail(`org project set: KEY "${dupe}" was passed to both --var and --set; --set is an alias of --var, so pass each KEY once`);
|
|
55553
55755
|
if (o.secretsFile) {
|
|
55554
55756
|
try {
|
|
55555
|
-
vars.push(`secrets=${(0,
|
|
55757
|
+
vars.push(`secrets=${(0, import_node_fs57.readFileSync)(o.secretsFile, "utf8")}`);
|
|
55556
55758
|
} catch (e) {
|
|
55557
55759
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
55558
55760
|
}
|
|
@@ -55821,9 +56023,11 @@ projectTenantTasks.command("declare <name>").description("register a NEW tenant
|
|
|
55821
56023
|
if (!res.ok) process.exitCode = 1;
|
|
55822
56024
|
});
|
|
55823
56025
|
var registry = program2.command("registry").description("the DDB org registry \u2014 org-level constants");
|
|
55824
|
-
registry.command("org").description("the org config (account id, region, orgProjectId, sagaApiUrl)").option("--json", "machine-readable output").action(async (_o) => {
|
|
56026
|
+
registry.command("org").description("the org config (account id, region, orgProjectId, sagaApiUrl)").option("--json", "machine-readable output").option("--github-spend-minutes <minutes>", "master-only App spend by route, caller and pool over the last 1\u201360 minutes").action(async (_o) => {
|
|
56027
|
+
const minutes = _o.githubSpendMinutes === void 0 ? void 0 : Number(_o.githubSpendMinutes);
|
|
56028
|
+
if (minutes !== void 0 && (!Number.isInteger(minutes) || minutes < 1 || minutes > 60)) return fail("github-spend-minutes must be an integer from 1 to 60");
|
|
55825
56029
|
const cfg = await loadConfig();
|
|
55826
|
-
const org = await fetchOrgConfig(registryClientDeps(cfg));
|
|
56030
|
+
const org = await fetchOrgConfig(registryClientDeps(cfg), minutes);
|
|
55827
56031
|
if (!org) return failGraceful("org config get: Hub API unreachable, unseeded, or this repo is not bootstrapped");
|
|
55828
56032
|
console.log(JSON.stringify(org));
|
|
55829
56033
|
});
|
|
@@ -55940,16 +56144,16 @@ function ciAuditDeps2() {
|
|
|
55940
56144
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
55941
56145
|
readSeedFile: (path2) => {
|
|
55942
56146
|
if (!root) return null;
|
|
55943
|
-
const fullPath = (0,
|
|
55944
|
-
return (0,
|
|
56147
|
+
const fullPath = (0, import_node_path54.join)(root, path2);
|
|
56148
|
+
return (0, import_node_fs57.existsSync)(fullPath) ? (0, import_node_fs57.readFileSync)(fullPath, "utf8") : null;
|
|
55945
56149
|
}
|
|
55946
56150
|
};
|
|
55947
56151
|
}
|
|
55948
56152
|
function hubRoot2() {
|
|
55949
|
-
const fromPkg = (0,
|
|
56153
|
+
const fromPkg = (0, import_node_path54.join)(__dirname, "..", "..");
|
|
55950
56154
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
55951
|
-
if ((0,
|
|
55952
|
-
if ((0,
|
|
56155
|
+
if ((0, import_node_fs57.existsSync)((0, import_node_path54.join)(fromPkg, marker))) return fromPkg;
|
|
56156
|
+
if ((0, import_node_fs57.existsSync)((0, import_node_path54.join)(process.cwd(), marker))) return process.cwd();
|
|
55953
56157
|
return null;
|
|
55954
56158
|
}
|
|
55955
56159
|
registerQueryCommands(program2);
|
|
@@ -56079,10 +56283,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
56079
56283
|
targets = resolution.targets;
|
|
56080
56284
|
}
|
|
56081
56285
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
56082
|
-
const fileMatrix = (0,
|
|
56286
|
+
const fileMatrix = (0, import_node_fs57.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs57.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
56083
56287
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
56084
56288
|
const dataAccess = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
56085
|
-
const sanctioned = (0,
|
|
56289
|
+
const sanctioned = (0, import_node_fs57.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs57.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
56086
56290
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
56087
56291
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
56088
56292
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -56113,16 +56317,16 @@ function directoryBytes(path2) {
|
|
|
56113
56317
|
let total = 0;
|
|
56114
56318
|
let entries;
|
|
56115
56319
|
try {
|
|
56116
|
-
entries = (0,
|
|
56320
|
+
entries = (0, import_node_fs57.readdirSync)(path2, { withFileTypes: true });
|
|
56117
56321
|
} catch {
|
|
56118
56322
|
return 0;
|
|
56119
56323
|
}
|
|
56120
56324
|
for (const entry of entries) {
|
|
56121
|
-
const child2 = (0,
|
|
56325
|
+
const child2 = (0, import_node_path54.join)(path2, entry.name);
|
|
56122
56326
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
56123
56327
|
else {
|
|
56124
56328
|
try {
|
|
56125
|
-
total += (0,
|
|
56329
|
+
total += (0, import_node_fs57.statSync)(child2).size;
|
|
56126
56330
|
} catch {
|
|
56127
56331
|
}
|
|
56128
56332
|
}
|
|
@@ -56130,25 +56334,25 @@ function directoryBytes(path2) {
|
|
|
56130
56334
|
return total;
|
|
56131
56335
|
}
|
|
56132
56336
|
function listDirEntries(dir) {
|
|
56133
|
-
return (0,
|
|
56337
|
+
return (0, import_node_fs57.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
56134
56338
|
}
|
|
56135
56339
|
function readInstalledPluginRefs(configRoot) {
|
|
56136
56340
|
const p = installedPluginsPathForConfig(configRoot);
|
|
56137
|
-
if (!(0,
|
|
56341
|
+
if (!(0, import_node_fs57.existsSync)(p)) return [];
|
|
56138
56342
|
try {
|
|
56139
|
-
return installedPluginPaths((0,
|
|
56343
|
+
return installedPluginPaths((0, import_node_fs57.readFileSync)(p, "utf8"));
|
|
56140
56344
|
} catch {
|
|
56141
56345
|
return null;
|
|
56142
56346
|
}
|
|
56143
56347
|
}
|
|
56144
56348
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
56145
56349
|
return {
|
|
56146
|
-
exists: (p) => (0,
|
|
56147
|
-
listVersionDirs: (root) => (0,
|
|
56350
|
+
exists: (p) => (0, import_node_fs57.existsSync)(p),
|
|
56351
|
+
listVersionDirs: (root) => (0, import_node_fs57.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
56148
56352
|
dirBytes,
|
|
56149
|
-
listStagingDirs: (root) => (0,
|
|
56353
|
+
listStagingDirs: (root) => (0, import_node_fs57.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
56150
56354
|
try {
|
|
56151
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
56355
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path54.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs57.statSync)(p).mtimeMs) };
|
|
56152
56356
|
} catch {
|
|
56153
56357
|
return { name: d.name, mtimeMs: Date.now() };
|
|
56154
56358
|
}
|
|
@@ -56162,10 +56366,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
56162
56366
|
return {
|
|
56163
56367
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
56164
56368
|
mtimeMs: (name) => {
|
|
56165
|
-
const p = (0,
|
|
56166
|
-
if (!(0,
|
|
56369
|
+
const p = (0, import_node_path54.join)(stagingRoot, name);
|
|
56370
|
+
if (!(0, import_node_fs57.existsSync)(p)) return null;
|
|
56167
56371
|
try {
|
|
56168
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
56372
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs57.statSync)(q).mtimeMs);
|
|
56169
56373
|
} catch {
|
|
56170
56374
|
return null;
|
|
56171
56375
|
}
|
|
@@ -56185,13 +56389,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
56185
56389
|
return;
|
|
56186
56390
|
}
|
|
56187
56391
|
const plan = buildPluginCachePlan(
|
|
56188
|
-
(0,
|
|
56392
|
+
(0, import_node_os25.homedir)(),
|
|
56189
56393
|
running,
|
|
56190
56394
|
pluginCacheFsDeps(configRoot, directoryBytes),
|
|
56191
56395
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
56192
56396
|
);
|
|
56193
56397
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
56194
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
56398
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs57.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
56195
56399
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
56196
56400
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
56197
56401
|
else console.log(renderPluginCachePlan(plan, result));
|