pr-shepherd 0.53.0 → 0.54.0
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/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/bin/cli/api-usage-formatter.mjs +17 -3
- package/bin/cli/args.mjs +3 -0
- package/bin/cli/check-blocker-handler.d.mts +2 -0
- package/bin/cli/check-blocker-handler.mjs +82 -0
- package/bin/cli/help-command-pages.d.mts +22 -3
- package/bin/cli/help-command-pages.mjs +21 -2
- package/bin/cli/help-iterate-poll-pages.d.mts +1 -1
- package/bin/cli/help-iterate-poll-pages.mjs +1 -1
- package/bin/cli/help-top-page.d.mts +1 -1
- package/bin/cli/help-top-page.mjs +3 -1
- package/bin/cli/help.d.mts +23 -4
- package/bin/cli/help.mjs +2 -0
- package/bin/cli/poll-handler.mjs +5 -1
- package/bin/cli-parser.mjs +4 -0
- package/bin/commands/apply-check-blocker.d.mts +22 -0
- package/bin/commands/apply-check-blocker.mjs +36 -0
- package/bin/commands/check-annotations.d.mts +1 -1
- package/bin/commands/check-annotations.mjs +27 -17
- package/bin/commands/check-blocker-ref.d.mts +3 -0
- package/bin/commands/check-blocker-ref.mjs +50 -0
- package/bin/commands/iterate/api-usage.mjs +5 -4
- package/bin/commands/iterate/check-blocker-gate.d.mts +18 -0
- package/bin/commands/iterate/check-blocker-gate.mjs +115 -0
- package/bin/commands/iterate/check-instructions.d.mts +2 -0
- package/bin/commands/iterate/check-instructions.mjs +4 -0
- package/bin/commands/iterate/fix-code.d.mts +2 -0
- package/bin/commands/iterate/fix-code.mjs +31 -15
- package/bin/commands/iterate/index.mjs +22 -8
- package/bin/commands/poll-quota.d.mts +20 -6
- package/bin/commands/poll-quota.mjs +70 -19
- package/bin/commands/poll-rate-limit-cancel.d.mts +35 -0
- package/bin/commands/poll-rate-limit-cancel.mjs +101 -0
- package/bin/commands/poll-rate-limit-delay.d.mts +1 -0
- package/bin/commands/poll-rate-limit-delay.mjs +7 -0
- package/bin/commands/poll-rate-limit-wait.d.mts +14 -0
- package/bin/commands/poll-rate-limit-wait.mjs +125 -0
- package/bin/commands/poll-summary.mjs +22 -10
- package/bin/commands/poll.mjs +24 -19
- package/bin/commands/quota-selection.d.mts +7 -0
- package/bin/commands/quota-selection.mjs +20 -0
- package/bin/config/load.d.mts +2 -0
- package/bin/config/load.mjs +24 -2
- package/bin/config.json +1 -0
- package/bin/github/check-annotation-cache.d.mts +8 -0
- package/bin/github/check-annotation-cache.mjs +23 -0
- package/bin/github/check-annotation-pages.d.mts +11 -0
- package/bin/github/check-annotation-pages.mjs +36 -0
- package/bin/github/check-annotation-shape.d.mts +21 -0
- package/bin/github/check-annotation-shape.mjs +50 -0
- package/bin/github/check-annotations-batch.d.mts +17 -0
- package/bin/github/check-annotations-batch.mjs +92 -0
- package/bin/github/check-annotations.d.mts +8 -11
- package/bin/github/check-annotations.mjs +14 -103
- package/bin/github/gql/batch-pr.gql +1 -1
- package/bin/github/gql/check-run-annotations-batch.gql +41 -0
- package/bin/github/pagination.d.mts +1 -1
- package/bin/github/pagination.mjs +1 -1
- package/bin/github/poll-summary-check-blockers.d.mts +16 -0
- package/bin/github/poll-summary-check-blockers.mjs +25 -0
- package/bin/github/poll-summary-checks.d.mts +2 -0
- package/bin/github/poll-summary-checks.mjs +35 -20
- package/bin/github/poll-summary-projector.mjs +5 -0
- package/bin/github/queries.d.mts +6 -0
- package/bin/github/queries.mjs +6 -0
- package/bin/quota-budgets.d.mts +16 -0
- package/bin/quota-budgets.mjs +65 -0
- package/bin/quota-warning.mjs +11 -1
- package/bin/state/check-blockers.d.mts +30 -0
- package/bin/state/check-blockers.mjs +88 -0
- package/bin/state/graphql-quota-policy.mjs +1 -1
- package/bin/state/graphql-quota-warnings.d.mts +2 -2
- package/bin/state/graphql-quota-warnings.mjs +5 -4
- package/bin/types/api-usage.d.mts +14 -3
- package/package.json +1 -1
- package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
- package/plugins/pr-shepherd/.codex.mcp.json +1 -1
- package/plugins/pr-shepherd/.mcp.json +1 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { GraphqlQuotaWarningBand } from "./config/load.mts";
|
|
2
|
+
import type { ApiResourceUsage, GraphqlQuotaWarning } from "./types.mts";
|
|
3
|
+
type Usage = Pick<ApiResourceUsage, "resource" | "remaining" | "limit" | "used" | "resetAt">;
|
|
4
|
+
/**
|
|
5
|
+
* One warning for the budgets that are low on this tick. A GraphQL warning
|
|
6
|
+
* recommends REST only when REST core is still above its bands. When both are
|
|
7
|
+
* low, the result uses the later reset and does not recommend a switch.
|
|
8
|
+
*/
|
|
9
|
+
export declare function composeQuotaWarning(input: {
|
|
10
|
+
graphqlWarning?: GraphqlQuotaWarning;
|
|
11
|
+
coreWarning?: GraphqlQuotaWarning;
|
|
12
|
+
graphql?: Usage;
|
|
13
|
+
core?: Usage;
|
|
14
|
+
bands: GraphqlQuotaWarningBand[];
|
|
15
|
+
}): GraphqlQuotaWarning | undefined;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** True when this sample is at or below a configured remaining-percent band. */
|
|
2
|
+
function budgetBelowBand(usage, bands) {
|
|
3
|
+
if (usage === undefined || usage.limit <= 0 || bands.length === 0)
|
|
4
|
+
return false;
|
|
5
|
+
return bands.some((band) => usage.remaining * 100 <= usage.limit * band.remainingPercent);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* One warning for the budgets that are low on this tick. A GraphQL warning
|
|
9
|
+
* recommends REST only when REST core is still above its bands. When both are
|
|
10
|
+
* low, the result uses the later reset and does not recommend a switch.
|
|
11
|
+
*/
|
|
12
|
+
export function composeQuotaWarning(input) {
|
|
13
|
+
const graphqlBelow = budgetBelowBand(input.graphql, input.bands);
|
|
14
|
+
const coreBelow = budgetBelowBand(input.core, input.bands);
|
|
15
|
+
const graphql = input.graphqlWarning;
|
|
16
|
+
const core = input.coreWarning;
|
|
17
|
+
if (graphql && core)
|
|
18
|
+
return combineWarnings(graphql, core);
|
|
19
|
+
if (graphql && coreBelow && input.core)
|
|
20
|
+
return combineWarnings(graphql, describeBudget(input.core, input.bands));
|
|
21
|
+
if (core && graphqlBelow && input.graphql) {
|
|
22
|
+
return combineWarnings(core, describeBudget(input.graphql, input.bands));
|
|
23
|
+
}
|
|
24
|
+
return graphql ?? core;
|
|
25
|
+
}
|
|
26
|
+
function describeBudget(usage, bands) {
|
|
27
|
+
const active = bands
|
|
28
|
+
.filter((band) => usage.remaining * 100 <= usage.limit * band.remainingPercent)
|
|
29
|
+
.reduce((lowest, band) => (band.remainingPercent < lowest.remainingPercent ? band : lowest));
|
|
30
|
+
return {
|
|
31
|
+
resource: usage.resource === "core" ? "core" : "graphql",
|
|
32
|
+
thresholdPercent: active.remainingPercent,
|
|
33
|
+
remaining: usage.remaining,
|
|
34
|
+
limit: usage.limit,
|
|
35
|
+
...(usage.used !== undefined && { used: usage.used }),
|
|
36
|
+
resetAt: usage.resetAt,
|
|
37
|
+
pollIntervalMinutes: active.pollIntervalMinutes,
|
|
38
|
+
pollTimeoutMinutes: active.pollIntervalMinutes * 2,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function combineWarnings(left, right) {
|
|
42
|
+
const later = left.resetAt >= right.resetAt ? left : right;
|
|
43
|
+
const interval = Math.max(left.pollIntervalMinutes, right.pollIntervalMinutes);
|
|
44
|
+
return {
|
|
45
|
+
resource: "combined",
|
|
46
|
+
thresholdPercent: Math.min(left.thresholdPercent, right.thresholdPercent),
|
|
47
|
+
remaining: later.remaining,
|
|
48
|
+
limit: later.limit,
|
|
49
|
+
...(later.used !== undefined && { used: later.used }),
|
|
50
|
+
resetAt: later.resetAt,
|
|
51
|
+
pollIntervalMinutes: interval,
|
|
52
|
+
pollTimeoutMinutes: interval * 2,
|
|
53
|
+
budgets: [toBudget(left), toBudget(right)].sort((a, b) => a.resource.localeCompare(b.resource)),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function toBudget(warning) {
|
|
57
|
+
return {
|
|
58
|
+
resource: warning.resource === "core" ? "core" : "graphql",
|
|
59
|
+
thresholdPercent: warning.thresholdPercent,
|
|
60
|
+
remaining: warning.remaining,
|
|
61
|
+
limit: warning.limit,
|
|
62
|
+
...(warning.used !== undefined && { used: warning.used }),
|
|
63
|
+
resetAt: warning.resetAt,
|
|
64
|
+
};
|
|
65
|
+
}
|
package/bin/quota-warning.mjs
CHANGED
|
@@ -2,5 +2,15 @@ export function buildQuotaAwareContinuation(warning, prefix) {
|
|
|
2
2
|
const interval = `${warning.pollIntervalMinutes}m`;
|
|
3
3
|
const timeout = `${warning.pollTimeoutMinutes}m`;
|
|
4
4
|
const resetTime = new Date(warning.resetAt * 1000).toISOString();
|
|
5
|
-
|
|
5
|
+
const opening = warning.resource === "combined"
|
|
6
|
+
? "GitHub's GraphQL and REST core quotas are both low. Keep using pr-shepherd at the cadence below. Do not shift incidental calls between GraphQL and REST."
|
|
7
|
+
: warning.resource === "core"
|
|
8
|
+
? `GitHub's REST core quota is low (crossed the ${warning.thresholdPercent}% remaining threshold). Keep using pr-shepherd at the cadence below. Do not add incidental REST \`gh\` calls (\`gh pr view\`, \`gh pr review\`, \`gh api\`) while REST core is below its warning threshold.`
|
|
9
|
+
: `GitHub's GraphQL API quota is low (crossed the ${warning.thresholdPercent}% remaining threshold). Keep using pr-shepherd at the cadence below; for incidental PR operations that do not need Shepherd's full snapshot, prefer non-GraphQL \`gh\` CLI commands (e.g. \`gh pr view\`, \`gh pr review\`, \`gh api\` REST endpoints) — they draw on the separate REST budget, not the depleted GraphQL pool.`;
|
|
10
|
+
const resetLabel = warning.resource === "combined"
|
|
11
|
+
? "both quotas have reset"
|
|
12
|
+
: warning.resource === "core"
|
|
13
|
+
? "the REST core quota resets"
|
|
14
|
+
: "the GraphQL quota resets";
|
|
15
|
+
return `${prefix} ${opening} Do not substitute \`gh pr checks\` or \`gh pr watch\` for the Shepherd loop. Resume full-cadence pr-shepherd after ${resetLabel} at ${resetTime}. If you must keep polling before then, poll no more often than every ${warning.pollIntervalMinutes} minutes. With a polling CLI command, preserve the other options, raise any shorter interval and timeout flags to at least \`--interval ${interval} --timeout ${timeout}\`, keep any longer cadence, and omit \`--timeout\` when using \`--until-terminal\`. With a single-tick CLI, API, or MCP call, wait at least ${warning.pollIntervalMinutes} minutes before the next tick.`;
|
|
6
16
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-PR records that a failing check is blocked on an external PR or issue.
|
|
3
|
+
*
|
|
4
|
+
* `$PR_SHEPHERD_STATE_DIR/<owner>/<repo>/<pr>/check-blockers.json`
|
|
5
|
+
*/
|
|
6
|
+
export interface CheckBlockerRef {
|
|
7
|
+
owner: string;
|
|
8
|
+
name: string;
|
|
9
|
+
number: number;
|
|
10
|
+
kind: "pull" | "issue";
|
|
11
|
+
}
|
|
12
|
+
export interface CheckBlockerRecord {
|
|
13
|
+
checkName: string;
|
|
14
|
+
blocker: CheckBlockerRef;
|
|
15
|
+
/** Unix seconds. */
|
|
16
|
+
recordedAt: number;
|
|
17
|
+
}
|
|
18
|
+
interface StateKey {
|
|
19
|
+
owner: string;
|
|
20
|
+
repo: string;
|
|
21
|
+
pr: number;
|
|
22
|
+
}
|
|
23
|
+
export declare function formatCheckBlockerRef(blocker: CheckBlockerRef): string;
|
|
24
|
+
/** Missing, unreadable, or malformed state is an empty list. Does not throw. */
|
|
25
|
+
export declare function readCheckBlockers(key: StateKey): Promise<CheckBlockerRecord[]>;
|
|
26
|
+
/** Insert or replace the record for `checkName`. Returns false when the file cannot be written. */
|
|
27
|
+
export declare function writeCheckBlocker(key: StateKey, record: CheckBlockerRecord): Promise<boolean>;
|
|
28
|
+
/** Drop one check's record. A missing record is success and does not create a file. */
|
|
29
|
+
export declare function clearCheckBlocker(key: StateKey, checkName: string): Promise<boolean>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-PR records that a failing check is blocked on an external PR or issue.
|
|
3
|
+
*
|
|
4
|
+
* `$PR_SHEPHERD_STATE_DIR/<owner>/<repo>/<pr>/check-blockers.json`
|
|
5
|
+
*/
|
|
6
|
+
import { readFile, writeFile, rename, unlink, mkdir } from "node:fs/promises";
|
|
7
|
+
import { randomUUID } from "node:crypto";
|
|
8
|
+
import { dirname } from "node:path";
|
|
9
|
+
import { resolvePrStatePath } from "./base.mjs";
|
|
10
|
+
const FILE = "check-blockers.json";
|
|
11
|
+
export function formatCheckBlockerRef(blocker) {
|
|
12
|
+
return `${blocker.owner}/${blocker.name}#${blocker.number}`;
|
|
13
|
+
}
|
|
14
|
+
/** Missing, unreadable, or malformed state is an empty list. Does not throw. */
|
|
15
|
+
export async function readCheckBlockers(key) {
|
|
16
|
+
try {
|
|
17
|
+
const parsed = JSON.parse(await readFile(resolvePrStatePath(key, FILE), "utf8"));
|
|
18
|
+
if (parsed === null || typeof parsed !== "object")
|
|
19
|
+
return [];
|
|
20
|
+
const blockers = parsed.blockers;
|
|
21
|
+
if (!Array.isArray(blockers))
|
|
22
|
+
return [];
|
|
23
|
+
return blockers.filter(isRecord);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Insert or replace the record for `checkName`. Returns false when the file cannot be written. */
|
|
30
|
+
export async function writeCheckBlocker(key, record) {
|
|
31
|
+
const blockers = (await readCheckBlockers(key)).filter((item) => item.checkName !== record.checkName);
|
|
32
|
+
blockers.push(record);
|
|
33
|
+
return writeAll(key, blockers);
|
|
34
|
+
}
|
|
35
|
+
/** Drop one check's record. A missing record is success and does not create a file. */
|
|
36
|
+
export async function clearCheckBlocker(key, checkName) {
|
|
37
|
+
const current = await readCheckBlockers(key);
|
|
38
|
+
const blockers = current.filter((item) => item.checkName !== checkName);
|
|
39
|
+
if (blockers.length === current.length)
|
|
40
|
+
return true;
|
|
41
|
+
return writeAll(key, blockers);
|
|
42
|
+
}
|
|
43
|
+
async function writeAll(key, blockers) {
|
|
44
|
+
let tmp;
|
|
45
|
+
try {
|
|
46
|
+
const path = resolvePrStatePath(key, FILE);
|
|
47
|
+
tmp = `${path}.${randomUUID()}.tmp`;
|
|
48
|
+
await mkdir(dirname(path), { recursive: true });
|
|
49
|
+
await writeFile(tmp, `${JSON.stringify({ blockers })}\n`, "utf8");
|
|
50
|
+
await rename(tmp, path);
|
|
51
|
+
tmp = undefined;
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
if (tmp !== undefined) {
|
|
59
|
+
try {
|
|
60
|
+
await unlink(tmp);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// The failed write may not have created the temp file.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function isRecord(value) {
|
|
69
|
+
if (value === null || typeof value !== "object")
|
|
70
|
+
return false;
|
|
71
|
+
const record = value;
|
|
72
|
+
return (typeof record.checkName === "string" &&
|
|
73
|
+
record.checkName.length > 0 &&
|
|
74
|
+
typeof record.recordedAt === "number" &&
|
|
75
|
+
Number.isFinite(record.recordedAt) &&
|
|
76
|
+
isBlocker(record.blocker));
|
|
77
|
+
}
|
|
78
|
+
function isBlocker(value) {
|
|
79
|
+
if (value === null || typeof value !== "object")
|
|
80
|
+
return false;
|
|
81
|
+
const blocker = value;
|
|
82
|
+
return ((blocker.kind === "pull" || blocker.kind === "issue") &&
|
|
83
|
+
typeof blocker.owner === "string" &&
|
|
84
|
+
typeof blocker.name === "string" &&
|
|
85
|
+
typeof blocker.number === "number" &&
|
|
86
|
+
Number.isInteger(blocker.number) &&
|
|
87
|
+
blocker.number > 0);
|
|
88
|
+
}
|
|
@@ -32,7 +32,7 @@ export function evaluateGraphqlQuotaWarning(bands, sample, previous, observedAt
|
|
|
32
32
|
return { state };
|
|
33
33
|
return {
|
|
34
34
|
warning: {
|
|
35
|
-
resource: "graphql",
|
|
35
|
+
resource: sample.resource === "core" ? "core" : "graphql",
|
|
36
36
|
thresholdPercent: active.remainingPercent,
|
|
37
37
|
remaining: effective.remaining,
|
|
38
38
|
limit: effective.limit,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { GraphqlQuotaWarningBand } from "../config/load.mts";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ApiResourceUsage, GraphqlQuotaWarning } from "../types.mts";
|
|
3
3
|
export declare function evaluateWorktreeGraphqlQuotaWarning(key: {
|
|
4
4
|
owner: string;
|
|
5
5
|
repo: string;
|
|
6
|
-
}, bands: GraphqlQuotaWarningBand[], sample:
|
|
6
|
+
}, bands: GraphqlQuotaWarningBand[], sample: ApiResourceUsage, persist: boolean, now?: number): Promise<GraphqlQuotaWarning | undefined>;
|
|
@@ -10,9 +10,10 @@ const sessionStates = new Map();
|
|
|
10
10
|
export async function evaluateWorktreeGraphqlQuotaWarning(key, bands, sample, persist, now = Date.now() / 1000) {
|
|
11
11
|
if (bands.length === 0)
|
|
12
12
|
return undefined;
|
|
13
|
-
const
|
|
13
|
+
const quotaResource = sample.resource === "core" ? "core" : "graphql";
|
|
14
|
+
const path = await warningStatePath(key, quotaResource);
|
|
14
15
|
if (path === undefined) {
|
|
15
|
-
const sessionKey = `${key.owner}/${key.repo}`;
|
|
16
|
+
const sessionKey = `${key.owner}/${key.repo}/${quotaResource}`;
|
|
16
17
|
if (!persist) {
|
|
17
18
|
return evaluateGraphqlQuotaWarning(bands, sample, sessionStates.get(sessionKey) ?? null, now)
|
|
18
19
|
.warning;
|
|
@@ -50,7 +51,7 @@ async function serializeStateUpdate(key, update) {
|
|
|
50
51
|
});
|
|
51
52
|
return result;
|
|
52
53
|
}
|
|
53
|
-
async function warningStatePath(key) {
|
|
54
|
+
async function warningStatePath(key, resource) {
|
|
54
55
|
let worktreeKey;
|
|
55
56
|
try {
|
|
56
57
|
worktreeKey = await getWorktreeKey();
|
|
@@ -58,7 +59,7 @@ async function warningStatePath(key) {
|
|
|
58
59
|
catch {
|
|
59
60
|
return undefined;
|
|
60
61
|
}
|
|
61
|
-
return join(resolveRepoStateDir(key), "worktrees", `${worktreeKey}-
|
|
62
|
+
return join(resolveRepoStateDir(key), "worktrees", `${worktreeKey}-${resource}-quota-warnings.json`);
|
|
62
63
|
}
|
|
63
64
|
async function readState(path) {
|
|
64
65
|
try {
|
|
@@ -5,6 +5,8 @@ export interface ApiResourceUsage {
|
|
|
5
5
|
used?: number;
|
|
6
6
|
remaining: number;
|
|
7
7
|
resetAt: number;
|
|
8
|
+
/** Truncated SHA-256 of the credential. Quota state only; summarized usage omits it. */
|
|
9
|
+
credentialFingerprint?: string;
|
|
8
10
|
}
|
|
9
11
|
export interface GraphqlApiUsage extends ApiResourceUsage {
|
|
10
12
|
/** Exact sum reported by rateLimit.cost for GraphQL queries in this command. */
|
|
@@ -13,8 +15,15 @@ export interface GraphqlApiUsage extends ApiResourceUsage {
|
|
|
13
15
|
unmeasuredRequestCount: number;
|
|
14
16
|
/** Exact sum reported by rateLimit.nodeCount for measured GraphQL queries. */
|
|
15
17
|
nodeCount: number;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
}
|
|
19
|
+
/** One budget inside a combined GraphQL and REST core warning. */
|
|
20
|
+
export interface QuotaWarningBudget {
|
|
21
|
+
resource: "graphql" | "core";
|
|
22
|
+
thresholdPercent: number;
|
|
23
|
+
remaining: number;
|
|
24
|
+
limit: number;
|
|
25
|
+
used?: number;
|
|
26
|
+
resetAt: number;
|
|
18
27
|
}
|
|
19
28
|
export interface ApiUsage {
|
|
20
29
|
credentialSources: string[];
|
|
@@ -22,7 +31,7 @@ export interface ApiUsage {
|
|
|
22
31
|
rest?: ApiResourceUsage[];
|
|
23
32
|
}
|
|
24
33
|
export interface GraphqlQuotaWarning {
|
|
25
|
-
resource: "graphql";
|
|
34
|
+
resource: "graphql" | "core" | "combined";
|
|
26
35
|
thresholdPercent: number;
|
|
27
36
|
remaining: number;
|
|
28
37
|
limit: number;
|
|
@@ -30,4 +39,6 @@ export interface GraphqlQuotaWarning {
|
|
|
30
39
|
resetAt: number;
|
|
31
40
|
pollIntervalMinutes: number;
|
|
32
41
|
pollTimeoutMinutes: number;
|
|
42
|
+
/** Both budgets when GraphQL and REST core are low together. */
|
|
43
|
+
budgets?: QuotaWarningBudget[];
|
|
33
44
|
}
|
package/package.json
CHANGED