@mutmutco/cli 4.5.31 → 4.5.32
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 +124 -37
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -362,6 +362,16 @@ var init_error_codes = __esm({
|
|
|
362
362
|
});
|
|
363
363
|
|
|
364
364
|
// src/github-client.ts
|
|
365
|
+
var github_client_exports = {};
|
|
366
|
+
__export(github_client_exports, {
|
|
367
|
+
GitHubApiError: () => GitHubApiError,
|
|
368
|
+
classifyGhTokenExecFailure: () => classifyGhTokenExecFailure,
|
|
369
|
+
createGitHubClient: () => createGitHubClient,
|
|
370
|
+
defaultGitHubClient: () => defaultGitHubClient,
|
|
371
|
+
githubToken: () => githubToken,
|
|
372
|
+
githubTokenRead: () => githubTokenRead,
|
|
373
|
+
rateLimitResetNote: () => rateLimitResetNote
|
|
374
|
+
});
|
|
365
375
|
function classifyGhTokenExecFailure(e) {
|
|
366
376
|
const err = e;
|
|
367
377
|
const stderr = typeof err?.stderr === "string" ? err.stderr.trim() : "";
|
|
@@ -403,7 +413,7 @@ function rateLimitFromResponse(res, detail) {
|
|
|
403
413
|
const resetHeader = res.headers.get("x-ratelimit-reset");
|
|
404
414
|
const reset = resetHeader && /^\d+$/.test(resetHeader) ? Number(resetHeader) : void 0;
|
|
405
415
|
const rateLimited = (res.status === 403 || res.status === 429) && remaining === "0" || /rate limit|secondary rate|abuse detection/i.test(detail);
|
|
406
|
-
return { rateLimited, ...rateLimited && reset !== void 0 ? { rateLimitReset: reset } : {} };
|
|
416
|
+
return { rateLimited, ...remaining === "0" && !/secondary|abuse/i.test(detail) && res.headers.get("x-ratelimit-resource") ? { rateLimitResource: res.headers.get("x-ratelimit-resource") } : {}, ...rateLimited && reset !== void 0 ? { rateLimitReset: reset } : {} };
|
|
407
417
|
}
|
|
408
418
|
function joinUrl(base, path2) {
|
|
409
419
|
if (path2.startsWith("http://") || path2.startsWith("https://")) return path2;
|
|
@@ -456,7 +466,7 @@ async function errorFromResponse(res) {
|
|
|
456
466
|
const budgetNote = limit.rateLimited ? ` \u2014 ${rateLimitResetNote(limit.rateLimitReset)}` : "";
|
|
457
467
|
return new GitHubApiError(`HTTP ${res.status}${suffix}${budgetNote} (${res.url})`, {
|
|
458
468
|
status: res.status,
|
|
459
|
-
...limit.rateLimited ? { rateLimited: true, rateLimitReset: limit.rateLimitReset } : {}
|
|
469
|
+
...limit.rateLimited ? { rateLimited: true, rateLimitReset: limit.rateLimitReset, rateLimitResource: limit.rateLimitResource } : {}
|
|
460
470
|
});
|
|
461
471
|
}
|
|
462
472
|
function createGitHubClient(options = {}) {
|
|
@@ -530,6 +540,7 @@ function createGitHubClient(options = {}) {
|
|
|
530
540
|
status: 200,
|
|
531
541
|
graphqlErrors: parsed.errors,
|
|
532
542
|
rateLimited: true,
|
|
543
|
+
...!/secondary|abuse/i.test(message2) && parsed.errors.some((e) => e.type === "RATE_LIMITED") && (!res.headers.get("x-ratelimit-resource") || res.headers.get("x-ratelimit-resource") === "graphql") ? { rateLimitResource: "graphql" } : {},
|
|
533
544
|
rateLimitReset: reset
|
|
534
545
|
});
|
|
535
546
|
}
|
|
@@ -560,6 +571,7 @@ var init_github_client = __esm({
|
|
|
560
571
|
rateLimited;
|
|
561
572
|
/** Epoch seconds when the exhausted pool resets (`X-Ratelimit-Reset` of the refusing response). */
|
|
562
573
|
rateLimitReset;
|
|
574
|
+
rateLimitResource;
|
|
563
575
|
constructor(message2, opts = {}) {
|
|
564
576
|
super(message2);
|
|
565
577
|
this.name = "GitHubApiError";
|
|
@@ -568,6 +580,7 @@ var init_github_client = __esm({
|
|
|
568
580
|
this.graphqlErrors = opts.graphqlErrors;
|
|
569
581
|
this.rateLimited = opts.rateLimited;
|
|
570
582
|
this.rateLimitReset = opts.rateLimitReset;
|
|
583
|
+
this.rateLimitResource = opts.rateLimitResource;
|
|
571
584
|
}
|
|
572
585
|
};
|
|
573
586
|
DEFAULT_TIMEOUT_MS = 2e4;
|
|
@@ -5470,7 +5483,7 @@ function isSnapshotShape(body) {
|
|
|
5470
5483
|
const b = body;
|
|
5471
5484
|
const rate = b?.github?.rateLimit;
|
|
5472
5485
|
const staleValid = b?.stale === void 0 || typeof b.stale === "object" && b.stale !== null && Number.isFinite(b.stale.ageMs) && b.stale.ageMs >= 0;
|
|
5473
|
-
const githubValid = b?.github === void 0 || b.github.credential === "app_installation" && Number.isFinite(rate?.limit) && Number.isFinite(rate?.remaining) && Number.isFinite(rate?.cost) && typeof rate?.resetAt === "string";
|
|
5486
|
+
const githubValid = b?.github === void 0 || b.github.credential === "app_installation" && (rate === void 0 || Number.isFinite(rate?.limit) && Number.isFinite(rate?.remaining) && Number.isFinite(rate?.cost) && typeof rate?.resetAt === "string");
|
|
5474
5487
|
return Boolean(
|
|
5475
5488
|
b && typeof b === "object" && typeof b.project?.id === "string" && typeof b.project?.title === "string" && typeof b.viewer === "string" && b.viewer.length > 0 && Array.isArray(b.nodes) && Array.isArray(b.writableRepos) && Array.isArray(b.unreadableRepos) && Array.isArray(b.pullRequests) && Array.isArray(b.warnings) && typeof b.partial === "boolean" && githubValid && staleValid
|
|
5476
5489
|
);
|
|
@@ -5511,7 +5524,11 @@ async function fetchHubBoardSnapshot(request, deps) {
|
|
|
5511
5524
|
const body2 = await res.json().catch(() => null);
|
|
5512
5525
|
if (body2?.error === "rate_limited") {
|
|
5513
5526
|
const reset = typeof body2.resetAt === "string" ? `; resets ${body2.resetAt}` : "; reset time unavailable";
|
|
5514
|
-
return {
|
|
5527
|
+
return {
|
|
5528
|
+
state: "unavailable",
|
|
5529
|
+
reason: `GitHub App quota exhausted${reset}`,
|
|
5530
|
+
...body2.github?.credential === "app_installation" && body2.github.resource === "graphql" && /^github-app-v1:[a-f0-9]{64}$/.test(body2.github.quotaScope ?? "") ? { quota: { status: "rate_limited", ...typeof body2.resetAt === "string" ? { resetAt: body2.resetAt } : {}, github: { credential: "app_installation", resource: "graphql", quotaScope: body2.github.quotaScope } } } : {}
|
|
5531
|
+
};
|
|
5515
5532
|
}
|
|
5516
5533
|
}
|
|
5517
5534
|
if (!res.ok) return { state: "unavailable", reason: `board snapshot HTTP ${res.status}` };
|
|
@@ -12280,7 +12297,7 @@ function renderBoardItem(item) {
|
|
|
12280
12297
|
}
|
|
12281
12298
|
function renderBoardReport(report) {
|
|
12282
12299
|
const lines2 = [`Board \xB7 ${report.project.title} \xB7 @${report.viewer}`, renderBoardSource()];
|
|
12283
|
-
if (report.github) {
|
|
12300
|
+
if (report.github?.rateLimit) {
|
|
12284
12301
|
const { limit, remaining, cost, resetAt } = report.github.rateLimit;
|
|
12285
12302
|
lines2.push(`github: app installation \xB7 GraphQL ${remaining}/${limit} remaining \xB7 cost ${cost} \xB7 resets ${resetAt}`);
|
|
12286
12303
|
}
|
|
@@ -12510,7 +12527,7 @@ async function collectBoardPreferringSnapshot(cfg, options, deps) {
|
|
|
12510
12527
|
};
|
|
12511
12528
|
}
|
|
12512
12529
|
if (options.snapshotOnly && attempt?.state === "unavailable") {
|
|
12513
|
-
throw new Error(`Hub board snapshot unavailable (${attempt.reason}) \u2014 --snapshot-only refuses the direct user-auth fallback`);
|
|
12530
|
+
throw Object.assign(new Error(`Hub board snapshot unavailable (${attempt.reason}) \u2014 --snapshot-only refuses the direct user-auth fallback`), { quota: attempt.quota });
|
|
12514
12531
|
}
|
|
12515
12532
|
const collected = await collectBoardItems(cfg, { repo: options.repo, allowPartial: options.allowPartial, activeOnly: true }, deps);
|
|
12516
12533
|
if (attempt?.state === "unavailable") {
|
|
@@ -12535,7 +12552,7 @@ async function readBoard(options, deps = {}) {
|
|
|
12535
12552
|
unknown: new Set(snapshot.unreadableRepos.map((entry) => entry.repo.toLowerCase()))
|
|
12536
12553
|
};
|
|
12537
12554
|
pullRequests = snapshot.pullRequests;
|
|
12538
|
-
github = snapshot.github;
|
|
12555
|
+
github = snapshot.github && { ...snapshot.github, ...snapshot.quotaResetAt ? { quotaResetAt: snapshot.quotaResetAt } : {} };
|
|
12539
12556
|
} else {
|
|
12540
12557
|
const probed = await resolveWritableReposForClaimables(collected.items, client);
|
|
12541
12558
|
collected.warnings.push(...probed.warnings);
|
|
@@ -16820,10 +16837,10 @@ var rollout_plan_default = {
|
|
|
16820
16837
|
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)."
|
|
16821
16838
|
},
|
|
16822
16839
|
baseline: {
|
|
16823
|
-
version: "4.5.
|
|
16824
|
-
tag: "v4.5.
|
|
16825
|
-
commit: "
|
|
16826
|
-
npm: "@mutmutco/cli@4.5.
|
|
16840
|
+
version: "4.5.32",
|
|
16841
|
+
tag: "v4.5.32",
|
|
16842
|
+
commit: "7c2492e36877",
|
|
16843
|
+
npm: "@mutmutco/cli@4.5.32"
|
|
16827
16844
|
},
|
|
16828
16845
|
exitCriterion: "fleet-n-of-n",
|
|
16829
16846
|
hubOnlyShortcut: "forbidden",
|
|
@@ -16840,14 +16857,14 @@ var rollout_plan_default = {
|
|
|
16840
16857
|
repo: "mutmutco/mmi-hub",
|
|
16841
16858
|
role: "canary",
|
|
16842
16859
|
schedule: "train",
|
|
16843
|
-
v3Target: "v4.5.
|
|
16860
|
+
v3Target: "v4.5.32"
|
|
16844
16861
|
}
|
|
16845
16862
|
],
|
|
16846
16863
|
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.",
|
|
16847
16864
|
rollback: {
|
|
16848
16865
|
independent: true,
|
|
16849
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
16850
|
-
v3Target: "v4.5.
|
|
16866
|
+
mechanism: "npm dist-tag latest -> 4.5.32 and redeploy the Hub Lambda from tag v4.5.32 (7c2492e36877); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
16867
|
+
v3Target: "v4.5.32 (@mutmutco/cli@4.5.32, tag commit 7c2492e36877 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
16851
16868
|
}
|
|
16852
16869
|
},
|
|
16853
16870
|
{
|
|
@@ -17946,6 +17963,51 @@ function registerQueryCommands(program3) {
|
|
|
17946
17963
|
}
|
|
17947
17964
|
});
|
|
17948
17965
|
}
|
|
17966
|
+
async function readPrQuotaContext(repo, number, quotaScope, client) {
|
|
17967
|
+
if (!quotaScope) throw new Error("PR quota context requires the verified App actor");
|
|
17968
|
+
const [owner, name] = repo.split("/");
|
|
17969
|
+
const github = { credential: "app_installation", quotaScope, resource: "graphql" };
|
|
17970
|
+
try {
|
|
17971
|
+
const issues = [];
|
|
17972
|
+
const checks = [];
|
|
17973
|
+
let issuesCursor;
|
|
17974
|
+
let checksCursor;
|
|
17975
|
+
let readIssues = true;
|
|
17976
|
+
let readChecks = true;
|
|
17977
|
+
for (let page = 0; page < 100; page++) {
|
|
17978
|
+
const data = await client.graphql(`query($owner:String!,$name:String!,$number:Int!,$issuesCursor:String,$checksCursor:String,$readIssues:Boolean!,$readChecks:Boolean!) {
|
|
17979
|
+
rateLimit { limit remaining resetAt }
|
|
17980
|
+
repository(owner:$owner,name:$name) { pullRequest(number:$number) {
|
|
17981
|
+
number closingIssuesReferences(first:100,after:$issuesCursor) @include(if:$readIssues) { nodes { number } pageInfo { hasNextPage endCursor } }
|
|
17982
|
+
commits(last:1) @include(if:$readChecks) { nodes { commit { statusCheckRollup { contexts(first:100,after:$checksCursor) {
|
|
17983
|
+
nodes { ... on CheckRun { status conclusion } ... on StatusContext { state } }
|
|
17984
|
+
pageInfo { hasNextPage endCursor }
|
|
17985
|
+
} } } } }
|
|
17986
|
+
} }
|
|
17987
|
+
}`, { owner, name, number, issuesCursor, checksCursor, readIssues, readChecks });
|
|
17988
|
+
const pr = data.repository?.pullRequest;
|
|
17989
|
+
if (!pr) throw new Error("PR quota context could not resolve the PR");
|
|
17990
|
+
const issuePage = pr.closingIssuesReferences;
|
|
17991
|
+
const checkPage = pr.commits?.nodes[0]?.commit.statusCheckRollup?.contexts;
|
|
17992
|
+
if (readIssues && !issuePage) throw new Error("PR closing references unreadable");
|
|
17993
|
+
if (readIssues) issues.push(...issuePage.nodes);
|
|
17994
|
+
if (readChecks) checks.push(...checkPage?.nodes ?? []);
|
|
17995
|
+
readIssues = readIssues && issuePage.pageInfo.hasNextPage;
|
|
17996
|
+
readChecks = readChecks && (checkPage?.pageInfo.hasNextPage ?? false);
|
|
17997
|
+
if (!readIssues && !readChecks) return { number: pr.number, closingIssuesReferences: issues, statusCheckRollup: checks, github: { ...github, rateLimit: data.rateLimit } };
|
|
17998
|
+
const nextIssues = issuePage?.pageInfo.endCursor;
|
|
17999
|
+
const nextChecks = checkPage?.pageInfo.endCursor;
|
|
18000
|
+
if (readIssues && (!nextIssues || nextIssues === issuesCursor) || readChecks && (!nextChecks || nextChecks === checksCursor)) throw new Error("PR quota context pagination cursor did not advance");
|
|
18001
|
+
issuesCursor = nextIssues;
|
|
18002
|
+
checksCursor = nextChecks;
|
|
18003
|
+
}
|
|
18004
|
+
throw new Error("PR quota context pagination exceeded 100 pages");
|
|
18005
|
+
} catch (error) {
|
|
18006
|
+
const { GitHubApiError: GitHubApiError2 } = await Promise.resolve().then(() => (init_github_client(), github_client_exports));
|
|
18007
|
+
if (!(error instanceof GitHubApiError2) || !error.rateLimited || error.rateLimitResource !== "graphql") throw error;
|
|
18008
|
+
return { status: "rate_limited", github, ...error.rateLimitReset === void 0 ? {} : { resetAt: new Date(error.rateLimitReset * 1e3).toISOString() } };
|
|
18009
|
+
}
|
|
18010
|
+
}
|
|
17949
18011
|
|
|
17950
18012
|
// src/fleet-track-inventory.ts
|
|
17951
18013
|
var NO_DEPLOY_SURFACE_MODELS = /* @__PURE__ */ new Set(["none", "content"]);
|
|
@@ -28647,8 +28709,14 @@ function renderPluginCachePlan(plan, applied) {
|
|
|
28647
28709
|
return lines2.join("\n");
|
|
28648
28710
|
}
|
|
28649
28711
|
|
|
28650
|
-
//
|
|
28712
|
+
// ../shared/github-quota-scope.ts
|
|
28651
28713
|
var import_node_crypto6 = require("node:crypto");
|
|
28714
|
+
function appQuotaScope(appId, installationId, origin = "https://api.github.com", resource = "graphql") {
|
|
28715
|
+
return `github-app-v1:${(0, import_node_crypto6.createHash)("sha256").update(JSON.stringify([new URL(origin).origin, appId.trim(), installationId.trim(), resource])).digest("hex")}`;
|
|
28716
|
+
}
|
|
28717
|
+
|
|
28718
|
+
// src/app-actor.ts
|
|
28719
|
+
var import_node_crypto7 = require("node:crypto");
|
|
28652
28720
|
var APP_ACTOR_ENV = "MMI_ACTOR";
|
|
28653
28721
|
var APP_VAULT_REPO = "mutmutco/MMI-Hub";
|
|
28654
28722
|
var APP_VAULT_KEYS = ["GITHUB_APP_ID", "GITHUB_APP_INSTALLATION_ID", "GITHUB_APP_PRIVATE_KEY"];
|
|
@@ -28699,10 +28767,14 @@ function mintAppJwt(appId, privateKeyPem, nowSec) {
|
|
|
28699
28767
|
exp: now + APP_JWT_TTL_S,
|
|
28700
28768
|
iss: appId
|
|
28701
28769
|
}));
|
|
28702
|
-
const signer = (0,
|
|
28770
|
+
const signer = (0, import_node_crypto7.createSign)("RSA-SHA256");
|
|
28703
28771
|
signer.update(`${header}.${payload}`);
|
|
28704
28772
|
return `${header}.${payload}.${signer.sign(privateKeyPem, "base64url")}`;
|
|
28705
28773
|
}
|
|
28774
|
+
var activeQuotaScope;
|
|
28775
|
+
function activeAppQuotaScope() {
|
|
28776
|
+
return activeQuotaScope;
|
|
28777
|
+
}
|
|
28706
28778
|
async function mintInstallationToken(deps) {
|
|
28707
28779
|
const [appId, installationId, privateKey] = await Promise.all(APP_VAULT_KEYS.map((k) => deps.fetchSecret(k)));
|
|
28708
28780
|
if (!appId || !installationId || !privateKey) {
|
|
@@ -28748,7 +28820,7 @@ async function mintInstallationToken(deps) {
|
|
|
28748
28820
|
if (!body.token || !body.expires_at) {
|
|
28749
28821
|
throw new AppActorError("app actor: mint response carried no token \u2014 automation fails closed");
|
|
28750
28822
|
}
|
|
28751
|
-
return { token: body.token, expiresAt: body.expires_at };
|
|
28823
|
+
return { token: body.token, expiresAt: body.expires_at, quotaScope: appQuotaScope(appId, installationId, process.env.GITHUB_API_URL) };
|
|
28752
28824
|
}
|
|
28753
28825
|
async function pollTokenOrUndefined(deps) {
|
|
28754
28826
|
try {
|
|
@@ -28760,6 +28832,7 @@ async function pollTokenOrUndefined(deps) {
|
|
|
28760
28832
|
var notedIneligibleCommand = false;
|
|
28761
28833
|
async function activateAppActor(commandPath3, env, mint, note = (line) => process.stderr.write(`${line}
|
|
28762
28834
|
`)) {
|
|
28835
|
+
activeQuotaScope = void 0;
|
|
28763
28836
|
const requested = (env[APP_ACTOR_ENV] ?? "").trim();
|
|
28764
28837
|
if (!requested) return "personal";
|
|
28765
28838
|
if (requested !== "app") {
|
|
@@ -28776,6 +28849,7 @@ async function activateAppActor(commandPath3, env, mint, note = (line) => proces
|
|
|
28776
28849
|
delete env.GITHUB_TOKEN;
|
|
28777
28850
|
const minted = await mint();
|
|
28778
28851
|
env.GH_TOKEN = minted.token;
|
|
28852
|
+
activeQuotaScope = minted.quotaScope;
|
|
28779
28853
|
return "app";
|
|
28780
28854
|
}
|
|
28781
28855
|
|
|
@@ -28991,6 +29065,7 @@ function registerBoardCommands(program3) {
|
|
|
28991
29065
|
if (!o.out) console.log(output);
|
|
28992
29066
|
else console.log(`Wrote board read to ${o.out} (UTF-8, ${writeUtf8Receipt(o.out, output)} bytes)`);
|
|
28993
29067
|
} catch (e) {
|
|
29068
|
+
if (o.json && e && typeof e === "object" && "quota" in e && e.quota) console.log(JSON.stringify(e.quota));
|
|
28994
29069
|
return failGraceful(`board read failed: ${withDiscoverMissDetail(e.message)}`);
|
|
28995
29070
|
}
|
|
28996
29071
|
}
|
|
@@ -31221,7 +31296,7 @@ function renderDeployPortDoctor(report) {
|
|
|
31221
31296
|
}
|
|
31222
31297
|
|
|
31223
31298
|
// src/bootstrap-drift.ts
|
|
31224
|
-
var
|
|
31299
|
+
var import_node_crypto8 = require("node:crypto");
|
|
31225
31300
|
function byteComparableSeeds(manifest, cls) {
|
|
31226
31301
|
return manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self" && s.classes.includes(cls));
|
|
31227
31302
|
}
|
|
@@ -31241,7 +31316,7 @@ function compareSeedBytes(hubContent, repoContent) {
|
|
|
31241
31316
|
return normalize(hubContent) === normalize(repoContent) ? "match" : "drift";
|
|
31242
31317
|
}
|
|
31243
31318
|
function seedContentHash(content) {
|
|
31244
|
-
return (0,
|
|
31319
|
+
return (0, import_node_crypto8.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
|
|
31245
31320
|
}
|
|
31246
31321
|
function auditRepoSeedDrift(repo, seeds, hubContents, repoReads) {
|
|
31247
31322
|
const byTarget = new Map(repoReads.map((r) => [r.target, r.content]));
|
|
@@ -31403,7 +31478,7 @@ function renderPropagationReport(plan) {
|
|
|
31403
31478
|
}
|
|
31404
31479
|
|
|
31405
31480
|
// src/bootstrap-propagation-identity.ts
|
|
31406
|
-
var
|
|
31481
|
+
var import_node_crypto9 = require("node:crypto");
|
|
31407
31482
|
var PROPAGATION_BRANCH_PREFIX = "seed-propagate-";
|
|
31408
31483
|
var TARGET_MARKER_NAME = "mmi-bootstrap-propagation-target";
|
|
31409
31484
|
function safeBranchPart(value, maxLength, fallback) {
|
|
@@ -31416,7 +31491,7 @@ function repoSlug2(repo) {
|
|
|
31416
31491
|
function propagationBranch(repo, target) {
|
|
31417
31492
|
const repoPart = safeBranchPart(repoSlug2(repo), 32, "repo");
|
|
31418
31493
|
const targetPart = safeBranchPart(target, 48, "target");
|
|
31419
|
-
const hash = (0,
|
|
31494
|
+
const hash = (0, import_node_crypto9.createHash)("sha256").update(repo.trim().toLowerCase()).update("\0").update(target).digest("hex").slice(0, 12);
|
|
31420
31495
|
return `${PROPAGATION_BRANCH_PREFIX}${repoPart}-${targetPart}-${hash}`;
|
|
31421
31496
|
}
|
|
31422
31497
|
function legacyPropagationBranch(repo) {
|
|
@@ -32572,7 +32647,7 @@ function renderBootstrapVerifyReport(report) {
|
|
|
32572
32647
|
}
|
|
32573
32648
|
|
|
32574
32649
|
// src/bootstrap-seed-delivery.ts
|
|
32575
|
-
var
|
|
32650
|
+
var import_node_crypto10 = require("node:crypto");
|
|
32576
32651
|
init_github_client();
|
|
32577
32652
|
var SHA = /^[a-f0-9]{40}$/;
|
|
32578
32653
|
async function readSeedFile(repo, target, ref, client) {
|
|
@@ -32605,7 +32680,7 @@ async function targetedSeedPlan(repo, slug, target, baseBranch, sourceSha, clien
|
|
|
32605
32680
|
}
|
|
32606
32681
|
const baseSha = base?.object?.sha;
|
|
32607
32682
|
if (!baseSha || !SHA.test(baseSha) || !SHA.test(sourceSha)) throw new Error("bootstrap apply: seed identity is unreadable");
|
|
32608
|
-
const identity = (0,
|
|
32683
|
+
const identity = (0, import_node_crypto10.createHash)("sha256").update(JSON.stringify([repo.toLowerCase(), target, baseSha, sourceSha])).digest("hex").slice(0, 24);
|
|
32609
32684
|
return { ...planSeedDelivery(rules, slug, baseBranch, `bootstrap-seed-${identity}`), baseSha, baseBranch };
|
|
32610
32685
|
}
|
|
32611
32686
|
async function verifyTargetedSeedBranch(repo, plan, target, client, allowMissing = false) {
|
|
@@ -34173,12 +34248,13 @@ async function stageStatus(member, stage, deps) {
|
|
|
34173
34248
|
}
|
|
34174
34249
|
}
|
|
34175
34250
|
}
|
|
34251
|
+
const lag = commit && head !== "unknown" ? commit.toLowerCase() !== head.toLowerCase() : null;
|
|
34176
34252
|
return {
|
|
34177
34253
|
stage,
|
|
34178
34254
|
head,
|
|
34179
34255
|
served,
|
|
34180
34256
|
source,
|
|
34181
|
-
lag:
|
|
34257
|
+
lag: source === "registry-pin" && lag === false ? null : lag,
|
|
34182
34258
|
...deployStatus ? { deployStatus } : {}
|
|
34183
34259
|
};
|
|
34184
34260
|
}
|
|
@@ -34208,7 +34284,7 @@ function formatSuiteStatus(report) {
|
|
|
34208
34284
|
// src/suite-train.ts
|
|
34209
34285
|
var import_node_fs29 = require("node:fs");
|
|
34210
34286
|
var import_node_path28 = require("node:path");
|
|
34211
|
-
var
|
|
34287
|
+
var import_node_crypto11 = require("node:crypto");
|
|
34212
34288
|
var SCHEMA = "mmi-suite-cut/v1";
|
|
34213
34289
|
var SHA2 = /^[a-f0-9]{40}$/i;
|
|
34214
34290
|
var RC_TAG = /^v\d+\.\d+\.\d+-rc\.\d+$/;
|
|
@@ -34243,7 +34319,7 @@ function readSuiteCut(path2) {
|
|
|
34243
34319
|
}
|
|
34244
34320
|
function writeSuiteCut(path2, record) {
|
|
34245
34321
|
(0, import_node_fs29.mkdirSync)((0, import_node_path28.dirname)(path2), { recursive: true });
|
|
34246
|
-
const temp = `${path2}.${(0,
|
|
34322
|
+
const temp = `${path2}.${(0, import_node_crypto11.randomBytes)(8).toString("hex")}.tmp`;
|
|
34247
34323
|
try {
|
|
34248
34324
|
(0, import_node_fs29.writeFileSync)(temp, JSON.stringify(record, null, 2), { flag: "wx" });
|
|
34249
34325
|
(0, import_node_fs29.renameSync)(temp, path2);
|
|
@@ -37866,7 +37942,7 @@ init_github_client();
|
|
|
37866
37942
|
|
|
37867
37943
|
// src/issue-commands.ts
|
|
37868
37944
|
var import_node_fs34 = require("node:fs");
|
|
37869
|
-
var
|
|
37945
|
+
var import_node_crypto12 = require("node:crypto");
|
|
37870
37946
|
init_cli_shared();
|
|
37871
37947
|
init_clean_exit();
|
|
37872
37948
|
init_error_codes();
|
|
@@ -38339,7 +38415,7 @@ function rowIdempotencyKey(batchKey, spec) {
|
|
|
38339
38415
|
const identity = `${spec.type}
|
|
38340
38416
|
${spec.title.trim()}
|
|
38341
38417
|
${spec.body ?? ""}`;
|
|
38342
|
-
const hash = (0,
|
|
38418
|
+
const hash = (0, import_node_crypto12.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
|
|
38343
38419
|
return `${batchKey}:${hash}`;
|
|
38344
38420
|
}
|
|
38345
38421
|
var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
|
|
@@ -42316,7 +42392,7 @@ ${list}`);
|
|
|
42316
42392
|
'Keep closing keywords on a dedicated line ("Closes #N") \u2014 GitHub parses close/fix/resolve + #N even mid-sentence, so pr create rewrites such prose to "part of #N" (#6039).',
|
|
42317
42393
|
'When acceptance is only provable AFTER deploy, link with "Part of #N", never "Closes" \u2014 a closing reference closes the issue and advances its board card to Done on merge, before the acceptance evidence exists. Close it on that evidence instead (#6407).'
|
|
42318
42394
|
]);
|
|
42319
|
-
pr.command("view <ref>").description("read a PR as structured JSON (merged state, head/base, URL, merge commit) \u2014 the mmi-cli read path (#2347). --comments folds in every comment; --context also adds linkedIssues (the issues it closes/references, #2894)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json [fields...]", 'gh --json field list (overrides the default field set). Accepts commas, spaces, or repeated --json flags \u2014 in PowerShell an unquoted comma list is an array literal, so QUOTE it: --json "state,baseRefName,mergeCommit"').option("--comments", "include every comment (body + comments in one call) \u2014 read the whole PR before landing it (#2894)").option("--context", "full working context in one call: implies --comments and also adds linkedIssues (the issues the PR closes/references) (#2894)").action(async (ref, o) => {
|
|
42395
|
+
pr.command("view <ref>").description("read a PR as structured JSON (merged state, head/base, URL, merge commit) \u2014 the mmi-cli read path (#2347). --comments folds in every comment; --context also adds linkedIssues (the issues it closes/references, #2894)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json [fields...]", 'gh --json field list (overrides the default field set). Accepts commas, spaces, or repeated --json flags \u2014 in PowerShell an unquoted comma list is an array literal, so QUOTE it: --json "state,baseRefName,mergeCommit"').option("--comments", "include every comment (body + comments in one call) \u2014 read the whole PR before landing it (#2894)").option("--context", "full working context in one call: implies --comments and also adds linkedIssues (the issues the PR closes/references) (#2894)").option("--quota-context", "fixed App-only PR check read with verified quota scope and reset for harnesses").action(async (ref, o) => {
|
|
42320
42396
|
let parsed;
|
|
42321
42397
|
try {
|
|
42322
42398
|
parsed = parseIssueRef(ref, o.repo);
|
|
@@ -42329,6 +42405,13 @@ ${list}`);
|
|
|
42329
42405
|
const defaultPrFields = "number,title,state,url,isDraft,mergeable,mergedAt,mergeCommit,headRefName,baseRefName,author,labels";
|
|
42330
42406
|
const effective = resolvePrViewFields(o.json, defaultPrFields);
|
|
42331
42407
|
try {
|
|
42408
|
+
if (o.quotaContext) {
|
|
42409
|
+
if (o.comments || o.context || o.json) return fail("pr view: --quota-context cannot be combined with --comments, --context or --json");
|
|
42410
|
+
const data2 = await readPrQuotaContext(repo, n, activeAppQuotaScope(), defaultGitHubClient());
|
|
42411
|
+
console.log(JSON.stringify(data2));
|
|
42412
|
+
if (data2.status === "rate_limited") process.exitCode = 1;
|
|
42413
|
+
return;
|
|
42414
|
+
}
|
|
42332
42415
|
if (o.comments || o.context) {
|
|
42333
42416
|
const data2 = await runPrViewContext(queryDeps(), { number: n, repo, comments: Boolean(o.comments), context: Boolean(o.context), jsonFields: effective });
|
|
42334
42417
|
console.log(JSON.stringify(data2));
|
|
@@ -43244,6 +43327,10 @@ var NODE_VERIFY_TIMEOUT_MS = 5 * 6e4;
|
|
|
43244
43327
|
var NPM_TRAIN_TIMEOUT_MS = 6e4;
|
|
43245
43328
|
var NPM_FOLD_TIMEOUT_MS = NODE_PREPARE_TIMEOUT_MS;
|
|
43246
43329
|
var JERV_GATEWAY_DEPLOY_TIMEOUT_MS = 20 * 6e4;
|
|
43330
|
+
var GIT_COMMIT_TIMEOUT_MS = 6e4;
|
|
43331
|
+
function trainGitArgs(args) {
|
|
43332
|
+
return ["-c", "gc.auto=0", "-c", "maintenance.auto=false", ...args];
|
|
43333
|
+
}
|
|
43247
43334
|
async function announceSummaryRefusal(path2, isHub = false) {
|
|
43248
43335
|
let lines2;
|
|
43249
43336
|
try {
|
|
@@ -43283,10 +43370,10 @@ function trainApplyDeps() {
|
|
|
43283
43370
|
// #7027: every mutating train verb holds this checkout's train writer lock for its whole run.
|
|
43284
43371
|
trainWriter: (verb, cwd) => acquireTrainWriterLock(cwd, `mmi-cli devops ${verb}`),
|
|
43285
43372
|
run: async (file, args) => {
|
|
43286
|
-
const timeout = file === "node" && args[1] === "prepare" ? NODE_PREPARE_TIMEOUT_MS : file === "node" && args[1] === "verify" ? NODE_VERIFY_TIMEOUT_MS : file === "npm" && (args[0] === "version" || args[0] === "ci" || args[0] === "install") ? NPM_FOLD_TIMEOUT_MS : file === "npm" ? NPM_TRAIN_TIMEOUT_MS : file === "bash" && /jerv-gateway-release-deploy\.sh$/.test(args[0] ?? "") ? JERV_GATEWAY_DEPLOY_TIMEOUT_MS : file !== "gh" ? GIT_TIMEOUT_MS : args[0] === "run" && args[1] === "watch" ? GH_RUN_WATCH_TIMEOUT_MS : GH_TRAIN_TIMEOUT_MS;
|
|
43373
|
+
const timeout = file === "node" && args[1] === "prepare" ? NODE_PREPARE_TIMEOUT_MS : file === "node" && args[1] === "verify" ? NODE_VERIFY_TIMEOUT_MS : file === "npm" && (args[0] === "version" || args[0] === "ci" || args[0] === "install") ? NPM_FOLD_TIMEOUT_MS : file === "npm" ? NPM_TRAIN_TIMEOUT_MS : file === "bash" && /jerv-gateway-release-deploy\.sh$/.test(args[0] ?? "") ? JERV_GATEWAY_DEPLOY_TIMEOUT_MS : file === "git" && args[0] === "commit" ? GIT_COMMIT_TIMEOUT_MS : file !== "gh" ? GIT_TIMEOUT_MS : args[0] === "run" && args[1] === "watch" ? GH_RUN_WATCH_TIMEOUT_MS : GH_TRAIN_TIMEOUT_MS;
|
|
43287
43374
|
const watchToken = file === "gh" && args[0] === "run" && args[1] === "watch" ? await pollToken() : void 0;
|
|
43288
43375
|
try {
|
|
43289
|
-
return isWin3 && file === "npm" ? (await execFileP("cmd.exe", ["/c", "npm", ...args], { timeout })).stdout : (await execFileP(file, args, { timeout, ...watchToken ? { env: { ...process.env, GH_TOKEN: watchToken } } : {} })).stdout;
|
|
43376
|
+
return isWin3 && file === "npm" ? (await execFileP("cmd.exe", ["/c", "npm", ...args], { timeout })).stdout : (await execFileP(file, file === "git" ? trainGitArgs(args) : args, { timeout, ...watchToken ? { env: { ...process.env, GH_TOKEN: watchToken } } : {} })).stdout;
|
|
43290
43377
|
} catch (e) {
|
|
43291
43378
|
throw surfaceTrainSubprocessFailure(e, file, args);
|
|
43292
43379
|
}
|
|
@@ -48343,14 +48430,14 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
|
|
|
48343
48430
|
}
|
|
48344
48431
|
|
|
48345
48432
|
// src/tenant-artifact.ts
|
|
48346
|
-
var
|
|
48433
|
+
var import_node_crypto13 = require("node:crypto");
|
|
48347
48434
|
var import_node_fs48 = require("node:fs");
|
|
48348
48435
|
var import_promises10 = require("node:fs/promises");
|
|
48349
48436
|
var import_node_path45 = require("node:path");
|
|
48350
48437
|
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}$/;
|
|
48351
48438
|
var MAX_BYTES = 5 * 1024 * 1024 * 1024;
|
|
48352
48439
|
async function sha256File(path2) {
|
|
48353
|
-
const hash = (0,
|
|
48440
|
+
const hash = (0, import_node_crypto13.createHash)("sha256");
|
|
48354
48441
|
for await (const chunk of (0, import_node_fs48.createReadStream)(path2)) hash.update(chunk);
|
|
48355
48442
|
return hash.digest("hex");
|
|
48356
48443
|
}
|
|
@@ -48580,13 +48667,13 @@ async function executeWaveLand(plan, deps) {
|
|
|
48580
48667
|
|
|
48581
48668
|
// src/dist-drift.ts
|
|
48582
48669
|
var import_node_child_process17 = require("node:child_process");
|
|
48583
|
-
var
|
|
48670
|
+
var import_node_crypto15 = require("node:crypto");
|
|
48584
48671
|
var import_node_fs50 = require("node:fs");
|
|
48585
48672
|
var import_node_os21 = require("node:os");
|
|
48586
48673
|
var import_node_path47 = require("node:path");
|
|
48587
48674
|
|
|
48588
48675
|
// ../scripts/distribution-digest.mjs
|
|
48589
|
-
var
|
|
48676
|
+
var import_node_crypto14 = require("node:crypto");
|
|
48590
48677
|
var import_node_fs49 = require("node:fs");
|
|
48591
48678
|
var import_node_path46 = require("node:path");
|
|
48592
48679
|
var slash = (value) => value.replaceAll("\\", "/");
|
|
@@ -48599,7 +48686,7 @@ function repoPath(root, declaredPath, label) {
|
|
|
48599
48686
|
return target;
|
|
48600
48687
|
}
|
|
48601
48688
|
function digestFiles(files) {
|
|
48602
|
-
const hash = (0,
|
|
48689
|
+
const hash = (0, import_node_crypto14.createHash)("sha256");
|
|
48603
48690
|
for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
|
|
48604
48691
|
const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs49.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs49.readFileSync)(file.absolute);
|
|
48605
48692
|
hash.update(file.relative, "utf8");
|
|
@@ -48629,7 +48716,7 @@ var DIST_ARTIFACTS = [
|
|
|
48629
48716
|
];
|
|
48630
48717
|
var BOM_DIST_TREE_ID = "mmi-cli-dist";
|
|
48631
48718
|
var ABSENT = "absent";
|
|
48632
|
-
var sha256 = (bytes) => `sha256:${(0,
|
|
48719
|
+
var sha256 = (bytes) => `sha256:${(0, import_node_crypto15.createHash)("sha256").update(bytes).digest("hex")}`;
|
|
48633
48720
|
function artifactDrift(path2, committedBytes, rebuiltBytes) {
|
|
48634
48721
|
const committed = committedBytes ? sha256(committedBytes) : ABSENT;
|
|
48635
48722
|
const rebuiltExpected = rebuiltBytes ? sha256(rebuiltBytes) : ABSENT;
|
package/package.json
CHANGED