OpenCode authentication and model-provider plugin for CLIProxyAPI.
Connect OpenCode to a running CLIProxyAPI instance (local or remote), authenticate with an optional API key, dynamically load models from /v1/models, and enrich metadata from CLIProxyAPI models.json and models.dev.
/connect cliproxy— interactive setup (base URL + optional API key)- Provider
cliproxy— auto-registered with full model list - Dynamic models — fetched from CLIProxyAPI
/v1/modelswith TTL cache - models.json enrichment — defaults to CLIProxyAPI registry URL; override with local path or custom URL
- models.dev enrichment — fills missing metadata (graceful fallback)
- Auth-aware base URL —
/connect cliproxybase URL used whenopencode.jsonomitsbaseURL - Thinking / reasoning — maps CLIProxyAPI
thinking.levelsto OpenCode variants (low,medium,high,xhigh,max, …) - Optional API key — works when CLIProxyAPI runs without
api-keys - Fallback models — sensible defaults when the server is unreachable
- Safe logging — API keys, bearer tokens, and
sk-*values are redacted from logs - Concurrent fetch dedup — parallel model requests share a single in-flight fetch
- OpenCode ≥ 1.14.49 recommended (provider hook for dynamic models)
- Node.js ≥ 22.14 (npm trusted publishing); CI uses Node 24
- Running CLIProxyAPI (default port
8317)
Published: opencode-cliproxiapi-auth
npm install opencode-cliproxiapi-authAdd to opencode.json:
{
"plugin": ["opencode-cliproxiapi-auth"]
}git clone https://github.com/GRomR1/opencode-cliproxiapi-auth.git
cd opencode-cliproxiapi-auth
npm install
npm run buildIn your project's opencode.json:
{
"plugin": ["file:///absolute/path/to/opencode-cliproxiapi-auth/dist/index.js"]
}Or from inside the plugin repo (relative path):
{
"plugin": ["file://./dist/index.js"]
}Default endpoint: http://localhost:8317/v1
If api-keys is set in CLIProxyAPI config.yaml, use one of those keys. If omitted, no key is required.
/connect cliproxy
Prompts:
| Field | Default | Notes |
|---|---|---|
| Base URL | http://localhost:8317/v1 |
Include /v1 suffix |
| API key | (empty) | Optional if CLIProxyAPI has no api-keys |
Credentials are stored in ~/.local/share/opencode/auth.json.
opencode models cliproxyModels appear as cliproxy/<model-id>, e.g. cliproxy/claude-sonnet-4-6.
opencode run -m cliproxy/gpt-5.4-mini "Hello"Optional settings in opencode.json:
{
"plugin": ["opencode-cliproxiapi-auth"],
"provider": {
"cliproxy": {
"options": {
"baseURL": "http://localhost:8317/v1",
"apiKey": "your-key-from-config.yaml",
"modelCacheTtl": 300000,
"refreshOnList": true,
"modelsDev": { "enabled": true }
}
}
}
}/v1/models often returns minimal metadata. By default, the plugin loads CLIProxyAPI's registry from GitHub (DEFAULT_MODELS_JSON_URL). No config is required for thinking levels and token limits in most setups.
Override with a local clone or custom URL:
{
"provider": {
"cliproxy": {
"options": {
"modelsJsonPath": "/path/to/CLIProxyAPI/internal/registry/models/models.json"
}
}
}
}Set "modelsJsonPath": "" to disable registry enrichment entirely.
Enrichment mapping:
CLIProxyAPI models.json |
OpenCode field |
|---|---|
context_length / inputTokenLimit |
contextWindow |
max_completion_tokens / outputTokenLimit |
maxTokens |
thinking.levels |
reasoning variants |
display_name |
model name |
description |
model description |
| Option | Type | Default | Description |
|---|---|---|---|
baseURL |
string | http://localhost:8317/v1 |
CLIProxyAPI base URL; falls back to /connect cliproxy stored URL |
apiKey |
string | — | Key from config.yaml api-keys (optional) |
modelCacheTtl |
number | 300000 |
Model cache TTL (ms) |
refreshOnList |
boolean | true |
Refresh models when provider options reload |
modelsJsonPath |
string | CLIProxyAPI GitHub models.json |
Local path or URL; "" disables enrichment |
modelsDev.enabled |
boolean | true |
Enrich from models.dev |
modelsDev.url |
string | https://models.dev/api.json |
models.dev API URL |
modelsDev.cacheTtl |
number | 86400000 |
models.dev cache TTL (ms) |
modelsDev.timeoutMs |
number | 5000 |
models.dev fetch timeout (ms) |
modelsDev.providerAliases |
object | — | Map owned_by → models.dev provider |
Effective base URL is chosen in order:
provider.cliproxy.options.baseURLinopencode.jsonbaseURLstored by/connect cliproxyin~/.local/share/opencode/auth.json- Default
http://localhost:8317/v1
API key priority: options.apiKey → auth key → CLIPROXY_API_KEY env → none (no Authorization header).
| Variable | Description |
|---|---|
CLIPROXY_API_KEY |
API key fallback (config/auth take precedence) |
CLIPROXY_DEBUG=1 |
Enable debug logging to OpenCode log files |
CLIPROXY_BASE_URL |
Integration tests only — live CLIProxyAPI base URL |
CLIPROXY_TEST_MODEL |
Integration tests only — override model for chat-completion test |
For scripts and manual cache control:
import {
fetchModels,
clearModelCache,
refreshModels,
CLIPROXY_PROVIDER_ID,
CLIPROXY_ENDPOINTS,
} from 'opencode-cliproxiapi-auth/runtime';
const config = {
baseUrl: 'http://localhost:8317/v1',
apiKey: 'optional-key',
// modelsJsonPath omitted → uses CLIProxyAPI GitHub registry by default
};
const models = await fetchModels(config);
await refreshModels(config);
clearModelCache(config);Exported helpers also include loadModelsJson, parseModelsJson, thinkingToVariants, normalizeRegistryModel, and models.dev utilities.
index.ts → CliproxyAuthPlugin (OpenCode entry)
runtime.ts → programmatic exports
src/
plugin.ts → config / provider / auth hooks
model-fetcher.ts→ /v1/models + cache + orchestration
models-json.ts → CLIProxyAPI models.json loader
normalizer.ts → field normalization + thinking variants
models-dev.ts → models.dev enrichment
cache.ts → TTL cache primitive
logger.ts → sanitized file logging
constants.ts → defaults and endpoints
types.ts → TypeScript interfaces
Inspired by opencode-omniroute-auth, simplified for CLIProxyAPI (no combo models, no Gemini schema sanitization).
npm install
npm run build # compile TypeScript → dist/
npm test # unit tests (mocked)
npm run check:exportsPublishing uses npm trusted publishing (OIDC from GitHub Actions). No NPM_TOKEN secret is required.
Workflow: .github/workflows/publish.yml (triggers: tag v*, GitHub Release, manual dispatch).
- Log in to npmjs.com as gromr1.
- Open package settings for
opencode-cliproxiapi-auth(after first publish) or account publishing settings before the first release. - Section Trusted publishing → GitHub Actions.
- Configure exactly (case-sensitive):
| Field | Value |
|---|---|
| Organization or user | GRomR1 (exact GitHub casing — npm is case-sensitive) |
| Repository | opencode-cliproxiapi-auth |
| Workflow filename | publish.yml |
| Allowed actions | npm publish |
- Save. npm does not validate until the first publish attempt — double-check spelling.
package.json → repository.url must use the same casing as GitHub (git+https://github.com/GRomR1/opencode-cliproxiapi-auth.git).
Optional hardening after verified publish: package Settings → Publishing access → Require 2FA and disallow tokens, then revoke old automation tokens.
# 1. Bump version in package.json (must be higher than npm latest)
# 2. Commit, push, tag (tag must match version: v1.0.2 ↔ 1.0.2)
git tag v1.0.2
git push origin v1.0.2Or: Actions → Publish to npm → Run workflow (branch main).
CI uses actions/checkout@v6, actions/setup-node@v6, Node 24. The workflow runs npm test, checks the version is not already on npm, then npm publish via OIDC. Provenance is added automatically for public repos.
Do not re-publish an existing version — npm rejects duplicate versions; bump package.json first.
npm pack
tar -tf opencode-cliproxiapi-auth-*.tgzCredentials go in .env (never commit — see .env.example):
cp .env.example .env
# CLIPROXY_BASE_URL=https://your-cpa-host/v1
# CLIPROXY_API_KEY= # optional if CLIProxyAPI has no api-keys
# CLIPROXY_TEST_MODEL=gpt-5.4-mini # optional chat test modelnpm run test:integrationRequires CLIPROXY_BASE_URL. CLIPROXY_API_KEY is optional. The chat-completion test uses CLIPROXY_TEST_MODEL when set, otherwise prefers gpt-5.4-mini, then the first model from /v1/models.
Integration suite verifies:
- Live
/v1/modelsfetch and enrichment - Reasoning metadata from
models.json opencode models cliproxy(plugin registration)- Direct
/v1/chat/completionsagainst CLIProxyAPI
CLIPROXY_DEBUG=1 opencode --print-logs models cliproxyLogs: ~/.local/share/opencode/log/ (lines tagged service=cliproxy).
- Ensure plugin is in
opencode.jsonpluginarray. - Run
npm run buildif usingfile://./dist/index.js. - Do not use
opencode --pure(skips external plugins). - For plugin development from source: use
file://path todist/index.js.
curl -H "Authorization: Bearer YOUR_KEY" https://your-host/v1/models- Match
apiKeywith CLIProxyAPIconfig.yamlapi-keys. - Or leave key empty if CLIProxyAPI has no auth.
Registry enrichment is on by default (CLIProxyAPI GitHub models.json). If you disabled it with "modelsJsonPath": "", remove that or point to a local clone / custom URL.
import { clearModelCache } from 'opencode-cliproxiapi-auth/runtime';
clearModelCache();Or keep refreshOnList: true (default).
You may have a manual cliapiproxy provider in global opencode.json. This plugin registers cliproxy — use cliproxy/<model-id>.
- CLIProxyAPI
- OpenCode
- opencode-omniroute-auth — reference plugin
MIT