codex-manager 1.0.0__tar.gz
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.
- codex_manager-1.0.0/CODEX_MANAGER_SPEC.md +427 -0
- codex_manager-1.0.0/PKG-INFO +460 -0
- codex_manager-1.0.0/README.md +154 -0
- codex_manager-1.0.0/pyproject.toml +108 -0
- codex_manager-1.0.0/setup.cfg +4 -0
- codex_manager-1.0.0/src/codex_manager/__init__.py +3 -0
- codex_manager-1.0.0/src/codex_manager/args.py +484 -0
- codex_manager-1.0.0/src/codex_manager/backup.py +183 -0
- codex_manager-1.0.0/src/codex_manager/cli.py +219 -0
- codex_manager-1.0.0/src/codex_manager/config.py +50 -0
- codex_manager-1.0.0/src/codex_manager/cooldown.py +113 -0
- codex_manager-1.0.0/src/codex_manager/doctor.py +69 -0
- codex_manager-1.0.0/src/codex_manager/inventory.py +32 -0
- codex_manager-1.0.0/src/codex_manager/list_backups.py +129 -0
- codex_manager-1.0.0/src/codex_manager/normalize.py +119 -0
- codex_manager-1.0.0/src/codex_manager/profile.py +33 -0
- codex_manager-1.0.0/src/codex_manager/prune.py +80 -0
- codex_manager-1.0.0/src/codex_manager/prune_backups.py +63 -0
- codex_manager-1.0.0/src/codex_manager/recommend.py +54 -0
- codex_manager-1.0.0/src/codex_manager/restore.py +145 -0
- codex_manager-1.0.0/src/codex_manager/status.py +180 -0
- codex_manager-1.0.0/src/codex_manager/sync.py +97 -0
- codex_manager-1.0.0/src/codex_manager/use_account.py +112 -0
- codex_manager-1.0.0/src/codex_manager.egg-info/PKG-INFO +460 -0
- codex_manager-1.0.0/src/codex_manager.egg-info/SOURCES.txt +42 -0
- codex_manager-1.0.0/src/codex_manager.egg-info/dependency_links.txt +1 -0
- codex_manager-1.0.0/src/codex_manager.egg-info/entry_points.txt +3 -0
- codex_manager-1.0.0/src/codex_manager.egg-info/requires.txt +16 -0
- codex_manager-1.0.0/src/codex_manager.egg-info/top_level.txt +1 -0
- codex_manager-1.0.0/tests/test_backup.py +133 -0
- codex_manager-1.0.0/tests/test_config.py +34 -0
- codex_manager-1.0.0/tests/test_cooldown.py +85 -0
- codex_manager-1.0.0/tests/test_doctor.py +67 -0
- codex_manager-1.0.0/tests/test_inventory.py +30 -0
- codex_manager-1.0.0/tests/test_list_backups.py +152 -0
- codex_manager-1.0.0/tests/test_normalize.py +92 -0
- codex_manager-1.0.0/tests/test_profile.py +68 -0
- codex_manager-1.0.0/tests/test_prune.py +65 -0
- codex_manager-1.0.0/tests/test_prune_backups.py +104 -0
- codex_manager-1.0.0/tests/test_recommend.py +73 -0
- codex_manager-1.0.0/tests/test_restore.py +101 -0
- codex_manager-1.0.0/tests/test_status.py +35 -0
- codex_manager-1.0.0/tests/test_sync.py +92 -0
- codex_manager-1.0.0/tests/test_use.py +92 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
# Codex Manager Spec
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Build a `codex-manager` CLI for OpenAI Codex account snapshots, modeled after `geminiai_cli` where that design is sound, but adapted to Codex's weekly quota behavior.
|
|
6
|
+
|
|
7
|
+
The manager should:
|
|
8
|
+
|
|
9
|
+
- normalize existing Codex account artifacts into a stable machine-readable format
|
|
10
|
+
- back up and restore Codex account state safely
|
|
11
|
+
- track weekly availability based on session start time, not quota-end time
|
|
12
|
+
- recommend the next available account
|
|
13
|
+
- keep filename conventions simple and put richer timing details into metadata
|
|
14
|
+
|
|
15
|
+
## Source Context
|
|
16
|
+
|
|
17
|
+
This repo already contains a working `geminiai_cli` reference implementation.
|
|
18
|
+
|
|
19
|
+
Relevant patterns confirmed from that codebase:
|
|
20
|
+
|
|
21
|
+
- `backup.py` uses `timestamp + email` naming
|
|
22
|
+
- `cooldown.py` tracks `first_used` separately from `last_used`
|
|
23
|
+
- `cli.py` and `args.py` define a command-oriented structure we can mirror
|
|
24
|
+
|
|
25
|
+
Codex sample data lives in `.codex_sample/` and includes:
|
|
26
|
+
|
|
27
|
+
- shared state files such as `auth.json`, `history.jsonl`, sqlite state, sessions, logs
|
|
28
|
+
- many per-account auth snapshots named like `21apr_drdpsbose023@gmail.com_auth.json`
|
|
29
|
+
|
|
30
|
+
## Weekly Quota Model
|
|
31
|
+
|
|
32
|
+
For Codex, the controlling timestamp is:
|
|
33
|
+
|
|
34
|
+
- `session_start_at`
|
|
35
|
+
|
|
36
|
+
Not:
|
|
37
|
+
|
|
38
|
+
- auth file modified time
|
|
39
|
+
- quota-ended time
|
|
40
|
+
- availability date encoded in the legacy filename
|
|
41
|
+
|
|
42
|
+
For new live backups, the manager should obtain exact status from Codex `/status`.
|
|
43
|
+
|
|
44
|
+
Preferred live source:
|
|
45
|
+
|
|
46
|
+
- current `~/.codex/auth.json` for the active account identity where available
|
|
47
|
+
- Codex `/status` output for actual account email and actual reset time
|
|
48
|
+
|
|
49
|
+
Derived fields:
|
|
50
|
+
|
|
51
|
+
- `quota_end_detected_at`: when the auth snapshot file was last modified
|
|
52
|
+
- `session_start_at`: inferred start time of the weekly session
|
|
53
|
+
- `next_available_at`: `session_start_at + 7 days`
|
|
54
|
+
|
|
55
|
+
For live status-based backups:
|
|
56
|
+
|
|
57
|
+
- `reset_at` comes directly from `/status`
|
|
58
|
+
- `session_start_at = reset_at - 7 days`
|
|
59
|
+
- no prediction is needed beyond subtracting the fixed weekly window
|
|
60
|
+
|
|
61
|
+
Example:
|
|
62
|
+
|
|
63
|
+
- legacy file: `21apr_drdpsbose023@gmail.com_auth.json`
|
|
64
|
+
- mtime: `2026-04-14 17:55:00`
|
|
65
|
+
- inferred session duration before exhaustion: `2 hours`
|
|
66
|
+
- therefore `session_start_at = 2026-04-14 15:55:00`
|
|
67
|
+
- `next_available_at = 2026-04-21 15:55:00`
|
|
68
|
+
|
|
69
|
+
## Archive Naming
|
|
70
|
+
|
|
71
|
+
Recommended archive naming format:
|
|
72
|
+
|
|
73
|
+
`yyyy-mm-dd-hhmmss-email-codex.tar.gz`
|
|
74
|
+
|
|
75
|
+
Example:
|
|
76
|
+
|
|
77
|
+
`2026-04-14-155500-drdpsbose023@gmail.com-codex.tar.gz`
|
|
78
|
+
|
|
79
|
+
For live backups, the filename timestamp should be built from exact `session_start_at` derived from `/status`, not from legacy auth-file mtimes.
|
|
80
|
+
|
|
81
|
+
This is preferred over `dd-mm-yyyy-hhmmss-...` because:
|
|
82
|
+
|
|
83
|
+
- it sorts lexically and chronologically
|
|
84
|
+
- it matches the existing Gemini philosophy of timestamp-first naming
|
|
85
|
+
- it uses the actual quota anchor event: session start
|
|
86
|
+
|
|
87
|
+
## Filename Contract
|
|
88
|
+
|
|
89
|
+
The archive filename must encode only:
|
|
90
|
+
|
|
91
|
+
- `session_start_at`
|
|
92
|
+
- `email`
|
|
93
|
+
- product suffix `codex`
|
|
94
|
+
|
|
95
|
+
The filename must not try to encode:
|
|
96
|
+
|
|
97
|
+
- inferred quota duration
|
|
98
|
+
- quota end detection time
|
|
99
|
+
- availability date separately
|
|
100
|
+
- source legacy filename
|
|
101
|
+
|
|
102
|
+
Those belong in metadata.
|
|
103
|
+
|
|
104
|
+
## Metadata Contract
|
|
105
|
+
|
|
106
|
+
Each archive should have a sidecar metadata file with the same base name:
|
|
107
|
+
|
|
108
|
+
`yyyy-mm-dd-hhmmss-email-codex.metadata.json`
|
|
109
|
+
|
|
110
|
+
Example:
|
|
111
|
+
|
|
112
|
+
`2026-04-14-155500-drdpsbose023@gmail.com-codex.metadata.json`
|
|
113
|
+
|
|
114
|
+
Suggested schema:
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"product": "codex",
|
|
119
|
+
"email": "drdpsbose023@gmail.com",
|
|
120
|
+
"session_start_at": "2026-04-14T15:55:00+05:30",
|
|
121
|
+
"next_available_at": "2026-04-21T15:55:00+05:30",
|
|
122
|
+
"reset_at": "2026-04-21T15:55:00+05:30",
|
|
123
|
+
"quota_end_detected_at": "2026-04-14T17:55:00+05:30",
|
|
124
|
+
"inferred_session_duration_seconds": 7200,
|
|
125
|
+
"inference_basis": "legacy filename day-month plus auth file mtime minus configured session duration",
|
|
126
|
+
"legacy_auth_filename": "21apr_drdpsbose023@gmail.com_auth.json",
|
|
127
|
+
"legacy_quota_day_token": "21apr",
|
|
128
|
+
"source_codex_home": "~/.codex",
|
|
129
|
+
"captured_files": [
|
|
130
|
+
"auth.json",
|
|
131
|
+
"history.jsonl",
|
|
132
|
+
"state_5.sqlite",
|
|
133
|
+
"sessions/"
|
|
134
|
+
],
|
|
135
|
+
"created_at": "2026-04-19T15:30:00+05:30",
|
|
136
|
+
"manager_version": "0.1.0"
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Legacy Normalization Rules
|
|
141
|
+
|
|
142
|
+
Legacy auth files follow this rough pattern:
|
|
143
|
+
|
|
144
|
+
`<day><mon>_<email>_auth.json`
|
|
145
|
+
|
|
146
|
+
Example:
|
|
147
|
+
|
|
148
|
+
`21apr_drdpsbose023@gmail.com_auth.json`
|
|
149
|
+
|
|
150
|
+
Interpretation rule:
|
|
151
|
+
|
|
152
|
+
- `<day><mon>` is the date when quota becomes available again
|
|
153
|
+
- year is inferred from the current operating year unless explicitly overridden
|
|
154
|
+
- file mtime is treated as `quota_end_detected_at`
|
|
155
|
+
- `session_start_at = quota_end_detected_at - inferred_session_duration`
|
|
156
|
+
- `next_available_at = session_start_at + 7 days`
|
|
157
|
+
|
|
158
|
+
That means the legacy day token is a validation signal, not the primary timestamp we preserve.
|
|
159
|
+
|
|
160
|
+
## Validation Rule During Normalization
|
|
161
|
+
|
|
162
|
+
When normalizing legacy data:
|
|
163
|
+
|
|
164
|
+
1. parse the legacy token, for example `21apr`
|
|
165
|
+
2. infer `session_start_at`
|
|
166
|
+
3. compute `next_available_at`
|
|
167
|
+
4. verify that `next_available_at` falls on the same calendar day as the legacy token
|
|
168
|
+
|
|
169
|
+
If the day does not match:
|
|
170
|
+
|
|
171
|
+
- keep the record
|
|
172
|
+
- mark it as `validation_status = "mismatch"`
|
|
173
|
+
- do not silently rewrite the evidence
|
|
174
|
+
|
|
175
|
+
## Duration Inference
|
|
176
|
+
|
|
177
|
+
The 2-hour subtraction in your example should not be hardcoded into the filename logic.
|
|
178
|
+
|
|
179
|
+
It should be a configurable normalization parameter:
|
|
180
|
+
|
|
181
|
+
- `--session-duration-hours`
|
|
182
|
+
|
|
183
|
+
Default for first-pass normalization:
|
|
184
|
+
|
|
185
|
+
- `2`
|
|
186
|
+
|
|
187
|
+
Reason:
|
|
188
|
+
|
|
189
|
+
- old files do not appear to contain enough readable metadata to derive exact session start purely from content
|
|
190
|
+
- different accounts or future operating behavior may require a different duration assumption
|
|
191
|
+
|
|
192
|
+
## Timezone Rule
|
|
193
|
+
|
|
194
|
+
Store full ISO timestamps with timezone in metadata.
|
|
195
|
+
|
|
196
|
+
For filenames:
|
|
197
|
+
|
|
198
|
+
- use local wall-clock time rendered as `YYYY-MM-DD-HHMMSS`
|
|
199
|
+
|
|
200
|
+
This keeps filenames readable while metadata stays unambiguous.
|
|
201
|
+
|
|
202
|
+
## What Gets Backed Up
|
|
203
|
+
|
|
204
|
+
Codex backups should represent account-usable state, not only auth snapshots.
|
|
205
|
+
|
|
206
|
+
Minimum snapshot set:
|
|
207
|
+
|
|
208
|
+
- `auth.json`
|
|
209
|
+
- account-specific auth snapshot copied into the archive manifest
|
|
210
|
+
- `config.toml`
|
|
211
|
+
- `history.jsonl`
|
|
212
|
+
- `sessions/`
|
|
213
|
+
- `state_5.sqlite*`
|
|
214
|
+
- `logs_2.sqlite*`
|
|
215
|
+
- `models_cache.json`
|
|
216
|
+
- `installation_id`
|
|
217
|
+
- any other files required for a functioning restore after validation
|
|
218
|
+
|
|
219
|
+
Files or directories that are clearly ephemeral can be made optional:
|
|
220
|
+
|
|
221
|
+
- `tmp/`
|
|
222
|
+
- `.tmp/`
|
|
223
|
+
- transient caches
|
|
224
|
+
|
|
225
|
+
## Backup Source Priority
|
|
226
|
+
|
|
227
|
+
When creating a new backup:
|
|
228
|
+
|
|
229
|
+
1. query live Codex `/status`
|
|
230
|
+
2. parse exact `email` and exact `reset_at`
|
|
231
|
+
3. derive `session_start_at = reset_at - 7 days`
|
|
232
|
+
4. build archive name from that exact session start
|
|
233
|
+
5. capture current `~/.codex` state
|
|
234
|
+
|
|
235
|
+
Legacy `*_auth.json` normalization remains useful for historical inventory and recovery, but should not be the primary source for naming new backups.
|
|
236
|
+
|
|
237
|
+
## Proposed Commands
|
|
238
|
+
|
|
239
|
+
Mirror `geminiai_cli` at a high level:
|
|
240
|
+
|
|
241
|
+
- `codex-manager backup`
|
|
242
|
+
- `codex-manager restore`
|
|
243
|
+
- `codex-manager list-backups`
|
|
244
|
+
- `codex-manager cooldown`
|
|
245
|
+
- `codex-manager recommend`
|
|
246
|
+
- `codex-manager normalize`
|
|
247
|
+
- `codex-manager profile`
|
|
248
|
+
- `codex-manager sync`
|
|
249
|
+
- `codex-manager prune`
|
|
250
|
+
- `codex-manager doctor`
|
|
251
|
+
- `codex-manager config`
|
|
252
|
+
|
|
253
|
+
## Command Responsibilities
|
|
254
|
+
|
|
255
|
+
### `normalize`
|
|
256
|
+
|
|
257
|
+
Consumes legacy auth files and emits a normalized inventory.
|
|
258
|
+
|
|
259
|
+
Responsibilities:
|
|
260
|
+
|
|
261
|
+
- scan legacy `*_auth.json` files
|
|
262
|
+
- parse legacy day tokens and emails
|
|
263
|
+
- read file mtimes
|
|
264
|
+
- infer `session_start_at`
|
|
265
|
+
- compute `next_available_at`
|
|
266
|
+
- validate against the legacy quota-day token
|
|
267
|
+
- optionally create normalized archive names and metadata records
|
|
268
|
+
|
|
269
|
+
### `backup`
|
|
270
|
+
|
|
271
|
+
Creates a Codex snapshot archive.
|
|
272
|
+
|
|
273
|
+
Responsibilities:
|
|
274
|
+
|
|
275
|
+
- read live status from Codex `/status`
|
|
276
|
+
- extract exact `email` and exact `reset_at`
|
|
277
|
+
- derive exact `session_start_at = reset_at - 7 days`
|
|
278
|
+
- build archive base name from `session_start_at` and `email`
|
|
279
|
+
- capture the Codex state set
|
|
280
|
+
- write `.tar.gz`
|
|
281
|
+
- write sidecar metadata json
|
|
282
|
+
- optionally upload to cloud later if desired
|
|
283
|
+
|
|
284
|
+
### `restore`
|
|
285
|
+
|
|
286
|
+
Restores a selected archive back into the active Codex home.
|
|
287
|
+
|
|
288
|
+
Responsibilities:
|
|
289
|
+
|
|
290
|
+
- choose archive by filename or metadata
|
|
291
|
+
- validate archive contents
|
|
292
|
+
- restore atomically
|
|
293
|
+
- optionally preserve current state as a safety backup
|
|
294
|
+
|
|
295
|
+
### `cooldown`
|
|
296
|
+
|
|
297
|
+
Shows current weekly availability.
|
|
298
|
+
|
|
299
|
+
Responsibilities:
|
|
300
|
+
|
|
301
|
+
- read normalized metadata records
|
|
302
|
+
- compute `next_available_at`
|
|
303
|
+
- show `ready` vs `cooldown`
|
|
304
|
+
- display start time, detected end time, and next available time
|
|
305
|
+
|
|
306
|
+
### `recommend`
|
|
307
|
+
|
|
308
|
+
Selects the best account to use next.
|
|
309
|
+
|
|
310
|
+
Priority:
|
|
311
|
+
|
|
312
|
+
1. available now
|
|
313
|
+
2. least recently used
|
|
314
|
+
3. earliest next available if none are ready
|
|
315
|
+
|
|
316
|
+
## Proposed Package Layout
|
|
317
|
+
|
|
318
|
+
Match the Gemini shape where practical:
|
|
319
|
+
|
|
320
|
+
```text
|
|
321
|
+
src/codex_manager/
|
|
322
|
+
├── cli.py
|
|
323
|
+
├── args.py
|
|
324
|
+
├── config.py
|
|
325
|
+
├── backup.py
|
|
326
|
+
├── restore.py
|
|
327
|
+
├── normalize.py
|
|
328
|
+
├── inventory.py
|
|
329
|
+
├── cooldown.py
|
|
330
|
+
├── recommend.py
|
|
331
|
+
├── profile.py
|
|
332
|
+
├── sync.py
|
|
333
|
+
├── prune.py
|
|
334
|
+
├── doctor.py
|
|
335
|
+
├── metadata.py
|
|
336
|
+
├── utils.py
|
|
337
|
+
└── ui.py
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Module Mapping From `geminiai_cli`
|
|
341
|
+
|
|
342
|
+
Direct parallels:
|
|
343
|
+
|
|
344
|
+
- `geminiai_cli.cli` -> `codex_manager.cli`
|
|
345
|
+
- `geminiai_cli.args` -> `codex_manager.args`
|
|
346
|
+
- `geminiai_cli.config` -> `codex_manager.config`
|
|
347
|
+
- `geminiai_cli.backup` -> `codex_manager.backup`
|
|
348
|
+
- `geminiai_cli.restore` -> `codex_manager.restore`
|
|
349
|
+
- `geminiai_cli.cooldown` -> `codex_manager.cooldown`
|
|
350
|
+
- `geminiai_cli.recommend` -> `codex_manager.recommend`
|
|
351
|
+
- `geminiai_cli.profile` -> `codex_manager.profile`
|
|
352
|
+
- `geminiai_cli.sync` -> `codex_manager.sync`
|
|
353
|
+
- `geminiai_cli.prune` -> `codex_manager.prune`
|
|
354
|
+
- `geminiai_cli.doctor` -> `codex_manager.doctor`
|
|
355
|
+
|
|
356
|
+
Codex-specific additions:
|
|
357
|
+
|
|
358
|
+
- `codex_manager.normalize`
|
|
359
|
+
- `codex_manager.inventory`
|
|
360
|
+
- `codex_manager.metadata`
|
|
361
|
+
|
|
362
|
+
These are needed because the Codex side starts with a legacy auth-file naming problem that Gemini does not have.
|
|
363
|
+
|
|
364
|
+
## Storage Layout
|
|
365
|
+
|
|
366
|
+
Suggested manager home:
|
|
367
|
+
|
|
368
|
+
`~/.codexmgr/`
|
|
369
|
+
|
|
370
|
+
Suggested subpaths:
|
|
371
|
+
|
|
372
|
+
- `~/.codex-manager/backups/`
|
|
373
|
+
- `~/.codex-manager/inventory.json`
|
|
374
|
+
- `~/.codex-manager/cooldown.json`
|
|
375
|
+
- `~/.codex-manager/profiles/`
|
|
376
|
+
- `~/.codex-manager/tmp/`
|
|
377
|
+
|
|
378
|
+
## Regex Contracts
|
|
379
|
+
|
|
380
|
+
Normalized archive filename:
|
|
381
|
+
|
|
382
|
+
```regex
|
|
383
|
+
^\d{4}-\d{2}-\d{2}-\d{6}-.+-codex\.tar\.gz$
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Legacy auth filename:
|
|
387
|
+
|
|
388
|
+
```regex
|
|
389
|
+
^(?P<day>\d{1,2})(?P<month>[a-z]{3})_(?P<email>.+)_auth\.json$
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Safety Rules
|
|
393
|
+
|
|
394
|
+
- never infer and overwrite original legacy files in place
|
|
395
|
+
- preserve the original filename in metadata
|
|
396
|
+
- keep normalization idempotent
|
|
397
|
+
- restore atomically where possible
|
|
398
|
+
- do not rely only on filenames once metadata exists
|
|
399
|
+
|
|
400
|
+
## First Implementation Slice
|
|
401
|
+
|
|
402
|
+
Build in this order:
|
|
403
|
+
|
|
404
|
+
1. `config.py`
|
|
405
|
+
2. `normalize.py`
|
|
406
|
+
3. `metadata.py`
|
|
407
|
+
4. `cooldown.py`
|
|
408
|
+
5. `recommend.py`
|
|
409
|
+
6. `backup.py`
|
|
410
|
+
7. `restore.py`
|
|
411
|
+
8. CLI wiring in `args.py` and `cli.py`
|
|
412
|
+
|
|
413
|
+
This order gives a working normalization and scheduling engine before archive creation and restore.
|
|
414
|
+
|
|
415
|
+
## Immediate Next Task
|
|
416
|
+
|
|
417
|
+
Implement `normalize` first.
|
|
418
|
+
|
|
419
|
+
Output for phase 1 should be:
|
|
420
|
+
|
|
421
|
+
- parsed legacy account records
|
|
422
|
+
- inferred `session_start_at`
|
|
423
|
+
- computed `next_available_at`
|
|
424
|
+
- validation status against the legacy quota token
|
|
425
|
+
- proposed normalized archive basename
|
|
426
|
+
|
|
427
|
+
That gives a trustworthy inventory before touching live backup and restore flows.
|