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.
Files changed (44) hide show
  1. codex_manager-1.0.0/CODEX_MANAGER_SPEC.md +427 -0
  2. codex_manager-1.0.0/PKG-INFO +460 -0
  3. codex_manager-1.0.0/README.md +154 -0
  4. codex_manager-1.0.0/pyproject.toml +108 -0
  5. codex_manager-1.0.0/setup.cfg +4 -0
  6. codex_manager-1.0.0/src/codex_manager/__init__.py +3 -0
  7. codex_manager-1.0.0/src/codex_manager/args.py +484 -0
  8. codex_manager-1.0.0/src/codex_manager/backup.py +183 -0
  9. codex_manager-1.0.0/src/codex_manager/cli.py +219 -0
  10. codex_manager-1.0.0/src/codex_manager/config.py +50 -0
  11. codex_manager-1.0.0/src/codex_manager/cooldown.py +113 -0
  12. codex_manager-1.0.0/src/codex_manager/doctor.py +69 -0
  13. codex_manager-1.0.0/src/codex_manager/inventory.py +32 -0
  14. codex_manager-1.0.0/src/codex_manager/list_backups.py +129 -0
  15. codex_manager-1.0.0/src/codex_manager/normalize.py +119 -0
  16. codex_manager-1.0.0/src/codex_manager/profile.py +33 -0
  17. codex_manager-1.0.0/src/codex_manager/prune.py +80 -0
  18. codex_manager-1.0.0/src/codex_manager/prune_backups.py +63 -0
  19. codex_manager-1.0.0/src/codex_manager/recommend.py +54 -0
  20. codex_manager-1.0.0/src/codex_manager/restore.py +145 -0
  21. codex_manager-1.0.0/src/codex_manager/status.py +180 -0
  22. codex_manager-1.0.0/src/codex_manager/sync.py +97 -0
  23. codex_manager-1.0.0/src/codex_manager/use_account.py +112 -0
  24. codex_manager-1.0.0/src/codex_manager.egg-info/PKG-INFO +460 -0
  25. codex_manager-1.0.0/src/codex_manager.egg-info/SOURCES.txt +42 -0
  26. codex_manager-1.0.0/src/codex_manager.egg-info/dependency_links.txt +1 -0
  27. codex_manager-1.0.0/src/codex_manager.egg-info/entry_points.txt +3 -0
  28. codex_manager-1.0.0/src/codex_manager.egg-info/requires.txt +16 -0
  29. codex_manager-1.0.0/src/codex_manager.egg-info/top_level.txt +1 -0
  30. codex_manager-1.0.0/tests/test_backup.py +133 -0
  31. codex_manager-1.0.0/tests/test_config.py +34 -0
  32. codex_manager-1.0.0/tests/test_cooldown.py +85 -0
  33. codex_manager-1.0.0/tests/test_doctor.py +67 -0
  34. codex_manager-1.0.0/tests/test_inventory.py +30 -0
  35. codex_manager-1.0.0/tests/test_list_backups.py +152 -0
  36. codex_manager-1.0.0/tests/test_normalize.py +92 -0
  37. codex_manager-1.0.0/tests/test_profile.py +68 -0
  38. codex_manager-1.0.0/tests/test_prune.py +65 -0
  39. codex_manager-1.0.0/tests/test_prune_backups.py +104 -0
  40. codex_manager-1.0.0/tests/test_recommend.py +73 -0
  41. codex_manager-1.0.0/tests/test_restore.py +101 -0
  42. codex_manager-1.0.0/tests/test_status.py +35 -0
  43. codex_manager-1.0.0/tests/test_sync.py +92 -0
  44. 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.