feat(usage): report OpenCode Go subscription usage - #5
Conversation
OCG is a subscription (rolling/weekly/monthly windows) but had no usage reporting, so the dashboard couldn't show what actually stops the next request. opencode exposed the windows via GET /zen/go/v1/usage on 2026-08-11 (anomalyco/opencode#16513); it authenticates with the same Go API key already stored, so no cookie or workspace id is needed. The endpoint is still undocumented and was reshaped by a follow-up commit (d4704347) an hour after the PR merged, so the handler is written against the deployed response, captured live and pinned in the test as the contract. Note the shapes proposed in the upstream issues never shipped. category stays "apikey" — that field is the auth mechanism, not the billing model, and Go authenticates with a key. features.usageApikey is what lets /api/usage accept a non-OAuth connection. Quotas are percent-only (the server exposes no dollar amounts): set remainingPercentage and leave `remaining` unset, which the UI would otherwise read as a 0-100 percent.
The notice read "$5/mo (then 0/mo)" — a mangled string that lost the "$1" of "$10" and left a double space, so the ongoing price showed as "0/mo". Confirmed pricing is $5 for the first month, then $10/mo.
|
Warning Review limit reached
Next review available in: 55 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughOpenCode Go now supports usage retrieval through its ChangesOpenCode Go usage support
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant UsageService
participant OpenCodeGoUsage
participant OpenCodeGoAPI
participant ProviderLimits
UsageService->>OpenCodeGoUsage: pass API key and proxy options
OpenCodeGoUsage->>OpenCodeGoAPI: request /zen/go/v1/usage with bearer authorization
OpenCodeGoAPI-->>OpenCodeGoUsage: return quota windows and reset times
OpenCodeGoUsage->>ProviderLimits: provide normalized quota data
ProviderLimits-->>UsageService: render remaining percentages and limit status
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@open-sse/services/usage/opencode-go.js`:
- Around line 32-42: Update open-sse/services/usage/opencode-go.js:32-42 in
formatWindow to return no quota when window.percent is not finite, rather than
defaulting it to zero; update open-sse/services/usage/opencode-go.js:99-103 to
skip adding the quota when formatWindow rejects the window; update
tests/unit/opencode-go-usage.test.js:127-137 with a response missing percent and
assert the dashboard receives no 100%-remaining quota.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 88a8d36c-405b-4495-b7f0-898a398448dc
📒 Files selected for processing (5)
open-sse/providers/registry/opencode-go.jsopen-sse/services/usage.jsopen-sse/services/usage/opencode-go.jssrc/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.jstests/unit/opencode-go-usage.test.js
There was a problem hiding this comment.
Pull request overview
Adds OpenCode Go (opencode-go / ocg) usage & quota reporting so the dashboard can display rolling/weekly/monthly windows (percent-based) alongside other supported providers.
Changes:
- Introduces a new OpenCode Go usage handler that calls
GET /zen/go/v1/usageand normalizes the three windows into percent-based quotas. - Registers
opencode-goin the usage dispatcher and marks the provider registry entry as usage-capable via API key. - Extends dashboard quota parsing to render the OpenCode Go usage rows; adds unit tests covering success + failure modes.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
open-sse/services/usage/opencode-go.js |
New usage fetcher for OpenCode Go; maps rolling/weekly/monthly windows into percent-only quotas and handles error cases without throwing. |
open-sse/services/usage.js |
Registers the opencode-go usage handler in the dispatcher. |
open-sse/providers/registry/opencode-go.js |
Adds transport.usage.url and enables features.usage + features.usageApikey; updates the UI notice text. |
src/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.js |
Adds an opencode-go case in parseQuotaData so the dashboard renders the quota rows appropriately. |
tests/unit/opencode-go-usage.test.js |
New tests validating endpoint calling, quota mapping, dashboard parsing behavior, and key failure modes. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
formatWindow defaulted a missing or non-numeric `percent` to 0, so a malformed window was published as 100% remaining — malformed data read as full headroom, the most dangerous direction to be wrong in for a quota display. Return null instead and skip the window. Status is checked before the percent gate: "rate-limited" is authoritative on its own, so discarding an unusable percent must not also discard the throttling signal. Parsing still goes through toFiniteNumber, so a numeric string percent is accepted rather than rejected as malformed.
What
Adds usage/quota reporting for OpenCode Go (
ocg), so the dashboard shows its rolling, weekly, and monthly windows alongside Claude Code and Codex.open-sse/services/usage/opencode-go.js(new) — fetchesGET /zen/go/v1/usage, maps the three windows to percent-based quotas.open-sse/services/usage.js— registersopencode-goin the dispatcher.open-sse/providers/registry/opencode-go.js— addstransport.usage.urlandfeatures.usage/usageApikey; fixes the price in the connect notice.ProviderLimits/utils.js—parseQuotaDatacase so the dashboard renders the rows.tests/unit/opencode-go-usage.test.js(new) — 9 tests.Why
OCG has always been a subscription with rolling/weekly/monthly limits, but 10router had no usage reporting for it — the dashboard couldn't show what actually stops the next request. There was no public API for it until now: opencode shipped
GET /zen/go/v1/usageon 2026-08-11 (anomalyco/opencode#16513), closing a request open since March. It authenticates with the same Go API key already stored, so no session cookie or workspace id is needed.Notes for reviewers
The endpoint is undocumented and the obvious references are wrong. It was reshaped by a follow-up commit (
d4704347) less than an hour after the PR merged, so the PR description is already stale, and the JSON in upstream issues #16017 / #31084 was only ever a user proposal — none of those shapes shipped. This handler is written against the deployed response, captured live and pinned in the test as the contract:{"usage":{ "rolling": {"status":"ok","percent":0,"resetsAt":"2026-08-12T20:23:13.083Z"}, "weekly": {"status":"ok","percent":0,"resetsAt":"2026-08-17T00:00:00.083Z"}, "monthly": {"status":"ok","percent":91,"resetsAt":"2026-08-25T01:33:44.083Z"}}}categorydeliberately staysapikey. That field is the auth mechanism, not the billing model — cc/codex areoauthbecause of how they log in, and there is nosubscriptionvalue. Go authenticates with a plain key, sofeatures.usageApikeyis what lets/api/usageaccept the connection.Quotas are percent-only. The server exposes no dollar amounts (limits are held in micro-cents internally and only the percentage is returned). The handler sets
remainingPercentageand leavesremainingunset — the UI readsremainingas a 0-100 percent, the same trap Qoder and grok-cli hit. There's an explicit test that 91% used renders as 9% remaining.Failure modes are covered. 401, 403 (valid key without a Go subscription — distinct from an auth failure, since re-authorizing can't fix it), 5xx, the SPA 404 HTML you'd get if the endpoint were rolled back, an unexpected 200 shape, a missing key, and a network error. All return a message rather than throwing.
Testing
verify-aliasandverify-oauth-urlsbaselines are byte-for-byte equal.verify-providersshows only the pre-existingtokenrouter.thinkingFormatdiff, unrelated and untouched.translator/golden-url-header.test.js(blackbox,anthropic,cline,kimi) are pre-existing — verified by stashing and re-running on a clean tree.parseQuotaData/getRemainingPercentage, but the live key was disabled before the page could be loaded, so a render would have proved nothing. Worth one look at the usage page with a working Go key.The price fix is a separate commit so it can be reverted independently — it rests on confirmation from @some-du6e ($5 first month, then $10/mo), not on something read from source. The previous string,
"$5/mo (then 0/mo)", had lost the$1of$10.Summary by CodeRabbit
New Features
Bug Fixes