@mutmutco/cli 4.5.26 → 4.5.28
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 +98 -17
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -5491,7 +5491,8 @@ async function fetchHubBoardSnapshot(request, deps) {
|
|
|
5491
5491
|
...request.statusFieldId ? { statusFieldId: request.statusFieldId } : {},
|
|
5492
5492
|
activeOnly: request.activeOnly !== false,
|
|
5493
5493
|
allowPartial: request.allowPartial === true,
|
|
5494
|
-
includePullRequests: request.includePullRequests !== false
|
|
5494
|
+
includePullRequests: request.includePullRequests !== false,
|
|
5495
|
+
...request.minCursor !== void 0 ? { minCursor: request.minCursor } : {}
|
|
5495
5496
|
})
|
|
5496
5497
|
},
|
|
5497
5498
|
{ attempts: 3, timeoutMs: deps.timeoutMs ?? BOARD_SNAPSHOT_TIMEOUT_MS, sleep: deps.retrySleep }
|
|
@@ -12472,7 +12473,8 @@ async function collectBoardPreferringSnapshot(cfg, options, deps) {
|
|
|
12472
12473
|
number: cfg.projectNumber,
|
|
12473
12474
|
statusFieldId: cfg.statusFieldId,
|
|
12474
12475
|
activeOnly: true,
|
|
12475
|
-
allowPartial: options.allowPartial === true
|
|
12476
|
+
allowPartial: options.allowPartial === true,
|
|
12477
|
+
...options.minCursor !== void 0 ? { minCursor: options.minCursor } : {}
|
|
12476
12478
|
},
|
|
12477
12479
|
deps.snapshot
|
|
12478
12480
|
) : void 0;
|
|
@@ -16816,10 +16818,10 @@ var rollout_plan_default = {
|
|
|
16816
16818
|
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)."
|
|
16817
16819
|
},
|
|
16818
16820
|
baseline: {
|
|
16819
|
-
version: "4.5.
|
|
16820
|
-
tag: "v4.5.
|
|
16821
|
-
commit: "
|
|
16822
|
-
npm: "@mutmutco/cli@4.5.
|
|
16821
|
+
version: "4.5.28",
|
|
16822
|
+
tag: "v4.5.28",
|
|
16823
|
+
commit: "7b61e100e4df",
|
|
16824
|
+
npm: "@mutmutco/cli@4.5.28"
|
|
16823
16825
|
},
|
|
16824
16826
|
exitCriterion: "fleet-n-of-n",
|
|
16825
16827
|
hubOnlyShortcut: "forbidden",
|
|
@@ -16836,14 +16838,14 @@ var rollout_plan_default = {
|
|
|
16836
16838
|
repo: "mutmutco/mmi-hub",
|
|
16837
16839
|
role: "canary",
|
|
16838
16840
|
schedule: "train",
|
|
16839
|
-
v3Target: "v4.5.
|
|
16841
|
+
v3Target: "v4.5.28"
|
|
16840
16842
|
}
|
|
16841
16843
|
],
|
|
16842
16844
|
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.",
|
|
16843
16845
|
rollback: {
|
|
16844
16846
|
independent: true,
|
|
16845
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
16846
|
-
v3Target: "v4.5.
|
|
16847
|
+
mechanism: "npm dist-tag latest -> 4.5.28 and redeploy the Hub Lambda from tag v4.5.28 (7b61e100e4df); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
16848
|
+
v3Target: "v4.5.28 (@mutmutco/cli@4.5.28, tag commit 7b61e100e4df \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
16847
16849
|
}
|
|
16848
16850
|
},
|
|
16849
16851
|
{
|
|
@@ -20061,7 +20063,7 @@ async function appendMmiGetRedeploy(deps, input, dispatch) {
|
|
|
20061
20063
|
workflowRuns: [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: "success" }]
|
|
20062
20064
|
};
|
|
20063
20065
|
}
|
|
20064
|
-
async function appendServedChannelPublish(deps, input, dispatch) {
|
|
20066
|
+
async function appendServedChannelPublish(deps, input, dispatch, redeployed) {
|
|
20065
20067
|
if (!isHubControlRepo(input.repo)) return dispatch;
|
|
20066
20068
|
const channelUrl = input.channelUrl ?? readServedChannelUrl();
|
|
20067
20069
|
const remedy = getPublishDispatchCommand(input.repo);
|
|
@@ -20111,7 +20113,7 @@ async function appendServedChannelPublish(deps, input, dispatch) {
|
|
|
20111
20113
|
deployStatus: "failure"
|
|
20112
20114
|
};
|
|
20113
20115
|
}
|
|
20114
|
-
const expected = (deps.readLauncherVersion ?? readLocalLauncherVersion)();
|
|
20116
|
+
const expected = input.tag ? input.tag.replace(/^v/, "") : (deps.readLauncherVersion ?? readLocalLauncherVersion)();
|
|
20115
20117
|
const runs = [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: "success" }];
|
|
20116
20118
|
if (!channelUrl) {
|
|
20117
20119
|
return {
|
|
@@ -20120,6 +20122,7 @@ async function appendServedChannelPublish(deps, input, dispatch) {
|
|
|
20120
20122
|
workflowRuns: runs
|
|
20121
20123
|
};
|
|
20122
20124
|
}
|
|
20125
|
+
if (redeployed) await redeployed;
|
|
20123
20126
|
const probe = await (deps.readServedChannelVersion ?? fetchServedChannelVersion)(channelUrl);
|
|
20124
20127
|
const evidence = (conclusion, text) => [...runs, servedChannelRow(HUB_SERVED_PRODUCT, conclusion, text)];
|
|
20125
20128
|
if (!probe.ok && staleServedProcess(probe)) {
|
|
@@ -20271,12 +20274,31 @@ async function appendInstallHomePublish(deps, input, dispatch) {
|
|
|
20271
20274
|
workflowRuns: [...dispatch.workflowRuns ?? [], { ...row, conclusion: "success" }]
|
|
20272
20275
|
};
|
|
20273
20276
|
}
|
|
20277
|
+
function mergeServedLegs(base, first, second) {
|
|
20278
|
+
if (first === base) return second;
|
|
20279
|
+
if (second === base) return first;
|
|
20280
|
+
const priorRows = base.workflowRuns?.length ?? 0;
|
|
20281
|
+
const rows = [...(first.workflowRuns ?? []).slice(priorRows), ...(second.workflowRuns ?? []).slice(priorRows)];
|
|
20282
|
+
return {
|
|
20283
|
+
...base,
|
|
20284
|
+
note: base.note + first.note.slice(base.note.length) + second.note.slice(base.note.length),
|
|
20285
|
+
...rows.length ? { workflowRuns: [...base.workflowRuns ?? [], ...rows] } : {},
|
|
20286
|
+
...first.deployStatus === "failure" || second.deployStatus === "failure" ? { deployStatus: "failure" } : {}
|
|
20287
|
+
};
|
|
20288
|
+
}
|
|
20274
20289
|
async function appendServedChannelLegs(deps, input, dispatch) {
|
|
20275
20290
|
const publishStatus = input.publish?.state === "complete" ? "success" : "failure";
|
|
20276
|
-
|
|
20277
|
-
|
|
20278
|
-
|
|
20279
|
-
|
|
20291
|
+
const redeployed = appendMmiGetRedeploy(deps, { repo: input.repo, publishStatus, watch: input.watch, releaseSha: input.releaseSha }, dispatch);
|
|
20292
|
+
const [box, channel] = await Promise.all([
|
|
20293
|
+
redeployed,
|
|
20294
|
+
appendServedChannelPublish(deps, { repo: input.repo, publishStatus, watch: input.watch, releaseSha: input.releaseSha, tag: input.tag }, dispatch, redeployed)
|
|
20295
|
+
]);
|
|
20296
|
+
const out = mergeServedLegs(dispatch, box, channel);
|
|
20297
|
+
const [home, proof] = await Promise.all([
|
|
20298
|
+
appendInstallHomePublish(deps, { repo: input.repo, installer: input.installer, publishStatus, watch: input.watch }, out),
|
|
20299
|
+
appendServedChannelProof(deps, { repo: input.repo, tag: input.tag, shipped: shipLegsGreen(input.deploy, input.publish), installer: input.installer }, out)
|
|
20300
|
+
]);
|
|
20301
|
+
return mergeServedLegs(out, home, proof);
|
|
20280
20302
|
}
|
|
20281
20303
|
function servedChannelRowsAfter(before, after) {
|
|
20282
20304
|
return (after.workflowRuns ?? []).slice((before.workflowRuns ?? []).length);
|
|
@@ -28769,6 +28791,50 @@ async function attachBoardIssues(selectors, repoOption, deps = {}) {
|
|
|
28769
28791
|
// src/board-commands.ts
|
|
28770
28792
|
init_clean_exit();
|
|
28771
28793
|
init_error_codes();
|
|
28794
|
+
|
|
28795
|
+
// src/board-events.ts
|
|
28796
|
+
init_fetch_retry();
|
|
28797
|
+
init_client_version();
|
|
28798
|
+
var BOARD_EVENTS_POLL_MS = 2e3;
|
|
28799
|
+
var BOARD_EVENTS_MAX_WAIT_S = 120;
|
|
28800
|
+
function isPage(b) {
|
|
28801
|
+
const p = b;
|
|
28802
|
+
return Boolean(p && Number.isInteger(p.cursor) && Array.isArray(p.events) && typeof p.resync === "boolean" && typeof p.more === "boolean");
|
|
28803
|
+
}
|
|
28804
|
+
async function readBoardEvents(since, deps) {
|
|
28805
|
+
if (!deps.baseUrl) throw new Error("no Hub API URL (set MMI_HUB_URL or use a current MMI CLI build)");
|
|
28806
|
+
const token = await deps.token();
|
|
28807
|
+
if (!token) throw new Error("no Hub session token (run `gh auth login`)");
|
|
28808
|
+
const query = since === void 0 ? "" : `?since=${since}`;
|
|
28809
|
+
const res = await fetchWithRetry(
|
|
28810
|
+
deps.fetch ?? fetch,
|
|
28811
|
+
`${deps.baseUrl.replace(/\/$/, "")}/board/events${query}`,
|
|
28812
|
+
{ method: "GET", headers: { ...clientVersionHeaders(), Authorization: `Bearer ${token}` } },
|
|
28813
|
+
{ attempts: 3, timeoutMs: 1e4 }
|
|
28814
|
+
);
|
|
28815
|
+
if (res.status === 426) throw new Error(upgradeRequiredError(res, await res.json().catch(() => null)));
|
|
28816
|
+
const body = await res.json().catch(() => null);
|
|
28817
|
+
if (!res.ok) {
|
|
28818
|
+
const detail = body?.error;
|
|
28819
|
+
throw new Error(`board events HTTP ${res.status}${detail ? `: ${detail}` : ""}`);
|
|
28820
|
+
}
|
|
28821
|
+
if (!isPage(body)) throw new Error("malformed board events response");
|
|
28822
|
+
return body;
|
|
28823
|
+
}
|
|
28824
|
+
async function waitBoardEvents(since, waitS, deps) {
|
|
28825
|
+
const now = deps.now ?? Date.now;
|
|
28826
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
28827
|
+
const deadline = now() + Math.min(Math.max(waitS, 0), BOARD_EVENTS_MAX_WAIT_S) * 1e3;
|
|
28828
|
+
let cursor = since;
|
|
28829
|
+
for (; ; ) {
|
|
28830
|
+
const page = await readBoardEvents(cursor, deps);
|
|
28831
|
+
if (page.events.length || page.resync || page.more || now() + BOARD_EVENTS_POLL_MS > deadline) return page;
|
|
28832
|
+
cursor = page.cursor;
|
|
28833
|
+
await sleep2(BOARD_EVENTS_POLL_MS);
|
|
28834
|
+
}
|
|
28835
|
+
}
|
|
28836
|
+
|
|
28837
|
+
// src/board-commands.ts
|
|
28772
28838
|
function refuseRateLimited(e, json) {
|
|
28773
28839
|
if (!isRateLimitedError(e)) return false;
|
|
28774
28840
|
if (json) console.log(JSON.stringify(e.receipt));
|
|
@@ -28801,6 +28867,8 @@ function registerBoardCommands(program3) {
|
|
|
28801
28867
|
}
|
|
28802
28868
|
async function runBoardRead(o) {
|
|
28803
28869
|
try {
|
|
28870
|
+
if (o.minCursor !== void 0 && !/^\d{1,15}$/.test(o.minCursor)) throw new Error("--min-cursor must be a non-negative integer board-event cursor");
|
|
28871
|
+
if (o.minCursor !== void 0 && o.direct) throw new Error("--min-cursor applies to the Hub snapshot and cannot be combined with --direct");
|
|
28804
28872
|
const config = await loadConfigForRepo(o.repo);
|
|
28805
28873
|
const report = await readBoard({
|
|
28806
28874
|
config,
|
|
@@ -28808,7 +28876,8 @@ function registerBoardCommands(program3) {
|
|
|
28808
28876
|
includeBundleDetails: o.bundleDetails,
|
|
28809
28877
|
includeAllBodies: o.bodies,
|
|
28810
28878
|
allowPartial: o.allowPartial,
|
|
28811
|
-
snapshotOnly: o.snapshotOnly
|
|
28879
|
+
snapshotOnly: o.snapshotOnly,
|
|
28880
|
+
...o.minCursor !== void 0 ? { minCursor: Number(o.minCursor) } : {}
|
|
28812
28881
|
}, o.direct ? {} : { snapshot: registryClientDeps(config) });
|
|
28813
28882
|
const output = o.json ? JSON.stringify(report) : renderBoardReport(report);
|
|
28814
28883
|
if (!o.out) console.log(output);
|
|
@@ -28851,7 +28920,19 @@ function registerBoardCommands(program3) {
|
|
|
28851
28920
|
return `Claimed ${ref} for ${holder} - In Progress${reclaimed}`;
|
|
28852
28921
|
}
|
|
28853
28922
|
const board = program3.command("board").description("read, claim, show, and move Project v2 work items for the current repo");
|
|
28854
|
-
board.command("read", { isDefault: true }).alias("list").description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--direct", "bypass the Hub snapshot and read the live board through direct GitHub GraphQL").option("--snapshot-only", "fail instead of falling back to your personal GraphQL pool when the Hub snapshot is unavailable \u2014 for ambient readers such as sidebars (#7203)").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").option("--out <path>", "write the output to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5802)").addHelpText("after", "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--direct skips the Hub snapshot and uses the existing direct GitHub GraphQL read immediately.\n--allow-partial applies to the paginated path and detail reads.\n\nWhen using Windows PowerShell 5.1, avoid shell redirects: PowerShell 5.1's `> file.json` is Out-File,\nwhich writes UTF-16LE with a BOM, and Node reading it as 'utf8' then fails JSON.parse at position 1\n(#5802). Use --out instead \u2014 the CLI writes the file itself as UTF-8:\n mmi-cli oracle board read --json --out .jerv/tmp/board.json\n").action((o) => runBoardRead(o));
|
|
28923
|
+
board.command("read", { isDefault: true }).alias("list").description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--direct", "bypass the Hub snapshot and read the live board through direct GitHub GraphQL").option("--snapshot-only", "fail instead of falling back to your personal GraphQL pool when the Hub snapshot is unavailable \u2014 for ambient readers such as sidebars (#7203)").option("--min-cursor <n>", "board-event cursor (from `oracle board events`) the Hub snapshot must postdate; a cached snapshot older than that event is rebuilt (#7240)").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").option("--out <path>", "write the output to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5802)").addHelpText("after", "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--direct skips the Hub snapshot and uses the existing direct GitHub GraphQL read immediately.\n--allow-partial applies to the paginated path and detail reads.\n\nWhen using Windows PowerShell 5.1, avoid shell redirects: PowerShell 5.1's `> file.json` is Out-File,\nwhich writes UTF-16LE with a BOM, and Node reading it as 'utf8' then fails JSON.parse at position 1\n(#5802). Use --out instead \u2014 the CLI writes the file itself as UTF-8:\n mmi-cli oracle board read --json --out .jerv/tmp/board.json\n").action((o) => runBoardRead(o));
|
|
28924
|
+
board.command("events").description("content-free board/CI change records after a cursor, from the Hub event feed (no GitHub call) \u2014 for real-time sidebars (#7235)").option("--since <cursor>", "cursor from the previous answer; omit on first read (answers the head cursor with resync:true)").option("--wait <seconds>", "hold up to this long (max 120), polling every 2s, and return on the first event or resync", "0").option("--repo <owner/repo>", "repo whose Hub config to use (defaults to git origin)").option("--json", "machine-readable output (already the default)").addHelpText("after", "\nPrints {cursor, events, resync, more[, hidden]}. Pass the returned cursor as the next --since.\nresync:true means records were missed (retention ~1h, or a gap): re-read the board with\n`mmi-cli oracle board read --snapshot-only --json`.\n").action(async (o) => {
|
|
28925
|
+
try {
|
|
28926
|
+
if (o.since !== void 0 && !/^\d{1,15}$/.test(o.since)) throw new Error("--since must be a non-negative integer cursor");
|
|
28927
|
+
const wait = Number(o.wait ?? "0");
|
|
28928
|
+
if (!Number.isFinite(wait) || wait < 0) throw new Error("--wait must be a non-negative number of seconds");
|
|
28929
|
+
const config = await loadConfigForRepo(o.repo);
|
|
28930
|
+
const page = await waitBoardEvents(o.since === void 0 ? void 0 : Number(o.since), wait, registryClientDeps(config));
|
|
28931
|
+
console.log(JSON.stringify(page));
|
|
28932
|
+
} catch (e) {
|
|
28933
|
+
return failGraceful(`oracle board events failed: ${e.message}`);
|
|
28934
|
+
}
|
|
28935
|
+
});
|
|
28855
28936
|
withExamples(mutating(
|
|
28856
28937
|
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); the board scan rides the Hub snapshot, same as board read").addHelpText("after", "\nclaim reads the board through the same Hub snapshot leg as `board read` (the App-installation\ncredential, never your personal GraphQL pool); the direct user-auth read is an emergency fallback\nand is named in a Warning line after the verdict (#6162).\n\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\nsame-owner resume (#6035): when the prior marker was posted by YOUR login on THIS host and its\nlocal session is verifiably dead (transcript probe), the claim proceeds as a resume without\n--force and names the evidence. A live, foreign-host, or unprobeable prior lane still refuses.\n\nreclaim from In Review (#6339): an In Review item with no holder, or one held by the claiming\nlogin, is claimable \u2014 it moves back to In Progress and the receipt names the status it came from.\nAn In Review item another login holds is refused (ask the holder, or wait for the review to land),\nand Done stays refused. The board status is the authority; no PR state is consulted.\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"),
|
|
28857
28938
|
(_opts, args) => ({ command: "oracle board claim", issues: args[0] ?? [] })
|
package/package.json
CHANGED