OpenCode plugin provider for 9Router — FREE AI Router & Token Saver. 40+ providers, 100+ models.
Registers 9Router as a custom provider in OpenCode with auto-discovery of models.
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@vheins/opencode-9router@latest"]
}- Add the plugin to
opencode.json - Restart OpenCode
/models→ select a 9Router model
The plugin will auto-discover models from http://localhost:20128 (default).
- Auto-discover models — Models from 9Router are automatically detected on startup
- Smart caching — Discovery results cached for 3 hours (configurable) for instant subsequent loads
- Stale fallback — If the backend is unreachable, returns cached models instead of failing
- Configurable timeout — Adjustable discovery timeout (default 30s) for slow backends
- Dynamic model list — All models from 9Router are available, including custom combos
- Combo multimodal defaults — Combo models (
owned_by: "combo") that expose no capability info are forced to accept text/image/audio input with tool calling, reasoning, 256K context, 128K output, andlow/medium/high/xhigh/max/minimal/thinkingvariants - OpenAI-compatible — Uses
@ai-sdk/openai-compatible - Type-safe — Uses the
confighook for provider registration that conforms to the OpenCode config schema
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@vheins/opencode-9router@latest"]
}No need to define a provider manually — the plugin registers it automatically.
Add one or more 9Router-family providers with custom options:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@vheins/opencode-9router@latest"],
"provider": {
"9router": {
"npm": "@ai-sdk/openai-compatible",
"name": "Your Provider",
"options": {
"baseURL": "https://your-9router-endpoint.example.com/v1",
"apiKey": "{env:ROUTER_API_KEY}",
"cache": true,
"cacheTTL": 10800000,
"discoveryTimeout": 60000
}
},
"9router-local": {
"npm": "@ai-sdk/openai-compatible",
"name": "Local Router",
"options": {
"baseURL": "http://127.0.0.1:20128/v1",
"apiKey": "sk-...",
"cache": true,
"discoveryTimeout": 60000
}
}
}
}Each provider key must start with 9router. The npm must be @ai-sdk/openai-compatible. The name is a display label shown in OpenCode.
Never commit real API keys. Replace the
baseURL/apiKeyabove with your own endpoint and a placeholder such as{env:ROUTER_API_KEY}(or a dummy value likesk-...). Real keys belong in environment variables, not inopencode.jsonor this README.
{
"provider": {
"9router": {
"npm": "@ai-sdk/openai-compatible",
"name": "Remote 9Router",
"options": {
"baseURL": "https://example.com/v1",
"apiKey": "{env:ROUTER_API_KEY}",
"cache": true,
"discoveryTimeout": 30000
}
}
}
}export ROUTER_API_KEY=your-api-key-here
opencode| Option | Type | Default | Description |
|---|---|---|---|
baseURL |
string |
http://localhost:20128 |
9Router API endpoint |
apiKey |
string |
— | API key (if required by backend) |
cache |
boolean |
true |
Cache discovery results to ~/.cache/opencode-9router/ |
cacheTTL |
number |
10800000 (3h) |
Cache TTL in milliseconds |
discoveryTimeout |
number |
30000 (30s) |
Timeout for /v1/models request in milliseconds |
{
"provider": {
"9router": {
"npm": "@ai-sdk/openai-compatible",
"name": "Local Router",
"options": {
"baseURL": "http://localhost:20128",
"cache": true,
"discoveryTimeout": 60000
}
},
"9router-remote": {
"npm": "@ai-sdk/openai-compatible",
"name": "Remote API",
"options": {
"baseURL": "https://your-9router-endpoint.example.com/v1",
"apiKey": "{env:ROUTER_API_KEY}",
"cacheTTL": 3600000
}
}
}
}{
"provider": {
"9router": {
"npm": "@ai-sdk/openai-compatible",
"name": "Local Router",
"options": {
"baseURL": "http://localhost:20128",
"apiKey": "sk-...",
"cache": false,
"discoveryTimeout": 30000
}
}
}
}/models
→ Find the 9Router provider
→ Pick any model (e.g., kr/claude-sonnet-4.5, cc/claude-opus-4-7)
| Prefix | Provider | Tier |
|---|---|---|
cc/ |
Claude Code | Subscription |
cx/ |
Codex | Subscription |
gh/ |
GitHub Copilot | Subscription |
cu/ |
Cursor IDE | Subscription |
kr/ |
Kiro AI | FREE |
oc/ |
OpenCode Free | FREE |
vertex/ |
Vertex AI | $300 credits |
glm/ |
GLM | $0.6/1M |
minimax/ |
MiniMax | $0.2/1M |
kimi/ |
Kimi | $9/mo flat |
openrouter/ |
OpenRouter | API key |
deepseek/ |
DeepSeek | API key |
groq/ |
Groq | API key |
opencode.json "plugin": ["@vheins/opencode-9router@latest"]
↓
Plugin loads at startup detects 9Router-family providers in config
↓
For each provider:
1. Check local cache (~/.cache/opencode-9router/discovery-*.json)
└─ Fresh cache (≤3h) → return cached models instantly
2. If cache miss/stale:
└─ GET {baseURL}/v1/models with 30s timeout (configurable)
└─ If OK → register live models + save to cache
└─ If fail → fallback to stale cache (if exists), else skip
↓
config hook creates/updates provider with discovered models
↓
Provider appears in /models
Cache files are stored at ~/.cache/opencode-9router/discovery-{base64url}.json, one per unique baseURL. The cache directory also stores the models.dev capability catalog (models-dev.json).
- Combos count as enriched only on an exact models.dev segment match (no more loose substring hits like
vision→gpt-4-turbo-vision) - Ignore bare
/models/infocapability stubs ({tools: true}) for combos, so backends that return stubs still get forced multimodal defaults
- Detect combo models via
owned_by: "combo"(fallback: no provider prefix) - Force multimodal defaults on combos with no resolved capability info: text/image/audio input, tool calling, reasoning, 256K context, 128K output
- Add
low/medium/high/xhigh/max/minimal/thinkingvariants, plussearch: false,context_length, andmax_completion_tokens - Version discovery cache so older entries are refetched
- Add INFO-level logging for cache hit/miss, fetch status, timeout, and stale fallback
- Model discovery cache — 3-hour TTL with stale fallback when backend is unreachable
- Configurable timeout — 30s default, adjustable via
discoveryTimeoutoption - Provider options —
cache,cacheTTL,discoveryTimeout - New constant:
DISCOVERY_CACHE_TTL,DISCOVERY_TIMEOUT
- Support multiple 9Router-family providers (
9router,9router-local, etc.) - Fallback models catalog via
models.devAPI - Per-model capability resolution (vision, tools, reasoning)
- Basic auto-discovery from
localhost:20128 - Fallback models when API unreachable
- Single provider support
git clone https://github.com/vheins/opencode-9router
cd opencode-9router
npm install
npm run buildnode test-minimal.mjs
opencode models 9router --print-logsAfter publishing to npm, verify the plugin works end-to-end:
opencode models 9router-local --print-logs --log-level DEBUGThis runs model discovery against the published package with full debug logging to confirm caching, timeout, and capability resolution are working correctly.
npm login
npm version patch # or minor, major
npm publish --access public
git push --follow-tagsopencode-9router/
src/
plugin.ts # Plugin entry (provider registration + config hook)
discovery.ts # Model discovery pipeline
capabilities.ts # Capability resolution (catalog + per-model API)
cache.ts # Discovery + models.dev cache
types.ts # Shared types
utils.ts # Shared helpers
constants.ts # Models, defaults, prefixes
dist/ # Compiled output (generated)
package.json # npm package config
tsconfig.json # TypeScript config
README.md # This file
README.id.md # Bahasa Indonesia version
LICENSE # MIT