prodex 0.2.38

Safe multi-account auto-rotate for Codex CLI with isolated CODEX_HOME profiles
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
# prodex

Safe multi-account auto-rotate for `codex`.

`prodex` is a CLI wrapper for `codex` that separates multiple profiles by giving each one its own `CODEX_HOME`.

Install from [crates.io](https://crates.io/crates/prodex):

```bash
cargo install prodex
```

For the shorter version, see [QUICKSTART.md](./QUICKSTART.md).

In short:

- one `prodex` profile = one `CODEX_HOME` directory
- login is still handled by `codex`
- `prodex` manages profiles, the active profile, built-in quota checks, and launching `codex`
- the headline feature is built-in auto-rotate across multiple logged-in ChatGPT accounts, so `prodex run` can keep working when the current profile is quota-blocked or temporarily unhealthy
- `prodex run` uses a local runtime proxy that keeps Codex transport behavior as intact as possible while rotating accounts safely
- Prodex-owned screens adapt to the current terminal width, and live views can also adapt to terminal height when that improves readability without silently breaking sort order

The mental model is similar to browser profiles, but for `codex`.

## Quick Start

### 1. Install

```bash
cargo install prodex
```

Package page:

[https://crates.io/crates/prodex](https://crates.io/crates/prodex)

### 2. Import your current `codex` profile

If you already have an active login in `~/.codex`:

```bash
prodex profile import-current main
```

This will:

- copy `~/.codex` into a new managed profile
- store the profile in `~/.prodex/profiles/main`
- set `main` as the active profile

### 3. Log in and let `prodex` create the profile

If you want a fresh login, `prodex` can create or reuse a profile automatically from the account email:

```bash
prodex login
```

This will:

- run `codex login` in a temporary isolated `CODEX_HOME`
- resolve the logged-in account email from the ChatGPT `id_token` stored in `auth.json`
- create a managed profile whose name is derived from that email
- reuse the existing profile instead of creating a duplicate when that email is already registered
- switch the active profile to the reused or newly created profile

If the email-derived profile name is already taken by a different account, `prodex` keeps the email uniqueness rule and creates a suffixed name such as `main_example.com-2`.

If you want to target a specific existing profile name instead:

```bash
prodex profile add second
prodex login --profile second
```

Use `prodex login --profile <name>` when you want a fixed profile name, or when you are not using the ChatGPT login flow that writes an email-bearing `id_token`.

`prodex login` still delegates the actual authentication flow to `codex`.

### 4. View all quotas

```bash
prodex quota --all
```

`prodex quota --all` now refreshes continuously by default every 5 seconds. It renders a `Quota Overview` table that adapts to the current terminal width. The main quota summary appears in the `REMAINING` column, and each profile gets a wrapped `status:` detail line underneath the row.

Press `Ctrl+C` to stop the live refresh loop.

The previous snapshot stays on screen while the next refresh is loading, so the view does not go blank between updates.

When the terminal is not tall enough to show every profile, the live `--all` view keeps the existing sort order and shows only the top rows that fit, plus a summary of how many profiles are hidden.

If you also want the exact reset timestamps for the required main windows:

```bash
prodex quota --all --detail
```

This adds a `resets:` line under each profile row, including the full local timestamp for both `5h` and `weekly`.

Use `--once` when you want a single snapshot instead of continuous refresh:

```bash
prodex quota --all --once
```

Example `REMAINING` value:

```text
5h 63% left | weekly 88% left
```

### 5. Select the active profile and run `codex`

```bash
prodex use main
prodex run
```

Or run directly with a specific profile:

```bash
prodex run --profile second
```

By default, `prodex run` will auto-rotate to another ready profile when the current one is blocked by quota or temporary runtime health signals. Use `--no-auto-rotate` only when you explicitly want to stay pinned to one profile and fail there.

## Why Prodex

`prodex` is primarily useful when you keep more than one ChatGPT-backed `codex` login and want account rotation built in.

The main value is:

- automatic preflight rotation when the selected profile does not have clear remaining required quota
- safe runtime rotation before a request or stream is committed
- hard affinity preservation for ongoing chains, so rotation does not break continuations
- transport behavior kept close to direct `codex`, so reconnect and fallback still feel native

## Requirements

`prodex` relies on the following binaries:

- `codex`

Quick check:

```bash
codex --help
```

If you want to audit the `prodex` environment:

```bash
prodex doctor
prodex doctor --quota
prodex doctor --runtime
```

## How It Works

`prodex` stores its own state in:

```text
~/.prodex
```

The main structure is:

- `state.json`: the list of profiles and the active profile
- `profiles/<name>`: the managed `CODEX_HOME` for each profile

Authentication is still stored by `codex` inside each profile's `auth.json`.

## Most Common Commands

### Profile Management

Install from crates.io:

```bash
cargo install prodex
```

Add an empty profile:

```bash
prodex profile add work
```

Import from `~/.codex`:

```bash
prodex profile import-current work
```

List all profiles:

```bash
prodex profile list
```

This renders a `Profiles` panel with wrapped fields and one panel per profile.

Select the active profile:

```bash
prodex use work
```

Remove a profile:

```bash
prodex profile remove work
```

Remove a profile and its managed home:

```bash
prodex profile remove work --delete-home
```

### Login/Logout

Log in and auto-create or reuse a unique profile based on the email you use:

```bash
prodex login
```

This only works when the login flow writes a ChatGPT `id_token` with an email claim into `auth.json`.

Log in to a specific profile:

```bash
prodex login --profile work
```

Log out from a specific profile:

```bash
prodex logout --profile work
```

### Quota

Show quota for one profile:

```bash
prodex quota --profile work
```

`prodex quota --profile work` also refreshes continuously by default every 5 seconds. Add `--once` when you want a single snapshot:

```bash
prodex quota --profile work --once
```

Show raw quota JSON:

```bash
prodex quota --profile work --raw
```

`--raw` remains a one-shot command.

View all profiles at once:

```bash
prodex quota --all
```

This continuously refreshes a `Quota Overview` table with `PROFILE`, `CUR`, `AUTH`, `ACCOUNT`, `PLAN`, and `REMAINING`, plus a `status:` line for each profile. Add `--once` if you want a single render.

Add `--detail` to include exact local reset timestamps for `5h` and `weekly` under each row:

```bash
prodex quota --all --detail
```

### Run `codex`

Run `codex` with the active profile:

```bash
prodex run
```

Run `codex` with arguments:

```bash
prodex run -- --version
prodex run exec "review this repo"
```

Run with a specific profile:

```bash
prodex run --profile work
```

Temporarily disable auto-rotate:

```bash
prodex run --profile work --no-auto-rotate
```

Skip quota preflight:

```bash
prodex run --profile work --skip-quota-check
```

`prodex run` is the core command of the project. Its main job is to auto-rotate between profiles safely while keeping the transport side as close as possible to direct `codex`.

At runtime, `prodex` keeps the transport side as close as possible to direct `codex`:

- WebSocket and HTTP/SSE traffic still follow Codex reconnect and fallback behavior
- account rotation happens inside the proxy, but only before a request or stream is committed
- existing chains stay pinned through `previous_response_id` and `x-codex-turn-state`
- session-scoped unary routes such as `/responses/compact` also honor `session_id -> profile` affinity when a session owner is already known
- temporary quota, overload, or transport failures can move later requests to another ready profile without rotating mid-stream
- the current profile is still tried optimistically first when it looks healthy
- new candidate selection is also load-aware, so one profile is less likely to become a hotspot when several terminals are active at once
- fresh pre-commit selection also respects a short per-profile in-flight cap, so new work fails fast instead of piling more pressure onto an already busy account
- that per-profile cap only applies to fresh pre-commit selection; it does not override hard affinity for an existing continuation
- local proxy admission is also lane-aware, so bursty `compact`, `responses`, `websocket`, and other unary traffic are less likely to starve each other
- lane-aware admission is also only for fresh local admission; it does not override hard affinity for an existing continuation
- quota backoff, transport backoff, and short-lived profile health penalties are tracked separately so the proxy can stop hammering a flaky account without weakening hard affinity
- short-lived profile health penalties are endpoint-specific, so a hot `/responses/compact` path or flaky WebSocket route does not automatically poison fresh `responses` selection
- pre-commit candidate selection is bounded, so when all candidates are currently bad the proxy fails fast instead of spinning in the background for too long
- when a fresh request only exhausts local selection heuristics, the proxy may make one last direct attempt on the current profile before giving up
- generic upstream `429 Too Many Requests` responses are passed through; they are not treated as account-specific quota unless the upstream payload explicitly reports `insufficient_quota` or `rate_limit_exceeded`
- if the proxy cannot secure a healthy upstream profile before any upstream response exists, it now fails with local `503 service_unavailable` instead of synthesizing a local quota `429`
- the unary compact path (`/responses/compact`) is also eligible for safe retry and rotation on temporary overload or quota exhaustion
- `session_id` affinity is persisted in Prodex state, so compact and other session-scoped unary routes can keep using the owning profile after a proxy restart

## Quota Behavior

Before `prodex run` launches `codex`, `prodex` tries to check quota for the selected profile.

This preflight is the first layer of auto-rotate.

Before a profile is considered safe to use, `prodex` requires both the `5h` and `weekly` quota windows to be present and still have remaining capacity.

If that profile does not clearly have remaining required quota:

- `prodex run` tries to rotate to the next ready profile by default, including when you pass `--profile`
- if you want the command to stay blocked on that profile instead, use `--no-auto-rotate`
- it prints the missing, unknown, or exhausted quota reasons
- it suggests other profiles that appear ready, when available

If auto-rotate succeeds, the active profile is updated to the profile that was used.

## Runtime Diagnostics

If a session appears stalled or reconnect-heavy, inspect the latest runtime proxy log:

```bash
prodex doctor --runtime
cat /tmp/prodex-runtime-latest.path
tail -n 200 "$(cat /tmp/prodex-runtime-latest.path)"
```

Useful files:

- `/tmp/prodex-runtime-latest.path`: pointer to the latest runtime log
- `/tmp/prodex-runtime-*.log`: per-run transport, fallback, and rotation diagnostics

Useful log markers:

- `runtime_proxy_queue_overloaded`
- `runtime_proxy_active_limit_reached`
- `runtime_proxy_lane_limit_reached`
- `runtime_proxy_overload_backoff`
- `profile_inflight_saturated`
- `profile_retry_backoff`
- `profile_transport_backoff`
- `profile_inflight`
- `profile_health`
- `precommit_budget_exhausted`
- `first_upstream_chunk`
- `first_local_chunk`
- `stream_read_error`

If `profile_health` appears, also check its `route=` value before changing selection behavior globally.
If `runtime_proxy_lane_limit_reached` appears, inspect its `lane=` value before changing upstream-facing behavior.
Repeated `lane=responses` markers suggest the main model lane is saturated locally; repeated non-`responses` markers usually mean a side lane is consuming proxy capacity.
If `runtime_proxy_active_limit_reached` or `profile_inflight_saturated` appears repeatedly without matching quota or transport markers, suspect local concurrency pressure before changing upstream-facing behavior.
If users report `exceeded retry limit, last status: 429 Too Many Requests`, confirm whether the latest runtime log shows real upstream quota markers or only local pre-commit exhaustion before changing rotation behavior.

## Important Notes

- quota checks are built into `prodex` and use the ChatGPT backend endpoint used by Codex
- ChatGPT quota can only be read when the profile uses ChatGPT auth, not an API key
- `prodex login` without `--profile` depends on being able to read the ChatGPT account email from `tokens.id_token` in `auth.json`
- if a profile uses API key auth, `quota --all` will show `error` for that profile
- managed Prodex profiles share the default `~/.codex` session history store, so `/resume` shows the same saved threads across accounts
- `profile list`, `current`, `doctor`, `login`, `quota`, and other Prodex-owned screens adapt to the current terminal width instead of assuming a fixed 110-character layout
- runtime proxy diagnostics are written to `/tmp`, not to the Codex TUI
- `prodex` does not replace `codex`; it only acts as a launcher and profile manager

## Environment Variables

Override the `prodex` state location:

```bash
PRODEX_HOME=/path/to/prodex-home
```

Override the `codex` binary:

```bash
PRODEX_CODEX_BIN=/path/to/codex
```

Override the default ChatGPT quota base URL:

```bash
CODEX_CHATGPT_BASE_URL=https://chatgpt.com/backend-api
```

## Development

Run during development:

```bash
cargo run -- profile list
cargo run -- quota --all
cargo run -- doctor
```

Tests:

```bash
cargo test
```