diff --git a/packages/ui/src/components/usage/UsageProviderCards.tsx b/packages/ui/src/components/usage/UsageProviderCards.tsx
index 68e2a518..56f2ada0 100644
--- a/packages/ui/src/components/usage/UsageProviderCards.tsx
+++ b/packages/ui/src/components/usage/UsageProviderCards.tsx
@@ -35,6 +35,11 @@ export const UsageProviderCards: React.FC<{
{group.providerName}
+ {group.planLabel ? (
+
+ {group.planLabel}
+
+ ) : null}
{group.status && group.rows.length === 0 ? (
{group.status}
) : null}
@@ -54,12 +59,12 @@ export const UsageProviderCards: React.FC<{
);
return (
-
-
+
+
{row.subtitle ? `${row.subtitle} · ${row.label}` : row.label}
{resetLabel ? (
-
+
{resetLabel}
) : null}
diff --git a/packages/ui/src/components/usage/usageGroups.ts b/packages/ui/src/components/usage/usageGroups.ts
index 3b1bdb0e..68a85bce 100644
--- a/packages/ui/src/components/usage/usageGroups.ts
+++ b/packages/ui/src/components/usage/usageGroups.ts
@@ -15,6 +15,7 @@ export type UsageLimitRow = {
export type UsageProviderGroup = {
providerId: QuotaProviderId;
providerName: string;
+ planLabel?: string | null;
rows: UsageLimitRow[];
/** Provider-level message: a fetch error, or "nothing reported". */
status: string | null;
@@ -76,6 +77,7 @@ export const useUsageProviderGroups = (): UsageProviderGroup[] => {
return {
providerId: providerMeta.id,
providerName: providerMeta.name,
+ planLabel: result.planLabel,
rows,
status,
};
diff --git a/packages/ui/src/lib/i18n/messages/de.ts b/packages/ui/src/lib/i18n/messages/de.ts
index 35d1cab5..fec60411 100644
--- a/packages/ui/src/lib/i18n/messages/de.ts
+++ b/packages/ui/src/lib/i18n/messages/de.ts
@@ -2853,9 +2853,9 @@ export const dict = {
'quota.window.5h': '5-Stunden-Limit',
'quota.window.7d': '7-Tage-Limit',
'quota.window.extraUsage': 'Zusätzliche Nutzung',
- 'quota.window.weekly': 'Wöchentliches Limit',
+ 'quota.window.weekly': 'Wöchentlich',
'quota.window.daily': 'Täglich',
- 'quota.window.monthly': 'Monatliches Limit',
+ 'quota.window.monthly': 'Monatlich',
'quota.window.credits': 'Credits',
'quota.window.creditsBalance': 'Kreditguthaben',
'quota.window.monthlyCredits': 'Monatliche Credits',
diff --git a/packages/ui/src/lib/i18n/messages/en.ts b/packages/ui/src/lib/i18n/messages/en.ts
index a2af1dcc..4abb4e1d 100644
--- a/packages/ui/src/lib/i18n/messages/en.ts
+++ b/packages/ui/src/lib/i18n/messages/en.ts
@@ -3031,9 +3031,9 @@ export const dict = {
'quota.window.5h': '5-Hour',
'quota.window.7d': '7-Day Limit',
'quota.window.extraUsage': 'Extra Usage',
- 'quota.window.weekly': 'Weekly Limit',
+ 'quota.window.weekly': 'Weekly',
'quota.window.daily': 'Daily',
- 'quota.window.monthly': 'Monthly Limit',
+ 'quota.window.monthly': 'Monthly',
'quota.window.credits': 'Credits',
'quota.window.creditsBalance': 'Credits Balance',
'quota.window.monthlyCredits': 'Monthly Credits',
diff --git a/packages/ui/src/lib/i18n/messages/es.ts b/packages/ui/src/lib/i18n/messages/es.ts
index 34649bcd..458a6adc 100644
--- a/packages/ui/src/lib/i18n/messages/es.ts
+++ b/packages/ui/src/lib/i18n/messages/es.ts
@@ -3032,9 +3032,9 @@ export const dict: Record = {
"quota.window.5h": "5-Hour",
"quota.window.7d": "7-Day Limit",
"quota.window.extraUsage": "Uso adicional",
- "quota.window.weekly": "Weekly Limit",
+ "quota.window.weekly": "Semanal",
"quota.window.daily": "Daily",
- "quota.window.monthly": "Monthly Limit",
+ "quota.window.monthly": "Mensual",
"quota.window.credits": "Credits",
"quota.window.creditsBalance": "Credits Balance",
"quota.window.monthlyCredits": "Créditos mensuales",
diff --git a/packages/ui/src/lib/i18n/messages/fr.ts b/packages/ui/src/lib/i18n/messages/fr.ts
index bde799e2..bba07ce2 100644
--- a/packages/ui/src/lib/i18n/messages/fr.ts
+++ b/packages/ui/src/lib/i18n/messages/fr.ts
@@ -2724,9 +2724,9 @@ export const dict = {
'quota.window.5h': '5 heures',
'quota.window.7d': 'Limite sur 7 jours',
'quota.window.extraUsage': 'Utilisation supplémentaire',
- 'quota.window.weekly': 'Limite hebdomadaire',
+ 'quota.window.weekly': 'Hebdomadaire',
'quota.window.daily': 'Quotidien',
- 'quota.window.monthly': 'Limite mensuelle',
+ 'quota.window.monthly': 'Mensuel',
'quota.window.credits': 'Crédits',
'quota.window.creditsBalance': 'Solde de crédits',
'quota.window.monthlyCredits': 'Crédits mensuels',
diff --git a/packages/ui/src/lib/i18n/messages/ja.ts b/packages/ui/src/lib/i18n/messages/ja.ts
index a18ca8dc..7d26028d 100644
--- a/packages/ui/src/lib/i18n/messages/ja.ts
+++ b/packages/ui/src/lib/i18n/messages/ja.ts
@@ -3027,9 +3027,9 @@ export const dict: Record = {
'quota.window.5h': '5時間',
'quota.window.7d': '7日間制限',
'quota.window.extraUsage': '追加利用',
- 'quota.window.weekly': '週間制限',
+ 'quota.window.weekly': '毎週',
'quota.window.daily': '日次',
- 'quota.window.monthly': '月間制限',
+ 'quota.window.monthly': '毎月',
'quota.window.credits': 'クレジット',
'quota.window.creditsBalance': 'クレジット残高',
'quota.window.monthlyCredits': '月間クレジット',
diff --git a/packages/ui/src/lib/i18n/messages/ko.ts b/packages/ui/src/lib/i18n/messages/ko.ts
index 170ac4b2..b7a0accd 100644
--- a/packages/ui/src/lib/i18n/messages/ko.ts
+++ b/packages/ui/src/lib/i18n/messages/ko.ts
@@ -3031,9 +3031,9 @@ export const dict: Record = {
'quota.window.5h': '5-Hour',
'quota.window.7d': '7-Day Limit',
'quota.window.extraUsage': '추가 사용량',
- 'quota.window.weekly': 'Weekly Limit',
+ 'quota.window.weekly': '매주',
'quota.window.daily': 'Daily',
- 'quota.window.monthly': 'Monthly Limit',
+ 'quota.window.monthly': '매월',
'quota.window.credits': 'Credits',
'quota.window.creditsBalance': 'Credits Balance',
'quota.window.monthlyCredits': '월간 크레딧',
diff --git a/packages/ui/src/lib/i18n/messages/pl.ts b/packages/ui/src/lib/i18n/messages/pl.ts
index fd0ebd78..d9247510 100644
--- a/packages/ui/src/lib/i18n/messages/pl.ts
+++ b/packages/ui/src/lib/i18n/messages/pl.ts
@@ -3048,9 +3048,9 @@ export const dict: Record = {
'quota.window.5h': '5-Hour',
'quota.window.7d': '7-Day Limit',
'quota.window.extraUsage': 'Dodatkowe zużycie',
- 'quota.window.weekly': 'Weekly Limit',
+ 'quota.window.weekly': 'Tygodniowo',
'quota.window.daily': 'Daily',
- 'quota.window.monthly': 'Monthly Limit',
+ 'quota.window.monthly': 'Miesięcznie',
'quota.window.credits': 'Credits',
'quota.window.creditsBalance': 'Credits Balance',
'quota.window.monthlyCredits': 'Kredyty miesięczne',
diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.ts b/packages/ui/src/lib/i18n/messages/pt-BR.ts
index 93a53b2c..b5dd9ae7 100644
--- a/packages/ui/src/lib/i18n/messages/pt-BR.ts
+++ b/packages/ui/src/lib/i18n/messages/pt-BR.ts
@@ -3032,9 +3032,9 @@ export const dict: Record = {
"quota.window.5h": "5-Hour",
"quota.window.7d": "7-Day Limit",
"quota.window.extraUsage": "Uso adicional",
- "quota.window.weekly": "Weekly Limit",
+ "quota.window.weekly": "Semanal",
"quota.window.daily": "Daily",
- "quota.window.monthly": "Monthly Limit",
+ "quota.window.monthly": "Mensal",
"quota.window.credits": "Credits",
"quota.window.creditsBalance": "Credits Balance",
"quota.window.monthlyCredits": "Créditos mensais",
diff --git a/packages/ui/src/lib/i18n/messages/uk.ts b/packages/ui/src/lib/i18n/messages/uk.ts
index ce9a0963..e30dea88 100644
--- a/packages/ui/src/lib/i18n/messages/uk.ts
+++ b/packages/ui/src/lib/i18n/messages/uk.ts
@@ -3032,9 +3032,9 @@ export const dict: Record = {
"quota.window.5h": "5-Hour",
"quota.window.7d": "7-Day Limit",
"quota.window.extraUsage": "Додаткове використання",
- "quota.window.weekly": "Weekly Limit",
+ "quota.window.weekly": "Щотижня",
"quota.window.daily": "Daily",
- "quota.window.monthly": "Monthly Limit",
+ "quota.window.monthly": "Щомісяця",
"quota.window.credits": "Credits",
"quota.window.creditsBalance": "Credits Balance",
"quota.window.monthlyCredits": "Місячні кредити",
diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.ts b/packages/ui/src/lib/i18n/messages/zh-CN.ts
index eb6b7241..0b1034e3 100644
--- a/packages/ui/src/lib/i18n/messages/zh-CN.ts
+++ b/packages/ui/src/lib/i18n/messages/zh-CN.ts
@@ -3032,9 +3032,9 @@ export const dict: Record = {
'quota.window.5h': '5-Hour',
'quota.window.7d': '7-Day Limit',
'quota.window.extraUsage': '额外用量',
- 'quota.window.weekly': 'Weekly Limit',
+ 'quota.window.weekly': '每周',
'quota.window.daily': 'Daily',
- 'quota.window.monthly': 'Monthly Limit',
+ 'quota.window.monthly': '每月',
'quota.window.credits': 'Credits',
'quota.window.creditsBalance': 'Credits Balance',
'quota.window.monthlyCredits': '每月积分',
diff --git a/packages/ui/src/lib/i18n/messages/zh-TW.ts b/packages/ui/src/lib/i18n/messages/zh-TW.ts
index 89ab710d..4b2caab8 100644
--- a/packages/ui/src/lib/i18n/messages/zh-TW.ts
+++ b/packages/ui/src/lib/i18n/messages/zh-TW.ts
@@ -3031,9 +3031,9 @@ export const dict: Record = {
'quota.window.5h': '5-Hour',
'quota.window.7d': '7-Day Limit',
'quota.window.extraUsage': '額外用量',
- 'quota.window.weekly': 'Weekly Limit',
+ 'quota.window.weekly': '每週',
'quota.window.daily': 'Daily',
- 'quota.window.monthly': 'Monthly Limit',
+ 'quota.window.monthly': '每月',
'quota.window.credits': 'Credits',
'quota.window.creditsBalance': 'Credits Balance',
'quota.window.monthlyCredits': '每月點數',
diff --git a/packages/vscode/src/quotaProviders.test.ts b/packages/vscode/src/quotaProviders.test.ts
index 2309ea6d..ee88916c 100644
--- a/packages/vscode/src/quotaProviders.test.ts
+++ b/packages/vscode/src/quotaProviders.test.ts
@@ -327,6 +327,33 @@ describe('Z.ai quota provider (VS Code parity)', () => {
assert.equal(windows['MCP Tools']!.windowSeconds, 30 * 24 * 60 * 60);
assert.equal(windows['MCP Tools']!.resetAt, 1787128459979);
});
+
+ test('maps CREDIT_LIMIT entries to windows with credit value labels and plan level', async () => {
+ stubFetchReturning(() => Promise.resolve(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 fetchQuotaForProvider('zai-coding-plan');
+ const windows = result.usage!.windows;
+
+ assert.equal(result.ok, true);
+ assert.equal(result.planLabel, 'pro');
+ assert.equal(windows['5h']!.usedPercent, 1);
+ assert.equal(windows['5h']!.windowSeconds, 5 * 60 * 60);
+ assert.equal(windows['5h']!.resetAt, 1787257978907);
+ assert.equal(windows['5h']!.valueLabel, '65 / 12k credits');
+ assert.equal(windows.weekly!.usedPercent, 1);
+ assert.equal(windows.weekly!.windowSeconds, 7 * 24 * 60 * 60);
+ assert.equal(windows.weekly!.resetAt, 1787844668997);
+ assert.equal(windows.weekly!.valueLabel, '65 / 60k credits');
+ });
});
describe('NeuralWatt quota provider (VS Code parity)', () => {
diff --git a/packages/vscode/src/quotaProviders.ts b/packages/vscode/src/quotaProviders.ts
index a33bf6b6..042fc5d4 100644
--- a/packages/vscode/src/quotaProviders.ts
+++ b/packages/vscode/src/quotaProviders.ts
@@ -72,13 +72,31 @@ type ZaiLimit = {
type?: string;
number?: number;
unit?: number;
+ usage?: number;
+ currentValue?: number;
+ remaining?: number;
nextResetTime?: number;
percentage?: number;
};
+// CREDIT_LIMIT entries carry `usage` (total credits) and `currentValue` (consumed);
+// TOKENS_LIMIT entries only carry a percentage.
+const formatZaiCreditAmount = (value: number): string => {
+ if (value < 1000) return value.toLocaleString('en-US');
+ return `${Math.round(value / 100) / 10}k`;
+};
+
+const formatZaiCreditValueLabel = (limit: ZaiLimit): string | null => {
+ const used = toNumber(limit.currentValue);
+ const total = toNumber(limit.usage);
+ if (used === null || total === null) return null;
+ return `${formatZaiCreditAmount(used)} / ${formatZaiCreditAmount(total)} credits`;
+};
+
type ZaiPayload = {
data?: {
limits?: ZaiLimit[];
+ level?: string;
};
};
@@ -411,15 +429,20 @@ const buildResult = (data: {
configured: boolean;
usage?: ProviderUsage | null;
error?: string;
-}): ProviderResult => ({
- providerId: data.providerId,
- providerName: data.providerName,
- ok: data.ok,
- configured: data.configured,
- usage: data.usage ?? null,
- ...(data.error ? { error: data.error } : {}),
- fetchedAt: Date.now(),
-});
+ planLabel?: string | null;
+}): ProviderResult => {
+ const result: ProviderResult = {
+ providerId: data.providerId,
+ providerName: data.providerName,
+ ok: data.ok,
+ configured: data.configured,
+ usage: data.usage ?? null,
+ ...(data.error ? { error: data.error } : {}),
+ fetchedAt: Date.now(),
+ };
+ if (data.planLabel) result.planLabel = data.planLabel;
+ return result;
+};
const resolveXaiAuth = (): XaiAuthEntry | null => {
const entry = getProviderAuth('xai');
@@ -1291,7 +1314,7 @@ const buildClaudeRateLimitResult = (): ProviderResult => (
providerName: 'Claude',
ok: false,
configured: true,
- error: 'Rate limited by Anthropic. Retrying shortly.',
+ error: 'Rate limited. Retrying soon.',
})
);
@@ -2059,16 +2082,19 @@ const fetchZaiQuota = async (): Promise => {
const payload = await response.json() as ZaiPayload;
const limits = Array.isArray(payload?.data?.limits) ? payload.data.limits : [];
const windows: Record = {};
- for (const tokensLimit of limits.filter((limit) => limit?.type === 'TOKENS_LIMIT')) {
- const windowSeconds = resolveWindowSeconds(tokensLimit as Record);
+ // 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 as Record);
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;
windows[windowLabel] = toUsageWindow({
usedPercent,
windowSeconds,
resetAt,
+ valueLabel: formatZaiCreditValueLabel(limit),
});
}
@@ -2087,6 +2113,7 @@ const fetchZaiQuota = async (): Promise => {
ok: true,
configured: true,
usage: { windows },
+ planLabel: payload?.data?.level || null,
});
} catch (error) {
return buildResult({
diff --git a/packages/web/server/lib/quota/DOCUMENTATION.md b/packages/web/server/lib/quota/DOCUMENTATION.md
index 78d86706..83e905a4 100644
--- a/packages/web/server/lib/quota/DOCUMENTATION.md
+++ b/packages/web/server/lib/quota/DOCUMENTATION.md
@@ -101,4 +101,4 @@ The provider computes `usedPercent` from whichever of `used`/`remaining` is pres
- Keep provider IDs stable; clients use them directly.
- Avoid adding alias-based dispatch in `fetchQuotaForProvider`; dispatch currently expects exact provider IDs.
- Keep Google behavior changes isolated and review `providers/google/*` together.
-- Z.ai Coding Plan exposes separate 5-hour and weekly `TOKENS_LIMIT` entries plus a monthly `TIME_LIMIT` for MCP tools; web and VS Code must preserve all three windows.
+- Z.ai Coding Plan exposes separate 5-hour and weekly token/credit limit entries plus a monthly `TIME_LIMIT` for MCP tools. The API renamed the limit type from `TOKENS_LIMIT` to `CREDIT_LIMIT` (same `unit`/`number` window semantics); `CREDIT_LIMIT` entries additionally carry `usage` (total), `currentValue` (consumed), and `remaining`, surfaced as a credit `valueLabel`, and the payload's `data.level` becomes `planLabel`. Web and VS Code must preserve these windows and stay in sync.
diff --git a/packages/web/server/lib/quota/providers/claude/index.js b/packages/web/server/lib/quota/providers/claude/index.js
index 8ee0d677..71fa3e43 100644
--- a/packages/web/server/lib/quota/providers/claude/index.js
+++ b/packages/web/server/lib/quota/providers/claude/index.js
@@ -81,7 +81,7 @@ const fetchQuotaUncoalesced = async () => {
if (Date.now() < cooldownUntil) {
return cachedResultFor(fingerprint, credential.planLabel)
- ?? failure('Rate limited by Anthropic. Retrying shortly.');
+ ?? failure('Rate limited. Retrying soon.');
}
let response;
@@ -100,7 +100,7 @@ const fetchQuotaUncoalesced = async () => {
if (response.status === 429) {
cooldownUntil = Date.now() + cooldownFromHeader(response);
return cachedResultFor(fingerprint, credential.planLabel)
- ?? failure('Rate limited by Anthropic. Retrying shortly.');
+ ?? failure('Rate limited. Retrying soon.');
}
if (response.status === 401 || response.status === 403) {
diff --git a/packages/web/server/lib/quota/providers/zai.js b/packages/web/server/lib/quota/providers/zai.js
index 0c526f99..b3d9efac 100644
--- a/packages/web/server/lib/quota/providers/zai.js
+++ b/packages/web/server/lib/quota/providers/zai.js
@@ -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({
diff --git a/packages/web/server/lib/quota/providers/zai.test.js b/packages/web/server/lib/quota/providers/zai.test.js
index 39d5dcf5..d721cd9e 100644
--- a/packages/web/server/lib/quota/providers/zai.test.js
+++ b/packages/web/server/lib/quota/providers/zai.test.js
@@ -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',
+ });
+ });
});