opencode2api-proxy 0.0.0-stage → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +621 -0
- package/README.md +650 -2
- package/config.example.json +61 -0
- package/dist/cli.js +10895 -0
- package/dist/cli.js.map +7 -0
- package/package.json +50 -4
- package/worker/worker.js +7030 -0
package/README.md
CHANGED
|
@@ -1,3 +1,651 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src="assets/images/OpenCode2API_Banner.webp" alt="OpenCode2API Banner" width="100%">
|
|
4
|
+
|
|
5
|
+
# OpenCode2API-Proxy
|
|
6
|
+
|
|
7
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
8
|
+
[](https://www.npmjs.com/package/opencode2api-proxy)
|
|
9
|
+
[](https://github.com/IMROVOID/OpenCode2API/actions/workflows/test.yml)
|
|
10
|
+
[](https://nodejs.org/)
|
|
11
|
+
[](https://www.typescriptlang.org/)
|
|
12
|
+
[](https://workers.cloudflare.com/)
|
|
13
|
+
[](https://opencode.ai/)
|
|
14
|
+
[](https://platform.openai.com/docs/api-reference)
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
OpenCode Zen and Go to a universal OpenAI, Responses and Anthropic compatible API router. Serve <b>Zen</b> and <b>Go</b> models from one endpoint with <b>protocol conversion, streaming, key rotation and a built-in control WebUI</b>.
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
[Overview](#what-is-opencode2api-proxy) • [Features](#key-features) • [Quick Start](#quick-start) • [Models](#model-catalog--aliases) • [Clients](#client-integration) • [Architecture](#architecture--structure) • [Guides](#advanced-guides) • [Disclaimer](#disclaimer--legal-notice) • [References](#references--acknowledgements) • [License](#license)
|
|
21
|
+
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
## Table of Contents
|
|
25
|
+
|
|
26
|
+
- [What is OpenCode2API-Proxy?](#what-is-opencode2api-proxy)
|
|
27
|
+
- [Key Features](#key-features)
|
|
28
|
+
- [Quick Start](#quick-start)
|
|
29
|
+
- [Method 1: Local Daemon](#method-1-local-daemon)
|
|
30
|
+
- [Method 2: Built Release](#method-2-built-release)
|
|
31
|
+
- [Method 3: Cloudflare Workers (Serverless)](#method-3-cloudflare-workers-serverless)
|
|
32
|
+
- [Method 4: npm](#method-4-npm)
|
|
33
|
+
- [Model Catalog & Aliases](#model-catalog--aliases)
|
|
34
|
+
- [Client Integration](#client-integration)
|
|
35
|
+
- [Architecture & Structure](#architecture--structure)
|
|
36
|
+
- [Advanced Guides](#advanced-guides)
|
|
37
|
+
- [Protocol Conversion](#protocol-conversion)
|
|
38
|
+
- [Key Rotation & Failure Handling](#key-rotation--failure-handling)
|
|
39
|
+
- [Unlimited Lane & Self-Healing Guard](#unlimited-lane--self-healing-guard)
|
|
40
|
+
- [Environment Variables](#environment-variables)
|
|
41
|
+
- [Development & Testing](#development--testing)
|
|
42
|
+
- [Continuous Integration (CI)](#continuous-integration-ci)
|
|
43
|
+
- [Building the Release Archives](#building-the-release-archives)
|
|
44
|
+
- [Disclaimer & Legal Notice](#disclaimer--legal-notice)
|
|
45
|
+
- [References & Acknowledgements](#references--acknowledgements)
|
|
46
|
+
- [License](#license)
|
|
47
|
+
|
|
48
|
+
## What is OpenCode2API-Proxy?
|
|
49
|
+
|
|
50
|
+
[OpenCode](https://opencode.ai/) serves its **Zen** and **Go** model families behind two different wire shapes. The Zen lane expects the OpenCode 1.x request format, the Go lane the OpenCode 2.x CLI format, and neither is directly consumable by the OpenAI-compatible clients developers actually use.
|
|
51
|
+
|
|
52
|
+
**OpenCode2API-Proxy** bridges that gap:
|
|
53
|
+
|
|
54
|
+
1. It discovers the models OpenCode publishes, per tier, and learns each model's **native protocol** from OpenCode's own capability catalog.
|
|
55
|
+
2. It serves a single endpoint that speaks **OpenAI Chat Completions**, **OpenAI Responses** and **Anthropic Messages** at the same time, converting a request from the client's protocol to the upstream's and converting the answer back.
|
|
56
|
+
3. It rotates across several upstream keys, cools down the ones that fail, and falls back from the anonymous lane to the authenticated tiers.
|
|
57
|
+
4. It ships a **control WebUI** with a greyscale dark theme on every deployment method, so the same dashboard works locally, from an archive and on Cloudflare Workers.
|
|
58
|
+
|
|
59
|
+
> **Note**: The Go lane requires an active OpenCode Go subscription and a workspace in the Global region. The free tier is served from the official OpenCode client only, so it needs that client's TLS fingerprint. See [Disclaimer & Legal Notice](#disclaimer--legal-notice).
|
|
60
|
+
|
|
61
|
+
> **Reference**: Inspired by and referencing the original [jasonxu114514/opencode2api](https://github.com/jasonxu114514/opencode2api) repository.
|
|
62
|
+
|
|
63
|
+
## Key Features
|
|
64
|
+
|
|
65
|
+
- **Three Protocols, One Endpoint**: `/v1/chat/completions`, `/v1/responses` and `/v1/messages` all accept the same request body and answer in the protocol the client asked for.
|
|
66
|
+
- **Four Deployment Methods**:
|
|
67
|
+
- **Local Daemon**: Runs on `127.0.0.1:8080` with a control WebUI on `127.0.0.1:8081`.
|
|
68
|
+
- **Built Release**: Cross-platform ZIP archives for Linux, Windows and macOS on x64 and arm64, each with an example configuration.
|
|
69
|
+
- **Cloudflare Worker**: A standalone bundle with no Node built-ins, full control WebUI, and self-healing rotation.
|
|
70
|
+
- **npm**: `npm install -g opencode2api-proxy`.
|
|
71
|
+
- **Model Catalog Discovery**: The Zen and Go listings refresh on a timer, and OpenCode's capability catalog teaches the router which protocol each model actually speaks.
|
|
72
|
+
- **Protocol-Native Routing**: A request is sent in the upstream's own protocol, so no conversion happens on the hot path unless the client asked for a different one.
|
|
73
|
+
- **Key Rotation & Cooldown**: A 401, 403 or 429 rotates to the next key of the tier; the failing key is cooled down with an exponential backoff, and a non-retryable 4xx leaves the tier immediately.
|
|
74
|
+
- **Dual-Lane Self-Healing & Quota Bypass**:
|
|
75
|
+
- **OpenCode Unlimited Lane**: Discovers live Cloudflare `AAAA` edge pools via DoH (`1.1.1.1/dns-query`), steers Worker subrequests with `cf.resolveOverride`, rotates device/session/project identities on `QuotaExceeded` / `429` walls (up to 8 hops), and blacklists burned `/64` prefixes.
|
|
76
|
+
- **Kilo Free Gateway Failover**: Seamless secondary fallback to high-throughput free models without quotas.
|
|
77
|
+
- **Full SSE Streaming**: Reasoning deltas, tool call arguments and the usage report survive the trip, and a non-streaming client gets the same answer collapsed back into one document.
|
|
78
|
+
- **Control WebUI**: A greyscale dark dashboard with the overview, the model routing table, the API keys, the quota, a Playground, the rotation groups, the configuration, the account and a live log stream.
|
|
79
|
+
- **Safe & Resource-Bounded**:
|
|
80
|
+
- 32 MB request body limit and 64 MB response body limit.
|
|
81
|
+
- Client disconnect cancellation through `AbortSignal`.
|
|
82
|
+
- An upstream timeout that ends a stalled request instead of hanging it.
|
|
83
|
+
- Secrets masked out of every log line and every WebUI view.
|
|
84
|
+
|
|
85
|
+
## Quick Start
|
|
86
|
+
|
|
87
|
+
### Method 1: Local Daemon
|
|
88
|
+
|
|
89
|
+
#### Step 1: Get an API Key
|
|
90
|
+
|
|
91
|
+
Create a key in the [OpenCode Console](https://opencode.ai/) and copy it. You need a key for the **Zen** tier, the **Go** tier, or both.
|
|
92
|
+
|
|
93
|
+
#### Step 2: Install
|
|
94
|
+
|
|
95
|
+
Install globally via npm:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm install -g opencode2api-proxy
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Or run directly with npx without installation:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npx opencode2api-proxy start
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Or when running from a local clone:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm install
|
|
111
|
+
npm run build
|
|
112
|
+
node dist/cli.js start
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### Step 3: Configure
|
|
116
|
+
|
|
117
|
+
Copy the example configuration and edit it:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
cp config.example.json config.json
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"listen": "127.0.0.1:8080",
|
|
126
|
+
"server_keys": ["sk-your-local-key"],
|
|
127
|
+
"zen_keys": ["sk-your-zen-key"],
|
|
128
|
+
"go_keys": ["sk-your-go-key"],
|
|
129
|
+
"webui": {
|
|
130
|
+
"enabled": true,
|
|
131
|
+
"listen": "127.0.0.1:8081",
|
|
132
|
+
"username": "admin",
|
|
133
|
+
"password": "change-this-admin-password"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`server_keys` are the keys your clients authenticate with. `zen_keys` and `go_keys` are the keys OpenCode issued you.
|
|
139
|
+
|
|
140
|
+
#### Step 4: Start the Router
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
opencode2api-proxy start
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Or specify a custom port and key:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
opencode2api-proxy start --port 8080 --key sk-my-secret-key
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Your local endpoint is available at `http://127.0.0.1:8080/v1`, and the control WebUI at `http://127.0.0.1:8081`.
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
curl http://127.0.0.1:8080/v1/chat/completions \
|
|
156
|
+
-H "Authorization: Bearer sk-your-local-key" \
|
|
157
|
+
-H "Content-Type: application/json" \
|
|
158
|
+
-d '{"model":"deepseek-v4.1-flash","messages":[{"role":"user","content":"Hello"}]}'
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Method 2: Built Release
|
|
162
|
+
|
|
163
|
+
Prebuilt archives for every supported platform are attached to each release.
|
|
164
|
+
|
|
165
|
+
#### Step 1: Download
|
|
166
|
+
|
|
167
|
+
Grab the archive that matches your platform from the [releases page](https://github.com/IMROVOID/OpenCode2API/releases):
|
|
168
|
+
|
|
169
|
+
| Target | File |
|
|
170
|
+
|---|---|
|
|
171
|
+
| Linux x64 | `opencode2api-proxy-v1.0.0-linux-x64.zip` |
|
|
172
|
+
| Linux arm64 | `opencode2api-proxy-v1.0.0-linux-arm64.zip` |
|
|
173
|
+
| Windows x64 | `opencode2api-proxy-v1.0.0-win-x64.zip` |
|
|
174
|
+
| Windows arm64 | `opencode2api-proxy-v1.0.0-win-arm64.zip` |
|
|
175
|
+
| macOS (Intel) | `opencode2api-proxy-v1.0.0-macos-x64.zip` |
|
|
176
|
+
| macOS (Apple Silicon) | `opencode2api-proxy-v1.0.0-macos-arm64.zip` |
|
|
177
|
+
|
|
178
|
+
#### Step 2: Extract and configure
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
unzip opencode2api-proxy-v1.0.0-linux-x64.zip -d opencode2api-proxy
|
|
182
|
+
cd opencode2api-proxy
|
|
183
|
+
mv config.example.json config.json
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Each archive ships a **separate example configuration** named `config.example.json`, so the proxy never overwrites a configuration you already wrote. Rename it to `config.json` and fill in your keys.
|
|
187
|
+
|
|
188
|
+
#### Step 3: Run
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
node opencode2api-proxy.js
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
On Windows:
|
|
195
|
+
|
|
196
|
+
```powershell
|
|
197
|
+
node opencode2api-proxy.js
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The archive runs on any system with **Node.js 22 or newer**. Install it from [nodejs.org](https://nodejs.org/) if you do not have it.
|
|
201
|
+
|
|
202
|
+
### Method 3: Cloudflare Workers (Serverless)
|
|
203
|
+
|
|
204
|
+
Deploy a 24/7 serverless gateway without keeping your computer running.
|
|
205
|
+
|
|
206
|
+
#### Step 1: Copy the Code
|
|
207
|
+
|
|
208
|
+
Open [`worker/worker.js`](./worker/worker.js) and copy the entire file contents.
|
|
209
|
+
|
|
210
|
+
#### Step 2: Create Worker in Cloudflare Dashboard
|
|
211
|
+
|
|
212
|
+
1. Log into the [Cloudflare Dashboard](https://dash.cloudflare.com).
|
|
213
|
+
2. Go to **Workers & Pages** -> **Create application** -> **Create Worker**.
|
|
214
|
+
3. Set name to `opencode2api-proxy` and click **Deploy**.
|
|
215
|
+
4. Click **Edit code**, select all existing code, delete it, and paste the copied contents of `worker/worker.js`.
|
|
216
|
+
5. Click **Deploy** in the top right.
|
|
217
|
+
|
|
218
|
+
#### Step 3: Add Variables & Secrets
|
|
219
|
+
|
|
220
|
+
In your Worker, go to **Settings** -> **Variables and Secrets** and configure as needed:
|
|
221
|
+
|
|
222
|
+
| Variable | Type | Default | Description |
|
|
223
|
+
|---|---|---|---|
|
|
224
|
+
| `ADMIN_USERNAME` | Text / Secret | `admin` | Username for the control WebUI |
|
|
225
|
+
| `ADMIN_PASSWORD` / `WEBUI_PASSWORD` | Secret | `admin` | Password for the control WebUI (change from default in production!) |
|
|
226
|
+
| `WEBUI_PATH` / `ADMIN_PATH` | Text | `/` | Web path where the control panel is served (e.g. `/admin`). When customized, `/` returns API health JSON. |
|
|
227
|
+
| `WEBUI_ENABLED` | Text | `true` | Set to `false` to completely disable the control WebUI |
|
|
228
|
+
| `API_KEY` | Secret | *(None)* | Client bearer authorization key (e.g. `sk-local-key`). Required if clients must authenticate. |
|
|
229
|
+
| `OPENCODE_ZEN_KEYS` | Secret | *(None)* | Your OpenCode Zen key(s). Multiple keys separated by commas. |
|
|
230
|
+
| `OPENCODE_GO_KEYS` | Secret | *(None)* | Your OpenCode Go key(s). Multiple keys separated by commas. |
|
|
231
|
+
| `KILO_API_KEY` | Secret | *(None)* | Kilo Free Gateway key for zero-quota fallback |
|
|
232
|
+
| `KILO_API_BASE` | Text | `https://api.kilo.ai/v1` | Kilo API base URL |
|
|
233
|
+
| `UPSTREAM_ZEN` | Text | `https://opencode.ai/zen` | Zen upstream base URL |
|
|
234
|
+
| `UPSTREAM_GO` | Text | `https://opencode.ai/zen/go` | Go upstream base URL |
|
|
235
|
+
| `UPSTREAM_ANONYMOUS` | Text | `https://opencode.ai/inference` | Anonymous free upstream base URL |
|
|
236
|
+
|
|
237
|
+
Click **Save and deploy**.
|
|
238
|
+
|
|
239
|
+
#### Worker Control Panel
|
|
240
|
+
|
|
241
|
+
The built-in control panel provides a live dashboard for monitoring requests, model routing, active pools, quotas, and testing completions in a Playground.
|
|
242
|
+
|
|
243
|
+
- **Access URL**: By default, navigate to `https://opencode2api-proxy.<your-subdomain>.workers.dev/` (or custom path like `/admin` if `WEBUI_PATH` is set).
|
|
244
|
+
- **Default Username**: `admin`
|
|
245
|
+
- **Default Password**: `admin`
|
|
246
|
+
- **Session Auth**: Stores a secure HTTP-only cookie (`opencode2api_session`).
|
|
247
|
+
- **Custom Path Routing**: When `WEBUI_PATH="/admin"` is configured:
|
|
248
|
+
- `GET /` serves `{ "name": "OpenCode2API", "status": "ok", "admin": "/admin" }`
|
|
249
|
+
- `GET /admin` serves the full interactive dashboard
|
|
250
|
+
- All API routes (`/v1/chat/completions`, etc.) continue serving client requests cleanly.
|
|
251
|
+
|
|
252
|
+
Your Cloudflare Worker API URL:
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
https://opencode2api-proxy.<your-subdomain>.workers.dev/v1
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Method 4: npm
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
npm install -g opencode2api-proxy
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
opencode2api-proxy start --config ./config.json
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
The package ships the CLI bundle, the Cloudflare Worker bundle and the example configuration. `npm run build:release` produces the same cross-platform archives the release workflow uploads.
|
|
269
|
+
|
|
270
|
+
## Model Catalog & Aliases
|
|
271
|
+
|
|
272
|
+
The router discovers the models OpenCode publishes on each tier and learns each model's native protocol from OpenCode's capability catalog. You always request a **model id**, and the router picks the lane and the protocol.
|
|
273
|
+
|
|
274
|
+
| Requested model | Lane | Notes |
|
|
275
|
+
|---|---|---|
|
|
276
|
+
| `deepseek-v4.1-flash` | Zen or Go | Served by the tier that advertises it. |
|
|
277
|
+
| `glm-5.3` | Zen or Go | Part of the SOTA rotation group. |
|
|
278
|
+
| `grok-4.6` | Zen or Go | Part of the SOTA rotation group. |
|
|
279
|
+
| `kimi-k3` | Zen or Go | Part of the SOTA rotation group. |
|
|
280
|
+
| `qwen3.8-max` | Zen or Go | Part of the SOTA rotation group. |
|
|
281
|
+
| `claude-haiku-5-5` | Zen or Go | Native protocol is Anthropic Messages. |
|
|
282
|
+
| `*free` | Anonymous | Served on the anonymous lane when it is enabled. |
|
|
283
|
+
|
|
284
|
+
The **SOTA rotation group** holds exactly `deepseek-v4.1-flash`, `glm-5.3`, `grok-4.6`, `kimi-k3` and `qwen3.8-max`. The **sweet** group holds the models you add yourself. A rotation alias rotates across the models of its group, so a request for `sota` picks the group member that is currently healthy.
|
|
285
|
+
|
|
286
|
+
`GET /v1/models` lists everything the router can serve, with each model's native protocol, its context window and its supported endpoints:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
curl http://127.0.0.1:8080/v1/models -H "Authorization: Bearer sk-your-local-key"
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Client Integration
|
|
293
|
+
|
|
294
|
+
<details>
|
|
295
|
+
<summary><b>Claude Code</b></summary>
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
export ANTHROPIC_BASE_URL="http://127.0.0.1:8080"
|
|
299
|
+
export ANTHROPIC_AUTH_TOKEN="sk-your-local-key"
|
|
300
|
+
export ANTHROPIC_MODEL="deepseek-v4.1-flash"
|
|
301
|
+
claude
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
</details>
|
|
305
|
+
|
|
306
|
+
<details>
|
|
307
|
+
<summary><b>Codex CLI</b></summary>
|
|
308
|
+
|
|
309
|
+
```toml
|
|
310
|
+
# ~/.codex/config.toml
|
|
311
|
+
model = "deepseek-v4.1-flash"
|
|
312
|
+
model_provider = "opencode2api"
|
|
313
|
+
|
|
314
|
+
[model_providers.opencode2api]
|
|
315
|
+
name = "OpenCode2API"
|
|
316
|
+
base_url = "http://127.0.0.1:8080/v1"
|
|
317
|
+
env_key = "OPENCODE2API_KEY"
|
|
318
|
+
wire_api = "responses"
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
</details>
|
|
322
|
+
|
|
323
|
+
<details>
|
|
324
|
+
<summary><b>OpenCode</b></summary>
|
|
325
|
+
|
|
326
|
+
```json
|
|
327
|
+
{
|
|
328
|
+
"$schema": "https://opencode.ai/config.json",
|
|
329
|
+
"provider": {
|
|
330
|
+
"opencode2api": {
|
|
331
|
+
"npm": "@ai-sdk/openai-compatible",
|
|
332
|
+
"options": {
|
|
333
|
+
"baseURL": "http://127.0.0.1:8080/v1",
|
|
334
|
+
"apiKey": "sk-your-local-key"
|
|
335
|
+
},
|
|
336
|
+
"models": {
|
|
337
|
+
"deepseek-v4.1-flash": { "name": "DeepSeek v4.1 Flash" }
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
"model": "opencode2api/deepseek-v4.1-flash"
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
</details>
|
|
346
|
+
|
|
347
|
+
<details>
|
|
348
|
+
<summary><b>Cline / Roo Code / Continue</b></summary>
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"apiProvider": "openai",
|
|
353
|
+
"openaiApiBase": "http://127.0.0.1:8080/v1",
|
|
354
|
+
"openaiApiKey": "sk-your-local-key",
|
|
355
|
+
"openaiModelId": "deepseek-v4.1-flash"
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
</details>
|
|
360
|
+
|
|
361
|
+
<details>
|
|
362
|
+
<summary><b>Cherry Studio / LobeChat / NextChat</b></summary>
|
|
363
|
+
|
|
364
|
+
Add a custom OpenAI provider:
|
|
365
|
+
|
|
366
|
+
| Field | Value |
|
|
367
|
+
|---|---|
|
|
368
|
+
| API address | `http://127.0.0.1:8080/v1` |
|
|
369
|
+
| API key | `sk-your-local-key` |
|
|
370
|
+
| Model | `deepseek-v4.1-flash` |
|
|
371
|
+
|
|
372
|
+
</details>
|
|
373
|
+
|
|
374
|
+
<details>
|
|
375
|
+
<summary><b>Any OpenAI-compatible client</b></summary>
|
|
376
|
+
|
|
377
|
+
Point the client at `http://127.0.0.1:8080/v1` and use `sk-your-local-key` as the API key. Every OpenAI-compatible client works, because the router speaks the client's protocol.
|
|
378
|
+
|
|
379
|
+
</details>
|
|
380
|
+
|
|
381
|
+
## Architecture & Structure
|
|
382
|
+
|
|
383
|
+
```
|
|
384
|
+
OpenCode2API-Proxy/
|
|
385
|
+
├── src/
|
|
386
|
+
│ ├── types/
|
|
387
|
+
│ │ ├── config.ts # Configuration, tier, protocol and log-level contracts
|
|
388
|
+
│ │ └── inference.ts # The protocol-neutral bridge model
|
|
389
|
+
│ ├── config/
|
|
390
|
+
│ │ ├── defaults.ts # Default configuration and the SOTA model list
|
|
391
|
+
│ │ ├── schema.ts # The strict configuration schema
|
|
392
|
+
│ │ ├── normalize.ts # The single validation point
|
|
393
|
+
│ │ ├── secrets.ts # Masking, fingerprinting and redaction
|
|
394
|
+
│ │ └── store.ts # Atomic configuration persistence
|
|
395
|
+
│ ├── protocol/
|
|
396
|
+
│ │ ├── identity.ts # Session, message and project id derivation
|
|
397
|
+
│ │ ├── sha256.ts # Portable SHA-256 for the Worker bundle
|
|
398
|
+
│ │ ├── decode-request.ts # Client body -> bridge model
|
|
399
|
+
│ │ ├── encode-request.ts # Bridge model -> upstream body
|
|
400
|
+
│ │ ├── response.ts # Upstream body -> client body
|
|
401
|
+
│ │ ├── stream-parser.ts # Upstream SSE -> bridge events
|
|
402
|
+
│ │ ├── stream-encoder.ts # Bridge events -> client SSE
|
|
403
|
+
│ │ ├── collapse.ts # SSE stream -> one response document
|
|
404
|
+
│ │ ├── rotation-stream.ts # Rotation with a commit point
|
|
405
|
+
│ │ ├── tools.ts # The v1 read gate and the v2 core tool gate
|
|
406
|
+
│ │ └── lanes.ts # The v1 and v2 upstream lanes
|
|
407
|
+
│ ├── proxy/
|
|
408
|
+
│ │ ├── model-catalog.ts # The discovered models and the routing rules
|
|
409
|
+
│ │ ├── model-discovery.ts # The listings and the capability catalog
|
|
410
|
+
│ │ ├── catalog-refresh.ts # The refresh timer
|
|
411
|
+
│ │ ├── attempt-runner.ts # The key rotation and the cooldown
|
|
412
|
+
│ │ ├── request-shaping.ts # The per-lane request shaping
|
|
413
|
+
│ │ └── delivery.ts # The response relay and the conversion
|
|
414
|
+
│ ├── auth/
|
|
415
|
+
│ │ └── key-pool.ts # The key pool with its cooldown
|
|
416
|
+
│ ├── unlimited/
|
|
417
|
+
│ │ ├── lane.ts # DoH AAAA edge pool, /64 normalization & identity rotation
|
|
418
|
+
│ │ ├── guard.ts # Quota-wall detector, /64 blacklist & self-healing hop loop
|
|
419
|
+
│ │ ├── doctor.ts # Cloudflare egress verifier & health diagnostics
|
|
420
|
+
│ │ └── index.ts # Public unlimited module exports
|
|
421
|
+
│ ├── server/
|
|
422
|
+
│ │ ├── dispatcher.ts # Route matching, CORS and client authentication
|
|
423
|
+
│ │ ├── gateway.ts # The runtime that owns the pools and the catalog
|
|
424
|
+
│ │ ├── routes.ts # Public API routes (/v1/*, /v1/unlimited/*, /healthz)
|
|
425
|
+
│ │ ├── completion-handler.ts # The core completion flow
|
|
426
|
+
│ │ └── admin/ # The control WebUI and the admin API
|
|
427
|
+
│ ├── telemetry/
|
|
428
|
+
│ │ ├── metrics.ts # The request counters, the latency and the usage
|
|
429
|
+
│ │ ├── log-broker.ts # The log ring and the live subscribers
|
|
430
|
+
│ │ └── logger.ts # The structured logger
|
|
431
|
+
│ ├── webui/
|
|
432
|
+
│ │ ├── document.ts # The single-file control WebUI
|
|
433
|
+
│ │ ├── theme.ts # The greyscale dark tokens
|
|
434
|
+
│ │ └── styles.ts # The stylesheet
|
|
435
|
+
│ ├── cli/
|
|
436
|
+
│ │ ├── index.ts # The CLI entrypoint
|
|
437
|
+
│ │ └── commands/start.ts # The daemon command
|
|
438
|
+
│ └── worker/
|
|
439
|
+
│ ├── index.ts # The Cloudflare Workers fetch entrypoint
|
|
440
|
+
│ └── unlimited.ts # Worker self-healing guard, hop & doctor handlers
|
|
441
|
+
├── worker/
|
|
442
|
+
│ └── worker.js # Standalone Cloudflare Worker bundle (95.5 KB)
|
|
443
|
+
├── dist/
|
|
444
|
+
│ └── cli.js # Standalone Node.js CLI executable bundle (309.7 KB)
|
|
445
|
+
├── tests/ # 145 automated unit & integration tests
|
|
446
|
+
├── scripts/
|
|
447
|
+
│ ├── build.ts # The esbuild build pipeline
|
|
448
|
+
│ ├── build-release.ts # The cross-platform archive builder
|
|
449
|
+
│ └── zip.ts # The dependency-free ZIP writer
|
|
450
|
+
├── .github/
|
|
451
|
+
│ └── workflows/
|
|
452
|
+
│ ├── test.yml # The CI workflow with skip flags
|
|
453
|
+
│ └── release.yml # The manual multi-OS draft release workflow
|
|
454
|
+
├── config.example.json
|
|
455
|
+
└── package.json
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Advanced Guides
|
|
459
|
+
|
|
460
|
+
### Protocol Conversion
|
|
461
|
+
|
|
462
|
+
The router sends a request in the **upstream's own protocol** and only converts when the client asked for a different one.
|
|
463
|
+
|
|
464
|
+
| Client endpoint | Upstream lane | What happens |
|
|
465
|
+
|---|---|---|
|
|
466
|
+
| `/v1/chat/completions` | v1 (Zen) | The body is sent as Chat Completions, with a `read` tool guaranteed. |
|
|
467
|
+
| `/v1/chat/completions` | v2 (Go) | The body is sent as Chat Completions, with the five core tools guaranteed. |
|
|
468
|
+
| `/v1/responses` | any | The body is sent as Responses, and the answer is converted back. |
|
|
469
|
+
| `/v1/messages` | any | The body is sent as Anthropic Messages, and the answer is converted back. |
|
|
470
|
+
|
|
471
|
+
A request to a different protocol than the upstream's is decoded into the bridge model, re-encoded for the upstream, and the answer is converted back on the way out. The same protocol on both sides is passed through untouched, so the common case costs nothing.
|
|
472
|
+
|
|
473
|
+
**The v1 read gate.** The v1 free tier requires a tool named `read`. A client tool that reads files (`read_file`, `ReadFile`, `read-text-file` and similar) is renamed to `read` on the wire, and the original name is restored in the answer.
|
|
474
|
+
|
|
475
|
+
**The v2 core tool gate.** The v2 free tier requires the five core tools `shell`, `edit`, `glob`, `grep` and `read`, and compares their description and parameter schema against the official CLI. The genuine definitions ship with the router, so a client that declares its own tools keeps them and the missing core tools are added.
|
|
476
|
+
|
|
477
|
+
### Key Rotation & Failure Handling
|
|
478
|
+
|
|
479
|
+
Put several keys in `zen_keys` or `go_keys` and the router distributes the requests across them.
|
|
480
|
+
|
|
481
|
+
1. **Round-robin**: Each request starts on a different key.
|
|
482
|
+
2. **Failure detection**: A 401, 403 or 429 rotates to the next key of the tier.
|
|
483
|
+
3. **Cooldown**: The failing key is held back with an exponential backoff, up to eight times the base cooldown. A longer `Retry-After` is honored in full.
|
|
484
|
+
4. **Immediate exit**: Any other 4xx is deterministic, so the router leaves the tier at once instead of burning the other keys.
|
|
485
|
+
5. **Tier fallback**: When the first tier is exhausted, the next tier in the prefer order takes over.
|
|
486
|
+
6. **Anonymous first**: A free model starts on the anonymous lane when `anonymous` is enabled, and falls back to the authenticated tiers when the anonymous lane rejects it.
|
|
487
|
+
|
|
488
|
+
The rotation groups add one more layer: an alias like `sota` rotates across the models of its group, so a request keeps working when one model is rate-limited.
|
|
489
|
+
|
|
490
|
+
### Unlimited Lane & Dual-Lane Architecture
|
|
491
|
+
|
|
492
|
+
OpenCode enforces strict rate limits and device quotas on free-tier inference (`[429]: Free usage limit reached`). OpenCode2API provides a dual-lane architecture to maintain high availability:
|
|
493
|
+
|
|
494
|
+
```text
|
|
495
|
+
Incoming Client Request
|
|
496
|
+
│
|
|
497
|
+
▼
|
|
498
|
+
┌──────────────┐
|
|
499
|
+
│ Quota Check │
|
|
500
|
+
└──────┬───────┘
|
|
501
|
+
│
|
|
502
|
+
├────────────────────────────────────────────────┐
|
|
503
|
+
▼ (Lane A) ▼ (Lane B)
|
|
504
|
+
┌───────────────────────────────┐ ┌─────────────────────────────────┐
|
|
505
|
+
│ OpenCode v2.2.5 Direct Lane │ │ Kilo Free Gateway Failover │
|
|
506
|
+
├───────────────────────────────┤ ├─────────────────────────────────┤
|
|
507
|
+
│ • Live DoH Cloudflare AAAA │ │ • Zero-quota free tier models │
|
|
508
|
+
│ • cf.resolveOverride routing │ ──(Quota ──► │ • Automatic secondary fallback │
|
|
509
|
+
│ • /64 block blacklist (200) │ Wall) │ • KILO_API_KEY / KILO_API_BASE │
|
|
510
|
+
│ • Clean device ID generation │ └─────────────────────────────────┘
|
|
511
|
+
│ • Auto-hop (up to 8 attempts) │
|
|
512
|
+
└───────────────────────────────┘
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
1. **OpenCode v2.2.5 Direct Lane (`zen-lane` & `zen-guard`)**:
|
|
516
|
+
- **Live DoH Edge Pool**: Resolves live `AAAA` records for `opencode.ai` via Cloudflare DNS-over-HTTPS (`1.1.1.1/dns-query`), falling back to verified Cloudflare IPv6 edge literals (`2606:4700:10::ac42:ad95`, `2606:4700:10::ac42:ad96`, `2606:4700:10::6814:2011`). On Cloudflare Workers, subrequests are steered directly via `cf.resolveOverride`.
|
|
517
|
+
- **Self-Healing Auto-Hop**: When an upstream request hits a `429`, `403 FreeTierError`, or body containing `QuotaExceeded` / `Free usage limit reached`, the guard normalizes the edge to its `/64` prefix, blacklists it, generates a fresh device identity (`ses_`, `msg_`, 40-hex `project`, 32-hex `machineId`), and retries with a new edge (up to `8` hops transparently).
|
|
518
|
+
- **Oldest-Block Recycling**: Blacklists up to 200 `/64` blocks. If all edges are blacklisted, it recycles the oldest block to prevent starvation.
|
|
519
|
+
2. **Kilo Free Gateway Failover**:
|
|
520
|
+
- If direct OpenCode lanes are saturated or repeatedly blocked, configure `KILO_API_KEY` to enable instant fallback to high-throughput free models without quota limits.
|
|
521
|
+
3. **Diagnostic & Hop Endpoints**:
|
|
522
|
+
- `GET /v1/unlimited/doctor` — Inspects outbound IPv6 `/64` egress, active edge and session identity, hop count, and blacklist telemetry.
|
|
523
|
+
- `POST /v1/unlimited/hop` — Manually rotates to a fresh `/64` edge and identity immediately.
|
|
524
|
+
|
|
525
|
+
```bash
|
|
526
|
+
# Check unlimited lane & guard health
|
|
527
|
+
curl http://127.0.0.1:8080/v1/unlimited/doctor \
|
|
528
|
+
-H "Authorization: Bearer sk-your-local-key"
|
|
529
|
+
|
|
530
|
+
# Manually hop to a fresh edge and device/session identity
|
|
531
|
+
curl -X POST http://127.0.0.1:8080/v1/unlimited/hop \
|
|
532
|
+
-H "Authorization: Bearer sk-your-local-key"
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### Environment Variables
|
|
536
|
+
|
|
537
|
+
| Variable | Mode | Default | Description |
|
|
538
|
+
|---|---|---|---|
|
|
539
|
+
| `OPENCODE2API_CONFIG` | Local | `./config.json` | Path to local configuration file |
|
|
540
|
+
| `API_KEY` | Worker | *(None)* | Client authorization bearer token |
|
|
541
|
+
| `ADMIN_USERNAME` | Worker | `admin` | Control panel username |
|
|
542
|
+
| `ADMIN_PASSWORD` / `WEBUI_PASSWORD` | Worker | `admin` | Control panel password |
|
|
543
|
+
| `WEBUI_PATH` / `ADMIN_PATH` | Worker | `/` | Web path where the control panel is served (e.g. `/admin`) |
|
|
544
|
+
| `WEBUI_ENABLED` | Worker | `true` | Set to `false` to disable the control WebUI entirely |
|
|
545
|
+
| `OPENCODE_ZEN_KEYS` | Worker | *(None)* | Comma-separated OpenCode Zen keys |
|
|
546
|
+
| `OPENCODE_GO_KEYS` | Worker | *(None)* | Comma-separated OpenCode Go keys |
|
|
547
|
+
| `KILO_API_KEY` | Worker | *(None)* | Kilo Free Gateway API key for quota failover |
|
|
548
|
+
| `KILO_API_BASE` | Worker | `https://api.kilo.ai/v1` | Kilo Free Gateway base URL |
|
|
549
|
+
| `UPSTREAM_ZEN` | Worker | `https://opencode.ai/zen` | Zen upstream base URL |
|
|
550
|
+
| `UPSTREAM_GO` | Worker | `https://opencode.ai/zen/go` | Go upstream base URL |
|
|
551
|
+
| `UPSTREAM_ANONYMOUS` | Worker | `https://opencode.ai/inference` | Anonymous free upstream base URL |
|
|
552
|
+
|
|
553
|
+
## Development & Testing
|
|
554
|
+
|
|
555
|
+
<!-- AUTO-GENERATED:SCRIPTS -->
|
|
556
|
+
| Command | Description |
|
|
557
|
+
|---|---|
|
|
558
|
+
| `npm run typecheck` | Run TypeScript compiler type checking (`tsc --noEmit`) |
|
|
559
|
+
| `npm test` | Run automated unit and integration test suite (`tsx --test tests/**/*.test.ts`) |
|
|
560
|
+
| `npm run build` | Bundle standalone CLI (`dist/cli.js`) and Cloudflare Worker (`worker/worker.js`) |
|
|
561
|
+
| `npm run verify:worker` | Verify `worker/worker.js` in a Cloudflare Workers global-scope sandbox and run handler smoke tests |
|
|
562
|
+
| `npm run build:release` | Build cross-platform release ZIP archives into `release/` |
|
|
563
|
+
| `npm run prepublishOnly` | Run typecheck, tests, bundle build, and Worker verification before publishing |
|
|
564
|
+
| `npm start` | Start the local proxy daemon directly from TypeScript source |
|
|
565
|
+
<!-- /AUTO-GENERATED:SCRIPTS -->
|
|
566
|
+
|
|
567
|
+
Run typecheck:
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
npm run typecheck
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
Run the automated test suite:
|
|
574
|
+
|
|
575
|
+
```bash
|
|
576
|
+
npm test
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
Build the standalone bundles:
|
|
580
|
+
|
|
581
|
+
```bash
|
|
582
|
+
npm run build
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### Continuous Integration (CI)
|
|
586
|
+
|
|
587
|
+
Every commit and pull request runs automated verification through the [Test & Verify](.github/workflows/test.yml) GitHub Actions workflow (Node.js 22):
|
|
588
|
+
|
|
589
|
+
- **Typecheck**: `npm run typecheck`
|
|
590
|
+
- **Unit & Integration Tests**: `npm test`
|
|
591
|
+
- **Bundle Verification**: `npm run build`, plus a check that the Worker bundle carries no Node built-in.
|
|
592
|
+
|
|
593
|
+
#### Skipping Automated Tests
|
|
594
|
+
|
|
595
|
+
To skip the test suite on specific commits (e.g. for docs updates, asset additions, or fast iterations while still verifying types and builds), include any of the following tags in the commit message:
|
|
596
|
+
|
|
597
|
+
- `[skip test]` or `[skip tests]`
|
|
598
|
+
- `[skip-test]` or `[skip-tests]`
|
|
599
|
+
|
|
600
|
+
When triggered manually via **Actions** -> **Run workflow**, check the **Skip running automated tests** input option.
|
|
601
|
+
|
|
602
|
+
### Building & Publishing Releases
|
|
603
|
+
|
|
604
|
+
The [Release workflow](.github/workflows/release.yml) builds multi-arch platform binaries and the standalone Cloudflare Worker bundle, publishing them as a **Draft Release** on GitHub.
|
|
605
|
+
|
|
606
|
+
#### Trigger Options
|
|
607
|
+
|
|
608
|
+
1. **Manual Trigger (`workflow_dispatch`)**:
|
|
609
|
+
- Go to **Actions** -> **Release** -> **Run workflow**.
|
|
610
|
+
- **Tag**: Release tag (defaults to `v` + `version` from `package.json`, e.g. `v1.0.0`).
|
|
611
|
+
- **Draft**: Create as Draft release (defaults to `true`). Uncheck when ready to publish publicly.
|
|
612
|
+
- **Overwrite**: If a release or tag with this name already exists, delete and recreate it (defaults to `true`).
|
|
613
|
+
- **Custom Release Notes**: Optional markdown text to use as release notes. Leave empty to use GitHub's auto-generated release notes.
|
|
614
|
+
- **Skip tests**: Check to bypass test execution during release packaging.
|
|
615
|
+
2. **Automatic Trigger via Commit Flag**:
|
|
616
|
+
- Push a commit to `master` containing `[release]`, `[draft-release]`, or `[release-draft]` in the commit message.
|
|
617
|
+
- Automatically builds and publishes a Draft release for the current `package.json` version.
|
|
618
|
+
|
|
619
|
+
#### Release Assets
|
|
620
|
+
|
|
621
|
+
Each release attaches:
|
|
622
|
+
- 6 platform ZIP archives: `linux-x64`, `linux-arm64`, `win-x64`, `win-arm64`, `macos-x64`, `macos-arm64`.
|
|
623
|
+
- Standalone Cloudflare Worker script: `worker.js` (evaluated and pre-tested).
|
|
624
|
+
|
|
625
|
+
To build the release archives locally:
|
|
626
|
+
|
|
627
|
+
```bash
|
|
628
|
+
npm run build
|
|
629
|
+
npm run build:release
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
The archives land in `release/`.
|
|
633
|
+
|
|
634
|
+
## Disclaimer & Legal Notice
|
|
635
|
+
|
|
636
|
+
> **IMPORTANT**: Please read this notice carefully before using or deploying OpenCode2API-Proxy.
|
|
637
|
+
|
|
638
|
+
1. **Educational & Research Purposes Only**: This project is developed and distributed exclusively for personal educational, research, and non-commercial API interoperability testing purposes.
|
|
639
|
+
2. **Upstream Terms of Service**: Routing requests to OpenCode's Zen, Go or free tier through a third-party client may violate OpenCode's Terms of Service. The free tier explicitly restricts access to the official OpenCode client, and the Go tier requires a paid subscription. OpenCode may rate-limit, revoke keys, or suspend accounts that use the upstream outside its own client.
|
|
640
|
+
3. **No Warranty & No Guarantee**: The author and contributors make no claims, promises, or guarantees regarding the safety, status, or longevity of your OpenCode account or subscription. This software is provided "AS IS", without warranty of any kind, express or implied.
|
|
641
|
+
4. **Assumption of Risk**: You assume full and sole responsibility for any outcomes or damages resulting from using this software, including but not limited to account termination, loss of API access, or data loss. Use strictly at your own risk.
|
|
642
|
+
5. **Trademark Attribution**: All product names, logos, and brands (such as "OpenCode", "OpenAI", "Anthropic") are trademarks or registered trademarks of their respective owners. OpenCode2API-Proxy is an independent open-source project and is neither affiliated with, maintained by, nor endorsed by any of them.
|
|
643
|
+
|
|
644
|
+
## References & Acknowledgements
|
|
645
|
+
|
|
646
|
+
- [jasonxu114514/opencode2api](https://github.com/jasonxu114514/opencode2api) — Original reference repository for OpenCode API proxying and reverse engineering.
|
|
647
|
+
- [ElectronBreach/opencode-unlimited-usage](https://github.com/ElectronBreach/opencode-unlimited-usage) — Reference implementation for `AAAA` edge pool rotation, `/64` prefix blacklisting, and self-healing quota-wall recovery.
|
|
648
|
+
|
|
649
|
+
## License
|
|
650
|
+
|
|
651
|
+
This project is licensed under the **GNU General Public License v3.0 (GPLv3)**. See the [LICENSE](./LICENSE) file for details.
|