stricter checks
CI / verify (pull_request) Successful in 12s

This commit is contained in:
2026-10-10 15:16:53 +02:00
parent ced43fbb11
commit 513d51f702
30 changed files with 984 additions and 470 deletions
+38 -25
View File
@@ -11,11 +11,15 @@ const TIMEOUT_MS = 5_000;
const RETRY_MS = 2_000;
const RETRIES = 4;
const WEEK = 604_800;
const jsonValue = z.json();
type QuotaFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
export function claudeCredentialsPath() {
return join(process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), ".claude"), ".credentials.json");
function claudeCredentialsPath() {
return join(
process.env["CLAUDE_CONFIG_DIR"] ?? join(homedir(), ".claude"),
".credentials.json",
);
}
const credentials = z.object({
@@ -27,7 +31,7 @@ const credentials = z.object({
});
const usageWindow = z
.object({ utilization: z.number().finite(), resets_at: z.string().nullish() })
.object({ utilization: z.number(), resets_at: z.string().nullish() })
.nullish();
const usagePayload = z.object({
five_hour: usageWindow,
@@ -38,7 +42,7 @@ const usagePayload = z.object({
.array(
z.object({
kind: z.string(),
percent: z.number().finite(),
percent: z.number(),
resets_at: z.string().nullish(),
scope: z
.object({
@@ -63,7 +67,7 @@ function window(
label,
seconds,
used: Math.max(0, Math.min(100, used)),
resetsAt: resetsAt ? Date.parse(resetsAt) : Number.NaN,
resetsAt: resetsAt === null || resetsAt === undefined ? Number.NaN : Date.parse(resetsAt),
};
}
@@ -173,31 +177,14 @@ export class ClaudeQuotaTracker {
const credential = parsed.data.claudeAiOauth;
if (this.disposed) return true;
if (credential.expiresAt <= Date.now()) return false;
const controller = new AbortController();
this.controller = controller;
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
let response: Response;
try {
response = await this.fetcher(USAGE_URL, {
headers: {
Authorization: `Bearer ${credential.accessToken}`,
"anthropic-beta": "oauth-2025-04-20",
Accept: "application/json",
},
signal: controller.signal,
});
} finally {
clearTimeout(timeout);
if (this.controller === controller) this.controller = undefined;
}
const response = await this.fetchUsage(credential.accessToken);
if (!response.ok) return false;
// SAFETY: Response.json yields JSON values; usagePayload validates fields used.
const payload = (await response.json()) as Schema.Json;
const payload = jsonValue.parse(await response.json());
const snapshot = normalizeClaudeQuota(
payload,
credential.subscriptionType ?? "unknown",
);
if (this.disposed) return true;
if (this.isDisposed()) return true;
if (!snapshot) return false;
this.latest = snapshot;
} catch {
@@ -207,6 +194,32 @@ export class ClaudeQuotaTracker {
return true;
}
private async fetchUsage(accessToken: string) {
const controller = new AbortController();
this.controller = controller;
const timeout = setTimeout(() => {
controller.abort();
}, TIMEOUT_MS);
try {
return await this.fetcher(USAGE_URL, {
headers: {
Authorization: `Bearer ${accessToken}`,
"anthropic-beta": "oauth-2025-04-20",
Accept: "application/json",
},
signal: controller.signal,
});
} finally {
clearTimeout(timeout);
if (this.controller === controller) this.controller = undefined;
}
}
/** Reads the flag through a call so checks after `await` are not narrowed away. */
private isDisposed() {
return this.disposed;
}
status(): QuotaStatus | undefined {
if (!this.latest) return;
return {