Skip to content

feat: show OpenCode Go subscription rate limits on Mac and iPhone - #12

Merged
cte13 merged 1 commit into
mainfrom
feat/opencode-go-limits
Aug 21, 2026
Merged

feat: show OpenCode Go subscription rate limits on Mac and iPhone#12
cte13 merged 1 commit into
mainfrom
feat/opencode-go-limits

Conversation

@cte13

@cte13 cte13 commented Aug 21, 2026

Copy link
Copy Markdown
Owner

What

Shows the OpenCode Go plan's usage limits (5-hour rolling / weekly / monthly percentages with reset countdowns) everywhere the Claude/Codex limits already appear:

  • macOS popover — OpenCode tab gets a "Limits (official)" section with three rows, a red "Limit reached" badge when a window reports rate-limited, and the shared 15-min stale badge
  • Menu barOpenCode 41% line (max of the three windows), gated on OpenCode usage today like Claude/Codex
  • iPhone appGo 5h / Go Weekly / Go Monthly rows in the LimitsCard, with real reset dates (unlike the flattened Codex rows)
  • Warnings/notifications/bubble alerts, highestLimitUtilization, candy-eligible windows, and limitsReady all include the new windows

How

Official endpoint GET https://opencode.ai/zen/go/v1/usage (shipped upstream 2026-08-11, anomalyco/opencode#16513), authenticated with the opencode-go key already on disk in ~/.local/share/opencode/auth.json — no browser cookies, no workspace ID. Verified live against production during development.

Implementation notes:

  • OpenCodeGoLimitsProvider reuses LocalAdditionalUsageReader.defaultOpenCodeRoots (so OPENCODE_DATA_DIR just works) and sends an explicit User-Agent: PokeTokenBar/<version> — the server 403s default tool UAs (reproduced with Python-urllib; regression-tested)
  • Polling is throttled to one attempt per 5 minutes (success or failure): the endpoint is a 3-table join with no cache headers or documented poll interval, and the default refresh cycle is 2 minutes
  • 401/403 (unknown key / no Go subscription) and network failures keep the previous value and just hide the section — same contract as Codex limits
  • 403-vs-UA distinction: a 403 with a valid key means "no subscription"; the UA block was a separate 403 that a custom header fixes

Test hermeticity fix (found by the suite)

Adding a third limits provider to UsageStore.init made every existing test construction that runs refresh() without stubbing it hit the live network with the developer's real API keyRareCandyGrantIntegrationTests failed with live data leaking into candy windows. Beyond stubbing the affected sites (Grok/RareCandy/UsageStoreTests), the real provider is now gated on AppEnv.isBundledApp (same single gate as notifications/keychain/production logs) so swift test can never make this class of call by construction. PTB_PARITY=1 unlocks a live endpoint smoke test (testLiveOpenCodeGoUsageEndpoint) for schema-drift detection.

The env-guard test needed AppEnv allowlisted for the new PTB_PARITY flag read (AppEnv.isParityRun) — same QA-flag category as CompanionStore's PTB_STATE_DIR.

Verification

  • swift test: 751 tests, only the documented pre-existing testHermesAcceptsMillisecondStartedAt timezone failure (fails identically on main)
  • 16 new tests: payload decoding (real production fixture incl. rate-limited window + empty windows), auth.json key parsing, request headers, menu line gating, warning state, highest-utilization gating, candy windows, poll throttle, failure handling, phone payload backward/forward compatibility
  • llvm-cov --show-regions: zero ^0 regions in the new provider, models, and all touched UsageStore branches
  • PTB_PARITY=1 live check: rolling=72 weekly=69 monthly=34 decoded correctly end-to-end
  • Both Xcode targets (PokeTokenBar macOS, PokeTokenBariOS simulator) build

The widget intentionally keeps its curated compact limit set (no Opus/Sonnet/Codex-2nd either) — can extend later if wanted.

Query the official GET https://opencode.ai/zen/go/v1/usage endpoint with
the opencode-go API key from ~/.local/share/opencode/auth.json and surface
the 5-hour rolling / weekly / monthly utilization windows like the existing
Claude and Codex limit sections.

- OpenCodeGoLimitsProvider: HTTP fetch with explicit User-Agent (the server
  403s default tool UAs — verified against production), key read from the
  shared OPENCODE_DATA_DIR-aware root list, gated on AppEnv.isBundledApp so
  swift test / raw binaries never spend the user's key on network calls
  (PTB_PARITY=1 unlocks a live endpoint check)
- UsageStore: fetch alongside Claude/Codex limits with a 5-minute minimum
  poll interval (endpoint is a 3-table join with no cache guidance), feed
  menu bar line, warning state, highestLimitUtilization, candy windows,
  notifications and limitsReady
- Popover: three limit rows with reset countdowns on the OpenCode tab,
  rate-limited badge and 15-min stale badge
- iPhone payload: new optional opencodeGo{5h,Weekly,Monthly} windows,
  backward/forward compatible, rendered in the LimitsCard
@cte13
cte13 merged commit 8e929a5 into main Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant