@mutmutco/cli 4.0.10 → 4.0.11
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 +175 -23
- package/dist/repo-index-v4.cjs +31 -8
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -10827,10 +10827,10 @@ var rollout_plan_default = {
|
|
|
10827
10827
|
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)."
|
|
10828
10828
|
},
|
|
10829
10829
|
baseline: {
|
|
10830
|
-
version: "4.0.
|
|
10831
|
-
tag: "v4.0.
|
|
10832
|
-
commit: "
|
|
10833
|
-
npm: "@mutmutco/cli@4.0.
|
|
10830
|
+
version: "4.0.11",
|
|
10831
|
+
tag: "v4.0.11",
|
|
10832
|
+
commit: "5b62441055eb",
|
|
10833
|
+
npm: "@mutmutco/cli@4.0.11"
|
|
10834
10834
|
},
|
|
10835
10835
|
exitCriterion: "fleet-n-of-n",
|
|
10836
10836
|
hubOnlyShortcut: "forbidden",
|
|
@@ -10847,14 +10847,14 @@ var rollout_plan_default = {
|
|
|
10847
10847
|
repo: "mutmutco/mmi-hub",
|
|
10848
10848
|
role: "canary",
|
|
10849
10849
|
schedule: "train",
|
|
10850
|
-
v3Target: "v4.0.
|
|
10850
|
+
v3Target: "v4.0.11"
|
|
10851
10851
|
}
|
|
10852
10852
|
],
|
|
10853
10853
|
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.",
|
|
10854
10854
|
rollback: {
|
|
10855
10855
|
independent: true,
|
|
10856
|
-
mechanism: "npm dist-tag latest -> 4.0.
|
|
10857
|
-
v3Target: "v4.0.
|
|
10856
|
+
mechanism: "npm dist-tag latest -> 4.0.11 and redeploy the Hub Lambda from tag v4.0.11 (5b62441055eb); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
10857
|
+
v3Target: "v4.0.11 (@mutmutco/cli@4.0.11, tag commit 5b62441055eb \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
10858
10858
|
}
|
|
10859
10859
|
},
|
|
10860
10860
|
{
|
|
@@ -17552,6 +17552,12 @@ function evaluateClosingGuard(input, opts) {
|
|
|
17552
17552
|
};
|
|
17553
17553
|
}
|
|
17554
17554
|
const closedSkipMessage = skippedClosed.length ? alreadyClosedCommitClosingMessage(skippedClosed, opts.context) : void 0;
|
|
17555
|
+
const unregistered = findClaimedButUnregisteredClosings(input.text, input.closing, input.alreadyClosed);
|
|
17556
|
+
if (unregistered.length) {
|
|
17557
|
+
const base = claimedClosingWarning(unregistered, opts.context);
|
|
17558
|
+
return closedSkipMessage ? { blocked: false, message: `${base}
|
|
17559
|
+
${closedSkipMessage}` } : { blocked: false, message: base };
|
|
17560
|
+
}
|
|
17555
17561
|
if (input.closing.length === 0) {
|
|
17556
17562
|
return closedSkipMessage ? { blocked: false, message: closedSkipMessage } : { blocked: false };
|
|
17557
17563
|
}
|
|
@@ -17575,9 +17581,25 @@ function evaluateClosingGuard(input, opts) {
|
|
|
17575
17581
|
}
|
|
17576
17582
|
return closedSkipMessage ? { blocked: false, message: closedSkipMessage } : { blocked: false };
|
|
17577
17583
|
}
|
|
17584
|
+
function findClaimedButUnregisteredClosings(text, closing, alreadyClosed = []) {
|
|
17585
|
+
const registered = new Set(closing);
|
|
17586
|
+
const closed = new Set(alreadyClosed);
|
|
17587
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
17588
|
+
for (const m of findClosingMentions(text)) {
|
|
17589
|
+
if (m.negated) continue;
|
|
17590
|
+
claimed.add(m.issue);
|
|
17591
|
+
}
|
|
17592
|
+
return [...claimed].filter((n) => !registered.has(n) && !closed.has(n));
|
|
17593
|
+
}
|
|
17594
|
+
function claimedClosingWarning(unregistered, context = "pr merge") {
|
|
17595
|
+
const named = unregistered.map((n) => `#${n}`).join(", ");
|
|
17596
|
+
const first = `#${unregistered[0] ?? "N"}`;
|
|
17597
|
+
return `${context}: WARNING \u2014 the PR text claims it will close ${named}, but GitHub has NOT registered ${named} as closing references, so the merge will NOT close ${first}. Link the issue in the PR's Development sidebar or keep the closing keyword where GitHub parses it (an already-closed issue is omitted silently \u2014 that case is fine). Otherwise ${first} stays open after the merge.`;
|
|
17598
|
+
}
|
|
17578
17599
|
async function withAlreadyClosedCommitTargets(input, fetchIssueState) {
|
|
17579
17600
|
if (!input) return void 0;
|
|
17580
|
-
const
|
|
17601
|
+
const unregistered = findClaimedButUnregisteredClosings(input.text, input.closing);
|
|
17602
|
+
const candidates = [.../* @__PURE__ */ new Set([...(input.commitClosing ?? []).filter((n) => !input.closing.includes(n)), ...unregistered])];
|
|
17581
17603
|
if (!candidates.length) return input;
|
|
17582
17604
|
const alreadyClosed = [];
|
|
17583
17605
|
await Promise.all(
|
|
@@ -17589,7 +17611,7 @@ async function withAlreadyClosedCommitTargets(input, fetchIssueState) {
|
|
|
17589
17611
|
}
|
|
17590
17612
|
})
|
|
17591
17613
|
);
|
|
17592
|
-
return alreadyClosed.length ? { ...input, alreadyClosed } : input;
|
|
17614
|
+
return alreadyClosed.length ? { ...input, alreadyClosed: [.../* @__PURE__ */ new Set([...input.alreadyClosed ?? [], ...alreadyClosed])] } : input;
|
|
17593
17615
|
}
|
|
17594
17616
|
|
|
17595
17617
|
// src/issue-comment.ts
|
|
@@ -23444,22 +23466,32 @@ ${chunk.blurb ?? ""}`;
|
|
|
23444
23466
|
}
|
|
23445
23467
|
}
|
|
23446
23468
|
var V4_EMBED_TIMEOUT_MS = 15 * 6e4;
|
|
23447
|
-
|
|
23448
|
-
|
|
23469
|
+
var V4_EMBED_RETRY = 1;
|
|
23470
|
+
function runEmbedderOnce(cwd, chunks, modelDirectory, createdAt) {
|
|
23471
|
+
if (!chunks.length) return { ok: true, embeddings: [] };
|
|
23449
23472
|
const orchestratorRunner = (0, import_node_path23.join)(process.cwd(), "repo-indexer", "src", "batch.mjs");
|
|
23450
23473
|
const targetRunner = (0, import_node_path23.join)(cwd, "repo-indexer", "src", "batch.mjs");
|
|
23451
23474
|
const file = (0, import_node_fs25.existsSync)(orchestratorRunner) ? orchestratorRunner : targetRunner;
|
|
23452
|
-
if (!(0, import_node_fs25.existsSync)(file)) return {
|
|
23475
|
+
if (!(0, import_node_fs25.existsSync)(file)) return { ok: false, reason: "embeddings-unavailable" };
|
|
23453
23476
|
const request = { texts: chunks.map((chunk) => ({ id: chunk.id, text: embeddingInput(cwd, chunk) })), maxBatch: V4_EMBED_BATCH };
|
|
23454
23477
|
const env = { ...process.env, ...modelDirectory ? { MMI_REPO_INDEXER_MODEL_DIR: modelDirectory } : {} };
|
|
23455
23478
|
const result = (0, import_node_child_process13.spawnSync)(process.execPath, [file], { input: JSON.stringify(request), encoding: "utf8", windowsHide: true, timeout: V4_EMBED_TIMEOUT_MS, maxBuffer: V4_MAX_ARTIFACT_BYTES, env });
|
|
23456
23479
|
if (result.error || result.status !== 0) {
|
|
23457
|
-
const
|
|
23458
|
-
|
|
23480
|
+
const cleanExit2 = result.error === void 0 && result.signal === void 0 && typeof result.status === "number" && result.status !== 0;
|
|
23481
|
+
let code;
|
|
23482
|
+
let stderrTail;
|
|
23483
|
+
if (cleanExit2) {
|
|
23484
|
+
try {
|
|
23485
|
+
code = JSON.parse(String(result.stdout))?.code;
|
|
23486
|
+
} catch {
|
|
23487
|
+
}
|
|
23488
|
+
stderrTail = String(result.stderr).trim().slice(-400) || void 0;
|
|
23489
|
+
}
|
|
23490
|
+
return { ok: false, code, stderrTail, cleanExit: cleanExit2, detail: result.error?.message ?? result.signal ?? `exit status ${result.status ?? "unknown"}` };
|
|
23459
23491
|
}
|
|
23460
23492
|
try {
|
|
23461
23493
|
const response = JSON.parse(result.stdout);
|
|
23462
|
-
if (!response.ok || !response.provenance || !Array.isArray(response.embeddings)) return {
|
|
23494
|
+
if (!response.ok || !response.provenance || !Array.isArray(response.embeddings)) return { ok: false, reason: "embeddings-unavailable" };
|
|
23463
23495
|
const provenance = {
|
|
23464
23496
|
provider: response.provenance.provider,
|
|
23465
23497
|
model: response.provenance.model,
|
|
@@ -23470,11 +23502,24 @@ function runEmbedder(cwd, chunks, modelDirectory, createdAt) {
|
|
|
23470
23502
|
};
|
|
23471
23503
|
const byId = new Map(response.embeddings.map((e) => [e.id, e.vector]));
|
|
23472
23504
|
const embeddings = chunks.map((chunk) => ({ chunkId: chunk.id, vector: byId.get(chunk.id), provenance })).filter((e) => Array.isArray(e.vector) && e.vector.length === provenance.dimensions && e.vector.every(Number.isFinite));
|
|
23473
|
-
return embeddings.length === chunks.length ? { embeddings } : {
|
|
23505
|
+
return embeddings.length === chunks.length ? { ok: true, embeddings } : { ok: false, reason: "partial-coverage" };
|
|
23474
23506
|
} catch {
|
|
23475
|
-
return {
|
|
23507
|
+
return { ok: false, reason: "embeddings-unavailable" };
|
|
23476
23508
|
}
|
|
23477
23509
|
}
|
|
23510
|
+
function runEmbedder(cwd, chunks, modelDirectory, createdAt) {
|
|
23511
|
+
if (!chunks.length) return { embeddings: [] };
|
|
23512
|
+
let last;
|
|
23513
|
+
for (let attempt = 1; attempt <= V4_EMBED_RETRY + 1; attempt++) {
|
|
23514
|
+
last = runEmbedderOnce(cwd, chunks, modelDirectory, createdAt);
|
|
23515
|
+
if (last.ok) return { embeddings: last.embeddings, reason: last.reason };
|
|
23516
|
+
if (attempt > V4_EMBED_RETRY || last.cleanExit !== true) break;
|
|
23517
|
+
}
|
|
23518
|
+
const runner = last;
|
|
23519
|
+
const code = runner.code ? ` code=${runner.code}` : "";
|
|
23520
|
+
const stderr = runner.stderrTail ? ` stderr=${runner.stderrTail}` : "";
|
|
23521
|
+
throw new Error(`repo-index v4 embedding runner failed: ${runner.detail ?? "unknown"}${code}${stderr}`);
|
|
23522
|
+
}
|
|
23478
23523
|
async function buildRepoIndexV4(cwd, repo, opts = {}) {
|
|
23479
23524
|
const { commit, defaultBranch, createdAt } = gitInfo(cwd);
|
|
23480
23525
|
const chunks = await buildStructuralChunks(cwd, repo, commit);
|
|
@@ -29480,9 +29525,25 @@ async function advanceClosedIssuesToDone(deps) {
|
|
|
29480
29525
|
const issue2 = `#${ref.number}`;
|
|
29481
29526
|
try {
|
|
29482
29527
|
const outcome = await deps.moveIssueToDone(issue2);
|
|
29483
|
-
|
|
29484
|
-
|
|
29485
|
-
|
|
29528
|
+
if (!outcome.moved) {
|
|
29529
|
+
entries.push({ issue: issue2, moved: false, status: "failed", error: outcome.error });
|
|
29530
|
+
continue;
|
|
29531
|
+
}
|
|
29532
|
+
if (deps.readIssueStatus) {
|
|
29533
|
+
const verified = await deps.readIssueStatus(issue2);
|
|
29534
|
+
if ("error" in verified) {
|
|
29535
|
+
entries.push({ issue: issue2, moved: true, status: "moved", error: `advanced but could not verify on the board (${verified.error})` });
|
|
29536
|
+
} else if (verified.status !== "Done") {
|
|
29537
|
+
entries.push({
|
|
29538
|
+
issue: issue2,
|
|
29539
|
+
moved: false,
|
|
29540
|
+
status: "failed",
|
|
29541
|
+
error: `advanced to Done but the board reports ${verified.status} \u2014 left behind; move it to Done by hand`
|
|
29542
|
+
});
|
|
29543
|
+
continue;
|
|
29544
|
+
}
|
|
29545
|
+
}
|
|
29546
|
+
entries.push({ issue: issue2, moved: true, status: "moved" });
|
|
29486
29547
|
} catch (e) {
|
|
29487
29548
|
entries.push({ issue: issue2, moved: false, status: "failed", error: e.message });
|
|
29488
29549
|
}
|
|
@@ -29559,6 +29620,20 @@ async function resolveBoardAdvanceForPr(prNumber, repoOption) {
|
|
|
29559
29620
|
moveIssueToDone: async (ref) => {
|
|
29560
29621
|
const moved = await moveBoardItem({ config: await loadConfigForBoardSelector2(ref, repoOption), selector: ref, status: "Done", repo: repoOption, allowPartial: true });
|
|
29561
29622
|
return moved.partial ? { moved: false, error: moved.warning } : { moved: true };
|
|
29623
|
+
},
|
|
29624
|
+
// #5317: after a successful move, re-read the board to verify Done actually holds — a move the API
|
|
29625
|
+
// reported but the board did not reflect is named as left behind, never silently dropped.
|
|
29626
|
+
readIssueStatus: async (ref) => {
|
|
29627
|
+
const number = Number(ref.replace(/^#/, ""));
|
|
29628
|
+
if (!Number.isInteger(number) || number <= 0 || !repo) return { error: `unusable ref ${ref}` };
|
|
29629
|
+
try {
|
|
29630
|
+
const board = await readBoard({ config: await loadConfigForBoardSelector2(ref, repoOption), repo: repoOption });
|
|
29631
|
+
const buckets = [...board.primary.userOwned, ...board.primary.claimable, ...board.primary.taken, ...board.primary.unownedInFlight, ...board.secondary.userOwned, ...board.secondary.claimable, ...board.secondary.taken, ...board.secondary.unownedInFlight];
|
|
29632
|
+
const item = buckets.find((candidate2) => candidate2.repository.toLowerCase() === repo.toLowerCase() && candidate2.number === number);
|
|
29633
|
+
return item ? { status: item.status } : { error: `issue ${ref} not found on the board after advance` };
|
|
29634
|
+
} catch (e) {
|
|
29635
|
+
return { error: e.message };
|
|
29636
|
+
}
|
|
29562
29637
|
}
|
|
29563
29638
|
});
|
|
29564
29639
|
}
|
|
@@ -35638,6 +35713,34 @@ ${pr2.body ?? ""}`;
|
|
|
35638
35713
|
}
|
|
35639
35714
|
}
|
|
35640
35715
|
|
|
35716
|
+
// src/pr-deploy-dev.ts
|
|
35717
|
+
function slugOfRepo(repo) {
|
|
35718
|
+
return repo.split("/")[1]?.toLowerCase() ?? "";
|
|
35719
|
+
}
|
|
35720
|
+
async function planDevDeployOnDevelopmentMerge(repo, baseRef, deps) {
|
|
35721
|
+
if (baseRef !== "development") return { applicable: false, reason: "not-development-base" };
|
|
35722
|
+
const slug = slugOfRepo(repo);
|
|
35723
|
+
if (!slug) return { applicable: false, reason: "unreadable" };
|
|
35724
|
+
let project2;
|
|
35725
|
+
try {
|
|
35726
|
+
project2 = await fetchProjectBySlug(slug, deps);
|
|
35727
|
+
} catch {
|
|
35728
|
+
return { applicable: false, reason: "unreadable" };
|
|
35729
|
+
}
|
|
35730
|
+
const model = project2?.deployModel;
|
|
35731
|
+
if (!model || !isCentralDispatchModel(model)) return { applicable: false, reason: "not-tenant" };
|
|
35732
|
+
return {
|
|
35733
|
+
applicable: true,
|
|
35734
|
+
// The issue's fallback ask, verbatim-shaped: the gap must be visible and actionable at the moment it opens.
|
|
35735
|
+
manualPointer: `dev not redeployed \u2014 run \`mmi-cli devops runtime tenant redeploy ${repo} dev\``
|
|
35736
|
+
};
|
|
35737
|
+
}
|
|
35738
|
+
async function dispatchDevDeploy(repo, deps) {
|
|
35739
|
+
const res = await tenantDeploy({ repo, stage: "dev" }, deps);
|
|
35740
|
+
if (res.ok) return { ok: true };
|
|
35741
|
+
return { ok: false, detail: res.body?.error ?? res.error ?? `HTTP ${res.status}` };
|
|
35742
|
+
}
|
|
35743
|
+
|
|
35641
35744
|
// src/index.ts
|
|
35642
35745
|
var execFileGitRun = async (file, args) => (await execFileP2(file, args, { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
35643
35746
|
async function githubRepoReachProbe() {
|
|
@@ -37107,6 +37210,43 @@ ${filelessTransitionGuide(target, o.stage)}`
|
|
|
37107
37210
|
const res = await setDeployCoords(slug, body, registryClientDeps(cfg));
|
|
37108
37211
|
return reportWrite("org project set-deploy", res);
|
|
37109
37212
|
});
|
|
37213
|
+
var projectTenantTasks = project.command("tenant-tasks").description("project-admin self-declare of tenant tasks for your own repo's dev/rc stages (#5316, #5308) \u2014 new-task-only, dev/rc-only; editing an existing task or declaring a main-stage task stays master-only (`org project set --var tenantTasks=...`)");
|
|
37214
|
+
projectTenantTasks.command("declare <name>").description('register a NEW tenant task \u2014 becomes runnable via `runtime tenant control \u2026 run-task` on dev/rc. Body shape {service,command[],stages[],timeoutSeconds?,artifact?} \u2014 stages limited to dev/rc here; artifact "required" tasks must carry {artifact} in the command argv.').option("--service <name>", "service in the tenant runtime the task runs in (required unless --var carries it)").option("--command <argv...>", "task command as argv (repeatable; required unless --var carries it)").option("--stages <list>", "comma-separated dev,rc stages this task may run on (required unless --var carries it)").option("--timeout-seconds <n>", "optional wall-clock ceiling 1..3600").option("--artifact <none|required>", "artifact contract (default none; required tasks must contain {artifact} in argv)").option("--var <json>", "full task object {service,command[],stages[],timeoutSeconds?,artifact?} \u2014 use instead of the individual flags").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "machine-readable output").action(async (name, o) => {
|
|
37215
|
+
const cfg = await loadConfig();
|
|
37216
|
+
let target;
|
|
37217
|
+
try {
|
|
37218
|
+
target = await projectTarget("org project tenant-tasks declare", o.repo);
|
|
37219
|
+
} catch (e) {
|
|
37220
|
+
return fail(e.message);
|
|
37221
|
+
}
|
|
37222
|
+
const slug = slugOf(target);
|
|
37223
|
+
let task;
|
|
37224
|
+
if (o.var) {
|
|
37225
|
+
try {
|
|
37226
|
+
task = JSON.parse(o.var);
|
|
37227
|
+
} catch {
|
|
37228
|
+
return fail("org project tenant-tasks declare: --var must be valid JSON");
|
|
37229
|
+
}
|
|
37230
|
+
} else {
|
|
37231
|
+
if (!o.service || !o.command?.length || !o.stages) {
|
|
37232
|
+
return fail("org project tenant-tasks declare: pass --var <json> or all of --service, --command, --stages");
|
|
37233
|
+
}
|
|
37234
|
+
task = {
|
|
37235
|
+
service: o.service,
|
|
37236
|
+
command: o.command,
|
|
37237
|
+
stages: o.stages.split(",").map((s) => s.trim()).filter(Boolean),
|
|
37238
|
+
...o.timeoutSeconds !== void 0 ? { timeoutSeconds: Number(o.timeoutSeconds) } : {},
|
|
37239
|
+
...o.artifact !== void 0 ? { artifact: o.artifact } : {}
|
|
37240
|
+
};
|
|
37241
|
+
}
|
|
37242
|
+
const res = await postJson(`/projects/${encodeURIComponent(slug)}/tenant-tasks`, { repo: target, tenantTasks: { [name]: task } }, registryClientDeps(cfg), "POST", { noRetry: true });
|
|
37243
|
+
if (o.json) {
|
|
37244
|
+
console.log(JSON.stringify(res, null, 2));
|
|
37245
|
+
} else {
|
|
37246
|
+
reportWrite("org project tenant-tasks declare", res);
|
|
37247
|
+
}
|
|
37248
|
+
if (!res.ok) process.exitCode = 1;
|
|
37249
|
+
});
|
|
37110
37250
|
var registry = program2.command("registry").description("the DDB org registry \u2014 org-level constants");
|
|
37111
37251
|
registry.command("org").description("the org config (account id, region, orgProjectId, sagaApiUrl)").option("--json", "machine-readable output").action(async (_o) => {
|
|
37112
37252
|
const cfg = await loadConfig();
|
|
@@ -38000,11 +38140,16 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
38000
38140
|
const method = o.rebase ? "--rebase" : o.merge ? "--merge" : "--squash";
|
|
38001
38141
|
const repoArgs = o.repo ? ["--repo", o.repo] : [];
|
|
38002
38142
|
const repoForPostCleanup = await resolveRepo(o.repo) ?? o.repo;
|
|
38003
|
-
const
|
|
38143
|
+
const prMeta = await execFileP2("gh", ["pr", "view", number, ...repoArgs, "--json", "headRefName,baseRefName", "--jq", "{head: .headRefName, base: .baseRefName}"], { timeout: GC_GH_TIMEOUT_MS }).then((r) => JSON.parse(r.stdout)).catch(async (e) => {
|
|
38004
38144
|
if (!isGitHubRateLimitError(e) || !repoForPostCleanup) throw e;
|
|
38005
38145
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 reading PR #${number} via REST instead (#4588).`);
|
|
38006
|
-
|
|
38146
|
+
const snapshot = await fetchRestPrSnapshot(number, repoForPostCleanup);
|
|
38147
|
+
return { head: snapshot.headRef, base: snapshot.baseRef };
|
|
38007
38148
|
});
|
|
38149
|
+
const headRef = prMeta.head;
|
|
38150
|
+
const baseRef = prMeta.base;
|
|
38151
|
+
const devDeployDeps = repoForPostCleanup ? registryClientDeps(await loadConfig()) : void 0;
|
|
38152
|
+
const devDeployPlan = repoForPostCleanup && devDeployDeps ? await planDevDeployOnDevelopmentMerge(repoForPostCleanup, baseRef, devDeployDeps).catch(() => ({ applicable: false, reason: "unreadable" })) : { applicable: false, reason: "unreadable" };
|
|
38008
38153
|
const closingGuardInput = await withAlreadyClosedCommitTargets(
|
|
38009
38154
|
await readClosingGuardInput(number, repoArgs, repoForPostCleanup, "pr merge"),
|
|
38010
38155
|
async (n) => {
|
|
@@ -38133,6 +38278,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
38133
38278
|
const enqueued = describePrMergeEnqueuedReason(await pollGhPrChecks(number, repoArgs).catch(() => void 0));
|
|
38134
38279
|
console.log(JSON.stringify({ mergeStatus: "auto-merge-enqueued", enqueuedReason: enqueued.reason, pr: number, branch: headRef, state: state || "unknown", upgradedToAuto: upgradedToAuto || void 0 }));
|
|
38135
38280
|
console.warn(`pr merge: PR #${number} is ENQUEUED, not merged \u2014 ${enqueued.message}.`);
|
|
38281
|
+
if (devDeployPlan.applicable) console.warn(`pr merge: ${devDeployPlan.manualPointer} once the merge lands (auto-merge does not redeploy the dev stage).`);
|
|
38136
38282
|
if (upgradedToAuto && !o.auto) {
|
|
38137
38283
|
console.warn(`pr merge: exiting ${PR_MERGE_ENQUEUED_EXIT_CODE} so a chained command does not treat this as a completed merge.`);
|
|
38138
38284
|
process.exitCode = PR_MERGE_ENQUEUED_EXIT_CODE;
|
|
@@ -38156,6 +38302,8 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
38156
38302
|
const boardAdvance = await advanceClosedIssuesToDone2(number, repoForPostCleanup);
|
|
38157
38303
|
const crossRepoFilingIssue = repoForPostCleanup ? await reconcileCrossRepoFilingIssue(defaultGitHubClient(), repoForPostCleanup, Number(number)) : { status: "failed", error: "could not resolve the PR repo for cross-repo filing-issue reconciliation" };
|
|
38158
38304
|
invalidateStatuslineBoardCache();
|
|
38305
|
+
const devDeploy = devDeployPlan.applicable && devDeployDeps && remoteNotAttemptedReason !== "pr-already-merged" ? await dispatchDevDeploy(repoForPostCleanup, devDeployDeps) : void 0;
|
|
38306
|
+
const devDeployManual = devDeployPlan.applicable ? devDeployPlan.manualPointer : void 0;
|
|
38159
38307
|
console.log(JSON.stringify({
|
|
38160
38308
|
mergeStatus: "merged",
|
|
38161
38309
|
merged: number,
|
|
@@ -38167,10 +38315,14 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
38167
38315
|
// tell "merged clean" from "merged, but the board did not follow" without guessing from the exit code.
|
|
38168
38316
|
...boardAdvance.entries.length ? { boardAdvance: boardAdvance.entries } : {},
|
|
38169
38317
|
boardAdvanceStatus: boardAdvance.status,
|
|
38170
|
-
...crossRepoFilingIssue.status !== "not-applicable" ? { crossRepoFilingIssue } : {}
|
|
38318
|
+
...crossRepoFilingIssue.status !== "not-applicable" ? { crossRepoFilingIssue } : {},
|
|
38319
|
+
...devDeploy ? { devDeploy: devDeploy.ok ? { dispatched: true } : { dispatched: false, manual: devDeployManual } } : {}
|
|
38171
38320
|
}));
|
|
38172
38321
|
const boardAdvanceMessage = boardAdvanceFailureMessage(boardAdvance);
|
|
38173
38322
|
if (boardAdvanceMessage) console.error(boardAdvanceMessage);
|
|
38323
|
+
if (devDeploy && !devDeploy.ok) {
|
|
38324
|
+
console.error(`pr merge: ${devDeployManual} (${devDeploy.detail})`);
|
|
38325
|
+
}
|
|
38174
38326
|
if (crossRepoFilingIssue.status === "failed") {
|
|
38175
38327
|
console.error(`pr merge: cross-repo filing-issue reconciliation failed (${crossRepoFilingIssue.error}) \u2014 the PR MERGED, but a foreign filing issue may remain open.`);
|
|
38176
38328
|
}
|
package/dist/repo-index-v4.cjs
CHANGED
|
@@ -439,22 +439,32 @@ ${chunk.blurb ?? ""}`;
|
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
441
|
var V4_EMBED_TIMEOUT_MS = 15 * 6e4;
|
|
442
|
-
|
|
443
|
-
|
|
442
|
+
var V4_EMBED_RETRY = 1;
|
|
443
|
+
function runEmbedderOnce(cwd, chunks, modelDirectory, createdAt) {
|
|
444
|
+
if (!chunks.length) return { ok: true, embeddings: [] };
|
|
444
445
|
const orchestratorRunner = (0, import_node_path4.join)(process.cwd(), "repo-indexer", "src", "batch.mjs");
|
|
445
446
|
const targetRunner = (0, import_node_path4.join)(cwd, "repo-indexer", "src", "batch.mjs");
|
|
446
447
|
const file = (0, import_node_fs3.existsSync)(orchestratorRunner) ? orchestratorRunner : targetRunner;
|
|
447
|
-
if (!(0, import_node_fs3.existsSync)(file)) return {
|
|
448
|
+
if (!(0, import_node_fs3.existsSync)(file)) return { ok: false, reason: "embeddings-unavailable" };
|
|
448
449
|
const request = { texts: chunks.map((chunk) => ({ id: chunk.id, text: embeddingInput(cwd, chunk) })), maxBatch: V4_EMBED_BATCH };
|
|
449
450
|
const env = { ...process.env, ...modelDirectory ? { MMI_REPO_INDEXER_MODEL_DIR: modelDirectory } : {} };
|
|
450
451
|
const result = (0, import_node_child_process3.spawnSync)(process.execPath, [file], { input: JSON.stringify(request), encoding: "utf8", windowsHide: true, timeout: V4_EMBED_TIMEOUT_MS, maxBuffer: V4_MAX_ARTIFACT_BYTES, env });
|
|
451
452
|
if (result.error || result.status !== 0) {
|
|
452
|
-
const
|
|
453
|
-
|
|
453
|
+
const cleanExit = result.error === void 0 && result.signal === void 0 && typeof result.status === "number" && result.status !== 0;
|
|
454
|
+
let code;
|
|
455
|
+
let stderrTail;
|
|
456
|
+
if (cleanExit) {
|
|
457
|
+
try {
|
|
458
|
+
code = JSON.parse(String(result.stdout))?.code;
|
|
459
|
+
} catch {
|
|
460
|
+
}
|
|
461
|
+
stderrTail = String(result.stderr).trim().slice(-400) || void 0;
|
|
462
|
+
}
|
|
463
|
+
return { ok: false, code, stderrTail, cleanExit, detail: result.error?.message ?? result.signal ?? `exit status ${result.status ?? "unknown"}` };
|
|
454
464
|
}
|
|
455
465
|
try {
|
|
456
466
|
const response = JSON.parse(result.stdout);
|
|
457
|
-
if (!response.ok || !response.provenance || !Array.isArray(response.embeddings)) return {
|
|
467
|
+
if (!response.ok || !response.provenance || !Array.isArray(response.embeddings)) return { ok: false, reason: "embeddings-unavailable" };
|
|
458
468
|
const provenance = {
|
|
459
469
|
provider: response.provenance.provider,
|
|
460
470
|
model: response.provenance.model,
|
|
@@ -465,10 +475,23 @@ function runEmbedder(cwd, chunks, modelDirectory, createdAt) {
|
|
|
465
475
|
};
|
|
466
476
|
const byId = new Map(response.embeddings.map((e) => [e.id, e.vector]));
|
|
467
477
|
const embeddings = chunks.map((chunk) => ({ chunkId: chunk.id, vector: byId.get(chunk.id), provenance })).filter((e) => Array.isArray(e.vector) && e.vector.length === provenance.dimensions && e.vector.every(Number.isFinite));
|
|
468
|
-
return embeddings.length === chunks.length ? { embeddings } : {
|
|
478
|
+
return embeddings.length === chunks.length ? { ok: true, embeddings } : { ok: false, reason: "partial-coverage" };
|
|
469
479
|
} catch {
|
|
470
|
-
return {
|
|
480
|
+
return { ok: false, reason: "embeddings-unavailable" };
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function runEmbedder(cwd, chunks, modelDirectory, createdAt) {
|
|
484
|
+
if (!chunks.length) return { embeddings: [] };
|
|
485
|
+
let last;
|
|
486
|
+
for (let attempt = 1; attempt <= V4_EMBED_RETRY + 1; attempt++) {
|
|
487
|
+
last = runEmbedderOnce(cwd, chunks, modelDirectory, createdAt);
|
|
488
|
+
if (last.ok) return { embeddings: last.embeddings, reason: last.reason };
|
|
489
|
+
if (attempt > V4_EMBED_RETRY || last.cleanExit !== true) break;
|
|
471
490
|
}
|
|
491
|
+
const runner = last;
|
|
492
|
+
const code = runner.code ? ` code=${runner.code}` : "";
|
|
493
|
+
const stderr = runner.stderrTail ? ` stderr=${runner.stderrTail}` : "";
|
|
494
|
+
throw new Error(`repo-index v4 embedding runner failed: ${runner.detail ?? "unknown"}${code}${stderr}`);
|
|
472
495
|
}
|
|
473
496
|
async function buildRepoIndexV4(cwd, repo, opts = {}) {
|
|
474
497
|
const { commit, defaultBranch, createdAt } = gitInfo(cwd);
|
package/package.json
CHANGED