Skip to content

feat(quota): use official OpenCode Go usage API - #12124

Merged
diegosouzapw merged 1 commit into
cheaper-inference:release/v3.8.51from
ddarkr:feat/opencode-go-usage-api
Aug 30, 2026
Merged

diegosouzapw merged 1 commit into
cheaper-inference:release/v3.8.51from
ddarkr:feat/opencode-go-usage-api

Conversation

@ddarkr

@ddarkr ddarkr commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Why

OpenCode now exposes Go subscription usage through the API-key-authenticated GET https://opencode.ai/zen/go/v1/usage endpoint. OmniRoute still targeted the retired /quota probe and required operators to copy a workspace ID plus browser auth cookie for dashboard scraping. That left Provider Limits and quota preflight on separate data paths.

The upstream blocker tracked in #3844 is resolved by anomalyco/opencode#16513 and anomalyco/opencode@d4704347465c.

What changed

  • make /zen/go/v1/usage the default OMNIROUTE_OPENCODE_QUOTA_URL endpoint
  • validate the official rolling/weekly/monthly response and normalize percent from 0..100 to OmniRoute's 0..1 quota fraction
  • route opencode-go Provider Limits through the same cached fetcher used by preflight and quota monitoring
  • remove OpenCode Go workspace/auth-cookie scraping, modal inputs, persistence wiring, env examples, and documentation
  • preserve the existing $12 / 5-hour, $30 / week, and $60 / month Provider Limits presentation
  • keep HTTP/network/malformed-response handling fail-open, with the existing throttle and 60-second success TTL

Quota and routing safety

  • status: rate-limited is treated as effective 100% usage, so Provider Limits shows 0% remaining and routing uses that window's own reset time
  • status: ok at 100% is also exhausted
  • malformed or partial window sets are rejected instead of fabricating quota data
  • pasted Bearer ... credentials are normalized before constructing Authorization
  • cache entries are bound to both connection ID and normalized credential, preventing quota leakage after an in-place API-key edit
  • legacy persisted cookie fields remain response-redacted but are no longer read or written by the OpenCode Go flow

Validation

  • node --import tsx/esm --test tests/unit/opencode-go-quota-no-zai.test.ts tests/unit/opencode-go-usage.test.ts tests/unit/opencode-quota-fetcher.test.ts tests/unit/quota-exhaustion-cutoff-opencode.test.ts — 28 passed
  • npx vitest run --config vitest.config.ts tests/unit/ui/add-api-key-modal-free-models.test.tsx — 5 passed
  • npm run typecheck:core — passed
  • npx eslint . --cache --cache-location .eslintcache --suppressions-location config/quality/eslint-suppressions.json --pass-on-unpruned-suppressions — passed
  • git diff upstream/release/v3.8.51 --check — passed

Inherited base failures

⚠️ base-red inherited: #11874

The latest release/v3.8.51 tip currently has unrelated failures:

  • npm run lint: stale suppressions in tests/unit/cli-nodes-commands.test.ts (no-explicit-any count 19→15 and stale no-unused-vars)
  • npm run typecheck:noimplicit:core: implicit-any errors in open-sse/services/systemPrompt.ts:80,93
  • npm run check:docs-all: undocumented base env vars CLI_5DIVE_BIN and CLI_5DIVE_STATE_DIR
  • isolated next dev visual smoke: existing Turbopack panic, there must be a path to a root

None of those files are changed by this PR.

@ddarkr
ddarkr requested a review from diegosouzapw as a code owner August 30, 2026 12:41
@diegosouzapw
diegosouzapw merged commit a2c5d8a into cheaper-inference:release/v3.8.51 Aug 30, 2026
10 of 16 checks passed
diegosouzapw added a commit that referenced this pull request Aug 30, 2026
…align 7 tests to merged contracts

No new ESLint warnings: the exact CI command (lint:json --max-warnings 0) reports 278
problems on the tip — 226 from eslint-plugin-react-hooks 7 compiler rules
(set-state-in-effect 167, immutability 36, refs/static-components/purity/
preserve-manual-memoization) that were masked until the lockfile change of
dfc84ba invalidated the ESLint cache, plus 46 no-explicit-any in
tests/unit/call-log-cap.test.ts (#12026). Velocity phase: frozen with
`eslint --suppress-all` (+668 suppressions); the 5 now-unused
`eslint-disable react-hooks/immutability` directives and one unused import removed.
Verified: lint:json --max-warnings 0 → 0 problems.

Tests aligned to contracts merged this afternoon (all reproduced red on the pure tip):
- providers-constants-split: 235 → 236 (Perplexity Agent, #12103)
- sse-auth: a forced pin outside allowedConnections now yields no credential
  instead of silently falling back (#12080)
- with-chat-admission-10786: withInjectionGuard(postHandler, { logger: null }) (#12117)
- hard-session-lease-bypass-inventory: classify src/app/api/oauth/codex/import/route.ts (#12116)
- usage-service-hardening: OpenCode Go official usage API shape (#12124)
- i18n placeholder parity: apiManager.restrictedToConnections rewritten as a plain
  ICU plural (`{count, plural, one {# connection} other {# connections}}`) in en,
  vi, pt-BR and the 40 __MISSING__ mirrors — the parity extractor counts every
  `{word}` including the old literal `{s}`

Refs #12103, #12080, #12117, #12116, #12124, #12026
diegosouzapw added a commit that referenced this pull request Aug 30, 2026
…e/v3.8.51 (round 5: provider count 352, TS2554/TS2677) (#12144)

* fix(ci): clear the base-reds the 2026-08-30 afternoon merge batch left on release/v3.8.51 (round 5)

- docs-counts / check-docs-counts-sync test: #12103 (Perplexity Agent) made it 352
  providers; README, AGENTS.md, llm.txt (+42 i18n mirrors), package.json description
  and the 4 README diagrams still said 351.
- api-route-typecheck: #11971 passes a third `{ featureEnabled }` argument to
  appendNoThinkingVariants() that the helper never accepted (TS2554 — and the flag
  silently did nothing); the helper now honours it. src/lib/skills/interception.ts
  narrowed a mapped object with a `Record<string, string>` predicate (TS2677) —
  predicate typed with the actual element shape.

Gates: check:docs-counts OK (test 28/28), check:docs-sync PASS, check:api-typecheck
OK (289 frozen). Refs #12103, #11971

* docs(env): document RATE_LIMIT_EXECUTION_MAX_WAIT_MS (#12027 added it to .env.example only)

* fix(ci): round 5b — freeze the react-hooks compiler-rule violations, align 7 tests to merged contracts

No new ESLint warnings: the exact CI command (lint:json --max-warnings 0) reports 278
problems on the tip — 226 from eslint-plugin-react-hooks 7 compiler rules
(set-state-in-effect 167, immutability 36, refs/static-components/purity/
preserve-manual-memoization) that were masked until the lockfile change of
dfc84ba invalidated the ESLint cache, plus 46 no-explicit-any in
tests/unit/call-log-cap.test.ts (#12026). Velocity phase: frozen with
`eslint --suppress-all` (+668 suppressions); the 5 now-unused
`eslint-disable react-hooks/immutability` directives and one unused import removed.
Verified: lint:json --max-warnings 0 → 0 problems.

Tests aligned to contracts merged this afternoon (all reproduced red on the pure tip):
- providers-constants-split: 235 → 236 (Perplexity Agent, #12103)
- sse-auth: a forced pin outside allowedConnections now yields no credential
  instead of silently falling back (#12080)
- with-chat-admission-10786: withInjectionGuard(postHandler, { logger: null }) (#12117)
- hard-session-lease-bypass-inventory: classify src/app/api/oauth/codex/import/route.ts (#12116)
- usage-service-hardening: OpenCode Go official usage API shape (#12124)
- i18n placeholder parity: apiManager.restrictedToConnections rewritten as a plain
  ICU plural (`{count, plural, one {# connection} other {# connections}}`) in en,
  vi, pt-BR and the 40 __MISSING__ mirrors — the parity extractor counts every
  `{word}` including the old literal `{s}`

Refs #12103, #12080, #12117, #12116, #12124, #12026

* fix(ci): run the ESLint warnings job on the box with an 8 GB heap; reserved-prefix set 398 → 400

The cold full lint with the react-hooks 7 compiler rules is killed on the 7 GB hosted
runner with no message (status null → exit 1, JSON never written) — it only looked
green while the ESLint cache was warm. tests/unit/provider-node-reserved-prefix.test.ts
aligned to the two prefixes the afternoon batch registered (#12103).

* test(ci): document the lint-guard runner exception; #9147 event-loop gap 400 → 800 ms

quality-rail-gate-membership pinned lint-guard to ubuntu-latest; the cold full lint is
OOM-killed there, so the job now runs on omni-light with an 8 GB heap — the test keeps
fast-gates pinned and asserts the documented exception. With the catalog at 352
providers the hosted shards measure 410–633 ms gaps on 9147-catalog-eventloop-yield
(3 runs); 800 ms still fails a true pin. Re-tighten with the v4.0 catalog split.

* chore(quality): summarize the ESLint report on failure — a red lint:json printed nothing

--format json --output-file swallows every problem; a red 'No new ESLint warnings' job
gave zero output (three blind debugging rounds in #12144), and a killed process (OOM,
status null) was equally silent. On any non-zero exit the runner now prints the problem
count and the first 60 'file:line rule — message' lines from the report.

* chore(lint): freeze react-hooks/immutability for the 5 UI test harnesses in the suppressions file

The rule fires for these files in CI but not locally (compiler analysis divergence),
so the inline eslint-disable directives read as 'unused directive' warnings locally.
A suppressions entry is symmetric: suppressed where the rule fires, tolerated as
unpruned (--pass-on-unpruned-suppressions) where it does not. Found via the new
lint:json failure summary.
fenix007 pushed a commit to fenix007/OmniRoute that referenced this pull request Sep 4, 2026
Backport upstream cheaper-inference#12124 onto the frozen 3.8.48 stable fork. Replace the obsolete Z.AI/dashboard scraping path with the API-key-authenticated OpenCode usage endpoint and keep quota preflight fail-open.

Co-Authored-By: GPT-5 Codex <codex@openai.com>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
)

Migra o quota fetcher do OpenCode Go para a API oficial de uso, com refactor substancial que remove ~1850 linhas de código legado e atualiza a suíte de testes existente inteira para o novo contrato. Validado no worktree combinado (typecheck limpo, testes focados verdes). Obrigado!
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…e/v3.8.51 (round 5: provider count 352, TS2554/TS2677) (cheaper-inference#12144)

* fix(ci): clear the base-reds the 2026-08-30 afternoon merge batch left on release/v3.8.51 (round 5)

- docs-counts / check-docs-counts-sync test: cheaper-inference#12103 (Perplexity Agent) made it 352
  providers; README, AGENTS.md, llm.txt (+42 i18n mirrors), package.json description
  and the 4 README diagrams still said 351.
- api-route-typecheck: cheaper-inference#11971 passes a third `{ featureEnabled }` argument to
  appendNoThinkingVariants() that the helper never accepted (TS2554 — and the flag
  silently did nothing); the helper now honours it. src/lib/skills/interception.ts
  narrowed a mapped object with a `Record<string, string>` predicate (TS2677) —
  predicate typed with the actual element shape.

Gates: check:docs-counts OK (test 28/28), check:docs-sync PASS, check:api-typecheck
OK (289 frozen). Refs cheaper-inference#12103, cheaper-inference#11971

* docs(env): document RATE_LIMIT_EXECUTION_MAX_WAIT_MS (cheaper-inference#12027 added it to .env.example only)

* fix(ci): round 5b — freeze the react-hooks compiler-rule violations, align 7 tests to merged contracts

No new ESLint warnings: the exact CI command (lint:json --max-warnings 0) reports 278
problems on the tip — 226 from eslint-plugin-react-hooks 7 compiler rules
(set-state-in-effect 167, immutability 36, refs/static-components/purity/
preserve-manual-memoization) that were masked until the lockfile change of
9d8554f invalidated the ESLint cache, plus 46 no-explicit-any in
tests/unit/call-log-cap.test.ts (cheaper-inference#12026). Velocity phase: frozen with
`eslint --suppress-all` (+668 suppressions); the 5 now-unused
`eslint-disable react-hooks/immutability` directives and one unused import removed.
Verified: lint:json --max-warnings 0 → 0 problems.

Tests aligned to contracts merged this afternoon (all reproduced red on the pure tip):
- providers-constants-split: 235 → 236 (Perplexity Agent, cheaper-inference#12103)
- sse-auth: a forced pin outside allowedConnections now yields no credential
  instead of silently falling back (cheaper-inference#12080)
- with-chat-admission-10786: withInjectionGuard(postHandler, { logger: null }) (cheaper-inference#12117)
- hard-session-lease-bypass-inventory: classify src/app/api/oauth/codex/import/route.ts (cheaper-inference#12116)
- usage-service-hardening: OpenCode Go official usage API shape (cheaper-inference#12124)
- i18n placeholder parity: apiManager.restrictedToConnections rewritten as a plain
  ICU plural (`{count, plural, one {# connection} other {# connections}}`) in en,
  vi, pt-BR and the 40 __MISSING__ mirrors — the parity extractor counts every
  `{word}` including the old literal `{s}`

Refs cheaper-inference#12103, cheaper-inference#12080, cheaper-inference#12117, cheaper-inference#12116, cheaper-inference#12124, cheaper-inference#12026

* fix(ci): run the ESLint warnings job on the box with an 8 GB heap; reserved-prefix set 398 → 400

The cold full lint with the react-hooks 7 compiler rules is killed on the 7 GB hosted
runner with no message (status null → exit 1, JSON never written) — it only looked
green while the ESLint cache was warm. tests/unit/provider-node-reserved-prefix.test.ts
aligned to the two prefixes the afternoon batch registered (cheaper-inference#12103).

* test(ci): document the lint-guard runner exception; cheaper-inference#9147 event-loop gap 400 → 800 ms

quality-rail-gate-membership pinned lint-guard to ubuntu-latest; the cold full lint is
OOM-killed there, so the job now runs on omni-light with an 8 GB heap — the test keeps
fast-gates pinned and asserts the documented exception. With the catalog at 352
providers the hosted shards measure 410–633 ms gaps on 9147-catalog-eventloop-yield
(3 runs); 800 ms still fails a true pin. Re-tighten with the v4.0 catalog split.

* chore(quality): summarize the ESLint report on failure — a red lint:json printed nothing

--format json --output-file swallows every problem; a red 'No new ESLint warnings' job
gave zero output (three blind debugging rounds in cheaper-inference#12144), and a killed process (OOM,
status null) was equally silent. On any non-zero exit the runner now prints the problem
count and the first 60 'file:line rule — message' lines from the report.

* chore(lint): freeze react-hooks/immutability for the 5 UI test harnesses in the suppressions file

The rule fires for these files in CI but not locally (compiler analysis divergence),
so the inline eslint-disable directives read as 'unused directive' warnings locally.
A suppressions entry is symmetric: suppressed where the rule fires, tolerated as
unpruned (--pass-on-unpruned-suppressions) where it does not. Found via the new
lint:json failure summary.
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.

2 participants