mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 08:37:49 +00:00
fix(rate-limits): keep Claude OAuth credits through live statusline updates (#29759)
The statusline payload carries only the 5-hour and 7-day windows, so each live post rebuilt Claude's usage without extraUsage and cleared the credit balance until the next OAuth poll. Retain the last OAuth extraUsage alongside fableWeekly. Automated Claude polls on main are gated only by Retry-After, so credits keep refreshing on cadence. Fixes #29755 Co-authored-by: Neil <neil@stably.ai>
This commit is contained in:
@@ -130,16 +130,27 @@ describe('RateLimitService', () => {
|
||||
}
|
||||
)
|
||||
|
||||
it('keeps polling on cadence for an account with a Fable window the live feed cannot carry', async () => {
|
||||
it('keeps OAuth-only Fable and credit fields through live posts and polls them on cadence', async () => {
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
const withFable = (usedPercent: number): ProviderRateLimits => ({
|
||||
const withOAuthUsage = (usedPercent: number): ProviderRateLimits => ({
|
||||
...okProvider('claude', 40),
|
||||
fableWeekly: { usedPercent, windowMinutes: 10080, resetsAt: null, resetDescription: null }
|
||||
fableWeekly: { usedPercent, windowMinutes: 10080, resetsAt: null, resetDescription: null },
|
||||
extraUsage: {
|
||||
balance: usedPercent,
|
||||
unit: 'currency',
|
||||
currencyCode: 'USD',
|
||||
enabled: true,
|
||||
disabledReason: null,
|
||||
resetsAt: null,
|
||||
spent: null,
|
||||
spendLimit: null,
|
||||
spentPercent: null
|
||||
}
|
||||
})
|
||||
vi.mocked(fetchClaudeRateLimits)
|
||||
.mockImplementationOnce(async () => withFable(18))
|
||||
.mockImplementation(async () => withFable(39))
|
||||
.mockImplementationOnce(async () => withOAuthUsage(18))
|
||||
.mockImplementation(async () => withOAuthUsage(39))
|
||||
mockFreshBackgroundProviderFetches()
|
||||
|
||||
const service = new RateLimitService()
|
||||
@@ -155,11 +166,14 @@ describe('RateLimitService', () => {
|
||||
fiveHour: { used_percentage: 50 + minute },
|
||||
sevenDay: { used_percentage: 30 }
|
||||
})
|
||||
expect(service.getState().claude?.fableWeekly?.usedPercent).toBe(18)
|
||||
expect(service.getState().claude?.extraUsage?.balance).toBe(18)
|
||||
await vi.advanceTimersByTimeAsync(60 * 1000)
|
||||
}
|
||||
|
||||
expect(fetchClaudeRateLimits).toHaveBeenCalledTimes(2)
|
||||
expect(service.getState().claude?.fableWeekly?.usedPercent).toBe(39)
|
||||
expect(service.getState().claude?.extraUsage?.balance).toBe(39)
|
||||
|
||||
service.stop()
|
||||
} finally {
|
||||
|
||||
@@ -172,8 +172,9 @@ export abstract class RateLimitServiceFetchPolicy extends RateLimitServiceFetchT
|
||||
provider: 'claude',
|
||||
session,
|
||||
weekly,
|
||||
// Statusline lacks Fable scope; retain the latest OAuth value.
|
||||
// Statusline lacks Fable scope and credits; retain the latest OAuth values.
|
||||
fableWeekly: inherited?.fableWeekly ?? null,
|
||||
extraUsage: inherited?.extraUsage ?? null,
|
||||
updatedAt: Date.now(),
|
||||
error: null,
|
||||
status: 'ok',
|
||||
|
||||
Reference in New Issue
Block a user