codex-claude-proxy 1.0.0
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 +206 -0
- package/docs/ACCOUNTS.md +202 -0
- package/docs/API.md +274 -0
- package/docs/ARCHITECTURE.md +133 -0
- package/docs/CLAUDE_INTEGRATION.md +163 -0
- package/docs/OAUTH.md +201 -0
- package/docs/OPENCLAW.md +338 -0
- package/images/f757093f-507b-4453-994e-f8275f8b07a9.png +0 -0
- package/package.json +44 -0
- package/public/css/style.css +791 -0
- package/public/index.html +783 -0
- package/public/js/app.js +511 -0
- package/src/account-manager.js +483 -0
- package/src/claude-config.js +143 -0
- package/src/cli/accounts.js +413 -0
- package/src/cli/index.js +66 -0
- package/src/direct-api.js +123 -0
- package/src/format-converter.js +331 -0
- package/src/index.js +41 -0
- package/src/kilo-api.js +68 -0
- package/src/kilo-format-converter.js +270 -0
- package/src/kilo-streamer.js +198 -0
- package/src/model-api.js +189 -0
- package/src/oauth.js +554 -0
- package/src/response-streamer.js +329 -0
- package/src/routes/api-routes.js +1035 -0
- package/src/server-settings.js +48 -0
- package/src/server.js +30 -0
- package/src/utils/logger.js +156 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
┌──────────────────┐ ┌─────────────────────┐ ┌────────────────────────────┐
|
|
7
|
+
│ Claude Code │────▶│ This Proxy Server │────▶│ ChatGPT Backend API │
|
|
8
|
+
│ (Anthropic │ │ (Anthropic format) │ │ (chatgpt.com/backend-api/ │
|
|
9
|
+
│ API format) │ │ │ │ codex/responses) │
|
|
10
|
+
└──────────────────┘ └─────────────────────┘ └────────────────────────────┘
|
|
11
|
+
│
|
|
12
|
+
▼
|
|
13
|
+
┌─────────────────────┐
|
|
14
|
+
│ Account Manager │
|
|
15
|
+
│ (~/.codex-claude- │
|
|
16
|
+
│ proxy/accounts.json)
|
|
17
|
+
└─────────────────────┘
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Key Discovery
|
|
21
|
+
|
|
22
|
+
The endpoint `https://chatgpt.com/backend-api/codex/responses` bypasses Cloudflare protection when called with proper authentication headers.
|
|
23
|
+
|
|
24
|
+
## Project Structure
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
codex-claude-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 # Alpine UI logic
|
|
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
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Module Responsibilities
|
|
56
|
+
|
|
57
|
+
| File | Purpose |
|
|
58
|
+
|------|---------|
|
|
59
|
+
| `index.js` | Entry point (starts server) |
|
|
60
|
+
| `server.js` | Express server, routes, request handling |
|
|
61
|
+
| `routes/api-routes.js` | API route registrations (mounted by server) |
|
|
62
|
+
| `oauth.js` | OAuth 2.0 PKCE flow, token exchange |
|
|
63
|
+
| `account-manager.js` | Account persistence, switching, token refresh |
|
|
64
|
+
| `format-converter.js` | Convert between Anthropic and OpenAI Responses API formats |
|
|
65
|
+
| `response-streamer.js` | Parse SSE events, convert to Anthropic streaming format |
|
|
66
|
+
| `direct-api.js` | HTTP client for ChatGPT backend |
|
|
67
|
+
| `kilo-api.js` | Alternate upstream client |
|
|
68
|
+
| `kilo-format-converter.js` | Anthropic ↔ OpenAI Chat conversion |
|
|
69
|
+
| `kilo-streamer.js` | Streaming adapter |
|
|
70
|
+
| `server-settings.js` | Server-wide settings persistence |
|
|
71
|
+
| `model-api.js` | Fetch models, usage, quota |
|
|
72
|
+
| `claude-config.js` | Read/write `~/.claude/settings.json` |
|
|
73
|
+
|
|
74
|
+
## Data Flow
|
|
75
|
+
|
|
76
|
+
### Request Flow
|
|
77
|
+
|
|
78
|
+
1. Claude Code sends Anthropic-format request to `/v1/messages`
|
|
79
|
+
2. The proxy maps the requested model to an upstream target
|
|
80
|
+
3. If the mapped path requires ChatGPT auth, the account manager loads/refreshes credentials
|
|
81
|
+
4. Request is converted and sent upstream
|
|
82
|
+
5. Response is streamed back as Anthropic SSE events
|
|
83
|
+
|
|
84
|
+
### Web UI Account/Quota Flow
|
|
85
|
+
|
|
86
|
+
1. Web UI loads account list from `/accounts`
|
|
87
|
+
2. Web UI fetches quota snapshots from `/accounts/quota/all`
|
|
88
|
+
3. Quota values are merged into account rows for table + modal views
|
|
89
|
+
4. Remaining quota is rendered from normalized usage percentages
|
|
90
|
+
5. On mobile/tablet, sidebar navigation auto-closes after tab change and account table uses horizontal scrolling
|
|
91
|
+
|
|
92
|
+
### Format Conversion
|
|
93
|
+
|
|
94
|
+
**Anthropic → OpenAI Responses API:**
|
|
95
|
+
- `messages` → `input` array with `type: 'message'`
|
|
96
|
+
- `system` → `instructions`
|
|
97
|
+
- `tools` → OpenAI function format
|
|
98
|
+
- `tool_use` → `function_call` input item
|
|
99
|
+
- `tool_result` → `function_call_output` input item
|
|
100
|
+
|
|
101
|
+
**OpenAI → Anthropic:**
|
|
102
|
+
- `output_text` → `{ type: 'text', text: ... }`
|
|
103
|
+
- `function_call` → `{ type: 'tool_use', id, name, input }`
|
|
104
|
+
- SSE events converted to Anthropic streaming format
|
|
105
|
+
|
|
106
|
+
## Available Models
|
|
107
|
+
|
|
108
|
+
| Model | Description |
|
|
109
|
+
|-------|-------------|
|
|
110
|
+
| `gpt-5.3-codex` | Latest agentic coding model |
|
|
111
|
+
| `gpt-5.2-codex` | Frontier agentic coding model |
|
|
112
|
+
| `gpt-5.2` | General-purpose frontier model |
|
|
113
|
+
|
|
114
|
+
## Model Mapping
|
|
115
|
+
|
|
116
|
+
Claude model names are automatically mapped:
|
|
117
|
+
|
|
118
|
+
| Claude Model | Codex Model |
|
|
119
|
+
|--------------|-------------|
|
|
120
|
+
| `claude-opus-4-5` | `gpt-5.3-codex` |
|
|
121
|
+
| `claude-sonnet-4-5` | `gpt-5.2` |
|
|
122
|
+
| `claude-haiku-4` | routed by server setting |
|
|
123
|
+
|
|
124
|
+
Haiku routing is controlled by a server-wide setting (`/settings/haiku-model`).
|
|
125
|
+
|
|
126
|
+
## Data Storage
|
|
127
|
+
|
|
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) |
|
|
@@ -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.2-codex",
|
|
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,201 @@
|
|
|
1
|
+
# OAuth Implementation
|
|
2
|
+
|
|
3
|
+
## Configuration
|
|
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
|
+
```
|
|
15
|
+
|
|
16
|
+
## Authorization Methods
|
|
17
|
+
|
|
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
|
|
183
|
+
|
|
184
|
+
## 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
|