mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 00:02:31 +00:00
fix: parse Claude Fable current-week usage
Claude Code 2.1.199 renders the Fable plan bucket as Current week (Fable), while Orca only parsed a standalone Fable heading. Accept weekly-style Fable headings as the distinct Fable weekly meter while keeping broader Fable-weekly copy as a section boundary to avoid false positives. Validation: - pnpm exec vitest run --config config/vitest.config.ts src/main/rate-limits/claude-pty.test.ts src/main/rate-limits/claude-fetcher.test.ts src/main/rate-limits/service.test.ts src/renderer/src/components/status-bar/tooltip.test.ts src/renderer/src/components/status-bar/inline-usage-bars.test.tsx - pnpm run typecheck - pnpm exec oxlint src/main/rate-limits/claude-pty.ts src/main/rate-limits/claude-pty.test.ts src/main/rate-limits/claude-fetcher.ts src/main/rate-limits/claude-fetcher.test.ts src/main/rate-limits/service.ts src/main/rate-limits/service.test.ts src/renderer/src/components/status-bar/tooltip.test.ts src/renderer/src/components/status-bar/inline-usage-bars.test.tsx - git diff --check HEAD~1..HEAD - live local Claude fetcher returned fableWeekly: 62
This commit is contained in:
@@ -258,6 +258,49 @@ describe('fetchViaPty', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('parses Claude current-week Fable usage as a distinct weekly window', async () => {
|
||||
const term = makeMockTerm()
|
||||
spawnMock.mockReturnValue(term)
|
||||
|
||||
const resultPromise = fetchViaPty()
|
||||
|
||||
await vi.advanceTimersByTimeAsync(2_000)
|
||||
term.emitData(`
|
||||
Plan usage limits
|
||||
|
||||
Current session
|
||||
8% used
|
||||
Resets 3:39am
|
||||
|
||||
Current week (all models)
|
||||
33% used
|
||||
Resets Jul 3 at 12:59pm
|
||||
|
||||
Current week (Fable)
|
||||
62% used
|
||||
Resets Jul 3 at 12:59pm
|
||||
`)
|
||||
await vi.advanceTimersByTimeAsync(2_000)
|
||||
|
||||
await expect(resultPromise).resolves.toMatchObject({
|
||||
provider: 'claude',
|
||||
status: 'ok',
|
||||
session: {
|
||||
usedPercent: 8,
|
||||
resetDescription: '3:39am'
|
||||
},
|
||||
weekly: {
|
||||
usedPercent: 33,
|
||||
resetDescription: 'Jul 3 at 12:59pm'
|
||||
},
|
||||
fableWeekly: {
|
||||
usedPercent: 62,
|
||||
resetDescription: 'Jul 3 at 12:59pm'
|
||||
},
|
||||
error: null
|
||||
})
|
||||
})
|
||||
|
||||
it('does not let an incomplete Fable section consume later usage sections', async () => {
|
||||
const term = makeMockTerm()
|
||||
spawnMock.mockReturnValue(term)
|
||||
|
||||
@@ -22,6 +22,8 @@ const SESSION_RE = /current\s*session/i
|
||||
const WEEKLY_RE = /(?:current\s*week|weekly\s*(?:limits?|usage|rate\s*limits?)|7\s*[- ]?\s*day)/i
|
||||
const FABLE_WORD_RE = /\bfable\b/i
|
||||
const FABLE_LABEL_RE = /^\s*fable\s*$/i
|
||||
const FABLE_WEEKLY_LABEL_RE =
|
||||
/(?:current\s*week|weekly\s*(?:limits?|usage|rate\s*limits?)|7\s*[- ]?\s*day)\s*(?:\([^)]*\bfable\b[^)]*\)|[-:]?\s*\bfable\b)/i
|
||||
const PERCENT_RE = /(\d{1,3})(?:\.\d+)?\s*%\s*(used|consumed|left|remaining|available)/i
|
||||
const RESET_LINE_RE = /resets?\s+(?:at\s+|in\s+)?(.+)/i
|
||||
const ESC = String.fromCharCode(27)
|
||||
@@ -45,6 +47,12 @@ function matchesFableBoundary(line: string): boolean {
|
||||
return FABLE_LABEL_RE.test(line) || (FABLE_WORD_RE.test(line) && WEEKLY_RE.test(line))
|
||||
}
|
||||
|
||||
function matchesFableUsageLabel(line: string): boolean {
|
||||
// Why: broad Fable-weekly copy should stop nearby scans, but only a real
|
||||
// Fable usage heading should produce the distinct Fable meter.
|
||||
return FABLE_LABEL_RE.test(line) || FABLE_WEEKLY_LABEL_RE.test(line)
|
||||
}
|
||||
|
||||
function isSectionLabel(line: string): boolean {
|
||||
return SESSION_RE.test(line) || matchesWeeklyLabel(line) || matchesFableBoundary(line)
|
||||
}
|
||||
@@ -104,7 +112,7 @@ function parsePtyUsage(output: string): {
|
||||
|
||||
const sessionPct = extractPercentAfterLabel(lines, (line) => SESSION_RE.test(line))
|
||||
const weeklyPct = extractPercentAfterLabel(lines, matchesWeeklyLabel)
|
||||
const fableWeeklyPct = extractPercentAfterLabel(lines, (line) => FABLE_LABEL_RE.test(line))
|
||||
const fableWeeklyPct = extractPercentAfterLabel(lines, matchesFableUsageLabel)
|
||||
|
||||
const session: RateLimitWindow | null =
|
||||
sessionPct !== null
|
||||
@@ -132,7 +140,7 @@ function parsePtyUsage(output: string): {
|
||||
usedPercent: Math.min(100, Math.max(0, fableWeeklyPct)),
|
||||
windowMinutes: 10080,
|
||||
resetsAt: null,
|
||||
resetDescription: extractResetAfterLabel(lines, (line) => FABLE_LABEL_RE.test(line))
|
||||
resetDescription: extractResetAfterLabel(lines, matchesFableUsageLabel)
|
||||
}
|
||||
: null
|
||||
|
||||
|
||||
Reference in New Issue
Block a user