Skip to content

Commit e038b21

Browse files
committed
feat: add authenticated live quota adapter with opt-in config
What: new authenticatedQuota.ts reads OAuth credentials from ~/.claude and ~/.codex, fetches live 5h/7d usage from provider APIs, and parses window data into LiveQuotaWindow. Wired into RefreshScheduler behind promptFuel.liveQuotaEnabled (default false). Errors flow through sanitizeError() to fixed labels only; no raw responses, paths, or credentials surface to UI. Why: local history reflects usage on this machine only; live quota from the provider APIs is the authoritative remaining-capacity signal. Boundaries: adapter is purely local auth (no extension-side auth flow, no credential storage). Live fetch is skipped entirely when the setting is false; stub readers return freshness=unavailable instead. Validation: npm run compile PASS, smoke:core 97/97, smoke:providers 51/51, validate:manifest PASS, package PASS, git diff --check clean. Security review: two AgentBridge internal comments removed from authenticatedQuota.ts before commit.
1 parent 0b02009 commit e038b21

6 files changed

Lines changed: 417 additions & 3 deletions

File tree

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@
9696
"minimum": 0,
9797
"maximum": 1440,
9898
"description": "Auto-refresh interval in minutes. Set to 0 to disable auto-refresh."
99+
},
100+
"promptFuel.liveQuotaEnabled": {
101+
"type": "boolean",
102+
"default": false,
103+
"description": "Fetch live 5h/7d quota from provider APIs. Requires existing Claude/Codex OAuth credentials."
99104
}
100105
}
101106
}

scripts/smoke-core.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ test('CONFIG_DEFAULTS.refreshIntervalMinutes is number', () => {
9696

9797
test('CONFIG_DEFAULTS has expected keys', () => {
9898
const keys = Object.keys(CONFIG_DEFAULTS).sort();
99-
assert.deepStrictEqual(keys, ['displayMode', 'enabledProviders', 'refreshIntervalMinutes']);
99+
assert.deepStrictEqual(keys, ['displayMode', 'enabledProviders', 'liveQuotaEnabled', 'refreshIntervalMinutes']);
100100
});
101101

102102
// --- formatQuota ---

src/config.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,13 @@ export function getConfig(): PromptFuelConfig {
2525
? Math.max(0, Math.min(1440, rawInterval))
2626
: CONFIG_DEFAULTS.refreshIntervalMinutes;
2727

28-
return { enabledProviders, displayMode, refreshIntervalMinutes };
28+
const rawLiveQuota = cfg.get<boolean>(
29+
'liveQuotaEnabled',
30+
CONFIG_DEFAULTS.liveQuotaEnabled
31+
);
32+
const liveQuotaEnabled = typeof rawLiveQuota === 'boolean'
33+
? rawLiveQuota
34+
: CONFIG_DEFAULTS.liveQuotaEnabled;
35+
36+
return { enabledProviders, displayMode, refreshIntervalMinutes, liveQuotaEnabled };
2937
}

src/core/configDefaults.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ export interface PromptFuelConfig {
66
enabledProviders: ProviderId[];
77
displayMode: DisplayMode;
88
refreshIntervalMinutes: number;
9+
liveQuotaEnabled: boolean;
910
}
1011

1112
export const CONFIG_DEFAULTS: PromptFuelConfig = {
1213
enabledProviders: ['claude', 'codex'],
1314
displayMode: 'compact',
1415
refreshIntervalMinutes: 5,
16+
liveQuotaEnabled: false,
1517
};

src/core/refreshScheduler.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { CodexLocalReader } from '../providers/codexLocal';
99
import { runEnabledReaders } from '../providers/readProviders';
1010
import { createStubReader } from '../providers/liveQuotaReader';
1111
import { runLiveQuotaReaders } from '../providers/readLiveQuota';
12+
import { createAuthenticatedReader } from '../providers/authenticatedQuota';
1213

1314
export class RefreshScheduler {
1415
private timer: ReturnType<typeof setTimeout> | undefined;
@@ -82,7 +83,15 @@ export class RefreshScheduler {
8283

8384
const cfg = getConfig();
8485
const localReaders = [new ClaudeLocalReader(), new CodexLocalReader()];
85-
const liveReaders = [createStubReader('claude'), createStubReader('codex')];
86+
87+
let liveReaders;
88+
if (cfg.liveQuotaEnabled) {
89+
liveReaders = await Promise.all(
90+
cfg.enabledProviders.map(id => createAuthenticatedReader(id)),
91+
);
92+
} else {
93+
liveReaders = cfg.enabledProviders.map(id => createStubReader(id));
94+
}
8695

8796
let localResults: ReadResult[];
8897
let liveResults: LiveQuotaStatus[];

0 commit comments

Comments
 (0)