mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 23:19:43 +02:00
feat(web): show Codex plan usage in header
This commit is contained in:
+44
-2
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @fileoverview Pure parsing + formatting of Claude Code statusline telemetry.
|
||||
* @fileoverview Pure parsing + formatting of Claude and Codex plan telemetry.
|
||||
*
|
||||
* Claude Code (v2.1.80+) pipes a JSON blob to a configured `statusLine.command`
|
||||
* on each render. On Pro/Max subscriptions that blob carries a `rate_limits`
|
||||
@@ -15,7 +15,10 @@
|
||||
* Only those two windows exist (no Opus-weekly field). `rate_limits` is absent
|
||||
* before the first API response and for non-subscriber auth — both yield null.
|
||||
*
|
||||
* All functions are pure for testability. See `test/usage-telemetry.test.ts`.
|
||||
* The Codex parser consumes the read-only `account/rateLimits/read` app-server
|
||||
* response and selects only the main `codex` bucket, excluding model-specific
|
||||
* buckets. All functions are pure for testability. See
|
||||
* `test/usage-telemetry.test.ts` and `test/codex-plan-usage.test.ts`.
|
||||
*
|
||||
* @module usage-telemetry
|
||||
*/
|
||||
@@ -51,6 +54,22 @@ export interface RawStatuslinePayload {
|
||||
model?: { display_name?: string };
|
||||
}
|
||||
|
||||
interface RawCodexRateLimitWindow {
|
||||
usedPercent?: unknown;
|
||||
windowDurationMins?: unknown;
|
||||
resetsAt?: unknown;
|
||||
}
|
||||
|
||||
interface RawCodexRateLimitSnapshot {
|
||||
primary?: RawCodexRateLimitWindow | null;
|
||||
secondary?: RawCodexRateLimitWindow | null;
|
||||
}
|
||||
|
||||
interface RawCodexRateLimitsResponse {
|
||||
rateLimits?: RawCodexRateLimitSnapshot | null;
|
||||
rateLimitsByLimitId?: Record<string, RawCodexRateLimitSnapshot | null> | null;
|
||||
}
|
||||
|
||||
function clampPct(n: number): number {
|
||||
if (!Number.isFinite(n)) return 0;
|
||||
return Math.max(0, Math.min(100, n));
|
||||
@@ -88,6 +107,29 @@ export function parseStatusTelemetry(data: RawStatuslinePayload | undefined): St
|
||||
return t;
|
||||
}
|
||||
|
||||
/** Normalize the main Codex app-server bucket into the chip's two known windows. */
|
||||
export function parseCodexRateLimitsResponse(value: unknown): StatusTelemetry | null {
|
||||
if (!value || typeof value !== 'object') return null;
|
||||
const response = value as RawCodexRateLimitsResponse;
|
||||
const snapshot = response.rateLimitsByLimitId?.codex ?? response.rateLimits;
|
||||
if (!snapshot || typeof snapshot !== 'object') return null;
|
||||
|
||||
const telemetry: StatusTelemetry = {};
|
||||
for (const window of [snapshot.primary, snapshot.secondary]) {
|
||||
if (!window || typeof window.usedPercent !== 'number' || !Number.isFinite(window.usedPercent)) continue;
|
||||
if (window.windowDurationMins !== 300 && window.windowDurationMins !== 10_080) continue;
|
||||
const resetsAt =
|
||||
typeof window.resetsAt === 'number' && Number.isFinite(window.resetsAt) && window.resetsAt > 0
|
||||
? Math.round(window.resetsAt * 1000)
|
||||
: 0;
|
||||
const normalized = { usedPercentage: clampPct(window.usedPercent), resetAt: resetsAt };
|
||||
if (window.windowDurationMins === 300) telemetry.fiveHour = normalized;
|
||||
if (window.windowDurationMins === 10_080) telemetry.sevenDay = normalized;
|
||||
}
|
||||
|
||||
return telemetry.fiveHour || telemetry.sevenDay ? telemetry : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current-session status for the in-terminal statusline footer. This is the
|
||||
* "status of the current session" the user sees in Claude's footer — distinct
|
||||
|
||||
Reference in New Issue
Block a user