codex-claude-proxy 1.0.0 → 1.0.2

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
@@ -6,7 +6,17 @@ A local proxy server that exposes an **Anthropic-compatible API** (Claude Messag
6
6
 
7
7
  It is designed primarily for **Claude Code CLI** (Anthropic-format client) while actually executing requests against Codex.
8
8
 
9
- > This project is inspired by the structure of the Antigravity Claude Proxy README in `antigravity-claude-proxy-main/README.md`, but this proxy targets **ChatGPT Codex** and does **not** implement all Antigravity features.
9
+ ## Table of Contents
10
+ - [How it works](#how-it-works)
11
+ - [Model Mapping](#model-mapping-claude-name--codex)
12
+ - [Features](#features)
13
+ - [Installation](#installation)
14
+ - [Quick Start](#quick-start)
15
+ - [Authentication](#authenticate-codex--chatgpt)
16
+ - [Claude Code Integration](#configure-claude-code-to-use-the-proxy)
17
+ - [Documentation](#documentation)
18
+ - [Legal](#legal)
19
+
10
20
 
11
21
  ---
12
22
 
@@ -17,13 +27,6 @@ It is designed primarily for **Claude Code CLI** (Anthropic-format client) while
17
27
  - “Claude” and “Anthropic” are trademarks of Anthropic PBC.
18
28
  - Software is provided **“as is”**, without warranty. You are responsible for complying with all applicable Terms of Service and Acceptable Use Policies.
19
29
 
20
- ### Legal
21
-
22
- - **Not affiliated with OpenAI or Anthropic.** This is an independent open-source project and is not endorsed by, sponsored by, or affiliated with OpenAI or Anthropic.
23
- - “ChatGPT”, “OpenAI”, and “Codex” are trademarks of their respective owners.
24
- - “Claude” and “Anthropic” are trademarks of Anthropic PBC.
25
- - Software is provided "as is", without warranty. You are responsible for complying with all applicable Terms of Service and Acceptable Use Policies.
26
-
27
30
  ---
28
31
 
29
32
  ## How it works
@@ -41,6 +44,10 @@ At a high level:
41
44
  2. The proxy maps the incoming “Claude model name” into a Codex model.
42
45
  3. The proxy converts formats as needed and returns responses back in Anthropic-compatible shapes, including streaming and tool calls.
43
46
 
47
+ Notes:
48
+ - Requests are proxied to the ChatGPT Codex backend.
49
+ - The `claude-haiku-4` (“Haiku”) lane can optionally route via **OpenRouter** to **MiniMax M2.5** / **GLM-5** (depending on server configuration).
50
+
44
51
  ---
45
52
 
46
53
  ## Model mapping (Claude name → Codex)
@@ -51,7 +58,7 @@ This proxy accepts Claude-style model IDs (what Claude Code expects) and maps th
51
58
  |---|---|---:|---|
52
59
  | `claude-sonnet-4-5` | **GPT-5.2 Codex** | Yes | Default “Sonnet” lane |
53
60
  | `claude-opus-4-5` | **GPT-5.3 Codex** | Yes | Default “Opus” lane |
54
- | `claude-haiku-4` | **GPT-5.2** (fast lane) | Yes | Lightweight / fast |
61
+ | `claude-haiku-4` | **GLM-5 / MiniMax M2.5** | No | Unlimited / No Auth required |
55
62
 
56
63
  ---
57
64
 
@@ -78,12 +85,26 @@ This proxy accepts Claude-style model IDs (what Claude Code expects) and maps th
78
85
 
79
86
  ---
80
87
 
81
- ## Quick start
88
+ ## Installation
82
89
 
83
90
  ```bash
91
+ # Run once (no install)
92
+ npx codex-claude-proxy@latest start
93
+
94
+ # Or install globally
95
+ npm i -g codex-claude-proxy
96
+ codex-claude-proxy start
97
+
98
+ # Or from source
99
+ git clone <repo-url>
100
+ cd codex-claude-proxy
84
101
  npm install
85
102
  npm start
103
+ ```
104
+
105
+ ## Quick start
86
106
 
107
+ ```bash
87
108
  # WebUI
88
109
  open http://localhost:8081
89
110
 
@@ -110,29 +131,47 @@ Codex-backed routes require at least one authenticated ChatGPT account.
110
131
  2. Open `http://localhost:8081`
111
132
  3. Go to Accounts and add an account via OAuth (or use the Manual mode for headless environments)
112
133
 
113
- ### Option B: CLI helpers
134
+ ### Option B: CLI
114
135
 
115
- Desktop (opens browser):
136
+ Add an account:
137
+
138
+ ```bash
139
+ # Opens browser
140
+ codex-claude-proxy accounts add
141
+
142
+ # Headless / VM (prints URL; you paste callback URL/code)
143
+ codex-claude-proxy accounts add --no-browser
144
+ ```
145
+
146
+ List accounts:
147
+
148
+ ```bash
149
+ codex-claude-proxy accounts list
150
+ ```
151
+
152
+ (If you’re running from source, use `npm start` in one terminal and run the CLI commands in another.)
153
+
154
+ Desktop (equivalent npm script):
116
155
 
117
156
  ```bash
118
157
  npm run accounts:add
119
158
  ```
120
159
 
121
- Headless / VM:
160
+ Headless / VM (equivalent npm script):
122
161
 
123
162
  ```bash
124
163
  npm run accounts:add:headless
125
- # Prints URL, you paste callback URL/code
126
164
  ```
127
165
 
128
166
  ### Option C: Import from Codex app
129
167
 
168
+ You can import an existing Codex app session via the server (see [Accounts](./docs/ACCOUNTS.md) for details).
169
+
130
170
  ```bash
131
171
  curl -X POST http://localhost:8081/accounts/import
132
- # Imports from ~/.codex/auth.json
133
172
  ```
134
173
 
135
- Details: `docs/OAUTH.md` and `docs/ACCOUNTS.md`.
174
+ Details: [OAuth](./docs/OAUTH.md) and [Accounts](./docs/ACCOUNTS.md).
136
175
 
137
176
  ---
138
177
 
@@ -144,7 +183,7 @@ Details: `docs/OAUTH.md` and `docs/ACCOUNTS.md`.
144
183
  curl -X POST http://localhost:8081/claude/config/proxy
145
184
  ```
146
185
 
147
- This updates `~/.claude/settings.json` to point Claude Code at `http://localhost:8081`.
186
+ This updates your local Claude Code settings to point at `http://localhost:8081`.
148
187
 
149
188
  ### Manual (env vars)
150
189
 
@@ -154,53 +193,42 @@ export ANTHROPIC_API_KEY=any-key
154
193
  claude
155
194
  ```
156
195
 
157
- More details: `docs/CLAUDE_INTEGRATION.md`.
196
+ More details: [Claude Code Integration](./docs/CLAUDE_INTEGRATION.md).
158
197
 
159
198
  ---
160
199
 
161
- ## API surface (high level)
200
+ ## Security
162
201
 
163
- - Anthropic-compatible:
164
- - `POST /v1/messages`
165
- - `GET /v1/models`
166
- - `POST /v1/messages/count_tokens`
202
+ - **Localhost Only**: CORS is strictly restricted to `localhost` and `127.0.0.1` to prevent unauthorized cross-origin requests from external websites.
203
+ - **Credential Safety**: Credentials are not allowed in cross-origin requests.
167
204
 
168
- - OpenAI-compatible:
169
- - `POST /v1/chat/completions`
205
+ ---
170
206
 
171
- - Accounts:
172
- - `GET /accounts`
173
- - `POST /accounts/add`
174
- - `POST /accounts/add/manual`
175
- - `POST /accounts/switch`
176
- - `POST /accounts/refresh`, `POST /accounts/refresh/all`
177
207
 
178
- - Logs:
179
- - `GET /api/logs`
180
- - `GET /api/logs/stream?history=true`
208
+ ## Documentation
181
209
 
182
- Full reference: `docs/API.md`.
210
+ - [**Architecture**](./docs/ARCHITECTURE.md) - Design and flow
211
+ - [**API Reference**](./docs/API.md) - Endpoints and model list
212
+ - [**Accounts**](./docs/ACCOUNTS.md) - Multi-account management
213
+ - [**OAuth**](./docs/OAUTH.md) - Authentication details
214
+ - [**OpenClaw**](./docs/OPENCLAW.md) - Using with messaging gateways
215
+ - [**Claude Code**](./docs/CLAUDE_INTEGRATION.md) - Detailed CLI setup guide
183
216
 
184
- ---
217
+ ## Legal
185
218
 
186
- ## Documentation
187
-
188
- - `docs/ARCHITECTURE.md` — system overview and data flow
189
- - `docs/API.md` — endpoints, formats, streaming
190
- - `docs/OAUTH.md` — OAuth PKCE implementation + headless flow
191
- - `docs/ACCOUNTS.md` — multi-account storage, switching, refresh, quota caching
192
- - `docs/CLAUDE_INTEGRATION.md` — how to use with Claude Code
193
- - `docs/OPENCLAW.md` — using the proxy behind OpenClaw
219
+ See [Legal Notices](./docs/legal.md).
194
220
 
195
221
  ---
196
222
 
197
- ## Notes / limitations
223
+ ## Credits
224
+
225
+ This project is based on insights and code from:
198
226
 
199
- - This project is intended for **local** or **trusted** environments.
200
- - Some behavior differs from Antigravity proxy; this repo focuses on ChatGPT Codex routing.
227
+ - [badrisnarayanan/antigravity-claude-proxy](https://github.com/badrisnarayanan/antigravity-claude-proxy)
228
+ - [1rgs/claude-code-proxy](https://github.com/1rgs/claude-code-proxy)
201
229
 
202
230
  ---
203
231
 
204
232
  ## License
205
233
 
206
- MIT
234
+ MIT
package/docs/API.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  ### Haiku Routing Settings
6
6
 
7
+ The `claude-haiku-4` (“Haiku”) lane can be configured to route via an alternate provider (e.g. **OpenRouter**) for supported models (e.g. **MiniMax M2.5** / **GLM-5**).
8
+
7
9
  ```bash
8
10
  GET /settings/haiku-model
9
11
 
@@ -29,8 +31,6 @@ Content-Type: application/json
29
31
  }
30
32
  ```
31
33
 
32
- `claude-haiku-4` requests are routed according to this server-wide setting.
33
-
34
34
  ### Chat Completions (OpenAI-compatible)
35
35
 
36
36
  ```bash
@@ -81,22 +81,21 @@ Content-Type: application/json
81
81
 
82
82
  ## Account Management
83
83
 
84
+ Common endpoints:
85
+
84
86
  | Endpoint | Method | Description |
85
87
  |----------|--------|-------------|
86
88
  | `/accounts` | GET | List all accounts |
87
89
  | `/accounts/status` | GET | Get account status summary |
88
90
  | `/accounts/add` | POST | Start OAuth flow (returns URL) |
89
91
  | `/accounts/switch` | POST | Switch active account |
90
- | `/accounts/:email` | DELETE | Remove account |
91
- | `/accounts/refresh` | POST | Refresh active account token |
92
- | `/accounts/refresh/all` | POST | Refresh all tokens |
93
- | `/accounts/:email/refresh` | POST | Refresh specific account |
94
- | `/accounts/import` | POST | Import from `~/.codex/auth.json` |
95
92
  | `/accounts/models` | GET | Get models for account |
96
93
  | `/accounts/quota` | GET | Get quota info |
97
94
  | `/accounts/quota/all` | GET | Refresh all quotas |
98
95
  | `/accounts/usage` | GET | Get usage stats |
99
96
 
97
+ (Additional maintenance endpoints exist for token refresh/import/removal; see the source if you need them.)
98
+
100
99
  ### Add Account
101
100
 
102
101
  ```bash
@@ -163,7 +162,7 @@ GET /health
163
162
  {
164
163
  "status": "ok",
165
164
  "total": 2,
166
- "active": "user@gmail.com",
165
+ "active": "active@example.com",
167
166
  "accounts": [...]
168
167
  }
169
168
  ```
@@ -4,22 +4,22 @@
4
4
 
5
5
  ```
6
6
  ┌──────────────────┐ ┌─────────────────────┐ ┌────────────────────────────┐
7
- │ Claude Code │────▶│ This Proxy Server │────▶│ ChatGPT Backend API
8
- │ (Anthropic │ │ (Anthropic format) │ │ (chatgpt.com/backend-api/
9
- │ API format) │ │ │ │ codex/responses)
7
+ │ Claude Code │────▶│ This Proxy Server │────▶│ ChatGPT Codex backend
8
+ │ (Anthropic │ │ (Anthropic format) │ │ (internal API)
9
+ │ API format) │ │ │ │
10
10
  └──────────────────┘ └─────────────────────┘ └────────────────────────────┘
11
11
 
12
12
 
13
13
  ┌─────────────────────┐
14
14
  │ Account Manager │
15
- │ (~/.codex-claude-
16
- proxy/accounts.json)
15
+ │ (local storage)
16
+
17
17
  └─────────────────────┘
18
18
  ```
19
19
 
20
20
  ## Key Discovery
21
21
 
22
- The endpoint `https://chatgpt.com/backend-api/codex/responses` bypasses Cloudflare protection when called with proper authentication headers.
22
+ This proxy forwards requests from Anthropic-compatible clients (like Claude Code) to the ChatGPT Codex backend, handling authentication, format conversion, and streaming.
23
23
 
24
24
  ## Project Structure
25
25
 
@@ -36,28 +36,22 @@ codex-claude-proxy/
36
36
  ├── public/
37
37
  │ ├── index.html
38
38
  │ ├── css/style.css
39
- │ └── js/app.js # Alpine UI logic
39
+ │ └── js/app.js # Web UI logic
40
40
  └── src/
41
- ├── index.js # Express server + routes
42
- ├── oauth.js # OAuth 2.0 with PKCE
43
- ├── account-manager.js # Multi-account storage
44
- ├── format-converter.js # Anthropic OpenAI format
45
- ├── response-streamer.js # SSE streaming
46
- ├── direct-api.js # ChatGPT API client
47
- ├── kilo-api.js # Alternate upstream client
48
- ├── kilo-format-converter.js # Anthropic ↔ OpenAI Chat conversion
49
- ├── kilo-streamer.js # Streaming adapter
50
- ├── model-api.js # Models & quota
51
- ├── server-settings.js # Server-wide settings persistence
52
- └── claude-config.js # Claude CLI config
41
+ ├── index.js # App entrypoint
42
+ ├── server.js # Express server setup
43
+ ├── routes/api-routes.js # API route registrations
44
+ └── ... # OAuth, accounts, converters, upstream clients
53
45
  ```
54
46
 
47
+ (See the `src/` directory for the full implementation; this doc focuses on the high-level shape.)
48
+
55
49
  ## Module Responsibilities
56
50
 
57
51
  | File | Purpose |
58
52
  |------|---------|
59
53
  | `index.js` | Entry point (starts server) |
60
- | `server.js` | Express server, routes, request handling |
54
+ | `server.js` | Express server, routes, request handling (CORS restricted) |
61
55
  | `routes/api-routes.js` | API route registrations (mounted by server) |
62
56
  | `oauth.js` | OAuth 2.0 PKCE flow, token exchange |
63
57
  | `account-manager.js` | Account persistence, switching, token refresh |
@@ -69,7 +63,7 @@ codex-claude-proxy/
69
63
  | `kilo-streamer.js` | Streaming adapter |
70
64
  | `server-settings.js` | Server-wide settings persistence |
71
65
  | `model-api.js` | Fetch models, usage, quota |
72
- | `claude-config.js` | Read/write `~/.claude/settings.json` |
66
+ | `claude-config.js` | Read/write Claude Code settings |
73
67
 
74
68
  ## Data Flow
75
69
 
@@ -125,9 +119,4 @@ Haiku routing is controlled by a server-wide setting (`/settings/haiku-model`).
125
119
 
126
120
  ## Data Storage
127
121
 
128
- | Location | Contents |
129
- |----------|----------|
130
- | `~/.codex-claude-proxy/accounts.json` | Account registry, active account |
131
- | `~/.codex-claude-proxy/accounts/<email>/auth.json` | Per-account tokens |
132
- | `~/.claude/settings.json` | Claude CLI config (we update this) |
133
- | `~/.codex/auth.json` | Codex app auth (read for import) |
122
+ Account and configuration files are stored under your home directory (platform-specific). See `docs/ACCOUNTS.md` and `docs/CLAUDE_INTEGRATION.md` for details.
package/docs/OAUTH.md CHANGED
@@ -1,201 +1,21 @@
1
1
  # OAuth Implementation
2
2
 
3
- ## Configuration
3
+ This proxy uses **OAuth 2.0 with PKCE** for secure authentication with ChatGPT.
4
4
 
5
- ```javascript
6
- OAUTH_CONFIG = {
7
- clientId: 'app_EMoamEEZ73f0CkXaXp7hrann',
8
- authUrl: 'https://auth.openai.com/oauth/authorize',
9
- tokenUrl: 'https://auth.openai.com/oauth/token',
10
- scopes: ['openid', 'profile', 'email', 'offline_access'],
11
- callbackPort: 1455,
12
- callbackPath: '/auth/callback'
13
- }
14
- ```
5
+ ## Quick Start
6
+ - **WebUI (Recommended)**: Open `http://localhost:8081` → **Add Account** → **Connect**.
7
+ - **Headless**: Use `codex-claude-proxy accounts add --no-browser`.
15
8
 
16
- ## Authorization Methods
9
+ ## OAuth Config
10
+ - **Client ID**: `app_EMoamEEZ73f0CkXaXp7hrann`
11
+ - **Auth URL**: `https://auth.openai.com/oauth/authorize`
12
+ - **Redirect**: `http://localhost:1455/auth/callback`
17
13
 
18
- ### Method A: WebUI (Recommended)
19
-
20
- 1. Open `http://localhost:8081` in your browser
21
- 2. Click **Add Account** **Connect via OAuth**
22
- 3. Complete authorization in the popup
23
-
24
- ### Method B: WebUI Manual Mode (Headless)
25
-
26
- For servers without a browser:
27
-
28
- 1. Click **Add Account** → **Manual Authorization**
29
- 2. Copy the OAuth URL
30
- 3. Open URL on another device (your local machine)
31
- 4. Complete authorization
32
- 5. Copy the callback URL or authorization code
33
- 6. Paste back in the WebUI
34
-
35
- ### Method C: CLI (Desktop)
36
-
37
- ```bash
38
- npm run accounts:add
39
- # Opens browser for OAuth
40
- ```
41
-
42
- ### Method D: CLI Headless Mode
43
-
44
- ```bash
45
- npm run accounts:add:headless
46
- # Prints URL, you paste the callback URL/code
47
- ```
48
-
49
- ### Method E: API (Headless)
50
-
51
- ```bash
52
- # 1. Get OAuth URL and verifier
53
- curl -X POST http://localhost:8081/accounts/add
54
-
55
- # Response includes oauth_url, verifier, state
56
-
57
- # 2. Open URL on another device, complete auth
58
-
59
- # 3. Submit authorization code
60
- curl -X POST http://localhost:8081/accounts/add/manual \
61
- -H "Content-Type: application/json" \
62
- -d '{"code":"<code_or_callback_url>","verifier":"<verifier_from_step_1>"}'
63
- ```
64
-
65
- ## PKCE Flow
66
-
67
- ### 1. Generate PKCE Challenge
68
-
69
- ```javascript
70
- // Verifier: 32 random bytes, base64url encoded
71
- verifier = crypto.randomBytes(32).toString('base64url')
72
-
73
- // Challenge: SHA256 hash of verifier
74
- challenge = sha256(verifier).base64url
75
- ```
76
-
77
- ### 2. Authorization URL
78
-
79
- ```
80
- https://auth.openai.com/oauth/authorize?
81
- response_type=code
82
- &client_id=app_EMoamEEZ73f0CkXaXp7hrann
83
- &redirect_uri=http://localhost:1455/auth/callback
84
- &scope=openid profile email offline_access
85
- &code_challenge=<challenge>
86
- &code_challenge_method=S256
87
- &state=<random_state>
88
- &prompt=login
89
- &max_age=0
90
- ```
91
-
92
- **Key parameters for multi-account:**
93
- - `prompt=login` - Forces login screen (ignores session cookies)
94
- - `max_age=0` - Forces re-authentication
95
-
96
- ### 3. Token Exchange
97
-
98
- ```javascript
99
- POST https://auth.openai.com/oauth/token
100
- Content-Type: application/x-www-form-urlencoded
101
-
102
- grant_type=authorization_code
103
- &code=<authorization_code>
104
- &redirect_uri=http://localhost:1455/auth/callback
105
- &client_id=app_EMoamEEZ73f0CkXaXp7hrann
106
- &code_verifier=<verifier>
107
- ```
108
-
109
- ### 4. Token Response
110
-
111
- ```json
112
- {
113
- "access_token": "eyJhbGciOiJSUzI1NiIs...",
114
- "refresh_token": "rt_WpTMn1...",
115
- "id_token": "eyJhbGciOiJSUzI1NiIs...",
116
- "expires_in": 3600,
117
- "token_type": "Bearer"
118
- }
119
- ```
120
-
121
- ## JWT Claims
122
-
123
- Decoded `access_token` payload:
124
-
125
- ```json
126
- {
127
- "https://api.openai.com/auth": {
128
- "chatgpt_account_id": "d41e9636-...",
129
- "chatgpt_plan_type": "plus",
130
- "chatgpt_user_id": "user-..."
131
- },
132
- "https://api.openai.com/profile": {
133
- "email": "user@gmail.com",
134
- "email_verified": true
135
- },
136
- "exp": 1770886178
137
- }
138
- ```
139
-
140
- ## Token Refresh
141
-
142
- ```javascript
143
- POST https://auth.openai.com/oauth/token
144
- Content-Type: application/x-www-form-urlencoded
145
-
146
- grant_type=refresh_token
147
- &refresh_token=<refresh_token>
148
- &client_id=app_EMoamEEZ73f0CkXaXp7hrann
149
- ```
150
-
151
- ## Auto-Refresh
152
-
153
- - Tokens auto-refresh every **55 minutes**
154
- - Proactive refresh before API calls if expiring within 5 minutes
155
- - Startup refresh 2 seconds after server start
156
-
157
- ## Web Flow vs CLI Flow
158
-
159
- ### Web Flow (WebUI)
160
-
161
- 1. `POST /accounts/add` → returns OAuth URL
162
- 2. Frontend opens popup with URL
163
- 3. User authenticates in popup
164
- 4. Browser redirects to callback
165
- 5. Server handles callback, stores tokens
166
- 6. Popup notifies parent via `postMessage`
167
-
168
- ### CLI Flow
169
-
170
- 1. `POST /accounts/add` → starts callback server on port 1455
171
- 2. Server opens browser with OAuth URL
172
- 3. User authenticates
173
- 4. Browser redirects to callback
174
- 5. Server exchanges code for tokens
175
-
176
- ## Multi-Account Support
177
-
178
- The key to multi-account support is forcing a fresh login each time:
179
-
180
- 1. `prompt=login` - Shows login screen even if already logged in
181
- 2. `max_age=0` - Requires re-authentication
182
- 3. Each account stored with unique email as identifier
14
+ ## Features
15
+ - **Auto-Refresh**: Tokens refresh every 55 minutes.
16
+ - **Multi-Account**: Uses `prompt=login` to force account switching.
17
+ - **Headless Support**: Manual code entry for servers without browsers.
183
18
 
184
19
  ## Troubleshooting
185
-
186
- ### OAuth returns existing account
187
-
188
- - Browser may have aggressive cookie caching
189
- - Try manual logout: https://auth.openai.com/logout
190
- - Clear browser cookies for auth.openai.com
191
-
192
- ### Callback timeout
193
-
194
- - Default timeout: 2 minutes
195
- - Ensure port 1455 is available
196
- - Check firewall isn't blocking localhost
197
-
198
- ### Token refresh fails
199
-
200
- - Refresh token may have expired
201
- - Re-add the account via WebUI
20
+ - **Existing Account**: If it keeps picking the same account, logout at `auth.openai.com` or clear cookies.
21
+ - **Port 1455**: Must be available for the CLI callback.
package/docs/OPENCLAW.md CHANGED
@@ -1,338 +1,33 @@
1
1
  # Using with OpenClaw
2
2
 
3
- [OpenClaw](https://docs.openclaw.ai/) (formerly ClawdBot/Moltbot) is an AI agent gateway that connects to messaging apps like Telegram, WhatsApp, Discord, Slack, and iMessage. You can configure it to use this proxy for GPT-5 Codex models.
3
+ [OpenClaw](https://docs.openclaw.ai/) is an AI agent gateway. This proxy provides the Anthropic-compatible API it needs.
4
4
 
5
- ## What is OpenClaw?
5
+ ## Quick Integration
6
6
 
7
- OpenClaw acts as a bridge between messaging platforms and AI models:
8
-
9
- ```
10
- ┌──────────────┐ ┌─────────────┐ ┌─────────────────────┐ ┌──────────────┐
11
- │ Telegram │────▶│ │ │ Codex-Claude-Proxy │ │ ChatGPT │
12
- │ WhatsApp │ │ OpenClaw │────▶│ (Anthropic API) │────▶│ Codex API │
13
- │ Discord │ │ Gateway │ │ │ │ │
14
- │ Slack │ │ │ │ │ │ │
15
- │ iMessage │ │ │ │ │ │ │
16
- └──────────────┘ └─────────────┘ └─────────────────────┘ └──────────────┘
17
- ```
18
-
19
- OpenClaw expects providers to expose an Anthropic-compatible or OpenAI-compatible API, which this proxy provides.
20
-
21
- ## Prerequisites
22
-
23
- - OpenClaw installed:
24
- ```bash
25
- npm install -g openclaw@latest
26
- ```
27
- - Codex-Claude-Proxy running on port 8081 (or your configured port)
28
- - At least one ChatGPT account linked to the proxy
29
-
30
- ## Configure OpenClaw
31
-
32
- Edit your OpenClaw config file:
33
- - **macOS/Linux**: `~/.openclaw/openclaw.json`
34
- - **Windows**: `%USERPROFILE%\.openclaw\openclaw.json`
35
-
36
- ### Basic Configuration
37
-
38
- ```json
39
- {
40
- "models": {
41
- "mode": "merge",
42
- "providers": {
7
+ 1. **Add Provider** to `~/.openclaw/openclaw.json`:
8
+ ```json
9
+ {
43
10
  "codex-proxy": {
44
11
  "baseUrl": "http://127.0.0.1:8081",
45
12
  "apiKey": "test",
46
13
  "api": "anthropic-messages",
47
14
  "models": [
48
- {
49
- "id": "gpt-5.3-codex",
50
- "name": "GPT-5.3 Codex",
51
- "reasoning": true,
52
- "input": ["text", "image"],
53
- "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
54
- "contextWindow": 200000,
55
- "maxTokens": 128000
56
- },
57
- {
58
- "id": "gpt-5.2-codex",
59
- "name": "GPT-5.2 Codex",
60
- "reasoning": true,
61
- "input": ["text", "image"],
62
- "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
63
- "contextWindow": 200000,
64
- "maxTokens": 128000
65
- },
66
- {
67
- "id": "gpt-5.2",
68
- "name": "GPT-5.2",
69
- "reasoning": false,
70
- "input": ["text", "image"],
71
- "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
72
- "contextWindow": 128000,
73
- "maxTokens": 16384
74
- }
15
+ { "id": "claude-sonnet-4-5", "name": "GPT-5.2 Codex" },
16
+ { "id": "claude-opus-4-5", "name": "GPT-5.3 Codex" }
75
17
  ]
76
18
  }
77
19
  }
78
- },
79
- "agents": {
80
- "defaults": {
81
- "model": {
82
- "primary": "codex-proxy/gpt-5.2-codex",
83
- "fallbacks": ["codex-proxy/gpt-5.2"]
84
- },
85
- "models": {
86
- "codex-proxy/gpt-5.2-codex": {}
87
- }
88
- }
89
- }
90
- }
91
- ```
92
-
93
- ### Using Claude Model Names (Auto-Mapped)
94
-
95
- The proxy automatically maps Claude model names to Codex equivalents. You can use familiar names:
96
-
97
- ```json
98
- {
99
- "models": {
100
- "mode": "merge",
101
- "providers": {
102
- "codex-proxy": {
103
- "baseUrl": "http://127.0.0.1:8081",
104
- "apiKey": "test",
105
- "api": "anthropic-messages",
106
- "models": [
107
- {
108
- "id": "claude-opus-4-5",
109
- "name": "Claude Opus 4.5 (GPT-5.3 Codex)",
110
- "reasoning": true,
111
- "input": ["text", "image"],
112
- "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
113
- "contextWindow": 200000,
114
- "maxTokens": 128000
115
- },
116
- {
117
- "id": "claude-sonnet-4-5",
118
- "name": "Claude Sonnet 4.5 (GPT-5.2 Codex)",
119
- "reasoning": true,
120
- "input": ["text", "image"],
121
- "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
122
- "contextWindow": 200000,
123
- "maxTokens": 128000
124
- },
125
- {
126
- "id": "claude-haiku-4",
127
- "name": "Claude Haiku 4 (GPT-5.2)",
128
- "reasoning": false,
129
- "input": ["text", "image"],
130
- "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
131
- "contextWindow": 128000,
132
- "maxTokens": 16384
133
- }
134
- ]
135
- }
136
- }
137
- },
138
- "agents": {
139
- "defaults": {
140
- "model": {
141
- "primary": "codex-proxy/claude-sonnet-4-5",
142
- "fallbacks": ["codex-proxy/claude-haiku-4"]
143
- }
144
- }
145
- }
146
- }
147
- ```
148
-
149
- > **Note**: The `reasoning` field indicates whether the model supports extended thinking/reasoning. Set to `true` for Codex models which excel at complex reasoning tasks.
150
-
151
- ## Model Reference
152
-
153
- | Proxy Model | Mapped From | Best For |
154
- |-------------|-------------|----------|
155
- | `gpt-5.3-codex` | `claude-opus-4-5` | Most capable, complex coding tasks |
156
- | `gpt-5.2-codex` | `claude-sonnet-4-5` | Balanced performance, recommended default |
157
- | `gpt-5.2` | `claude-haiku-4` | Fast responses, simpler tasks |
158
-
159
- ## Start Both Services
160
-
161
- ```bash
162
- # Terminal 1: Start the proxy
163
- cd codex-claude-proxy
164
- npm start
165
-
166
- # Terminal 2: Start OpenClaw gateway
167
- openclaw gateway
168
- ```
169
-
170
- ## Verify Configuration
171
-
172
- ```bash
173
- # Check available models
174
- openclaw models list
175
-
176
- # Check gateway status
177
- openclaw status
178
- ```
179
-
180
- You should see models prefixed with `codex-proxy/` in the list.
181
-
182
- ## Switch Models
183
-
184
- To change the default model:
185
-
186
- ```bash
187
- openclaw models set codex-proxy/gpt-5.3-codex
188
- ```
189
-
190
- Or edit the `model.primary` field in your config file.
191
-
192
- ## API Compatibility
193
-
194
- The proxy exposes an **Anthropic Messages API** compatible interface:
195
-
196
- | Endpoint | Description |
197
- |----------|-------------|
198
- | `POST /v1/messages` | Main chat endpoint |
199
- | `GET /v1/models` | List available models |
200
- | `POST /v1/messages/count_tokens` | Token counting |
201
-
202
- OpenClaw's `api: "anthropic-messages"` setting tells it to use the Anthropic format, which this proxy fully supports including:
203
- - Streaming responses (SSE)
204
- - Tool/function calling
205
- - Multi-turn conversations
206
- - Image inputs
207
- - System prompts
208
-
209
- ## Multi-Account Support
210
-
211
- The proxy supports multiple ChatGPT accounts with automatic switching:
212
-
213
- ```bash
214
- # Add accounts via Web UI at http://localhost:8081
215
- # Or via CLI:
216
- npm run accounts:add
217
-
218
- # List accounts
219
- curl http://localhost:8081/accounts
220
- ```
221
-
222
- When one account hits rate limits, the proxy can automatically switch to another. This provides seamless continuity for OpenClaw users.
223
-
224
- ## Advanced Configuration
225
-
226
- ### Custom Port
227
-
228
- If running the proxy on a different port:
229
-
230
- ```json
231
- {
232
- "models": {
233
- "providers": {
234
- "codex-proxy": {
235
- "baseUrl": "http://127.0.0.1:3000",
236
- ...
237
- }
238
- }
239
- }
240
- }
241
- ```
242
-
243
- ### VPS/Remote Server
244
-
245
- When running on a VPS, bind the proxy to localhost only:
246
-
247
- ```bash
248
- HOST=127.0.0.1 PORT=8081 npm start
249
- ```
250
-
251
- Then use SSH port forwarding on your local machine:
252
-
253
- ```bash
254
- ssh -L 8081:127.0.0.1:8081 user@your-vps
255
- ```
256
-
257
- Configure OpenClaw to use the local tunnel:
20
+ ```
21
+ 2. **Set Primary Model**:
22
+ ```bash
23
+ openclaw models set codex-proxy/claude-sonnet-4-5
24
+ ```
258
25
 
259
- ```json
260
- {
261
- "baseUrl": "http://127.0.0.1:8081"
262
- }
263
- ```
264
-
265
- ### Authentication
266
-
267
- To protect the proxy with an API key:
268
-
269
- ```bash
270
- API_KEY=your-secret-key npm start
271
- ```
272
-
273
- Update OpenClaw config:
274
-
275
- ```json
276
- {
277
- "providers": {
278
- "codex-proxy": {
279
- "baseUrl": "http://127.0.0.1:8081",
280
- "apiKey": "your-secret-key",
281
- ...
282
- }
283
- }
284
- }
285
- ```
26
+ ## Key Benefits
27
+ - **Multi-Account**: Proxy handles rate limits by switching ChatGPT accounts.
28
+ - **SSE Streaming**: Full real-time response support.
29
+ - **Tool Calling**: Native support for agentic workflows.
286
30
 
287
31
  ## Troubleshooting
288
-
289
- ### Connection Refused
290
-
291
- Ensure the proxy is running:
292
- ```bash
293
- curl http://127.0.0.1:8081/health
294
- ```
295
-
296
- ### Models Not Showing
297
-
298
- 1. Verify config file is valid JSON
299
- 2. Check `mode` is set to `"merge"`
300
- 3. Restart OpenClaw after config changes:
301
- ```bash
302
- openclaw gateway restart
303
- ```
304
-
305
- ### Rate Limiting
306
-
307
- If you see rate limit errors:
308
- 1. Add more ChatGPT accounts to the proxy
309
- 2. The proxy will auto-switch between accounts
310
- 3. Check `/accounts` endpoint for account status
311
-
312
- ### Use 127.0.0.1 Not localhost
313
-
314
- Always use `127.0.0.1` instead of `localhost` in `baseUrl`:
315
- - Avoids DNS resolution issues
316
- - Explicitly stays on loopback interface
317
- - Prevents accidental exposure on VPS
318
-
319
- ## Comparison: Codex Proxy vs Antigravity Proxy
320
-
321
- | Feature | Codex-Claude-Proxy | Antigravity Proxy |
322
- |---------|-------------------|-------------------|
323
- | Backend API | ChatGPT Codex API | Google Cloud Code API |
324
- | Models | GPT-5.2/5.3 Codex | Gemini 3, Claude (via Google) |
325
- | Auth | ChatGPT OAuth | Google OAuth |
326
- | Account Source | ChatGPT accounts | Google accounts |
327
- | Rate Limits | ChatGPT limits | Google Cloud quotas |
328
- | Best For | Codex-native access | Google Cloud users |
329
-
330
- Both proxies expose the same Anthropic-compatible API, so OpenClaw configuration is nearly identical.
331
-
332
- ## Further Reading
333
-
334
- - [OpenClaw Documentation](https://docs.openclaw.ai/)
335
- - [OpenClaw Configuration Reference](https://docs.openclaw.ai/gateway/configuration)
336
- - [Proxy API Reference](./API.md)
337
- - [Account Management](./ACCOUNTS.md)
338
- - [OAuth Setup](./OAUTH.md)
32
+ - Use `127.0.0.1` instead of `localhost`.
33
+ - Verify proxy health: `curl http://127.0.0.1:8081/health`.
package/docs/legal.md ADDED
@@ -0,0 +1,11 @@
1
+ # Legal
2
+
3
+ - **Not affiliated with OpenAI or Anthropic.** This is an independent open-source project and is not endorsed by, sponsored by, or affiliated with OpenAI or Anthropic.
4
+
5
+ - "ChatGPT", "OpenAI", and "Codex" are trademarks of OpenAI.
6
+
7
+ - "Claude" and "Anthropic" are trademarks of Anthropic PBC.
8
+
9
+ - "Kilo" and "Kilo Search" are trademarks of their respective owners.
10
+
11
+ - Software is provided "as is", without warranty. You are responsible for complying with all applicable Terms of Service and Acceptable Use Policies.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-claude-proxy",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Multi-account proxy server for OpenAI Codex CLI with Claude API compatibility",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/public/js/app.js CHANGED
@@ -1,6 +1,6 @@
1
1
  document.addEventListener('alpine:init', () => {
2
2
  Alpine.data('app', () => ({
3
- version: '2.0.0',
3
+ version: '1.0.2',
4
4
  connectionStatus: 'connecting',
5
5
  activeTab: 'dashboard',
6
6
  sidebarOpen: window.innerWidth >= 1024,
@@ -26,10 +26,10 @@ const tokenCache = new Map();
26
26
 
27
27
  function ensureConfigDir() {
28
28
  if (!existsSync(CONFIG_DIR)) {
29
- mkdirSync(CONFIG_DIR, { recursive: true });
29
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
30
30
  }
31
31
  if (!existsSync(ACCOUNTS_DIR)) {
32
- mkdirSync(ACCOUNTS_DIR, { recursive: true });
32
+ mkdirSync(ACCOUNTS_DIR, { recursive: true, mode: 0o700 });
33
33
  }
34
34
  }
35
35
 
@@ -64,7 +64,7 @@ function loadAccounts() {
64
64
 
65
65
  function saveAccounts(data) {
66
66
  ensureConfigDir();
67
- writeFileSync(ACCOUNTS_FILE, JSON.stringify(data, null, 2));
67
+ writeFileSync(ACCOUNTS_FILE, JSON.stringify(data, null, 2), { mode: 0o600 });
68
68
  }
69
69
 
70
70
  function getActiveAccount() {
@@ -80,7 +80,7 @@ function updateAccountAuth(account) {
80
80
  const authFile = getAccountAuthFile(account.email);
81
81
 
82
82
  if (!existsSync(accountDir)) {
83
- mkdirSync(accountDir, { recursive: true });
83
+ mkdirSync(accountDir, { recursive: true, mode: 0o700 });
84
84
  }
85
85
 
86
86
  const authData = {
@@ -96,7 +96,7 @@ function updateAccountAuth(account) {
96
96
  };
97
97
 
98
98
  try {
99
- writeFileSync(authFile, JSON.stringify(authData, null, 2));
99
+ writeFileSync(authFile, JSON.stringify(authData, null, 2), { mode: 0o600 });
100
100
  console.log(`[AccountManager] Updated auth for: ${account.email}`);
101
101
  } catch (e) {
102
102
  console.error('[AccountManager] Failed to update auth:', e.message);
@@ -60,13 +60,13 @@ export async function updateClaudeConfig(updates) {
60
60
 
61
61
  const configDir = path.dirname(configPath);
62
62
  try {
63
- await fs.mkdir(configDir, { recursive: true });
63
+ await fs.mkdir(configDir, { recursive: true, mode: 0o700 });
64
64
  } catch (error) {
65
65
  // Ignore if exists
66
66
  }
67
67
 
68
68
  try {
69
- await fs.writeFile(configPath, JSON.stringify(newConfig, null, 2), 'utf8');
69
+ await fs.writeFile(configPath, JSON.stringify(newConfig, null, 2), { encoding: 'utf8', mode: 0o600 });
70
70
  console.log(`[ClaudeConfig] Updated config at ${configPath}`);
71
71
  return newConfig;
72
72
  } catch (error) {
@@ -39,9 +39,9 @@ function saveAccounts(data) {
39
39
  try {
40
40
  const dir = dirname(ACCOUNTS_FILE);
41
41
  if (!existsSync(dir)) {
42
- mkdirSync(dir, { recursive: true });
42
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
43
43
  }
44
- writeFileSync(ACCOUNTS_FILE, JSON.stringify(data, null, 2));
44
+ writeFileSync(ACCOUNTS_FILE, JSON.stringify(data, null, 2), { mode: 0o600 });
45
45
  console.log(`\n✓ Saved ${data.accounts.length} account(s) to ${ACCOUNTS_FILE}`);
46
46
  } catch (error) {
47
47
  console.error('Error saving accounts:', error.message);
package/src/index.js CHANGED
@@ -13,7 +13,7 @@ startServer({ port: PORT });
13
13
 
14
14
  console.log(`
15
15
  ╔══════════════════════════════════════════════════════════════╗
16
- ║ Codex Claude Proxy v2.0.0
16
+ ║ Codex Claude Proxy v1.0.2
17
17
  ║ (Direct API Mode) ║
18
18
  ╠══════════════════════════════════════════════════════════════╣
19
19
  ║ Server: http://localhost:${PORT} ║
@@ -10,7 +10,7 @@ const DEFAULT_SETTINGS = {
10
10
 
11
11
  function ensureConfigDir() {
12
12
  if (!existsSync(CONFIG_DIR)) {
13
- mkdirSync(CONFIG_DIR, { recursive: true });
13
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
14
14
  }
15
15
  }
16
16
 
@@ -35,7 +35,7 @@ export function setServerSettings(patch = {}) {
35
35
  const next = { ...current, ...patch };
36
36
 
37
37
  ensureConfigDir();
38
- writeFileSync(SETTINGS_FILE, JSON.stringify(next, null, 2));
38
+ writeFileSync(SETTINGS_FILE, JSON.stringify(next, null, 2), { mode: 0o600 });
39
39
  return next;
40
40
  }
41
41
 
package/src/server.js CHANGED
@@ -14,7 +14,34 @@ export function createServer({ port }) {
14
14
  startAutoRefresh();
15
15
 
16
16
  const app = express();
17
- app.use(cors());
17
+ app.disable('x-powered-by');
18
+
19
+ // High-level request logging
20
+ app.use((req, res, next) => {
21
+ const start = Date.now();
22
+ res.on('finish', () => {
23
+ const duration = Date.now() - start;
24
+ const msg = `[${req.method}] ${req.originalUrl} ${res.statusCode} (${duration}ms)`;
25
+ if (res.statusCode >= 400) {
26
+ console.log(`\x1b[31m${msg}\x1b[0m`); // Red for error
27
+ } else if (req.originalUrl !== '/health') { // Skip health check logs to reduce noise
28
+ console.log(`\x1b[36m${msg}\x1b[0m`); // Cyan for success
29
+ }
30
+ });
31
+ next();
32
+ });
33
+
34
+ app.use(cors({
35
+ origin: [
36
+ `http://localhost:${port}`,
37
+ `http://127.0.0.1:${port}`,
38
+ 'http://localhost',
39
+ 'http://127.0.0.1'
40
+ ],
41
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
42
+ allowedHeaders: ['Content-Type', 'Authorization'],
43
+ credentials: false
44
+ }));
18
45
  app.use(express.json({ limit: '10mb' }));
19
46
 
20
47
  registerApiRoutes(app, { port });