A reverse proxy that converts Command Code API to OpenAI / Anthropic compatible endpoints. TypeScript, zero runtime dependencies.
Built by analyzing official CLI v0.32.3 network traffic to accurately replicate the Command Code API request protocol.
Features:
- OpenAI Chat Completions + Anthropic Messages API
- Streaming & non-streaming
- Tool calling (tool_use)
- Multimodal image input
- Reasoning effort
- Dynamic model list
- Cache hit metrics
- Client disconnect detection with upstream abort
- Zero-output → 502 auto-retry
- Consecutive timeout → 429 auto-retry
- Privacy-aware logging
bun start # Start (default http://0.0.0.0:3050)
bun run dev # Watch mode (auto-reload on file changes)API Key is passed via the Authorization request header — no need to store it in config files. Key must start with user_ (automatically matched with any prefix, e.g. Bearer token_user_xxx):
curl http://127.0.0.1:3050/v1/chat/completions \
-H "Authorization: Bearer user_xxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek/deepseek-v4-flash","messages":[{"role":"user","content":"hi"}]}'commandcode/
├── config.json # Port / log path etc.
├── LICENSE # MIT License
├── package.json # bun start / bun run dev
├── tsconfig.json # TypeScript configuration
├── Dockerfile # Container build (oven/bun:1)
├── docker-compose.yml # Container orchestration
├── .dockerignore # Build context exclusions
├── README.md # This document
└── src/
├── server.ts # HTTP server entry point
├── config.ts # Config loader (config.json + env vars)
├── types.ts # Shared TypeScript types
├── models.ts # Model list (hardcoded + dynamic fetch)
├── logger.ts # Logging (console + file)
├── helpers.ts # Utilities (traceparent, slug, fingerprint)
├── http-util.ts # Body parsing, JSON responses, auth
├── upstream.ts # CC API forwarding + error mapping
├── runtime-state.ts # Session, version, fingerprint, timeouts
├── openai.ts # OpenAI protocol conversion
├── anthropic.ts # Anthropic protocol conversion
└── routes/
├── chat.ts # POST /v1/chat/completions
├── messages.ts # POST /v1/messages
└── meta.ts # GET /v1/models, GET /health
| Field | Default | Description |
|---|---|---|
port |
3050 |
Listen port |
host |
0.0.0.0 |
Listen address |
apiBase |
https://api.commandcode.ai |
CC API base URL |
projectSlug |
cc-proxy |
x-project-slug header |
logFile |
"" |
Log file path (empty = console only) |
logLevel |
info |
Log level |
useProviderModels |
true |
Dynamically fetch model list from Provider API |
modelRefreshIntervalMs |
300000 |
Model list cache refresh interval (5 min) |
| Variable | Overrides |
|---|---|
PORT |
port |
HOST |
host |
CC_API_BASE |
apiBase |
PROJECT_SLUG |
projectSlug |
LOG_FILE |
logFile |
CC_USE_PROVIDER_MODELS |
useProviderModels |
OpenAI Chat Completions compatible. Supports streaming, non-streaming, tool calling, multimodal image input, and reasoning effort.
Request parameters:
| Parameter | Required | Description |
|---|---|---|
model |
Yes | Model ID (see model list) |
messages |
Yes | Conversation messages, supports system/user/assistant/tool roles |
max_tokens |
No | Max tokens to generate (default 64000) |
stream |
No | SSE streaming (default false) |
temperature |
No | Sampling temperature (0-2) |
reasoning_effort |
No | Reasoning intensity: low/medium/high/max |
tools |
No | Tool definitions (OpenAI function calling format) |
tool_choice |
No | Tool selection strategy |
parallel_tool_calls |
No | Allow parallel tool calls |
Simple request:
{
"model": "deepseek/deepseek-v4-flash",
"messages": [{ "role": "user", "content": "hello" }],
"stream": true
}Multimodal image input (vision model required):
{
"model": "xiaomi/mimo-v2.5",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Describe this image" },
{ "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,..." } }
]
}
]
}Tool calling:
{
"model": "deepseek/deepseek-v4-flash",
"messages": [...],
"tools": [{
"type": "function",
"function": { "name": "get_weather", "description": "...", "parameters": {...} }
}],
"tool_choice": "auto"
}Streaming response (SSE):
data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"thinking..."}}]}
data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Hello"}}]}
data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":10,"completion_tokens":20,"total_tokens":30,"prompt_tokens_details":{"cached_tokens":8}}}
data: [DONE]Non-streaming response (with cache hits):
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"created": 1234567890,
"model": "deepseek/deepseek-v4-flash",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello!",
"reasoning_content": "The user said hello, I should respond."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 7558,
"completion_tokens": 42,
"total_tokens": 7600,
"prompt_tokens_details": { "cached_tokens": 7552 }
}
}Anthropic Messages API compatible endpoint. Supports streaming, non-streaming, and tool calling.
Request body:
{
"model": "claude-sonnet-4-6",
"max_tokens": 1000,
"system": "You are a helpful assistant.",
"messages": [{ "role": "user", "content": "hello" }],
"stream": true
}Anthropic protocol conversion (automatic):
| Concept | Anthropic Format | Conversion |
|---|---|---|
| System prompt | Top-level system field |
Auto-converted to OpenAI system message |
| Message content | content array (text/tool_use/tool_result) |
Auto-mapped to corresponding roles |
| Tool results | tool_result blocks in user messages |
Auto-converted to role: "tool" |
| Tool definitions | input_schema |
Auto-mapped to parameters |
tool_choice |
{type:"auto"/"any"/"tool"} |
any→required, tool→function object |
| Reasoning | thinking.budget_tokens |
Auto-mapped to reasoning_effort (≥10000→high, ≥5000→medium, ≥2000→low) |
| Stop reason | end_turn/max_tokens/tool_use |
Auto-mapped to stop/length/tool_calls |
| Token usage | input_tokens/output_tokens + cache |
Passed through, cache fields mapped to Anthropic format |
Streaming response (SSE, Anthropic format):
event: message_start
data: {"type":"message_start","message":{"id":"msg_xxx","type":"message","role":"assistant","content":[],"model":"...","usage":{"input_tokens":0,"output_tokens":0}}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":10,"cache_read_input_tokens":0,"input_tokens":100}}
event: message_stop
data: {"type":"message_stop"}Non-streaming response:
{
"id": "msg_xxx",
"type": "message",
"role": "assistant",
"model": "deepseek/deepseek-v4-flash",
"content": [{ "type": "text", "text": "Hello!" }],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 7558,
"output_tokens": 42,
"cache_read_input_tokens": 7552,
"cache_creation_input_tokens": null
}
}Returns available model list. Fetched dynamically from Provider API (5 min cache), falls back to hardcoded list on failure.
Health check. Returns OK.
| HTTP Status | Description |
|---|---|
| 400 | Invalid request format |
| 401 | API Key missing / invalid format / rejected (Key must start with user_) |
| 429 | Stream idle timeout (30s streaming / 90s non-streaming, SDK auto-retry, consecutive 3: reduce context hint) |
| 502 | Zero output tokens or CC upstream error |
| 503 | Service temporarily unavailable |
The proxy returns a live model list via GET /v1/models. Below are common models for reference; the actual list depends on the live API response — see Command Code Pricing for plan details.
| Model ID | Name | Features |
|---|---|---|
deepseek/deepseek-v4-flash |
DeepSeek V4 Flash | Fast, general-purpose |
deepseek/deepseek-v4-pro |
DeepSeek V4 Pro | High-precision reasoning |
claude-sonnet-4-6 |
Claude Sonnet 4.6 | Long context |
claude-opus-4-8 |
Claude Opus 4.8 | Best reasoning |
claude-opus-4-7 |
Claude Opus 4.7 | Reasoning |
claude-haiku-4-5-20251001 |
Claude Haiku 4.5 | Fast, lightweight |
gpt-5.5 |
GPT-5.5 | OpenAI flagship |
gpt-5.4 |
GPT-5.4 | General-purpose |
gpt-5.4-mini |
GPT-5.4 Mini | Fast, cost-efficient |
gpt-5.3-codex |
GPT-5.3 Codex | Code generation |
moonshotai/Kimi-K2.6 |
Kimi K2.6 | Multimodal / frontend |
moonshotai/Kimi-K2.5 |
Kimi K2.5 | Multimodal / frontend |
zai-org/GLM-5.1 |
GLM 5.1 | Large context |
zai-org/GLM-5 |
GLM 5 | General-purpose |
MiniMaxAI/MiniMax-M3 |
MiniMax M3 | Multimodal |
MiniMaxAI/MiniMax-M2.7 |
MiniMax M2.7 | General-purpose |
MiniMaxAI/MiniMax-M2.5 |
MiniMax M2.5 | Fast, lightweight |
Qwen/Qwen3.7-Max |
Qwen 3.7 Max | Large parameters |
Qwen/Qwen3.6-Max-Preview |
Qwen 3.6 Max Preview | Preview |
Qwen/Qwen3.6-Plus |
Qwen 3.6 Plus | Balanced |
stepfun/Step-3.7-Flash |
Step 3.7 Flash | Fast reasoning |
stepfun/Step-3.5-Flash |
Step 3.5 Flash | Fast, lightweight |
xiaomi/mimo-v2.5-pro |
MiMo V2.5 Pro | Image input supported |
xiaomi/mimo-v2.5 |
MiMo V2.5 | Image input supported |
google/gemini-3.5-flash |
Gemini 3.5 Flash | Reasoning model |
google/gemini-3.1-flash-lite |
Gemini 3.1 Flash Lite | Fast, lightweight |
⚠️ Not all models support image input. Use vision-capable models (e.g.xiaomi/mimo-v2.5,Kimi-K2.6,MiniMax-M3) for multimodal requests.
from openai import OpenAI
client = OpenAI(
api_key="user_xxxxxxxxx",
base_url="http://127.0.0.1:3050/v1",
)
response = client.chat.completions.create(
model="deepseek/deepseek-v4-flash",
messages=[{"role": "user", "content": "hello"}],
stream=True,
)
for chunk in response:
print(chunk.choices[0].delta.content or "", end="")curl http://127.0.0.1:3050/v1/chat/completions \
-H "Authorization: Bearer user_xxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek/deepseek-v4-flash",
"messages": [{"role": "user", "content": "hello"}],
"stream": true
}'Add a Custom Provider in Cursor settings:
- API Base URL:
http://127.0.0.1:3050/v1 - API Key:
user_xxxxxxxxx - Model: Choose from the model list
import anthropic
client = anthropic.Anthropic(
api_key="user_xxxxxxxxx",
base_url="http://127.0.0.1:3050",
)
message = client.messages.create(
model="deepseek/deepseek-v4-flash",
max_tokens=1000,
system="You are helpful.",
messages=[{"role": "user", "content": "hello"}],
)
print(message.content[0].text){
"provider": "openai-compatible",
"baseUrl": "http://127.0.0.1:3050/v1",
"apiKey": "user_xxxxxxxxx"
}Based on analysis of official CLI v0.32.3 traffic:
| Mechanism | Implementation |
|---|---|
| Per-Key Session | One session per API key, 12h expiry + 1h random jitter |
| Session ID | x-session-id header, per-key with automatic rotation |
| Version | x-command-code-version auto-fetched from npm registry (24h refresh) |
| CLI Envelope | config/memory/taste/permissionMode/params/threadId |
| OpenTelemetry | traceparent (W3C Trace Context) |
| Environment | x-cli-environment: production |
| Project Slug | Fake x-project-slug derived from session ID (matches real CLI slug format) |
| Device Fingerprint | Generated CPU/memory/timezone/MAC fingerprint per key (mimics real CLI) |
| Reasoning Effort | reasoning_effort pass-through (low/medium/high/max) |
| Key Validation | Regex user_[a-zA-Z0-9_-]+, auto-cleans extra paths/prefixes, rejects sk-xxx format |
| Stream Timeout | 30s streaming / 90s non-streaming → 429 with SDK auto-retry |
| Consecutive Timeout | 3 consecutive timeouts before "reduce context" hint |
| Zero-Output Guard | outputTokens=0 → 502 error (SDK auto-retry, anti false billing) |
| Upstream Abort | AbortController on client disconnect + all error paths |
| Privacy Logging | No API key fragments, no error bodies, no stack traces in logs |
{
"config": {
"workingDir": "C:\\project",
"date": "2026-06-07",
"environment": "win32-x64, Node.js v24.16.0",
"structure": [],
"isGitRepo": false,
"currentBranch": "",
"mainBranch": "",
"gitStatus": "",
"recentCommits": []
},
"memory": "",
"taste": "",
"skills": "",
"permissionMode": "standard",
"params": {
"model": "deepseek/deepseek-v4-flash",
"messages": [...],
"max_tokens": 64000,
"stream": true,
"reasoning_effort": "max"
},
"threadId": "<uuid>"
}The CLI sends images in this format:
{
"role": "user",
"content": [
{ "type": "image", "image": "data:image/jpeg;base64,..." },
{ "type": "text", "text": "What does this image say?" }
]
}The proxy receives OpenAI image_url format and converts it to the above CC format transparently.
docker compose up -dThe proxy will listen on http://0.0.0.0:3050. Set PROXY_PORT to customize the host port:
PROXY_PORT=13050 docker compose up -ddocker build -t commandcode-proxy:latest .
docker run -d -p 3050:3050 -e PORT=3050 commandcode-proxy:latestbun run docker:build:multi| Variable | Default | Description |
|---|---|---|
PORT |
3050 |
Container listen port |
PROXY_PORT |
3050 |
Host port (compose only) |
This project is for educational and research purposes only.
- Unofficial: This project is not affiliated with Command Code in any way.
- Personal Use: Users assume all responsibility. Please comply with the Command Code Terms of Service.
- API Key: This project does not collect, upload, or leak your API Key. The key must be sent in every request via the
Authorization: Bearer <key>header and is never stored in configuration. - Compliance: The protocol is based on passive observation of local CLI network traffic. No unauthorized access, cracking, or tampering of the server has been performed.
- Account Risk: Keep usage frequency consistent with normal CLI usage. Extremely high concurrent calls may trigger risk controls.
bun run dev # Watch mode (auto-reload on file changes)
bun run typecheck # Type-check without emitting
bun run lint # ESLint
bun run format # Prettier