@mutmutco/cli 2.53.0 → 2.54.0
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 +396 -219
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3408,7 +3408,7 @@ var program = new Command();
|
|
|
3408
3408
|
|
|
3409
3409
|
// src/index.ts
|
|
3410
3410
|
var import_promises8 = require("node:fs/promises");
|
|
3411
|
-
var
|
|
3411
|
+
var import_node_fs28 = require("node:fs");
|
|
3412
3412
|
|
|
3413
3413
|
// src/rules-sync.ts
|
|
3414
3414
|
function normalizeEol(s) {
|
|
@@ -3601,7 +3601,7 @@ async function sweepDeferredWorktreesWithRetry(store, deps, opts = {}) {
|
|
|
3601
3601
|
}
|
|
3602
3602
|
return last;
|
|
3603
3603
|
}
|
|
3604
|
-
var defaultSleep = (ms) => new Promise((
|
|
3604
|
+
var defaultSleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
3605
3605
|
async function removeWorktreeWithRecovery(wtPath, deps) {
|
|
3606
3606
|
const maxAttempts = deps.maxAttempts ?? 3;
|
|
3607
3607
|
const backoff = deps.backoffMs ?? [250, 1e3];
|
|
@@ -4609,7 +4609,7 @@ async function runMergeTreePreflight(deps, ours, theirs) {
|
|
|
4609
4609
|
async function predictMergeConflicts(deps, ours, theirs) {
|
|
4610
4610
|
return runMergeTreePreflight(deps, ours, theirs);
|
|
4611
4611
|
}
|
|
4612
|
-
async function mergeWithSpineResolution(deps, sourceRef, label,
|
|
4612
|
+
async function mergeWithSpineResolution(deps, sourceRef, label, resolve6, extraTolerated = []) {
|
|
4613
4613
|
try {
|
|
4614
4614
|
await deps.run("git", ["merge", sourceRef, "--no-edit"]);
|
|
4615
4615
|
return;
|
|
@@ -4623,7 +4623,7 @@ async function mergeWithSpineResolution(deps, sourceRef, label, resolve5, extraT
|
|
|
4623
4623
|
unmerged.length === 0 ? `${label} merge failed without conflicted paths \u2014 merge aborted; inspect the repo state and rerun` : `${label} merge conflicts on non-spine path(s): ${blocking.join(", ")} \u2014 merge aborted (the train is misaligned; reconcile the branches via an approved alignment PR, then rerun)`
|
|
4624
4624
|
);
|
|
4625
4625
|
}
|
|
4626
|
-
await deps.run("git", ["checkout", `--${
|
|
4626
|
+
await deps.run("git", ["checkout", `--${resolve6}`, "--", ...unmerged]);
|
|
4627
4627
|
await deps.run("git", ["add", "--", ...unmerged]);
|
|
4628
4628
|
await deps.run("git", ["commit", "--no-edit"]);
|
|
4629
4629
|
}
|
|
@@ -4740,7 +4740,7 @@ function requireProjectMetaForTrain(load, repo) {
|
|
|
4740
4740
|
var CORRELATE_ATTEMPTS = 5;
|
|
4741
4741
|
var CORRELATE_DELAY_MS = 1500;
|
|
4742
4742
|
var CORRELATE_SKEW_SLACK_MS = 1e4;
|
|
4743
|
-
var defaultSleep2 = (ms) => new Promise((
|
|
4743
|
+
var defaultSleep2 = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
4744
4744
|
function resolveSleep(deps) {
|
|
4745
4745
|
return deps.sleep ?? defaultSleep2;
|
|
4746
4746
|
}
|
|
@@ -4959,6 +4959,23 @@ async function waitForRequiredTrainChecks(deps, ctx, sha, required) {
|
|
|
4959
4959
|
`timed out waiting for required train checks on ${sha}: ${lastError ? `last error: ${lastError}` : lastStatus}`
|
|
4960
4960
|
);
|
|
4961
4961
|
}
|
|
4962
|
+
function partialTrainRecoveryError(cause, input) {
|
|
4963
|
+
const causeMessage = cause instanceof Error ? cause.message : String(cause);
|
|
4964
|
+
const branch = input.stage;
|
|
4965
|
+
const releaseState = input.stage === "rc" ? "GitHub Release n/a" : "GitHub Release not created";
|
|
4966
|
+
const releaseCommand = input.stage === "main" ? `
|
|
4967
|
+
2. gh release create ${input.tag} --target main --generate-notes --latest --repo ${input.repo}` : "";
|
|
4968
|
+
const deployStep = input.stage === "main" ? "3" : "2";
|
|
4969
|
+
return new Error(
|
|
4970
|
+
`${causeMessage}
|
|
4971
|
+
|
|
4972
|
+
partial train state: tag ${input.tag} is already pushed; origin/${branch} has not been pushed; ${releaseState}; deploy not dispatched.
|
|
4973
|
+
Recovery sequence:
|
|
4974
|
+
1. git push origin ${branch}` + releaseCommand + `
|
|
4975
|
+
${deployStep}. mmi-cli tenant redeploy ${input.repo} ${input.stage} --watch
|
|
4976
|
+
Do not delete or force-move the pushed tag; rerun the train only after confirming the branch, release, and deploy states above.`
|
|
4977
|
+
);
|
|
4978
|
+
}
|
|
4962
4979
|
async function ensureTagPushed(deps, tag, sha) {
|
|
4963
4980
|
const remoteOut = await deps.run("git", ["ls-remote", "origin", `refs/tags/${tag}`]);
|
|
4964
4981
|
const remoteSha = clean(remoteOut).split(/\s+/)[0] || "";
|
|
@@ -5167,7 +5184,12 @@ async function mergeSourceToMain(deps, deployModel, args) {
|
|
|
5167
5184
|
async function completeMainRelease(deps, ctx, meta, deployModel, watch, options, tag, releaseSha) {
|
|
5168
5185
|
await ensureTagPushed(deps, tag, releaseSha);
|
|
5169
5186
|
const requiredChecks = await discoverRequiredCheckContexts(deps, ctx, "main");
|
|
5170
|
-
|
|
5187
|
+
let checks;
|
|
5188
|
+
try {
|
|
5189
|
+
checks = await waitForRequiredTrainChecks(deps, ctx, releaseSha, requiredChecks);
|
|
5190
|
+
} catch (e) {
|
|
5191
|
+
throw partialTrainRecoveryError(e, { repo: ctx.repo, tag, stage: "main" });
|
|
5192
|
+
}
|
|
5171
5193
|
await deps.run("git", ["push", "origin", "main"]);
|
|
5172
5194
|
const releaseUrl = clean(await deps.run("gh", ["release", "create", tag, "--target", "main", "--generate-notes", "--latest", "--repo", ctx.repo])) || void 0;
|
|
5173
5195
|
await verifyPublishedRelease(deps, ctx.repo, tag, "main", releaseSha);
|
|
@@ -5218,7 +5240,12 @@ async function runTrainApplyPipeline(mode, input) {
|
|
|
5218
5240
|
const resumeNote = resume.tag ? resume.note : void 0;
|
|
5219
5241
|
await ensureTagPushed(deps, tag2, rcSha);
|
|
5220
5242
|
const requiredChecks = await discoverRequiredCheckContexts(deps, ctx, "rc");
|
|
5221
|
-
|
|
5243
|
+
let checks2;
|
|
5244
|
+
try {
|
|
5245
|
+
checks2 = await waitForRequiredTrainChecks(deps, ctx, rcSha, requiredChecks);
|
|
5246
|
+
} catch (e) {
|
|
5247
|
+
throw partialTrainRecoveryError(e, { repo: ctx.repo, tag: tag2, stage: "rc" });
|
|
5248
|
+
}
|
|
5222
5249
|
const autoRunSince = (deps.now ?? Date.now)();
|
|
5223
5250
|
await deps.run("git", ["push", "origin", "rc"]);
|
|
5224
5251
|
const d2 = await dispatchDeploy(deps, ctx, "rc", "rc", deployModel2, watch, autoRunSince, rcSha);
|
|
@@ -5654,7 +5681,7 @@ async function fetchWithRetry(fetchImpl, url, init, opts = {}) {
|
|
|
5654
5681
|
const attempts = opts.attempts ?? 3;
|
|
5655
5682
|
const baseDelayMs = opts.baseDelayMs ?? 250;
|
|
5656
5683
|
const retryOn = opts.retryOn ?? ((res) => res.status >= 500);
|
|
5657
|
-
const sleep = opts.sleep ?? ((ms) => new Promise((
|
|
5684
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
5658
5685
|
let lastErr;
|
|
5659
5686
|
for (let i = 0; i < attempts; i++) {
|
|
5660
5687
|
const isLast = i === attempts - 1;
|
|
@@ -5713,7 +5740,7 @@ function hardExit(code) {
|
|
|
5713
5740
|
async function cleanExit(code) {
|
|
5714
5741
|
process.exitCode = code;
|
|
5715
5742
|
await closeHttpPool();
|
|
5716
|
-
await new Promise((
|
|
5743
|
+
await new Promise((resolve6) => setImmediate(resolve6));
|
|
5717
5744
|
process.exit(code);
|
|
5718
5745
|
}
|
|
5719
5746
|
async function failGraceful(msg) {
|
|
@@ -6140,12 +6167,12 @@ function parseHeadUpdate(raw) {
|
|
|
6140
6167
|
}
|
|
6141
6168
|
async function runHeadEngine(prompt, timeoutMs = HEAD_ENGINE_TIMEOUT_MS) {
|
|
6142
6169
|
const { cmd, args, shell: shell2 } = resolveEngine(process.platform, process.env.SAGA_HEAD_ENGINE);
|
|
6143
|
-
return await new Promise((
|
|
6170
|
+
return await new Promise((resolve6) => {
|
|
6144
6171
|
let child;
|
|
6145
6172
|
try {
|
|
6146
6173
|
child = (0, import_node_child_process3.spawn)(cmd, args, { shell: shell2, windowsHide: true });
|
|
6147
6174
|
} catch {
|
|
6148
|
-
return
|
|
6175
|
+
return resolve6("");
|
|
6149
6176
|
}
|
|
6150
6177
|
let out = "";
|
|
6151
6178
|
let done = false;
|
|
@@ -6153,7 +6180,7 @@ async function runHeadEngine(prompt, timeoutMs = HEAD_ENGINE_TIMEOUT_MS) {
|
|
|
6153
6180
|
if (done) return;
|
|
6154
6181
|
done = true;
|
|
6155
6182
|
clearTimeout(timer);
|
|
6156
|
-
|
|
6183
|
+
resolve6(v);
|
|
6157
6184
|
};
|
|
6158
6185
|
const timer = setTimeout(() => {
|
|
6159
6186
|
try {
|
|
@@ -6301,8 +6328,8 @@ async function readStdin(opts = {}) {
|
|
|
6301
6328
|
})().catch(() => {
|
|
6302
6329
|
});
|
|
6303
6330
|
let timer;
|
|
6304
|
-
const timeout = new Promise((
|
|
6305
|
-
timer = setTimeout(
|
|
6331
|
+
const timeout = new Promise((resolve6) => {
|
|
6332
|
+
timer = setTimeout(resolve6, timeoutMs);
|
|
6306
6333
|
});
|
|
6307
6334
|
try {
|
|
6308
6335
|
await Promise.race([drain, timeout]);
|
|
@@ -6975,7 +7002,7 @@ var execFileP3 = (0, import_node_util5.promisify)(import_node_child_process5.exe
|
|
|
6975
7002
|
var DOCKER_TIMEOUT_MS = 15e3;
|
|
6976
7003
|
var EARLY_EXIT_GRACE_MS = 2e3;
|
|
6977
7004
|
function waitForProcessStability(child, graceMs = EARLY_EXIT_GRACE_MS) {
|
|
6978
|
-
return new Promise((
|
|
7005
|
+
return new Promise((resolve6, reject) => {
|
|
6979
7006
|
let settled = false;
|
|
6980
7007
|
const finish = (fn) => {
|
|
6981
7008
|
if (settled) return;
|
|
@@ -6985,7 +7012,7 @@ function waitForProcessStability(child, graceMs = EARLY_EXIT_GRACE_MS) {
|
|
|
6985
7012
|
child.removeAllListeners("exit");
|
|
6986
7013
|
fn();
|
|
6987
7014
|
};
|
|
6988
|
-
const timer = setTimeout(() => finish(
|
|
7015
|
+
const timer = setTimeout(() => finish(resolve6), graceMs);
|
|
6989
7016
|
child.on("error", (err) => finish(() => reject(new Error(`stage process failed to start: ${err.message}`))));
|
|
6990
7017
|
child.on("exit", (code, signal) => {
|
|
6991
7018
|
const detail = code != null ? `code ${code}` : signal ? `signal ${signal}` : "unknown reason";
|
|
@@ -7195,10 +7222,10 @@ function pickStagePort(range, isFree) {
|
|
|
7195
7222
|
throw new Error(`no free stage port in range ${start}-${end} \u2014 every port is in use`);
|
|
7196
7223
|
}
|
|
7197
7224
|
function isPortFree(port) {
|
|
7198
|
-
return new Promise((
|
|
7225
|
+
return new Promise((resolve6) => {
|
|
7199
7226
|
const srv = (0, import_node_net.createServer)();
|
|
7200
|
-
srv.once("error", () =>
|
|
7201
|
-
srv.once("listening", () => srv.close(() =>
|
|
7227
|
+
srv.once("error", () => resolve6(false));
|
|
7228
|
+
srv.once("listening", () => srv.close(() => resolve6(true)));
|
|
7202
7229
|
srv.listen(port, "127.0.0.1");
|
|
7203
7230
|
});
|
|
7204
7231
|
}
|
|
@@ -7409,7 +7436,7 @@ async function killTree(pid) {
|
|
|
7409
7436
|
} catch {
|
|
7410
7437
|
}
|
|
7411
7438
|
}
|
|
7412
|
-
await new Promise((
|
|
7439
|
+
await new Promise((resolve6) => setTimeout(resolve6, 500));
|
|
7413
7440
|
try {
|
|
7414
7441
|
process.kill(-pid, "SIGKILL");
|
|
7415
7442
|
} catch {
|
|
@@ -7430,7 +7457,7 @@ async function waitForHealth(url, timeoutMs, anyStatus = false) {
|
|
|
7430
7457
|
} catch (e) {
|
|
7431
7458
|
last = e.message;
|
|
7432
7459
|
}
|
|
7433
|
-
await new Promise((
|
|
7460
|
+
await new Promise((resolve6) => setTimeout(resolve6, 1e3));
|
|
7434
7461
|
}
|
|
7435
7462
|
throw new Error(`stage health check timed out for ${url}${last ? ` (${last})` : ""}`);
|
|
7436
7463
|
}
|
|
@@ -11400,7 +11427,7 @@ function whoamiLine(report) {
|
|
|
11400
11427
|
}
|
|
11401
11428
|
|
|
11402
11429
|
// src/index.ts
|
|
11403
|
-
var
|
|
11430
|
+
var import_node_path25 = require("node:path");
|
|
11404
11431
|
|
|
11405
11432
|
// src/merge-ci-policy.ts
|
|
11406
11433
|
function resolveMergeCiPolicy(input) {
|
|
@@ -12464,7 +12491,7 @@ var PR_LAND_STATE_READ_DELAY_MS = 2e3;
|
|
|
12464
12491
|
async function readGhPrStateWithRetry(fetchState2, options) {
|
|
12465
12492
|
const retries = options?.retries ?? PR_LAND_STATE_READ_RETRIES;
|
|
12466
12493
|
const delayMs = options?.delayMs ?? PR_LAND_STATE_READ_DELAY_MS;
|
|
12467
|
-
const sleep = options?.sleep ?? ((ms) => new Promise((
|
|
12494
|
+
const sleep = options?.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
12468
12495
|
let lastError = "empty state";
|
|
12469
12496
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
12470
12497
|
try {
|
|
@@ -14207,7 +14234,7 @@ function clean3(out) {
|
|
|
14207
14234
|
return out.trim();
|
|
14208
14235
|
}
|
|
14209
14236
|
function sleeper(deps) {
|
|
14210
|
-
return deps.sleep ?? ((ms) => new Promise((
|
|
14237
|
+
return deps.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
14211
14238
|
}
|
|
14212
14239
|
function normalizeHotfixVersion(input) {
|
|
14213
14240
|
const m = /^v?(\d+\.\d+\.\d+)$/.exec(input.trim());
|
|
@@ -16881,8 +16908,8 @@ function vaultPointer(slug) {
|
|
|
16881
16908
|
slug,
|
|
16882
16909
|
root,
|
|
16883
16910
|
tiers: {
|
|
16884
|
-
project: `${root}
|
|
16885
|
-
org: [
|
|
16911
|
+
project: `${root}/{dev,rc,main}/* (project-admin self-serve for this repo)`,
|
|
16912
|
+
org: [`/mmi-future/{shared,cloudflare,mmi-hub,...}/* (org-infra, master-gated)`]
|
|
16886
16913
|
},
|
|
16887
16914
|
stages: ["dev", "rc", "main"],
|
|
16888
16915
|
// Google OAuth is one client per repo; creds live at every stage under the standard key names
|
|
@@ -16895,15 +16922,16 @@ function vaultPointer(slug) {
|
|
|
16895
16922
|
function formatVaultPointer(p) {
|
|
16896
16923
|
const lines = [
|
|
16897
16924
|
`vault root: ${p.root}`,
|
|
16898
|
-
` project
|
|
16899
|
-
` org
|
|
16925
|
+
` project repo tree: ${p.tiers.project}`,
|
|
16926
|
+
` org-infra tree: ${p.tiers.org.join(" \xB7 ")}`,
|
|
16900
16927
|
`stages: ${p.stages.join(", ")} (local is port-agnostic, reuses dev)`,
|
|
16901
16928
|
`well-known keys:`,
|
|
16902
16929
|
...Object.entries(p.wellKnown).map(([k, keys]) => ` ${k}: ${keys.join(", ")}`),
|
|
16903
16930
|
``,
|
|
16904
16931
|
`enumerate actual keys: mmi-cli secrets list`,
|
|
16905
16932
|
`read one: mmi-cli secrets get <stage>/<KEY> (e.g. main/GOOGLE_CLIENT_ID)`,
|
|
16906
|
-
`set a
|
|
16933
|
+
`set a key: mmi-cli secrets set <stage>/<KEY> (value via stdin; project-admin self-serves own repo)`,
|
|
16934
|
+
`import Rails creds: mmi-cli secrets import-rails-credentials --stage main --map secret_key_base=SECRET_KEY_BASE`,
|
|
16907
16935
|
`copy provider keys: mmi-cli secrets copy --from rc --to dev --keys RECALL_API_KEY,GEMINI_API_KEY`
|
|
16908
16936
|
];
|
|
16909
16937
|
return lines.join("\n");
|
|
@@ -17256,6 +17284,89 @@ async function secretsSet(deps, key, opts) {
|
|
|
17256
17284
|
}
|
|
17257
17285
|
return putSecret(deps, key, value, opts);
|
|
17258
17286
|
}
|
|
17287
|
+
function parseRailsCredentialMapping(raw, stage2) {
|
|
17288
|
+
const eq = raw.indexOf("=");
|
|
17289
|
+
if (eq <= 0 || eq === raw.length - 1) return null;
|
|
17290
|
+
const credentialPath = raw.slice(0, eq).trim();
|
|
17291
|
+
const envKey = raw.slice(eq + 1).trim();
|
|
17292
|
+
const vaultKey = stageKey2(stage2, envKey);
|
|
17293
|
+
if (!credentialPath || !envKey || !isValidSecretKey(vaultKey)) return null;
|
|
17294
|
+
return { credentialPath, envKey, vaultKey };
|
|
17295
|
+
}
|
|
17296
|
+
function credentialValueAt(root, path2) {
|
|
17297
|
+
let cur = root;
|
|
17298
|
+
for (const part of path2.split(".").filter(Boolean)) {
|
|
17299
|
+
if (!cur || typeof cur !== "object" || !(part in cur)) return void 0;
|
|
17300
|
+
cur = cur[part];
|
|
17301
|
+
}
|
|
17302
|
+
return cur;
|
|
17303
|
+
}
|
|
17304
|
+
function credentialValueToSecret(value) {
|
|
17305
|
+
if (value === void 0 || value === null) return null;
|
|
17306
|
+
if (typeof value === "string") return value || null;
|
|
17307
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
17308
|
+
return JSON.stringify(value);
|
|
17309
|
+
}
|
|
17310
|
+
async function secretsImportRailsCredentials(deps, opts) {
|
|
17311
|
+
const mappings = opts.mappings.map((m) => parseRailsCredentialMapping(m, opts.stage));
|
|
17312
|
+
const bad = mappings.findIndex((m) => !m);
|
|
17313
|
+
if (bad !== -1) {
|
|
17314
|
+
deps.err(`invalid Rails credential mapping ${JSON.stringify(opts.mappings[bad])}; use credential.path=ENV_KEY`);
|
|
17315
|
+
return false;
|
|
17316
|
+
}
|
|
17317
|
+
const parsed = mappings;
|
|
17318
|
+
if (!parsed.length) {
|
|
17319
|
+
deps.err("secrets import-rails-credentials: at least one --map credential.path=ENV_KEY is required");
|
|
17320
|
+
return false;
|
|
17321
|
+
}
|
|
17322
|
+
if (opts.removeFiles && !deps.removeFile) {
|
|
17323
|
+
deps.err("secrets import-rails-credentials: --remove-files is unavailable in this execution context");
|
|
17324
|
+
return false;
|
|
17325
|
+
}
|
|
17326
|
+
let credentials;
|
|
17327
|
+
try {
|
|
17328
|
+
credentials = await deps.decryptRailsCredentials({
|
|
17329
|
+
appDir: opts.appDir,
|
|
17330
|
+
credentialsFile: opts.credentialsFile,
|
|
17331
|
+
masterKeyFile: opts.masterKeyFile
|
|
17332
|
+
});
|
|
17333
|
+
} catch (e) {
|
|
17334
|
+
deps.err(`secrets import-rails-credentials: could not decrypt Rails credentials: ${e.message}`);
|
|
17335
|
+
return false;
|
|
17336
|
+
}
|
|
17337
|
+
const imports = [];
|
|
17338
|
+
for (const mapping of parsed) {
|
|
17339
|
+
const value = credentialValueToSecret(credentialValueAt(credentials, mapping.credentialPath));
|
|
17340
|
+
if (!value) {
|
|
17341
|
+
deps.err(`credential path ${mapping.credentialPath} not found or empty; nothing written`);
|
|
17342
|
+
return false;
|
|
17343
|
+
}
|
|
17344
|
+
imports.push({ ...mapping, value });
|
|
17345
|
+
}
|
|
17346
|
+
if (opts.dryRun) {
|
|
17347
|
+
for (const item of imports) deps.log(`would import ${item.credentialPath} -> ${item.vaultKey}`);
|
|
17348
|
+
deps.log(`${imports.length} Rails credential mapping(s) checked; no values printed and nothing written`);
|
|
17349
|
+
return true;
|
|
17350
|
+
}
|
|
17351
|
+
let written = 0;
|
|
17352
|
+
for (const item of imports) {
|
|
17353
|
+
const ok = await putSecret(deps, item.vaultKey, item.value, opts);
|
|
17354
|
+
if (!ok) {
|
|
17355
|
+
deps.err(
|
|
17356
|
+
`Rails credentials import stopped after a partial write (${written}/${imports.length}); local encrypted files left untouched`
|
|
17357
|
+
);
|
|
17358
|
+
return false;
|
|
17359
|
+
}
|
|
17360
|
+
written += 1;
|
|
17361
|
+
deps.log(`imported ${item.credentialPath} -> ${item.vaultKey}`);
|
|
17362
|
+
}
|
|
17363
|
+
if (opts.removeFiles) {
|
|
17364
|
+
const files = [...new Set([opts.credentialsFile, opts.masterKeyFile].filter((p) => Boolean(p)))];
|
|
17365
|
+
for (const file of files) await deps.removeFile(file);
|
|
17366
|
+
if (files.length) deps.log(`removed ${files.length} local Rails encrypted credential file(s) after successful import`);
|
|
17367
|
+
}
|
|
17368
|
+
return true;
|
|
17369
|
+
}
|
|
17259
17370
|
async function secretsEdit(deps, key, opts) {
|
|
17260
17371
|
return secretsSet(deps, key, opts);
|
|
17261
17372
|
}
|
|
@@ -17366,12 +17477,52 @@ async function secretsUse(deps, key, opts) {
|
|
|
17366
17477
|
` \u2022 Runtime / agents: read it keylessly at runtime via the box's OIDC role (it can read its own ${tier} tier). Never bake it into an image or commit it.`,
|
|
17367
17478
|
` \u2022 CI (GitHub Actions): the workflow assumes its OIDC role and runs \`aws ssm get-parameter --with-decryption --name ${path2}\` \u2014 no GitHub secret.`,
|
|
17368
17479
|
" \u2022 Local dev: pull it into a gitignored .env from the vault. To confirm access without printing in PowerShell: `$null = mmi-cli secrets get " + key + "`; in POSIX shells: `mmi-cli secrets get " + key + " >/dev/null`. Never paste it into tracked files or chat.",
|
|
17369
|
-
tier === "project" ? " \u2022
|
|
17480
|
+
tier === "project" ? " \u2022 Bare keys default to dev/. Use an explicit rc/<KEY> or main/<KEY> when the stage needs its own value." : " \u2022 For your own product repo, project-admins self-serve this stage key. Org-infra/cross-slug keys remain master-gated."
|
|
17370
17481
|
].join("\n")
|
|
17371
17482
|
);
|
|
17372
17483
|
}
|
|
17373
17484
|
|
|
17374
17485
|
// src/secrets-commands.ts
|
|
17486
|
+
var import_node_fs22 = require("node:fs");
|
|
17487
|
+
var import_node_path20 = require("node:path");
|
|
17488
|
+
var RAILS_CREDENTIALS_DECRYPT_TIMEOUT_MS = 3e4;
|
|
17489
|
+
var DEFAULT_RAILS_CREDENTIALS_FILE = "config/credentials.yml.enc";
|
|
17490
|
+
var DEFAULT_RAILS_MASTER_KEY_FILE = "config/master.key";
|
|
17491
|
+
function collectMap(value, previous = []) {
|
|
17492
|
+
return [...previous, value];
|
|
17493
|
+
}
|
|
17494
|
+
async function decryptRailsCredentials(input) {
|
|
17495
|
+
const appDir = (0, import_node_path20.resolve)(input.appDir ?? process.cwd());
|
|
17496
|
+
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
17497
|
+
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
17498
|
+
const credentialsPath = (0, import_node_path20.resolve)(appDir, credentialsFile);
|
|
17499
|
+
const masterKeyPath = (0, import_node_path20.resolve)(appDir, masterKeyFile);
|
|
17500
|
+
const env = {
|
|
17501
|
+
...process.env,
|
|
17502
|
+
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
17503
|
+
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
17504
|
+
};
|
|
17505
|
+
if ((0, import_node_fs22.existsSync)(masterKeyPath)) {
|
|
17506
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs22.readFileSync)(masterKeyPath, "utf8").trim();
|
|
17507
|
+
}
|
|
17508
|
+
const script = [
|
|
17509
|
+
'require "json"',
|
|
17510
|
+
'require "active_support/encrypted_configuration"',
|
|
17511
|
+
'config_path = ENV.fetch("MMI_RAILS_CREDENTIALS_FILE")',
|
|
17512
|
+
'key_path = ENV.fetch("MMI_RAILS_MASTER_KEY_FILE")',
|
|
17513
|
+
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
17514
|
+
"puts JSON.generate(config.config)"
|
|
17515
|
+
].join("; ");
|
|
17516
|
+
const args = ["exec", "ruby", "-e", script];
|
|
17517
|
+
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
17518
|
+
const cmdArgs = process.platform === "win32" ? ["/c", "bundle", ...args] : args;
|
|
17519
|
+
const { stdout } = await execFileP2(cmd, cmdArgs, {
|
|
17520
|
+
cwd: appDir,
|
|
17521
|
+
env,
|
|
17522
|
+
timeout: RAILS_CREDENTIALS_DECRYPT_TIMEOUT_MS
|
|
17523
|
+
});
|
|
17524
|
+
return JSON.parse(stdout);
|
|
17525
|
+
}
|
|
17375
17526
|
async function readSecretStdin() {
|
|
17376
17527
|
if (process.stdin.isTTY) {
|
|
17377
17528
|
process.stderr.write(
|
|
@@ -17466,6 +17617,32 @@ function registerSecretsCommands(program3) {
|
|
|
17466
17617
|
});
|
|
17467
17618
|
if (!ok) process.exitCode = 1;
|
|
17468
17619
|
}));
|
|
17620
|
+
secrets.command("import-rails-credentials").description("decrypt Rails credentials and import explicit mappings into the vault (values never printed)").requiredOption("--stage <dev|rc|main>", "target vault stage").option("--map <credential.path=ENV_KEY>", "explicit credential-to-env mapping; repeat for each key", collectMap, []).option("--app-dir <path>", "Rails app directory (defaults to cwd)").option("--credentials-file <path>", `encrypted credentials path relative to --app-dir (default: ${DEFAULT_RAILS_CREDENTIALS_FILE})`).option("--master-key-file <path>", `master key path relative to --app-dir (default: ${DEFAULT_RAILS_MASTER_KEY_FILE})`).option("--dry-run", "decrypt and show mapped key names without writing values").option("--remove-files", "delete credentials/master key files after every mapped value imports successfully").option("--repo <owner/repo>", "target repo (defaults to the current repo)").action((o) => withSecrets(async (d) => {
|
|
17621
|
+
const stages = ["dev", "rc", "main"];
|
|
17622
|
+
if (!stages.includes(o.stage)) {
|
|
17623
|
+
return fail("secrets import-rails-credentials: --stage must be dev, rc, or main");
|
|
17624
|
+
}
|
|
17625
|
+
const credentialsFile = o.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
17626
|
+
const masterKeyFile = o.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
17627
|
+
const ok = await secretsImportRailsCredentials(
|
|
17628
|
+
{
|
|
17629
|
+
...d,
|
|
17630
|
+
decryptRailsCredentials,
|
|
17631
|
+
removeFile: (path2) => (0, import_node_fs22.unlinkSync)((0, import_node_path20.resolve)(o.appDir ?? process.cwd(), path2))
|
|
17632
|
+
},
|
|
17633
|
+
{
|
|
17634
|
+
repo: o.repo,
|
|
17635
|
+
stage: o.stage,
|
|
17636
|
+
mappings: o.map,
|
|
17637
|
+
appDir: o.appDir,
|
|
17638
|
+
credentialsFile,
|
|
17639
|
+
masterKeyFile,
|
|
17640
|
+
dryRun: o.dryRun,
|
|
17641
|
+
removeFiles: o.removeFiles
|
|
17642
|
+
}
|
|
17643
|
+
);
|
|
17644
|
+
if (!ok) process.exitCode = 1;
|
|
17645
|
+
}));
|
|
17469
17646
|
secrets.command("rm <key>").description("remove a secret (project tier self-serve; org tier needs a grant)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").action((key, o) => withSecrets((d) => secretsRemove(d, key, o)));
|
|
17470
17647
|
secrets.command("use <key>").description("print guidance on consuming a secret without committing it (no value)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").action((key, o) => withSecrets((d) => secretsUse(d, key, o)));
|
|
17471
17648
|
secrets.command("grant <repo> <login> <key>").description("MASTER-ONLY: grant a project-admin standing access to a specific org-tier secret").action((repo, login, key) => withSecrets((d) => secretsGrant(d, repo, login, key, {})));
|
|
@@ -17627,9 +17804,9 @@ function authorizeBodyHasMismatch(body) {
|
|
|
17627
17804
|
}
|
|
17628
17805
|
|
|
17629
17806
|
// src/doctor-run.ts
|
|
17630
|
-
var
|
|
17807
|
+
var import_node_fs27 = require("node:fs");
|
|
17631
17808
|
var import_promises7 = require("node:fs/promises");
|
|
17632
|
-
var
|
|
17809
|
+
var import_node_path24 = require("node:path");
|
|
17633
17810
|
var import_node_os5 = require("node:os");
|
|
17634
17811
|
|
|
17635
17812
|
// src/plugin-guard.ts
|
|
@@ -17651,9 +17828,9 @@ function buildGuardSessionStartLine(state, opts = {}) {
|
|
|
17651
17828
|
|
|
17652
17829
|
// src/cursor-plugin-seed.ts
|
|
17653
17830
|
var import_node_child_process12 = require("node:child_process");
|
|
17654
|
-
var
|
|
17831
|
+
var import_node_fs23 = require("node:fs");
|
|
17655
17832
|
var import_node_os4 = require("node:os");
|
|
17656
|
-
var
|
|
17833
|
+
var import_node_path21 = require("node:path");
|
|
17657
17834
|
var import_node_util7 = require("node:util");
|
|
17658
17835
|
function isSemverVersion(v) {
|
|
17659
17836
|
return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
|
|
@@ -17670,17 +17847,17 @@ function ghReleaseTarballApiArgs(tag) {
|
|
|
17670
17847
|
}
|
|
17671
17848
|
function cursorUserGlobalStatePath() {
|
|
17672
17849
|
if (process.platform === "win32") {
|
|
17673
|
-
const base = process.env.APPDATA || (0,
|
|
17674
|
-
return (0,
|
|
17850
|
+
const base = process.env.APPDATA || (0, import_node_path21.join)((0, import_node_os4.homedir)(), "AppData", "Roaming");
|
|
17851
|
+
return (0, import_node_path21.join)(base, "Cursor", "User", "globalStorage", "state.vscdb");
|
|
17675
17852
|
}
|
|
17676
17853
|
if (process.platform === "darwin") {
|
|
17677
|
-
return (0,
|
|
17854
|
+
return (0, import_node_path21.join)((0, import_node_os4.homedir)(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
17678
17855
|
}
|
|
17679
|
-
return (0,
|
|
17856
|
+
return (0, import_node_path21.join)((0, import_node_os4.homedir)(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
17680
17857
|
}
|
|
17681
17858
|
async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
|
|
17682
17859
|
const dbPath = cursorUserGlobalStatePath();
|
|
17683
|
-
if (!(0,
|
|
17860
|
+
if (!(0, import_node_fs23.existsSync)(dbPath)) return void 0;
|
|
17684
17861
|
try {
|
|
17685
17862
|
const { stdout } = await execFileP5("sqlite3", [dbPath, `SELECT value FROM ItemTable WHERE key = '${CURSOR_THIRD_PARTY_STATE_KEY}';`], {
|
|
17686
17863
|
timeout: 5e3
|
|
@@ -17694,57 +17871,57 @@ async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
|
|
|
17694
17871
|
}
|
|
17695
17872
|
}
|
|
17696
17873
|
function syncDirContents(src, dest) {
|
|
17697
|
-
(0,
|
|
17698
|
-
for (const name of (0,
|
|
17699
|
-
(0,
|
|
17874
|
+
(0, import_node_fs23.mkdirSync)(dest, { recursive: true });
|
|
17875
|
+
for (const name of (0, import_node_fs23.readdirSync)(dest)) {
|
|
17876
|
+
(0, import_node_fs23.rmSync)((0, import_node_path21.join)(dest, name), { recursive: true, force: true });
|
|
17700
17877
|
}
|
|
17701
|
-
(0,
|
|
17878
|
+
(0, import_node_fs23.cpSync)(src, dest, { recursive: true });
|
|
17702
17879
|
}
|
|
17703
17880
|
function releaseTag(releasedVersion) {
|
|
17704
17881
|
return releasedVersion.startsWith("v") ? releasedVersion : `v${releasedVersion}`;
|
|
17705
17882
|
}
|
|
17706
17883
|
async function extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5) {
|
|
17707
|
-
const tarFile = (0,
|
|
17884
|
+
const tarFile = (0, import_node_path21.join)(tmpRoot, "archive.tar");
|
|
17708
17885
|
try {
|
|
17709
17886
|
await execFileP5("git", gitFetchReleaseTagArgs(hubCheckout, tag), { timeout: 6e4 });
|
|
17710
17887
|
await execFileP5("git", ["-C", hubCheckout, "archive", "--format=tar", `--output=${tarFile}`, tag, "plugins/mmi"], {
|
|
17711
17888
|
timeout: 6e4
|
|
17712
17889
|
});
|
|
17713
17890
|
await execFileP5("tar", ["-xf", tarFile, "-C", tmpRoot], { timeout: 6e4 });
|
|
17714
|
-
const pluginMmi = (0,
|
|
17715
|
-
return (0,
|
|
17891
|
+
const pluginMmi = (0, import_node_path21.join)(tmpRoot, "plugins", "mmi");
|
|
17892
|
+
return (0, import_node_fs23.existsSync)((0, import_node_path21.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
17716
17893
|
} catch {
|
|
17717
17894
|
return void 0;
|
|
17718
17895
|
}
|
|
17719
17896
|
}
|
|
17720
17897
|
async function downloadPluginMmiViaGh(tag, tmpRoot) {
|
|
17721
|
-
const tarPath = (0,
|
|
17898
|
+
const tarPath = (0, import_node_path21.join)(tmpRoot, "repo.tgz");
|
|
17722
17899
|
try {
|
|
17723
|
-
(0,
|
|
17900
|
+
(0, import_node_fs23.mkdirSync)(tmpRoot, { recursive: true });
|
|
17724
17901
|
const { stdout } = await execFileBuffer("gh", ghReleaseTarballApiArgs(tag), {
|
|
17725
17902
|
timeout: 12e4,
|
|
17726
17903
|
maxBuffer: 100 * 1024 * 1024,
|
|
17727
17904
|
encoding: "buffer",
|
|
17728
17905
|
windowsHide: true
|
|
17729
17906
|
});
|
|
17730
|
-
(0,
|
|
17907
|
+
(0, import_node_fs23.writeFileSync)(tarPath, stdout);
|
|
17731
17908
|
await execFileBuffer("tar", ["-xzf", tarPath, "-C", tmpRoot], { timeout: 12e4, windowsHide: true });
|
|
17732
|
-
const top = (0,
|
|
17909
|
+
const top = (0, import_node_fs23.readdirSync)(tmpRoot).find((entry) => entry !== "repo.tgz");
|
|
17733
17910
|
if (!top) return void 0;
|
|
17734
|
-
const pluginMmi = (0,
|
|
17735
|
-
return (0,
|
|
17911
|
+
const pluginMmi = (0, import_node_path21.join)(tmpRoot, top, "plugins", "mmi");
|
|
17912
|
+
return (0, import_node_fs23.existsSync)((0, import_node_path21.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
17736
17913
|
} catch {
|
|
17737
17914
|
return void 0;
|
|
17738
17915
|
}
|
|
17739
17916
|
}
|
|
17740
17917
|
async function resolvePluginMmiSource(releasedVersion, hubCheckout, tmpRoot, execFileP5) {
|
|
17741
|
-
(0,
|
|
17918
|
+
(0, import_node_fs23.mkdirSync)(tmpRoot, { recursive: true });
|
|
17742
17919
|
const tag = releaseTag(releasedVersion);
|
|
17743
17920
|
if (hubCheckout) {
|
|
17744
17921
|
const fromHub = await extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5);
|
|
17745
17922
|
if (fromHub) return fromHub;
|
|
17746
17923
|
}
|
|
17747
|
-
return downloadPluginMmiViaGh(tag, (0,
|
|
17924
|
+
return downloadPluginMmiViaGh(tag, (0, import_node_path21.join)(tmpRoot, "gh"));
|
|
17748
17925
|
}
|
|
17749
17926
|
function cursorPluginPinsNeedingSeed(pins, releasedVersion) {
|
|
17750
17927
|
if (!isSemverVersion(releasedVersion)) return pins.filter((pin) => !pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty);
|
|
@@ -17765,7 +17942,7 @@ async function applyCursorPluginCacheSeed(input) {
|
|
|
17765
17942
|
for (const pin of pinsToSeed) {
|
|
17766
17943
|
syncDirContents(source, pin.path);
|
|
17767
17944
|
}
|
|
17768
|
-
(0,
|
|
17945
|
+
(0, import_node_fs23.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
17769
17946
|
return true;
|
|
17770
17947
|
}
|
|
17771
17948
|
|
|
@@ -18874,16 +19051,16 @@ function buildPluginResolvabilityCheck(input) {
|
|
|
18874
19051
|
}
|
|
18875
19052
|
|
|
18876
19053
|
// src/kb-drift-report.ts
|
|
18877
|
-
var
|
|
18878
|
-
var
|
|
19054
|
+
var import_node_fs24 = require("node:fs");
|
|
19055
|
+
var import_node_path22 = require("node:path");
|
|
18879
19056
|
function yesterdayIso() {
|
|
18880
19057
|
const d = /* @__PURE__ */ new Date();
|
|
18881
19058
|
d.setUTCDate(d.getUTCDate() - 1);
|
|
18882
19059
|
return d.toISOString().slice(0, 10);
|
|
18883
19060
|
}
|
|
18884
19061
|
async function fetchLatestKbDriftReport(execFileP5, repoRoot) {
|
|
18885
|
-
const sagaIo = (0,
|
|
18886
|
-
if (!(0,
|
|
19062
|
+
const sagaIo = (0, import_node_path22.join)(repoRoot, "infra", "saga-io.mjs");
|
|
19063
|
+
if (!(0, import_node_fs24.existsSync)(sagaIo)) return null;
|
|
18887
19064
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
18888
19065
|
for (const date of [today, yesterdayIso()]) {
|
|
18889
19066
|
try {
|
|
@@ -18899,9 +19076,9 @@ async function fetchLatestKbDriftReport(execFileP5, repoRoot) {
|
|
|
18899
19076
|
}
|
|
18900
19077
|
|
|
18901
19078
|
// src/cli-doctor-shared.ts
|
|
18902
|
-
var import_node_fs24 = require("node:fs");
|
|
18903
|
-
var import_node_path22 = require("node:path");
|
|
18904
19079
|
var import_node_fs25 = require("node:fs");
|
|
19080
|
+
var import_node_path23 = require("node:path");
|
|
19081
|
+
var import_node_fs26 = require("node:fs");
|
|
18905
19082
|
var GC_GH_TIMEOUT_MS = 2e4;
|
|
18906
19083
|
async function awsCallerArn() {
|
|
18907
19084
|
try {
|
|
@@ -18947,7 +19124,7 @@ async function localBranchHeads() {
|
|
|
18947
19124
|
}
|
|
18948
19125
|
async function currentRepoWorktreeGitRoot(repoRoot) {
|
|
18949
19126
|
const gitCommonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
18950
|
-
return gitCommonDir ? (0,
|
|
19127
|
+
return gitCommonDir ? (0, import_node_path23.resolve)(repoRoot, gitCommonDir, "worktrees") : "";
|
|
18951
19128
|
}
|
|
18952
19129
|
async function worktreeBranches() {
|
|
18953
19130
|
const { stdout } = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS });
|
|
@@ -18967,18 +19144,18 @@ function resolveGitdirForWorktreeFile(worktreePath, content) {
|
|
|
18967
19144
|
const match = /^gitdir:\s*(.+)\s*$/im.exec(content);
|
|
18968
19145
|
if (!match?.[1]) return void 0;
|
|
18969
19146
|
const raw = match[1].trim();
|
|
18970
|
-
return (0,
|
|
19147
|
+
return (0, import_node_path23.isAbsolute)(raw) ? raw : (0, import_node_path23.resolve)(worktreePath, raw);
|
|
18971
19148
|
}
|
|
18972
19149
|
function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
18973
19150
|
if (!worktreeGitRoot) return false;
|
|
18974
19151
|
try {
|
|
18975
|
-
const entries = (0,
|
|
19152
|
+
const entries = (0, import_node_fs26.readdirSync)(worktreeGitRoot, { withFileTypes: true });
|
|
18976
19153
|
for (const ent of entries) {
|
|
18977
19154
|
if (!ent.isDirectory()) continue;
|
|
18978
19155
|
try {
|
|
18979
|
-
const gitdirPath = (0,
|
|
18980
|
-
const resolvedGitdir = (0,
|
|
18981
|
-
if (sameWorktreeMetadataPath((0,
|
|
19156
|
+
const gitdirPath = (0, import_node_fs25.readFileSync)((0, import_node_path23.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
|
|
19157
|
+
const resolvedGitdir = (0, import_node_path23.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path23.resolve)(worktreeGitRoot, ent.name, gitdirPath);
|
|
19158
|
+
if (sameWorktreeMetadataPath((0, import_node_path23.dirname)(resolvedGitdir), worktreePath)) return true;
|
|
18982
19159
|
} catch {
|
|
18983
19160
|
}
|
|
18984
19161
|
}
|
|
@@ -18988,7 +19165,7 @@ function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
|
18988
19165
|
}
|
|
18989
19166
|
function pathExistsKnown(path2) {
|
|
18990
19167
|
try {
|
|
18991
|
-
(0,
|
|
19168
|
+
(0, import_node_fs26.statSync)(path2);
|
|
18992
19169
|
return true;
|
|
18993
19170
|
} catch (e) {
|
|
18994
19171
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
@@ -18997,10 +19174,10 @@ function pathExistsKnown(path2) {
|
|
|
18997
19174
|
}
|
|
18998
19175
|
}
|
|
18999
19176
|
function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
19000
|
-
const gitPath = (0,
|
|
19177
|
+
const gitPath = (0, import_node_path23.join)(path2, ".git");
|
|
19001
19178
|
let st;
|
|
19002
19179
|
try {
|
|
19003
|
-
st = (0,
|
|
19180
|
+
st = (0, import_node_fs26.lstatSync)(gitPath);
|
|
19004
19181
|
} catch (e) {
|
|
19005
19182
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
19006
19183
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
@@ -19017,7 +19194,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
|
19017
19194
|
if (st.isDirectory()) return { path: path2, gitType: "dir" };
|
|
19018
19195
|
if (!st.isFile()) return { path: path2, gitType: "other" };
|
|
19019
19196
|
try {
|
|
19020
|
-
const gitFileContent = (0,
|
|
19197
|
+
const gitFileContent = (0, import_node_fs25.readFileSync)(gitPath, "utf8");
|
|
19021
19198
|
const gitdir = resolveGitdirForWorktreeFile(path2, gitFileContent);
|
|
19022
19199
|
const gitDirExists = gitdir ? pathExistsKnown(gitdir) : false;
|
|
19023
19200
|
return {
|
|
@@ -19034,7 +19211,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
|
19034
19211
|
}
|
|
19035
19212
|
function inspectDeadWorktreeDirContent(path2) {
|
|
19036
19213
|
try {
|
|
19037
|
-
return { entries: (0,
|
|
19214
|
+
return { entries: (0, import_node_fs26.readdirSync)(path2) };
|
|
19038
19215
|
} catch (e) {
|
|
19039
19216
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
19040
19217
|
return { error: code ? `unable to inspect directory contents (${code})` : "unable to inspect directory contents" };
|
|
@@ -19053,8 +19230,8 @@ async function siblingWorktreeDirs() {
|
|
|
19053
19230
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot);
|
|
19054
19231
|
const siblingRoot = siblingMmiWorktreesRoot(repoRoot);
|
|
19055
19232
|
try {
|
|
19056
|
-
const entries = (0,
|
|
19057
|
-
return entries.filter((ent) => ent.isDirectory()).map((ent) => inspectSiblingWorktreeDir((0,
|
|
19233
|
+
const entries = (0, import_node_fs26.readdirSync)(siblingRoot, { withFileTypes: true });
|
|
19234
|
+
return entries.filter((ent) => ent.isDirectory()).map((ent) => inspectSiblingWorktreeDir((0, import_node_path23.join)(siblingRoot, ent.name), worktreeGitRoot)).filter((entry) => Boolean(entry));
|
|
19058
19235
|
} catch {
|
|
19059
19236
|
return [];
|
|
19060
19237
|
}
|
|
@@ -19104,7 +19281,7 @@ async function fetchHubVersionInfo(baseUrl) {
|
|
|
19104
19281
|
}
|
|
19105
19282
|
function readRepoVersion() {
|
|
19106
19283
|
try {
|
|
19107
|
-
return JSON.parse((0,
|
|
19284
|
+
return JSON.parse((0, import_node_fs27.readFileSync)((0, import_node_path24.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
19108
19285
|
} catch {
|
|
19109
19286
|
return void 0;
|
|
19110
19287
|
}
|
|
@@ -19240,11 +19417,11 @@ async function applyPluginHeal(token, surface, log, opts) {
|
|
|
19240
19417
|
}
|
|
19241
19418
|
var installedPluginsPath = (surface = detectSurface(process.env)) => {
|
|
19242
19419
|
const homeDir = surface === "codex" ? ".codex" : ".claude";
|
|
19243
|
-
return (0,
|
|
19420
|
+
return (0, import_node_path24.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "installed_plugins.json");
|
|
19244
19421
|
};
|
|
19245
19422
|
function readInstalledPlugins(surface = detectSurface(process.env)) {
|
|
19246
19423
|
try {
|
|
19247
|
-
return JSON.parse((0,
|
|
19424
|
+
return JSON.parse((0, import_node_fs27.readFileSync)(installedPluginsPath(surface), "utf8"));
|
|
19248
19425
|
} catch {
|
|
19249
19426
|
return null;
|
|
19250
19427
|
}
|
|
@@ -19255,15 +19432,15 @@ function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRep
|
|
|
19255
19432
|
return {
|
|
19256
19433
|
isOrgRepo,
|
|
19257
19434
|
installRecordPresent: hasUserInstallRecord(installed, MMI_PLUGIN_ID) || hasProjectInstallRecord(installed, MMI_PLUGIN_ID, process.cwd()),
|
|
19258
|
-
marketplaceClonePresent: (0,
|
|
19259
|
-
pluginCachePresent: (0,
|
|
19435
|
+
marketplaceClonePresent: (0, import_node_fs27.existsSync)((0, import_node_path24.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "marketplaces", "mutmutco")),
|
|
19436
|
+
pluginCachePresent: (0, import_node_fs27.existsSync)((0, import_node_path24.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "cache", "mutmutco", "mmi"))
|
|
19260
19437
|
};
|
|
19261
19438
|
}
|
|
19262
19439
|
function installedPluginSources() {
|
|
19263
19440
|
return ["claude", "codex"].map((surface) => {
|
|
19264
|
-
const recordPath = (0,
|
|
19441
|
+
const recordPath = (0, import_node_path24.join)((0, import_node_os5.homedir)(), `.${surface}`, "plugins", "installed_plugins.json");
|
|
19265
19442
|
try {
|
|
19266
|
-
return { surface, installed: JSON.parse((0,
|
|
19443
|
+
return { surface, installed: JSON.parse((0, import_node_fs27.readFileSync)(recordPath, "utf8")), recordPath };
|
|
19267
19444
|
} catch {
|
|
19268
19445
|
return { surface, installed: null, recordPath };
|
|
19269
19446
|
}
|
|
@@ -19271,7 +19448,7 @@ function installedPluginSources() {
|
|
|
19271
19448
|
}
|
|
19272
19449
|
function readClaudeSettings() {
|
|
19273
19450
|
try {
|
|
19274
|
-
return JSON.parse((0,
|
|
19451
|
+
return JSON.parse((0, import_node_fs27.readFileSync)((0, import_node_path24.join)(process.cwd(), ".claude", "settings.json"), "utf8"));
|
|
19275
19452
|
} catch {
|
|
19276
19453
|
return null;
|
|
19277
19454
|
}
|
|
@@ -19293,7 +19470,7 @@ function writeProjectInstallRecord(record) {
|
|
|
19293
19470
|
const list = file.plugins[MMI_PLUGIN_ID] ?? [];
|
|
19294
19471
|
list.push(record);
|
|
19295
19472
|
file.plugins[MMI_PLUGIN_ID] = list;
|
|
19296
|
-
(0,
|
|
19473
|
+
(0, import_node_fs27.writeFileSync)(installedPluginsPath(), `${JSON.stringify(file, null, 2)}
|
|
19297
19474
|
`, "utf8");
|
|
19298
19475
|
return true;
|
|
19299
19476
|
} catch {
|
|
@@ -19306,9 +19483,9 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
|
|
|
19306
19483
|
if (!file) return false;
|
|
19307
19484
|
if (!file.plugins) file.plugins = {};
|
|
19308
19485
|
const path2 = installedPluginsPath();
|
|
19309
|
-
(0,
|
|
19486
|
+
(0, import_node_fs27.copyFileSync)(path2, `${path2}.bak`);
|
|
19310
19487
|
file.plugins[pluginId] = records;
|
|
19311
|
-
(0,
|
|
19488
|
+
(0, import_node_fs27.writeFileSync)(path2, `${JSON.stringify(file, null, 2)}
|
|
19312
19489
|
`, "utf8");
|
|
19313
19490
|
return true;
|
|
19314
19491
|
} catch {
|
|
@@ -19316,22 +19493,22 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
|
|
|
19316
19493
|
}
|
|
19317
19494
|
}
|
|
19318
19495
|
function opencodeConfigDir() {
|
|
19319
|
-
return (0,
|
|
19496
|
+
return (0, import_node_path24.join)((0, import_node_os5.homedir)(), ".config", "opencode");
|
|
19320
19497
|
}
|
|
19321
19498
|
function opencodeConfigPath() {
|
|
19322
|
-
return (0,
|
|
19499
|
+
return (0, import_node_path24.join)(opencodeConfigDir(), "opencode.jsonc");
|
|
19323
19500
|
}
|
|
19324
19501
|
function opencodeCommandsDir() {
|
|
19325
|
-
return (0,
|
|
19502
|
+
return (0, import_node_path24.join)(opencodeConfigDir(), "commands");
|
|
19326
19503
|
}
|
|
19327
19504
|
function opencodeSkillsPath() {
|
|
19328
|
-
return (0,
|
|
19505
|
+
return (0, import_node_path24.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "skills");
|
|
19329
19506
|
}
|
|
19330
19507
|
function opencodeConfigSnapshot() {
|
|
19331
19508
|
const path2 = opencodeConfigPath();
|
|
19332
|
-
if (!(0,
|
|
19509
|
+
if (!(0, import_node_fs27.existsSync)(path2)) return { path: path2, hasConfig: false, hasPluginField: false, parseOk: true };
|
|
19333
19510
|
try {
|
|
19334
|
-
const raw = (0,
|
|
19511
|
+
const raw = (0, import_node_fs27.readFileSync)(path2, "utf8");
|
|
19335
19512
|
const parsed = JSON.parse(stripJsonc(raw));
|
|
19336
19513
|
const hasPluginField = Object.prototype.hasOwnProperty.call(parsed, "plugin");
|
|
19337
19514
|
const skillsPaths = Array.isArray(parsed.skills?.paths) ? parsed.skills.paths.filter((p) => typeof p === "string") : void 0;
|
|
@@ -19354,9 +19531,9 @@ function writeOpencodeConfigPlugin(snapshot) {
|
|
|
19354
19531
|
const plan2 = planOpencodeConfigWrite(snapshot.hasConfig ? snapshot.raw : void 0);
|
|
19355
19532
|
if (plan2.action === "already") return true;
|
|
19356
19533
|
if (!plan2.text || plan2.action === "unsafe") return false;
|
|
19357
|
-
(0,
|
|
19358
|
-
if (snapshot.hasConfig) (0,
|
|
19359
|
-
(0,
|
|
19534
|
+
(0, import_node_fs27.mkdirSync)((0, import_node_path24.dirname)(path2), { recursive: true });
|
|
19535
|
+
if (snapshot.hasConfig) (0, import_node_fs27.copyFileSync)(path2, `${path2}.bak`);
|
|
19536
|
+
(0, import_node_fs27.writeFileSync)(path2, plan2.text, "utf8");
|
|
19360
19537
|
return true;
|
|
19361
19538
|
} catch {
|
|
19362
19539
|
return false;
|
|
@@ -19371,9 +19548,9 @@ function writeOpencodeSkillsPath(snapshot, skillsPath) {
|
|
|
19371
19548
|
const normalized = skillsPath.replace(/\\/g, "/");
|
|
19372
19549
|
if (!paths.some((p) => p.replace(/\\/g, "/") === normalized)) paths.push(skillsPath.replace(/\\/g, "/"));
|
|
19373
19550
|
parsed.skills = { ...skills, paths };
|
|
19374
|
-
(0,
|
|
19375
|
-
if (snapshot.hasConfig && (0,
|
|
19376
|
-
(0,
|
|
19551
|
+
(0, import_node_fs27.mkdirSync)((0, import_node_path24.dirname)(snapshot.path), { recursive: true });
|
|
19552
|
+
if (snapshot.hasConfig && (0, import_node_fs27.existsSync)(snapshot.path)) (0, import_node_fs27.copyFileSync)(snapshot.path, `${snapshot.path}.bak`);
|
|
19553
|
+
(0, import_node_fs27.writeFileSync)(snapshot.path, `${JSON.stringify(parsed, null, 2)}
|
|
19377
19554
|
`, "utf8");
|
|
19378
19555
|
return true;
|
|
19379
19556
|
} catch {
|
|
@@ -19382,7 +19559,7 @@ function writeOpencodeSkillsPath(snapshot, skillsPath) {
|
|
|
19382
19559
|
}
|
|
19383
19560
|
function opencodeExistingCommands() {
|
|
19384
19561
|
try {
|
|
19385
|
-
return (0,
|
|
19562
|
+
return (0, import_node_fs27.readdirSync)(opencodeCommandsDir(), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name.slice(0, -3).toLowerCase());
|
|
19386
19563
|
} catch {
|
|
19387
19564
|
return [];
|
|
19388
19565
|
}
|
|
@@ -19390,9 +19567,9 @@ function opencodeExistingCommands() {
|
|
|
19390
19567
|
function writeOpencodeCommandFiles() {
|
|
19391
19568
|
try {
|
|
19392
19569
|
const dir = opencodeCommandsDir();
|
|
19393
|
-
(0,
|
|
19570
|
+
(0, import_node_fs27.mkdirSync)(dir, { recursive: true });
|
|
19394
19571
|
for (const command of OPENCODE_WORKFLOW_COMMANDS) {
|
|
19395
|
-
(0,
|
|
19572
|
+
(0, import_node_fs27.writeFileSync)((0, import_node_path24.join)(dir, `${command}.md`), opencodeCommandMarkdown(command), "utf8");
|
|
19396
19573
|
}
|
|
19397
19574
|
return true;
|
|
19398
19575
|
} catch {
|
|
@@ -19401,12 +19578,12 @@ function writeOpencodeCommandFiles() {
|
|
|
19401
19578
|
}
|
|
19402
19579
|
function readOpencodeAdapterDiskVersion() {
|
|
19403
19580
|
const candidates = [
|
|
19404
|
-
(0,
|
|
19405
|
-
(0,
|
|
19581
|
+
(0, import_node_path24.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "package.json"),
|
|
19582
|
+
(0, import_node_path24.join)((0, import_node_os5.homedir)(), ".cache", "opencode", "node_modules", "@mutmutco", "opencode-mmi", "package.json")
|
|
19406
19583
|
];
|
|
19407
19584
|
for (const path2 of candidates) {
|
|
19408
19585
|
try {
|
|
19409
|
-
const parsed = JSON.parse((0,
|
|
19586
|
+
const parsed = JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8"));
|
|
19410
19587
|
if (typeof parsed.version === "string" && parsed.version.trim()) return parsed.version.trim();
|
|
19411
19588
|
} catch {
|
|
19412
19589
|
continue;
|
|
@@ -19422,7 +19599,7 @@ async function forceInstallOpencodeMmiPlugins(snapshot, log) {
|
|
|
19422
19599
|
try {
|
|
19423
19600
|
const specs = opencodeMmiPluginSpecs(snapshot);
|
|
19424
19601
|
log(` \u21BB force-refreshing OpenCode MMI npm plugin(s): ${specs.join(", ")}\u2026`);
|
|
19425
|
-
(0,
|
|
19602
|
+
(0, import_node_fs27.mkdirSync)(opencodeConfigDir(), { recursive: true });
|
|
19426
19603
|
await runHostBin("npm", ["install", "--prefix", opencodeConfigDir(), "--force", ...specs], { timeout: NPM_UPDATE_TIMEOUT_MS });
|
|
19427
19604
|
return true;
|
|
19428
19605
|
} catch {
|
|
@@ -19437,30 +19614,30 @@ function opencodePluginVersionsForReport() {
|
|
|
19437
19614
|
}
|
|
19438
19615
|
function opencodeDesktopLogsRoot() {
|
|
19439
19616
|
if (process.platform === "win32") {
|
|
19440
|
-
const base = process.env.APPDATA || (0,
|
|
19441
|
-
return (0,
|
|
19617
|
+
const base = process.env.APPDATA || (0, import_node_path24.join)((0, import_node_os5.homedir)(), "AppData", "Roaming");
|
|
19618
|
+
return (0, import_node_path24.join)(base, "ai.opencode.desktop", "logs");
|
|
19442
19619
|
}
|
|
19443
19620
|
if (process.platform === "darwin") {
|
|
19444
|
-
return (0,
|
|
19621
|
+
return (0, import_node_path24.join)((0, import_node_os5.homedir)(), "Library", "Application Support", "ai.opencode.desktop", "logs");
|
|
19445
19622
|
}
|
|
19446
|
-
return (0,
|
|
19623
|
+
return (0, import_node_path24.join)((0, import_node_os5.homedir)(), ".config", "ai.opencode.desktop", "logs");
|
|
19447
19624
|
}
|
|
19448
19625
|
function opencodeDesktopBootstrapSnapshot() {
|
|
19449
19626
|
const root = opencodeDesktopLogsRoot();
|
|
19450
19627
|
try {
|
|
19451
|
-
const sessionDirs = (0,
|
|
19628
|
+
const sessionDirs = (0, import_node_fs27.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => (0, import_node_path24.join)(root, entry.name)).sort((a, b) => (0, import_node_fs27.statSync)(b).mtimeMs - (0, import_node_fs27.statSync)(a).mtimeMs);
|
|
19452
19629
|
const newest = sessionDirs[0];
|
|
19453
19630
|
if (!newest) return [];
|
|
19454
|
-
const logPath = (0,
|
|
19455
|
-
const text = (0,
|
|
19456
|
-
return opencodeAgentDirectoriesFromLog(text).filter((directory) => !(0,
|
|
19631
|
+
const logPath = (0, import_node_path24.join)(newest, "renderer.log");
|
|
19632
|
+
const text = (0, import_node_fs27.readFileSync)(logPath, "utf8");
|
|
19633
|
+
return opencodeAgentDirectoriesFromLog(text).filter((directory) => !(0, import_node_fs27.existsSync)(directory)).map((directory) => ({ directory, logPath }));
|
|
19457
19634
|
} catch {
|
|
19458
19635
|
return [];
|
|
19459
19636
|
}
|
|
19460
19637
|
}
|
|
19461
19638
|
function opencodeLegacyConfigSnapshot() {
|
|
19462
|
-
const legacyPath = (0,
|
|
19463
|
-
if (!(0,
|
|
19639
|
+
const legacyPath = (0, import_node_path24.join)((0, import_node_os5.homedir)(), ".opencode", "opencode.json");
|
|
19640
|
+
if (!(0, import_node_fs27.existsSync)(legacyPath)) return {};
|
|
19464
19641
|
const content = readTextFile(legacyPath);
|
|
19465
19642
|
if (content == null) return {};
|
|
19466
19643
|
const plugins = parseOpencodeLegacyConfigPlugins(content);
|
|
@@ -19472,43 +19649,43 @@ function opencodeLegacyConfigSnapshot() {
|
|
|
19472
19649
|
function quarantineOpencodeLegacyConfig(legacyPath) {
|
|
19473
19650
|
try {
|
|
19474
19651
|
const backupPath = `${legacyPath}.bak`;
|
|
19475
|
-
if ((0,
|
|
19476
|
-
(0,
|
|
19652
|
+
if ((0, import_node_fs27.existsSync)(backupPath)) return false;
|
|
19653
|
+
(0, import_node_fs27.renameSync)(legacyPath, backupPath);
|
|
19477
19654
|
return true;
|
|
19478
19655
|
} catch {
|
|
19479
19656
|
return false;
|
|
19480
19657
|
}
|
|
19481
19658
|
}
|
|
19482
19659
|
function cursorPluginCacheRoot() {
|
|
19483
|
-
return (0,
|
|
19660
|
+
return (0, import_node_path24.join)((0, import_node_os5.homedir)(), ".cursor", "plugins", "cache", "mutmutco", "mmi");
|
|
19484
19661
|
}
|
|
19485
19662
|
function cursorPluginCachePinSnapshots() {
|
|
19486
19663
|
const root = cursorPluginCacheRoot();
|
|
19487
19664
|
try {
|
|
19488
|
-
return (0,
|
|
19489
|
-
const path2 = (0,
|
|
19490
|
-
const pluginJson = (0,
|
|
19491
|
-
const hooksJson = (0,
|
|
19492
|
-
const cliBundle = (0,
|
|
19665
|
+
return (0, import_node_fs27.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => {
|
|
19666
|
+
const path2 = (0, import_node_path24.join)(root, entry.name);
|
|
19667
|
+
const pluginJson = (0, import_node_path24.join)(path2, ".cursor-plugin", "plugin.json");
|
|
19668
|
+
const hooksJson = (0, import_node_path24.join)(path2, "hooks", "hooks.json");
|
|
19669
|
+
const cliBundle = (0, import_node_path24.join)(path2, "cli", "dist", "index.cjs");
|
|
19493
19670
|
let version;
|
|
19494
19671
|
try {
|
|
19495
|
-
const raw = JSON.parse((0,
|
|
19672
|
+
const raw = JSON.parse((0, import_node_fs27.readFileSync)(pluginJson, "utf8"));
|
|
19496
19673
|
version = typeof raw.version === "string" ? raw.version : void 0;
|
|
19497
19674
|
} catch {
|
|
19498
19675
|
version = void 0;
|
|
19499
19676
|
}
|
|
19500
19677
|
let isEmpty = true;
|
|
19501
19678
|
try {
|
|
19502
|
-
isEmpty = (0,
|
|
19679
|
+
isEmpty = (0, import_node_fs27.readdirSync)(path2).length === 0;
|
|
19503
19680
|
} catch {
|
|
19504
19681
|
isEmpty = true;
|
|
19505
19682
|
}
|
|
19506
19683
|
return {
|
|
19507
19684
|
name: entry.name,
|
|
19508
19685
|
path: path2,
|
|
19509
|
-
hasPluginJson: (0,
|
|
19510
|
-
hasHooksJson: (0,
|
|
19511
|
-
hasCliBundle: (0,
|
|
19686
|
+
hasPluginJson: (0, import_node_fs27.existsSync)(pluginJson),
|
|
19687
|
+
hasHooksJson: (0, import_node_fs27.existsSync)(hooksJson),
|
|
19688
|
+
hasCliBundle: (0, import_node_fs27.existsSync)(cliBundle),
|
|
19512
19689
|
isEmpty,
|
|
19513
19690
|
version
|
|
19514
19691
|
};
|
|
@@ -19518,19 +19695,19 @@ function cursorPluginCachePinSnapshots() {
|
|
|
19518
19695
|
}
|
|
19519
19696
|
}
|
|
19520
19697
|
function hubCheckoutForCursorSeed() {
|
|
19521
|
-
const manifest = (0,
|
|
19522
|
-
return (0,
|
|
19698
|
+
const manifest = (0, import_node_path24.join)(process.cwd(), "plugins", "mmi", ".cursor-plugin", "plugin.json");
|
|
19699
|
+
return (0, import_node_fs27.existsSync)(manifest) ? process.cwd() : void 0;
|
|
19523
19700
|
}
|
|
19524
19701
|
function mmiPluginCacheRootSnapshots() {
|
|
19525
19702
|
const roots = [
|
|
19526
|
-
{ surface: "claude", root: (0,
|
|
19527
|
-
{ surface: "codex", root: (0,
|
|
19703
|
+
{ surface: "claude", root: (0, import_node_path24.join)((0, import_node_os5.homedir)(), ".claude", "plugins", "cache", "mutmutco", "mmi") },
|
|
19704
|
+
{ surface: "codex", root: (0, import_node_path24.join)((0, import_node_os5.homedir)(), ".codex", "plugins", "cache", "mutmutco", "mmi") }
|
|
19528
19705
|
];
|
|
19529
19706
|
return roots.flatMap(({ surface, root }) => {
|
|
19530
19707
|
try {
|
|
19531
|
-
const entries = (0,
|
|
19708
|
+
const entries = (0, import_node_fs27.readdirSync)(root, { withFileTypes: true }).map((entry) => ({
|
|
19532
19709
|
name: entry.name,
|
|
19533
|
-
path: (0,
|
|
19710
|
+
path: (0, import_node_path24.join)(root, entry.name),
|
|
19534
19711
|
isDirectory: entry.isDirectory()
|
|
19535
19712
|
}));
|
|
19536
19713
|
return [{ surface, root, entries }];
|
|
@@ -19541,7 +19718,7 @@ function mmiPluginCacheRootSnapshots() {
|
|
|
19541
19718
|
}
|
|
19542
19719
|
function hasNestedMmiChild(versionDir) {
|
|
19543
19720
|
try {
|
|
19544
|
-
return (0,
|
|
19721
|
+
return (0, import_node_fs27.statSync)((0, import_node_path24.join)(versionDir, "mmi")).isDirectory();
|
|
19545
19722
|
} catch {
|
|
19546
19723
|
return false;
|
|
19547
19724
|
}
|
|
@@ -19552,10 +19729,10 @@ function nestedPluginTreeSnapshot() {
|
|
|
19552
19729
|
);
|
|
19553
19730
|
}
|
|
19554
19731
|
function uniqueQuarantineTarget(path2) {
|
|
19555
|
-
if (!(0,
|
|
19732
|
+
if (!(0, import_node_fs27.existsSync)(path2)) return path2;
|
|
19556
19733
|
for (let i = 1; i < 100; i += 1) {
|
|
19557
19734
|
const candidate = `${path2}-${i}`;
|
|
19558
|
-
if (!(0,
|
|
19735
|
+
if (!(0, import_node_fs27.existsSync)(candidate)) return candidate;
|
|
19559
19736
|
}
|
|
19560
19737
|
return `${path2}-${Date.now()}`;
|
|
19561
19738
|
}
|
|
@@ -19564,10 +19741,10 @@ function quarantinePluginCacheDirs(plan2) {
|
|
|
19564
19741
|
const failed = [];
|
|
19565
19742
|
for (const move of plan2) {
|
|
19566
19743
|
try {
|
|
19567
|
-
if (!(0,
|
|
19744
|
+
if (!(0, import_node_fs27.existsSync)(move.from)) continue;
|
|
19568
19745
|
const target = uniqueQuarantineTarget(move.to);
|
|
19569
|
-
(0,
|
|
19570
|
-
(0,
|
|
19746
|
+
(0, import_node_fs27.mkdirSync)((0, import_node_path24.dirname)(target), { recursive: true });
|
|
19747
|
+
(0, import_node_fs27.renameSync)(move.from, target);
|
|
19571
19748
|
moved += 1;
|
|
19572
19749
|
} catch {
|
|
19573
19750
|
failed.push(move);
|
|
@@ -19586,23 +19763,23 @@ async function robocopyMirrorEmpty(emptyDir, target) {
|
|
|
19586
19763
|
}
|
|
19587
19764
|
async function clearNestedPluginTreeDir(targetPath) {
|
|
19588
19765
|
try {
|
|
19589
|
-
if (!(0,
|
|
19766
|
+
if (!(0, import_node_fs27.existsSync)(targetPath)) return true;
|
|
19590
19767
|
if (isWin) {
|
|
19591
|
-
const emptyDir = (0,
|
|
19592
|
-
(0,
|
|
19768
|
+
const emptyDir = (0, import_node_path24.join)((0, import_node_os5.tmpdir)(), `mmi-empty-${Date.now()}`);
|
|
19769
|
+
(0, import_node_fs27.mkdirSync)(emptyDir, { recursive: true });
|
|
19593
19770
|
try {
|
|
19594
19771
|
await robocopyMirrorEmpty(emptyDir, targetPath);
|
|
19595
|
-
(0,
|
|
19772
|
+
(0, import_node_fs27.rmSync)(targetPath, { recursive: true, force: true });
|
|
19596
19773
|
} finally {
|
|
19597
19774
|
try {
|
|
19598
|
-
(0,
|
|
19775
|
+
(0, import_node_fs27.rmSync)(emptyDir, { recursive: true, force: true });
|
|
19599
19776
|
} catch {
|
|
19600
19777
|
}
|
|
19601
19778
|
}
|
|
19602
|
-
return !(0,
|
|
19779
|
+
return !(0, import_node_fs27.existsSync)(targetPath);
|
|
19603
19780
|
}
|
|
19604
|
-
(0,
|
|
19605
|
-
return !(0,
|
|
19781
|
+
(0, import_node_fs27.rmSync)(targetPath, { recursive: true, force: true });
|
|
19782
|
+
return !(0, import_node_fs27.existsSync)(targetPath);
|
|
19606
19783
|
} catch {
|
|
19607
19784
|
return false;
|
|
19608
19785
|
}
|
|
@@ -19615,11 +19792,11 @@ async function applyNestedPluginTreeCleanup(paths, log) {
|
|
|
19615
19792
|
}
|
|
19616
19793
|
return true;
|
|
19617
19794
|
}
|
|
19618
|
-
var gitignorePath = () => (0,
|
|
19795
|
+
var gitignorePath = () => (0, import_node_path24.join)(process.cwd(), ".gitignore");
|
|
19619
19796
|
function readTextFile(path2) {
|
|
19620
19797
|
try {
|
|
19621
|
-
if (!(0,
|
|
19622
|
-
return (0,
|
|
19798
|
+
if (!(0, import_node_fs27.existsSync)(path2)) return null;
|
|
19799
|
+
return (0, import_node_fs27.readFileSync)(path2, "utf8");
|
|
19623
19800
|
} catch {
|
|
19624
19801
|
return null;
|
|
19625
19802
|
}
|
|
@@ -19628,10 +19805,10 @@ function playwrightMcpConfigSnapshots() {
|
|
|
19628
19805
|
const cwd = process.cwd();
|
|
19629
19806
|
const home = (0, import_node_os5.homedir)();
|
|
19630
19807
|
const candidates = [
|
|
19631
|
-
(0,
|
|
19632
|
-
(0,
|
|
19633
|
-
(0,
|
|
19634
|
-
(0,
|
|
19808
|
+
(0, import_node_path24.join)(cwd, ".mcp.json"),
|
|
19809
|
+
(0, import_node_path24.join)(cwd, ".cursor", "mcp.json"),
|
|
19810
|
+
(0, import_node_path24.join)(home, ".cursor", "mcp.json"),
|
|
19811
|
+
(0, import_node_path24.join)(home, ".codex", "config.toml")
|
|
19635
19812
|
];
|
|
19636
19813
|
const out = [];
|
|
19637
19814
|
for (const path2 of candidates) {
|
|
@@ -19644,7 +19821,7 @@ function strayBrowserArtifactPaths() {
|
|
|
19644
19821
|
const cwd = process.cwd();
|
|
19645
19822
|
return STRAY_BROWSER_ARTIFACT_DIRS.filter((rel) => {
|
|
19646
19823
|
try {
|
|
19647
|
-
return (0,
|
|
19824
|
+
return (0, import_node_fs27.existsSync)((0, import_node_path24.join)(cwd, rel));
|
|
19648
19825
|
} catch {
|
|
19649
19826
|
return false;
|
|
19650
19827
|
}
|
|
@@ -19663,8 +19840,8 @@ function latestIso(values) {
|
|
|
19663
19840
|
return best;
|
|
19664
19841
|
}
|
|
19665
19842
|
function latestNorthstarContinuityAt() {
|
|
19666
|
-
const meta = parseMeta(readTextFile((0,
|
|
19667
|
-
const queue = parseQueue(readTextFile((0,
|
|
19843
|
+
const meta = parseMeta(readTextFile((0, import_node_path24.join)(process.cwd(), META_FILE)));
|
|
19844
|
+
const queue = parseQueue(readTextFile((0, import_node_path24.join)(process.cwd(), QUEUE_FILE)));
|
|
19668
19845
|
return latestIso([
|
|
19669
19846
|
...Object.values(meta).map((entry) => entry.syncedAt),
|
|
19670
19847
|
...queue.map((entry) => entry.queuedAt)
|
|
@@ -19678,14 +19855,14 @@ async function latestBranchWorkAt() {
|
|
|
19678
19855
|
}
|
|
19679
19856
|
function readGitignore() {
|
|
19680
19857
|
try {
|
|
19681
|
-
return (0,
|
|
19858
|
+
return (0, import_node_fs27.readFileSync)(gitignorePath(), "utf8");
|
|
19682
19859
|
} catch {
|
|
19683
19860
|
return null;
|
|
19684
19861
|
}
|
|
19685
19862
|
}
|
|
19686
19863
|
function writeGitignore(content) {
|
|
19687
19864
|
try {
|
|
19688
|
-
(0,
|
|
19865
|
+
(0, import_node_fs27.writeFileSync)(gitignorePath(), content, "utf8");
|
|
19689
19866
|
return true;
|
|
19690
19867
|
} catch {
|
|
19691
19868
|
return false;
|
|
@@ -19724,7 +19901,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19724
19901
|
const semverPrefix = /^\d+\.\d+\.\d+/;
|
|
19725
19902
|
const isBehind = (installed2, released) => Boolean(installed2 && released && semverPrefix.test(installed2) && semverPrefix.test(released) && compareVersions(installed2, released) < 0);
|
|
19726
19903
|
const opencodeAdapterStale = isBehind(opencodeInstalledVersionForDoctor(), releasedVersion);
|
|
19727
|
-
const cursorCacheStale = (0,
|
|
19904
|
+
const cursorCacheStale = (0, import_node_fs27.existsSync)(cursorPluginCacheRoot()) && (cursorPluginCachePinSnapshots() ?? []).some((p) => isBehind(p.version, releasedVersion));
|
|
19728
19905
|
const healPlan = doctorHealPlan({
|
|
19729
19906
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19730
19907
|
surface,
|
|
@@ -19759,7 +19936,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19759
19936
|
let onPath = pathProbe;
|
|
19760
19937
|
if (!onPath) {
|
|
19761
19938
|
const root = process.env.CLAUDE_PLUGIN_ROOT;
|
|
19762
|
-
if (root && (0,
|
|
19939
|
+
if (root && (0, import_node_fs27.existsSync)(`${root}/bin/mmi-cli${isWin ? ".cmd" : ""}`)) onPath = true;
|
|
19763
19940
|
}
|
|
19764
19941
|
checks.push({ ok: onPath, label: "mmi-cli on PATH", fix: "auto-provisioned at session start \u2014 reopen the session, or install the MMI plugin" });
|
|
19765
19942
|
const reloadHint = reloadAction(surface);
|
|
@@ -20097,7 +20274,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
20097
20274
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
20098
20275
|
surface,
|
|
20099
20276
|
cacheRoot: cursorCacheRoot,
|
|
20100
|
-
cacheRootExists: (0,
|
|
20277
|
+
cacheRootExists: (0, import_node_fs27.existsSync)(cursorCacheRoot),
|
|
20101
20278
|
pins: cursorPins,
|
|
20102
20279
|
hubCheckout: hubCheckoutForCursorSeed(),
|
|
20103
20280
|
releasedVersion
|
|
@@ -20108,7 +20285,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
20108
20285
|
releasedVersion,
|
|
20109
20286
|
hubCheckout: hubCheckoutForCursorSeed(),
|
|
20110
20287
|
execFileP: execFileP2,
|
|
20111
|
-
mkdtemp: (prefix) => (0, import_promises7.mkdtemp)((0,
|
|
20288
|
+
mkdtemp: (prefix) => (0, import_promises7.mkdtemp)((0, import_node_path24.join)((0, import_node_os5.tmpdir)(), prefix)),
|
|
20112
20289
|
log: (m) => io.err(m)
|
|
20113
20290
|
});
|
|
20114
20291
|
if (seeded) {
|
|
@@ -20117,7 +20294,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
20117
20294
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
20118
20295
|
surface,
|
|
20119
20296
|
cacheRoot: cursorCacheRoot,
|
|
20120
|
-
cacheRootExists: (0,
|
|
20297
|
+
cacheRootExists: (0, import_node_fs27.existsSync)(cursorCacheRoot),
|
|
20121
20298
|
pins: cursorPins,
|
|
20122
20299
|
hubCheckout: hubCheckoutForCursorSeed(),
|
|
20123
20300
|
releasedVersion
|
|
@@ -20306,23 +20483,23 @@ function mergeGuardHook(settings) {
|
|
|
20306
20483
|
next.hooks = hooks;
|
|
20307
20484
|
return next;
|
|
20308
20485
|
}
|
|
20309
|
-
var userScopeSettingsPath = (surface = detectSurface(process.env)) => (0,
|
|
20486
|
+
var userScopeSettingsPath = (surface = detectSurface(process.env)) => (0, import_node_path24.join)((0, import_node_os5.homedir)(), surface === "codex" ? ".codex" : ".claude", "settings.json");
|
|
20310
20487
|
function ensureUserScopeGuardHook(opts = {}) {
|
|
20311
20488
|
const path2 = opts.settingsPath ?? userScopeSettingsPath();
|
|
20312
20489
|
try {
|
|
20313
20490
|
let current = null;
|
|
20314
|
-
if ((0,
|
|
20491
|
+
if ((0, import_node_fs27.existsSync)(path2)) {
|
|
20315
20492
|
try {
|
|
20316
|
-
current = JSON.parse((0,
|
|
20493
|
+
current = JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8"));
|
|
20317
20494
|
} catch {
|
|
20318
20495
|
return "failed";
|
|
20319
20496
|
}
|
|
20320
20497
|
}
|
|
20321
20498
|
if (settingsHasGuardHook(current)) return "already";
|
|
20322
20499
|
const merged = mergeGuardHook(current);
|
|
20323
|
-
(0,
|
|
20324
|
-
if ((0,
|
|
20325
|
-
(0,
|
|
20500
|
+
(0, import_node_fs27.mkdirSync)((0, import_node_path24.dirname)(path2), { recursive: true });
|
|
20501
|
+
if ((0, import_node_fs27.existsSync)(path2)) (0, import_node_fs27.copyFileSync)(path2, `${path2}.bak`);
|
|
20502
|
+
(0, import_node_fs27.writeFileSync)(path2, `${JSON.stringify(merged, null, 2)}
|
|
20326
20503
|
`, "utf8");
|
|
20327
20504
|
return "written";
|
|
20328
20505
|
} catch {
|
|
@@ -20452,7 +20629,7 @@ async function applyGcPlan(plan2, remote) {
|
|
|
20452
20629
|
cleanupBranch: (branch, expectedHeadOid) => cleanupPrMergeLocalBranch(branch.branch, {
|
|
20453
20630
|
beforeWorktrees,
|
|
20454
20631
|
startingPath: branch.worktreePath,
|
|
20455
|
-
pathExists: (p) => (0,
|
|
20632
|
+
pathExists: (p) => (0, import_node_fs28.existsSync)(p),
|
|
20456
20633
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
20457
20634
|
teardownWorktreeStage,
|
|
20458
20635
|
deferredStore,
|
|
@@ -20475,7 +20652,7 @@ async function applyGcPlan(plan2, remote) {
|
|
|
20475
20652
|
for (const wt of plan2.worktreeDirs) {
|
|
20476
20653
|
try {
|
|
20477
20654
|
const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
|
|
20478
|
-
realpath: (path2) => (0,
|
|
20655
|
+
realpath: (path2) => (0, import_node_fs28.realpathSync)(path2)
|
|
20479
20656
|
});
|
|
20480
20657
|
if (!cleanupTarget.ok) {
|
|
20481
20658
|
result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
|
|
@@ -20555,10 +20732,10 @@ async function runRulesSync(opts, io = consoleIo) {
|
|
|
20555
20732
|
for (const entry of fetched) {
|
|
20556
20733
|
if ("error" in entry) continue;
|
|
20557
20734
|
const { file, source } = entry;
|
|
20558
|
-
const current = (0,
|
|
20735
|
+
const current = (0, import_node_fs28.existsSync)(file) ? await (0, import_promises8.readFile)(file, "utf8") : null;
|
|
20559
20736
|
if (needsUpdate(source, current)) {
|
|
20560
20737
|
const slash = file.lastIndexOf("/");
|
|
20561
|
-
if (slash > 0) (0,
|
|
20738
|
+
if (slash > 0) (0, import_node_fs28.mkdirSync)(file.slice(0, slash), { recursive: true });
|
|
20562
20739
|
await (0, import_promises8.writeFile)(file, normalizeEol(source), "utf8");
|
|
20563
20740
|
changed++;
|
|
20564
20741
|
if (!opts.quiet) io.log(`mmi-cli rules: updated ${file}`);
|
|
@@ -20572,13 +20749,13 @@ rules.command("sync").option("--quiet", "stay silent unless something changed or
|
|
|
20572
20749
|
if (!await runRulesSync(opts)) process.exitCode = 1;
|
|
20573
20750
|
});
|
|
20574
20751
|
rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
|
|
20575
|
-
const path2 = (0,
|
|
20576
|
-
const current = (0,
|
|
20752
|
+
const path2 = (0, import_node_path25.join)(process.cwd(), ".gitignore");
|
|
20753
|
+
const current = (0, import_node_fs28.existsSync)(path2) ? (0, import_node_fs28.readFileSync)(path2, "utf8") : null;
|
|
20577
20754
|
const plan2 = planManagedGitignore(current);
|
|
20578
20755
|
const drift = [...plan2.added.map((l) => `+${l}`), ...plan2.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
20579
20756
|
if (opts.write) {
|
|
20580
20757
|
if (plan2.changed) {
|
|
20581
|
-
(0,
|
|
20758
|
+
(0, import_node_fs28.writeFileSync)(path2, plan2.content, "utf8");
|
|
20582
20759
|
console.log(`mmi-cli rules gitignore: updated .gitignore (${drift})`);
|
|
20583
20760
|
} else {
|
|
20584
20761
|
console.log("mmi-cli rules gitignore: up to date");
|
|
@@ -20605,7 +20782,7 @@ async function runDocsSync(opts, io = consoleIo) {
|
|
|
20605
20782
|
return null;
|
|
20606
20783
|
}
|
|
20607
20784
|
},
|
|
20608
|
-
localContent: async (f) => (0,
|
|
20785
|
+
localContent: async (f) => (0, import_node_fs28.existsSync)(f) ? await (0, import_promises8.readFile)(f, "utf8") : null,
|
|
20609
20786
|
writeDoc: async (f, c) => {
|
|
20610
20787
|
await (0, import_promises8.writeFile)(f, c, "utf8");
|
|
20611
20788
|
}
|
|
@@ -20756,7 +20933,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
20756
20933
|
const [bin, ...args] = command.split(" ");
|
|
20757
20934
|
const file = isWin2 ? "cmd.exe" : bin;
|
|
20758
20935
|
const spawnArgs = isWin2 ? ["/c", bin, ...args] : args;
|
|
20759
|
-
return new Promise((
|
|
20936
|
+
return new Promise((resolve6, reject) => {
|
|
20760
20937
|
const child = (0, import_node_child_process13.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
|
|
20761
20938
|
const timer = setTimeout(() => {
|
|
20762
20939
|
try {
|
|
@@ -20771,7 +20948,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
20771
20948
|
});
|
|
20772
20949
|
child.on("exit", (code) => {
|
|
20773
20950
|
clearTimeout(timer);
|
|
20774
|
-
if (code === 0)
|
|
20951
|
+
if (code === 0) resolve6();
|
|
20775
20952
|
else reject(new Error(`${command} exited ${code} in ${cwd}`));
|
|
20776
20953
|
});
|
|
20777
20954
|
});
|
|
@@ -20779,7 +20956,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
20779
20956
|
async function primaryCheckoutRoot(worktreeRoot) {
|
|
20780
20957
|
try {
|
|
20781
20958
|
const out = (await execFileP2("git", ["-C", worktreeRoot, "rev-parse", "--path-format=absolute", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
|
|
20782
|
-
return out ? (0,
|
|
20959
|
+
return out ? (0, import_node_path25.dirname)(out) : void 0;
|
|
20783
20960
|
} catch {
|
|
20784
20961
|
return void 0;
|
|
20785
20962
|
}
|
|
@@ -20792,28 +20969,28 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
20792
20969
|
};
|
|
20793
20970
|
}
|
|
20794
20971
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
20795
|
-
const lockPath = (0,
|
|
20972
|
+
const lockPath = (0, import_node_path25.join)(worktreeRoot, ".mmi", "worktree-setup.lock");
|
|
20796
20973
|
const take = () => {
|
|
20797
|
-
const fd = (0,
|
|
20974
|
+
const fd = (0, import_node_fs28.openSync)(lockPath, "wx");
|
|
20798
20975
|
try {
|
|
20799
|
-
(0,
|
|
20976
|
+
(0, import_node_fs28.writeSync)(fd, String(Date.now()));
|
|
20800
20977
|
} finally {
|
|
20801
|
-
(0,
|
|
20978
|
+
(0, import_node_fs28.closeSync)(fd);
|
|
20802
20979
|
}
|
|
20803
20980
|
return () => {
|
|
20804
20981
|
try {
|
|
20805
|
-
(0,
|
|
20982
|
+
(0, import_node_fs28.rmSync)(lockPath, { force: true });
|
|
20806
20983
|
} catch {
|
|
20807
20984
|
}
|
|
20808
20985
|
};
|
|
20809
20986
|
};
|
|
20810
20987
|
try {
|
|
20811
|
-
(0,
|
|
20988
|
+
(0, import_node_fs28.mkdirSync)((0, import_node_path25.dirname)(lockPath), { recursive: true });
|
|
20812
20989
|
return take();
|
|
20813
20990
|
} catch {
|
|
20814
20991
|
try {
|
|
20815
|
-
if (Date.now() - (0,
|
|
20816
|
-
(0,
|
|
20992
|
+
if (Date.now() - (0, import_node_fs28.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
|
|
20993
|
+
(0, import_node_fs28.rmSync)(lockPath, { force: true });
|
|
20817
20994
|
return take();
|
|
20818
20995
|
}
|
|
20819
20996
|
} catch {
|
|
@@ -21061,8 +21238,8 @@ tenant.command("sweep-rc").description("discover (and optionally retire) running
|
|
|
21061
21238
|
async function resolveDnsBounded(host, timeoutMs = 3e3) {
|
|
21062
21239
|
const { lookup } = await import("node:dns/promises");
|
|
21063
21240
|
const probe = lookup(host).then(() => true).catch((e) => dnsErrorToResolution(e?.code));
|
|
21064
|
-
const timeout = new Promise((
|
|
21065
|
-
setTimeout(() =>
|
|
21241
|
+
const timeout = new Promise((resolve6) => {
|
|
21242
|
+
setTimeout(() => resolve6(void 0), timeoutMs).unref?.();
|
|
21066
21243
|
});
|
|
21067
21244
|
return Promise.race([probe, timeout]);
|
|
21068
21245
|
}
|
|
@@ -21817,9 +21994,9 @@ pr.command("create").description("create a PR and print {number,url} JSON").opti
|
|
|
21817
21994
|
console.log(JSON.stringify(created));
|
|
21818
21995
|
});
|
|
21819
21996
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
21820
|
-
const wfDir = (0,
|
|
21821
|
-
if (!(0,
|
|
21822
|
-
return (0,
|
|
21997
|
+
const wfDir = (0, import_node_path25.join)(cwd, ".github", "workflows");
|
|
21998
|
+
if (!(0, import_node_fs28.existsSync)(wfDir)) return [];
|
|
21999
|
+
return (0, import_node_fs28.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).map((name) => `.github/workflows/${name}`);
|
|
21823
22000
|
}
|
|
21824
22001
|
async function resolveMergeCiPolicyForCheckout(repoOpt) {
|
|
21825
22002
|
const repo = repoOpt ?? await resolveRepo();
|
|
@@ -21838,7 +22015,7 @@ function ciAuditDeps() {
|
|
|
21838
22015
|
// Continuous CI delivery (#1550): the gate re-seed renders from the Hub's on-disk seed templates. The
|
|
21839
22016
|
// reconcile runs IN the Hub checkout, so this is local-file I/O (no network fetch). Path is relative to
|
|
21840
22017
|
// the repo root (e.g. skills/bootstrap/seeds/gate.template.yml).
|
|
21841
|
-
readSeedFile: (path2) => (0,
|
|
22018
|
+
readSeedFile: (path2) => (0, import_node_fs28.existsSync)(path2) ? (0, import_node_fs28.readFileSync)(path2, "utf8") : null
|
|
21842
22019
|
};
|
|
21843
22020
|
}
|
|
21844
22021
|
pr.command("ci-policy").description("report merge CI policy: wait-for-checks vs no-ci (for grind/build agents)").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the current checkout)").action(async (o) => {
|
|
@@ -21868,7 +22045,7 @@ pr.command("checks-wait <number>").description("bounded wait for PR checks; skip
|
|
|
21868
22045
|
const result = await waitForPrChecks({
|
|
21869
22046
|
resolvePolicy: () => resolveMergeCiPolicyForCheckout(o.repo),
|
|
21870
22047
|
pollChecks: () => pollGhPrChecks(number, repoArgs),
|
|
21871
|
-
sleep: (ms) => new Promise((
|
|
22048
|
+
sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms))
|
|
21872
22049
|
});
|
|
21873
22050
|
if (o.json) printLine(JSON.stringify(result));
|
|
21874
22051
|
else printLine(`pr checks-wait: ${result.status}${result.reason ? ` \u2014 ${result.reason}` : ""}${result.detail ? ` (${result.detail})` : ""}`);
|
|
@@ -21895,7 +22072,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
|
|
|
21895
22072
|
waitForChecks: (prNumber, repo) => waitForPrChecks({
|
|
21896
22073
|
resolvePolicy: () => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
|
|
21897
22074
|
pollChecks: () => pollGhPrChecks(prNumber, repo ? ["--repo", repo] : []),
|
|
21898
|
-
sleep: (ms) => new Promise((
|
|
22075
|
+
sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms))
|
|
21899
22076
|
}),
|
|
21900
22077
|
mergeAuto: async (prNumber, repo) => {
|
|
21901
22078
|
const args = repo ? ["--repo", repo] : [];
|
|
@@ -21931,7 +22108,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
|
|
|
21931
22108
|
while (Date.now() < deadlineMs) {
|
|
21932
22109
|
const stateRead = await readGhPrStateWithRetry(async () => (await execFileP2("gh", ["pr", "view", prNumber, ...args, "--json", "state", "--jq", ".state"], { timeout: GC_GH_TIMEOUT_MS2 })).stdout, { retries: 2, delayMs: 1e3 });
|
|
21933
22110
|
if (stateRead.ok && stateRead.state === "MERGED") return true;
|
|
21934
|
-
await new Promise((
|
|
22111
|
+
await new Promise((resolve6) => setTimeout(resolve6, PR_LAND_POLL_MS));
|
|
21935
22112
|
}
|
|
21936
22113
|
return false;
|
|
21937
22114
|
}
|
|
@@ -21991,7 +22168,7 @@ async function createDeferredWorktreeStore() {
|
|
|
21991
22168
|
},
|
|
21992
22169
|
write: async (entries) => {
|
|
21993
22170
|
try {
|
|
21994
|
-
await (0, import_promises8.mkdir)((0,
|
|
22171
|
+
await (0, import_promises8.mkdir)((0, import_node_path25.dirname)(registryPath), { recursive: true });
|
|
21995
22172
|
await (0, import_promises8.writeFile)(registryPath, serializeDeferredWorktrees(entries), "utf8");
|
|
21996
22173
|
} catch {
|
|
21997
22174
|
}
|
|
@@ -22005,13 +22182,13 @@ var realWorktreeDirRemover = {
|
|
|
22005
22182
|
probe: (p) => {
|
|
22006
22183
|
let st;
|
|
22007
22184
|
try {
|
|
22008
|
-
st = (0,
|
|
22185
|
+
st = (0, import_node_fs28.lstatSync)(p);
|
|
22009
22186
|
} catch {
|
|
22010
22187
|
return null;
|
|
22011
22188
|
}
|
|
22012
22189
|
if (st.isSymbolicLink()) return "link";
|
|
22013
22190
|
try {
|
|
22014
|
-
(0,
|
|
22191
|
+
(0, import_node_fs28.readlinkSync)(p);
|
|
22015
22192
|
return "link";
|
|
22016
22193
|
} catch {
|
|
22017
22194
|
}
|
|
@@ -22019,7 +22196,7 @@ var realWorktreeDirRemover = {
|
|
|
22019
22196
|
},
|
|
22020
22197
|
readdir: (p) => {
|
|
22021
22198
|
try {
|
|
22022
|
-
return (0,
|
|
22199
|
+
return (0, import_node_fs28.readdirSync)(p);
|
|
22023
22200
|
} catch {
|
|
22024
22201
|
return [];
|
|
22025
22202
|
}
|
|
@@ -22028,9 +22205,9 @@ var realWorktreeDirRemover = {
|
|
|
22028
22205
|
// leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
|
|
22029
22206
|
detachLink: (p) => {
|
|
22030
22207
|
try {
|
|
22031
|
-
(0,
|
|
22208
|
+
(0, import_node_fs28.rmdirSync)(p);
|
|
22032
22209
|
} catch {
|
|
22033
|
-
(0,
|
|
22210
|
+
(0, import_node_fs28.unlinkSync)(p);
|
|
22034
22211
|
}
|
|
22035
22212
|
},
|
|
22036
22213
|
removeTree: (p) => (0, import_promises8.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
|
|
@@ -22045,7 +22222,7 @@ async function resolvePrimaryCheckout(execGit) {
|
|
|
22045
22222
|
function worktreeRemoveDeps(execGit) {
|
|
22046
22223
|
return {
|
|
22047
22224
|
git: execGit,
|
|
22048
|
-
sleep: (ms) => new Promise((
|
|
22225
|
+
sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)),
|
|
22049
22226
|
removeWorktreeDir: async (worktreePath) => removeWorktreeTree(worktreePath, await resolvePrimaryCheckout(execGit), realWorktreeDirRemover)
|
|
22050
22227
|
};
|
|
22051
22228
|
}
|
|
@@ -22060,9 +22237,9 @@ async function worktreeHasStageState(worktreePath) {
|
|
|
22060
22237
|
}
|
|
22061
22238
|
}
|
|
22062
22239
|
function stageStateFileBelongsToWorktree(statePath, worktreePath) {
|
|
22063
|
-
if (!(0,
|
|
22240
|
+
if (!(0, import_node_fs28.existsSync)(statePath)) return false;
|
|
22064
22241
|
try {
|
|
22065
|
-
const state = JSON.parse((0,
|
|
22242
|
+
const state = JSON.parse((0, import_node_fs28.readFileSync)(statePath, "utf8"));
|
|
22066
22243
|
const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
|
|
22067
22244
|
return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
|
|
22068
22245
|
} catch {
|
|
@@ -22135,7 +22312,7 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
|
|
|
22135
22312
|
} : await cleanupPrMergeLocalBranch(headRef, {
|
|
22136
22313
|
beforeWorktrees,
|
|
22137
22314
|
startingPath,
|
|
22138
|
-
pathExists: (p) => (0,
|
|
22315
|
+
pathExists: (p) => (0, import_node_fs28.existsSync)(p),
|
|
22139
22316
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
22140
22317
|
teardownWorktreeStage,
|
|
22141
22318
|
deferredStore,
|
|
@@ -22332,7 +22509,7 @@ function stageScopedRunOpts(o) {
|
|
|
22332
22509
|
};
|
|
22333
22510
|
}
|
|
22334
22511
|
function printLine(value) {
|
|
22335
|
-
(0,
|
|
22512
|
+
(0, import_node_fs28.writeSync)(1, `${value}
|
|
22336
22513
|
`);
|
|
22337
22514
|
}
|
|
22338
22515
|
function stageKeepAlive() {
|
|
@@ -22349,8 +22526,8 @@ async function resolveStage() {
|
|
|
22349
22526
|
local,
|
|
22350
22527
|
shell: shellFor(),
|
|
22351
22528
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
22352
|
-
hasCompose: (0,
|
|
22353
|
-
hasEnvExample: (0,
|
|
22529
|
+
hasCompose: (0, import_node_fs28.existsSync)((0, import_node_path25.join)(process.cwd(), "docker-compose.yml")),
|
|
22530
|
+
hasEnvExample: (0, import_node_fs28.existsSync)((0, import_node_path25.join)(process.cwd(), ".env.example"))
|
|
22354
22531
|
});
|
|
22355
22532
|
}
|
|
22356
22533
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -22812,7 +22989,7 @@ bootstrap.command("verify <repo>").description("audit whether an existing repo i
|
|
|
22812
22989
|
client: defaultGitHubClient(),
|
|
22813
22990
|
projectMeta: meta,
|
|
22814
22991
|
deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
|
|
22815
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
22992
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs28.existsSync)(path2) ? (0, import_node_fs28.readFileSync)(path2, "utf8") : null,
|
|
22816
22993
|
// requiredGcpApis is stored as an array by a JSON write, but `project set --var KEY=VALUE` stores a raw
|
|
22817
22994
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
22818
22995
|
requiredGcpApis: (() => {
|
|
@@ -22856,12 +23033,12 @@ bootstrap.command("apply <repo>").description("idempotent seed apply from skills
|
|
|
22856
23033
|
return fail(`bootstrap apply: ${e.message}`);
|
|
22857
23034
|
}
|
|
22858
23035
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
22859
|
-
if (!(0,
|
|
22860
|
-
const manifest = loadBootstrapSeeds((0,
|
|
23036
|
+
if (!(0, import_node_fs28.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; run from the MMI-Hub repo root`);
|
|
23037
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs28.readFileSync)(manifestPath, "utf8"));
|
|
22861
23038
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
22862
23039
|
const slug = parsedRepo.slug;
|
|
22863
23040
|
const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
|
|
22864
|
-
const readFile7 = (p) => (0,
|
|
23041
|
+
const readFile7 = (p) => (0, import_node_fs28.existsSync)(p) ? (0, import_node_fs28.readFileSync)(p, "utf8") : null;
|
|
22865
23042
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
22866
23043
|
const rawVars = {};
|
|
22867
23044
|
for (const value of rawValues("--var")) {
|
|
@@ -23112,16 +23289,16 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
23112
23289
|
if (o.class !== "deployable" && o.class !== "content") return failGraceful("access audit: --class must be deployable or content");
|
|
23113
23290
|
targets = [{ repo: o.repo, class: o.class }];
|
|
23114
23291
|
} else {
|
|
23115
|
-
const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0,
|
|
23292
|
+
const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0, import_node_fs28.existsSync)("projects.json") ? (0, import_node_fs28.readFileSync)("projects.json", "utf8") : null;
|
|
23116
23293
|
if (!projectsJson) return failGraceful("access audit: no project registry \u2014 Hub API unreachable and projects.json not found; run from the MMI-Hub repo root or pass --repo <owner/repo>");
|
|
23117
|
-
const fanoutJson = (0,
|
|
23294
|
+
const fanoutJson = (0, import_node_fs28.existsSync)(".github/fanout-targets.json") ? (0, import_node_fs28.readFileSync)(".github/fanout-targets.json", "utf8") : null;
|
|
23118
23295
|
targets = loadAccessTargets(projectsJson, fanoutJson);
|
|
23119
23296
|
}
|
|
23120
23297
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
23121
|
-
const fileMatrix = (0,
|
|
23298
|
+
const fileMatrix = (0, import_node_fs28.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs28.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
23122
23299
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
23123
23300
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
23124
|
-
const fileContracts = (0,
|
|
23301
|
+
const fileContracts = (0, import_node_fs28.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs28.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
23125
23302
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
23126
23303
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess);
|
|
23127
23304
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|