Initial commit: OpenCode Zen Proxy

Proxy translating OpenAI/Anthropic Messages API to OpenCode Zen's free models.
Pure stdlib Go 1.22, no external dependencies.

- OpenAI Chat Completions (/v1/chat/completions) with streaming
- Anthropic Messages API (/v1/messages) with streaming
- Model aliases for 5 free models
- Auth middleware support
- Session rotation (30min per user key)
This commit is contained in:
renato97
2026-06-25 11:49:11 -03:00
commit 0a4289cafc
11 changed files with 2396 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
# OpenCode Zen Proxy
Proxy translating OpenAI and Anthropic Messages API requests to OpenCode Zen's free model API.
## Build & Run
```bash
go build -o opencode-proxy .
./opencode-proxy # no auth
./opencode-proxy -api-key "secret" # with auth
```
Auth reads `OPENCODE_PROXY_KEY` env var; `-api-key` flag takes precedence.
Defaults: `127.0.0.1:6446`. Use `-host 0.0.0.0` for external access.
## Architecture
```
Client → internal/server/ (HTTP, auth, routing) → internal/proxy/ (model resolve, session mgmt, forwarding, Anthropic↔OpenAI conversion) → opencode.ai/zen/v1
```
- **Pure stdlib Go 1.22** — no router, no deps beyond stdlib
- **Entrypoint:** `main.go` wires `proxy.NewProxy(key)` into `server.NewServer(p)`
- **Auth middleware** in `server.go:88` checks `Authorization: Bearer` or `x-api-key` header
- **Model aliases** in `internal/proxy/models.go:59` — short names, company names, and Claude model names → full IDs
- **Session rotation:** session IDs rotate every 30 min per user key (`proxy.SessionID()` at `models.go:154`)
- **Pass-through:** unknown model names are forwarded as-is, not rejected
## Endpoints
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/health` | Health check (returns hardcoded `"1.0.0"`) |
| `GET` | `/v1/models` | List available free models (auth required if key set) |
| `GET` | `/v1/models/{id}` | Single model detail |
| `POST` | `/v1/chat/completions` | OpenAI Chat Completions (streaming + non-streaming) |
| `POST` | `/v1/messages` | Anthropic Messages API |
| `POST` | `/v1/v1/messages` | Same as above — workaround for Claude Code double-path bug |
## Anthropic Messages API
Fully implemented in `internal/proxy/anthropic.go`. Converts Anthropic → OpenAI format upstream, then converts responses back to Anthropic SSE (streaming) or JSON (non-streaming).
**Claude Code quirk:** Claude Code appends `/v1/messages` to `ANTHROPIC_BASE_URL` automatically. Set `ANTHROPIC_BASE_URL` to `http://127.0.0.1:6446` (without `/v1` suffix) to avoid double-path. The proxy also handles `/v1/v1/messages` as a fallback (`server.go:62`).
**Claude model name aliases** are defined in `models.go:77-88` — Claude Code validates model names client-side, so you must set `ANTHROPIC_MODEL` to a valid Claude name like `claude-sonnet-4-6` (maps to `deepseek-v4-flash-free`).
## Available Models (from `models.go`)
| Model ID | Aliases | Context | Max Output |
|----------|---------|---------|------------|
| `deepseek-v4-flash-free` | `deepseek`, `deepseek-v4`, `ds` | 1M | 384K |
| `big-pickle` | `pickle` | 200K | 32K |
| `mimo-v2.5-free` | `mimo`, `mimo-v2.5`, `xiaomi` | 1M | 32K |
| `north-mini-code-free` | `north`, `north-mini`, `cohere` | 256K | 64K |
| `nemotron-3-ultra-free` | `nemotron`, `nemotron-3`, `nvidia` | 1M | 16K |
**Source of truth is `models.go`, not `README.md`.** The README may list models that differ from the code (e.g. `minimax-m2.5-free`, `kimi-k2.5-free`, `gpt-5-nano`, `nemotron-3-super-free`, `qwen3.6-plus-free` are NOT in the code). The code controls what works.
## Quirks & Gotchas
- **No tests exist.** Any change is untested unless you add them.
- **No CI/CD, no Makefile, no lint config.** All manual.
- **In-memory only.** Session state dies on restart. No database.
- **`deploy/start-proxy.bat`** and **`deploy/CREDENCIALES.md`** contain a hardcoded API key — do not commit them.
- **`opencode-proxy.exe~`** in root is a backup artifact; ignore.
- Upstream Zen API expects `x-opencode-request`, `x-opencode-session`, `x-opencode-client`, `x-opencode-project` headers (set in `proxy.go:107`).
- **Server logs request bodies to stdout** (truncated to 500 chars in `server.go:265-271`). Verbose by design.
- **Health endpoint** returns hardcoded version `"1.0.0"` in `server.go:126`. Update both `main.go:12` and `server.go:126` when bumping.
- **Upstream timeout:** `proxy.go:167` sets 10-minute HTTP client timeout — adjust if Zen models are slow on first call.
- **Unknown models are forwarded as-is** (`models.go:140`) — useful when new models appear upstream before code is updated.
- **Reasoning models** (DeepSeek, Nemotron) need `max_tokens` ≥ 500 — first tokens go to reasoning, not visible content.