chore: satisfy lint

This commit is contained in:
Peter Steinberger
2026-01-07 11:48:53 +01:00
parent c74f89c871
commit eef90b47a3
5 changed files with 133 additions and 71 deletions

View File

@@ -20,6 +20,10 @@ import {
saveSessionStore, saveSessionStore,
} from "../../config/sessions.js"; } from "../../config/sessions.js";
import { logVerbose } from "../../globals.js"; import { logVerbose } from "../../globals.js";
import {
formatUsageSummaryLine,
loadProviderUsageSummary,
} from "../../infra/provider-usage.js";
import { triggerClawdbotRestart } from "../../infra/restart.js"; import { triggerClawdbotRestart } from "../../infra/restart.js";
import { enqueueSystemEvent } from "../../infra/system-events.js"; import { enqueueSystemEvent } from "../../infra/system-events.js";
import { parseAgentSessionKey } from "../../routing/session-key.js"; import { parseAgentSessionKey } from "../../routing/session-key.js";
@@ -38,10 +42,6 @@ import {
formatContextUsageShort, formatContextUsageShort,
formatTokenCount, formatTokenCount,
} from "../status.js"; } from "../status.js";
import {
formatUsageSummaryLine,
loadProviderUsageSummary,
} from "../../infra/provider-usage.js";
import type { MsgContext } from "../templating.js"; import type { MsgContext } from "../templating.js";
import type { import type {
ElevatedLevel, ElevatedLevel,

View File

@@ -11,13 +11,13 @@ import {
resolveStorePath, resolveStorePath,
type SessionEntry, type SessionEntry,
} from "../config/sessions.js"; } from "../config/sessions.js";
import { callGateway } from "../gateway/call.js";
import { info } from "../globals.js";
import { buildProviderSummary } from "../infra/provider-summary.js";
import { import {
formatUsageReportLines, formatUsageReportLines,
loadProviderUsageSummary, loadProviderUsageSummary,
} from "../infra/provider-usage.js"; } from "../infra/provider-usage.js";
import { callGateway } from "../gateway/call.js";
import { info } from "../globals.js";
import { buildProviderSummary } from "../infra/provider-summary.js";
import { peekSystemEvents } from "../infra/system-events.js"; import { peekSystemEvents } from "../infra/system-events.js";
import type { RuntimeEnv } from "../runtime.js"; import type { RuntimeEnv } from "../runtime.js";
import { resolveWhatsAppAccount } from "../web/accounts.js"; import { resolveWhatsAppAccount } from "../web/accounts.js";

View File

@@ -7,4 +7,3 @@ export const usageHandlers: GatewayRequestHandlers = {
respond(true, summary, undefined); respond(true, summary, undefined);
}, },
}; };

View File

@@ -56,9 +56,7 @@ describe("provider usage formatting", () => {
{ {
provider: "anthropic", provider: "anthropic",
displayName: "Claude", displayName: "Claude",
windows: [ windows: [{ label: "5h", usedPercent: 20, resetAt: now + 60_000 }],
{ label: "5h", usedPercent: 20, resetAt: now + 60_000 },
],
}, },
], ],
}; };
@@ -69,40 +67,49 @@ describe("provider usage formatting", () => {
describe("provider usage loading", () => { describe("provider usage loading", () => {
it("loads usage snapshots with injected auth", async () => { it("loads usage snapshots with injected auth", async () => {
const makeResponse = (status: number, body: unknown) => const makeResponse = (status: number, body: unknown): Response => {
({ const payload = typeof body === "string" ? body : JSON.stringify(body);
ok: status >= 200 && status < 300, const headers =
status, typeof body === "string"
json: async () => body, ? undefined
}) as any; : { "Content-Type": "application/json" };
return new Response(payload, { status, headers });
};
const mockFetch = vi.fn(async (input: any) => { const mockFetch = vi.fn<Parameters<typeof fetch>, ReturnType<typeof fetch>>(
const url = String(input); async (input) => {
if (url.includes("api.anthropic.com")) { const url =
return makeResponse(200, { typeof input === "string"
five_hour: { utilization: 20, resets_at: "2026-01-07T01:00:00Z" }, ? input
}); : input instanceof URL
} ? input.toString()
if (url.includes("api.z.ai")) { : input.url;
return makeResponse(200, { if (url.includes("api.anthropic.com")) {
success: true, return makeResponse(200, {
code: 200, five_hour: { utilization: 20, resets_at: "2026-01-07T01:00:00Z" },
data: { });
planName: "Pro", }
limits: [ if (url.includes("api.z.ai")) {
{ return makeResponse(200, {
type: "TOKENS_LIMIT", success: true,
percentage: 25, code: 200,
unit: 3, data: {
number: 6, planName: "Pro",
nextResetTime: "2026-01-07T06:00:00Z", limits: [
}, {
], type: "TOKENS_LIMIT",
}, percentage: 25,
}); unit: 3,
} number: 6,
return makeResponse(404, "not found"); nextResetTime: "2026-01-07T06:00:00Z",
}); },
],
},
});
}
return makeResponse(404, "not found");
},
);
const summary = await loadProviderUsageSummary({ const summary = await loadProviderUsageSummary({
now: Date.UTC(2026, 0, 7, 0, 0, 0), now: Date.UTC(2026, 0, 7, 0, 0, 0),

View File

@@ -2,7 +2,6 @@ import fs from "node:fs";
import os from "node:os"; import os from "node:os";
import path from "node:path"; import path from "node:path";
import { loadConfig } from "../config/config.js";
import { import {
ensureAuthProfileStore, ensureAuthProfileStore,
listProfilesForProvider, listProfilesForProvider,
@@ -14,6 +13,7 @@ import {
resolveEnvApiKey, resolveEnvApiKey,
} from "../agents/model-auth.js"; } from "../agents/model-auth.js";
import { normalizeProviderId } from "../agents/model-selection.js"; import { normalizeProviderId } from "../agents/model-selection.js";
import { loadConfig } from "../config/config.js";
export type UsageWindow = { export type UsageWindow = {
label: string; label: string;
@@ -42,6 +42,59 @@ export type UsageProviderId =
| "openai-codex" | "openai-codex"
| "zai"; | "zai";
type ClaudeUsageResponse = {
five_hour?: { utilization?: number; resets_at?: string };
seven_day?: { utilization?: number; resets_at?: string };
seven_day_sonnet?: { utilization?: number };
seven_day_opus?: { utilization?: number };
};
type CopilotUsageResponse = {
quota_snapshots?: {
premium_interactions?: { percent_remaining?: number | null };
chat?: { percent_remaining?: number | null };
};
copilot_plan?: string;
};
type GeminiUsageResponse = {
buckets?: Array<{ modelId?: string; remainingFraction?: number }>;
};
type CodexUsageResponse = {
rate_limit?: {
primary_window?: {
limit_window_seconds?: number;
used_percent?: number;
reset_at?: number;
};
secondary_window?: {
limit_window_seconds?: number;
used_percent?: number;
reset_at?: number;
};
};
plan_type?: string;
credits?: { balance?: number | string | null };
};
type ZaiUsageResponse = {
success?: boolean;
code?: number;
msg?: string;
data?: {
planName?: string;
plan?: string;
limits?: Array<{
type?: string;
percentage?: number;
unit?: number;
number?: number;
nextResetTime?: string;
}>;
};
};
type ProviderAuth = { type ProviderAuth = {
provider: UsageProviderId; provider: UsageProviderId;
token: string; token: string;
@@ -121,8 +174,10 @@ function formatResetRemaining(targetMs?: number, now?: number): string | null {
const days = Math.floor(hours / 24); const days = Math.floor(hours / 24);
if (days < 7) return `${days}d ${hours % 24}h`; if (days < 7) return `${days}d ${hours % 24}h`;
return new Intl.DateTimeFormat("en-US", { month: "short", day: "numeric" }) return new Intl.DateTimeFormat("en-US", {
.format(new Date(targetMs)); month: "short",
day: "numeric",
}).format(new Date(targetMs));
} }
function pickPrimaryWindow(windows: UsageWindow[]): UsageWindow | undefined { function pickPrimaryWindow(windows: UsageWindow[]): UsageWindow | undefined {
@@ -148,11 +203,13 @@ export function formatUsageSummaryLine(
.slice(0, opts?.maxProviders ?? summary.providers.length); .slice(0, opts?.maxProviders ?? summary.providers.length);
if (providers.length === 0) return null; if (providers.length === 0) return null;
const parts = providers.map((entry) => { const parts = providers
const window = pickPrimaryWindow(entry.windows); .map((entry) => {
if (!window) return null; const window = pickPrimaryWindow(entry.windows);
return `${entry.displayName} ${formatWindowShort(window, opts?.now)}`; if (!window) return null;
}).filter(Boolean) as string[]; return `${entry.displayName} ${formatWindowShort(window, opts?.now)}`;
})
.filter(Boolean) as string[];
if (parts.length === 0) return null; if (parts.length === 0) return null;
return `📊 Usage: ${parts.join(" · ")}`; return `📊 Usage: ${parts.join(" · ")}`;
@@ -244,7 +301,7 @@ async function fetchClaudeUsage(
}; };
} }
const data = (await res.json()) as any; const data = (await res.json()) as ClaudeUsageResponse;
const windows: UsageWindow[] = []; const windows: UsageWindow[] = [];
if (data.five_hour?.utilization !== undefined) { if (data.five_hour?.utilization !== undefined) {
@@ -310,12 +367,12 @@ async function fetchCopilotUsage(
}; };
} }
const data = (await res.json()) as any; const data = (await res.json()) as CopilotUsageResponse;
const windows: UsageWindow[] = []; const windows: UsageWindow[] = [];
if (data.quota_snapshots?.premium_interactions) { if (data.quota_snapshots?.premium_interactions) {
const remaining = data.quota_snapshots.premium_interactions const remaining =
.percent_remaining; data.quota_snapshots.premium_interactions.percent_remaining;
windows.push({ windows.push({
label: "Premium", label: "Premium",
usedPercent: clampPercent(100 - (remaining ?? 0)), usedPercent: clampPercent(100 - (remaining ?? 0)),
@@ -367,7 +424,7 @@ async function fetchGeminiUsage(
}; };
} }
const data = (await res.json()) as any; const data = (await res.json()) as GeminiUsageResponse;
const quotas: Record<string, number> = {}; const quotas: Record<string, number> = {};
for (const bucket of data.buckets || []) { for (const bucket of data.buckets || []) {
@@ -395,7 +452,10 @@ async function fetchGeminiUsage(
} }
if (hasPro) { if (hasPro) {
windows.push({ label: "Pro", usedPercent: clampPercent((1 - proMin) * 100) }); windows.push({
label: "Pro",
usedPercent: clampPercent((1 - proMin) * 100),
});
} }
if (hasFlash) { if (hasFlash) {
windows.push({ windows.push({
@@ -445,7 +505,7 @@ async function fetchCodexUsage(
}; };
} }
const data = (await res.json()) as any; const data = (await res.json()) as CodexUsageResponse;
const windows: UsageWindow[] = []; const windows: UsageWindow[] = [];
if (data.rate_limit?.primary_window) { if (data.rate_limit?.primary_window) {
@@ -513,7 +573,7 @@ async function fetchZaiUsage(
}; };
} }
const data = (await res.json()) as any; const data = (await res.json()) as ZaiUsageResponse;
if (!data.success || data.code !== 200) { if (!data.success || data.code !== 200) {
return { return {
provider: "zai", provider: "zai",
@@ -562,8 +622,7 @@ async function fetchZaiUsage(
function resolveZaiApiKey(): string | undefined { function resolveZaiApiKey(): string | undefined {
const envDirect = const envDirect =
process.env.ZAI_API_KEY?.trim() || process.env.ZAI_API_KEY?.trim() || process.env.Z_AI_API_KEY?.trim();
process.env.Z_AI_API_KEY?.trim();
if (envDirect) return envDirect; if (envDirect) return envDirect;
const envResolved = resolveEnvApiKey("zai"); const envResolved = resolveEnvApiKey("zai");
@@ -571,8 +630,7 @@ function resolveZaiApiKey(): string | undefined {
const cfg = loadConfig(); const cfg = loadConfig();
const key = const key =
getCustomProviderApiKey(cfg, "zai") || getCustomProviderApiKey(cfg, "zai") || getCustomProviderApiKey(cfg, "z-ai");
getCustomProviderApiKey(cfg, "z-ai");
if (key) return key; if (key) return key;
const store = ensureAuthProfileStore(); const store = ensureAuthProfileStore();
@@ -637,9 +695,7 @@ async function resolveOAuthToken(params: {
? (cred as { accountId?: string }).accountId ? (cred as { accountId?: string }).accountId
: undefined, : undefined,
}; };
} catch { } catch {}
continue;
}
} }
return null; return null;
@@ -648,9 +704,7 @@ async function resolveOAuthToken(params: {
function resolveOAuthProviders(): UsageProviderId[] { function resolveOAuthProviders(): UsageProviderId[] {
const store = ensureAuthProfileStore(); const store = ensureAuthProfileStore();
const cfg = loadConfig(); const cfg = loadConfig();
const providers = usageProviders.filter((provider) => const providers = usageProviders.filter((provider) => provider !== "zai");
provider !== "zai",
);
return providers.filter((provider) => { return providers.filter((provider) => {
const profiles = listProfilesForProvider(store, provider).filter((id) => { const profiles = listProfilesForProvider(store, provider).filter((id) => {
const cred = store.profiles[id]; const cred = store.profiles[id];
@@ -659,7 +713,9 @@ function resolveOAuthProviders(): UsageProviderId[] {
if (profiles.length > 0) return true; if (profiles.length > 0) return true;
const normalized = normalizeProviderId(provider); const normalized = normalizeProviderId(provider);
const configuredProfiles = Object.entries(cfg.auth?.profiles ?? {}) const configuredProfiles = Object.entries(cfg.auth?.profiles ?? {})
.filter(([, profile]) => normalizeProviderId(profile.provider) === normalized) .filter(
([, profile]) => normalizeProviderId(profile.provider) === normalized,
)
.map(([id]) => id) .map(([id]) => id)
.filter((id) => store.profiles[id]?.type === "oauth"); .filter((id) => store.profiles[id]?.type === "oauth");
return configuredProfiles.length > 0; return configuredProfiles.length > 0;