@mutmutco/cli 4.5.17 → 4.5.18
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 +78 -26
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -9544,11 +9544,16 @@ function resolveUseEnvNames(keys, name) {
|
|
|
9544
9544
|
function referencesDollarEnv(arg, name) {
|
|
9545
9545
|
return arg.includes(`\${${name}}`) || new RegExp(`\\$${name}(?![A-Za-z0-9_])`).test(arg);
|
|
9546
9546
|
}
|
|
9547
|
+
function isShellScriptCommand(command) {
|
|
9548
|
+
const exe = (command[0] ?? "").split(/[/\\]/).pop()?.toLowerCase().replace(/\.exe$/, "") ?? "";
|
|
9549
|
+
return /^(?:ba|z|da|k)?sh$/.test(exe) && command.slice(1).some((a) => /^-[a-z]*c[a-z]*$/i.test(a));
|
|
9550
|
+
}
|
|
9547
9551
|
function secretsUseDollarHint(command, envNames) {
|
|
9552
|
+
if (isShellScriptCommand(command)) return null;
|
|
9548
9553
|
for (const arg of command) {
|
|
9549
9554
|
for (const name of envNames) {
|
|
9550
9555
|
if (!referencesDollarEnv(arg, name)) continue;
|
|
9551
|
-
return `secrets use: $${name} is injected into the CHILD's env,
|
|
9556
|
+
return `secrets use: ${command[0]} receives the literal text $${name} \u2014 the value is injected into the CHILD's env, and ${command[0]} does not expand variables in its arguments. Wrap the consumer in a shell so expansion happens inside: bash -c 'your-command "$${name}"'`;
|
|
9552
9557
|
}
|
|
9553
9558
|
}
|
|
9554
9559
|
return null;
|
|
@@ -10865,6 +10870,9 @@ function readSessionId(env) {
|
|
|
10865
10870
|
}
|
|
10866
10871
|
return void 0;
|
|
10867
10872
|
}
|
|
10873
|
+
function readSessionIdSource(env) {
|
|
10874
|
+
return SESSION_ID_ENV_VARS.find((key) => Boolean(env[key]?.trim()));
|
|
10875
|
+
}
|
|
10868
10876
|
var synthesizedSessionId;
|
|
10869
10877
|
function fallbackSessionId(surface) {
|
|
10870
10878
|
if (!synthesizedSessionId) {
|
|
@@ -12581,15 +12589,36 @@ async function moveBoardItem(options, deps = {}) {
|
|
|
12581
12589
|
({ credential } = await updateItemSingleSelect(client, cfg.projectId, item.itemId, cfg.statusFieldId, optionId));
|
|
12582
12590
|
} catch (e) {
|
|
12583
12591
|
if (options.status === "Done" && isArchivedItemRefusal(ghError(e))) {
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12592
|
+
if (item.status === "Done") {
|
|
12593
|
+
return {
|
|
12594
|
+
item,
|
|
12595
|
+
viewer: lookup.viewer,
|
|
12596
|
+
repo: currentRepo,
|
|
12597
|
+
status: "Done",
|
|
12598
|
+
partial: false,
|
|
12599
|
+
archived: true,
|
|
12600
|
+
warning: `${item.ref} is archived and already Done; no status write was needed`
|
|
12601
|
+
};
|
|
12602
|
+
}
|
|
12603
|
+
try {
|
|
12604
|
+
await client.graphql(
|
|
12605
|
+
`mutation($projectId: ID!, $itemId: ID!) { unarchiveProjectV2Item(input: { projectId: $projectId, itemId: $itemId }) { item { id } } }`,
|
|
12606
|
+
{ projectId: cfg.projectId, itemId: item.itemId }
|
|
12607
|
+
);
|
|
12608
|
+
({ credential } = await updateItemSingleSelect(client, cfg.projectId, item.itemId, cfg.statusFieldId, optionId));
|
|
12609
|
+
return {
|
|
12610
|
+
item: { ...item, status: "Done", statusOptionId: optionId },
|
|
12611
|
+
viewer: lookup.viewer,
|
|
12612
|
+
repo: currentRepo,
|
|
12613
|
+
status: "Done",
|
|
12614
|
+
partial: false,
|
|
12615
|
+
credential
|
|
12616
|
+
};
|
|
12617
|
+
} catch (recoveryError) {
|
|
12618
|
+
const warning2 = `partial move: ${item.ref} was archived at '${item.status}' and could not be unarchived and moved to Done (${ghError(recoveryError)})`;
|
|
12619
|
+
if (!options.allowPartial) throw new Error(warning2);
|
|
12620
|
+
return { item, viewer: lookup.viewer, repo: currentRepo, status: item.status, partial: true, warning: warning2 };
|
|
12621
|
+
}
|
|
12593
12622
|
}
|
|
12594
12623
|
const warning = `partial move: ${item.ref} status was not changed to ${options.status} (${ghError(e)})`;
|
|
12595
12624
|
if (!options.allowPartial) throw new Error(warning);
|
|
@@ -12663,9 +12692,26 @@ async function prepareClaimContext(options, selectors, deps, collected, snapshot
|
|
|
12663
12692
|
items: collected.items,
|
|
12664
12693
|
writable: writableOrUnknown(writable),
|
|
12665
12694
|
report,
|
|
12666
|
-
session: deps.session ?? describeSessionIdentity()
|
|
12695
|
+
session: deps.session ?? describeSessionIdentity(),
|
|
12696
|
+
...deps.snapshot ? {
|
|
12697
|
+
freshAppItem: async (selector) => {
|
|
12698
|
+
const fresh = await collectBoardPreferringSnapshot(cfg, { repo: options.repo }, deps);
|
|
12699
|
+
if (!fresh.snapshot || fresh.collected.partial) return void 0;
|
|
12700
|
+
return fresh.collected.items.find((candidate) => candidate.repository.toLowerCase() === selector.repo.toLowerCase() && candidate.number === selector.number);
|
|
12701
|
+
}
|
|
12702
|
+
} : {}
|
|
12667
12703
|
};
|
|
12668
12704
|
}
|
|
12705
|
+
async function freshClaimItem(ctx, selector) {
|
|
12706
|
+
try {
|
|
12707
|
+
return (await fetchIssueProjectItem(ctx.client, ctx.cfg, selector)).item;
|
|
12708
|
+
} catch (e) {
|
|
12709
|
+
if (!isRateLimitRefusal(e) || !ctx.freshAppItem) throw e;
|
|
12710
|
+
const item = await ctx.freshAppItem(selector).catch(() => void 0);
|
|
12711
|
+
if (!item) throw e;
|
|
12712
|
+
return item;
|
|
12713
|
+
}
|
|
12714
|
+
}
|
|
12669
12715
|
async function claimOneBoardItem(ctx, selector, options) {
|
|
12670
12716
|
const { cfg, client, report } = ctx;
|
|
12671
12717
|
const flatItem = findBoardItem(ctx.items, selector, { owner: cfg.projectOwner, number: cfg.projectNumber });
|
|
@@ -12721,7 +12767,7 @@ async function claimOneBoardItem(ctx, selector, options) {
|
|
|
12721
12767
|
const pre = evaluateClaim(flatItem, assignedLogin);
|
|
12722
12768
|
if (!pre.ok) throw new NotClaimableError(pre.reason, { ref: flatItem.ref, status: flatItem.status });
|
|
12723
12769
|
let item = flatItem;
|
|
12724
|
-
const fresh =
|
|
12770
|
+
const fresh = await freshClaimItem(ctx, { repo: item.repository, number: item.number });
|
|
12725
12771
|
if (!fresh) throw new Error(`${item.ref} is not on this project board`);
|
|
12726
12772
|
const verdict = evaluateClaim(fresh, assignedLogin);
|
|
12727
12773
|
if (!verdict.ok) throw new NotClaimableError(verdict.reason, { ref: fresh.ref, status: fresh.status });
|
|
@@ -14838,9 +14884,10 @@ function buildRegisterPayload(repo, cls, vars, options = {}) {
|
|
|
14838
14884
|
deployModel,
|
|
14839
14885
|
// Persist the canonical stage hosts at the same write-time boundary as deployModel. Read paths never
|
|
14840
14886
|
// infer from an absent edgeDomains field.
|
|
14841
|
-
edgeDomains: inferEdgeDomainsForWrite(slug),
|
|
14887
|
+
edgeDomains: options.existing?.edgeDomains ?? inferEdgeDomainsForWrite(slug),
|
|
14842
14888
|
// #1359: always persist an explicit track so release tooling never guesses from absence alone.
|
|
14843
|
-
|
|
14889
|
+
// #7134: an explicit flag wins, then the registered track, then the class default (first bootstrap).
|
|
14890
|
+
releaseTrack: options.releaseTrack ?? (isReleaseTrack(options.existing?.releaseTrack) ? options.existing.releaseTrack : void 0) ?? resolveBootstrapReleaseTrack(cls, void 0),
|
|
14844
14891
|
// Board coords (from GraphQL at bootstrap, passed as --var by the skill).
|
|
14845
14892
|
projectOwner: vars.PROJECT_OWNER || void 0,
|
|
14846
14893
|
projectNumber: num(vars.PROJECT_NUMBER),
|
|
@@ -16115,10 +16162,10 @@ var rollout_plan_default = {
|
|
|
16115
16162
|
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)."
|
|
16116
16163
|
},
|
|
16117
16164
|
baseline: {
|
|
16118
|
-
version: "4.5.
|
|
16119
|
-
tag: "v4.5.
|
|
16120
|
-
commit: "
|
|
16121
|
-
npm: "@mutmutco/cli@4.5.
|
|
16165
|
+
version: "4.5.18",
|
|
16166
|
+
tag: "v4.5.18",
|
|
16167
|
+
commit: "afc70753c56b",
|
|
16168
|
+
npm: "@mutmutco/cli@4.5.18"
|
|
16122
16169
|
},
|
|
16123
16170
|
exitCriterion: "fleet-n-of-n",
|
|
16124
16171
|
hubOnlyShortcut: "forbidden",
|
|
@@ -16135,14 +16182,14 @@ var rollout_plan_default = {
|
|
|
16135
16182
|
repo: "mutmutco/mmi-hub",
|
|
16136
16183
|
role: "canary",
|
|
16137
16184
|
schedule: "train",
|
|
16138
|
-
v3Target: "v4.5.
|
|
16185
|
+
v3Target: "v4.5.18"
|
|
16139
16186
|
}
|
|
16140
16187
|
],
|
|
16141
16188
|
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.",
|
|
16142
16189
|
rollback: {
|
|
16143
16190
|
independent: true,
|
|
16144
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
16145
|
-
v3Target: "v4.5.
|
|
16191
|
+
mechanism: "npm dist-tag latest -> 4.5.18 and redeploy the Hub Lambda from tag v4.5.18 (afc70753c56b); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
16192
|
+
v3Target: "v4.5.18 (@mutmutco/cli@4.5.18, tag commit afc70753c56b \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
16146
16193
|
}
|
|
16147
16194
|
},
|
|
16148
16195
|
{
|
|
@@ -30225,7 +30272,7 @@ function registerBootstrapCommands(program3) {
|
|
|
30225
30272
|
for (const [k, v] of Object.entries(gateConfigToVars(meta?.gate))) if (rawVars[k] == null) rawVars[k] = v;
|
|
30226
30273
|
} catch {
|
|
30227
30274
|
}
|
|
30228
|
-
const vars = withDerivedRepoVars(rawVars, parsedRepo, o.class,
|
|
30275
|
+
const vars = withDerivedRepoVars(rawVars, parsedRepo, o.class, effectiveTrack, registryRequiredCheckBranches, registryMeta?.requiredChecks);
|
|
30229
30276
|
if (!vars.PROJECT_ID) {
|
|
30230
30277
|
try {
|
|
30231
30278
|
const r = await gh(linkedProjectsQueryArgs(parsedRepo.owner, parsedRepo.name));
|
|
@@ -30591,7 +30638,9 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
|
|
|
30591
30638
|
projectType: o.projectType || registryMeta?.projectType || void 0,
|
|
30592
30639
|
// #6090: META is authoritative on a rerun
|
|
30593
30640
|
deployModel: o.deployModel || registryMeta?.deployModel || void 0,
|
|
30594
|
-
|
|
30641
|
+
// #7134: explicit flag only; the registered track and edge domains win on a re-run.
|
|
30642
|
+
releaseTrack: o.releaseTrack || void 0,
|
|
30643
|
+
existing: registryMeta
|
|
30595
30644
|
});
|
|
30596
30645
|
} catch (e) {
|
|
30597
30646
|
return fail(`bootstrap apply: ${e.message}`);
|
|
@@ -33115,7 +33164,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
33115
33164
|
let board = { ok: false, detail: "no config" };
|
|
33116
33165
|
try {
|
|
33117
33166
|
if (cfg.sagaApiUrl) {
|
|
33118
|
-
const
|
|
33167
|
+
const boardCfg = await loadConfigOrDiscover();
|
|
33168
|
+
const report = await readBoard({ config: boardCfg }, { snapshot: registryClientDeps(boardCfg) });
|
|
33119
33169
|
const total = report.primary.claimable.length + report.primary.userOwned.length + report.primary.taken.length;
|
|
33120
33170
|
board = { ok: true, detail: `board has ${total} active items (${report.primary.claimable.length} claimable, ${report.primary.userOwned.length} yours)` };
|
|
33121
33171
|
} else {
|
|
@@ -41149,6 +41199,8 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
|
|
|
41149
41199
|
const repo = await requireRepo(repoOption);
|
|
41150
41200
|
const { client, throttledPaths } = withRateLimitRetry(deps.client ?? defaultGitHubClient(), deps);
|
|
41151
41201
|
const actor = deps.actor ?? describeSessionIdentity();
|
|
41202
|
+
const source = deps.actorSource ?? (deps.actor ? "the caller-supplied identity" : readSessionIdSource(process.env) ?? "a synthesized fallback");
|
|
41203
|
+
const requester = `requesting lane: ${actor.surface}/${actor.session}@${actor.host} (session id from ${source})`;
|
|
41152
41204
|
const checkContest = deps.checkContest ?? checkLaneContest;
|
|
41153
41205
|
for (const number of issues) {
|
|
41154
41206
|
const state = await client.rest("GET", `repos/${repo}/issues/${number}`).then((issue) => issue?.state, () => void 0);
|
|
@@ -41166,10 +41218,10 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
|
|
|
41166
41218
|
contest.unverifiable.length ? `liveness could not be fully verified (failed to read: ${contest.unverifiable.join(", ")})` : ""
|
|
41167
41219
|
].filter(Boolean).join("; ");
|
|
41168
41220
|
if (!contest.marker && !contest.unverifiable.includes("comments")) {
|
|
41169
|
-
return `pr create: REFUSED \u2014 ${ref} carries no claim marker (its claim-marker comments were read from ${commentsPath} and name no holder) and shows ${kinds} work evidence (${why}); if this work is yours, \`mmi-cli oracle board claim ${ref} --force\` re-stamps your lane's marker and clears the refusal (move it to In Progress first if it sits In Review), or drop the closing line before creating this PR`;
|
|
41221
|
+
return `pr create: REFUSED \u2014 ${ref} carries no claim marker (its claim-marker comments were read from ${commentsPath} and name no holder) and shows ${kinds} work evidence (${why}); if this work is yours, \`mmi-cli oracle board claim ${ref} --force\` re-stamps your lane's marker and clears the refusal (move it to In Progress first if it sits In Review), or drop the closing line before creating this PR; ${requester}`;
|
|
41170
41222
|
}
|
|
41171
41223
|
const holder = contest.marker ? `lane ${describeClaimMarker(contest.marker)} (claim marker read from ${commentsPath}${posted})` : `an unreadable lane (its claim-marker comments could not be read${throttledPaths.has(commentsPath) ? " \u2014 GitHub rate-limited the read (HTTP 403) and the bounded reset-aware retry could not clear it" : ""}; the holder may be this very lane)`;
|
|
41172
|
-
return `pr create: REFUSED \u2014 ${ref} is held by ${holder} with ${kinds} work evidence (${why}); run \`mmi-cli oracle board claim ${ref} --force\` (move it to In Progress first if it sits In Review) or drop the closing line before creating this PR`;
|
|
41224
|
+
return `pr create: REFUSED \u2014 ${ref} is held by ${holder} with ${kinds} work evidence (${why}); run \`mmi-cli oracle board claim ${ref} --force\` (move it to In Progress first if it sits In Review) or drop the closing line before creating this PR; ${requester}`;
|
|
41173
41225
|
}
|
|
41174
41226
|
return void 0;
|
|
41175
41227
|
}
|
package/package.json
CHANGED