claude-codex-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 +227 -0
- package/bin/cli.js +113 -0
- package/docs/ACCOUNTS.md +202 -0
- package/docs/API.md +244 -0
- package/docs/ARCHITECTURE.md +119 -0
- package/docs/CLAUDE_INTEGRATION.md +163 -0
- package/docs/OAUTH.md +83 -0
- package/docs/OPENCLAW.md +33 -0
- package/docs/legal.md +9 -0
- package/images/demo-screenshot.png +0 -0
- package/images/f757093f-507b-4453-994e-f8275f8b07a9.png +0 -0
- package/package.json +56 -0
- package/public/css/style.css +791 -0
- package/public/index.html +838 -0
- package/public/js/app.js +619 -0
- package/src/account-manager.js +526 -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 +154 -0
- package/src/cli/accounts.js +551 -0
- package/src/direct-api.js +214 -0
- package/src/format-converter.js +563 -0
- package/src/index.js +45 -0
- package/src/middleware/credentials.js +116 -0
- package/src/middleware/sse.js +130 -0
- package/src/model-api.js +189 -0
- package/src/model-mapper.js +88 -0
- package/src/oauth.js +623 -0
- package/src/response-streamer.js +469 -0
- package/src/routes/accounts-route.js +296 -0
- package/src/routes/api-routes.js +102 -0
- package/src/routes/chat-route.js +239 -0
- package/src/routes/claude-config-route.js +114 -0
- package/src/routes/logs-route.js +43 -0
- package/src/routes/messages-route.js +164 -0
- package/src/routes/models-route.js +115 -0
- package/src/routes/settings-route.js +154 -0
- package/src/server-settings.js +70 -0
- package/src/server.js +57 -0
- package/src/signature-cache.js +106 -0
- package/src/thinking-utils.js +312 -0
- package/src/utils/logger.js +170 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Codex Claude Proxy
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
[](https://choosealicense.com/licenses/mit/)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
[](https://github.com/zzy-life/codex-claude-proxy)
|
|
8
|
+
|
|
9
|
+
> **Use Claude Code CLI with the power of ChatGPT Codex models.**
|
|
10
|
+
> A local proxy that translates Anthropic API requests into ChatGPT Codex calls, enabling you to use the `claude` CLI tool with your ChatGPT Free/Plus/Pro subscription.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 🚀 Features
|
|
15
|
+
|
|
16
|
+
- **Seamless Translation**: Translates Anthropic Messages API calls to ChatGPT Codex format.
|
|
17
|
+
- **Model Mapping**: automatically maps `claude-sonnet` and `claude-opus` to their Codex equivalents.
|
|
18
|
+
- **Multi-Account Support**: Manage multiple ChatGPT accounts with easy switching and auto-refresh.
|
|
19
|
+
- **Web Dashboard**: Built-in UI (`http://localhost:8081`) for managing accounts, viewing logs, and testing prompts.
|
|
20
|
+
- **Raw Proxy Payload Logs**: Optional debug logging for Claude Code requests, upstream Codex payloads, completed responses, and upstream errors.
|
|
21
|
+
- **Streaming Support**: Full Server-Sent Events (SSE) support for real-time responses.
|
|
22
|
+
- **Native Tool Calling**: Supports Claude's tool use capabilities by translating them to Codex function calls.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## � Security & Privacy
|
|
27
|
+
|
|
28
|
+
**Is this a malicious proxy? No.**
|
|
29
|
+
|
|
30
|
+
- **Local Execution**: This server runs entirely on your local machine (`localhost`).
|
|
31
|
+
- **Direct Communication**: It connects *directly* to OpenAI/ChatGPT endpoints. No data is sent to any third-party server.
|
|
32
|
+
- **Open Source**: The full source code is available here for you to audit.
|
|
33
|
+
- **No Data Collection**: We do not track your prompts, keys, or personal data.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## ⚙️ How it works
|
|
38
|
+
|
|
39
|
+
This tool acts as a "translation layer" between the Claude CLI and ChatGPT's Codex backend.
|
|
40
|
+
|
|
41
|
+
1. **Intercept**: Claude Code CLI sends a request to `localhost:8081` (thinking it's Anthropic's API).
|
|
42
|
+
2. **Translate**: The proxy converts the Anthropic-format JSON into the specific payload format required by ChatGPT's internal Codex API.
|
|
43
|
+
3. **Forward**: The request is sent securely to ChatGPT using your own authenticated session.
|
|
44
|
+
4. **Stream**: The response from ChatGPT is converted back into Anthropic's Server-Sent Events (SSE) format and streamed to your terminal.
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
┌──────────────────┐ ┌─────────────────────┐ ┌────────────────────────────┐
|
|
48
|
+
│ Claude Code │────▶│ This Proxy Server │────▶│ ChatGPT Codex Backend API │
|
|
49
|
+
│ (Anthropic API) │ │ (Anthropic ⇄ OpenAI)│ │ (codex/responses) │
|
|
50
|
+
└──────────────────┘ └─────────────────────┘ └────────────────────────────┘
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## �📦 Installation
|
|
56
|
+
|
|
57
|
+
You don't need to install anything if you just want to run it:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Run directly with npx
|
|
61
|
+
npx claude-codex-proxy@latest start
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or install globally to use the CLI commands anywhere:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install -g claude-codex-proxy
|
|
68
|
+
claude-codex-proxy start
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 🚦 Quick Start
|
|
74
|
+
|
|
75
|
+
### 1. Start the Proxy
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx claude-codex-proxy@latest start
|
|
79
|
+
```
|
|
80
|
+
The server will start at `http://localhost:8081`.
|
|
81
|
+
|
|
82
|
+
### 2. Add Your Account
|
|
83
|
+
|
|
84
|
+
#### **Option A: Web Dashboard (Local Desktop)**
|
|
85
|
+
|
|
86
|
+
1. Open the dashboard at **[http://localhost:8081](http://localhost:8081)**
|
|
87
|
+
2. Go to the **Accounts** tab
|
|
88
|
+
3. Click **Add Account** and login with your ChatGPT account
|
|
89
|
+
|
|
90
|
+
#### **Option B: CLI (Desktop or Headless/VM)**
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Desktop (opens browser)
|
|
94
|
+
claude-codex-proxy accounts add
|
|
95
|
+
|
|
96
|
+
# Headless/VM server (manual code input)
|
|
97
|
+
claude-codex-proxy accounts add --no-browser
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
For **headless/VM servers** without a browser:
|
|
101
|
+
1. Run the command with `--no-browser`
|
|
102
|
+
2. It will print a URL - copy and open it on a device with a browser
|
|
103
|
+
3. Complete login on that device
|
|
104
|
+
4. After redirect, copy the callback URL (or just the code)
|
|
105
|
+
5. Paste it back in the terminal
|
|
106
|
+
|
|
107
|
+
### 3. Configure Claude Code
|
|
108
|
+
Run this command to automatically configure your `claude` CLI to use the proxy:
|
|
109
|
+
```bash
|
|
110
|
+
curl -X POST http://localhost:8081/claude/config/proxy
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
*Alternatively, set the environment variables manually:*
|
|
114
|
+
```bash
|
|
115
|
+
export ANTHROPIC_BASE_URL=http://localhost:8081
|
|
116
|
+
export ANTHROPIC_API_KEY=dummy-key # The key is ignored but required by the CLI
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
To skip Claude Code's login/onboarding prompt, edit `~/.claude.json` and add:
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"hasCompletedOnboarding": true
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
4. **Run Claude**:
|
|
127
|
+
```bash
|
|
128
|
+
claude
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 🧠 Model Mapping
|
|
134
|
+
|
|
135
|
+
The proxy resolves model names dynamically, so Claude Code can request Claude-style names while the proxy routes them to Codex models through your ChatGPT account.
|
|
136
|
+
|
|
137
|
+
Default mapping:
|
|
138
|
+
|
|
139
|
+
| Requested Model | Upstream Model | Auth Required | Description |
|
|
140
|
+
| :--- | :--- | :---: | :--- |
|
|
141
|
+
| `claude-*sonnet*` | `gpt-5.5` | ✅ | Uses the configured Sonnet-family Codex model. |
|
|
142
|
+
| `claude-*opus*` | `gpt-5.5` | ✅ | Uses the configured Opus-family Codex model. |
|
|
143
|
+
| `claude-*haiku*` | `gpt-5.4-mini` | ✅ | Uses the configured Haiku-family Codex model by default. |
|
|
144
|
+
| `gpt-*` | unchanged | ✅ | Direct Codex model IDs pass through without remapping. |
|
|
145
|
+
| unknown model | `gpt-5.5` | ✅ | Falls back to the configured default Codex model. |
|
|
146
|
+
|
|
147
|
+
You can customize mappings from the Web Dashboard, go to the model settings, and choose the Codex model used for each Claude family.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 🛠️ Configuration & API
|
|
152
|
+
|
|
153
|
+
### Web Dashboard
|
|
154
|
+
Visit `http://localhost:8081` to:
|
|
155
|
+
- **Manage Accounts**: Add, remove, or switch active ChatGPT accounts.
|
|
156
|
+
- **View Logs**: See real-time request/response logs for debugging.
|
|
157
|
+
- **Test Models**: Run quick tests against the configured models.
|
|
158
|
+
- **Configure Raw Proxy Payload Logs**: Enable detailed debugging logs from the Settings tab when you need to inspect raw Claude Code requests and upstream Codex payloads.
|
|
159
|
+
|
|
160
|
+
### Raw Proxy Payload Logs
|
|
161
|
+
Raw payload logging is disabled by default because it may include prompts, tool results, file contents, and upstream response bodies. When enabled, the Logs tab captures:
|
|
162
|
+
|
|
163
|
+
- `[Claude Request]`: the original Anthropic-compatible request body received from Claude Code.
|
|
164
|
+
- `[Client Request]`: the original OpenAI Chat Completions-compatible request body, when using `/v1/chat/completions`.
|
|
165
|
+
- `[OpenAI Request]`: the converted payload sent to the ChatGPT Codex Responses API.
|
|
166
|
+
- `[OpenAI Response Completed]`: the completed upstream response object.
|
|
167
|
+
- `[OpenAI Error]`: non-2xx upstream status and error body.
|
|
168
|
+
|
|
169
|
+
You can enable it from the Web Dashboard under **Settings → Raw Proxy Payload Logs**, or via API:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
curl -X POST http://localhost:8081/settings/raw-proxy-logs \
|
|
173
|
+
-H "Content-Type: application/json" \
|
|
174
|
+
-d '{"enabled":true}'
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Disable it again with:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
curl -X POST http://localhost:8081/settings/raw-proxy-logs \
|
|
181
|
+
-H "Content-Type: application/json" \
|
|
182
|
+
-d '{"enabled":false}'
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Check the current state:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
curl http://localhost:8081/settings/raw-proxy-logs
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Authentication headers and access tokens are not logged.
|
|
192
|
+
|
|
193
|
+
### API Endpoints
|
|
194
|
+
- `GET /health`: Check server status.
|
|
195
|
+
- `GET /accounts`: List configured accounts.
|
|
196
|
+
- `POST /v1/messages`: Anthropic-compatible chat completion endpoint.
|
|
197
|
+
|
|
198
|
+
See [API Documentation](./docs/API.md) for full details.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## 🤝 Contributing
|
|
203
|
+
|
|
204
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
205
|
+
|
|
206
|
+
1. Fork the repository
|
|
207
|
+
2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
|
|
208
|
+
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
|
209
|
+
4. Push to the branch (`git push origin feature/AmazingFeature`)
|
|
210
|
+
5. Open a Pull Request
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 📄 License
|
|
215
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
216
|
+
|
|
217
|
+
## ⚠️ Disclaimer
|
|
218
|
+
This project is an independent open-source tool and is not affiliated with, endorsed by, or sponsored by Anthropic or OpenAI. "Claude" is a trademark of Anthropic PBC. "ChatGPT" and "Codex" are trademarks of OpenAI. Use responsibly and in accordance with applicable Terms of Service.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
<div align="center">
|
|
223
|
+
<p>If you find this project useful, please give it a star! ⭐️</p>
|
|
224
|
+
<a href="https://github.com/zzy-life/codex-claude-proxy">
|
|
225
|
+
<img src="https://img.shields.io/github/stars/zzy-life/codex-claude-proxy?style=social" alt="Star on GitHub">
|
|
226
|
+
</a>
|
|
227
|
+
</div>
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { readFileSync } from 'fs';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
|
|
10
|
+
const packageJson = JSON.parse(
|
|
11
|
+
readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const command = args[0];
|
|
16
|
+
|
|
17
|
+
function showHelp() {
|
|
18
|
+
console.log(`
|
|
19
|
+
codex-claude-proxy v${packageJson.version}
|
|
20
|
+
|
|
21
|
+
Proxy server for using ChatGPT Codex models with Claude Code CLI.
|
|
22
|
+
|
|
23
|
+
USAGE:
|
|
24
|
+
codex-claude-proxy <command> [options]
|
|
25
|
+
|
|
26
|
+
COMMANDS:
|
|
27
|
+
start Start the proxy server (default port: 8081)
|
|
28
|
+
accounts Manage ChatGPT accounts (interactive)
|
|
29
|
+
accounts add Add a new ChatGPT account via OAuth
|
|
30
|
+
accounts add --no-browser Add account manually (headless/VM)
|
|
31
|
+
accounts list List all configured accounts
|
|
32
|
+
accounts remove Remove accounts interactively
|
|
33
|
+
accounts verify Verify account tokens are valid
|
|
34
|
+
accounts clear Remove all accounts
|
|
35
|
+
|
|
36
|
+
OPTIONS:
|
|
37
|
+
--help, -h Show this help message
|
|
38
|
+
--version, -v Show version number
|
|
39
|
+
|
|
40
|
+
ENVIRONMENT:
|
|
41
|
+
PORT Server port (default: 8081)
|
|
42
|
+
|
|
43
|
+
EXAMPLES:
|
|
44
|
+
codex-claude-proxy start
|
|
45
|
+
PORT=3000 codex-claude-proxy start
|
|
46
|
+
codex-claude-proxy accounts add
|
|
47
|
+
codex-claude-proxy accounts add --no-browser
|
|
48
|
+
codex-claude-proxy accounts list
|
|
49
|
+
codex-claude-proxy accounts verify
|
|
50
|
+
|
|
51
|
+
HEADLESS/VM USAGE:
|
|
52
|
+
1. Run: codex-claude-proxy accounts add --no-browser
|
|
53
|
+
2. Copy the URL shown and open in browser on another device
|
|
54
|
+
3. After login, paste the callback URL back in terminal
|
|
55
|
+
|
|
56
|
+
CONFIGURATION:
|
|
57
|
+
Claude Code CLI (~/.claude/settings.json):
|
|
58
|
+
{
|
|
59
|
+
"env": {
|
|
60
|
+
"ANTHROPIC_BASE_URL": "http://localhost:8081",
|
|
61
|
+
"ANTHROPIC_API_KEY": "dummy"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function showVersion() {
|
|
68
|
+
console.log(packageJson.version);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function main() {
|
|
72
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
73
|
+
showHelp();
|
|
74
|
+
process.exit(0);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
78
|
+
showVersion();
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
switch (command) {
|
|
83
|
+
case 'start':
|
|
84
|
+
case undefined:
|
|
85
|
+
await import('../src/index.js');
|
|
86
|
+
break;
|
|
87
|
+
|
|
88
|
+
case 'accounts': {
|
|
89
|
+
const subCommand = args[1] || 'add';
|
|
90
|
+
process.argv = ['node', 'accounts-cli.js', subCommand, ...args.slice(2)];
|
|
91
|
+
await import('../src/cli/accounts.js');
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
case 'help':
|
|
96
|
+
showHelp();
|
|
97
|
+
break;
|
|
98
|
+
|
|
99
|
+
case 'version':
|
|
100
|
+
showVersion();
|
|
101
|
+
break;
|
|
102
|
+
|
|
103
|
+
default:
|
|
104
|
+
console.error(`Unknown command: ${command}`);
|
|
105
|
+
console.error('Run "codex-claude-proxy --help" for usage information.');
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main().catch((err) => {
|
|
111
|
+
console.error('Error:', err.message);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
});
|
package/docs/ACCOUNTS.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Account Management
|
|
2
|
+
|
|
3
|
+
## Storage Structure
|
|
4
|
+
|
|
5
|
+
### Main Registry
|
|
6
|
+
|
|
7
|
+
**Location:** `~/.codex-claude-proxy/accounts.json`
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"accounts": [
|
|
12
|
+
{
|
|
13
|
+
"email": "user@gmail.com",
|
|
14
|
+
"accountId": "d41e9636-16d8-42be-91da-7ea8773bfb7e",
|
|
15
|
+
"planType": "plus",
|
|
16
|
+
"accessToken": "eyJhbGciOiJSUzI1NiIs...",
|
|
17
|
+
"refreshToken": "rt_WpTMn1...",
|
|
18
|
+
"idToken": "eyJhbGciOiJSUzI1NiIs...",
|
|
19
|
+
"expiresAt": 1770886178000,
|
|
20
|
+
"addedAt": "2026-02-13T04:00:00.000Z",
|
|
21
|
+
"lastUsed": "2026-02-13T04:30:00.000Z",
|
|
22
|
+
"quota": {
|
|
23
|
+
"usage": {...},
|
|
24
|
+
"account": {...},
|
|
25
|
+
"lastChecked": "2026-02-14T10:00:00.000Z"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"activeAccount": "user@gmail.com",
|
|
30
|
+
"version": 1
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Per-Account Tokens
|
|
35
|
+
|
|
36
|
+
**Location:** `~/.codex-claude-proxy/accounts/<email>/auth.json`
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"auth_mode": "chatgpt",
|
|
41
|
+
"OPENAI_API_KEY": null,
|
|
42
|
+
"tokens": {
|
|
43
|
+
"id_token": "...",
|
|
44
|
+
"access_token": "...",
|
|
45
|
+
"refresh_token": "...",
|
|
46
|
+
"account_id": "..."
|
|
47
|
+
},
|
|
48
|
+
"last_refresh": "2026-02-14T10:00:00.000Z"
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Operations
|
|
53
|
+
|
|
54
|
+
### Add Account (OAuth)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
curl -X POST http://localhost:8081/accounts/add
|
|
58
|
+
|
|
59
|
+
# Returns OAuth URL to open in browser
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Import from Codex App
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
curl -X POST http://localhost:8081/accounts/import
|
|
66
|
+
|
|
67
|
+
# Imports from ~/.codex/auth.json
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### List Accounts
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
curl http://localhost:8081/accounts
|
|
74
|
+
|
|
75
|
+
# Response
|
|
76
|
+
{
|
|
77
|
+
"accounts": [
|
|
78
|
+
{
|
|
79
|
+
"email": "user@gmail.com",
|
|
80
|
+
"accountId": "...",
|
|
81
|
+
"planType": "plus",
|
|
82
|
+
"addedAt": "...",
|
|
83
|
+
"lastUsed": "...",
|
|
84
|
+
"isActive": true,
|
|
85
|
+
"tokenExpired": false,
|
|
86
|
+
"quota": {...}
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"activeAccount": "user@gmail.com",
|
|
90
|
+
"total": 1
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Switch Active Account
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
curl -X POST http://localhost:8081/accounts/switch \
|
|
98
|
+
-H "Content-Type: application/json" \
|
|
99
|
+
-d '{"email":"other@gmail.com"}'
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Switching:
|
|
103
|
+
1. Updates `activeAccount` in `accounts.json`
|
|
104
|
+
2. Updates auth file for the account
|
|
105
|
+
3. Next API calls use new account's credentials
|
|
106
|
+
|
|
107
|
+
### Remove Account
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
curl -X DELETE http://localhost:8081/accounts/user@gmail.com
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Removes:
|
|
114
|
+
- Account from registry
|
|
115
|
+
- Per-account token directory
|
|
116
|
+
|
|
117
|
+
### Refresh Tokens
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# Active account
|
|
121
|
+
curl -X POST http://localhost:8081/accounts/refresh
|
|
122
|
+
|
|
123
|
+
# Specific account
|
|
124
|
+
curl -X POST http://localhost:8081/accounts/user@gmail.com/refresh
|
|
125
|
+
|
|
126
|
+
# All accounts
|
|
127
|
+
curl -X POST http://localhost:8081/accounts/refresh/all
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Token Lifecycle
|
|
131
|
+
|
|
132
|
+
### Expiration
|
|
133
|
+
|
|
134
|
+
- Access tokens expire in ~1 hour (3600 seconds)
|
|
135
|
+
- Refresh tokens are long-lived (weeks/months)
|
|
136
|
+
|
|
137
|
+
### Auto-Refresh
|
|
138
|
+
|
|
139
|
+
- Background refresh every **55 minutes**
|
|
140
|
+
- Startup refresh 2 seconds after server start
|
|
141
|
+
- Proactive refresh 5 minutes before expiry
|
|
142
|
+
|
|
143
|
+
### Token Validation
|
|
144
|
+
|
|
145
|
+
Before each API call:
|
|
146
|
+
1. Check if token is expired or expiring within 5 minutes
|
|
147
|
+
2. If yes, refresh using refresh token
|
|
148
|
+
3. Use new access token for the call
|
|
149
|
+
|
|
150
|
+
## Quota Tracking
|
|
151
|
+
|
|
152
|
+
### Fetch Quota
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
curl http://localhost:8081/accounts/quota
|
|
156
|
+
|
|
157
|
+
# Response
|
|
158
|
+
{
|
|
159
|
+
"success": true,
|
|
160
|
+
"email": "user@gmail.com",
|
|
161
|
+
"quota": {
|
|
162
|
+
"usage": {
|
|
163
|
+
"totalTokenUsage": 15,
|
|
164
|
+
"limit": 100,
|
|
165
|
+
"remaining": 85,
|
|
166
|
+
"percentage": 15,
|
|
167
|
+
"resetAt": "..."
|
|
168
|
+
},
|
|
169
|
+
"account": {...}
|
|
170
|
+
},
|
|
171
|
+
"cached": false
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Web UI Quota Display Rules
|
|
176
|
+
|
|
177
|
+
- The Accounts table displays **remaining quota** as a percentage.
|
|
178
|
+
- Remaining percentage is normalized to `0-100` to avoid broken UI values.
|
|
179
|
+
- If `limitReached=true` or `allowed=false`, UI shows quota as exhausted even when percentage data is missing.
|
|
180
|
+
- If usage data is unavailable, UI shows `-` instead of rendering a broken bar.
|
|
181
|
+
- Reset window is shown using `usage.resetAt` (with fallback to `usage.raw.rate_limit.primary_window.reset_at`).
|
|
182
|
+
- UI also shows a relative countdown (e.g. `Resets in 6d 13h`) when reset data is available.
|
|
183
|
+
|
|
184
|
+
### Refresh All Quotas
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
curl http://localhost:8081/accounts/quota/all
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Account Persistence
|
|
191
|
+
|
|
192
|
+
On server startup:
|
|
193
|
+
1. `ensureAccountsPersist()` loads accounts
|
|
194
|
+
2. Restores active account's auth
|
|
195
|
+
3. Starts auto-refresh timer
|
|
196
|
+
|
|
197
|
+
## Security
|
|
198
|
+
|
|
199
|
+
- Tokens stored locally in `~/.codex-claude-proxy/`
|
|
200
|
+
- Directory permissions: user read/write only
|
|
201
|
+
- Never logged or exposed in API responses
|
|
202
|
+
- Per-account isolation via separate directories
|