npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@route_33/opencode

v0.4.6

Published

opencode provider plugin for the route33.ai AI gateway — key-scoped model discovery, cost/limit enrichment, auth, and the key-setup wizard.

Readme

@route_33/opencode

An opencode provider plugin for the route33.ai AI gateway: model discovery, cost/limit metadata, and a key-setup wizard in the TUI.

Install

opencode plugin @route_33/opencode

The installer patches both configs: the server plugin into opencode.json and the key-setup wizard (TUI plugin) into tui.json. To install manually instead, declare both entries yourself:

// opencode.json — server plugin (model discovery, cost/limit enrichment, auth)
{
  "plugin": ["@route_33/opencode"]
}
// tui.json — TUI plugin (the key-setup wizard)
{
  "plugin": ["@route_33/opencode"]
}

This plugin never writes your opencode.json or opencode.jsonc: opencode has no TUI-plugin auto-discovery, so without a tui.json entry the wizard simply never appears — the server plugin works regardless. The only files the plugin ever writes inside your repo are an optional .opencode/route33.key (git-ignored automatically) and your own .opencode/route33-overrides.json (yours to commit or ignore).

Auth

Run opencode auth login and choose route_33, or set the ROUTE33_API_KEY environment variable. Without a key, the TUI wizard offers to save one: per-project inside a git workspace, global otherwise. A globally stored key applies only outside git workspaces — inside a workspace, keys resolve from the environment, the workspace config, or .opencode/route33.key (per-project keys are the metering boundary). Skipping the project-key wizard snoozes it for 7 days — "Skip" for that workspace only, "Skip for all workspaces" everywhere (both live in opencode's TUI kv store; remove project-key-prompt.dismissed-at* from ~/.local/state/opencode/kv.json to re-arm sooner). ROUTE33_BASE_URL overrides the default gateway URL; ROUTE33_TUI_PROMPT=0 disables the wizard.

Your key is never written into the resolved provider configuration: authentication is attached per request at fetch time, so opencode debug config output contains no credential material. One inert exception: the options carry the non-secret marker __route33_fetch_shim_auth__ as apiKey, because the native per-model SDKs used for anthropic/, openai/, and mistralai/ models (@ai-sdk/anthropic, @ai-sdk/openai, @ai-sdk/mistral) refuse to construct a request without one — the marker satisfies that null check, never authenticates anything (the gateway reads only the request-time Authorization header, which the fetch shim injects), and no vendor env var (ANTHROPIC_API_KEY etc.) is needed or consulted. The marker is skipped by key resolution and never overrides an apiKey you authored yourself; it is visible in config dumps precisely because it is not a secret.

Model catalog

The catalog is discovered dynamically. The plugin calls GET {baseURL}/models with your resolved API key, caches the response on disk, and injects only the models that key can see. The refresh ladder is:

  1. Return the fresh on-disk cache if it has not expired.
  2. Fetch live from the gateway and update the cache.
  3. If the live fetch fails, return the stale cache.
  4. If there is no cache and no live response, preserve existing generated entries and inject no replacement models.

The plugin does not embed or serve a static catalog. The on-disk cache is keyed by gateway URL and API key, so the same key pointed at a different gateway (ROUTE33_BASE_URL, e.g. a local cluster) never shares a cache with production. Catalog cache envelopes are versioned; cache files from older plugin versions are ignored after an upgrade and repopulated from the gateway.

If discovery yields no catalog, the plugin logs exactly one warning:

route33: no model catalog available — 0 models injected (reason). route33 is unusable without a catalog: resolve an API key (opencode auth login / ROUTE33_API_KEY) — the key dictates which models are available.

Set ROUTE33_DISCOVERY=0 to opt out of discovery entirely. This is logged at info and injects zero models without the warning.

Metadata provenance

Models are decorated with metadata from the route33 gateway broadcast; models.dev is used only as a backfill source.

  • Cost: the gateway's broadcast pricing wins; models.dev fills in when the broadcast carries no pricing.
  • Limits: the gateway's broadcast limits win; models.dev fills in when the gateway broadcasts none. Limits are omitted entirely when neither source knows them; the plugin never fabricates them.
  • Modalities: gateway modalities.input / modalities.output are passed through to opencode. Invalid modality values and malformed modality objects are omitted per model.
  • Attachment: gateway attachment wins whenever present. models.dev supplies it only when the gateway omits the field.
  • Capabilities: the broadcast reasoning and tool_support flags win (tool_support is mapped to tool_call); models.dev always backfills any capability flag left undefined.
  • Variants: R33 broadcast variants are passed verbatim (for example, kimi-k3's max reasoningEffort variant).

The normative broadcast contract is docs/gateway-limits-variants-broadcast.md.

Managed model entries

Gateway-discovered provider.route_33.models entries are fully managed: every startup rebuilds the map from the current broadcast, then deep-merges your overrides on top. The ownership rule is the whole design — in the broadcast (or in your overrides) ⇒ kept; anything else ⇒ dropped and logged — so retired gateway models disappear automatically and no bookkeeping file is needed. (Versions ≤0.4.3 kept a .opencode/route33-generated.json ledger for this; the plugin deletes that obsolete file on first run.)

Keep customizations in .opencode/route33-overrides.json, because the installed OpenCode ProviderConfig does not allow unknown provider-level keys such as modelOverrides. Overrides deep-merge onto matching generated entries and can add custom models:

{
  "models": {
    "openai/gpt-5.5": { "variants": { "fast": { "temperature": 0 } } },
    "custom/internal": { "name": "Internal model" }
  }
}

If the gateway fetch fails with no usable cache, regeneration is skipped entirely: existing entries are never pruned on zero information.

ROUTE33_ENRICH_CAPS has been removed; capability backfill from models.dev is now always on.

Model-reference tolerance

Model references in your config that differ from the served ID only by digit-boundary dots versus dashes in the tail resolve automatically. For example, anthropic/claude-opus-4.8 resolves to the served anthropic/claude-opus-4-8 and is injected with the served wire ID. Arbitrary renames, such as gpt5.5 for openai/gpt-5.5, do not resolve.

Environment variables

| Variable | Effect | |---|---| | ROUTE33_API_KEY | Gateway API key; highest precedence. See Auth. | | ROUTE33_BASE_URL | Override the default gateway base URL. See Auth. | | ROUTE33_TUI_PROMPT | Set to 0 to disable the key-setup wizard. See Auth. | | ROUTE33_DISCOVERY | Set to 0 to disable dynamic catalog discovery. | | ROUTE33_STRICT | Set to 0 to downgrade unknown-model reference errors to a warning plus a stub entry. | | ROUTE33_CACHE_DIR | Override the directory used for the on-disk catalog cache. |

The plugin reads only these variables. ROUTE33_ENRICH_CAPS is no longer supported.

Exhaustion retry

Starting with v0.4.6, the plugin wraps live chat/compat requests in an exhaustion retry layer. It triggers only on an HTTP 429 whose error code is PROVIDER_LANES_EXHAUSTED (all provider lanes exhausted). It reads the gateway's Retry-After response header and falls back to a client-side schedule of 5·2^(n−1) seconds capped at 1200 when the header is missing. It waits through the delay while respecting the request's abort signal, then re-issues the identical request. It keeps retrying indefinitely until the request succeeds or the user cancels it.

Non-exhaustion errors (network failures, 4xx responses from the gateway other than the exhaustion code, etc.) are passed through untouched.

Each wait is logged with the attempt number, waited seconds, and the gateway's correlationId:

route33: exhausted; waiting <seconds>s before retry <attempt> (correlationId=<correlationId>)

The provider option headerTimeout is set to 1_500_000 ms (25 minutes). This is higher than opencode's own 300-second fetch default so that scheduled waits are not aborted while waiting for a lane to cool down. It is safe because the gateway caps provider hangs at 120 s TTFB and 240 s first-event timeouts, so the connection is never silent for anywhere near 25 minutes.

License

Proprietary — "SEE LICENSE IN LICENSE". Install and use as-is via npm; copying, modification, and redistribution are not permitted. Provided AS-IS, without warranty or liability.