diff options
| author | Adam Malczewski <[email protected]> | 2026-10-14 17:04:35 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-10-14 17:04:35 +0900 |
| commit | 10e78065201c8387494b8aafabb987da87183b3a (patch) | |
| tree | f038f368459acbd3b817974100fb4bd4817ecbff /.dispatch | |
| parent | 551a7a050e8cd25e795026a1060c9d5b5ea028bf (diff) | |
| download | dispatch-web-10e78065201c8387494b8aafabb987da87183b3a.tar.gz dispatch-web-10e78065201c8387494b8aafabb987da87183b3a.zip | |
feat(account-usage): sidebar Usage view with Claude/Codex quota bars + on-track pace markers
Consumes GET /usage (transport-contract 0.25.0). DaisyUI progress bars per quota
window with a dot at the elapsed fraction of the window (the 'on track' level),
ahead/under-pace labels, reset countdowns, stale/unavailable/not-configured states.
Diffstat (limited to '.dispatch')
| -rw-r--r-- | .dispatch/transport-contract.reference.md | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/.dispatch/transport-contract.reference.md b/.dispatch/transport-contract.reference.md index 0a3a347..043c95b 100644 --- a/.dispatch/transport-contract.reference.md +++ b/.dispatch/transport-contract.reference.md @@ -5,6 +5,9 @@ > permission prompt). Your CODE still imports `@dispatch/transport-contract` normally — this file is for > READING only. > +> **2026-10-14 delta (account-usage — ADDITIVE, `0.25.0`):** `GET /usage` + `GET /usage/:provider` and the +> `AccountUsage*` types (verbatim at the end of this file). +> > **Orchestrator:** SNAPSHOT of `[email protected]` (MCP status + computers + provider concurrency + cancel queued message). Regenerate whenever > it changes. > @@ -1253,3 +1256,117 @@ ALWAYS route to the self-hosted Firecrawl client; the tool's `map` mode is REMOV (service not loaded). The umans entry appears in `available` only when the backend has `UMANS_API_KEY`; an unconfigured backend is listed but `configured: false` — the UI disables it, it is never an error. + +## Account usage (`[email protected]`, verbatim) + +```ts +// ─── Account usage (Claude / Codex subscription quotas) ─────────────────────── +// +// Subscription quota usage polled from each provider's first-party account +// API (Claude `GET /api/oauth/usage`, Codex `GET /backend-api/wham/usage`), +// cached server-side. Distinct from per-turn token `Usage`: these are the +// account's plan allowance windows, never estimated from token counts. + +/** The subscription accounts the usage service reports on. */ +export type AccountUsageProviderId = "claude" | "codex"; + +/** + * Freshness/availability of one provider's report: + * - `fresh` — the last refresh succeeded and is within the cache TTL. + * - `stale` — the latest refresh FAILED; `limits` are the last good + * report (≤ 24h old), see `fetchedAt` / `error`. + * - `unavailable` — refresh failed and there is no last-good report. + * - `unsupported` — a credential exists but cannot query subscription usage + * (e.g. a plain Anthropic API key). + * - `not-configured` — no credential / the auth extension is not loaded. + */ +export type AccountUsageState = + | "fresh" + | "stale" + | "unavailable" + | "unsupported" + | "not-configured"; + +/** Severity of a single limit: `warning` ≥ 90% used, `exhausted` ≥ 100%. */ +export type AccountUsageLimitStatus = "ok" | "warning" | "exhausted" | "unknown"; + +/** + * One quota meter. Percent meters have `unit: "percent"`, `limit: 100`. + * Spend meters (`kind: "spend"`, `unit: "usd"`) may have `limit: null` when the + * cap is unlimited/unset — never treat that as a zero-dollar cap. + */ +export interface AccountUsageLimit { + /** Stable id, e.g. `claude:5h`, `claude:7d:opus`, `codex:primary`, `codex:spark:secondary`. */ + readonly id: string; + /** Human label, e.g. "5 Hour", "7 Day (Opus)", "Spark · 5 hours". */ + readonly label: string; + readonly kind: "window" | "spend"; + /** Window id, e.g. `5h`, `7d`, `30d`, `extra`. */ + readonly windowId: string; + /** True for account-wide windows that gate every request. */ + readonly shared: boolean; + /** Model family / metered-feature slug for scoped meters, else null. */ + readonly tier: string | null; + /** Window length in ms (null when unknown, e.g. spend meters). */ + readonly durationMs: number | null; + /** Epoch-ms when the window resets (null when unknown). */ + readonly resetsAt: number | null; + readonly unit: "percent" | "usd"; + readonly used: number; + readonly limit: number | null; + readonly remaining: number | null; + /** 0..1 (null when there is no cap). */ + readonly usedFraction: number | null; + readonly remainingFraction: number | null; + readonly status: AccountUsageLimitStatus; +} + +/** Codex paid-credit details (from `/wham/usage` `credits` + `spend_control`). */ +export interface AccountUsageCredits { + readonly hasCredits: boolean | null; + readonly unlimited: boolean | null; + readonly overageLimitReached: boolean | null; + /** Remaining credit balance as reported (string or number upstream; normalized to number). */ + readonly balance: number | null; + readonly spendControlReached: boolean | null; +} + +/** One provider's usage report as served to clients. Never carries credentials. */ +export interface AccountUsageReport { + readonly provider: AccountUsageProviderId; + /** Display name: "Claude" / "Codex". */ + readonly label: string; + readonly state: AccountUsageState; + readonly account: { + readonly accountId: string | null; + readonly email: string | null; + readonly planType: string | null; + } | null; + readonly limits: readonly AccountUsageLimit[]; + /** + * Plan-level verdict (Codex): `limitReached` = plan allowance consumed; + * `canServe` = the account can still serve requests (credits may fund + * overage). Null when unknown (Claude reports per-window only). + */ + readonly limitReached: boolean | null; + readonly canServe: boolean | null; + /** True when Codex credits fund requests past an exhausted plan window. */ + readonly creditOverage: boolean; + readonly credits: AccountUsageCredits | null; + /** Epoch-ms of the report's data (last successful fetch), null if never. */ + readonly fetchedAt: number | null; + /** Epoch-ms of the last refresh attempt (success or failure), null if never. */ + readonly lastAttemptAt: number | null; + /** Epoch-ms after which the server will refresh on the next request. */ + readonly nextRefreshAt: number | null; + /** Last refresh failure message (sanitized, no secrets), null on success. */ + readonly error: string | null; +} + +/** Response of `GET /usage` — one report per provider, always both, in fixed order (claude, codex). */ +export interface AccountUsageResponse { + readonly providers: readonly AccountUsageReport[]; + /** Server clock (epoch-ms) when this response was built — use it to compute window progress. */ + readonly servedAt: number; +} +``` |
