English | 简体中文
A native dynamic Go plugin for CLIProxyAPI that exposes a CommandCode Go/GOAT/Pro/Max plan as a single provider (commandcode; published model ids carry the commandcode/ prefix).
The plugin owns model discovery, protocol translation, execution, key scheduling, and a quota page for the CommandCode account surface, so one API-key pool serves OpenAI, Anthropic, and Responses clients through CLIProxyAPI.
CommandCode Go/GOAT/Pro/Max is reached through its OpenAI-compatible provider surface:
| Upstream route | Status |
|---|---|
POST {base-url}/chat/completions |
Serves the OSS models (deepseek/*, z-ai/*, Qwen/*, MiniMaxAI/*, moonshotai/*, …) |
GET {base-url}/models |
Model catalog |
POST {base-url}/messages |
Claude models only — OSS ids are rejected with "Model … is not supported on this endpoint" |
POST {base-url}/responses |
Not a registered route |
The default upstream base URL is https://api.commandcode.ai/provider/v1.
Because there is exactly one usable route for the catalog, every model discovered from /models is routed to chat-completions by default, whatever its family or vendor prefix. route-overrides exist for upstreams that really do serve another protocol on another endpoint.
Without this plugin, using a CommandCode Go/GOAT/Pro/Max plan in CLIProxyAPI requires hand-written provider blocks per protocol family, and the pieces that CLI clients depend on are missing or wrong:
- Duplicated configuration & keys: the same keys must be configured in several provider blocks.
- Fragmented scheduling: rotation, rate limits, and cooldowns cannot be shared, and the Management Center cannot show per-key usage.
- Client protocol burden: clients must know which upstream endpoint a model requires.
- Lost reasoning: CommandCode returns thinking text under
reasoning/reasoning_details[].text(never the standardreasoning_content), so a plain OpenAI-compatible provider silently drops every chain-of-thought for Anthropic and Responses clients. - Rejected reasoning effort: CommandCode's
/modelscarries no thinking metadata, so a provider that validatesreasoning_effortlocally rejectsxhigh/maxeven though the upstream accepts them.
- Unified auth pool: the plugin registers each configured key as a CLIProxyAPI auth record, so the host's scheduler handles rotation, retries, error cooldowns, session affinity, and per-key statistics.
- Transparent translation & routing: clients ask for
commandcode/<upstream-id>and never need to know the upstream protocol. - Reasoning fidelity: upstream thinking text is preserved for all three client formats, and
reasoning_effortis forwarded verbatim when the model declares no capability. - One model catalog: everything published by
{base-url}/modelsis discoverable under thecommandcode/prefix in/v1/models.
- Single Provider Namespace: models appear as
commandcode/deepseek/deepseek-v4.1-flash,commandcode/z-ai/glm-5.3-flash, … (prefix configurable, can be disabled for bare upstream ids). - Multi-Protocol Client Translation: OpenAI Chat Completions, Anthropic Messages, and OpenAI Responses requests all become upstream chat-completions calls, and responses are converted back — including streaming.
- Reasoning Preservation: upstream
reasoning,reasoning_details[].text, andreasoning_contentare normalized across targets:- Claude clients get a leading
thinkingblock withthinking_deltaevents; - Responses clients get a leading
reasoningoutput item withresponse.reasoning_summary_*events; - OpenAI clients get
reasoning_contentbackfilled onto each chunk while the vendor fields stay intact.
- Claude clients get a leading
- Capability-aware Reasoning Controls: an explicit
route-overridesdeclaration (or catalog thinking metadata) re-enables eager validation and clamping; with nothing declared, the client'sreasoning_effortis forwarded verbatim (auto/noneomit the field) and the upstream is the authority. - Dynamic Catalog Discovery: remote catalog with local fallback, deduplication, and diagnostics for what was excluded.
- CommandCode Quota Page: a Management Center page listing every configured credential with its account email, plan, remaining plan credits, and the rolling 5-hour/weekly windows (used, cap, reset time), refreshed per card on demand.
- Multi-Key Auth Scheduling: one key pool shared across all protocols through CLIProxyAPI's native scheduler.
- CLIProxyAPI:
v7.2.138+ - Go Toolchain: Go 1.26+ (CGO enabled for
-buildmode=c-shared)
# Linux (AMD64)
go build -buildmode=c-shared -o plugins/linux/amd64/commandcode-go-cliproxyapi.so .
# macOS (ARM64)
go build -buildmode=c-shared -o plugins/darwin/arm64/commandcode-go-cliproxyapi.dylib .
# Windows (AMD64)
go build -buildmode=c-shared -o plugins/windows/amd64/commandcode-go-cliproxyapi.dll .Place the artifact into the host's plugin directory (e.g. <cliproxyapi_root>/plugins/<os>/<arch>/).
plugins:
enabled: true
dir: /data/plugins
configs:
commandcode-go-cliproxyapi:
# Upstream base URL (default: "https://api.commandcode.ai/provider/v1")
base-url: "https://api.commandcode.ai/provider/v1"
# Optional catalog override (default: "{base-url}/models")
# catalog-url: "https://api.commandcode.ai/provider/v1/models"
# Client-facing model id prefix
model-prefix:
enabled: true # true -> "commandcode/<model>" (default: true)
value: "commandcode"
# CommandCode API keys (at least one required); supports ${ENV_VAR}
api-keys:
- value: "user_xxx"
- value: "${COMMANDCODE_API_KEY}"
# Catalog discovery
catalog:
refresh-interval: "15m" # minimum "1m"
stale-while-unavailable: true
# Protocol switches: disabling one removes every model routed to it
protocols:
chat-completions: true
messages: true
responses: true
# Per-model route pins; only needed when the upstream serves another
# endpoint (CommandCode itself serves OSS models on chat-completions)
route-overrides:
"claude-sonnet-5":
protocol: "messages" # chat-completions | messages | responses
endpoint: "/v1/messages" # required, must start with "/"
request-timeout: "5m"
max-response-bytes: 67108864 # 64 MiB
allow-http: false # http:// base-url for local testing| Option | Type | Default | Description |
|---|---|---|---|
api-keys |
[]object |
(Required) | List of API keys (- value: "..."). Supports ${ENV_VAR} expansion. Duplicates and empty values are rejected. |
base-url |
string |
https://api.commandcode.ai/provider/v1 |
Upstream provider base URL. Valid HTTPS (or HTTP with allow-http: true), no query, fragment, or userinfo. |
catalog-url |
string |
{base-url}/models |
Catalog discovery URL. |
model-prefix.enabled |
bool |
true |
Client-facing ids use <prefix>/<model>; false publishes bare upstream ids. |
model-prefix.value |
string |
commandcode |
Provider prefix. |
catalog.refresh-interval |
duration |
15m |
Catalog polling cadence (minimum 1m). |
catalog.stale-while-unavailable |
bool |
true |
Keep serving the last good snapshot when a refresh fails. |
protocols.* |
bool |
true |
Route kill switches. A disabled protocol excludes its models with a diagnostic. |
route-overrides |
map |
{} |
{ model: { protocol, endpoint } } pins a model onto another upstream route. endpoint is required. |
request-timeout |
duration |
5m |
Upstream HTTP timeout (also bounds account/quota calls to 30s). |
max-response-bytes |
int64 |
67108864 |
Maximum non-streaming response body size. |
allow-http |
bool |
false |
Permit http:// upstreams for local testing. |
The CommandCode Quota page (Management Center → plugins) reads the account surface on the same authority as base-url:
| Endpoint | Purpose |
|---|---|
GET {authority}/alpha/billing/credits |
Remaining plan credits and the 5-hour/weekly windows |
GET {authority}/alpha/billing/subscriptions |
Plan id/status |
GET {authority}/alpha/whoami?limits=1 |
Account email for the card label |
{authority} is derived from base-url by trimming its provider path (/provider/v1). Each card is refreshed manually and independently; the page never polls, and quota values never influence routing.
CommandCode publishes no capability API: {base-url}/models returns only id, object, created, owned_by, name and context_length, and its /alpha/* account surface has no models or capabilities route. The per-model effort lists that exist live inside the vendor's own clients (the command-code CLI and the web app both ship a static table), and the upstream gateway itself accepts every value in the union low | medium | high | xhigh | max regardless of the per-model list.
Consequences for this plugin:
- with nothing declared in the catalog,
reasoning_effortis forwarded verbatim (auto/noneomit the field) soxhighandmaxwork; - if a catalog entry ever carries a
thinkingobject (levels,min,max,zero_allowed,dynamic_allowed), that declaration takes over and eager validation plus budget clamping are re-enabled; - the vendor's per-model table, the probes behind these statements and the evidence for the missing capability API are recorded in
docs/model-capabilities.md.
go test ./... # unit + mocked end-to-end tests
go test ./... -cover # with coverage
go vet ./... # vettingThis plugin was built with reference to two prior CommandCode plugins:
- opencode-go-cliproxyapi (v0.1.7) is the direct ancestor: the adapter kernel, catalog, config, auth, and quota scaffolding come from there, and the CommandCode-specific behaviour — reasoning preservation, unattributed-capability effort passthrough, the chat-completions-only route default, and the account API used by the quota page — was adapted on top.
- cpa-plugin-commandcode (ahoo) is an independent earlier CommandCode plugin whose write-up established the vendor behaviours this plugin had to reproduce: thinking text arriving under
reasoning/reasoning_details[].textand neverreasoning_content, the resulting need to backfillreasoning_contentbefore CLIProxyAPI's openai→claude translator sees it, the SSEdata:prefix that streaming/v1/messagesrequires, and the fact that only fully-qualified vendor names (deepseek/deepseek-v4.1-flash) are accepted upstream.
Repository: https://github.com/mczhoucn/commandcode-go-cliproxyapi