chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
---
|
||||
title: "AgentRouter Setup Guide"
|
||||
version: 3.8.40
|
||||
lastUpdated: 2026-06-28
|
||||
---
|
||||
|
||||
# AgentRouter Setup Guide
|
||||
|
||||
[AgentRouter](https://agentrouter.org) is an Anthropic-compatible relay that resells
|
||||
Claude and other models, often at lower prices than the direct Anthropic API. It is
|
||||
designed as a drop-in `ANTHROPIC_BASE_URL` replacement for the official Claude Code
|
||||
client, so it only accepts traffic that matches the Claude Code wire image (specific
|
||||
User-Agent, `anthropic-beta` flags, Stainless SDK headers, etc.).
|
||||
|
||||
## Quick start — use the native `agentrouter` provider (recommended)
|
||||
|
||||
For most users, **no special setup is required**. OmniRoute ships a built-in
|
||||
`agentrouter` provider with the full Claude Code wire image already baked in (see
|
||||
`open-sse/config/providerRegistry.ts` → `agentrouter`). To use it:
|
||||
|
||||
1. Open **Dashboard → Providers → Add Provider**.
|
||||
2. Select **AgentRouter** from the list.
|
||||
3. Paste your `sk-...` API key and save.
|
||||
|
||||
That's it — no environment variables, no custom provider type. Built-in models
|
||||
include `claude-opus-4-6`, `claude-haiku-4-5-20251001`, `glm-5.1`, and
|
||||
`deepseek-v3.2`.
|
||||
|
||||
The rest of this guide covers the **advanced path**: using the
|
||||
`anthropic-compatible-cc-*` provider type. Use that when you need more control
|
||||
over the wire image — for example, when connecting to other AgentRouter-style
|
||||
relays that are not yet in the native provider registry, or when overriding the
|
||||
base URL, chat path, or header set.
|
||||
|
||||
---
|
||||
|
||||
## Advanced: connecting via the Claude Code compatible provider type
|
||||
|
||||
OmniRoute also supports AgentRouter (and similar relays) through the **Claude Code
|
||||
compatible** provider type (`anthropic-compatible-cc-*`), which speaks the
|
||||
Anthropic Messages API with the correct wire image. A generic
|
||||
`openai-compatible-chat` provider pointing at `https://agentrouter.org` will
|
||||
**not** work — the upstream WAF rejects requests that do not look like Claude
|
||||
Code.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- An AgentRouter account and API key. New signups get free credits via the affiliate
|
||||
link in the project [README](../README.md).
|
||||
- OmniRoute running with the `ENABLE_CC_COMPATIBLE_PROVIDER` feature flag enabled
|
||||
(see below).
|
||||
|
||||
## 1. Enable the CC-compatible provider type
|
||||
|
||||
The Claude Code compatible provider type is gated behind a feature flag because it
|
||||
sends traffic that closely mirrors the official Claude Code client. Enable it by
|
||||
setting an environment variable before starting OmniRoute:
|
||||
|
||||
```bash
|
||||
ENABLE_CC_COMPATIBLE_PROVIDER=true
|
||||
```
|
||||
|
||||
Docker example:
|
||||
|
||||
```bash
|
||||
docker run -d --name omniroute \
|
||||
--restart unless-stopped \
|
||||
-p 20128:20128 \
|
||||
-v omniroute-data:/app/data \
|
||||
-e ENABLE_CC_COMPATIBLE_PROVIDER=true \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
After restarting, the dashboard exposes an **Add Claude Code Compatible** option in
|
||||
addition to the existing OpenAI-compatible and Anthropic-compatible flows.
|
||||
|
||||
## 2. Create the provider in the dashboard
|
||||
|
||||
1. Open **Dashboard → Providers → Add Provider**.
|
||||
2. Choose **Add Claude Code Compatible** (only visible when the flag above is set).
|
||||
3. Fill in the fields:
|
||||
|
||||
| Field | Value |
|
||||
| --------- | -------------------------------------------------------------- |
|
||||
| Name | `AgentRouter` (or any label) |
|
||||
| Prefix | `agentrouter` (friendly alias shown in logs and the dashboard) |
|
||||
| Base URL | `https://agentrouter.org` |
|
||||
| Chat path | `/v1/messages?beta=true` (default — leave as-is) |
|
||||
|
||||
> The canonical model identifier still uses the full provider node ID
|
||||
> (`anthropic-compatible-cc-{uuid}/{model}`). The **Prefix** is just a display
|
||||
> alias resolved by `src/lib/usage/callLogs.ts` for friendlier log output.
|
||||
|
||||
4. (Optional) Paste your API key in the **Validate** field and click **Check** to
|
||||
confirm connectivity before saving.
|
||||
5. Click **Add**.
|
||||
|
||||
Once created, open the provider and add a **Connection** with your AgentRouter API
|
||||
key (`sk-...`). The connection's `test_status` should turn `active`.
|
||||
|
||||
## 3. Use it through a combo or directly
|
||||
|
||||
Reference the model using your provider's prefix as the namespace:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:20128/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "agentrouter/claude-opus-4-6",
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
"max_tokens": 100
|
||||
}'
|
||||
```
|
||||
|
||||
The canonical model ID `anthropic-compatible-cc-{uuid}/claude-opus-4-6` also works
|
||||
and is what shows up in the database and combo configuration.
|
||||
|
||||
Or add it to a combo for routing, fallback, and quota management like any other
|
||||
provider.
|
||||
|
||||
---
|
||||
|
||||
## Wire image details
|
||||
|
||||
For reference, the cc-compatible bridge sends the following on each upstream
|
||||
request (see `open-sse/services/claudeCodeCompatible.ts`):
|
||||
|
||||
| Header | Value |
|
||||
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| `Authorization` | `Bearer <api-key>` |
|
||||
| `User-Agent` | `claude-cli/2.1.195 (external, sdk-cli)` |
|
||||
| `anthropic-version` | `2023-06-01` |
|
||||
| `anthropic-beta` | `claude-code-20250219,interleaved-thinking-2025-05-14,effort-2025-11-24` |
|
||||
| Per-connection redact-thinking beta toggle | Adds `redact-thinking-2026-02-12` for upstreams that specifically require redacted thinking streams |
|
||||
| Per-connection summarized thinking toggle | Adds `display: "summarized"` to CC Compatible thinking requests that did not already set a display mode |
|
||||
| `anthropic-dangerous-direct-browser-access` | `true` |
|
||||
| `x-app` | `cli` |
|
||||
| `X-Stainless-*` | Various Stainless SDK headers (lang, package version, OS, arch, etc.) |
|
||||
|
||||
This is what allows requests to pass the upstream WAF / client whitelist.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**`{"error":{"message":"unauthorized client detected, ..."}}`** — Your request did
|
||||
not match the Claude Code wire image. This happens when the provider is configured
|
||||
as `openai-compatible-chat` instead of `anthropic-compatible-cc`, or when the
|
||||
`ENABLE_CC_COMPATIBLE_PROVIDER=true` flag was not set at startup.
|
||||
|
||||
**`{"error":{"message":"无效的令牌","type":"new_api_error"}}` (HTTP 401)** —
|
||||
"Invalid token". The wire image is correct but the API key is rejected. Generate a
|
||||
new key in the AgentRouter dashboard and update the connection.
|
||||
|
||||
**`{"error":{"code":"content-blocked","type":"agent_router_api_error"}}`
|
||||
(HTTP 400)** — AgentRouter's moderation hook rejected the request content, or the
|
||||
key's plan does not permit the requested model. Try a different prompt or model;
|
||||
contact AgentRouter support if a benign prompt is consistently blocked.
|
||||
|
||||
**`[400]: content-blocked` only on specific models** — Most AgentRouter plans only
|
||||
allow a subset of models (e.g. `claude-opus-4-6`). Other model IDs return
|
||||
`unauthorized_client_error` even though the key is valid. Check which models your
|
||||
plan covers in the AgentRouter dashboard.
|
||||
|
||||
**`Invalid JSON response from provider (reset after Ns)` from the omniroute logs** —
|
||||
The upstream returned a non-JSON body (typically an HTML error page from the WAF).
|
||||
This usually means the request never reached the AgentRouter backend — recheck that
|
||||
the provider ID starts with `anthropic-compatible-cc-` (note the trailing dash —
|
||||
see `CLAUDE_CODE_COMPATIBLE_PREFIX` in `open-sse/services/claudeCodeCompatible.ts`)
|
||||
and the feature flag is enabled.
|
||||
|
||||
---
|
||||
|
||||
## See also
|
||||
|
||||
- [`docs/providers/CLAUDE_WEB.md`](./CLAUDE_WEB.md) — Claude Web provider integration notes
|
||||
- [`docs/reference/FREE_TIERS.md`](../reference/FREE_TIERS.md) — Free-tier provider
|
||||
catalog
|
||||
- [`open-sse/services/claudeCodeCompatible.ts`](../../open-sse/services/claudeCodeCompatible.ts)
|
||||
— Wire image implementation
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: "Providers — Claude Web"
|
||||
version: 3.8.40
|
||||
lastUpdated: 2026-06-28
|
||||
---
|
||||
|
||||
# Providers — Claude Web
|
||||
|
||||
## claude-web
|
||||
|
||||
Web-cookie-based provider for **Claude AI** (`claude.ai`) using session cookie authentication.
|
||||
|
||||
### How It Works
|
||||
|
||||
1. User pastes their `claude.ai` session cookies into the OmniRoute dashboard
|
||||
2. `ClaudeWebExecutor` transforms OpenAI-format requests to Claude Web API format
|
||||
3. Requests are sent via **`tls-client-node`** with **Chrome 124 TLS fingerprint** to bypass Cloudflare Turnstile
|
||||
4. Responses are streamed back via SSE (`text/event-stream`)
|
||||
|
||||
### Required Cookies
|
||||
|
||||
| Cookie | Purpose | Source |
|
||||
| -------------- | ------------------------------ | -------------------------------------- |
|
||||
| `sessionKey` | Main authentication | `claude.ai` browser session |
|
||||
| `routingHint` | Anthropic routing | `claude.ai` browser session |
|
||||
| `cf_clearance` | Cloudflare Turnstile clearance | Auto-set by Cloudflare after challenge |
|
||||
| `__cf_bm` | Cloudflare bot management | Auto-set by Cloudflare |
|
||||
| `_cfuvid` | Cloudflare visitor ID | Auto-set by Cloudflare |
|
||||
|
||||
> **Note**: `cf_clearance` is bound to the TLS fingerprint of the browser that solved Cloudflare's Turnstile challenge. The `tls-client-node` library (via `claudeTlsClient.ts`) spoofs a Chrome 124 TLS handshake so the clearance token works from the OmniRoute server.
|
||||
|
||||
### API Reference
|
||||
|
||||
**Endpoint**: `POST /api/organizations/{orgId}/chat_conversations/{convId}/completion`
|
||||
|
||||
**Required Headers**:
|
||||
|
||||
```
|
||||
accept: text/event-stream
|
||||
anthropic-client-platform: web_claude_ai
|
||||
anthropic-device-id: <uuid>
|
||||
content-type: application/json
|
||||
Referer: https://claude.ai/chat/{convId}
|
||||
```
|
||||
|
||||
**Request Body**:
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt": "user message",
|
||||
"model": "claude-sonnet-4-6",
|
||||
"timezone": "Asia/Jakarta",
|
||||
"locale": "en-US",
|
||||
"personalized_styles": [...],
|
||||
"tools": [...],
|
||||
"rendering_mode": "messages",
|
||||
"create_conversation_params": {
|
||||
"name": "",
|
||||
"model": "claude-sonnet-4-6",
|
||||
"is_temporary": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
User Cookies (claude.ai)
|
||||
↓
|
||||
OmniRoute Dashboard
|
||||
↓
|
||||
ClaudeWebExecutor (open-sse/executors/claude-web.ts)
|
||||
↓ Request transformation (OpenAI → Claude Web format)
|
||||
↓
|
||||
tlsFetchClaude() (open-sse/services/claudeTlsClient.ts)
|
||||
↓ Chrome 124 TLS fingerprint spoofing
|
||||
↓
|
||||
tls-client-node (Go native binding, koffi)
|
||||
↓
|
||||
claude.ai API
|
||||
↓ SSE stream
|
||||
```
|
||||
|
||||
### Files
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------------------------------------- | ---------------------------------------------------- |
|
||||
| `src/shared/constants/providers.ts` | Provider registration (WEB_COOKIE_PROVIDERS) |
|
||||
| `src/lib/providers/webCookieAuth.ts` | Cookie utilities (normalize/extract session cookies) |
|
||||
| `open-sse/executors/claude-web.ts` | Executor implementation |
|
||||
| `open-sse/executors/index.ts` | Executor registration |
|
||||
| `open-sse/services/claudeTlsClient.ts` | TLS fingerprint spoofing via tls-client-node |
|
||||
| `open-sse/services/__tests__/claudeTlsClient.test.ts` | TLS client tests |
|
||||
| `tests/unit/claude-web.test.ts` | Executor tests |
|
||||
|
||||
### Testing
|
||||
|
||||
```bash
|
||||
# Unit tests
|
||||
node --import tsx/esm --test tests/unit/claude-web.test.ts
|
||||
|
||||
# TLS client tests
|
||||
npx vitest run open-sse/services/__tests__/claudeTlsClient.test.ts
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
1. Start OmniRoute: `omniroute`
|
||||
2. Go to Dashboard → Providers → Add Provider
|
||||
3. Select "Web Cookie" category
|
||||
4. Choose "Claude Web"
|
||||
5. Paste your full cookie header from `claude.ai` browser DevTools (Network tab → Copy as fetch → Cookie header)
|
||||
@@ -0,0 +1,128 @@
|
||||
---
|
||||
title: "Zed IDE Integration in Docker Environments"
|
||||
version: 3.8.40
|
||||
lastUpdated: 2026-06-28
|
||||
---
|
||||
|
||||
# Zed IDE Integration in Docker Environments
|
||||
|
||||
When OmniRoute runs inside Docker, the standard "Import from Zed Keychain" flow fails
|
||||
because the container cannot reach the host OS keychain daemon (libsecret on Linux,
|
||||
Keychain on macOS, Credential Manager on Windows) and the Zed config directories on the
|
||||
host filesystem are not visible inside the container by default.
|
||||
|
||||
## Why Keychain Import Fails in Docker
|
||||
|
||||
Two blocking issues occur inside a container:
|
||||
|
||||
1. **Filesystem isolation** — `isZedInstalled()` looks for `~/.config/zed` (Linux),
|
||||
`~/Library/Application Support/Zed` (macOS), or the Windows equivalent. These paths
|
||||
live on the host and are not available unless explicitly volume-mounted.
|
||||
2. **IPC isolation** — Even when the config directory is mounted, the `keytar` native
|
||||
module communicates with the OS keychain service over a Unix socket or D-Bus session.
|
||||
Neither is bridged into the container by default, so credential reads always fail.
|
||||
|
||||
OmniRoute detects the Docker environment via two heuristics:
|
||||
|
||||
- Presence of `/.dockerenv` (written by the Docker daemon at container start).
|
||||
- The string `docker` appearing in `/proc/1/cgroup` (Linux cgroup v1).
|
||||
|
||||
When either heuristic triggers, the import route returns HTTP 422 with
|
||||
`zedDockerEnvironment: true` and a message directing you to the Manual Token Import tab.
|
||||
|
||||
## Using the Manual Token Import Tab
|
||||
|
||||
1. Open **Dashboard → Providers → Zed**.
|
||||
2. The **Manual Token Import** panel appears below the keychain import card. When
|
||||
OmniRoute detects Docker, this panel expands automatically after the first failed
|
||||
keychain import attempt.
|
||||
3. Select the provider from the dropdown (OpenAI, Anthropic, Google, Mistral, xAI,
|
||||
OpenRouter, or DeepSeek).
|
||||
4. Paste the API key in the password field.
|
||||
5. Click **Import**.
|
||||
|
||||
The key is saved as a new provider connection with the name
|
||||
`Zed Manual Import (<provider>)`.
|
||||
|
||||
## Where Zed Stores API Keys on the Host
|
||||
|
||||
Zed stores AI provider keys in the OS keychain under service names such as
|
||||
`zed-openai`, `ai.zed.openai`, `zed-anthropic`, etc. To retrieve them for manual
|
||||
import, look in:
|
||||
|
||||
**Linux**
|
||||
|
||||
```
|
||||
~/.config/zed/settings.json
|
||||
```
|
||||
|
||||
The `language_models` section contains provider configurations. Keys saved to the
|
||||
keychain via the Zed UI are not in plain text in `settings.json`; retrieve them through
|
||||
a keychain viewer such as GNOME Keyring / Seahorse, or by running:
|
||||
|
||||
```bash
|
||||
secret-tool lookup service zed-openai account api-key
|
||||
```
|
||||
|
||||
**macOS**
|
||||
|
||||
```
|
||||
~/Library/Application Support/Zed/settings.json
|
||||
```
|
||||
|
||||
Keychain entries can be found in **Keychain Access.app** by searching for `zed`.
|
||||
|
||||
## Volume-Mount Option (Advanced)
|
||||
|
||||
You can optionally mount the Zed config directory read-only into the container.
|
||||
This does not fix the keychain issue but may be useful for future features that read
|
||||
non-secret Zed config values (e.g., model preferences).
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml snippet
|
||||
services:
|
||||
omniroute:
|
||||
image: omniroute:latest
|
||||
volumes:
|
||||
# Linux host
|
||||
- "${HOME}/.config/zed:/host-zed-config:ro"
|
||||
# macOS host (uncomment instead)
|
||||
# - "${HOME}/Library/Application Support/Zed:/host-zed-config:ro"
|
||||
environment:
|
||||
# Future: ZED_CONFIG_PATH=/host-zed-config
|
||||
PORT: "20128"
|
||||
```
|
||||
|
||||
Note: a `ZED_CONFIG_PATH` environment variable override is not yet implemented. This
|
||||
snippet is provided as a reference for when that feature is added.
|
||||
|
||||
## Manual Import API
|
||||
|
||||
The manual import endpoint can also be called directly:
|
||||
|
||||
```
|
||||
POST /api/providers/zed/manual-import
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer <management-token>
|
||||
|
||||
{
|
||||
"provider": "openai",
|
||||
"token": "sk-...",
|
||||
"label": "My Zed OpenAI key" // optional
|
||||
}
|
||||
```
|
||||
|
||||
On success it returns:
|
||||
|
||||
```json
|
||||
{ "success": true, "connectionId": "...", "provider": "openai" }
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Cause | Fix |
|
||||
| ------------------------------------ | ---------------------------- | -------------------------------- |
|
||||
| 422 + `zedDockerEnvironment: true` | Running inside Docker | Use Manual Token Import tab |
|
||||
| 404 + `zedInstalled: false` | Zed not installed on host | Install Zed or use manual import |
|
||||
| 403 + keychain access denied | OS denied keychain access | Grant permission in OS prompt |
|
||||
| 404 + keychain service not available | `libsecret` missing on Linux | Install `libsecret-1-dev` |
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"title": "Providers",
|
||||
"description": "Provider-specific integration guides",
|
||||
"pages": ["CLAUDE_WEB", "AGENTROUTER", "ZED-DOCKER"]
|
||||
}
|
||||
Reference in New Issue
Block a user