-
Notifications
You must be signed in to change notification settings - Fork 1
inject the app system banner so cloud stops 400ing into the ws fallback #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e1d5af4
2dd4d23
82f451b
a34fb7e
441c6a6
0233880
79d0cfc
a5bf64b
ec1258c
97038a5
7005d1f
8a411bc
4d122b8
0ea6522
9a6fd5d
fa2c727
119a517
6f99986
7aaf420
e215300
fb86527
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,83 @@ | ||
| name: CI | ||
|
|
||
| on: | ||
| push: | ||
| branches: [master] | ||
| branches: [L-route, master] | ||
| pull_request: | ||
| branches: [master] | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: ci-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| test: | ||
| # Required gate: static syntax checks + pure-unit taxonomy tests. | ||
| # Matrix covers the oldest supported line (engines >=18 floor is 20 here) | ||
| # and the current LTS. Zero dependencies: no lockfile, no npm cache, | ||
| # nothing to install. | ||
| unit-and-static: | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| node-version: [20, 24] | ||
| timeout-minutes: 10 | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: actions/setup-node@v4 | ||
| - uses: actions/checkout@v5 | ||
| - uses: actions/setup-node@v6 | ||
| with: | ||
| node-version: ${{ matrix.node-version }} | ||
| - run: node --check lib/core.js && node --check openai.js && node --check anthropic.js && node --check bin/cli.js | ||
| - run: node tests/taxonomy.mjs | ||
|
|
||
| # Live-upstream suites: gated by cloud availability, so they run | ||
| # continue-on-error and must never redden code PRs. Each uploads whatever | ||
| # request logs its own workspace produced (workspaces are ephemeral — a | ||
| # separate collector job would find nothing). | ||
| pen-tests-fast: | ||
| needs: unit-and-static | ||
| continue-on-error: true | ||
| timeout-minutes: 15 | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v5 | ||
| - uses: actions/setup-node@v6 | ||
| with: | ||
| node-version: 24 | ||
| - run: node tests/pen-test-p1.mjs && node tests/pen-test-p2.mjs && node tests/pen-test-p3.mjs && node tests/pen-test-p4.mjs | ||
| - name: Upload request logs | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: ring-logs-fast-${{ github.run_id }}-${{ github.run_attempt }} | ||
| path: | | ||
| proxy_requests*.json | ||
| proxy_requests*.jsonl | ||
| retention-days: 3 | ||
| if-no-files-found: ignore | ||
|
|
||
| pen-tests-live: | ||
| needs: unit-and-static | ||
| continue-on-error: true | ||
| timeout-minutes: 15 | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v5 | ||
| - uses: actions/setup-node@v6 | ||
| with: | ||
| node-version: 24 | ||
| - run: node tests/pen-test-p5.mjs && node tests/catalog-refresh.mjs | ||
| - name: Upload request logs | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| node-version: 18 | ||
| - run: node --check main.js | ||
| - run: node --check anthropic.js | ||
| - run: npm test | ||
| env: | ||
| RATE_LIMIT: "200" | ||
| name: ring-logs-live-${{ github.run_id }}-${{ github.run_attempt }} | ||
| path: | | ||
| proxy_requests*.json | ||
| proxy_requests*.jsonl | ||
| retention-days: 3 | ||
| if-no-files-found: ignore |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -95,7 +95,7 @@ Without a TTY (piped stdin, CI), the CLI skips the menu and starts the OpenAI fo | |||||||||||||||||||||||||||||||||||||||
| You can still run either proxy directly without the CLI: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||||||||||||||||||||
| node main.js # OpenAI format, port 18791 | ||||||||||||||||||||||||||||||||||||||||
| node openai.js # OpenAI format, port 18791 | ||||||||||||||||||||||||||||||||||||||||
| node anthropic.js # Anthropic format, port 18792 | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
@@ -110,12 +110,14 @@ They read the same env vars and respect `HOST`, `PORT`, `PROXY_KEY`, `RATE_LIMIT | |||||||||||||||||||||||||||||||||||||||
| | `PROXY_KEY` / `--key` | `mewmew` | API key clients must send | | ||||||||||||||||||||||||||||||||||||||||
| | `RATE_LIMIT` / `--rate-limit` | `30` | Max requests per second per client IP | | ||||||||||||||||||||||||||||||||||||||||
| | `LOG_LEVEL` | `info` | `debug` / `info` / `silent` | | ||||||||||||||||||||||||||||||||||||||||
| | `PREFER_LOCAL` | off | Set to `1` to use the local AutoClaw gateway first, skipping cloud attempts | | ||||||||||||||||||||||||||||||||||||||||
| | `MAX_BODY_BYTES` | `52428800` | Max request body (50 MB) | | ||||||||||||||||||||||||||||||||||||||||
| | `JSONL_LOG` | off | Write structured JSONL request log when `true` | | ||||||||||||||||||||||||||||||||||||||||
| | `JSONL_FILE` | `proxy_requests.jsonl` (Anthropic: `proxy_requests_anthropic.jsonl`) | JSONL output path | | ||||||||||||||||||||||||||||||||||||||||
| | `JSONL_MAX_BYTES` | `10485760` | Rotate JSONL log when it exceeds this (10 MB) | | ||||||||||||||||||||||||||||||||||||||||
| | `--anthropic` | — | Run in Anthropic API format | | ||||||||||||||||||||||||||||||||||||||||
| | `--openai` | — | Run in OpenAI API format (default) | | ||||||||||||||||||||||||||||||||||||||||
| | `--doctor` | — | Scan AutoClaw's current runtime model catalog and show Anthropic routing | | ||||||||||||||||||||||||||||||||||||||||
| | `--help`, `-h` | — | Show CLI help | | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+113
to
121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Document The CLI accepts 📝 Proposed doc update-| `--doctor` | — | Scan AutoClaw's current runtime model catalog and show Anthropic routing |
+| `--doctor` | — | Scan the live model catalog (remote model-config, then runtime file, then built-ins) and show Anthropic routing |
+| `--test-models`, `--test` | — | Test every catalog model through a temporary proxy and report live health |
| `--help`, `-h` | — | Show CLI help |📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ### JSONL Request Logging | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -128,6 +130,16 @@ Set `JSONL_LOG=true` (or `LOG_LEVEL=debug`) to write one JSON line per request: | |||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| The Anthropic variant writes to `proxy_requests_anthropic.jsonl`. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ### Model doctor | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| 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: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||||||||||||||||||||
| node bin/cli.js --doctor | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| 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). | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ## API | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ### `GET /healthz` | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -163,19 +175,44 @@ Anthropic-compatible Messages API. Supports both streaming and non-streaming. Cl | |||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| | Claude model | Routes to | | ||||||||||||||||||||||||||||||||||||||||
| |---|---| | ||||||||||||||||||||||||||||||||||||||||
| | `claude-opus-*` | `zaicoding_glm-5.2` | | ||||||||||||||||||||||||||||||||||||||||
| | `claude-sonnet-*` | `zai_auto` | | ||||||||||||||||||||||||||||||||||||||||
| | `claude-haiku-*` | `zai_glm-5-turbo` | | ||||||||||||||||||||||||||||||||||||||||
| | `claude-opus-*` | First available GLM-5.3 / GLM-5 model | | ||||||||||||||||||||||||||||||||||||||||
| | `claude-sonnet-*` | `zai_auto` (or next available GLM-5 model) | | ||||||||||||||||||||||||||||||||||||||||
| | `claude-haiku-*` | `zai_glm-5-turbo` (or DeepSeek / Auto fallback) | | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ## Error handling | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Every failure maps to a semantically correct status with a machine-readable `code` — no more generic blobs: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| | Situation | HTTP | `code` | | ||||||||||||||||||||||||||||||||||||||||
| |-----------|------|--------| | ||||||||||||||||||||||||||||||||||||||||
| | Bad client input (bad JSON / oversized / wrong Content-Type) | `400` / `413` / `415` | `invalid_request` | | ||||||||||||||||||||||||||||||||||||||||
| | Model out of credits or free quota (upstream 402/403/810000) | `402` | `quota_exhausted` | | ||||||||||||||||||||||||||||||||||||||||
| | AutoClaw token expired | `401` | `token_expired` | | ||||||||||||||||||||||||||||||||||||||||
| | Model unknown upstream | `404` | `model_not_found` | | ||||||||||||||||||||||||||||||||||||||||
| | Upstream rate limit | `429` | `rate_limited_by_upstream` | | ||||||||||||||||||||||||||||||||||||||||
| | Upstream returned garbage or died | `502` | `upstream_failure` | | ||||||||||||||||||||||||||||||||||||||||
| | AutoClaw not running (no token file) | `503` | `no_token` | | ||||||||||||||||||||||||||||||||||||||||
| | Upstream timeout (2 min) | `504` | `upstream_timeout` | | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Quota errors are remembered for 60s per model: repeat requests fail instantly instead of replaying doomed cloud + fallback attempts. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ## Local gateway fallback | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| 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. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ## Models | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| | ID | Name | Context | Max Output | Notes | | ||||||||||||||||||||||||||||||||||||||||
| |----|------|---------|------------|-------| | ||||||||||||||||||||||||||||||||||||||||
| | `zai_auto` | Auto | 1M | 393K | Routes to optimal model (DeepSeek-V4, GLM-5.1, GLM-Air, …) | | ||||||||||||||||||||||||||||||||||||||||
| | `zai_auto` | Auto | 1M | 393K | Routes to AutoClaw's optimal model | | ||||||||||||||||||||||||||||||||||||||||
| | `zaicoding_glm-5.3` | GLM-5.3 | 1M | 307K | Latest GLM coding model | | ||||||||||||||||||||||||||||||||||||||||
| | `zai_glm-5-turbo` | GLM-5-Turbo | 200K | 131K | Zhipu AI GLM-5 Turbo | | ||||||||||||||||||||||||||||||||||||||||
| | `zaicoding_glm-5.2` | GLM-5.2 | 1M | 307K | Latest GLM-5.2 coding model | | ||||||||||||||||||||||||||||||||||||||||
| | `tdpsk_deepseek-v4-flash-202605` | Deepseek-V4-Flash | 1M | 393K | Fast DeepSeek model | | ||||||||||||||||||||||||||||||||||||||||
| | `tdpsk_deepseek-v4-pro-202606` | DeepSeek-V4-Pro | 1M | 393K | Deep reasoning model | | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| > 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. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| All models include `reasoning_content` in responses when the upstream model reasons. The model list is loaded from AutoClaw's `openclaw.runtime.json` at startup, with a built-in fallback if that file isn't readable. | ||||||||||||||||||||||||||||||||||||||||
| All models include `reasoning_content` in responses when the upstream model reasons. The model list is loaded dynamically from AutoClaw's `openclaw.runtime.json` at startup, 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. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ## Integrations | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
@@ -193,8 +230,9 @@ All models include `reasoning_content` in responses when the upstream model reas | |||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| "models": { | ||||||||||||||||||||||||||||||||||||||||
| "zai_auto": { "name": "AutoClaw Auto" }, | ||||||||||||||||||||||||||||||||||||||||
| "zaicoding_glm-5.3": { "name": "AutoClaw GLM-5.3" }, | ||||||||||||||||||||||||||||||||||||||||
| "zai_glm-5-turbo": { "name": "AutoClaw GLM-5 Turbo" }, | ||||||||||||||||||||||||||||||||||||||||
| "zaicoding_glm-5.2": { "name": "AutoClaw GLM-5.2" } | ||||||||||||||||||||||||||||||||||||||||
| "tdpsk_deepseek-v4-flash-202605": { "name": "AutoClaw Deepseek-V4-Flash" } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -274,9 +312,10 @@ Any tool that supports OpenAI-compatible providers works. Point it at `http://lo | |||||||||||||||||||||||||||||||||||||||
| - Only one AutoClaw account can be active at a time — multi-account pooling isn't supported | ||||||||||||||||||||||||||||||||||||||||
| - `PROXY_KEY` is just a local password for this proxy, not your AutoClaw credentials — set it to whatever you want | ||||||||||||||||||||||||||||||||||||||||
| - On a 401, the proxy invalidates its cached token and you can retry immediately | ||||||||||||||||||||||||||||||||||||||||
| - On a 400 "invalid request" from upstream, the proxy retries once after a 2s delay before surfacing the error | ||||||||||||||||||||||||||||||||||||||||
| - Upstream 400 "invalid request" gets one retry after a 2s delay (a known upstream hiccup); quota/plan errors are never retried | ||||||||||||||||||||||||||||||||||||||||
| - When cloud fails, requests fall back to AutoClaw's local desktop agent (`via: "local"` in logs) unless the model just failed permanently there too | ||||||||||||||||||||||||||||||||||||||||
| - The token file is watched for changes — AutoClaw can rotate auth mid-session without a restart | ||||||||||||||||||||||||||||||||||||||||
| - Rate limit is enforced per client IP (default 30 req/s) | ||||||||||||||||||||||||||||||||||||||||
| - Rate limit is enforced per client IP (default 30 req/s); X-Forwarded-For is only honored from `TRUSTED_PROXIES` | ||||||||||||||||||||||||||||||||||||||||
| - 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 | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ## Special Thanks | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
sed -n '1,120p' .github/workflows/release.ymlRepository: eequaled/GLM_proxy
Length of output: 715
Set explicit read-only GitHub token permissions.
The workflow uses
GITHUB_TOKENfor checkout and performs no GitHub write operation. Addpermissions: { contents: read }so token permissions do not depend on repository or organization defaults.npm publishusesNPM_TOKENand does not require GitHub write access.🧰 Tools
🪛 zizmor (1.29.0)
[warning] 7-25: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
🤖 Prompt for AI Agents
Source: Linters/SAST tools