@pikoloo/codex-proxy 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +199 -0
- package/bin/cli.js +118 -0
- package/docs/ACCOUNTS.md +202 -0
- package/docs/API.md +289 -0
- package/docs/ARCHITECTURE.md +129 -0
- package/docs/CLAUDE_INTEGRATION.md +163 -0
- package/docs/OAUTH.md +85 -0
- package/docs/OPENCLAW.md +34 -0
- package/docs/legal.md +11 -0
- package/images/dashboard-screenshot.png +0 -0
- package/images/demo-screenshot.png +0 -0
- package/images/f757093f-507b-4453-994e-f8275f8b07a9.png +0 -0
- package/package.json +61 -0
- package/public/css/style.css +1502 -0
- package/public/index.html +827 -0
- package/public/js/app.js +601 -0
- package/src/account-manager.js +528 -0
- package/src/account-rotation/index.js +93 -0
- package/src/account-rotation/rate-limits.js +293 -0
- package/src/account-rotation/strategies/base-strategy.js +48 -0
- package/src/account-rotation/strategies/index.js +31 -0
- package/src/account-rotation/strategies/round-robin-strategy.js +42 -0
- package/src/account-rotation/strategies/sticky-strategy.js +97 -0
- package/src/claude-config.js +153 -0
- package/src/cli/accounts.js +557 -0
- package/src/direct-api.js +164 -0
- package/src/format-converter.js +420 -0
- package/src/index.js +46 -0
- package/src/kilo-api.js +68 -0
- package/src/kilo-format-converter.js +285 -0
- package/src/kilo-models.js +103 -0
- package/src/kilo-streamer.js +243 -0
- package/src/middleware/credentials.js +116 -0
- package/src/middleware/sse.js +96 -0
- package/src/model-api.js +189 -0
- package/src/model-mapper.js +157 -0
- package/src/oauth.js +666 -0
- package/src/response-streamer.js +409 -0
- package/src/routes/accounts-route.js +332 -0
- package/src/routes/api-routes.js +98 -0
- package/src/routes/chat-route.js +229 -0
- package/src/routes/claude-config-route.js +121 -0
- package/src/routes/logs-route.js +43 -0
- package/src/routes/messages-route.js +203 -0
- package/src/routes/models-route.js +119 -0
- package/src/routes/settings-route.js +143 -0
- package/src/security.js +142 -0
- package/src/server-settings.js +56 -0
- package/src/server.js +58 -0
- package/src/signature-cache.js +106 -0
- package/src/thinking-utils.js +312 -0
- package/src/utils/logger.js +156 -0
package/docs/API.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
## Main Endpoints
|
|
4
|
+
|
|
5
|
+
### Kilo Routing Settings
|
|
6
|
+
|
|
7
|
+
The explicit `kilo` model route can be configured to use an alternate provider target such as **MiniMax M2.5** or **Kimi K2.5** when `CODEX_CLAUDE_PROXY_ENABLE_KILO=true` is set. Claude Haiku aliases use OpenAI by default.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
GET /settings/haiku-model
|
|
11
|
+
|
|
12
|
+
# Response
|
|
13
|
+
{
|
|
14
|
+
"success": true,
|
|
15
|
+
"haikuKiloModel": "minimax/minimax-m2.5:free",
|
|
16
|
+
"kiloEnabled": false
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
POST /settings/haiku-model
|
|
22
|
+
Content-Type: application/json
|
|
23
|
+
|
|
24
|
+
{
|
|
25
|
+
"haikuKiloModel": "minimax-2.5"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Response
|
|
29
|
+
{
|
|
30
|
+
"success": true,
|
|
31
|
+
"haikuKiloModel": "minimax-2.5",
|
|
32
|
+
"kiloEnabled": true
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Chat Completions (OpenAI-compatible)
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
POST /v1/chat/completions
|
|
40
|
+
Content-Type: application/json
|
|
41
|
+
|
|
42
|
+
{
|
|
43
|
+
"model": "gpt-5.5",
|
|
44
|
+
"messages": [{"role": "user", "content": "Hello"}],
|
|
45
|
+
"tools": [...],
|
|
46
|
+
"stream": true
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Messages (Anthropic-compatible)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
POST /v1/messages
|
|
54
|
+
Content-Type: application/json
|
|
55
|
+
|
|
56
|
+
{
|
|
57
|
+
"model": "claude-sonnet-4-5",
|
|
58
|
+
"max_tokens": 1024,
|
|
59
|
+
"system": "You are helpful.",
|
|
60
|
+
"messages": [{"role": "user", "content": "Hello"}],
|
|
61
|
+
"tools": [...],
|
|
62
|
+
"stream": true
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Models
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
GET /v1/models
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Token Counting
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
POST /v1/messages/count_tokens
|
|
76
|
+
Content-Type: application/json
|
|
77
|
+
|
|
78
|
+
{
|
|
79
|
+
"messages": [...],
|
|
80
|
+
"tools": [...]
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Account Management
|
|
85
|
+
|
|
86
|
+
Common endpoints:
|
|
87
|
+
|
|
88
|
+
| Endpoint | Method | Description |
|
|
89
|
+
|----------|--------|-------------|
|
|
90
|
+
| `/accounts` | GET | List all accounts |
|
|
91
|
+
| `/accounts/status` | GET | Get account status summary |
|
|
92
|
+
| `/accounts/add` | POST | Start OAuth flow (returns URL) |
|
|
93
|
+
| `/accounts/switch` | POST | Switch active account |
|
|
94
|
+
| `/accounts/models` | GET | Get models for account |
|
|
95
|
+
| `/accounts/quota` | GET | Get quota info |
|
|
96
|
+
| `/accounts/quota/all` | GET | Refresh all quotas |
|
|
97
|
+
| `/accounts/usage` | GET | Get usage stats |
|
|
98
|
+
|
|
99
|
+
(Additional maintenance endpoints exist for token refresh/import/removal; see the source if you need them.)
|
|
100
|
+
|
|
101
|
+
### Add Account
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
POST /accounts/add
|
|
105
|
+
Content-Type: application/json
|
|
106
|
+
|
|
107
|
+
# Optional: specify callback port
|
|
108
|
+
{"port": 1455}
|
|
109
|
+
|
|
110
|
+
# Response
|
|
111
|
+
{
|
|
112
|
+
"status": "oauth_url",
|
|
113
|
+
"oauth_url": "https://auth.openai.com/oauth/authorize?...",
|
|
114
|
+
"callback_port": 1455
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Account Selection Strategy
|
|
119
|
+
|
|
120
|
+
Requests use the active account only by default. Account rotation settings are inert unless `CODEX_CLAUDE_PROXY_ENABLE_MULTI_ACCOUNT_ROTATION=true` is set.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
GET /settings/account-strategy
|
|
124
|
+
|
|
125
|
+
# Response
|
|
126
|
+
{
|
|
127
|
+
"success": true,
|
|
128
|
+
"accountStrategy": "sticky",
|
|
129
|
+
"rotationEnabled": false
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Switch Account
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
POST /accounts/switch
|
|
137
|
+
Content-Type: application/json
|
|
138
|
+
|
|
139
|
+
{"email": "user@gmail.com"}
|
|
140
|
+
|
|
141
|
+
# Response
|
|
142
|
+
{"success": true, "message": "Switched to account: user@gmail.com"}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### OAuth Callback
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
GET /auth/callback?code=...&state=...
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Claude CLI Configuration
|
|
152
|
+
|
|
153
|
+
| Endpoint | Method | Description |
|
|
154
|
+
|----------|--------|-------------|
|
|
155
|
+
| `/claude/config` | GET | View current config |
|
|
156
|
+
| `/claude/config/proxy` | POST | Configure for proxy |
|
|
157
|
+
| `/claude/config/direct` | POST | Configure for direct API |
|
|
158
|
+
|
|
159
|
+
### Configure Proxy Mode
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
POST /claude/config/proxy
|
|
163
|
+
|
|
164
|
+
# Response
|
|
165
|
+
{
|
|
166
|
+
"success": true,
|
|
167
|
+
"message": "Claude CLI configured to use proxy at http://localhost:8081",
|
|
168
|
+
"config": {...}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Health
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
GET /health
|
|
176
|
+
|
|
177
|
+
# Response
|
|
178
|
+
{
|
|
179
|
+
"status": "ok",
|
|
180
|
+
"total": 2,
|
|
181
|
+
"active": "active@example.com",
|
|
182
|
+
"accounts": [...]
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Error Responses
|
|
187
|
+
|
|
188
|
+
### Authentication Error
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"type": "error",
|
|
193
|
+
"error": {
|
|
194
|
+
"type": "authentication_error",
|
|
195
|
+
"message": "No active account with valid credentials"
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Rate Limit Error
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"type": "error",
|
|
205
|
+
"error": {
|
|
206
|
+
"type": "rate_limit_error",
|
|
207
|
+
"message": "Rate limited: ..."
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Streaming Events
|
|
213
|
+
|
|
214
|
+
Anthropic SSE format:
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
event: message_start
|
|
218
|
+
data: {"type":"message_start","message":{...}}
|
|
219
|
+
|
|
220
|
+
event: content_block_start
|
|
221
|
+
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
|
|
222
|
+
|
|
223
|
+
event: content_block_delta
|
|
224
|
+
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}
|
|
225
|
+
|
|
226
|
+
event: content_block_stop
|
|
227
|
+
data: {"type":"content_block_stop","index":0}
|
|
228
|
+
|
|
229
|
+
event: message_delta
|
|
230
|
+
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{...}}
|
|
231
|
+
|
|
232
|
+
event: message_stop
|
|
233
|
+
data: {"type":"message_stop"}
|
|
234
|
+
|
|
235
|
+
data: [DONE]
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Tool Calling
|
|
239
|
+
|
|
240
|
+
### Request with Tools
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"model": "claude-sonnet-4-5",
|
|
245
|
+
"messages": [
|
|
246
|
+
{"role": "user", "content": "What's the weather in Tokyo?"}
|
|
247
|
+
],
|
|
248
|
+
"tools": [{
|
|
249
|
+
"name": "get_weather",
|
|
250
|
+
"description": "Get weather for a location",
|
|
251
|
+
"input_schema": {
|
|
252
|
+
"type": "object",
|
|
253
|
+
"properties": {
|
|
254
|
+
"location": {"type": "string"}
|
|
255
|
+
},
|
|
256
|
+
"required": ["location"]
|
|
257
|
+
}
|
|
258
|
+
}]
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Response with Tool Use
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"id": "msg_...",
|
|
267
|
+
"type": "message",
|
|
268
|
+
"role": "assistant",
|
|
269
|
+
"content": [{
|
|
270
|
+
"type": "tool_use",
|
|
271
|
+
"id": "toolu_...",
|
|
272
|
+
"name": "get_weather",
|
|
273
|
+
"input": {"location": "Tokyo"}
|
|
274
|
+
}],
|
|
275
|
+
"stop_reason": "tool_use"
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Tool Result
|
|
280
|
+
|
|
281
|
+
```json
|
|
282
|
+
{
|
|
283
|
+
"messages": [
|
|
284
|
+
{"role": "user", "content": "What's the weather?"},
|
|
285
|
+
{"role": "assistant", "content": [{"type": "tool_use", "id": "toolu_123", "name": "get_weather", "input": {"location": "Tokyo"}}]},
|
|
286
|
+
{"role": "user", "content": [{"type": "tool_result", "tool_use_id": "toolu_123", "content": "Sunny, 22°C"}]}
|
|
287
|
+
]
|
|
288
|
+
}
|
|
289
|
+
```
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
┌──────────────────┐ ┌─────────────────────┐ ┌────────────────────────────┐
|
|
7
|
+
│ Claude Code │────▶│ This Proxy Server │────▶│ ChatGPT Codex backend │
|
|
8
|
+
│ (Anthropic │ │ (Anthropic format) │ │ (internal API) │
|
|
9
|
+
│ API format) │ │ │ │ │
|
|
10
|
+
└──────────────────┘ └─────────────────────┘ └────────────────────────────┘
|
|
11
|
+
│
|
|
12
|
+
▼
|
|
13
|
+
┌─────────────────────┐
|
|
14
|
+
│ Account Manager │
|
|
15
|
+
│ (local storage) │
|
|
16
|
+
│ │
|
|
17
|
+
└─────────────────────┘
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Key Discovery
|
|
21
|
+
|
|
22
|
+
This proxy forwards requests from Anthropic-compatible clients (like Claude Code) to the ChatGPT Codex backend, handling authentication, format conversion, and streaming.
|
|
23
|
+
|
|
24
|
+
## Project Structure
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
codex-proxy/
|
|
28
|
+
├── package.json
|
|
29
|
+
├── README.md
|
|
30
|
+
├── docs/
|
|
31
|
+
│ ├── ARCHITECTURE.md
|
|
32
|
+
│ ├── API.md
|
|
33
|
+
│ ├── OAUTH.md
|
|
34
|
+
│ ├── ACCOUNTS.md
|
|
35
|
+
│ └── CLAUDE_INTEGRATION.md
|
|
36
|
+
├── public/
|
|
37
|
+
│ ├── index.html
|
|
38
|
+
│ ├── css/style.css
|
|
39
|
+
│ └── js/app.js # Web UI logic
|
|
40
|
+
└── src/
|
|
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
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
(See the `src/` directory for the full implementation; this doc focuses on the high-level shape.)
|
|
48
|
+
|
|
49
|
+
## Module Responsibilities
|
|
50
|
+
|
|
51
|
+
| File | Purpose |
|
|
52
|
+
|------|---------|
|
|
53
|
+
| `index.js` | Entry point (starts server) |
|
|
54
|
+
| `server.js` | Express server, routes, request handling (CORS restricted) |
|
|
55
|
+
| `routes/api-routes.js` | API route registrations (mounted by server) |
|
|
56
|
+
| `oauth.js` | OAuth 2.0 PKCE flow, token exchange |
|
|
57
|
+
| `account-manager.js` | Account persistence, switching, token refresh |
|
|
58
|
+
| `format-converter.js` | Convert between Anthropic and OpenAI Responses API formats |
|
|
59
|
+
| `response-streamer.js` | Parse SSE events, convert to Anthropic streaming format |
|
|
60
|
+
| `direct-api.js` | HTTP client for ChatGPT backend |
|
|
61
|
+
| `kilo-api.js` | Alternate upstream client |
|
|
62
|
+
| `kilo-format-converter.js` | Anthropic ↔ OpenAI Chat conversion |
|
|
63
|
+
| `kilo-streamer.js` | Streaming adapter |
|
|
64
|
+
| `server-settings.js` | Server-wide settings persistence |
|
|
65
|
+
| `model-api.js` | Fetch models, usage, quota |
|
|
66
|
+
| `claude-config.js` | Read/write Claude Code settings |
|
|
67
|
+
|
|
68
|
+
## Data Flow
|
|
69
|
+
|
|
70
|
+
### Request Flow
|
|
71
|
+
|
|
72
|
+
1. Claude Code sends Anthropic-format request to `/v1/messages`
|
|
73
|
+
2. The proxy maps the requested model to an upstream target
|
|
74
|
+
3. If the mapped path requires ChatGPT auth, the account manager loads/refreshes credentials
|
|
75
|
+
4. Request is converted and sent upstream
|
|
76
|
+
5. Response is streamed back as Anthropic SSE events
|
|
77
|
+
|
|
78
|
+
### Web UI Account/Quota Flow
|
|
79
|
+
|
|
80
|
+
1. Web UI loads account list from `/accounts`
|
|
81
|
+
2. Web UI fetches quota snapshots from `/accounts/quota/all`
|
|
82
|
+
3. Quota values are merged into account rows for table + modal views
|
|
83
|
+
4. Remaining quota is rendered from normalized usage percentages
|
|
84
|
+
5. On mobile/tablet, sidebar navigation auto-closes after tab change and account table uses horizontal scrolling
|
|
85
|
+
|
|
86
|
+
### Format Conversion
|
|
87
|
+
|
|
88
|
+
**Anthropic → OpenAI Responses API:**
|
|
89
|
+
- `messages` → `input` array with `type: 'message'`
|
|
90
|
+
- `system` → `instructions`
|
|
91
|
+
- `tools` → OpenAI function format
|
|
92
|
+
- `tool_use` → `function_call` input item
|
|
93
|
+
- `tool_result` → `function_call_output` input item
|
|
94
|
+
|
|
95
|
+
**OpenAI → Anthropic:**
|
|
96
|
+
- `output_text` → `{ type: 'text', text: ... }`
|
|
97
|
+
- `function_call` → `{ type: 'tool_use', id, name, input }`
|
|
98
|
+
- SSE events converted to Anthropic streaming format
|
|
99
|
+
|
|
100
|
+
## Available Models
|
|
101
|
+
|
|
102
|
+
| Model | Description |
|
|
103
|
+
|-------|-------------|
|
|
104
|
+
| `gpt-5.5` | Current OpenAI flagship model |
|
|
105
|
+
| `gpt-5.4` | Current lower-cost frontier model |
|
|
106
|
+
| `gpt-5.4-mini` | Current small OpenAI model |
|
|
107
|
+
| `gpt-5.3-codex` | Latest Codex-optimized model |
|
|
108
|
+
| `gpt-5.2` | Older general-purpose frontier model |
|
|
109
|
+
|
|
110
|
+
## Model Mapping
|
|
111
|
+
|
|
112
|
+
Claude model names are automatically mapped:
|
|
113
|
+
|
|
114
|
+
| Claude Model | Codex Model |
|
|
115
|
+
|--------------|-------------|
|
|
116
|
+
| `claude-opus-4-5` | `gpt-5.5` |
|
|
117
|
+
| `claude-sonnet-4-5` | `gpt-5.5` |
|
|
118
|
+
| `claude-haiku-4` | `gpt-5.4-mini` |
|
|
119
|
+
| `kilo` | selected Kilo target, disabled by default |
|
|
120
|
+
|
|
121
|
+
Kilo routing is explicit and disabled unless `CODEX_CLAUDE_PROXY_ENABLE_KILO=true` is set. The `/settings/haiku-model` endpoints choose the Kilo target used when the requested model is `kilo`.
|
|
122
|
+
|
|
123
|
+
## Account Selection
|
|
124
|
+
|
|
125
|
+
The default execution mode is personal local use: `/v1/messages` uses the active account only and does not rotate across configured accounts. Multi-account rotation is disabled unless `CODEX_CLAUDE_PROXY_ENABLE_MULTI_ACCOUNT_ROTATION=true` is set.
|
|
126
|
+
|
|
127
|
+
## Data Storage
|
|
128
|
+
|
|
129
|
+
Account and configuration files are stored under your home directory (platform-specific). See `docs/ACCOUNTS.md` and `docs/CLAUDE_INTEGRATION.md` for details.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Claude Code Integration
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
### Automatic Configuration
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
curl -X POST http://localhost:8081/claude/config/proxy
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Updates `~/.claude/settings.json`:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"env": {
|
|
16
|
+
"ANTHROPIC_BASE_URL": "http://localhost:8081",
|
|
17
|
+
"ANTHROPIC_API_KEY": "any-key",
|
|
18
|
+
"ANTHROPIC_MODEL": "claude-sonnet-4-5",
|
|
19
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-5",
|
|
20
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4-5",
|
|
21
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Manual Configuration
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
export ANTHROPIC_BASE_URL=http://localhost:8081
|
|
30
|
+
export ANTHROPIC_API_KEY=any-key
|
|
31
|
+
claude
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Using Claude Code
|
|
35
|
+
|
|
36
|
+
When prompted about API key:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
Detected a custom API key in your environment
|
|
40
|
+
ANTHROPIC_API_KEY: any-key
|
|
41
|
+
Do you want to use this API key?
|
|
42
|
+
❯ 1. Yes <-- Choose this
|
|
43
|
+
2. No (recommended)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## How It Works
|
|
47
|
+
|
|
48
|
+
### Request Flow
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
Claude Code (Anthropic format)
|
|
52
|
+
↓
|
|
53
|
+
Proxy Server
|
|
54
|
+
↓
|
|
55
|
+
Format Conversion
|
|
56
|
+
↓
|
|
57
|
+
ChatGPT Backend API
|
|
58
|
+
↓
|
|
59
|
+
Response Stream
|
|
60
|
+
↓
|
|
61
|
+
Format Conversion
|
|
62
|
+
↓
|
|
63
|
+
Claude Code (Anthropic format)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Format Conversion
|
|
67
|
+
|
|
68
|
+
**Anthropic → OpenAI Responses API:**
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
// Anthropic request
|
|
72
|
+
{
|
|
73
|
+
"model": "claude-sonnet-4-5",
|
|
74
|
+
"system": "You are helpful.",
|
|
75
|
+
"messages": [
|
|
76
|
+
{"role": "user", "content": "Hello"},
|
|
77
|
+
{"role": "assistant", "content": [{"type": "tool_use", "id": "t1", "name": "fn", "input": {}}]},
|
|
78
|
+
{"role": "user", "content": [{"type": "tool_result", "tool_use_id": "t1", "content": "result"}]}
|
|
79
|
+
],
|
|
80
|
+
"tools": [...]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Converted to OpenAI Responses API
|
|
84
|
+
{
|
|
85
|
+
"model": "gpt-5.5",
|
|
86
|
+
"instructions": "You are helpful.",
|
|
87
|
+
"input": [
|
|
88
|
+
{"type": "message", "role": "user", "content": "Hello"},
|
|
89
|
+
{"type": "function_call", "id": "fc_t1", "call_id": "fc_t1", "name": "fn", "arguments": "{}"},
|
|
90
|
+
{"type": "function_call_output", "call_id": "fc_t1", "output": "result"}
|
|
91
|
+
],
|
|
92
|
+
"tools": [...],
|
|
93
|
+
"store": false,
|
|
94
|
+
"stream": true
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Key Conversions:**
|
|
99
|
+
- `system` → `instructions`
|
|
100
|
+
- `messages` → `input` array
|
|
101
|
+
- `tool_use` → `function_call` + `function_call_output` items
|
|
102
|
+
- Tool IDs prefixed with `fc_` for API compatibility
|
|
103
|
+
|
|
104
|
+
### Streaming Events
|
|
105
|
+
|
|
106
|
+
OpenAI Responses API → Anthropic SSE:
|
|
107
|
+
|
|
108
|
+
| OpenAI Event | Anthropic Event |
|
|
109
|
+
|--------------|-----------------|
|
|
110
|
+
| `response.output_item.added` | `message_start`, `content_block_start` |
|
|
111
|
+
| `response.output_text.delta` | `content_block_delta` (text_delta) |
|
|
112
|
+
| `response.function_call_arguments.delta` | `content_block_delta` (input_json_delta) |
|
|
113
|
+
| `response.completed` | `message_delta`, `message_stop` |
|
|
114
|
+
|
|
115
|
+
## Tool Calling
|
|
116
|
+
|
|
117
|
+
Works natively via OpenAI Responses API:
|
|
118
|
+
|
|
119
|
+
1. Claude Code sends tools in Anthropic format
|
|
120
|
+
2. Proxy converts to OpenAI function format
|
|
121
|
+
3. ChatGPT executes and returns function calls
|
|
122
|
+
4. Proxy converts back to Anthropic `tool_use` blocks
|
|
123
|
+
5. Claude Code processes and returns tool results
|
|
124
|
+
|
|
125
|
+
## View Configuration
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
curl http://localhost:8081/claude/config
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Revert to Direct API
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
curl -X POST http://localhost:8081/claude/config/direct \
|
|
135
|
+
-H "Content-Type: application/json" \
|
|
136
|
+
-d '{"apiKey":"sk-ant-..."}'
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Troubleshooting
|
|
140
|
+
|
|
141
|
+
### Claude Code hangs
|
|
142
|
+
|
|
143
|
+
1. Check proxy health: `curl http://localhost:8081/health`
|
|
144
|
+
2. Verify config: `cat ~/.claude/settings.json`
|
|
145
|
+
3. Re-configure: `curl -X POST http://localhost:8081/claude/config/proxy`
|
|
146
|
+
|
|
147
|
+
### "No active account" error
|
|
148
|
+
|
|
149
|
+
Add an account first:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
curl -X POST http://localhost:8081/accounts/import
|
|
153
|
+
# or use WebUI
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Tool calls not working
|
|
157
|
+
|
|
158
|
+
Ensure you're using the direct API mode (not CLI subprocess). Check:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
curl http://localhost:8081/health
|
|
162
|
+
# Should show accounts with valid tokens
|
|
163
|
+
```
|
package/docs/OAUTH.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# OAuth Implementation
|
|
2
|
+
|
|
3
|
+
This proxy uses **OAuth 2.0 with PKCE** for secure authentication with ChatGPT.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### Desktop (Browser)
|
|
8
|
+
```bash
|
|
9
|
+
codex-proxy accounts add
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### Headless/VM (No Browser)
|
|
13
|
+
```bash
|
|
14
|
+
codex-proxy accounts add --no-browser
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The legacy `codex-claude-proxy` command remains supported as an alias.
|
|
18
|
+
|
|
19
|
+
## Headless/VM Workflow
|
|
20
|
+
|
|
21
|
+
When running on a server without a browser (VM, Docker, SSH):
|
|
22
|
+
|
|
23
|
+
1. Run the command with `--no-browser`:
|
|
24
|
+
```bash
|
|
25
|
+
codex-proxy accounts add --no-browser
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
2. It prints a URL like:
|
|
29
|
+
```
|
|
30
|
+
https://auth.openai.com/oauth/authorize?response_type=code&...
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
3. Copy the URL and open it in a browser on **any other device** (your laptop, phone, etc.)
|
|
34
|
+
|
|
35
|
+
4. Complete the ChatGPT login
|
|
36
|
+
|
|
37
|
+
5. After successful login, you'll be redirected to a localhost URL that looks like:
|
|
38
|
+
```
|
|
39
|
+
http://localhost:1455/auth/callback?code=ABC123...
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
6. Copy that entire URL (or just the `code` parameter) and paste it back in the terminal
|
|
43
|
+
|
|
44
|
+
7. The proxy exchanges the code for tokens and saves your account
|
|
45
|
+
|
|
46
|
+
## OAuth Config
|
|
47
|
+
|
|
48
|
+
- **Client ID**: `app_EMoamEEZ73f0CkXaXp7hrann`
|
|
49
|
+
- **Auth URL**: `https://auth.openai.com/oauth/authorize`
|
|
50
|
+
- **Token URL**: `https://auth.openai.com/oauth/token`
|
|
51
|
+
- **Callback Port**: `1455`
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- **PKCE**: Secure code exchange with SHA256 challenge
|
|
56
|
+
- **Auto-Refresh**: Tokens refresh automatically before expiry
|
|
57
|
+
- **Account Selection**: Uses `prompt=login` so you can choose the account to add
|
|
58
|
+
- **Headless Support**: Works on servers without browsers
|
|
59
|
+
|
|
60
|
+
## Managing Accounts
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# List accounts
|
|
64
|
+
codex-proxy accounts list
|
|
65
|
+
|
|
66
|
+
# Add account (browser)
|
|
67
|
+
codex-proxy accounts add
|
|
68
|
+
|
|
69
|
+
# Add account (headless)
|
|
70
|
+
codex-proxy accounts add --no-browser
|
|
71
|
+
|
|
72
|
+
# Clear all accounts
|
|
73
|
+
codex-proxy accounts clear
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Troubleshooting
|
|
77
|
+
|
|
78
|
+
### "Port already in use"
|
|
79
|
+
The `--no-browser` mode works independently of the server - you can add accounts even while the proxy is running.
|
|
80
|
+
|
|
81
|
+
### "Invalid state" error
|
|
82
|
+
This happens if you use a code from an old session. Generate a fresh URL and try again.
|
|
83
|
+
|
|
84
|
+
### Same account keeps getting selected
|
|
85
|
+
Clear cookies at `auth.openai.com` or use a private/incognito window.
|
package/docs/OPENCLAW.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Using with OpenClaw
|
|
2
|
+
|
|
3
|
+
[OpenClaw](https://docs.openclaw.ai/) is an AI agent gateway. This proxy provides the Anthropic-compatible API it needs.
|
|
4
|
+
|
|
5
|
+
## Quick Integration
|
|
6
|
+
|
|
7
|
+
1. **Add Provider** to `~/.openclaw/openclaw.json`:
|
|
8
|
+
```json
|
|
9
|
+
{
|
|
10
|
+
"codex-proxy": {
|
|
11
|
+
"baseUrl": "http://127.0.0.1:8081",
|
|
12
|
+
"apiKey": "test",
|
|
13
|
+
"api": "anthropic-messages",
|
|
14
|
+
"models": [
|
|
15
|
+
{ "id": "claude-sonnet-4-5", "name": "GPT-5.5" },
|
|
16
|
+
{ "id": "claude-opus-4-5", "name": "GPT-5.5" },
|
|
17
|
+
{ "id": "gpt-5.3-codex", "name": "GPT-5.3 Codex" }
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
2. **Set Primary Model**:
|
|
23
|
+
```bash
|
|
24
|
+
openclaw models set codex-proxy/claude-sonnet-4-5
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Key Benefits
|
|
28
|
+
- **Personal Account Mode**: Proxy uses the active local ChatGPT account by default.
|
|
29
|
+
- **SSE Streaming**: Full real-time response support.
|
|
30
|
+
- **Tool Calling**: Native support for agentic workflows.
|
|
31
|
+
|
|
32
|
+
## Troubleshooting
|
|
33
|
+
- Use `127.0.0.1` instead of `localhost`.
|
|
34
|
+
- 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.
|