Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/i18n/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,9 @@ export const jaOverrides = {
'quota.service.window.sevenDayOpus': '7 日間 Opus 枠',
'quota.service.window.sevenDaySonnet': '7 日間 Sonnet 枠',
'quota.service.window.sevenDayCowork': '7 日間 Cowork 枠',
'quota.service.window.sevenDayScoped': '7 日間 {model} 枠',
'quota.service.window.fiveHourScoped': '5 時間 {model} 枠',
'quota.service.activeLimit': '現在有効な制限',
'quota.service.window.duration': '{duration}枠',
'quota.service.extraUsage': '追加使用量',
'quota.service.usedOf': '使用済み {used} / {limit}',
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,9 @@ export const en: Record<MessageKey, string> = {
'quota.service.window.sevenDayOpus': '7-day Opus window',
'quota.service.window.sevenDaySonnet': '7-day Sonnet window',
'quota.service.window.sevenDayCowork': '7-day Cowork window',
'quota.service.window.sevenDayScoped': '7-day {model} window',
'quota.service.window.fiveHourScoped': '5-hour {model} window',
'quota.service.activeLimit': 'Active limit',
'quota.service.window.duration': '{duration} window',
'quota.service.extraUsage': 'Extra usage',
'quota.service.usedOf': 'Used {used} / {limit}',
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/locales/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,9 @@ export const zhCN = {
'quota.service.window.sevenDayOpus': '7 天 Opus 窗口',
'quota.service.window.sevenDaySonnet': '7 天 Sonnet 窗口',
'quota.service.window.sevenDayCowork': '7 天 Cowork 窗口',
'quota.service.window.sevenDayScoped': '7 天 {model} 窗口',
'quota.service.window.fiveHourScoped': '5 小时 {model} 窗口',
'quota.service.activeLimit': '当前生效限制',
'quota.service.window.duration': '{duration}窗口',
'quota.service.extraUsage': '额外用量',
'quota.service.usedOf': '已用 {used} / {limit}',
Expand Down
27 changes: 27 additions & 0 deletions src/services/quotaService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,31 @@ export const codexResetCreditDetailsFor = (
};
};

const claudeScopedLimitRows = (value: Record<string, unknown>): QuotaRow[] => {
const limits = Array.isArray(value.limits) ? value.limits : [];
const windowKeyByKind: Record<string, Parameters<typeof translate>[1]> = {
weekly_scoped: 'quota.service.window.sevenDayScoped',
session_scoped: 'quota.service.window.fiveHourScoped',
};
return limits.flatMap((raw): QuotaRow[] => {
if (!isRecord(raw)) return [];
const windowKey = windowKeyByKind[readString(raw, 'kind')];
if (!windowKey) return [];
const scope = isRecord(raw.scope) ? raw.scope : null;
const model = isRecord(scope?.model) ? scope.model : null;
const name = readString(model, 'display_name', 'displayName', 'id') || readString(scope, 'surface');
if (!name) return [];
return [{
label: quotaText(windowKey, { model: name }),
remainingPercent: remainingFromUsedPercent(raw.percent ?? raw.utilization),
reset: absoluteResetLabel(raw.resets_at ?? raw.resetsAt),
detail: booleanValue(raw.is_active ?? raw.isActive) === true
? quotaText('quota.service.activeLimit')
: undefined,
}];
});
};

export const quotaRowsFor = (provider: QuotaProvider, payload: unknown): QuotaRow[] => {
const value = parseBody(payload);
if (!isRecord(value)) return [];
Expand Down Expand Up @@ -342,6 +367,8 @@ export const quotaRowsFor = (provider: QuotaProvider, payload: unknown): QuotaRo
};
})
.filter((row): row is QuotaRow => row !== null);
// Model-scoped limits (e.g. Fable) are reported only in `limits`, not as named windows.
rows.push(...claudeScopedLimitRows(value));
const extraUsage = isRecord(value.extra_usage)
? value.extra_usage
: isRecord(value.extraUsage)
Expand Down
24 changes: 24 additions & 0 deletions tests/quotaService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,30 @@ describe('quotaRowsFor', () => {
expect(claude[0].remainingPercent).toBeCloseTo(99.56);
});

it('显示 Claude 按模型限定的周窗口(如 Fable)', () => {
const rows = quotaRowsFor('claude', {
five_hour: { utilization: 15, resets_at: '2027-01-01T00:00:00Z' },
seven_day: { utilization: 18, resets_at: '2027-01-02T00:00:00Z' },
seven_day_opus: null,
limits: [
{ kind: 'session', group: 'session', percent: 15, is_active: false },
{ kind: 'weekly_all', group: 'weekly', percent: 18, is_active: false },
{
kind: 'weekly_scoped',
group: 'weekly',
scope: { model: { id: null, display_name: 'Fable' }, surface: null },
percent: 36,
is_active: true,
resets_at: '2027-01-02T00:00:00Z',
},
],
});

expect(rows.map((row) => row.label)).toEqual(['5 小时窗口', '7 天窗口', '7 天 Fable 窗口']);
expect(rows[2]).toMatchObject({ remainingPercent: 64, detail: '当前生效限制' });
expect(rows[2].reset).toBeTruthy();
});

it('显示 Claude 已启用的额外用量', () => {
const rows = quotaRowsFor('claude', {
five_hour: { utilization: 20, resets_at: '2027-01-01T00:00:00Z' },
Expand Down