@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/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,199 @@
|
|
|
1
|
+
# Codex Claude Proxy
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
_Current dashboard preview: a real capture of the local Web UI with the macOS-style glass layout._
|
|
6
|
+
|
|
7
|
+
[](https://choosealicense.com/licenses/mit/)
|
|
8
|
+
[](https://nodejs.org/)
|
|
9
|
+
[](https://github.com/surajmandalcell/codex-proxy)
|
|
10
|
+
|
|
11
|
+
> **Use Claude Code CLI with the power of ChatGPT Codex models.**
|
|
12
|
+
> 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.
|
|
13
|
+
|
|
14
|
+
| Role | Details |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| Maintainer | Suraj Mandal |
|
|
17
|
+
| GitHub | [surajmandalcell](https://github.com/surajmandalcell) |
|
|
18
|
+
| Contact | [surajmandalcell@gmail.com](mailto:surajmandalcell@gmail.com) |
|
|
19
|
+
| Package | [@pikoloo/codex-proxy](https://www.npmjs.com/package/@pikoloo/codex-proxy) |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 🚀 Features
|
|
24
|
+
|
|
25
|
+
- **Seamless Translation**: Translates Anthropic Messages API calls to ChatGPT Codex format.
|
|
26
|
+
- **Model Mapping**: maps Claude model aliases to current OpenAI models, with direct GPT model IDs passed through.
|
|
27
|
+
- **Personal Account Mode**: Uses the active ChatGPT account by default for local-only personal use, with account switching and auto-refresh.
|
|
28
|
+
- **Web Dashboard**: Built-in macOS-style UI (`http://localhost:8081`) for managing accounts, viewing logs, adjusting settings, and testing prompts.
|
|
29
|
+
- **Streaming Support**: Full Server-Sent Events (SSE) support for real-time responses.
|
|
30
|
+
- **Native Tool Calling**: Supports Claude's tool use capabilities by translating them to Codex function calls.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Security & Privacy
|
|
35
|
+
|
|
36
|
+
**Is this a malicious proxy? No.**
|
|
37
|
+
|
|
38
|
+
- **Local Execution**: This server binds to `127.0.0.1` by default.
|
|
39
|
+
- **Direct Communication by Default**: Claude and GPT model requests connect directly to OpenAI/ChatGPT endpoints.
|
|
40
|
+
- **No Rotation by Default**: Requests use the active account only. Multi-account rotation is disabled unless `CODEX_CLAUDE_PROXY_ENABLE_MULTI_ACCOUNT_ROTATION=true` is set.
|
|
41
|
+
- **Third-Party Opt-In**: The explicit `kilo` model route uses Kilo/OpenRouter-backed free models only when `CODEX_CLAUDE_PROXY_ENABLE_KILO=true` is set. Default routing is OpenAI-only.
|
|
42
|
+
- **Open Source**: The full source code is available here for you to audit.
|
|
43
|
+
- **No Data Collection**: We do not track your prompts, keys, or personal data.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## ⚙️ How it works
|
|
48
|
+
|
|
49
|
+
This tool acts as a "translation layer" between the Claude CLI and ChatGPT's Codex backend.
|
|
50
|
+
|
|
51
|
+
1. **Intercept**: Claude Code CLI sends a request to `localhost:8081` (thinking it's Anthropic's API).
|
|
52
|
+
2. **Translate**: The proxy converts the Anthropic-format JSON into the specific payload format required by ChatGPT's internal Codex API.
|
|
53
|
+
3. **Forward**: The request is sent securely to ChatGPT using your own authenticated session.
|
|
54
|
+
4. **Stream**: The response from ChatGPT is converted back into Anthropic's Server-Sent Events (SSE) format and streamed to your terminal.
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
┌──────────────────┐ ┌─────────────────────┐ ┌────────────────────────────┐
|
|
58
|
+
│ Claude Code │────▶│ This Proxy Server │────▶│ ChatGPT Codex Backend API │
|
|
59
|
+
│ (Anthropic API) │ │ (Anthropic ⇄ OpenAI)│ │ (codex/responses) │
|
|
60
|
+
└──────────────────┘ └─────────────────────┘ └────────────────────────────┘
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
Install globally to use the CLI commands anywhere:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install -g @pikoloo/codex-proxy
|
|
71
|
+
codex-proxy start
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Or run the published package without a global install:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx @pikoloo/codex-proxy@latest start
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
For release work from this checkout, use `make update` and `make publish`.
|
|
81
|
+
|
|
82
|
+
The legacy `codex-claude-proxy` command remains available after installing this package.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 🚦 Quick Start
|
|
87
|
+
|
|
88
|
+
### 1. Start the Proxy
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
codex-proxy start
|
|
92
|
+
```
|
|
93
|
+
The server will start at `http://localhost:8081`.
|
|
94
|
+
|
|
95
|
+
### 2. Add Your Account
|
|
96
|
+
|
|
97
|
+
#### **Option A: Web Dashboard (Local Desktop)**
|
|
98
|
+
|
|
99
|
+
1. Open the dashboard at **[http://localhost:8081](http://localhost:8081)**
|
|
100
|
+
2. Go to the **Accounts** tab
|
|
101
|
+
3. Click **Add Account** and login with your ChatGPT account
|
|
102
|
+
|
|
103
|
+
#### **Option B: CLI (Desktop or Headless/VM)**
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Desktop (opens browser)
|
|
107
|
+
codex-proxy accounts add
|
|
108
|
+
|
|
109
|
+
# Headless/VM server (manual code input)
|
|
110
|
+
codex-proxy accounts add --no-browser
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
For **headless/VM servers** without a browser:
|
|
114
|
+
1. Run the command with `--no-browser`
|
|
115
|
+
2. It will print a URL - copy and open it on a device with a browser
|
|
116
|
+
3. Complete login on that device
|
|
117
|
+
4. After redirect, copy the callback URL (or just the code)
|
|
118
|
+
5. Paste it back in the terminal
|
|
119
|
+
|
|
120
|
+
### 3. Configure Claude Code
|
|
121
|
+
Run this command to automatically configure your `claude` CLI to use the proxy:
|
|
122
|
+
```bash
|
|
123
|
+
curl -X POST http://localhost:8081/claude/config/proxy
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
*Alternatively, set the environment variables manually:*
|
|
127
|
+
```bash
|
|
128
|
+
export ANTHROPIC_BASE_URL=http://localhost:8081
|
|
129
|
+
export ANTHROPIC_API_KEY=dummy-key # The key is ignored but required by the CLI
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
4. **Run Claude**:
|
|
133
|
+
```bash
|
|
134
|
+
claude
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 🧠 Model Mapping
|
|
140
|
+
|
|
141
|
+
The proxy automatically maps Claude model names to current OpenAI backend models. Direct `gpt-*` model IDs are passed through.
|
|
142
|
+
|
|
143
|
+
| Requested Model ID | Upstream Model | Auth Required | Description |
|
|
144
|
+
| :--- | :--- | :---: | :--- |
|
|
145
|
+
| `claude-sonnet-4-5` | `gpt-5.5` | ✅ | Current default high-intelligence model |
|
|
146
|
+
| `claude-opus-4-5` | `gpt-5.5` | ✅ | Current default high-intelligence model |
|
|
147
|
+
| `claude-haiku-4` | `gpt-5.4-mini` | ✅ | OpenAI small-model lane |
|
|
148
|
+
| `codex` | `gpt-5.3-codex` | ✅ | Latest Codex-optimized model |
|
|
149
|
+
| `kilo` | Selected Kilo target | ❌ | Explicit third-party free-model route, disabled unless `CODEX_CLAUDE_PROXY_ENABLE_KILO=true` |
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 🛠️ Configuration & API
|
|
154
|
+
|
|
155
|
+
### Web Dashboard
|
|
156
|
+
|
|
157
|
+
The dashboard uses a clean desktop split-view layout with a compact toolbar, native-feeling glass surfaces, account management, live logs, settings, and prompt test panels. The screenshot at the top of this README is captured from the actual local app.
|
|
158
|
+
|
|
159
|
+
Visit `http://localhost:8081` to:
|
|
160
|
+
- **Manage Accounts**: Add, remove, or switch active ChatGPT accounts.
|
|
161
|
+
- **Personal Mode**: Requests use the active account only unless multi-account rotation is explicitly enabled by environment variable.
|
|
162
|
+
- **View Logs**: See real-time request/response logs for debugging.
|
|
163
|
+
- **Test Models**: Run quick tests against the configured models.
|
|
164
|
+
|
|
165
|
+
### API Endpoints
|
|
166
|
+
- `GET /health`: Check server status.
|
|
167
|
+
- `GET /accounts`: List configured accounts.
|
|
168
|
+
- `POST /v1/messages`: Anthropic-compatible chat completion endpoint.
|
|
169
|
+
|
|
170
|
+
See [API Documentation](./docs/API.md) for full details.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 🤝 Contributing
|
|
175
|
+
|
|
176
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
177
|
+
|
|
178
|
+
1. Fork the repository
|
|
179
|
+
2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
|
|
180
|
+
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
|
181
|
+
4. Push to the branch (`git push origin feature/AmazingFeature`)
|
|
182
|
+
5. Open a Pull Request
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 📄 License
|
|
187
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
188
|
+
|
|
189
|
+
## ⚠️ Disclaimer
|
|
190
|
+
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.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
<div align="center">
|
|
195
|
+
<p>If you find this project useful, please give it a star! ⭐️</p>
|
|
196
|
+
<a href="https://github.com/surajmandalcell/codex-proxy">
|
|
197
|
+
<img src="https://img.shields.io/github/stars/surajmandalcell/codex-proxy?style=social" alt="Star on GitHub">
|
|
198
|
+
</a>
|
|
199
|
+
</div>
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
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 CLI_NAME = 'codex-proxy';
|
|
15
|
+
const LEGACY_CLI_NAME = 'codex-claude-proxy';
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
const command = args[0];
|
|
18
|
+
|
|
19
|
+
function showHelp() {
|
|
20
|
+
console.log(`
|
|
21
|
+
${CLI_NAME} v${packageJson.version}
|
|
22
|
+
|
|
23
|
+
Proxy server for using ChatGPT Codex models with Claude Code CLI.
|
|
24
|
+
|
|
25
|
+
USAGE:
|
|
26
|
+
${CLI_NAME} <command> [options]
|
|
27
|
+
|
|
28
|
+
COMMANDS:
|
|
29
|
+
start Start the proxy server (default port: 8081)
|
|
30
|
+
accounts Manage ChatGPT accounts (interactive)
|
|
31
|
+
accounts add Add a new ChatGPT account via OAuth
|
|
32
|
+
accounts add --no-browser Add account manually (headless/VM)
|
|
33
|
+
accounts list List all configured accounts
|
|
34
|
+
accounts remove Remove accounts interactively
|
|
35
|
+
accounts verify Verify account tokens are valid
|
|
36
|
+
accounts clear Remove all accounts
|
|
37
|
+
|
|
38
|
+
OPTIONS:
|
|
39
|
+
--help, -h Show this help message
|
|
40
|
+
--version, -v Show version number
|
|
41
|
+
|
|
42
|
+
ENVIRONMENT:
|
|
43
|
+
PORT Server port (default: 8081)
|
|
44
|
+
|
|
45
|
+
EXAMPLES:
|
|
46
|
+
${CLI_NAME} start
|
|
47
|
+
PORT=3000 ${CLI_NAME} start
|
|
48
|
+
${CLI_NAME} accounts add
|
|
49
|
+
${CLI_NAME} accounts add --no-browser
|
|
50
|
+
${CLI_NAME} accounts list
|
|
51
|
+
${CLI_NAME} accounts verify
|
|
52
|
+
|
|
53
|
+
ALIASES:
|
|
54
|
+
${LEGACY_CLI_NAME} Legacy command name, still supported
|
|
55
|
+
|
|
56
|
+
HEADLESS/VM USAGE:
|
|
57
|
+
1. Run: ${CLI_NAME} accounts add --no-browser
|
|
58
|
+
2. Copy the URL shown and open in browser on another device
|
|
59
|
+
3. After login, paste the callback URL back in terminal
|
|
60
|
+
|
|
61
|
+
CONFIGURATION:
|
|
62
|
+
Claude Code CLI (~/.claude/settings.json):
|
|
63
|
+
{
|
|
64
|
+
"env": {
|
|
65
|
+
"ANTHROPIC_BASE_URL": "http://localhost:8081",
|
|
66
|
+
"ANTHROPIC_API_KEY": "dummy"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function showVersion() {
|
|
73
|
+
console.log(packageJson.version);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function main() {
|
|
77
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
78
|
+
showHelp();
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
83
|
+
showVersion();
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
switch (command) {
|
|
88
|
+
case 'start':
|
|
89
|
+
case undefined:
|
|
90
|
+
await import('../src/index.js');
|
|
91
|
+
break;
|
|
92
|
+
|
|
93
|
+
case 'accounts': {
|
|
94
|
+
const subCommand = args[1] || 'add';
|
|
95
|
+
process.argv = ['node', 'accounts-cli.js', subCommand, ...args.slice(2)];
|
|
96
|
+
await import('../src/cli/accounts.js');
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
case 'help':
|
|
101
|
+
showHelp();
|
|
102
|
+
break;
|
|
103
|
+
|
|
104
|
+
case 'version':
|
|
105
|
+
showVersion();
|
|
106
|
+
break;
|
|
107
|
+
|
|
108
|
+
default:
|
|
109
|
+
console.error(`Unknown command: ${command}`);
|
|
110
|
+
console.error(`Run "${CLI_NAME} --help" for usage information.`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
main().catch((err) => {
|
|
116
|
+
console.error('Error:', err.message);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
});
|
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
|