- JavaScript 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
- Build cost object only from present finite values - Skip cache_read/cache_write unless input or output pricing exists - Omit cost key entirely when no finite pricing data - Add test for ECONNREFUSED path (fetch throws, not HTTP error) |
||
| test | ||
| dynamic-models.mjs | ||
| package.json | ||
| README.md | ||
opencode-plugins
Custom plugins for opencode.
Setup
git clone ssh://git@git.chaops.de/rwxd/opencode-plugins.git <path>
mkdir -p ~/.config/opencode/plugin
ln -s <path>/dynamic-models.mjs ~/.config/opencode/plugin/<plugin>
Replace <path> with the directory you want to clone into, e.g. ~/dev/opencode-plugins.
Then register the symlink in ~/.config/opencode/opencode.json and restart opencode:
{
"plugin": ["/home/<user>/.config/opencode/plugin/dynamic-models.mjs"]
}
Plugins
dynamic-models
Discovers models, pricing and capabilities for any OpenAI-compatible provider by querying its /v1/models endpoint at startup. Use it for gateways that expose an OpenAI-compatible API but are not a provider opencode already knows a model catalogue for (e.g. GoModel instances). opencode has no built-in support for this, so a custom provider plus this plugin is required.
Set dynamic: true on the provider's options. No models entry is needed, they are discovered automatically. Multiple providers are supported, add dynamic: true to each. Discovery failures are shown as a toast and logged via client.app.log, the provider simply has no models for that session.
apiKey is optional. If set it is used as a fallback, but credentials from opencode auth login for the same provider ID take precedence. They are read from $XDG_DATA_HOME/opencode/auth.json (default ~/.local/share/opencode/auth.json), honoring the OPENCODE_AUTH_CONTENT environment variable. Only api type credentials are used, OAuth credentials are ignored. A provider without a resolvable key is skipped and reported as an error.
Custom providers like this do not appear in /connect's provider list, that list only contains models.dev providers and providers registered by plugin auth hooks. To store a key via opencode, run /connect (or opencode auth login), pick Other, enter the provider ID (e.g. my-gateway) and paste the API key, the plugin picks it up from auth.json automatically.
{
"provider": {
"my-gateway": {
"npm": "@ai-sdk/openai-compatible",
"name": "My Gateway",
"options": {
"baseURL": "https://<your-gateway>/v1",
"apiKey": "sk-...",
"dynamic": true
}
}
}
}
Discovered metadata is mapped to opencode's model fields when present:
/v1/models field |
opencode field |
|---|---|
metadata.display_name |
name |
metadata.capabilities.reasoning |
reasoning |
metadata.capabilities.function_calling |
tool_call |
metadata.capabilities.vision |
attachment |
metadata.context_window / metadata.max_output_tokens |
limit.context / limit.output |
metadata.pricing.*_per_mtok |
cost.input / cost.output / cost.cache_read / cost.cache_write |
Model IDs are used verbatim, so a gateway that advertises a virtual model (e.g. deepseek-flash) alongside the concrete provider model it targets (deepseek/deepseek-flash) lists both. When both share a display name, the concrete entry is named by its full ID so the two stay distinguishable in the picker; the virtual model keeps its display name. Missing metadata fields are omitted so opencode's defaults apply (e.g. tool_call defaults to true).
Models whose mode is set to anything other than chat or completion are skipped, so LiteLLM-style gateways that also expose embeddings or rerankers don't list them in the picker. Gateways that don't send mode are unaffected.
Tests
npm test
Uses Node's built-in test runner, no dependencies. Gateway /v1/models responses are stored as fixtures in test/fixtures/.