Refactor application architecture and shared functionality

This commit is contained in:
Jakub Syty
2026-08-21 10:59:47 +02:00
398 changed files with 28819 additions and 4361 deletions
@@ -1,107 +0,0 @@
import { readAuthFile } from '../../opencode/auth.js';
import {
getAuthEntry,
normalizeAuthEntry,
buildResult,
toUsageWindow,
toNumber,
toTimestamp
} from '../utils/index.js';
export const providerId = 'claude';
export const providerName = 'Claude';
const aliases = ['anthropic', 'claude'];
export const isConfigured = () => {
const auth = readAuthFile();
const entry = normalizeAuthEntry(getAuthEntry(auth, aliases));
return Boolean(entry?.access || entry?.token);
};
export const fetchQuota = async () => {
const auth = readAuthFile();
const entry = normalizeAuthEntry(getAuthEntry(auth, aliases));
const accessToken = entry?.access ?? entry?.token;
if (!accessToken) {
return buildResult({
providerId,
providerName,
ok: false,
configured: false,
error: 'Not configured'
});
}
try {
const response = await fetch('https://api.anthropic.com/api/oauth/usage', {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`,
'anthropic-beta': 'oauth-2025-04-20'
}
});
if (!response.ok) {
return buildResult({
providerId,
providerName,
ok: false,
configured: true,
error: `API error: ${response.status}`
});
}
const payload = await response.json();
const windows = {};
const fiveHour = payload?.five_hour ?? null;
const sevenDay = payload?.seven_day ?? null;
const sevenDaySonnet = payload?.seven_day_sonnet ?? null;
const sevenDayOpus = payload?.seven_day_opus ?? null;
if (fiveHour) {
windows['5h'] = toUsageWindow({
usedPercent: toNumber(fiveHour.utilization),
windowSeconds: null,
resetAt: toTimestamp(fiveHour.resets_at)
});
}
if (sevenDay) {
windows['7d'] = toUsageWindow({
usedPercent: toNumber(sevenDay.utilization),
windowSeconds: null,
resetAt: toTimestamp(sevenDay.resets_at)
});
}
if (sevenDaySonnet) {
windows['7d-sonnet'] = toUsageWindow({
usedPercent: toNumber(sevenDaySonnet.utilization),
windowSeconds: null,
resetAt: toTimestamp(sevenDaySonnet.resets_at)
});
}
if (sevenDayOpus) {
windows['7d-opus'] = toUsageWindow({
usedPercent: toNumber(sevenDayOpus.utilization),
windowSeconds: null,
resetAt: toTimestamp(sevenDayOpus.resets_at)
});
}
return buildResult({
providerId,
providerName,
ok: true,
configured: true,
usage: { windows }
});
} catch (error) {
return buildResult({
providerId,
providerName,
ok: false,
configured: true,
error: error instanceof Error ? error.message : 'Request failed'
});
}
};
@@ -0,0 +1,113 @@
/**
* Claude credential discovery.
*
* Claude Code is the primary source: on macOS it keeps its OAuth tokens in the
* login Keychain, elsewhere in a credentials file. OpenCode's own `auth.json`
* entry is the fallback for users who signed into Anthropic through OpenCode
* instead of Claude Code.
*
* Every source is read-only. Claude rotates a Keychain/credentials entry from
* under us whenever Claude Code refreshes, so credentials are read fresh per
* request rather than cached; a stale cached token would outlive the record it
* came from.
*
* @module quota/providers/claude/auth
*/
import { execFileSync } from 'child_process';
import os from 'os';
import path from 'path';
import { readAuthFile } from '../../../opencode/auth.js';
import { asObject, asNonEmptyString, normalizeTimestamp, getAuthEntry, normalizeAuthEntry, readJsonFile } from '../../utils/index.js';
const KEYCHAIN_SERVICE = 'Claude Code-credentials';
const OPENCODE_AUTH_ALIASES = ['anthropic', 'claude'];
/**
* @typedef {object} ClaudeCredential
* @property {string} accessToken
* @property {string|null} refreshToken
* @property {number|null} expiresAt Epoch milliseconds, when the source reports it.
* @property {string|null} planLabel Subscription tier reported by Claude Code, e.g. `max`.
* @property {'keychain'|'credentials-file'|'opencode-auth'|'env'} source
*/
const claudeConfigDirectory = () => {
const override = asNonEmptyString(process.env.CLAUDE_CONFIG_DIR);
return override ? path.resolve(override) : path.join(os.homedir(), '.claude');
};
/**
* Claude Code writes one JSON blob holding both its own OAuth tokens
* (`claudeAiOauth`) and unrelated MCP server tokens. Only the former is read.
*/
const parseClaudeCodeBlob = (blob, source) => {
const oauth = asObject(asObject(blob)?.claudeAiOauth);
const accessToken = asNonEmptyString(oauth?.accessToken);
if (!accessToken) return null;
return {
accessToken,
refreshToken: asNonEmptyString(oauth.refreshToken),
expiresAt: normalizeTimestamp(oauth.expiresAt),
planLabel: asNonEmptyString(oauth.subscriptionType),
source
};
};
const readKeychainCredential = () => {
if (process.platform !== 'darwin') return null;
let raw;
try {
raw = execFileSync('security', ['find-generic-password', '-s', KEYCHAIN_SERVICE, '-w'], {
encoding: 'utf8',
timeout: 10_000,
stdio: ['ignore', 'pipe', 'ignore']
});
} catch {
// No entry, or the user denied Keychain access. Both mean "try the next source".
return null;
}
try {
return parseClaudeCodeBlob(JSON.parse(raw.trim()), 'keychain');
} catch {
console.warn('Claude quota: Keychain credentials are not valid JSON');
return null;
}
};
const readCredentialsFile = () =>
parseClaudeCodeBlob(readJsonFile(path.join(claudeConfigDirectory(), '.credentials.json')), 'credentials-file');
const readOpenCodeCredential = () => {
const entry = normalizeAuthEntry(getAuthEntry(readAuthFile(), OPENCODE_AUTH_ALIASES));
const accessToken = asNonEmptyString(entry?.access) ?? asNonEmptyString(entry?.token);
if (!accessToken) return null;
return {
accessToken,
refreshToken: asNonEmptyString(entry.refresh),
expiresAt: normalizeTimestamp(entry.expires),
planLabel: null,
source: 'opencode-auth'
};
};
const readEnvCredential = () => {
const accessToken = asNonEmptyString(process.env.CLAUDE_CODE_OAUTH_TOKEN);
if (!accessToken) return null;
return { accessToken, refreshToken: null, expiresAt: null, planLabel: null, source: 'env' };
};
/**
* First credential a source can produce, in priority order.
*
* The Keychain wins over the credentials file because on macOS the file is a
* leftover that Claude Code no longer updates.
*
* @returns {ClaudeCredential|null}
*/
export const loadClaudeCredential = () =>
readKeychainCredential()
?? readCredentialsFile()
?? readOpenCodeCredential()
?? readEnvCredential();
@@ -0,0 +1,117 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
const execFileSync = vi.fn();
const files = new Map();
const openCodeAuth = vi.fn(() => ({}));
vi.mock('child_process', () => ({ execFileSync: (...args) => execFileSync(...args) }));
vi.mock('fs', () => {
const fs = {
existsSync: (filePath) => files.has(filePath),
readFileSync: (filePath) => {
if (!files.has(filePath)) throw new Error('ENOENT');
return files.get(filePath);
},
};
return { ...fs, default: fs };
});
vi.mock('../../../opencode/auth.js', () => ({ readAuthFile: () => openCodeAuth() }));
import { loadClaudeCredential } from './auth.js';
const claudeCodeBlob = (accessToken) => JSON.stringify({
mcpOAuth: { 'linear|abc': { accessToken: 'unrelated-mcp-token' } },
claudeAiOauth: {
accessToken,
refreshToken: `${accessToken}-refresh`,
expiresAt: 1786735755912,
subscriptionType: 'max',
},
});
const withPlatform = (platform, run) => {
const original = Object.getOwnPropertyDescriptor(process, 'platform');
Object.defineProperty(process, 'platform', { value: platform, configurable: true });
try {
return run();
} finally {
Object.defineProperty(process, 'platform', original);
}
};
beforeEach(() => {
files.clear();
execFileSync.mockReset();
execFileSync.mockImplementation(() => { throw new Error('no keychain entry'); });
openCodeAuth.mockReturnValue({});
delete process.env.CLAUDE_CONFIG_DIR;
delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
});
afterEach(() => {
delete process.env.CLAUDE_CONFIG_DIR;
delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
});
describe('Claude credential discovery', () => {
it('prefers the macOS Keychain over a stale credentials file', () => {
execFileSync.mockReturnValue(claudeCodeBlob('keychain-token'));
files.set(`${process.env.HOME}/.claude/.credentials.json`, claudeCodeBlob('file-token'));
const credential = withPlatform('darwin', loadClaudeCredential);
expect(credential.accessToken).toBe('keychain-token');
expect(credential.refreshToken).toBe('keychain-token-refresh');
expect(credential.planLabel).toBe('max');
expect(credential.source).toBe('keychain');
});
it('reads the credentials file on Linux, where there is no Keychain', () => {
files.set(`${process.env.HOME}/.claude/.credentials.json`, claudeCodeBlob('file-token'));
const credential = withPlatform('linux', loadClaudeCredential);
expect(execFileSync).not.toHaveBeenCalled();
expect(credential.accessToken).toBe('file-token');
expect(credential.source).toBe('credentials-file');
});
it('honours CLAUDE_CONFIG_DIR when locating the credentials file', () => {
process.env.CLAUDE_CONFIG_DIR = '/tmp/claude-home';
files.set('/tmp/claude-home/.credentials.json', claudeCodeBlob('custom-dir-token'));
expect(withPlatform('linux', loadClaudeCredential).accessToken).toBe('custom-dir-token');
});
it('falls back to the OpenCode auth entry when Claude Code is not signed in', () => {
openCodeAuth.mockReturnValue({ anthropic: { access: 'opencode-token', refresh: 'opencode-refresh', expires: 1786735755912 } });
const credential = withPlatform('linux', loadClaudeCredential);
expect(credential.accessToken).toBe('opencode-token');
expect(credential.source).toBe('opencode-auth');
expect(credential.planLabel).toBeNull();
});
it('falls back to CLAUDE_CODE_OAUTH_TOKEN last, without a refresh token', () => {
process.env.CLAUDE_CODE_OAUTH_TOKEN = 'env-token';
const credential = withPlatform('linux', loadClaudeCredential);
expect(credential.accessToken).toBe('env-token');
expect(credential.refreshToken).toBeNull();
expect(credential.source).toBe('env');
});
it('ignores a Keychain blob that only holds unrelated MCP tokens', () => {
execFileSync.mockReturnValue(JSON.stringify({ mcpOAuth: { 'linear|abc': { accessToken: 'unrelated' } } }));
expect(withPlatform('darwin', loadClaudeCredential)).toBeNull();
});
it('returns null when every source is empty', () => {
expect(withPlatform('darwin', loadClaudeCredential)).toBeNull();
});
});
@@ -0,0 +1,151 @@
/**
* Claude subscription quota.
*
* Reports the plan limits Claude Code itself is bound by (rolling session
* window, weekly windows, model-scoped weekly windows, and paid extra usage)
* using the OAuth credential Claude Code already holds.
*
* @module quota/providers/claude
*/
import { createHash } from 'crypto';
import { buildResult } from '../../utils/index.js';
import { loadClaudeCredential } from './auth.js';
import { toClaudeUsage } from './transforms.js';
export const providerId = 'claude';
export const providerName = 'Claude';
export const aliases = ['anthropic', 'claude'];
const USAGE_URL = 'https://api.anthropic.com/api/oauth/usage';
const OAUTH_BETA_HEADER = 'oauth-2025-04-20';
const DEFAULT_COOLDOWN_MS = 5 * 60 * 1000;
const MAX_COOLDOWN_MS = 60 * 60 * 1000;
/**
* Last good payload, kept only to survive Anthropic's aggressive rate limiting.
* Keyed by credential fingerprint so a second account never sees the first
* account's numbers.
*
* @type {{ fingerprint: string, usage: object, planLabel: string|null }|null}
*/
let cachedUsage = null;
let cooldownUntil = 0;
let pendingFetch = null;
const fingerprintOf = (credential) =>
createHash('sha256').update(`${credential.accessToken}\0${credential.refreshToken ?? ''}`).digest('hex');
const cooldownFromHeader = (response) => {
const raw = response.headers.get('retry-after');
const retryAfter = Number(raw);
if (Number.isFinite(retryAfter) && retryAfter > 0) {
return Math.min(retryAfter * 1000, MAX_COOLDOWN_MS);
}
const retryAt = raw ? Date.parse(raw) : Number.NaN;
if (Number.isFinite(retryAt) && retryAt > Date.now()) {
return Math.min(retryAt - Date.now(), MAX_COOLDOWN_MS);
}
return DEFAULT_COOLDOWN_MS;
};
const cachedResultFor = (fingerprint, planLabel) => {
if (!cachedUsage || cachedUsage.fingerprint !== fingerprint) return null;
return buildResult({
providerId,
providerName,
ok: true,
configured: true,
usage: cachedUsage.usage,
planLabel: planLabel ?? cachedUsage.planLabel
});
};
const failure = (error, { configured = true } = {}) =>
buildResult({ providerId, providerName, ok: false, configured, error });
export const isConfigured = () => Boolean(loadClaudeCredential());
const fetchQuotaUncoalesced = async () => {
const credential = loadClaudeCredential();
if (!credential) {
return failure('Not configured', { configured: false });
}
const fingerprint = fingerprintOf(credential);
if (cachedUsage && cachedUsage.fingerprint !== fingerprint) {
cachedUsage = null;
cooldownUntil = 0;
}
if (Date.now() < cooldownUntil) {
return cachedResultFor(fingerprint, credential.planLabel)
?? failure('Rate limited. Retrying soon.');
}
let response;
try {
response = await fetch(USAGE_URL, {
method: 'GET',
headers: {
Authorization: `Bearer ${credential.accessToken}`,
'anthropic-beta': OAUTH_BETA_HEADER
}
});
} catch (error) {
return failure(error instanceof Error ? error.message : 'Request failed');
}
if (response.status === 429) {
cooldownUntil = Date.now() + cooldownFromHeader(response);
return cachedResultFor(fingerprint, credential.planLabel)
?? failure('Rate limited. Retrying soon.');
}
if (response.status === 401 || response.status === 403) {
return failure('Claude session expired. Open Claude Code to sign in again.');
}
if (!response.ok) {
return failure(`API error: ${response.status}`);
}
let payload;
try {
payload = await response.json();
} catch {
return failure('Unexpected response from Anthropic');
}
const { windows, models } = toClaudeUsage(payload);
const usage = Object.keys(models).length > 0 ? { windows, models } : { windows };
cachedUsage = { fingerprint, usage, planLabel: credential.planLabel };
return buildResult({
providerId,
providerName,
ok: true,
configured: true,
usage,
planLabel: credential.planLabel
});
};
export const fetchQuota = () => {
if (pendingFetch) return pendingFetch;
const request = fetchQuotaUncoalesced();
const pending = request.finally(() => {
if (pendingFetch === pending) pendingFetch = null;
});
pendingFetch = pending;
return pendingFetch;
};
/** Test seam: clears the rate-limit cache between cases. */
export const resetClaudeQuotaCache = () => {
cachedUsage = null;
cooldownUntil = 0;
pendingFetch = null;
};
@@ -0,0 +1,147 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
const credential = vi.fn();
vi.mock('./auth.js', () => ({
loadClaudeCredential: () => credential(),
}));
import { fetchQuota, isConfigured, resetClaudeQuotaCache } from './index.js';
const CREDENTIAL = {
accessToken: 'access-a',
refreshToken: 'refresh-a',
expiresAt: Date.now() + 3_600_000,
planLabel: 'max',
source: 'keychain',
};
const PAYLOAD = {
limits: [
{ kind: 'session', percent: 5, resets_at: '2026-08-14T19:10:00Z', scope: null },
{ kind: 'weekly_all', percent: 4, resets_at: '2026-08-20T15:00:00Z', scope: null },
],
};
const jsonResponse = (body) => ({
ok: true,
status: 200,
headers: new Headers(),
json: async () => body,
});
const errorResponse = (status, headers = {}) => ({
ok: false,
status,
headers: new Headers(headers),
json: async () => ({}),
});
beforeEach(() => {
resetClaudeQuotaCache();
credential.mockReturnValue(CREDENTIAL);
});
afterEach(() => {
vi.unstubAllGlobals();
});
describe('Claude quota provider', () => {
it('reports not configured when no credential source has a token', async () => {
credential.mockReturnValue(null);
expect(isConfigured()).toBe(false);
const result = await fetchQuota();
expect(result.configured).toBe(false);
expect(result.ok).toBe(false);
expect(result.usage).toBeNull();
});
it('returns windows and the plan label from the credential', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(jsonResponse(PAYLOAD)));
const result = await fetchQuota();
expect(result.ok).toBe(true);
expect(result.planLabel).toBe('max');
expect(result.usage.windows['5h'].usedPercent).toBe(5);
});
it('coalesces concurrent refreshes into one Anthropic request', async () => {
let resolveResponse;
const fetchMock = vi.fn().mockReturnValue(new Promise((resolve) => {
resolveResponse = resolve;
}));
vi.stubGlobal('fetch', fetchMock);
const first = fetchQuota();
const second = fetchQuota();
resolveResponse(jsonResponse(PAYLOAD));
const [firstResult, secondResult] = await Promise.all([first, second]);
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(firstResult.ok).toBe(true);
expect(secondResult.ok).toBe(true);
});
it('keeps serving the last good values while Anthropic rate limits', async () => {
const fetchMock = vi.fn()
.mockResolvedValueOnce(jsonResponse(PAYLOAD))
.mockResolvedValueOnce(errorResponse(429, { 'retry-after': '120' }));
vi.stubGlobal('fetch', fetchMock);
await fetchQuota();
const rateLimited = await fetchQuota();
expect(rateLimited.ok).toBe(true);
expect(rateLimited.usage.windows['5h'].usedPercent).toBe(5);
});
it('does not call Anthropic again while the rate-limit cooldown is active', async () => {
const fetchMock = vi.fn()
.mockResolvedValueOnce(jsonResponse(PAYLOAD))
.mockResolvedValueOnce(errorResponse(429));
vi.stubGlobal('fetch', fetchMock);
await fetchQuota();
await fetchQuota();
await fetchQuota();
expect(fetchMock).toHaveBeenCalledTimes(2);
});
it('never shows one account cached values after the credential changes', async () => {
const fetchMock = vi.fn()
.mockResolvedValueOnce(jsonResponse(PAYLOAD))
.mockResolvedValueOnce(errorResponse(429));
vi.stubGlobal('fetch', fetchMock);
await fetchQuota();
credential.mockReturnValue({ ...CREDENTIAL, accessToken: 'access-b', refreshToken: 'refresh-b', planLabel: null });
const afterSwitch = await fetchQuota();
expect(afterSwitch.ok).toBe(false);
expect(afterSwitch.usage).toBeNull();
});
it('explains an expired session instead of reporting a bare 401', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(errorResponse(401)));
const result = await fetchQuota();
expect(result.ok).toBe(false);
expect(result.configured).toBe(true);
expect(result.error).toContain('Claude Code');
});
it('surfaces a network failure instead of an empty successful result', async () => {
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('socket hang up')));
const result = await fetchQuota();
expect(result.ok).toBe(false);
expect(result.usage).toBeNull();
expect(result.error).toBe('socket hang up');
});
});
@@ -0,0 +1,127 @@
/**
* Shapes Anthropic's OAuth usage payload into quota windows.
*
* The payload carries the same limit twice: a legacy set of named fields
* (`five_hour`, `seven_day`, ...) and a newer `limits` array. Only the array
* reports model-scoped limits, and Anthropic ships new limit kinds there under
* rotating internal code names, so limits are read from the array by `kind` and
* fall back to the legacy fields when the array is missing.
*
* @module quota/providers/claude/transforms
*/
import { asObject, asNonEmptyString, toNumber, toTimestamp, toUsageWindow, formatMoney } from '../../utils/index.js';
const SESSION_WINDOW = '5h';
const WEEKLY_WINDOW = '7d';
const EXTRA_USAGE_WINDOW = 'extra_usage';
// Consumers rank limits by how soon they run out, so each window carries its
// duration. Extra usage is a monthly spend cap, not a rolling window.
const SESSION_WINDOW_SECONDS = 5 * 60 * 60;
const WEEKLY_WINDOW_SECONDS = 7 * 24 * 60 * 60;
const asArray = (value) => (Array.isArray(value) ? value : []);
/** Money in Anthropic's minor-unit form, e.g. `{ amount_minor: 10000, exponent: 2 }`. */
const toAmount = (value) => {
const money = asObject(value);
const minor = toNumber(money?.amount_minor);
if (minor === null) return null;
const exponent = toNumber(money?.exponent) ?? 2;
return minor / 10 ** exponent;
};
const formatSpendLabel = (used, limit, currency) => {
const usedLabel = formatMoney(used);
if (usedLabel === null) return null;
const prefix = currency === 'USD' || !currency ? '$' : `${currency} `;
const limitLabel = formatMoney(limit);
return limitLabel === null ? `${prefix}${usedLabel}` : `${prefix}${usedLabel} / ${prefix}${limitLabel}`;
};
const addWindow = (target, key, { percent, resetAt, valueLabel, windowSeconds = null }) => {
if (percent === null && !valueLabel) return;
target[key] = toUsageWindow({ usedPercent: percent, windowSeconds, resetAt, valueLabel });
};
const applyLimitsArray = (limits, windows, models) => {
for (const entry of limits) {
const limit = asObject(entry);
if (!limit) continue;
const percent = toNumber(limit.percent);
const resetAt = toTimestamp(limit.resets_at);
const modelName = asNonEmptyString(asObject(asObject(limit.scope)?.model)?.display_name);
if (limit.kind === 'session') {
addWindow(windows, SESSION_WINDOW, { percent, resetAt, windowSeconds: SESSION_WINDOW_SECONDS });
continue;
}
if (limit.kind === 'weekly_all') {
addWindow(windows, WEEKLY_WINDOW, { percent, resetAt, windowSeconds: WEEKLY_WINDOW_SECONDS });
continue;
}
if (limit.kind === 'weekly_scoped' && modelName) {
const modelWindows = {};
addWindow(modelWindows, WEEKLY_WINDOW, { percent, resetAt, windowSeconds: WEEKLY_WINDOW_SECONDS });
if (Object.keys(modelWindows).length > 0) models[modelName] = { windows: modelWindows };
}
}
};
const applyLegacyFields = (payload, windows) => {
const fiveHour = asObject(payload.five_hour);
const sevenDay = asObject(payload.seven_day);
if (fiveHour) {
addWindow(windows, SESSION_WINDOW, {
percent: toNumber(fiveHour.utilization),
resetAt: toTimestamp(fiveHour.resets_at),
windowSeconds: SESSION_WINDOW_SECONDS
});
}
if (sevenDay) {
addWindow(windows, WEEKLY_WINDOW, {
percent: toNumber(sevenDay.utilization),
resetAt: toTimestamp(sevenDay.resets_at),
windowSeconds: WEEKLY_WINDOW_SECONDS
});
}
};
/**
* Extra usage is the paid overflow beyond plan limits. It is only meaningful
* once the account has it enabled; a disabled block would render as a permanent
* empty bar.
*/
const applyExtraUsage = (payload, windows) => {
const spend = asObject(payload.spend);
if (!spend || spend.enabled !== true) return;
const used = toAmount(spend.used);
const limit = toAmount(spend.limit);
addWindow(windows, EXTRA_USAGE_WINDOW, {
percent: toNumber(spend.percent),
resetAt: null,
valueLabel: formatSpendLabel(used, limit, asNonEmptyString(asObject(spend.used)?.currency))
});
};
/**
* @param {unknown} rawPayload Parsed JSON body of the OAuth usage endpoint.
* @returns {{ windows: Record<string, object>, models: Record<string, object> }}
*/
export const toClaudeUsage = (rawPayload) => {
const payload = asObject(rawPayload) ?? {};
const windows = {};
const models = {};
const limits = asArray(payload.limits);
if (limits.length > 0) {
applyLimitsArray(limits, windows, models);
} else {
applyLegacyFields(payload, windows);
}
applyExtraUsage(payload, windows);
return { windows, models };
};
@@ -0,0 +1,82 @@
import { describe, expect, it } from 'vitest';
import { toClaudeUsage } from './transforms.js';
// Trimmed capture of GET https://api.anthropic.com/api/oauth/usage for a Max account.
const LIVE_PAYLOAD = {
five_hour: { utilization: 5.0, resets_at: '2026-08-14T19:10:00.313090+00:00' },
seven_day: { utilization: 4.0, resets_at: '2026-08-20T15:00:00.313112+00:00' },
seven_day_opus: null,
seven_day_sonnet: null,
nimbus_quill: { utilization: 0.0, resets_at: null },
limits: [
{ kind: 'session', group: 'session', percent: 5, resets_at: '2026-08-14T19:10:00.313090+00:00', scope: null, is_active: true },
{ kind: 'weekly_all', group: 'weekly', percent: 4, resets_at: '2026-08-20T15:00:00.313112+00:00', scope: null, is_active: false },
{
kind: 'weekly_scoped',
group: 'weekly',
percent: 12,
resets_at: '2026-08-20T15:00:00.313301+00:00',
scope: { model: { id: null, display_name: 'Fable' }, surface: null },
is_active: false
}
],
spend: {
used: { amount_minor: 250, currency: 'USD', exponent: 2 },
limit: { amount_minor: 10000, currency: 'USD', exponent: 2 },
percent: 2.5,
enabled: true
}
};
describe('Claude usage transforms', () => {
it('maps the limits array to session, weekly, and model-scoped windows', () => {
const { windows, models } = toClaudeUsage(LIVE_PAYLOAD);
expect(windows['5h'].usedPercent).toBe(5);
expect(windows['5h'].resetAt).toBe(Date.parse('2026-08-14T19:10:00.313090+00:00'));
expect(windows['7d'].usedPercent).toBe(4);
expect(models.Fable.windows['7d'].usedPercent).toBe(12);
});
it('reports each window duration so callers can rank limits by urgency', () => {
const { windows, models } = toClaudeUsage(LIVE_PAYLOAD);
expect(windows['5h'].windowSeconds).toBe(5 * 60 * 60);
expect(windows['7d'].windowSeconds).toBe(7 * 24 * 60 * 60);
expect(models.Fable.windows['7d'].windowSeconds).toBe(7 * 24 * 60 * 60);
expect(windows.extra_usage.windowSeconds).toBeNull();
});
it('reports extra usage as a spend window with a money label', () => {
const { windows } = toClaudeUsage(LIVE_PAYLOAD);
expect(windows.extra_usage.usedPercent).toBe(2.5);
expect(windows.extra_usage.valueLabel).toBe('$2.50 / $100.00');
});
it('omits extra usage when the account has it disabled', () => {
const { windows } = toClaudeUsage({ ...LIVE_PAYLOAD, spend: { ...LIVE_PAYLOAD.spend, enabled: false } });
expect(windows.extra_usage).toBeUndefined();
});
it('falls back to the legacy named fields when no limits array is present', () => {
const { windows, models } = toClaudeUsage({ five_hour: LIVE_PAYLOAD.five_hour, seven_day: LIVE_PAYLOAD.seven_day });
expect(windows['5h'].usedPercent).toBe(5);
expect(windows['7d'].usedPercent).toBe(4);
expect(models).toEqual({});
});
it('ignores unknown limit kinds instead of inventing windows for them', () => {
const { windows } = toClaudeUsage({ limits: [{ kind: 'iguana_necktie', percent: 90, resets_at: null }] });
expect(windows).toEqual({});
});
it('returns empty usage for a malformed payload', () => {
expect(toClaudeUsage(null)).toEqual({ windows: {}, models: {} });
expect(toClaudeUsage({ limits: 'nope' })).toEqual({ windows: {}, models: {} });
});
});
@@ -0,0 +1,90 @@
import { readAuthFile } from '../../opencode/auth.js';
import { asObject, buildResult, getAuthEntry, normalizeAuthEntry, toNumber, toUsageWindow } from '../utils/index.js';
export const providerId = 'command-code';
export const providerName = 'Command Code';
export const aliases = ['command-code'];
const API_BASE_URL = 'https://api.commandcode.ai';
const getApiKey = (auth = readAuthFile()) => {
const entry = normalizeAuthEntry(getAuthEntry(auth, aliases));
const stored = entry?.key ?? entry?.access ?? entry?.token;
return (typeof stored === 'string' ? stored.trim() : '') || process.env.COMMAND_CODE_API_KEY?.trim() || null;
};
const requestJson = async (path, apiKey, fetchImpl) => {
const response = await fetchImpl(`${API_BASE_URL}${path}`, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${apiKey}`,
'User-Agent': 'OpenChamber quota provider',
},
signal: AbortSignal.timeout(15_000),
});
if (response.status === 401 || response.status === 403) throw new Error('Command Code authentication failed');
if (!response.ok) throw new Error(`Command Code usage API returned HTTP ${response.status}`);
return response.json().catch(() => null);
};
const formatCredits = (value) => String(Math.round((value + Number.EPSILON) * 100) / 100);
const toBalanceWindow = (value) => toUsageWindow({
usedPercent: null,
windowSeconds: null,
resetAt: null,
valueLabel: formatCredits(value),
});
export const parseCommandCodeCredits = (payload) => {
const root = asObject(payload);
const credits = asObject(root?.credits);
const limits = asObject(root?.windowLimits);
const windows = {};
for (const [label, field] of [['monthly_credits', 'monthlyCredits'], ['purchased_credits', 'purchasedCredits'], ['free_credits', 'freeCredits']]) {
const value = toNumber(credits?.[field]);
if (value !== null) windows[label] = toBalanceWindow(value);
}
for (const [label, field, windowSeconds] of [['5h', 'fiveHour', 5 * 60 * 60], ['weekly', 'weekly', 7 * 24 * 60 * 60]]) {
const limit = asObject(limits?.[field]);
const used = toNumber(limit?.used);
const cap = toNumber(limit?.cap);
if (used === null || cap === null || cap <= 0) continue;
const resetAt = toNumber(limit?.resetAt);
windows[label] = toUsageWindow({
usedPercent: Math.min(100, Math.max(0, used / cap * 100)),
windowSeconds,
resetAt: resetAt === null ? null : resetAt < 1_000_000_000_000 ? resetAt * 1000 : resetAt,
valueLabel: `${formatCredits(used)} / ${formatCredits(cap)}`,
});
}
return windows;
};
export const fetchCommandCodeUsage = async (apiKey, fetchImpl = fetch) => {
const identity = asObject(await requestJson('/alpha/whoami', apiKey, fetchImpl));
const org = asObject(identity?.org);
const orgId = typeof org?.id === 'string' ? org.id.trim() : '';
const creditsPath = orgId
? `/alpha/billing/credits?orgId=${encodeURIComponent(orgId)}`
: '/alpha/billing/credits';
const credits = await requestJson(creditsPath, apiKey, fetchImpl);
const windows = parseCommandCodeCredits(credits);
if (Object.keys(windows).length === 0) throw new Error('Command Code usage data could not be parsed');
return windows;
};
export const isConfigured = () => Boolean(getApiKey());
export const fetchQuota = async (auth = readAuthFile()) => {
const apiKey = getApiKey(auth);
if (!apiKey) return buildResult({ providerId, providerName, ok: false, configured: false, error: 'Not configured' });
try {
return buildResult({ providerId, providerName, ok: true, configured: true, usage: { windows: await fetchCommandCodeUsage(apiKey) } });
} catch (error) {
return buildResult({ providerId, providerName, ok: false, configured: true, error: error instanceof Error ? error.message : 'Request failed' });
}
};
@@ -0,0 +1,72 @@
import { describe, expect, it, vi } from 'vitest';
import { fetchCommandCodeUsage, fetchQuota, parseCommandCodeCredits } from './command-code.js';
const creditsPayload = {
credits: { monthlyCredits: 120, purchasedCredits: 30, freeCredits: 5 },
windowLimits: {
fiveHour: { used: 25, cap: 100, resetAt: 1_776_000_000 },
weekly: { used: 70, cap: 200, resetAt: 1_776_604_800 },
},
};
describe('Command Code quota provider', () => {
it('parses balances and rate-limit windows', () => {
const windows = parseCommandCodeCredits(creditsPayload);
expect(windows.monthly_credits).toMatchObject({ usedPercent: null, valueLabel: '120' });
expect(windows.purchased_credits).toMatchObject({ usedPercent: null, valueLabel: '30' });
expect(windows.free_credits).toMatchObject({ usedPercent: null, valueLabel: '5' });
expect(windows['5h']).toMatchObject({ usedPercent: 25, valueLabel: '25 / 100', resetAt: 1_776_000_000_000 });
expect(windows.weekly.usedPercent).toBe(35);
});
it('formats fractional credit values for display', () => {
const windows = parseCommandCodeCredits({
credits: { monthlyCredits: 69.7947070034 },
windowLimits: { fiveHour: { used: 0.2052929966, cap: 14 } },
});
expect(windows.monthly_credits.valueLabel).toBe('69.79');
expect(windows['5h'].valueLabel).toBe('0.21 / 14');
});
it('resolves the organization before fetching credits', async () => {
const requests = [];
const windows = await fetchCommandCodeUsage('secret', async (url, options) => {
requests.push({ url, options });
return new Response(JSON.stringify(url.endsWith('/alpha/whoami') ? { org: { id: 'org/a' } } : creditsPayload));
});
expect(requests.map(({ url }) => url)).toEqual([
'https://api.commandcode.ai/alpha/whoami',
'https://api.commandcode.ai/alpha/billing/credits?orgId=org%2Fa',
]);
expect(requests[0].options.headers.Authorization).toBe('Bearer secret');
expect(windows['5h'].usedPercent).toBe(25);
});
it('fetches account-scoped credits without orgId for personal accounts', async () => {
const urls = [];
await fetchCommandCodeUsage('secret', async (url) => {
urls.push(url);
return new Response(JSON.stringify(url.endsWith('/alpha/whoami') ? { user: { id: 'user-1' }, org: null } : creditsPayload));
});
expect(urls).toEqual([
'https://api.commandcode.ai/alpha/whoami',
'https://api.commandcode.ai/alpha/billing/credits',
]);
});
it('does not expose credentials in authentication errors', async () => {
await expect(fetchCommandCodeUsage('secret', async () => new Response('', { status: 401 }))).rejects.toThrow('authentication failed');
});
it('reads OAuth access credentials from the OpenCode auth file', async () => {
const fetchMock = vi.fn()
.mockResolvedValueOnce(new Response(JSON.stringify({ org: { id: 'org-1' } })))
.mockResolvedValueOnce(new Response(JSON.stringify(creditsPayload)));
vi.stubGlobal('fetch', fetchMock);
const result = await fetchQuota({ 'command-code': { type: 'oauth', access: 'test-token' } });
expect(result).toMatchObject({ providerId: 'command-code', ok: true, configured: true });
expect(fetchMock.mock.calls[0][1].headers.Authorization).toBe('Bearer test-token');
vi.unstubAllGlobals();
});
});
@@ -7,8 +7,9 @@
import { buildResult } from '../utils/index.js';
import * as claude from './claude.js';
import * as claude from './claude/index.js';
import * as codex from './codex.js';
import * as commandCode from './command-code.js';
import * as copilot from './copilot.js';
import * as crof from './crof.js';
import * as cursor from './cursor.js';
@@ -29,6 +30,12 @@ import * as opencodeGo from './opencode-go.js';
import * as xai from './xai.js';
const registry = {
'command-code': {
providerId: commandCode.providerId,
providerName: commandCode.providerName,
isConfigured: commandCode.isConfigured,
fetchQuota: commandCode.fetchQuota
},
claude: {
providerId: claude.providerId,
providerName: claude.providerName,
@@ -151,6 +158,8 @@ const registry = {
}
};
const pendingFetches = new Map();
export const listConfiguredQuotaProviders = () => {
const configured = [];
@@ -167,7 +176,7 @@ export const listConfiguredQuotaProviders = () => {
return configured;
};
export const fetchQuotaForProvider = async (providerId) => {
const fetchQuotaForProviderUncoalesced = async (providerId) => {
const provider = registry[providerId];
if (!provider) {
@@ -193,6 +202,17 @@ export const fetchQuotaForProvider = async (providerId) => {
}
};
export const fetchQuotaForProvider = (providerId) => {
const existing = pendingFetches.get(providerId);
if (existing) return existing;
const pending = fetchQuotaForProviderUncoalesced(providerId).finally(() => {
if (pendingFetches.get(providerId) === pending) pendingFetches.delete(providerId);
});
pendingFetches.set(providerId, pending);
return pending;
};
export const fetchClaudeQuota = claude.fetchQuota;
export const fetchOpenaiQuota = openai.fetchQuota;
export const fetchGoogleQuota = google.fetchGoogleQuota;
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest';
import * as google from './google/index.js';
import { listConfiguredQuotaProviders } from './index.js';
import { fetchQuotaForProvider, listConfiguredQuotaProviders } from './index.js';
describe('quota provider registry', () => {
it('exposes google provider configuration helpers through the provider module', () => {
@@ -14,4 +14,13 @@ describe('quota provider registry', () => {
it('can list configured providers without missing provider exports', () => {
expect(() => listConfiguredQuotaProviders()).not.toThrow();
});
it('coalesces concurrent refreshes by provider ID', async () => {
const first = fetchQuotaForProvider('unsupported-test-provider');
const second = fetchQuotaForProvider('unsupported-test-provider');
expect(first).toBe(second);
await first;
expect(fetchQuotaForProvider('unsupported-test-provider')).not.toBe(first);
});
});
+26 -6
View File
@@ -4,6 +4,7 @@ import {
normalizeAuthEntry,
buildResult,
toUsageWindow,
toNumber,
resolveWindowSeconds,
resolveWindowLabel,
normalizeTimestamp
@@ -13,6 +14,20 @@ export const providerId = 'zai-coding-plan';
export const providerName = 'z.ai';
const aliases = ['zai-coding-plan', 'zai', 'z.ai'];
// CREDIT_LIMIT entries carry `usage` (total credits), `currentValue` (consumed),
// and `remaining`; TOKENS_LIMIT entries only carry a percentage.
const formatCreditAmount = (value) => {
if (value < 1000) return value.toLocaleString('en-US');
return `${Math.round(value / 100) / 10}k`;
};
const formatCreditValueLabel = (limit) => {
const used = toNumber(limit?.currentValue);
const total = toNumber(limit?.usage);
if (used === null || total === null) return null;
return `${formatCreditAmount(used)} / ${formatCreditAmount(total)} credits`;
};
export const isConfigured = () => {
const auth = readAuthFile();
const entry = normalizeAuthEntry(getAuthEntry(auth, aliases));
@@ -56,16 +71,20 @@ export const fetchQuota = async () => {
const payload = await response.json();
const limits = Array.isArray(payload?.data?.limits) ? payload.data.limits : [];
const windows = {};
for (const tokensLimit of limits.filter((limit) => limit?.type === 'TOKENS_LIMIT')) {
const windowSeconds = resolveWindowSeconds(tokensLimit);
// The API renamed TOKENS_LIMIT to CREDIT_LIMIT; field semantics stayed the same,
// so both limit types map to the same windows.
for (const limit of limits.filter((entry) => entry?.type === 'TOKENS_LIMIT' || entry?.type === 'CREDIT_LIMIT')) {
const windowSeconds = resolveWindowSeconds(limit);
const windowLabel = resolveWindowLabel(windowSeconds);
const resetAt = tokensLimit?.nextResetTime ? normalizeTimestamp(tokensLimit.nextResetTime) : null;
const usedPercent = typeof tokensLimit?.percentage === 'number' ? tokensLimit.percentage : null;
const resetAt = limit?.nextResetTime ? normalizeTimestamp(limit.nextResetTime) : null;
const usedPercent = typeof limit?.percentage === 'number' ? limit.percentage : null;
const creditValueLabel = formatCreditValueLabel(limit);
windows[windowLabel] = toUsageWindow({
usedPercent,
windowSeconds,
resetAt
resetAt,
valueLabel: creditValueLabel
});
}
@@ -83,7 +102,8 @@ export const fetchQuota = async () => {
providerName,
ok: true,
configured: true,
usage: { windows }
usage: { windows },
planLabel: typeof payload?.data?.level === 'string' && payload.data.level ? payload.data.level : null
});
} catch (error) {
return buildResult({
@@ -51,4 +51,37 @@ describe('Z.ai quota provider', () => {
resetAt: 1787128459979,
});
});
it('maps CREDIT_LIMIT entries to windows with credit value labels and plan level', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(mockResponse({
code: 200,
data: {
limits: [
{ type: 'CREDIT_LIMIT', unit: 3, number: 5, usage: 12000, currentValue: 65, remaining: 11934, percentage: 1, nextResetTime: 1787257978907 },
{ type: 'CREDIT_LIMIT', unit: 6, number: 1, usage: 60000, currentValue: 65, remaining: 59934, percentage: 1, nextResetTime: 1787844668997 },
],
level: 'pro',
},
})));
const result = await fetchQuota();
const windows = result.usage.windows;
expect(result.ok).toBe(true);
expect(result.planLabel).toBe('pro');
expect(windows['5h']).toMatchObject({
usedPercent: 1,
remainingPercent: 99,
windowSeconds: 5 * 60 * 60,
resetAt: 1787257978907,
valueLabel: '65 / 12k credits',
});
expect(windows.weekly).toMatchObject({
usedPercent: 1,
remainingPercent: 99,
windowSeconds: 7 * 24 * 60 * 60,
resetAt: 1787844668997,
valueLabel: '65 / 60k credits',
});
});
});