@mutmutco/cli 4.0.1 → 4.0.3
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 +165 -58
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -8237,6 +8237,25 @@ function awsScheduleRefs(payload) {
|
|
|
8237
8237
|
}
|
|
8238
8238
|
return refs;
|
|
8239
8239
|
}
|
|
8240
|
+
function decorateAwsModel(entries, registry2, verdicts) {
|
|
8241
|
+
if (!registry2 || registry2.length === 0) return [...entries];
|
|
8242
|
+
const rowById = new Map(registry2.map((r) => [r.id, r]));
|
|
8243
|
+
const literalById = /* @__PURE__ */ new Map();
|
|
8244
|
+
for (const v of verdicts ?? []) {
|
|
8245
|
+
if (typeof v.modelId === "string" && v.modelId.trim() && !literalById.has(v.id)) {
|
|
8246
|
+
literalById.set(v.id, v.modelId);
|
|
8247
|
+
}
|
|
8248
|
+
}
|
|
8249
|
+
return entries.map((e) => {
|
|
8250
|
+
if (!e.scheduleId) return e;
|
|
8251
|
+
const row = rowById.get(e.scheduleId);
|
|
8252
|
+
if (!row || row.executor !== "cursor-agent") return e;
|
|
8253
|
+
const literal = literalById.get(e.scheduleId);
|
|
8254
|
+
if (literal) return { ...e, model: literal };
|
|
8255
|
+
if (row.model) return { ...e, model: `role: ${row.model}` };
|
|
8256
|
+
return { ...e, model: "org-default" };
|
|
8257
|
+
});
|
|
8258
|
+
}
|
|
8240
8259
|
var DECLARED_ENTRIES = [
|
|
8241
8260
|
{ name: "mmi-backup (mm-central, mmi-fofu, zuber, mmi-oguz)", cadence: "17 2 * * *", executor: "box cron.d", llm: "no", resolved: "declared", source: "/etc/cron.d/mmi-backup \u2192 /opt/mmi-control/nightly-backup.sh (verify: mmi-cli devops runtime box get <box> --ssh)" },
|
|
8242
8261
|
// #3370: the floor under runner-hygiene.yml. That workflow runs ON the runner and needs checkout +
|
|
@@ -8247,8 +8266,16 @@ var DECLARED_ENTRIES = [
|
|
|
8247
8266
|
// #4118: Listening + systemd-active ≠ GitHub online after acquirejob 503. restart.conf cannot see
|
|
8248
8267
|
// it; an Actions workflow on mmi-live cannot heal it when half the fleet is offline. Root cron.
|
|
8249
8268
|
{ name: "mmi-runner-online-reconcile (mmi-runner)", cadence: "*/5 * * * *", executor: "box cron.d", llm: "no", resolved: "declared", source: "/etc/cron.d/mmi-runner-online \u2192 /opt/mmi-control/runner-online-reconcile.sh; API-offline + _diag acquirejob, max 2 restarts/tick (verify: mmi-cli devops runtime box get mmi-runner --ssh)" },
|
|
8250
|
-
|
|
8251
|
-
|
|
8269
|
+
// #3852: the runner /tmp sweep — daily inode hygiene (03:41 UTC), deliberately the one clutter path the
|
|
8270
|
+
// out-of-band disk-relief script must never touch. Verified installed 2026-08-18: the cron.d file and
|
|
8271
|
+
// flock line match scripts/runner-tmp-sweep.cron exactly.
|
|
8272
|
+
{ name: "mmi-runner-tmp-sweep (mmi-runner)", cadence: "41 3 * * *", executor: "box cron.d", llm: "no", resolved: "declared", source: "/etc/cron.d/mmi-runner-tmp-sweep \u2192 /opt/mmi-control/runner-tmp-sweep.sh; daily inode hygiene, flock -n (verify: mmi-cli devops runtime box get mmi-runner --ssh)" }
|
|
8273
|
+
// RETIRED 2026-08-18 (MMI-Hub#5234): `zuber-bake` (every 30 min) and `zuber-bake-full` (daily 00:30 UTC)
|
|
8274
|
+
// were declared as zuber systemd timers. The ZuberShade full park (#1774, 2026-08-06) stopped them: on
|
|
8275
|
+
// the box (46.225.58.31) zuber-bake.timer / zuber-bake-full.timer answer not-found and
|
|
8276
|
+
// zuber-bake-demand.timer is disabled — no bake/oven timer remains armed, and the notebook must not
|
|
8277
|
+
// claim a job the box does not run. Re-add when the hold lifts and the demand bake re-arms
|
|
8278
|
+
// (MMC-ZuberShade docs/aws-teardown-hold-2026-08-03.md).
|
|
8252
8279
|
];
|
|
8253
8280
|
function sortEntries(entries) {
|
|
8254
8281
|
return [...entries].sort(
|
|
@@ -8263,10 +8290,12 @@ function formatSchedulesTable(entries, now = /* @__PURE__ */ new Date()) {
|
|
|
8263
8290
|
const cadW = w((e) => e.cadence, "CADENCE");
|
|
8264
8291
|
const exeW = w((e) => e.executor, "EXECUTOR");
|
|
8265
8292
|
const llmW = w((e) => e.llm, "LLM");
|
|
8293
|
+
const modelOf = (e) => e.model ?? "-";
|
|
8294
|
+
const modW = w((e) => modelOf(e), "MODEL");
|
|
8266
8295
|
const resW = w((e) => resolvedOf.get(e) ?? e.resolved, "RESOLVED");
|
|
8267
|
-
const row = (n, c, x, l, r, s) => `${n.padEnd(nameW)} ${c.padEnd(cadW)} ${x.padEnd(exeW)} ${l.padEnd(llmW)} ${r.padEnd(resW)} ${s}`;
|
|
8268
|
-
const lines = [row("JOB", "CADENCE", "EXECUTOR", "LLM", "RESOLVED", "SOURCE")];
|
|
8269
|
-
for (const e of entries) lines.push(row(e.name, e.cadence, e.executor, e.llm, resolvedOf.get(e) ?? e.resolved, e.source));
|
|
8296
|
+
const row = (n, c, x, l, m, r, s) => `${n.padEnd(nameW)} ${c.padEnd(cadW)} ${x.padEnd(exeW)} ${l.padEnd(llmW)} ${m.padEnd(modW)} ${r.padEnd(resW)} ${s}`;
|
|
8297
|
+
const lines = [row("JOB", "CADENCE", "EXECUTOR", "LLM", "MODEL", "RESOLVED", "SOURCE")];
|
|
8298
|
+
for (const e of entries) lines.push(row(e.name, e.cadence, e.executor, e.llm, modelOf(e), resolvedOf.get(e) ?? e.resolved, e.source));
|
|
8270
8299
|
return lines.join("\n");
|
|
8271
8300
|
}
|
|
8272
8301
|
function withTickState(entry, now = /* @__PURE__ */ new Date()) {
|
|
@@ -8274,11 +8303,11 @@ function withTickState(entry, now = /* @__PURE__ */ new Date()) {
|
|
|
8274
8303
|
}
|
|
8275
8304
|
function renderDocSection(entries, generatedAtIso) {
|
|
8276
8305
|
const lines = [
|
|
8277
|
-
"| Job | Cadence | Executor | LLM | Resolved | Source |",
|
|
8278
|
-
"
|
|
8306
|
+
"| Job | Cadence | Executor | LLM | Model | Resolved | Source |",
|
|
8307
|
+
"|---|---|---|---|---|---|---|"
|
|
8279
8308
|
];
|
|
8280
8309
|
for (const e of entries) {
|
|
8281
|
-
lines.push(`| ${e.name} | \`${e.cadence}\` | ${e.executor} | ${e.llm} | ${e.resolved} | ${e.source} |`);
|
|
8310
|
+
lines.push(`| ${e.name} | \`${e.cadence}\` | ${e.executor} | ${e.llm} | ${e.model ?? "-"} | ${e.resolved} | ${e.source} |`);
|
|
8282
8311
|
}
|
|
8283
8312
|
return [
|
|
8284
8313
|
DOC_START_MARKER,
|
|
@@ -8694,6 +8723,22 @@ async function fetchSchedulesList(deps) {
|
|
|
8694
8723
|
return null;
|
|
8695
8724
|
}
|
|
8696
8725
|
}
|
|
8726
|
+
async function fetchRunVerdicts(deps) {
|
|
8727
|
+
if (!deps.baseUrl) return null;
|
|
8728
|
+
const token = await deps.token();
|
|
8729
|
+
if (!token) return null;
|
|
8730
|
+
try {
|
|
8731
|
+
const res = await retriedFetch(deps, `${deps.baseUrl.replace(/\/$/, "")}/schedules/verdicts`, {
|
|
8732
|
+
method: "GET",
|
|
8733
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
8734
|
+
});
|
|
8735
|
+
if (!res.ok) return null;
|
|
8736
|
+
const body = await res.json();
|
|
8737
|
+
return Array.isArray(body?.runVerdicts) ? body.runVerdicts : null;
|
|
8738
|
+
} catch {
|
|
8739
|
+
return null;
|
|
8740
|
+
}
|
|
8741
|
+
}
|
|
8697
8742
|
async function fetchOrgConfig(deps) {
|
|
8698
8743
|
if (!deps.baseUrl) return null;
|
|
8699
8744
|
const token = await deps.token();
|
|
@@ -9110,6 +9155,7 @@ var OWNER2 = "mutmutco";
|
|
|
9110
9155
|
var SSM_ROOT = "/mmi-future";
|
|
9111
9156
|
var PROJECT_TIER_SEGMENT = "dev";
|
|
9112
9157
|
var ORG_INFRA_SLUG = "_org";
|
|
9158
|
+
var JERV_VAULT_POINTER = "Not in the MMI vault? Jerv service, personal API and model-provider credentials live in the separate Jerv AWS Vault \u2014 enumerate: `jerv-cli vault list`; consume keyless: `jerv-cli vault use <KEY> -- <cmd>` (values are never printed).";
|
|
9113
9159
|
var KEY_MAX_PATH_SEGMENTS = 4;
|
|
9114
9160
|
var KEY_RE = new RegExp(`^(?:[a-z0-9][a-z0-9-]*/){0,${KEY_MAX_PATH_SEGMENTS}}[A-Za-z][A-Za-z0-9_]*$`);
|
|
9115
9161
|
function isValidSecretKey(key) {
|
|
@@ -9126,7 +9172,7 @@ function secretParamName(slug, key) {
|
|
|
9126
9172
|
return `${SSM_ROOT}/${slug}/${key}`;
|
|
9127
9173
|
}
|
|
9128
9174
|
function formatSecretList(items) {
|
|
9129
|
-
if (!items.length) return
|
|
9175
|
+
if (!items.length) return `no secrets \u2014 ${JERV_VAULT_POINTER}`;
|
|
9130
9176
|
const width = Math.max(...items.map((i) => i.key.length));
|
|
9131
9177
|
return items.map((i) => {
|
|
9132
9178
|
const head = `${i.canManage ? "*" : " "} ${i.key.padEnd(width)} ${i.tier}`;
|
|
@@ -9317,7 +9363,7 @@ function formatCapabilities(r) {
|
|
|
9317
9363
|
const items = [...r.capabilities ?? []].sort((a, b) => a.scope.localeCompare(b.scope));
|
|
9318
9364
|
if (!items.length) return `${head}
|
|
9319
9365
|
|
|
9320
|
-
no vault credentials visible`;
|
|
9366
|
+
no vault credentials visible \u2014 ${JERV_VAULT_POINTER}`;
|
|
9321
9367
|
const width = Math.max(...items.map((i) => i.scope.length));
|
|
9322
9368
|
const lines = items.map(
|
|
9323
9369
|
(i) => `${i.accessible ? "+" : " "} ${i.scope.padEnd(width)} ${i.tier.padEnd(7)} ${i.grantScope ? `${i.reason} (${i.grantScope})` : i.reason}`
|
|
@@ -9440,7 +9486,8 @@ function resolveNotFoundGuidance(input) {
|
|
|
9440
9486
|
const lines = [
|
|
9441
9487
|
`Secret ${key} was not found at ${tried}, and no key named ${leaf} is visible to you.`,
|
|
9442
9488
|
`Locate it by intent first: \`mmi-cli vault secrets find "<what it is for>"\` \u2014 it returns the exact keyless-use command.`,
|
|
9443
|
-
mnemonic
|
|
9489
|
+
mnemonic,
|
|
9490
|
+
JERV_VAULT_POINTER
|
|
9444
9491
|
];
|
|
9445
9492
|
if (isMaster) {
|
|
9446
9493
|
lines.push("You are master: if it truly does not exist yet, create it with `mmi-cli vault secrets set` rather than requesting a grant from yourself.");
|
|
@@ -9722,6 +9769,7 @@ async function secretsVerify(deps, key, opts) {
|
|
|
9722
9769
|
if (!value) {
|
|
9723
9770
|
deps.err(`${key}: ABSENT or unreachable at this coordinate \u2014 no value could be read`);
|
|
9724
9771
|
deps.err(`Locate a key across every vault you can reach: mmi-cli oracle org access capabilities${opts.repo ? ` --repo ${opts.repo}` : ""}`);
|
|
9772
|
+
deps.err(JERV_VAULT_POINTER);
|
|
9725
9773
|
return false;
|
|
9726
9774
|
}
|
|
9727
9775
|
deps.log(`${key}: PRESENT (${value.length} chars) \u2014 reachability only; no provider verifier is configured for ${secretLeafName(key)}, so the credential itself was not validated`);
|
|
@@ -10377,6 +10425,7 @@ async function fetchSecretForUse(deps, { repo, key, slug }) {
|
|
|
10377
10425
|
// src/report.ts
|
|
10378
10426
|
var HUB_REPO = "mutmutco/MMI-Hub";
|
|
10379
10427
|
var REPORT_LABEL = "report";
|
|
10428
|
+
var LEARNING_LABEL = "learning";
|
|
10380
10429
|
var REPORT_LOOP_KIND = "friction";
|
|
10381
10430
|
var REPORT_SURFACE_WAIVER_REASON = "Hub-only friction filing; attribution repo is footer-only; no honest single surface";
|
|
10382
10431
|
function preflightReportSurface() {
|
|
@@ -10774,10 +10823,10 @@ var rollout_plan_default = {
|
|
|
10774
10823
|
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)."
|
|
10775
10824
|
},
|
|
10776
10825
|
baseline: {
|
|
10777
|
-
version: "4.0.
|
|
10778
|
-
tag: "v4.0.
|
|
10779
|
-
commit: "
|
|
10780
|
-
npm: "@mutmutco/cli@4.0.
|
|
10826
|
+
version: "4.0.3",
|
|
10827
|
+
tag: "v4.0.3",
|
|
10828
|
+
commit: "3a588e3fa3a3",
|
|
10829
|
+
npm: "@mutmutco/cli@4.0.3"
|
|
10781
10830
|
},
|
|
10782
10831
|
exitCriterion: "fleet-n-of-n",
|
|
10783
10832
|
hubOnlyShortcut: "forbidden",
|
|
@@ -10794,14 +10843,14 @@ var rollout_plan_default = {
|
|
|
10794
10843
|
repo: "mutmutco/mmi-hub",
|
|
10795
10844
|
role: "canary",
|
|
10796
10845
|
schedule: "train",
|
|
10797
|
-
v3Target: "v4.0.
|
|
10846
|
+
v3Target: "v4.0.3"
|
|
10798
10847
|
}
|
|
10799
10848
|
],
|
|
10800
10849
|
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.",
|
|
10801
10850
|
rollback: {
|
|
10802
10851
|
independent: true,
|
|
10803
|
-
mechanism: "npm dist-tag latest -> 4.0.
|
|
10804
|
-
v3Target: "v4.0.
|
|
10852
|
+
mechanism: "npm dist-tag latest -> 4.0.3 and redeploy the Hub Lambda from tag v4.0.3 (3a588e3fa3a3); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
10853
|
+
v3Target: "v4.0.3 (@mutmutco/cli@4.0.3, tag commit 3a588e3fa3a3 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
10805
10854
|
}
|
|
10806
10855
|
},
|
|
10807
10856
|
{
|
|
@@ -17561,6 +17610,7 @@ async function postIssueComment(client, input) {
|
|
|
17561
17610
|
|
|
17562
17611
|
// src/skill-lesson.ts
|
|
17563
17612
|
var SKILL_LESSON_LABEL = "skill-lesson";
|
|
17613
|
+
var SKILL_LESSON_FILE_LABELS = [SKILL_LESSON_LABEL, LEARNING_LABEL];
|
|
17564
17614
|
var SKILL_LESSON_LOOP_KIND = "lesson";
|
|
17565
17615
|
var SKILL_NAMES = ["bootstrap", "browser-automation", "doctor", "epic", "hotfix", "mmi", "onboard", "rcand", "release", "resume", "secrets", "stage"];
|
|
17566
17616
|
function assertSkillName(name) {
|
|
@@ -17593,6 +17643,7 @@ function findDuplicateLesson(source, openLessons) {
|
|
|
17593
17643
|
}
|
|
17594
17644
|
|
|
17595
17645
|
// src/session-identity.ts
|
|
17646
|
+
var import_node_crypto4 = require("node:crypto");
|
|
17596
17647
|
var import_node_os7 = require("node:os");
|
|
17597
17648
|
|
|
17598
17649
|
// src/plugin-guard-io.ts
|
|
@@ -18580,12 +18631,19 @@ async function runPluginHeal(surface = detectSurface(process.env)) {
|
|
|
18580
18631
|
// src/session-identity.ts
|
|
18581
18632
|
var SESSION_ID_ENV_VARS = [
|
|
18582
18633
|
"MMI_SESSION_ID",
|
|
18634
|
+
"MMI_GATE_SESSION_ID",
|
|
18583
18635
|
"CLAUDE_SESSION_ID",
|
|
18584
18636
|
"CLAUDE_CODE_SESSION_ID",
|
|
18585
18637
|
"CODEX_SESSION_ID",
|
|
18638
|
+
"CODEX_THREAD_ID",
|
|
18586
18639
|
"CURSOR_SESSION_ID",
|
|
18640
|
+
"HERMES_SESSION_ID",
|
|
18587
18641
|
"PI_SESSION_ID"
|
|
18588
18642
|
];
|
|
18643
|
+
var SYNTH_SESSION_PREFIX = "synth-";
|
|
18644
|
+
function isHostSessionId(session) {
|
|
18645
|
+
return Boolean(session) && !session.startsWith(SYNTH_SESSION_PREFIX);
|
|
18646
|
+
}
|
|
18589
18647
|
function readSessionId(env) {
|
|
18590
18648
|
for (const key of SESSION_ID_ENV_VARS) {
|
|
18591
18649
|
const value = env[key]?.trim();
|
|
@@ -18593,11 +18651,18 @@ function readSessionId(env) {
|
|
|
18593
18651
|
}
|
|
18594
18652
|
return void 0;
|
|
18595
18653
|
}
|
|
18654
|
+
var synthesizedSessionId;
|
|
18655
|
+
function fallbackSessionId(surface) {
|
|
18656
|
+
if (!synthesizedSessionId) {
|
|
18657
|
+
synthesizedSessionId = `${SYNTH_SESSION_PREFIX}${surface}-${process.pid}-${(0, import_node_crypto4.randomBytes)(3).toString("hex")}`;
|
|
18658
|
+
}
|
|
18659
|
+
return synthesizedSessionId;
|
|
18660
|
+
}
|
|
18596
18661
|
function describeSessionIdentity(env = process.env) {
|
|
18597
|
-
const
|
|
18662
|
+
const surface = detectSurface(env);
|
|
18598
18663
|
return {
|
|
18599
|
-
|
|
18600
|
-
surface
|
|
18664
|
+
session: readSessionId(env) ?? fallbackSessionId(surface),
|
|
18665
|
+
surface,
|
|
18601
18666
|
host: (0, import_node_os7.hostname)()
|
|
18602
18667
|
};
|
|
18603
18668
|
}
|
|
@@ -19865,7 +19930,7 @@ async function postClaimMarkerComment(client, item) {
|
|
|
19865
19930
|
const actor = describeSessionIdentity();
|
|
19866
19931
|
const marker = {
|
|
19867
19932
|
v: 1,
|
|
19868
|
-
|
|
19933
|
+
session: actor.session,
|
|
19869
19934
|
surface: actor.surface,
|
|
19870
19935
|
host: actor.host,
|
|
19871
19936
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -20006,7 +20071,7 @@ function sameClaimHost(a, b) {
|
|
|
20006
20071
|
}
|
|
20007
20072
|
function laneOwnership(marker, current) {
|
|
20008
20073
|
if (!marker) return "unknown";
|
|
20009
|
-
if (marker.session && current.session) {
|
|
20074
|
+
if (isHostSessionId(marker.session) && isHostSessionId(current.session)) {
|
|
20010
20075
|
return marker.session === current.session ? "mine" : "other";
|
|
20011
20076
|
}
|
|
20012
20077
|
if (marker.surface && current.surface && marker.surface !== current.surface) return "other";
|
|
@@ -21682,14 +21747,14 @@ function renderVerifyBroker(input) {
|
|
|
21682
21747
|
}
|
|
21683
21748
|
|
|
21684
21749
|
// src/tenant-artifact.ts
|
|
21685
|
-
var
|
|
21750
|
+
var import_node_crypto5 = require("node:crypto");
|
|
21686
21751
|
var import_node_fs22 = require("node:fs");
|
|
21687
21752
|
var import_promises3 = require("node:fs/promises");
|
|
21688
21753
|
var import_node_path20 = require("node:path");
|
|
21689
21754
|
var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
21690
21755
|
var MAX_BYTES = 5 * 1024 * 1024 * 1024;
|
|
21691
21756
|
async function sha256File(path2) {
|
|
21692
|
-
const hash = (0,
|
|
21757
|
+
const hash = (0, import_node_crypto5.createHash)("sha256");
|
|
21693
21758
|
for await (const chunk of (0, import_node_fs22.createReadStream)(path2)) hash.update(chunk);
|
|
21694
21759
|
return hash.digest("hex");
|
|
21695
21760
|
}
|
|
@@ -22829,7 +22894,7 @@ async function announceRelease(deps, args) {
|
|
|
22829
22894
|
}
|
|
22830
22895
|
|
|
22831
22896
|
// src/repo-index.ts
|
|
22832
|
-
var
|
|
22897
|
+
var import_node_crypto6 = require("node:crypto");
|
|
22833
22898
|
var import_node_child_process12 = require("node:child_process");
|
|
22834
22899
|
var import_node_fs23 = require("node:fs");
|
|
22835
22900
|
var import_node_path21 = require("node:path");
|
|
@@ -23019,7 +23084,7 @@ function rebuildRepoIndex(cwd, repoSlug3) {
|
|
|
23019
23084
|
continue;
|
|
23020
23085
|
}
|
|
23021
23086
|
if (text.length > 15e5) continue;
|
|
23022
|
-
const hash = (0,
|
|
23087
|
+
const hash = (0, import_node_crypto6.createHash)("sha256").update(text).digest("hex").slice(0, 16);
|
|
23023
23088
|
const symbols = extractSymbols(text);
|
|
23024
23089
|
const docBlurb = extractModuleBlurb(text);
|
|
23025
23090
|
const top = rel.includes("/") ? rel.split("/")[0] : "";
|
|
@@ -23124,16 +23189,16 @@ function inferRepoSlug(cwd, exec = import_node_child_process12.execFileSync) {
|
|
|
23124
23189
|
}
|
|
23125
23190
|
|
|
23126
23191
|
// src/repo-index-cloud-client.ts
|
|
23127
|
-
var
|
|
23192
|
+
var import_node_crypto9 = require("node:crypto");
|
|
23128
23193
|
|
|
23129
23194
|
// src/repo-index-v4/builder.ts
|
|
23130
|
-
var
|
|
23195
|
+
var import_node_crypto8 = require("node:crypto");
|
|
23131
23196
|
var import_node_child_process13 = require("node:child_process");
|
|
23132
23197
|
var import_node_fs25 = require("node:fs");
|
|
23133
23198
|
var import_node_path23 = require("node:path");
|
|
23134
23199
|
|
|
23135
23200
|
// src/repo-index-v4/chunks.ts
|
|
23136
|
-
var
|
|
23201
|
+
var import_node_crypto7 = require("node:crypto");
|
|
23137
23202
|
var import_node_fs24 = require("node:fs");
|
|
23138
23203
|
var import_node_path22 = require("node:path");
|
|
23139
23204
|
|
|
@@ -23200,7 +23265,7 @@ async function parserFor(language) {
|
|
|
23200
23265
|
return parser;
|
|
23201
23266
|
}
|
|
23202
23267
|
function sha256(value) {
|
|
23203
|
-
return (0,
|
|
23268
|
+
return (0, import_node_crypto7.createHash)("sha256").update(value).digest("hex");
|
|
23204
23269
|
}
|
|
23205
23270
|
function pointerId(path2, kind, contentHash, start, end, symbol) {
|
|
23206
23271
|
return sha256(["repo-index-v4", path2, kind, contentHash, String(start), String(end), symbol ?? ""].join("\0"));
|
|
@@ -23320,7 +23385,7 @@ function canonicalJson(value) {
|
|
|
23320
23385
|
return `{${Object.keys(record).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson(record[k])}`).join(",")}}`;
|
|
23321
23386
|
}
|
|
23322
23387
|
function sha2562(value) {
|
|
23323
|
-
return (0,
|
|
23388
|
+
return (0, import_node_crypto8.createHash)("sha256").update(canonicalJson(value)).digest("hex");
|
|
23324
23389
|
}
|
|
23325
23390
|
function quantizeV4Vector(vector) {
|
|
23326
23391
|
return vector.map((value) => Number(value.toFixed(V4_VECTOR_DECIMAL_PLACES)));
|
|
@@ -23516,16 +23581,23 @@ async function probeRepoIndexV4ReadinessCloud(queries, deps) {
|
|
|
23516
23581
|
if (!token) return { ok: false, error: "no Hub session token (run `gh auth login`)" };
|
|
23517
23582
|
const baseUrl = deps.baseUrl.replace(/\/$/, "");
|
|
23518
23583
|
const headers = { ...clientVersionHeaders(), Authorization: ["Bearer", token].join(" "), "content-type": "application/json" };
|
|
23519
|
-
|
|
23584
|
+
const shadowPass = async () => {
|
|
23520
23585
|
for (const query of queries) {
|
|
23521
|
-
const
|
|
23586
|
+
const res = await fetchWithRetry(deps.fetch ?? fetch, `${baseUrl}/repo-index/v4/shadow`, {
|
|
23522
23587
|
method: "POST",
|
|
23523
23588
|
headers,
|
|
23524
23589
|
body: JSON.stringify({ q: query.query, mode: query.mode, readinessProbe: true })
|
|
23525
23590
|
}, { attempts: RETRY_ATTEMPTS2, timeoutMs: 12e4, sleep: deps.retrySleep });
|
|
23526
|
-
const
|
|
23527
|
-
if (!
|
|
23591
|
+
const body = await res.json().catch(() => ({}));
|
|
23592
|
+
if (!res.ok) return { ok: false, error: `${query.id}: ${body.error ?? `v4 readiness probe HTTP ${res.status}`}${body.errorClass ? ` (${body.errorClass})` : ""}`, status: res.status };
|
|
23528
23593
|
}
|
|
23594
|
+
return { ok: true };
|
|
23595
|
+
};
|
|
23596
|
+
try {
|
|
23597
|
+
const warmup = await shadowPass();
|
|
23598
|
+
if (!warmup.ok) return warmup;
|
|
23599
|
+
const verdict = await shadowPass();
|
|
23600
|
+
if (!verdict.ok) return verdict;
|
|
23529
23601
|
const res = await fetchWithRetry(deps.fetch ?? fetch, `${baseUrl}/repo-index/status`, {
|
|
23530
23602
|
method: "GET",
|
|
23531
23603
|
headers: { ...clientVersionHeaders(), Authorization: ["Bearer", token].join(" ") }
|
|
@@ -23534,8 +23606,8 @@ async function probeRepoIndexV4ReadinessCloud(queries, deps) {
|
|
|
23534
23606
|
if (!res.ok) return { ok: false, error: body.error ?? `v4 readiness status HTTP ${res.status}`, status: res.status };
|
|
23535
23607
|
const readiness = body.v4Readiness;
|
|
23536
23608
|
const complete = !!readiness && ["ready", "not-ready"].includes(readiness.verdict) && Array.isArray(readiness.reasons) && Number.isFinite(readiness.goldenCount) && Number.isFinite(readiness.evidenceCount) && !!readiness.modes && ["lexical", "semantic", "hybrid"].every((mode) => {
|
|
23537
|
-
const
|
|
23538
|
-
return
|
|
23609
|
+
const verdict2 = readiness.modes[mode]?.verdict;
|
|
23610
|
+
return verdict2 === "ready" || verdict2 === "not-ready";
|
|
23539
23611
|
});
|
|
23540
23612
|
if (!complete) return { ok: false, error: "Hub status omitted the explicit v4 readiness verdict/reasons/modes contract", status: res.status };
|
|
23541
23613
|
return { ok: true, readiness };
|
|
@@ -23640,7 +23712,7 @@ async function searchRepoIndexCloud(query, opts, deps) {
|
|
|
23640
23712
|
const body = await res.json().catch(() => ({}));
|
|
23641
23713
|
if (!res.ok) return { ok: false, error: body.error ?? `search HTTP ${res.status}`, status: res.status };
|
|
23642
23714
|
const shadowRate = Math.max(0, Math.min(1, Number(process.env.MMI_REPO_INDEX_V4_SHADOW_RATE ?? 0.1) || 0));
|
|
23643
|
-
if ((0,
|
|
23715
|
+
if ((0, import_node_crypto9.createHash)("sha256").update(query).digest()[0] / 256 < shadowRate) {
|
|
23644
23716
|
void fetchWithRetry(
|
|
23645
23717
|
deps.fetch ?? fetch,
|
|
23646
23718
|
`${deps.baseUrl.replace(/\/$/, "")}/repo-index/v4/shadow`,
|
|
@@ -25979,7 +26051,7 @@ function registerSecretsCommands(program3) {
|
|
|
25979
26051
|
}
|
|
25980
26052
|
|
|
25981
26053
|
// src/app-actor.ts
|
|
25982
|
-
var
|
|
26054
|
+
var import_node_crypto10 = require("node:crypto");
|
|
25983
26055
|
var APP_ACTOR_ENV = "MMI_ACTOR";
|
|
25984
26056
|
var APP_VAULT_REPO = "mutmutco/MMI-Hub";
|
|
25985
26057
|
var APP_VAULT_KEYS = ["GITHUB_APP_ID", "GITHUB_APP_INSTALLATION_ID", "GITHUB_APP_PRIVATE_KEY"];
|
|
@@ -26023,7 +26095,7 @@ function mintAppJwt(appId, privateKeyPem, nowSec) {
|
|
|
26023
26095
|
exp: now + APP_JWT_TTL_S,
|
|
26024
26096
|
iss: appId
|
|
26025
26097
|
}));
|
|
26026
|
-
const signer = (0,
|
|
26098
|
+
const signer = (0, import_node_crypto10.createSign)("RSA-SHA256");
|
|
26027
26099
|
signer.update(`${header}.${payload}`);
|
|
26028
26100
|
return `${header}.${payload}.${signer.sign(privateKeyPem, "base64url")}`;
|
|
26029
26101
|
}
|
|
@@ -26508,12 +26580,20 @@ async function readOrFail(read) {
|
|
|
26508
26580
|
async function defaultRegistryRead() {
|
|
26509
26581
|
return fetchSchedulesList(registryClientDeps(await loadConfig()));
|
|
26510
26582
|
}
|
|
26511
|
-
async function
|
|
26512
|
-
|
|
26583
|
+
async function defaultRunVerdictRead() {
|
|
26584
|
+
return fetchRunVerdicts(registryClientDeps(await loadConfig()));
|
|
26585
|
+
}
|
|
26586
|
+
function modelRoleKeyOf(row) {
|
|
26587
|
+
const model = row.model;
|
|
26588
|
+
return typeof model === "string" && model.trim() ? model : void 0;
|
|
26589
|
+
}
|
|
26590
|
+
async function fetchNotebook(client = defaultGitHubClient(), readRegistry = defaultRegistryRead, readProjects = defaultProjectsRead, readRunVerdicts = defaultRunVerdictRead) {
|
|
26591
|
+
const [gh, aws, registryRead, projectsRead, runVerdictRead] = await Promise.all([
|
|
26513
26592
|
githubEntries(client),
|
|
26514
26593
|
awsEntries(),
|
|
26515
26594
|
readOrFail(readRegistry),
|
|
26516
|
-
readOrFail(readProjects)
|
|
26595
|
+
readOrFail(readProjects),
|
|
26596
|
+
readOrFail(readRunVerdicts)
|
|
26517
26597
|
]);
|
|
26518
26598
|
const readFailures = [];
|
|
26519
26599
|
if (registryRead.state === "failed") {
|
|
@@ -26523,10 +26603,16 @@ async function fetchNotebook(client = defaultGitHubClient(), readRegistry = defa
|
|
|
26523
26603
|
readFailures.push(`registry: could not read the projects list \u2014 schedulesMode opt-outs unresolved (nothing suppressed) \u2014 ${projectsRead.error}`);
|
|
26524
26604
|
}
|
|
26525
26605
|
const registry2 = registryRead.state === "ok" ? registryRead.value : null;
|
|
26606
|
+
const runVerdicts = runVerdictRead.state === "ok" ? runVerdictRead.value : null;
|
|
26526
26607
|
const projects = projectsRead.state === "ok" ? projectsRead.value : null;
|
|
26608
|
+
const decoratedAws = decorateAwsModel(
|
|
26609
|
+
aws.entries,
|
|
26610
|
+
registry2?.map((r) => ({ id: r.id, executor: r.executor, model: modelRoleKeyOf(r) })) ?? null,
|
|
26611
|
+
runVerdicts
|
|
26612
|
+
);
|
|
26527
26613
|
const recon = assembleReconciliation(gh.entries, new Set(gh.readRepos), registry2, new Set(gh.workflowNames), {
|
|
26528
26614
|
// #3286: the harbour side joins registry rows against the live aws-scheduler clocks by scheduleId.
|
|
26529
|
-
awsEntries:
|
|
26615
|
+
awsEntries: decoratedAws,
|
|
26530
26616
|
schedulerRead: Boolean(aws.schedulerRead)
|
|
26531
26617
|
}, new Set(gh.disabledWorkflowNames));
|
|
26532
26618
|
const selfManaged = /* @__PURE__ */ new Set();
|
|
@@ -26540,7 +26626,7 @@ async function fetchNotebook(client = defaultGitHubClient(), readRegistry = defa
|
|
|
26540
26626
|
);
|
|
26541
26627
|
const driftLines = reconciliation.map(renderDrift);
|
|
26542
26628
|
return {
|
|
26543
|
-
entries: sortEntries([...gh.entries, ...
|
|
26629
|
+
entries: sortEntries([...gh.entries, ...decoratedAws, ...DECLARED_ENTRIES]),
|
|
26544
26630
|
incomplete: [...gh.incomplete, ...aws.incomplete, ...recon.incomplete, ...readFailures],
|
|
26545
26631
|
drift: driftLines,
|
|
26546
26632
|
reconciliation,
|
|
@@ -26937,7 +27023,7 @@ var import_node_os14 = require("node:os");
|
|
|
26937
27023
|
var import_node_path32 = require("node:path");
|
|
26938
27024
|
|
|
26939
27025
|
// src/bootstrap-drift.ts
|
|
26940
|
-
var
|
|
27026
|
+
var import_node_crypto11 = require("node:crypto");
|
|
26941
27027
|
function byteComparableSeeds(manifest, cls) {
|
|
26942
27028
|
return manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self" && s.classes.includes(cls));
|
|
26943
27029
|
}
|
|
@@ -26957,7 +27043,7 @@ function compareSeedBytes(hubContent, repoContent) {
|
|
|
26957
27043
|
return normalize(hubContent) === normalize(repoContent) ? "match" : "drift";
|
|
26958
27044
|
}
|
|
26959
27045
|
function seedContentHash(content) {
|
|
26960
|
-
return (0,
|
|
27046
|
+
return (0, import_node_crypto11.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
|
|
26961
27047
|
}
|
|
26962
27048
|
function auditRepoSeedDrift(repo, seeds, hubContents, repoReads) {
|
|
26963
27049
|
const byTarget = new Map(repoReads.map((r) => [r.target, r.content]));
|
|
@@ -27120,7 +27206,7 @@ function renderPropagationReport(plan) {
|
|
|
27120
27206
|
}
|
|
27121
27207
|
|
|
27122
27208
|
// src/bootstrap-propagation-identity.ts
|
|
27123
|
-
var
|
|
27209
|
+
var import_node_crypto12 = require("node:crypto");
|
|
27124
27210
|
var PROPAGATION_BRANCH_PREFIX = "seed-propagate-";
|
|
27125
27211
|
var TARGET_MARKER_NAME = "mmi-bootstrap-propagation-target";
|
|
27126
27212
|
function safeBranchPart(value, maxLength, fallback) {
|
|
@@ -27133,7 +27219,7 @@ function repoSlug2(repo) {
|
|
|
27133
27219
|
function propagationBranch(repo, target) {
|
|
27134
27220
|
const repoPart = safeBranchPart(repoSlug2(repo), 32, "repo");
|
|
27135
27221
|
const targetPart = safeBranchPart(target, 48, "target");
|
|
27136
|
-
const hash = (0,
|
|
27222
|
+
const hash = (0, import_node_crypto12.createHash)("sha256").update(repo.trim().toLowerCase()).update("\0").update(target).digest("hex").slice(0, 12);
|
|
27137
27223
|
return `${PROPAGATION_BRANCH_PREFIX}${repoPart}-${targetPart}-${hash}`;
|
|
27138
27224
|
}
|
|
27139
27225
|
function legacyPropagationBranch(repo) {
|
|
@@ -29673,7 +29759,7 @@ function registerBoardCommands(program3) {
|
|
|
29673
29759
|
const board = program3.command("board").description("read, claim, show, and move Project v2 work items for the current repo");
|
|
29674
29760
|
board.command("read", { isDefault: true }).description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").addHelpText("after", "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--allow-partial applies to the paginated path and detail reads.\n").action((o) => runBoardRead(o));
|
|
29675
29761
|
withExamples(mutating(
|
|
29676
|
-
board.command("claim <issues...>").description("claim issues: assign them and move their Project v2 Status to In Progress \u2014 idempotent, so an item already yours and In Progress succeeds unchanged (one or more refs)").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo for local issue numbers (defaults to git origin)").option("--for <login>", "assign to this login instead of @me \u2014 agent claims on behalf of the master").option("--force", "take an item already claimed by another lane that shows live evidence of active work (#3727)").option("--check", "read-only: run every claim gate and report the verdict, writing nothing \u2014 exits 1 with the same refusal a real claim would raise (#4511)").option("--allow-partial", "return success JSON if assignment succeeds but the status move fails"),
|
|
29762
|
+
board.command("claim <issues...>").description("claim issues: assign them and move their Project v2 Status to In Progress \u2014 idempotent, so an item already yours and In Progress succeeds unchanged (one or more refs)").addHelpText("after", "\nevery claim stamps a lane-identity marker comment on the issue (`<!-- mmi-claim: \u2026 -->`,\nsurface/session@host) so other agents can attribute the hold (#3727). The session is the\nhost-exported id when the surface provides one, otherwise a per-process `synth-` fallback \u2014\na claim is never anonymous (#5245). `board show`, doctor and unclaim read the latest marker.\n").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo for local issue numbers (defaults to git origin)").option("--for <login>", "assign to this login instead of @me \u2014 agent claims on behalf of the master").option("--force", "take an item already claimed by another lane that shows live evidence of active work (#3727)").option("--check", "read-only: run every claim gate and report the verdict, writing nothing \u2014 exits 1 with the same refusal a real claim would raise (#4511)").option("--allow-partial", "return success JSON if assignment succeeds but the status move fails"),
|
|
29677
29763
|
(_opts, args) => ({ command: "board claim", issues: args[0] ?? [] })
|
|
29678
29764
|
).action(async (issueRefs, o) => {
|
|
29679
29765
|
if (issueRefs.length === 1) {
|
|
@@ -30294,7 +30380,7 @@ async function checkDocsIndexAtHead(opts, deps) {
|
|
|
30294
30380
|
|
|
30295
30381
|
// src/issue-commands.ts
|
|
30296
30382
|
var import_node_fs38 = require("node:fs");
|
|
30297
|
-
var
|
|
30383
|
+
var import_node_crypto13 = require("node:crypto");
|
|
30298
30384
|
var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
|
|
30299
30385
|
var ReparentConflictError = class extends Error {
|
|
30300
30386
|
constructor(message, payload) {
|
|
@@ -30585,7 +30671,7 @@ function rowIdempotencyKey(batchKey, spec) {
|
|
|
30585
30671
|
const identity = `${spec.type}
|
|
30586
30672
|
${spec.title.trim()}
|
|
30587
30673
|
${spec.body ?? ""}`;
|
|
30588
|
-
const hash = (0,
|
|
30674
|
+
const hash = (0, import_node_crypto13.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
|
|
30589
30675
|
return `${batchKey}:${hash}`;
|
|
30590
30676
|
}
|
|
30591
30677
|
var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
|
|
@@ -31217,7 +31303,23 @@ async function collectStatus() {
|
|
|
31217
31303
|
} catch {
|
|
31218
31304
|
stage = { running: false };
|
|
31219
31305
|
}
|
|
31220
|
-
|
|
31306
|
+
let releaseTrack;
|
|
31307
|
+
let branches;
|
|
31308
|
+
try {
|
|
31309
|
+
const cfg = await loadConfigOrDiscover();
|
|
31310
|
+
if (cfg.sagaApiUrl) {
|
|
31311
|
+
const slug = await repoSlug();
|
|
31312
|
+
const deps = registryClientDeps(cfg);
|
|
31313
|
+
const read = await fetchProjectBySlugChecked(slug, deps);
|
|
31314
|
+
if (read.ok && read.project) {
|
|
31315
|
+
const trackRepo = repo ?? `mutmutco/${slug}`;
|
|
31316
|
+
releaseTrack = resolveReleaseTrack(read.project, void 0, trackRepo);
|
|
31317
|
+
branches = branchesForTrack(releaseTrack);
|
|
31318
|
+
}
|
|
31319
|
+
}
|
|
31320
|
+
} catch {
|
|
31321
|
+
}
|
|
31322
|
+
return { repo, branch, releaseTrack, branches, worktrees, myOpenPrs, claimedItems, stage };
|
|
31221
31323
|
}
|
|
31222
31324
|
var PRIORITY_RANK = {
|
|
31223
31325
|
Urgent: 0,
|
|
@@ -31362,6 +31464,9 @@ function registerDiscoveryCommands(program3) {
|
|
|
31362
31464
|
} else {
|
|
31363
31465
|
console.log(`repo: ${report.repo ?? "unknown"}`);
|
|
31364
31466
|
console.log(`branch: ${report.branch}`);
|
|
31467
|
+
if (report.releaseTrack) {
|
|
31468
|
+
console.log(`release track: ${report.releaseTrack} (${(report.branches ?? []).join(" -> ")})`);
|
|
31469
|
+
}
|
|
31365
31470
|
console.log(`worktrees: ${report.worktrees.length} linked`);
|
|
31366
31471
|
console.log(`my open PRs: ${report.myOpenPrs.length}`);
|
|
31367
31472
|
console.log(`claimed board items: ${report.claimedItems.length}`);
|
|
@@ -37502,7 +37607,7 @@ program2.command("skill-lesson").description("file a skill-lesson on the Hub boa
|
|
|
37502
37607
|
title = buildSkillLessonTitle(skill, rawTitle);
|
|
37503
37608
|
priority = resolveCreatePriority(o.priority, "skill-lesson");
|
|
37504
37609
|
body = buildSkillLessonBody(rawBody, sourceRepo, pluginSha);
|
|
37505
|
-
args = buildIssueArgs({ type: "task", title, body, priority, repo: targetRepo3, labels: [
|
|
37610
|
+
args = buildIssueArgs({ type: "task", title, body, priority, repo: targetRepo3, labels: [...SKILL_LESSON_FILE_LABELS] });
|
|
37506
37611
|
} catch (e) {
|
|
37507
37612
|
return fail(`skill-lesson: ${e.message}`);
|
|
37508
37613
|
}
|
|
@@ -37544,9 +37649,11 @@ program2.command("skill-lesson").description("file a skill-lesson on the Hub boa
|
|
|
37544
37649
|
});
|
|
37545
37650
|
if (surfaceWarn) process.stderr.write(`${surfaceWarn}
|
|
37546
37651
|
`);
|
|
37547
|
-
|
|
37548
|
-
|
|
37549
|
-
|
|
37652
|
+
for (const [label, color] of [[SKILL_LESSON_LABEL, "c2e0c6"], [LEARNING_LABEL, "ededed"]]) {
|
|
37653
|
+
try {
|
|
37654
|
+
await execFileP2("gh", ["label", "create", label, "--color", color, "--repo", targetRepo3], { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
37655
|
+
} catch {
|
|
37656
|
+
}
|
|
37550
37657
|
}
|
|
37551
37658
|
const created = await ghCreate(args);
|
|
37552
37659
|
const { projectItemId, onBoard } = await attachToProject(created.number, targetRepo3, priority);
|
package/package.json
CHANGED