Dynamic 9router provider plugin for opencode.
It discovers available models from your 9router endpoint at startup and injects them into opencode automatically, so model lists do not need to be hardcoded in opencode.json.
- Discovers models dynamically from
OPENCODE_9ROUTER_URL(default:http://localhost:20128/v1) - Registers provider
9routerusing@ai-sdk/openai-compatible - Sends
OPENCODE_9ROUTER_API_KEYas Bearer auth when discovering models - Injects dynamically discovered models into opencode config at runtime
- Accepts every valid live model ID;
kindis optional and ignored during discovery - Preserves valid live metadata for model name, reasoning, tools, vision, PDF support, and token limits
- Uses reviewed static catalog lookup and reasoning variants for known LLM routes
- Uses
models.devfor non-capability metadata and limit fallback only; mapper failures use a safe model template without stopping other models - Does not write opencode config from the runtime plugin
- Includes an explicit installer/check CLI for safer setup and troubleshooting
Use opencode's native plugin installer:
opencode plugin opencode-9router-plusThen set your API key and restart opencode.
Windows (cmd):
setx OPENCODE_9ROUTER_API_KEY "sk-..."macOS/Linux:
export OPENCODE_9ROUTER_API_KEY="sk-..."If your 9router endpoint is not the default local URL, also set:
export OPENCODE_9ROUTER_URL="http://localhost:20128/v1"Windows (cmd):
setx OPENCODE_9ROUTER_URL "http://localhost:20128/v1"Restart opencode, then verify:
opencode models 9routerIf the native installer is unavailable, use the package CLI:
npx opencode-9router-plus installThe CLI first tries opencode plugin opencode-9router-plus. If that fails, it falls back to safe config editing.
Useful options:
npx opencode-9router-plus install --global
npx opencode-9router-plus install --project
npx opencode-9router-plus install --config ./opencode.json
npx opencode-9router-plus install --dry-run
npx opencode-9router-plus install --yes
npx opencode-9router-plus install --manualThe fallback editor:
- detects
OPENCODE_CONFIGand warns aboutOPENCODE_CONFIG_CONTENT - supports global and project config targets
- creates backups before writing existing files
- writes atomically through a temp file and rename
- avoids duplicate plugin entries, including tuple entries like
["opencode-9router-plus", {}] - refuses to edit JSONC files with comments because preserving comments safely is not guaranteed
Run diagnostics:
npx opencode-9router-plus checkIt checks:
- target config path
- whether config parses successfully
- whether plugin entry is present
- whether
OPENCODE_9ROUTER_API_KEYis set - whether
opencode models 9routerreturns models
Use opencode's native plugin management if available. Otherwise:
npx opencode-9router-plus uninstall --globalor:
npx opencode-9router-plus uninstall --projectIf you prefer to edit config manually, add the package name to plugin:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-9router-plus"]
}Do not hardcode model lists. The plugin lists them dynamically from the live /v1/models endpoint at startup.
OPENCODE_9ROUTER_URL(optional): 9router base URL. Defaulthttp://localhost:20128/v1OPENCODE_9ROUTER_API_KEY(recommended): API key used by provider options and model discovery requestsOPENCODE_9ROUTER_TIMEOUT_MS(optional): fetch timeout in ms. Default5000
Runtime discovery accepts first occurrence of every explicit, valid model id from a root array or a models or data array. kind is ignored, so routes that are not LLMs may appear when exposed by endpoint. Invalid, control-character, whitespace-padded, and dangerous object-key IDs remain rejected.
Discovery reads own data properties only; inherited properties and accessors are ignored. Each endpoint response is capped at 5 MiB, listing is capped at 2,000 records, and records or capability objects with more than 32 own keys retain only valid ID with empty live metadata. Empty, malformed, or oversized responses try compatible fallback endpoints: /models, /model, then base URL.
Valid live metadata has final authority: name; reasoning; tools; vision/PDF attachment support; and complete token-limit pairs. Boolean values accept only true, false, "true", or "false"; invalid values are ignored. false reasoning remains false and removes reasoning variants. Vision or PDF enables attachments; tools enables tool calling; temperature remains disabled.
Limits are atomic: capability pair first, then top-level live pair, then models.dev. Pairs never combine values from different sources.
bun install
bun run check
bun run typecheck
bun run lint
bun run format:check
bun run format
bun run knip
bun run test:deterministic
bun run build
bun run clean
bun run prepublishOnlyprepublishOnly intentionally uses npm lifecycle commands internally: npm run clean && npm run build.
bun run check runs Prettier format check, Biome lint, unused-code, typecheck, and deterministic tests. bun run format writes Prettier formatting for tracked source, config, and docs files, excluding lockfiles and dist/. Pull requests and pushes to main run this same gate. Release tags run it before npm publish. Run bun run build to produce dist/. Run opt-in live smoke only against a reachable 9router endpoint with an API key:
OPENCODE_9ROUTER_API_KEY="sk-..." bun run test:livetest:live requires OPENCODE_9ROUTER_API_KEY; it is not part of check and has no claimed pass result here.
/v1/models controls runtime listing by valid ID, regardless of kind. Reviewed static catalog provides canonical metadata lookup, fallback reasoning, and supported reasoning variants for known LLM routes. Variants appear only when final reasoning is true.
models.dev enriches non-capability fields such as family, release date, cost, modalities, and complete token-limit pairs when live limits are unavailable. It never controls reasoning, attachments, tools, or temperature. For non-catalog routes, metadata applies only when exactly one global catalog key has an exact, case-sensitive final path segment matching route final path segment; no provider guessing, normalization, or route-ID rewrite occurs. Dynamic, unmatched, or unexpectedly failing models receive a safe template while remaining models continue loading.
Scheduled upstream watch reports only whitelisted catalog-input paths. It never runs upstream source, applies detector output, refreshes catalog, publishes, tags, or releases.
Manual catalog refresh is offline and audited: use explicit 40 lowercase hexadecimal upstream SHA, review changes in separate refresh PR, then run local tooling against approved inputs.
Project notes:
- Source lives in
src/; generated output goes todist/. - Tests run with
bun test, including catalog extraction and upstream watch behavior. AGENTS.mdtracks project facts for coding agents; keep it aligned with README changes.
- Restart opencode after changing config or installing plugins.
- If
/modeldoes not show 9router models, runnpx opencode-9router-plus check. - If models are empty, verify that your 9router endpoint is running and
/modelsis reachable. - If one model has malformed metadata or mapping fails, it is registered with conservative defaults; check endpoint data if its name or capabilities look incomplete.
- If you see
Missing API Key, setOPENCODE_9ROUTER_API_KEYand restart opencode. - If plugin loads twice, remove duplicate
opencode-9router-plusentries from your OpenCode config before restarting.
https://github.com/ricatix/opencode-9router-plus
Forked from the original opencode-9router-plugin by mdhb2.
