Ration v0.1 — AI quota tracker extension (Claude + Codex) - #1
Merged
Merged
Conversation
Zero runtime dependencies by design (see PRD store-review mitigations): typescript, esbuild, vitest and @types/chrome as dev-only tooling. build.mjs bundles the two MV3 entry points and copies public/ to dist/. scripts/gen-icons.mjs generates the committed PNG icons with node built-ins only, so no image tooling is required to regenerate them.
The ProviderAdapter/ProviderSnapshot/QuotaLane contract every provider normalizes to, with headroomPct as the universal comparator (a provider's headroom is the minimum across its lanes — you're blocked by whichever ceiling you hit first). The validator is a hand-rolled ~90-line combinator library instead of zod, keeping the extension at zero runtime dependencies. A shape mismatch fails with a precise path and is never coerced to zero, so endpoint drift always surfaces as an honest error state rather than a wrong number.
All state lives in chrome.storage.local under versioned v1: keys — MV3 service workers are ephemeral, so nothing survives in memory. Backoff is pure math (5 min base, doubling, 60 min cap, ±25% jitter with injectable randomness for deterministic tests) over a persisted record, so polite-polling state survives worker restarts too.
Rides the existing chatgpt.com browser session with credentials:'include'. Parses both observed field-name conventions (primary_window/secondary_window and five_hour_limit/weekly_limit) as named schema variants matched on literal field names, plus additional_rate_limits[] as extra lanes. Every failure path — 401/403, 429, 5xx, non-JSON body, network error, unknown shape — is an enumerated snapshot status; the adapter never throws and never renders an unparseable response as a number. Fixture tests cover both variants, all error paths, and assert that no Authorization header is ever sent.
Rides the user's existing claude.ai browser session only — the adapter never constructs an Authorization header (asserted by a test on every request), never touches token or cookie values, and the extension never requests the cookies permission. This keeps the project clear of Anthropic's restriction on Claude Code / claude.ai OAuth tokens. Org id is discovered via /api/organizations (chat-capable org preferred) and cached. The usage endpoint behind Settings -> Usage is undocumented, so the adapter probes a short candidate list; the first path that returns 200 AND validates is cached so steady-state refreshes make one request. If nothing verifies the honest result is an endpoint_not_verified error, and the issue templates give the community a DevTools capture path to pin the real endpoint. Also adds the adapter registry (src/adapters/index.ts) — adding a future provider is one file plus one registry line.
The service worker is wiring only — every handler reloads state from chrome.storage.local because MV3 workers are ephemeral. Refresh orchestration encodes the politeness policy: a hard floor of one request per provider per 60s regardless of trigger (persisted before the fetch so it survives worker death mid-flight), the persisted backoff gate, and a freshness gate so popup opens reuse recent snapshots. The badge shows the lowest headroom across enabled providers — the wall you hit first — going amber at 40%, red below 15%, grey ! on any error or stale snapshot, and staying invisible when everything is fine. computeBadge is pure and boundary-tested; refresh gating is tested against an in-memory chrome fake.
The popup reads only from chrome.storage.local: it renders the last snapshots instantly, asks the worker for a refresh, and re-renders as results stream in via storage.onChanged — no network on the render path. Cards sort by headroom descending (the routing answer), with per-lane bars, reset countdowns and a staleness header; error and logged-out states render as honest cards, and disabled providers appear as toggles that request their host permission at enable time (user gesture). Vanilla DOM via createElement/textContent only — provider-derived strings never meet innerHTML. MV3 manifest requests just storage+alarms up front; claude.ai and chatgpt.com are optional_host_permissions. No cookies permission.
README carries the commitments that belong in front of users, not just in a PRD: zero telemetry and no credential access, the Anthropic OAuth constraint (Ration never touches Claude Code tokens), and the honest warning that these are undocumented endpoints — breakage looks like a grey !, never a wrong number, and the adapter-broken issue template gives the community a redacted DevTools capture path to fix it fast. CONTRIBUTING documents the five-artifact adapter contribution with the non-negotiable rules (never throw, never coerce to zero, match raw field names) and a worked Cursor example. LICENSE placeholder filled.
Runs npm ci, typecheck, vitest, and the esbuild build on every PR and push to main; sanity-parses the built manifest, fails if any runtime dependency ever appears in package.json, and uploads dist/ as the ration-unpacked artifact — most of eventual store packaging for free.
devudilip
force-pushed
the
claude/ration-quota-tracker-g3uwc6
branch
from
August 28, 2026 20:11
5488cbe to
883b628
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
First working version of Ration — an MV3 Chrome extension that shows remaining quota across AI subscriptions in one click, so you can decide which tool to spend on this task without visiting each provider's settings page.
What's included
fetch(..., { credentials: 'include' })with per-provider optional host permissions requested at enable time. Nocookiespermission, no token handling of any kind — tests assert noAuthorizationheader is ever constructed.!on any error or stale reading.chrome.storage.localkeys (MV3 workers are ephemeral).Verification
npm run cigreen: strict typecheck, 54 tests, build. Tests cover the validator, backoff math, badge thresholds/precedence, headroom min-across-lanes logic, refresh gating against a fakedchrome.*, and both adapters against fixtures (happy path per schema variant, 401→unauthenticated, 429→rate_limited, garbage→schema_mismatch, never-throws).Honest limitation: this was built in a container with no logged-in provider sessions, so the real endpoints could not be hit pre-merge. The compensation is structural: every network outcome is an enumerated, tested state — the worst live-world case is a grey
!with a precise error code, never a wrong number. The claude.ai usage endpoint in particular is a runtime probe with an explicitendpoint_not_verifiedstate and a community capture path in the issue templates.To verify locally:
npm install && npm run build, loaddist/unpacked atchrome://extensions, toggle on providers while logged into their sites, and cross-check the percentages against each provider's own usage page.🤖 Generated with Claude Code
https://claude.ai/code/session_015gwwHsEgVxtmfRn3dEkBif
Generated by Claude Code