opencode-cliproxiapi-auth
v1.0.1
Published
OpenCode authentication plugin for CLIProxyAPI with /connect command and dynamic model fetching
Maintainers
Readme
opencode-cliproxiapi-auth
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.
Features
/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
Requirements
- OpenCode ≥ 1.14.49 recommended (provider hook for dynamic models)
- Node.js ≥ 20
- Running CLIProxyAPI (default port
8317)
Installation
From npm (after publish)
npm install opencode-cliproxiapi-authAdd to opencode.json:
{
"plugin": ["opencode-cliproxiapi-auth"]
}Local development (before publish)
git clone <repo-url>
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"]
}Quick Start
1. Start CLIProxyAPI
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.
2. Connect in OpenCode
/connect cliproxyPrompts:
| 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.
3. Verify models
opencode models cliproxyModels appear as cliproxy/<model-id>, e.g. cliproxy/claude-sonnet-4-6.
4. Use a model
opencode run -m cliproxy/gpt-5.4-mini "Hello"Configuration
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 }
}
}
}
}models.json enrichment
/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 |
Options reference
| 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 |
baseURL resolution
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).
Environment variables
| 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 |
Runtime API
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.
Architecture
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 interfacesInspired by opencode-omniroute-auth, simplified for CLIProxyAPI (no combo models, no Gemini schema sanitization).
Development
npm install
npm run build # compile TypeScript → dist/
npm test # unit tests (mocked)
npm run check:exportsPublishing to npm
Publishing 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).
One-time setup (npm trusted publisher)
- 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.
Release flow
# 1. Bump version in package.json (e.g. 1.0.1)
# 2. Commit, push, tag
git tag v1.0.0
git push origin v1.0.0Or: Actions → Publish to npm → Run workflow (branch main).
The workflow runs npm test, then npm publish via OIDC. Provenance is added automatically for public repos. Requires npm CLI ≥ 11.5.1 (Node 24 in CI).
Local dry run (no upload)
npm pack
tar -tf opencode-cliproxiapi-auth-1.0.0.tgzIntegration tests (live CLIProxyAPI)
Credentials 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
Debug
CLIPROXY_DEBUG=1 opencode --print-logs models cliproxyLogs: ~/.local/share/opencode/log/ (lines tagged service=cliproxy).
Troubleshooting
Provider not found: cliproxy
- Ensure plugin is in
opencode.jsonpluginarray. - Run
npm run buildif usingfile://./dist/index.js. - Do not use
opencode --pure(skips external plugins). - Unpublished package: use
file://path, not npm package name.
No models / connection failed
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.
Models lack context limits or thinking levels
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.
Stale model list
import { clearModelCache } from 'opencode-cliproxiapi-auth/runtime';
clearModelCache();Or keep refreshOnList: true (default).
OpenCode suggests cliapiproxy instead of cliproxy
You may have a manual cliapiproxy provider in global opencode.json. This plugin registers cliproxy — use cliproxy/<model-id>.
Related
- CLIProxyAPI
- OpenCode
- opencode-omniroute-auth — reference plugin
License
MIT
