glmproxy 2.5.1 → 2.6.1

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/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # GLM Proxy
2
2
 
3
3
  <p align="center">
4
- <b>A lightweight local proxy that exposes AutoClaw's AI models through<br>OpenAI-compatible and Anthropic-compatible APIs.</b>
4
+ <b>Use your AutoClaw GLM models in any tool that speaks the OpenAI or Anthropic API</b><br>
5
+ <sub>Claude Code, Cursor, Continue, OpenCode, LiteLLM, raw SDKs. Point them here instead.</sub>
5
6
  </p>
6
7
 
7
8
  <p align="center">
@@ -13,219 +14,273 @@
13
14
  <img src="https://github.com/eequaled/GLM_proxy/actions/workflows/ci.yml/badge.svg" alt="CI">
14
15
  </p>
15
16
 
16
- > **v2.5.1** one interactive CLI, shared core in `lib/`, zero dependencies. Install with `npm i -g glmproxy` or run with `npx glmproxy`.
17
+ <p align="center"><sub>v2.6.0. One CLI, two API formats, zero dependencies.</sub></p>
17
18
 
18
19
  ---
19
20
 
20
- ## Models are fetched automatically
21
+ ## Quick Start
22
+
23
+ ```bash
24
+ npm i -g glmproxy
25
+ glmproxy
26
+ ```
27
+
28
+ That's it, an interactive menu walks you through format, port, and key. Prefer flags?
21
29
 
22
- Point any OpenAI-compatible harness at `http://127.0.0.1:18791/v1` and it can pull the **live model catalog** through `GET /v1/models` — no config to maintain. The list is re-read from AutoClaw's runtime catalog on every request, so models AutoClaw adds or removes show up without a proxy restart. The Anthropic entrypoint (`/v1/models` on port `18792`) serves the same catalog in Anthropic's list shape.
30
+ ```bash
31
+ glmproxy --anthropic --port 3001 --key mykey
32
+ ```
33
+
34
+ Then point any OpenAI-compatible tool at `http://127.0.0.1:18791/v1`, or any Anthropic-compatible tool at `http://127.0.0.1:18792`. Default key is `mewmew` (see [Integrations](#integrations) below for exact per-tool setup).
23
35
 
24
- **GLM-5.3-Flash (known as "OX-alpha")** is in the proxy too served through the local-agent route (see [Models](#models) for the exact caveats).
36
+ **You need:** [AutoClaw](https://autoclaw.z.ai) installed, running, and logged in (Windows/macOS), plus Node.js 18+. The proxy reads auth straight from AutoClaw's local token file, so there's no manual token setup and no API keys to copy.
25
37
 
26
38
  ---
27
39
 
28
- Two API formats, one launcher. `node bin/cli.js` asks which one you want with an arrow-key menu and starts the right proxy. Flags skip the menu.
40
+ ## Integrations
29
41
 
30
- | Format | Flag | Default port | Use with |
31
- |--------|------|--------------|----------|
32
- | OpenAI (`/v1/chat/completions`) | `--openai` (default) | `18791` | OpenCode, Cursor, Continue, LiteLLM, Python/JS SDKs |
33
- | Anthropic (`/v1/messages`) | `--anthropic` | `18792` | Claude Code CLI, Anthropic SDK |
42
+ ### Claude Code CLI
43
+
44
+ Add to `~/.claude/settings.json`:
45
+
46
+ ```json
47
+ {
48
+ "env": {
49
+ "ANTHROPIC_BASE_URL": "http://localhost:18792",
50
+ "ANTHROPIC_AUTH_TOKEN": "mewmew"
51
+ }
52
+ }
53
+ ```
54
+
55
+ ### OpenCode
56
+
57
+ ```json
58
+ {
59
+ "provider": {
60
+ "autoclaw": {
61
+ "npm": "@ai-sdk/openai-compatible",
62
+ "name": "AutoClaw",
63
+ "options": {
64
+ "baseURL": "http://localhost:18791/v1",
65
+ "apiKey": "mewmew"
66
+ },
67
+ "models": {
68
+ "zai_auto": { "name": "AutoClaw Auto" },
69
+ "zaicoding_glm-5.3": { "name": "AutoClaw GLM-5.3" },
70
+ "zai_glm-5-turbo": { "name": "AutoClaw GLM-5 Turbo" },
71
+ "tdpsk_deepseek-v4-flash-202605": { "name": "AutoClaw Deepseek-V4-Flash" },
72
+ "tdpsk_deepseek-v4-pro-202606": { "name": "AutoClaw DeepSeek-V4-Pro" }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ ### Cursor / Continue / anything OpenAI-compatible
80
+
81
+ Point it at `http://localhost:18791/v1` with API key `mewmew`. Harnesses that probe for available models pick up the live list from `/v1/models` automatically.
82
+
83
+ Or add it as a custom model directly in the UI:
84
+ - **API Format**: OpenAI Chat Completions
85
+ - **URL**: `http://localhost:18791/v1`
86
+ - **Model ID**: `zai_auto` (or any model from the table below)
87
+ - **API Key**: `mewmew`
88
+
89
+ ### Python
90
+
91
+ ```python
92
+ from openai import OpenAI
93
+
94
+ client = OpenAI(base_url="http://localhost:18791/v1", api_key="mewmew")
95
+
96
+ with client.chat.completions.stream(
97
+ model="zai_auto",
98
+ messages=[{"role": "user", "content": "Hello!"}],
99
+ ) as stream:
100
+ for text in stream.text_stream:
101
+ print(text, end="", flush=True)
102
+ ```
103
+
104
+ ### JavaScript
105
+
106
+ ```javascript
107
+ import OpenAI from "openai";
34
108
 
35
- ## Screenshots
109
+ const client = new OpenAI({
110
+ baseURL: "http://localhost:18791/v1",
111
+ apiKey: "mewmew",
112
+ });
113
+
114
+ const stream = await client.chat.completions.create({
115
+ model: "zai_auto",
116
+ messages: [{ role: "user", content: "Hello!" }],
117
+ stream: true,
118
+ });
119
+
120
+ for await (const chunk of stream) {
121
+ process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
122
+ }
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Why this exists
128
+
129
+ AutoClaw gives you Zhipu's GLM models (GLM-5.3, GLM-5-Turbo, GLM-5.3-Flash, plus DeepSeek), but locks them inside its own desktop app. This proxy speaks OpenAI and Anthropic API dialects on one side and AutoClaw's native protocol on the other, so any tool built for those APIs can drive AutoClaw's models. The model list is pulled live from AutoClaw's runtime config, so anything AutoClaw adds or removes shows up without a proxy restart.
36
130
 
37
- ### 1. Prerequisites — AutoClaw running as your background service
131
+ ## How it works
38
132
 
39
- Make sure **AutoClaw is running and you're logged in**. The proxy reads auth from AutoClaw's local token file — as long as the AutoClaw desktop app is open, the proxy works.
133
+ ```
134
+ Your App GLM Proxy AutoClaw Backend
135
+ (OpenAI SDK) ───▶ 127.0.0.1:18791 (OpenAI format) ───▶ autoglm-api.autoglm.ai (cloud)
136
+ 127.0.0.1:18792 (Anthropic format)
137
+ │ cloud fails
138
+
139
+ AutoClaw desktop agent
140
+ 127.0.0.1:18789 (local WebSocket)
141
+ ```
142
+
143
+ AutoClaw handles authentication automatically. When the cloud path fails, requests fall back to AutoClaw's own desktop agent over a local WebSocket. See [Local gateway fallback](#local-gateway-fallback) for details.
40
144
 
41
145
  <p align="center">
42
- <i>Screenshot: AutoClaw desktop app running and logged in (background service)</i>
146
+ <i>AutoClaw running as your background service, that's the whole "auth" story</i>
43
147
  <br>
44
148
  <img src="./screenshots/autoclaw-background.png" alt="AutoClaw running as background service" width="700">
45
149
  </p>
46
150
 
47
- ### 2. Proxy in action — it works
48
-
49
- Start the proxy and watch it handle requests from your tool of choice.
50
-
51
151
  <p align="center">
52
- <i>ignore claude code here</i>
152
+ <i>the proxy in action (ignore claude code here)</i>
53
153
  <br>
54
154
  <img src="./screenshots/image.png" alt="Proxy terminal showing successful operation" width="700">
55
155
  </p>
56
156
 
57
- ## How it works
157
+ ## Current available models in autoclaw
58
158
 
59
- ```
60
- Your App AutoClaw Proxy CLI AutoClaw Backend
61
- (OpenAI SDK) ───▶ localhost:18791 (menu) ───▶ autoglm-api.autoglm.ai
62
- localhost:18792 (menu)
63
- ```
159
+ | ID | Name | Context | Max Output | Notes |
160
+ |----|------|---------|------------|-------|
161
+ | `zai_auto` | Auto | 1M | 131K | Routes to AutoClaw's optimal model (GLM-5.3-Flash today) |
162
+ | `zaicoding_glm-5.3` | GLM-5.3 | 1M | 131K | Latest GLM coding model |
163
+ | `zai_glm-5-turbo` | GLM-5-Turbo | 200K | 131K | Zhipu AI GLM-5 Turbo |
164
+ | `zai_glm-5.3-flash` | GLM-5.3-Flash ("OX-alpha") | 1M | 131K | Now a regular catalog model, served straight through the cloud path |
165
+ | `tdpsk_deepseek-v4-flash-202605` | Deepseek-V4-Flash | 1M | 393K | Fast DeepSeek model |
166
+ | `tdpsk_deepseek-v4-pro-202606` | DeepSeek-V4-Pro | 1M | 393K | Deep reasoning model |
64
167
 
65
- AutoClaw handles authentication automatically. As long as AutoClaw is running and you're logged in, the proxy will work no manual token setup needed.
168
+ > GLM 5.3 flash new in the proxy!!!!! ox alpha the goat
66
169
 
67
- The proxy speaks AutoClaw's native upstream dialect (client headers, bare model ids, and the app's system-prompt banner injected into every request without that banner the cloud returns 400 `"invalid request"`). When the cloud fails, requests fall back to AutoClaw's own desktop agent over a local WebSocket (`127.0.0.1:18789`).
170
+ The catalog is re-read from AutoClaw's `openclaw.runtime.json` on every `/v1/models` call, with a built-in fallback if that file isn't readable. Run `glmproxy --doctor` to inspect the current catalog after an AutoClaw update. Claude model names sent to the Anthropic proxy are mapped automatically:
68
171
 
69
- ## Prerequisites
172
+ | Claude model | Routes to |
173
+ |---|---|
174
+ | `claude-opus-*` | First available GLM-5.3 / GLM-5 model |
175
+ | `claude-sonnet-*` | `zai_auto` (or next available GLM-5 model) |
176
+ | `claude-haiku-*` | `zai_glm-5-turbo` (or DeepSeek / Auto fallback) |
70
177
 
71
- - [AutoClaw](https://autoclaw.com) installed, running, and logged in (Windows / macOS only)
72
- - Node.js 18+
178
+ ---
73
179
 
74
- ## Quick Start
180
+ <details>
181
+ <summary><h2>CLI reference</h2></summary>
75
182
 
76
- ```bash
77
- npm start
78
- # or: node bin/cli.js
79
- ```
183
+ Running `glmproxy` with no flags on a real terminal opens an interactive menu: arrow keys to move, Enter to pick. Choose format, port, host, and auth key, or run **Model Doctor** / **Test Models** without starting a proxy. Ctrl+C quits cleanly and restores your terminal. Without a TTY (piped stdin, CI), the CLI skips the menu and starts the OpenAI format on port 18791 using your env vars or defaults.
80
184
 
81
- You get an interactive menu: arrow keys to move, Enter to pick. Choose the format, port, host, and auth key, and the proxy starts. Ctrl+C quits cleanly and restores your terminal. The menu also offers **Model Doctor** (catalog + credit-tier routing) and **Test Models** (live health check) without starting a proxy.
185
+ | Format | Flag | Default port | Use with |
186
+ |--------|------|--------------|----------|
187
+ | OpenAI (`/v1/chat/completions`) | `--openai` (default) | `18791` | OpenCode, Cursor, Continue, LiteLLM, Python/JS SDKs |
188
+ | Anthropic (`/v1/messages`) | `--anthropic` | `18792` | Claude Code CLI, Anthropic SDK |
82
189
 
83
- Skip the menu with flags:
190
+ Config can also come from env vars. The CLI leaves existing ones alone:
84
191
 
85
192
  ```bash
86
- node bin/cli.js --anthropic --port 3001 --key mykey
193
+ PORT=3001 PROXY_KEY=mykey RATE_LIMIT=50 glmproxy
87
194
  ```
88
195
 
89
- Or feed config through env vars the CLI leaves existing env vars alone:
196
+ **Direct entry points**, if you'd rather skip the CLI entirely:
90
197
 
91
198
  ```bash
92
- PORT=3001 PROXY_KEY=mykey RATE_LIMIT=50 node bin/cli.js
199
+ node openai.js # OpenAI format, port 18791
200
+ node anthropic.js # Anthropic format, port 18792
93
201
  ```
94
202
 
95
- Without a TTY (piped stdin, CI), the CLI skips the menu and starts the OpenAI format on port 18791 using your env vars or the defaults.
203
+ They read the same env vars and respect `HOST`, `PORT`, `PROXY_KEY`, `RATE_LIMIT`, etc.
96
204
 
97
- ### npm commands
205
+ **npm commands:**
98
206
 
99
207
  | Command | What it does |
100
208
  |---------|--------------|
101
209
  | `npm start` | Launch the interactive CLI (`node bin/cli.js`) |
102
210
  | `npm run anthropic` | Start the Anthropic proxy directly (`node anthropic.js`), bypassing the menu |
103
- | `npm test` | Run the pen-test suite (`tests/pen-test-p1` through `p5`), plus the error-taxonomy tests (`tests/taxonomy.mjs`) and runtime-catalog refresh test (`tests/catalog-refresh.mjs`) |
211
+ | `npm test` | Run the pen-test suite, error-taxonomy tests, and the runtime-catalog refresh test |
104
212
 
105
- ### Direct entry points (optional)
213
+ Dev usage from a checkout: `npm start` or `node bin/cli.js`.
106
214
 
107
- You can still run either proxy directly without the CLI:
215
+ **Model doctor** scans AutoClaw's live model catalog with credit tiers and prints the Claude alias routing map:
108
216
 
109
217
  ```bash
110
- node openai.js # OpenAI format, port 18791
111
- node anthropic.js # Anthropic format, port 18792
218
+ glmproxy --doctor
112
219
  ```
113
220
 
114
- They read the same env vars and respect `HOST`, `PORT`, `PROXY_KEY`, `RATE_LIMIT`, etc.
221
+ Anthropic routing follows credit tiers: opus goes to High, sonnet to Medium, haiku to Low. UI display names can differ from API ids (e.g. the API's `zaicoding_glm-5.3` shows as "GLM-5.2" in AutoClaw's UI).
222
+
223
+ **Model health test** spawns a throwaway proxy and fires a minimal prompt at every catalog model:
224
+
225
+ ```bash
226
+ glmproxy --test-models
227
+ ```
228
+
229
+ ```
230
+ ✔ working [cloud ok] (1.2s) → PONG
231
+ ✔ working [cloud 402 → local agent] (38.4s) → PONG 🦞
232
+ ✗ failed (404) (0.9s) → Model ... is not recognized by AutoClaw upstream
233
+ ```
115
234
 
116
- ### Options
235
+ `[cloud ok]` means the cloud served it. `[cloud NNN → local agent]` means the cloud rejected it (HTTP NNN) and AutoClaw's desktop-agent fallback answered. This uses isolated log files, so it never clobbers your running proxy's records.
236
+
237
+ </details>
238
+
239
+ <details>
240
+ <summary><h2>Configuration (all flags & env vars)</h2></summary>
117
241
 
118
242
  | Variable / Flag | Default | Description |
119
243
  |-----------------|---------|-------------|
120
244
  | `PORT` / `--port` | `18791` (OpenAI), `18792` (Anthropic) | Port this proxy listens on |
121
245
  | `HOST` / `--host` | `127.0.0.1` | Bind address |
122
- | `PROXY_KEY` / `--key` | `mewmew` | API key clients must send |
246
+ | `PROXY_KEY` / `--key` | `mewmew` | API key clients must send. Fine for localhost, change it when binding beyond loopback |
123
247
  | `RATE_LIMIT` / `--rate-limit` | `30` | Max requests per second per client IP |
124
- | `MAX_MESSAGES` / `--max-messages` | unlimited (`0`/unset) | Max message / entity limit in request payload (explicit values: 128, 256, 512, 1024). Leave unlimited if your harness compresses or batches history raise it if you hit `413 / payload too large` |
248
+ | `MAX_MESSAGES` / `--max-messages` | unlimited (`0`/unset) | Max message/entity limit in request payload (explicit values: 128, 256, 512, 1024). Leave unlimited if your harness compresses or batches history, raise it if you hit `413 / payload too large` |
125
249
  | `LOG_LEVEL` | `info` | `debug` / `info` / `silent` |
126
250
  | `PREFER_LOCAL` | off | Set to `1` to use the local AutoClaw gateway first, skipping cloud attempts |
127
251
  | `TRUSTED_PROXIES` | empty | Comma-separated IPs whose `X-Forwarded-For` header is trusted for rate limiting |
128
252
  | `MAX_BODY_BYTES` | `52428800` | Max request body (50 MB) |
129
- | `JSONL_LOG` | off | Write structured JSONL request log when `true` |
253
+ | `JSONL_LOG` | off | Write structured JSONL request log when `true` (also on with `LOG_LEVEL=debug`) |
130
254
  | `JSONL_FILE` | `proxy_requests.jsonl` (Anthropic: `proxy_requests_anthropic.jsonl`) | JSONL output path |
255
+ | `JSONL_SYNC` | off | Write JSONL lines synchronously when `true` (flush every line) |
131
256
  | `JSONL_MAX_BYTES` | `10485760` | Rotate JSONL log when it exceeds this (10 MB) |
132
- | `UPSTREAM_TIMEOUT_MS` | `120000` | Per-attempt upstream budget (idle-based; the vendor allows up to 20 min raise this for slow thinking models) |
133
- | `GATEWAY_MIN_PROTOCOL` / `GATEWAY_MAX_PROTOCOL` | `3` / `4` | Local-gateway WS protocol range offered on connect (the proxy self-heals to the gateway's expected protocol on mismatch) |
257
+ | `UPSTREAM_TIMEOUT_MS` | `120000` | Per-attempt upstream budget (idle-based, the vendor allows up to 20 min, raise this for slow thinking models) |
258
+ | `GATEWAY_MIN_PROTOCOL` / `GATEWAY_MAX_PROTOCOL` | `3` / `4` | Local-gateway WS protocol range offered on connect (self-heals to the gateway's expected protocol on mismatch) |
134
259
  | `LOCAL_GATEWAY_HOST` / `LOCAL_GATEWAY_PORT` | `127.0.0.1` / `18789` | Where the AutoClaw desktop gateway is expected |
135
- | `FALLBACK_MODELS_PATH` | empty | Path to an external fallback model catalog JSON (`{"models":[...]}`) defaults to the shipped `lib/fallback-models.json` |
260
+ | `FALLBACK_MODELS_PATH` | empty | Path to an external fallback model catalog JSON (`{"models":[...]}`), defaults to the shipped `lib/fallback-models.json` |
136
261
  | `AUTOCLAW_SYSTEM_BANNER` | built-in | Override the system-prompt banner injected into cloud requests (keep the `## Tooling` line intact) |
137
262
  | `--anthropic` | — | Run in Anthropic API format |
138
263
  | `--openai` | — | Run in OpenAI API format (default) |
264
+ | `--limit [n]` | — | Set or clear the max message/entity limit (e.g. `--limit 256`; bare `--limit` prints the current value) |
139
265
  | `--doctor` | — | Scan AutoClaw's current runtime model catalog and show Anthropic routing |
140
266
  | `--test-models` / `--test` | — | Live health check: test every catalog model through the full pipeline |
141
267
  | `--help`, `-h` | — | Show CLI help |
142
268
 
143
- ### JSONL Request Logging
144
-
145
- Set `JSONL_LOG=true` (or `LOG_LEVEL=debug`) to write one JSON line per request:
269
+ **JSONL request logging.** Set `JSONL_LOG=true` (or `LOG_LEVEL=debug`) to write one JSON line per request:
146
270
 
147
271
  ```json
148
272
  {"ts":"2026-07-29T03:41:00.000Z","model":"zai_auto","status":200,"ip":"127.0.0.1","latencyMs":423}
149
273
  ```
150
274
 
151
- The Anthropic variant writes to `proxy_requests_anthropic.jsonl`.
152
-
153
- Alongside the JSONL stream, a compact ring log (`proxy_requests.json`, last 50 entries) records every terminal outcome — including `via: "local"` and the cloud verdict (`cloud_status` / `cloud_error`) when the cloud rejected a request that the local agent ended up serving.
154
-
155
- ### Model doctor
156
-
157
- Run the doctor to scan AutoClaw's live model catalog with **credit tiers** fetched from its remote model-config (falling back to the runtime file, then built-ins), and print the Claude alias routing map computed by the same resolver the Anthropic proxy uses:
158
-
159
- ```bash
160
- node bin/cli.js --doctor
161
- ```
162
-
163
- Anthropic routing follows credit tiers: opus → High, sonnet → Medium, haiku → Low. UI display names can differ from API ids (the API's `zaicoding_glm-5.3` shows as "GLM-5.2" in AutoClaw's UI).
164
-
165
- ### Model health test
166
-
167
- ```bash
168
- node bin/cli.js --test-models
169
- ```
170
-
171
- Spawns a throwaway proxy on a test port and fires a minimal prompt at **every model in the catalog**, reporting live status per model:
172
-
173
- ```
174
- ✔ working [cloud ok] (1.2s) → PONG
175
- ✔ working [cloud 402 → local agent] (38.4s) → PONG 🦞
176
- ✗ failed (404) (0.9s) → Model ... is not recognized by AutoClaw upstream
177
- ```
178
-
179
- The cloud verdict comes from the isolated test ring log: a `[cloud ok]` tag means the cloud served it; `[cloud NNN → local agent]` means the cloud rejected it (HTTP NNN) and AutoClaw's desktop-agent fallback answered. Zero-usage responses are the local-agent signature. The test uses isolated log files so it never clobbers your running proxy's records.
180
-
181
- ## Self-hosting behind a reverse proxy
182
-
183
- The proxy binds to `127.0.0.1` by default. To run it on a server (e.g. a VPS) and expose it with TLS, bind to all interfaces and put a reverse proxy in front:
184
-
185
- ```bash
186
- node bin/cli.js --host 0.0.0.0 --port 18791 --key change-me
187
- ```
188
-
189
- **Caddy** (automatic HTTPS):
190
-
191
- ```caddy
192
- glm.example.com {
193
- reverse_proxy 127.0.0.1:18791
194
- }
195
- ```
196
-
197
- **nginx**:
198
-
199
- ```nginx
200
- server {
201
- listen 443 ssl;
202
- server_name glm.example.com;
203
- # ssl_certificate / ssl_certificate_key ...
204
-
205
- location / {
206
- proxy_pass http://127.0.0.1:18791;
207
- proxy_set_header X-Forwarded-For $remote_addr;
208
- proxy_set_header Host $host;
209
- proxy_http_version 1.1;
210
- proxy_set_header Connection ""; # keep streaming (SSE) working
211
- }
212
- }
213
- ```
214
-
215
- Rate limiting keys off the client IP. When proxied, pass `X-Forwarded-For` and list the proxy's address in `TRUSTED_PROXIES` (comma-separated) so the real client IP is used — otherwise every client shares one bucket:
216
-
217
- ```bash
218
- TRUSTED_PROXIES=127.0.0.1 node bin/cli.js --host 0.0.0.0
219
- ```
275
+ Alongside the JSONL stream, a compact ring log (`proxy_requests.json`, last 50 entries; path via `REQUEST_LOG_FILE`) records every terminal outcome, including `via: "local"` and the cloud verdict (`cloud_status` / `cloud_error`) when the cloud rejected a request the local agent ended up serving.
220
276
 
221
- > The proxy needs a logged-in AutoClaw account running on the same machine (it reads the local token file), so a public endpoint is effectively a shared account — only expose it to people you trust.
277
+ </details>
222
278
 
223
- ## API
279
+ <details>
280
+ <summary><h2>API reference</h2></summary>
224
281
 
225
282
  ### `GET /healthz`
226
283
 
227
- Returns token status and upstream info.
228
-
229
284
  ```json
230
285
  {
231
286
  "ok": true,
@@ -237,49 +292,24 @@ Returns token status and upstream info.
237
292
 
238
293
  ### `GET /v1/models`
239
294
 
240
- Lists available models in OpenAI format (OpenAI proxy) or Anthropic format (Anthropic proxy). The catalog is **re-read from AutoClaw's runtime file on every call** no restart needed when AutoClaw's model list changes:
295
+ Lists available models in OpenAI format (OpenAI proxy) or Anthropic format (Anthropic proxy). Re-read from AutoClaw's runtime file on every call, so no restart is needed when AutoClaw's model list changes.
241
296
 
242
- ```json
243
- {
244
- "object": "list",
245
- "data": [
246
- {
247
- "id": "zai_auto",
248
- "object": "model",
249
- "owned_by": "autoclaw",
250
- "name": "Auto",
251
- "context_window": 1048576,
252
- "max_tokens": 393216
253
- }
254
- ]
255
- }
256
- ```
257
-
258
- Any OpenAI-compatible harness pointed at `http://127.0.0.1:18791/v1` will pick this list up automatically.
259
-
260
- ### `POST /v1/chat/completions` — OpenAI proxy
297
+ ### `POST /v1/chat/completions` (OpenAI proxy)
261
298
 
262
- OpenAI-compatible chat completions. Supports both streaming (`stream: true`) and non-streaming.
299
+ Supports streaming (`stream: true`) and non-streaming.
263
300
 
264
- **Headers:**
265
301
  ```
266
302
  Authorization: Bearer mewmew
267
303
  Content-Type: application/json
268
304
  ```
269
305
 
270
- ### `POST /v1/messages` Anthropic proxy
306
+ ### `POST /v1/messages` (Anthropic proxy)
271
307
 
272
- Anthropic-compatible Messages API. Supports both streaming and non-streaming. Claude model names are automatically mapped to the best available AutoClaw model:
273
-
274
- | Claude model | Routes to |
275
- |---|---|
276
- | `claude-opus-*` | First available GLM-5.3 / GLM-5 model |
277
- | `claude-sonnet-*` | `zai_auto` (or next available GLM-5 model) |
278
- | `claude-haiku-*` | `zai_glm-5-turbo` (or DeepSeek / Auto fallback) |
308
+ Anthropic-compatible Messages API. Supports both streaming and non-streaming. See the models table above for Claude GLM routing.
279
309
 
280
- ## Error handling
310
+ ### Error handling
281
311
 
282
- Every failure maps to a semantically correct status with a machine-readable `code` — no more generic blobs:
312
+ Every failure maps to a semantically correct status with a machine-readable `code`:
283
313
 
284
314
  | Situation | HTTP | `code` |
285
315
  |-----------|------|--------|
@@ -290,138 +320,82 @@ Every failure maps to a semantically correct status with a machine-readable `cod
290
320
  | Upstream rate limit | `429` | `rate_limited_by_upstream` |
291
321
  | Upstream returned garbage or died | `502` | `upstream_failure` |
292
322
  | AutoClaw not running (no token file) | `503` | `no_token` |
293
- | Upstream timeout (2 min) | `504` | `upstream_timeout` |
323
+ | Upstream timeout (default 2 min, see `UPSTREAM_TIMEOUT_MS`) | `504` | `upstream_timeout` |
294
324
 
295
- Quota errors are remembered for 60s per model: repeat requests fail instantly instead of replaying doomed cloud + fallback attempts.
325
+ Quota errors are remembered for 60s per model, so repeat requests fail instantly instead of replaying doomed cloud and fallback attempts.
296
326
 
297
- ## Local gateway fallback
327
+ </details>
298
328
 
299
- When the cloud upstream fails (and it's not a plain 404/429), the proxy re-runs your prompt through **AutoClaw's own desktop agent** over a local WebSocket (`127.0.0.1:18789`). Responses served this way are logged with `via: "local"` in the JSONL log. Caveats: it's a full agentic run (slower, tools included), and it shares your account's credits — quota walls stop it too. Set `PREFER_LOCAL=1` to skip the cloud attempt entirely while credits are exhausted.
329
+ <details>
330
+ <summary><h2>Local gateway fallback</h2></summary>
300
331
 
301
- ## Models
332
+ When the cloud upstream fails (and it's not a plain 404/429), the proxy re-runs your prompt through AutoClaw's own desktop agent over a local WebSocket (`127.0.0.1:18789`). Responses served this way are logged with `via: "local"`. It's a full agentic run (slower, tools included) and shares your account's credits, so quota walls stop it too. Set `PREFER_LOCAL=1` to skip the cloud attempt entirely while credits are exhausted.
302
333
 
303
- | ID | Name | Context | Max Output | Notes |
304
- |----|------|---------|------------|-------|
305
- | `zai_auto` | Auto | 1M | 393K | Routes to AutoClaw's optimal model |
306
- | `zaicoding_glm-5.3` | GLM-5.3 | 1M | 307K | Latest GLM coding model |
307
- | `zai_glm-5-turbo` | GLM-5-Turbo | 200K | 131K | Zhipu AI GLM-5 Turbo |
308
- | `zai_glm-5.3-flash` | GLM-5.3-Flash ("OX-alpha") | 1M | 131K | Newest GLM flash model. The cloud upstream 400s it, so it's served via the local-agent fallback; it now appears in `/v1/models` and in the built-in fallback catalog |
309
- | `tdpsk_deepseek-v4-flash-202605` | Deepseek-V4-Flash | 1M | 393K | Fast DeepSeek model |
310
- | `tdpsk_deepseek-v4-pro-202606` | DeepSeek-V4-Pro | 1M | 393K | Deep reasoning model |
334
+ </details>
311
335
 
312
- > GLM 5.3 new in the proxy? Maybe. Supposedly in the UI it's 5.2 but in the API it's 5.3. We'll never know, but it's a win-win xd.
336
+ <details>
337
+ <summary><h2>Self-hosting behind a reverse proxy</h2></summary>
313
338
 
314
- All models include `reasoning_content` in responses when the upstream model reasons. The model list is loaded dynamically from AutoClaw's `openclaw.runtime.json` (re-read on every `/v1/models` call, so the catalog stays live), with a built-in fallback if that file isn't readable. Run `node bin/cli.js --doctor` to inspect the current catalog after an AutoClaw update.
339
+ The proxy binds to `127.0.0.1` by default. To run it on a server and expose it with TLS, bind to all interfaces and put a reverse proxy in front:
315
340
 
316
- ## Integrations
341
+ ```bash
342
+ glmproxy --host 0.0.0.0 --port 18791 --key change-me
343
+ ```
317
344
 
318
- ### OpenCode
345
+ **Caddy** (automatic HTTPS):
319
346
 
320
- ```json
321
- {
322
- "provider": {
323
- "autoclaw": {
324
- "npm": "@ai-sdk/openai-compatible",
325
- "name": "AutoClaw",
326
- "options": {
327
- "baseURL": "http://localhost:18791/v1",
328
- "apiKey": "mewmew"
329
- },
330
- "models": {
331
- "zai_auto": { "name": "AutoClaw Auto" },
332
- "zaicoding_glm-5.3": { "name": "AutoClaw GLM-5.3" },
333
- "zai_glm-5-turbo": { "name": "AutoClaw GLM-5 Turbo" },
334
- "tdpsk_deepseek-v4-flash-202605": { "name": "AutoClaw Deepseek-V4-Flash" },
335
- "tdpsk_deepseek-v4-pro-202606": { "name": "AutoClaw DeepSeek-V4-Pro" }
336
- }
337
- }
338
- }
347
+ ```caddy
348
+ glm.example.com {
349
+ reverse_proxy 127.0.0.1:18791
339
350
  }
340
351
  ```
341
352
 
342
- Or add it as a custom model directly in the UI:
343
- - **API Format**: OpenAI Chat Completions
344
- - **URL**: `http://localhost:18791/v1`
345
- - **Model ID**: `zai_auto` (or any model from the table above)
346
- - **API Key**: `mewmew`
347
-
348
- ### Claude Code CLI
353
+ **nginx**:
349
354
 
350
- Add to `~/.claude/settings.json`:
355
+ ```nginx
356
+ server {
357
+ listen 443 ssl;
358
+ server_name glm.example.com;
359
+ # ssl_certificate / ssl_certificate_key ...
351
360
 
352
- ```json
353
- {
354
- "env": {
355
- "ANTHROPIC_BASE_URL": "http://localhost:18792",
356
- "ANTHROPIC_AUTH_TOKEN": "mewmew"
357
- }
361
+ location / {
362
+ proxy_pass http://127.0.0.1:18791;
363
+ proxy_set_header X-Forwarded-For $remote_addr;
364
+ proxy_set_header Host $host;
365
+ proxy_http_version 1.1;
366
+ proxy_set_header Connection ""; # keep streaming (SSE) working
367
+ }
358
368
  }
359
369
  ```
360
370
 
361
- ### Python
362
-
363
- ```python
364
- from openai import OpenAI
365
-
366
- client = OpenAI(base_url="http://localhost:18791/v1", api_key="mewmew")
371
+ Rate limiting keys off the client IP. When proxied, pass `X-Forwarded-For` and list the proxy's address in `TRUSTED_PROXIES` so the real client IP is used, otherwise every client shares one bucket:
367
372
 
368
- # Streaming
369
- with client.chat.completions.stream(
370
- model="zai_auto",
371
- messages=[{"role": "user", "content": "Hello!"}],
372
- ) as stream:
373
- for text in stream.text_stream:
374
- print(text, end="", flush=True)
375
-
376
- # Non-streaming
377
- response = client.chat.completions.create(
378
- model="zai_auto",
379
- messages=[{"role": "user", "content": "What is 2+2?"}],
380
- stream=False,
381
- )
382
- print(response.choices[0].message.content)
373
+ ```bash
374
+ TRUSTED_PROXIES=127.0.0.1 glmproxy --host 0.0.0.0
383
375
  ```
384
376
 
385
- ### JavaScript
386
-
387
- ```javascript
388
- import OpenAI from "openai";
389
-
390
- const client = new OpenAI({
391
- baseURL: "http://localhost:18791/v1",
392
- apiKey: "mewmew",
393
- });
377
+ > The proxy needs a logged-in AutoClaw account running on the same machine (it reads the local token file), so a public endpoint is effectively a shared account. Only expose it to people you trust.
394
378
 
395
- const stream = await client.chat.completions.create({
396
- model: "zai_auto",
397
- messages: [{ role: "user", content: "Hello!" }],
398
- stream: true,
399
- });
379
+ </details>
400
380
 
401
- for await (const chunk of stream) {
402
- process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
403
- }
404
- ```
381
+ <details>
382
+ <summary><h2>Good to know</h2></summary>
405
383
 
406
- ### Cursor / Continue / Other Tools
407
-
408
- Any tool that supports OpenAI-compatible providers works. Point it at `http://localhost:18791/v1` with API key `mewmew` and you're set. Harnesses that probe for available models will get the live list from `/v1/models` automatically.
384
+ - Only one AutoClaw account can be active at a time, multi-account pooling isn't supported
385
+ - `PROXY_KEY` is just a local password for this proxy, not your AutoClaw credentials. Set it to whatever you want. The default `mewmew` is for localhost-only use
386
+ - On a 401, the proxy invalidates its cached token and you can retry immediately
387
+ - Upstream 400 `"invalid request"` gets one retry after a 2s delay (a known upstream hiccup). Quota/plan errors are never retried
388
+ - The cloud upstream requires AutoClaw's app system-prompt banner in every request. The proxy injects it automatically and never duplicates it. In practice it doesn't change much since your harness's own system prompt overrides it anyway
389
+ - Max output is clamped to each model's real upstream cap (131K for every GLM model, 393K for DeepSeek). AutoClaw's runtime catalog overstates GLM-5.3's cap (307K), and asking the cloud for more than a model's real cap makes it **silently run a DeepSeek model instead and bill DeepSeek credits** — the proxy clamps so your `zai_glm-5.3` stays GLM-5.3
390
+ - The token file is watched for changes, so AutoClaw can rotate auth mid-session without a restart
391
+ - AutoClaw's client identity (app version, platform, channel) loads dynamically from its runtime file, same as the model catalog, so an AutoClaw app update is picked up without editing or restarting the proxy
392
+ - The fallback model catalog lives in `lib/fallback-models.json` (override with `FALLBACK_MODELS_PATH`). The built-in list is only a last resort when AutoClaw's runtime file is unreadable
393
+ - The local-gateway connection self-heals: if the gateway bumps its WS protocol, the proxy reconnects with the expected version automatically
394
+ - No dependencies at all. The interactive menu is hand-rolled on Node's built-in `readline`, so there's zero `node_modules` and zero install step
409
395
 
410
- ## Notes
396
+ </details>
411
397
 
412
- - Only one AutoClaw account can be active at a time — multi-account pooling isn't supported
413
- - `PROXY_KEY` is just a local password for this proxy, not your AutoClaw credentials — set it to whatever you want. The default key `mewmew` is for **localhost-only use**: change it via `--key` / `PROXY_KEY` if you bind beyond `127.0.0.1` (see [Self-hosting](#self-hosting-behind-a-reverse-proxy))
414
- - On a 401, the proxy invalidates its cached token and you can retry immediately
415
- - Upstream 400 "invalid request" gets one retry after a 2s delay (a known upstream hiccup); quota/plan errors are never retried
416
- - The cloud upstream requires AutoClaw's app system-prompt banner in every request. their new verification — the proxy injects it automatically (and never duplicates it). but it doesnt change much in practice/ my own testing and others testing. since it will be overridden by the harnesses own system prompt.
417
- - When cloud fails, requests fall back to AutoClaw's local desktop agent (`via: "local"` in logs) unless the model just failed permanently there too
418
- - The token file is watched for changes — AutoClaw can rotate auth mid-session without a restart
419
- - AutoClaw's client identity (`X-Version` app version, platform, channel) is loaded dynamically from its runtime file — the same file that feeds the model catalog — so an AutoClaw app update is picked up without editing or restarting the proxy
420
- - Rate limit is enforced per client IP (default 30 req/s); X-Forwarded-For is only honored from `TRUSTED_PROXIES`
421
- - The fallback model catalog lives in `lib/fallback-models.json` (override with `FALLBACK_MODELS_PATH`) — the built-in list is only a last resort when AutoClaw's runtime file is unreadable
422
- - The local-gateway connect self-heals: if the gateway bumps its WS protocol, the proxy reconnects with the expected version automatically
423
- - The ring log records cloud verdicts alongside local fallbacks, so every response is attributable
424
- - No dependencies at all: the interactive menu is hand-rolled on Node's built-in `readline`, so there's zero `node_modules` and zero install step
398
+ ---
425
399
 
426
400
  ## Special Thanks
427
401
 
@@ -431,4 +405,4 @@ Any tool that supports OpenAI-compatible providers works. Point it at `http://lo
431
405
 
432
406
  ## License
433
407
 
434
- MIT License + Jarona Rights™ (sorry to keep u waiting)
408
+ MIT License + Jarona Rights™ (sorry to keep u waiting)