codex-manager 3.0.0__tar.gz → 4.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 (89) hide show
  1. codex_manager-4.0.0/PKG-INFO +201 -0
  2. codex_manager-4.0.0/README.md +167 -0
  3. {codex_manager-3.0.0 → codex_manager-4.0.0}/pyproject.toml +2 -2
  4. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/__init__.py +1 -1
  5. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/account_status.py +31 -16
  6. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/args.py +65 -0
  7. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/cli.py +33 -10
  8. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/profile.py +15 -3
  9. codex_manager-4.0.0/src/codex_manager/purge.py +48 -0
  10. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/registry.py +26 -5
  11. codex_manager-4.0.0/src/codex_manager/remove.py +106 -0
  12. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/restore.py +1 -1
  13. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/status.py +1 -1
  14. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/ui.py +15 -0
  15. codex_manager-4.0.0/src/codex_manager.egg-info/PKG-INFO +201 -0
  16. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager.egg-info/SOURCES.txt +21 -1
  17. codex_manager-4.0.0/tests/test_account_status_coverage.py +141 -0
  18. codex_manager-4.0.0/tests/test_account_status_more2.py +81 -0
  19. codex_manager-4.0.0/tests/test_cli_coverage.py +69 -0
  20. codex_manager-4.0.0/tests/test_cli_coverage_2.py +33 -0
  21. codex_manager-4.0.0/tests/test_cli_coverage_3.py +42 -0
  22. codex_manager-4.0.0/tests/test_cli_coverage_4.py +71 -0
  23. codex_manager-4.0.0/tests/test_cli_coverage_5.py +33 -0
  24. codex_manager-4.0.0/tests/test_cli_coverage_6.py +30 -0
  25. codex_manager-4.0.0/tests/test_cli_coverage_7.py +58 -0
  26. codex_manager-4.0.0/tests/test_cli_coverage_8.py +81 -0
  27. codex_manager-4.0.0/tests/test_cli_coverage_extra.py +88 -0
  28. codex_manager-4.0.0/tests/test_cli_coverage_more.py +68 -0
  29. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_cooldown.py +2 -1
  30. codex_manager-4.0.0/tests/test_dry_run_coverage.py +262 -0
  31. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_list_backups3.py +0 -2
  32. codex_manager-4.0.0/tests/test_list_backups_coverage.py +83 -0
  33. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_next_gen_upgrade.py +11 -15
  34. codex_manager-4.0.0/tests/test_purge.py +83 -0
  35. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_recommend.py +2 -1
  36. codex_manager-4.0.0/tests/test_registry_coverage_more.py +23 -0
  37. codex_manager-4.0.0/tests/test_registry_dry_run_coverage.py +48 -0
  38. codex_manager-4.0.0/tests/test_remove.py +69 -0
  39. codex_manager-3.0.0/CODEX_MANAGER_SPEC.md +0 -49
  40. codex_manager-3.0.0/PKG-INFO +0 -83
  41. codex_manager-3.0.0/src/codex_manager.egg-info/PKG-INFO +0 -83
  42. {codex_manager-3.0.0 → codex_manager-4.0.0}/setup.cfg +0 -0
  43. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/backup.py +0 -0
  44. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/cloud.py +0 -0
  45. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/config.py +0 -0
  46. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/cooldown.py +0 -0
  47. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/credentials.py +0 -0
  48. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/doctor.py +0 -0
  49. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/list_backups.py +0 -0
  50. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/prune.py +0 -0
  51. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/prune_backups.py +0 -0
  52. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/recommend.py +0 -0
  53. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/sync.py +0 -0
  54. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/use_account.py +0 -0
  55. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager/utils.py +0 -0
  56. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager.egg-info/dependency_links.txt +0 -0
  57. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager.egg-info/entry_points.txt +0 -0
  58. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager.egg-info/requires.txt +0 -0
  59. {codex_manager-3.0.0 → codex_manager-4.0.0}/src/codex_manager.egg-info/top_level.txt +0 -0
  60. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_args_cli.py +0 -0
  61. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_args_cli_3.py +0 -0
  62. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_args_cli_extra.py +0 -0
  63. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_backup.py +0 -0
  64. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_backup2.py +0 -0
  65. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_cli2.py +0 -0
  66. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_cloud.py +0 -0
  67. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_config.py +0 -0
  68. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_cooldown2.py +0 -0
  69. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_credentials2.py +0 -0
  70. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_doctor.py +0 -0
  71. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_doctor2.py +0 -0
  72. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_list_backups.py +0 -0
  73. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_list_backups2.py +0 -0
  74. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_profile.py +0 -0
  75. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_prune.py +0 -0
  76. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_prune_backups.py +0 -0
  77. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_prune_backups2.py +0 -0
  78. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_recommend2.py +0 -0
  79. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_restore.py +0 -0
  80. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_restore2.py +0 -0
  81. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_status.py +0 -0
  82. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_status2.py +0 -0
  83. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_status3.py +0 -0
  84. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_sync.py +0 -0
  85. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_sync2.py +0 -0
  86. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_ui.py +0 -0
  87. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_ui_2.py +0 -0
  88. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_use.py +0 -0
  89. {codex_manager-3.0.0 → codex_manager-4.0.0}/tests/test_use2.py +0 -0
@@ -0,0 +1,201 @@
1
+ Metadata-Version: 2.4
2
+ Name: codex-manager
3
+ Version: 4.0.0
4
+ Summary: Codex account snapshot manager
5
+ Author-email: Dhruv <dhruv13x@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/dhruv13x/codex-manager
8
+ Project-URL: Repository, https://github.com/dhruv13x/codex-manager
9
+ Project-URL: Issues, https://github.com/dhruv13x/codex-manager/issues
10
+ Keywords: codex,backup,cli,automation
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Environment :: Console
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: rich>=13.0.0
19
+ Requires-Dist: boto3>=1.28.0
20
+ Requires-Dist: b2sdk>=2.0.0
21
+ Requires-Dist: relm>=7.1.1
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
24
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
25
+ Requires-Dist: pytest-timeout>=2.2.0; extra == "dev"
26
+ Requires-Dist: pytest-json-report>=1.5.0; extra == "dev"
27
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
28
+ Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
29
+ Requires-Dist: pyfakefs>=5.0.0; extra == "dev"
30
+ Requires-Dist: ruff>=0.6.0; extra == "dev"
31
+ Requires-Dist: black>=24.3.0; extra == "dev"
32
+ Requires-Dist: mypy>=1.11.0; extra == "dev"
33
+ Requires-Dist: types-PyYAML>=6.0; extra == "dev"
34
+
35
+ # 📦 Codex Manager
36
+
37
+ **The ultimate CLI tool for managing OpenAI Codex account snapshots, tracking quotas, and ensuring seamless workflow continuity.**
38
+
39
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
40
+ [![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
41
+ [![Code Style: Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
42
+ [![Linter: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
43
+ [![Maintenance Status](https://img.shields.io/badge/Maintenance-Active-success)](#)
44
+
45
+ ---
46
+
47
+ ## ⚡ Quick Start
48
+
49
+ Get up and running in under 5 minutes.
50
+
51
+ ### Prerequisites
52
+ - Python 3.10 or higher
53
+ - `uv` (recommended for fast dependency management)
54
+
55
+ ### Install
56
+ Clone the repository and install using `uv`:
57
+ ```bash
58
+ git clone https://github.com/dhruv13x/codex-manager.git
59
+ cd codex-manager
60
+ uv pip install --system -e .
61
+ ```
62
+
63
+ ### Run
64
+ Verify the installation by viewing the CLI help menu:
65
+ ```bash
66
+ codex-manager --help
67
+ # Or use the shorter alias:
68
+ cm --help
69
+ ```
70
+
71
+ ### Demo
72
+ Here's a quick 5-line workflow to backup your current account, view recommendations, and switch to a new one:
73
+ ```bash
74
+ # 1. Take a live snapshot of your active Codex account state
75
+ cm backup --cloud
76
+
77
+ # 2. Check cooldown statuses for all accounts
78
+ cm cooldown
79
+
80
+ # 3. Get the smartest recommendation for the next account to use
81
+ cm recommend
82
+
83
+ # 4. Switch to a new account using the 'auth-only' method
84
+ cm use --email new_user@example.com
85
+ ```
86
+
87
+ > **[🖼️ Suggestion: Add an animated GIF here demonstrating the `cm use` command in action with the Rich UI]**
88
+
89
+ ---
90
+
91
+ ## ✨ Features
92
+
93
+ **Core**
94
+ - **Live Status Tracking:** Automatically parses live Codex `/status` output to capture account email, quota text, and weekly reset timestamps.
95
+ - **Smart Recommendations:** Recommends the optimal account to use next based on calculated cooldowns and real-time metadata.
96
+ - **Full State Recovery:** Backup and restore full Codex runtime states (`auth.json`, history, logs) via `*.tar.gz` archives and `*.metadata.json`.
97
+
98
+ **Performance & Reliability**
99
+ - **Offline & Fallback Mode:** Employs an emergency `--without-status-check` fallback that gracefully estimates cooldowns even when the live tracker is temporarily unavailable.
100
+ - **Lightning Fast UI:** Employs the `rich` library for beautiful terminal output, tables, and status animations.
101
+
102
+ **Security & Cloud**
103
+ - **Cloud Synchronization:** First-class support for Backblaze B2 (and S3-compatible buckets) for remote backup metadata and archive storage.
104
+ - **Safe Operations:** Every modifying command supports a `--dry-run` flag to safely simulate actions without touching your files.
105
+
106
+ ---
107
+
108
+ ## 🛠️ Configuration
109
+
110
+ Codex Manager prioritizes configuration via environment variables and CLI arguments for flexibility.
111
+
112
+ ### Environment Variables
113
+
114
+ | Name | Description | Default | Required |
115
+ |---|---|---|---|
116
+ | `CODEX_MANAGER_HOME` | The primary home directory for the manager config and backups. | `~/.codex-manager` | No |
117
+ | `CODEX_HOME` | The target directory where Codex state resides. | `~/.codex` | No |
118
+
119
+ *Note: You can also place a `config.json` inside your `CODEX_MANAGER_HOME` to persist configuration settings.*
120
+
121
+ ### Key CLI Arguments
122
+
123
+ Most commands support these primary flags. Use `cm <command> --help` for a complete list.
124
+
125
+ | Flag | Description |
126
+ |---|---|
127
+ | `--dry-run` | Safely preview the changes without modifying local or cloud state. |
128
+ | `--cloud` | Enable Backblaze B2/S3 cloud capabilities for the command. |
129
+ | `--email <email>` | Specify a target account email for use, restore, or listing. |
130
+ | `--backup-dir <dir>` | Override the directory used for reading/writing backups. |
131
+ | `--without-status-check`| Bypass live capture and calculate cooldowns statically (+7 days). |
132
+ | `--auth-only` | During `use` or `backup`, target only identity/auth files instead of the full state. |
133
+
134
+ ---
135
+
136
+ ## 🏗️ Architecture
137
+
138
+ ### Directory Tree
139
+
140
+ ```
141
+ ~/.codex-manager/ (CODEX_MANAGER_HOME)
142
+ ├── backups/ # Local archives and metadata
143
+ │ ├── backup_1.tar.gz
144
+ │ └── backup_1.metadata.json
145
+ ├── config.json # Persistent local configurations
146
+ └── cooldown.json # Registry caching overall account cooldowns
147
+ ```
148
+
149
+ ### High-Level Data Flow
150
+ 1. **Capture:** `cm backup` or `cm status` reads live text from a `tmux` session running Codex.
151
+ 2. **Process:** The CLI parses the text to build a state model (Quota, Cooldown, Email) and packages the `~/.codex` directory into an archive.
152
+ 3. **Store:** Archives and adjacent JSON metadata are saved locally and pushed to the cloud (if configured).
153
+ 4. **Evaluate:** When `cm recommend` or `cm cooldown` is invoked, local and cloud metadata are fetched, evaluated against real-time, and ranked to find the optimal active account.
154
+ 5. **Switch:** `cm use` restores the selected account's data into the `~/.codex` home, rotating your session seamlessly.
155
+
156
+ ---
157
+
158
+ ## 🐞 Troubleshooting
159
+
160
+ ### Common Issues
161
+
162
+ | Error Message | Cause | Solution |
163
+ |---|---|---|
164
+ | `TokenExpiredError: TOKEN EXPIRED` | The active Codex session token has expired. | Re-authenticate in Codex manually, or run with `--without-status-check` to bypass. |
165
+ | `Could not resolve Cloud (B2) credentials.` | Missing B2 credentials for cloud sync. | Pass `--b2-id` and `--b2-key` flags, or ensure your credentials are set up. |
166
+ | `No backups found in Cloud for <email>.` | The requested account isn't backed up to the specified bucket. | Run `cm list-backups --cloud` to verify the email and backup availability. |
167
+
168
+ ### Debug Mode
169
+ While the CLI does not have a single `--debug` flag, you can often reveal more information by viewing the full exception traces or by utilizing the built-in doctor command:
170
+ ```bash
171
+ cm doctor
172
+ ```
173
+ The `doctor` command verifies your dependencies, runtime directories, and validates the status parser setup.
174
+
175
+ ---
176
+
177
+ ## 🤝 Contributing
178
+
179
+ We welcome contributions to Codex Manager! Please review our `CONTRIBUTING.md` (coming soon) before submitting PRs.
180
+
181
+ ### Dev Setup
182
+ To set up your local development environment:
183
+ ```bash
184
+ # 1. Install all development dependencies
185
+ uv pip install --system -e .[dev]
186
+
187
+ # 2. Run the tests (Ensure 90%+ coverage)
188
+ python -m pytest tests --cov=src --cov-report=term-missing
189
+
190
+ # 3. Format and lint the codebase
191
+ uv run ruff check --fix src/ tests/
192
+ uv run black src/ tests/
193
+ ```
194
+
195
+ ---
196
+
197
+ ## 🗺️ Roadmap
198
+
199
+ - **Plugin Architecture:** Allow custom plugins to manage other CLI authentication tokens.
200
+ - **Enhanced Cloud Coverage:** Add direct first-class integrations for Google Cloud Storage and Azure Blob.
201
+ - **Automated Rotation Daemon:** A background worker to automatically rotate accounts when token limits are reached in real-time.
@@ -0,0 +1,167 @@
1
+ # 📦 Codex Manager
2
+
3
+ **The ultimate CLI tool for managing OpenAI Codex account snapshots, tracking quotas, and ensuring seamless workflow continuity.**
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
+ [![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
7
+ [![Code Style: Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
8
+ [![Linter: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
9
+ [![Maintenance Status](https://img.shields.io/badge/Maintenance-Active-success)](#)
10
+
11
+ ---
12
+
13
+ ## ⚡ Quick Start
14
+
15
+ Get up and running in under 5 minutes.
16
+
17
+ ### Prerequisites
18
+ - Python 3.10 or higher
19
+ - `uv` (recommended for fast dependency management)
20
+
21
+ ### Install
22
+ Clone the repository and install using `uv`:
23
+ ```bash
24
+ git clone https://github.com/dhruv13x/codex-manager.git
25
+ cd codex-manager
26
+ uv pip install --system -e .
27
+ ```
28
+
29
+ ### Run
30
+ Verify the installation by viewing the CLI help menu:
31
+ ```bash
32
+ codex-manager --help
33
+ # Or use the shorter alias:
34
+ cm --help
35
+ ```
36
+
37
+ ### Demo
38
+ Here's a quick 5-line workflow to backup your current account, view recommendations, and switch to a new one:
39
+ ```bash
40
+ # 1. Take a live snapshot of your active Codex account state
41
+ cm backup --cloud
42
+
43
+ # 2. Check cooldown statuses for all accounts
44
+ cm cooldown
45
+
46
+ # 3. Get the smartest recommendation for the next account to use
47
+ cm recommend
48
+
49
+ # 4. Switch to a new account using the 'auth-only' method
50
+ cm use --email new_user@example.com
51
+ ```
52
+
53
+ > **[🖼️ Suggestion: Add an animated GIF here demonstrating the `cm use` command in action with the Rich UI]**
54
+
55
+ ---
56
+
57
+ ## ✨ Features
58
+
59
+ **Core**
60
+ - **Live Status Tracking:** Automatically parses live Codex `/status` output to capture account email, quota text, and weekly reset timestamps.
61
+ - **Smart Recommendations:** Recommends the optimal account to use next based on calculated cooldowns and real-time metadata.
62
+ - **Full State Recovery:** Backup and restore full Codex runtime states (`auth.json`, history, logs) via `*.tar.gz` archives and `*.metadata.json`.
63
+
64
+ **Performance & Reliability**
65
+ - **Offline & Fallback Mode:** Employs an emergency `--without-status-check` fallback that gracefully estimates cooldowns even when the live tracker is temporarily unavailable.
66
+ - **Lightning Fast UI:** Employs the `rich` library for beautiful terminal output, tables, and status animations.
67
+
68
+ **Security & Cloud**
69
+ - **Cloud Synchronization:** First-class support for Backblaze B2 (and S3-compatible buckets) for remote backup metadata and archive storage.
70
+ - **Safe Operations:** Every modifying command supports a `--dry-run` flag to safely simulate actions without touching your files.
71
+
72
+ ---
73
+
74
+ ## 🛠️ Configuration
75
+
76
+ Codex Manager prioritizes configuration via environment variables and CLI arguments for flexibility.
77
+
78
+ ### Environment Variables
79
+
80
+ | Name | Description | Default | Required |
81
+ |---|---|---|---|
82
+ | `CODEX_MANAGER_HOME` | The primary home directory for the manager config and backups. | `~/.codex-manager` | No |
83
+ | `CODEX_HOME` | The target directory where Codex state resides. | `~/.codex` | No |
84
+
85
+ *Note: You can also place a `config.json` inside your `CODEX_MANAGER_HOME` to persist configuration settings.*
86
+
87
+ ### Key CLI Arguments
88
+
89
+ Most commands support these primary flags. Use `cm <command> --help` for a complete list.
90
+
91
+ | Flag | Description |
92
+ |---|---|
93
+ | `--dry-run` | Safely preview the changes without modifying local or cloud state. |
94
+ | `--cloud` | Enable Backblaze B2/S3 cloud capabilities for the command. |
95
+ | `--email <email>` | Specify a target account email for use, restore, or listing. |
96
+ | `--backup-dir <dir>` | Override the directory used for reading/writing backups. |
97
+ | `--without-status-check`| Bypass live capture and calculate cooldowns statically (+7 days). |
98
+ | `--auth-only` | During `use` or `backup`, target only identity/auth files instead of the full state. |
99
+
100
+ ---
101
+
102
+ ## 🏗️ Architecture
103
+
104
+ ### Directory Tree
105
+
106
+ ```
107
+ ~/.codex-manager/ (CODEX_MANAGER_HOME)
108
+ ├── backups/ # Local archives and metadata
109
+ │ ├── backup_1.tar.gz
110
+ │ └── backup_1.metadata.json
111
+ ├── config.json # Persistent local configurations
112
+ └── cooldown.json # Registry caching overall account cooldowns
113
+ ```
114
+
115
+ ### High-Level Data Flow
116
+ 1. **Capture:** `cm backup` or `cm status` reads live text from a `tmux` session running Codex.
117
+ 2. **Process:** The CLI parses the text to build a state model (Quota, Cooldown, Email) and packages the `~/.codex` directory into an archive.
118
+ 3. **Store:** Archives and adjacent JSON metadata are saved locally and pushed to the cloud (if configured).
119
+ 4. **Evaluate:** When `cm recommend` or `cm cooldown` is invoked, local and cloud metadata are fetched, evaluated against real-time, and ranked to find the optimal active account.
120
+ 5. **Switch:** `cm use` restores the selected account's data into the `~/.codex` home, rotating your session seamlessly.
121
+
122
+ ---
123
+
124
+ ## 🐞 Troubleshooting
125
+
126
+ ### Common Issues
127
+
128
+ | Error Message | Cause | Solution |
129
+ |---|---|---|
130
+ | `TokenExpiredError: TOKEN EXPIRED` | The active Codex session token has expired. | Re-authenticate in Codex manually, or run with `--without-status-check` to bypass. |
131
+ | `Could not resolve Cloud (B2) credentials.` | Missing B2 credentials for cloud sync. | Pass `--b2-id` and `--b2-key` flags, or ensure your credentials are set up. |
132
+ | `No backups found in Cloud for <email>.` | The requested account isn't backed up to the specified bucket. | Run `cm list-backups --cloud` to verify the email and backup availability. |
133
+
134
+ ### Debug Mode
135
+ While the CLI does not have a single `--debug` flag, you can often reveal more information by viewing the full exception traces or by utilizing the built-in doctor command:
136
+ ```bash
137
+ cm doctor
138
+ ```
139
+ The `doctor` command verifies your dependencies, runtime directories, and validates the status parser setup.
140
+
141
+ ---
142
+
143
+ ## 🤝 Contributing
144
+
145
+ We welcome contributions to Codex Manager! Please review our `CONTRIBUTING.md` (coming soon) before submitting PRs.
146
+
147
+ ### Dev Setup
148
+ To set up your local development environment:
149
+ ```bash
150
+ # 1. Install all development dependencies
151
+ uv pip install --system -e .[dev]
152
+
153
+ # 2. Run the tests (Ensure 90%+ coverage)
154
+ python -m pytest tests --cov=src --cov-report=term-missing
155
+
156
+ # 3. Format and lint the codebase
157
+ uv run ruff check --fix src/ tests/
158
+ uv run black src/ tests/
159
+ ```
160
+
161
+ ---
162
+
163
+ ## 🗺️ Roadmap
164
+
165
+ - **Plugin Architecture:** Allow custom plugins to manage other CLI authentication tokens.
166
+ - **Enhanced Cloud Coverage:** Add direct first-class integrations for Google Cloud Storage and Azure Blob.
167
+ - **Automated Rotation Daemon:** A background worker to automatically rotate accounts when token limits are reached in real-time.
@@ -1,8 +1,8 @@
1
1
  [project]
2
2
  name = "codex-manager"
3
- version = "3.0.0"
3
+ version = "4.0.0"
4
4
  description = "Codex account snapshot manager"
5
- readme = "CODEX_MANAGER_SPEC.md"
5
+ readme = "README.md"
6
6
  requires-python = ">=3.10"
7
7
  license = { text = "MIT" }
8
8
  authors = [
@@ -1,3 +1,3 @@
1
1
  __all__ = ["__version__"]
2
2
 
3
- __version__ = "3.0.0"
3
+ __version__ = "4.0.0"
@@ -9,9 +9,8 @@ from typing import Any
9
9
 
10
10
  from .backup import read_status_text_from_args
11
11
  from .cloud import get_cloud_provider
12
- from .list_backups import list_backups, list_cloud_backups
12
+ from .list_backups import list_cloud_backups
13
13
  from .registry import sync_registry_with_cloud, update_registry_entry
14
- from .status import parse_live_status_text
15
14
  from .ui import console
16
15
  from .utils import build_archive_name
17
16
 
@@ -24,6 +23,7 @@ def patch_metadata(
24
23
  args: Any = None,
25
24
  session_start_at: Any | None = None,
26
25
  is_expired: bool = False,
26
+ dry_run: bool = False,
27
27
  ) -> None:
28
28
  backup_dir = Path(args.backup_dir).expanduser() if args and hasattr(args, "backup_dir") else Path("~/.codex-manager/backups").expanduser()
29
29
 
@@ -78,10 +78,13 @@ def patch_metadata(
78
78
  data["quota_percent_left"] = quota_percent_left
79
79
  data["is_expired"] = is_expired
80
80
  data["updated_at"] = datetime.now().astimezone().isoformat()
81
- metadata_path.write_text(json.dumps(data, indent=2), encoding="utf-8")
82
- console.print(
83
- f"Updated local metadata for [cyan]{email}[/]: [dim]{metadata_path.name}[/]"
84
- )
81
+ if not dry_run:
82
+ metadata_path.write_text(json.dumps(data, indent=2), encoding="utf-8")
83
+ console.print(
84
+ f"Updated local metadata for [cyan]{email}[/]: [dim]{metadata_path.name}[/]"
85
+ )
86
+ else:
87
+ console.print(f"Would update local metadata for [cyan]{email}[/]: [dim]{metadata_path.name}[/]")
85
88
  except Exception as exc:
86
89
  console.print(f"[yellow]Warning:[/] Failed to patch local metadata: {exc}")
87
90
  else:
@@ -113,10 +116,13 @@ def patch_metadata(
113
116
  "metadata_only": True,
114
117
  }
115
118
  try:
116
- metadata_path.write_text(json.dumps(data, indent=2), encoding="utf-8")
117
- console.print(
118
- f"Created cooldown-only metadata for [cyan]{email}[/]: [dim]{metadata_path.name}[/]"
119
- )
119
+ if not dry_run:
120
+ metadata_path.write_text(json.dumps(data, indent=2), encoding="utf-8")
121
+ console.print(
122
+ f"Created cooldown-only metadata for [cyan]{email}[/]: [dim]{metadata_path.name}[/]"
123
+ )
124
+ else:
125
+ console.print(f"Would create cooldown-only metadata for [cyan]{email}[/]: [dim]{metadata_path.name}[/]")
120
126
  except Exception as exc:
121
127
  console.print(f"[yellow]Warning:[/] Failed to create local metadata: {exc}")
122
128
 
@@ -127,12 +133,13 @@ def patch_metadata(
127
133
  quota_text=quota_text,
128
134
  quota_percent_left=quota_percent_left,
129
135
  session_start_at=final_session_start_at,
136
+ dry_run=dry_run,
130
137
  )
131
138
 
132
139
  if args and getattr(args, "cloud", False):
133
140
  cp = get_cloud_provider(args)
134
141
  if cp:
135
- sync_registry_with_cloud(cp)
142
+ sync_registry_with_cloud(cp, dry_run=dry_run)
136
143
  entries = list_cloud_backups(cp, email=email, latest_per_email=True)
137
144
  if entries:
138
145
  selected = entries[0]
@@ -161,11 +168,14 @@ def patch_metadata(
161
168
  data["updated_at"] = datetime.now().astimezone().isoformat()
162
169
  local_metadata.write_text(json.dumps(data, indent=2), encoding="utf-8")
163
170
 
164
- console.print(
165
- f"Uploading updated metadata to Cloud: [dim]{metadata_name}[/] ..."
166
- )
167
- cp.upload_file(local_metadata, metadata_name)
168
- console.print("Cloud metadata update complete.")
171
+ if not dry_run:
172
+ console.print(
173
+ f"Uploading updated metadata to Cloud: [dim]{metadata_name}[/] ..."
174
+ )
175
+ cp.upload_file(local_metadata, metadata_name)
176
+ console.print("Cloud metadata update complete.")
177
+ else:
178
+ console.print(f"Would upload updated metadata to Cloud: [dim]{metadata_name}[/]")
169
179
  except Exception as exc:
170
180
  console.print(f"[yellow]Warning:[/] Failed to patch cloud metadata: {exc}")
171
181
  else:
@@ -215,6 +225,7 @@ def sync_current_account_status(args: Any) -> None:
215
225
  quota_percent_left=None,
216
226
  args=args,
217
227
  session_start_at=session_start_at,
228
+ dry_run=getattr(args, "dry_run", False),
218
229
  )
219
230
  return
220
231
 
@@ -241,6 +252,7 @@ def sync_current_account_status(args: Any) -> None:
241
252
  args=args,
242
253
  session_start_at=None,
243
254
  is_expired=True,
255
+ dry_run=getattr(args, "dry_run", False),
244
256
  )
245
257
  except Exception:
246
258
  # If we can't even get the email, we use the one from auth.json
@@ -251,6 +263,7 @@ def sync_current_account_status(args: Any) -> None:
251
263
  quota_percent_left=None,
252
264
  args=args,
253
265
  is_expired=True,
266
+ dry_run=getattr(args, "dry_run", False),
254
267
  )
255
268
  sys.exit(1)
256
269
  except Exception as exc:
@@ -268,6 +281,7 @@ def sync_current_account_status(args: Any) -> None:
268
281
 
269
282
  if text:
270
283
  try:
284
+ from .status import parse_live_status_text
271
285
  status = parse_live_status_text(
272
286
  text,
273
287
  reference_year=getattr(args, "reference_year", None),
@@ -280,6 +294,7 @@ def sync_current_account_status(args: Any) -> None:
280
294
  args=args,
281
295
  session_start_at=status.session_start_at,
282
296
  is_expired=status.is_expired,
297
+ dry_run=getattr(args, "dry_run", False),
283
298
  )
284
299
  except Exception as exc:
285
300
  console.print(
@@ -565,6 +565,11 @@ def get_parser() -> argparse.ArgumentParser:
565
565
  "file",
566
566
  help="Path to the profile archive (.tar.gz) to export to or import from.",
567
567
  )
568
+ profile_parser.add_argument(
569
+ "--dry-run",
570
+ action="store_true",
571
+ help="Show what would happen without modifying metadata.",
572
+ )
568
573
 
569
574
  doctor_parser = subparsers.add_parser(
570
575
  "doctor",
@@ -689,6 +694,11 @@ def get_parser() -> argparse.ArgumentParser:
689
694
  use_parser.add_argument("--bucket", help="B2 Bucket Name")
690
695
  use_parser.add_argument("--b2-id", help="B2 Key ID")
691
696
  use_parser.add_argument("--b2-key", help="B2 App Key")
697
+ status_parser.add_argument(
698
+ "--dry-run",
699
+ action="store_true",
700
+ help="Show what would happen without modifying metadata.",
701
+ )
692
702
 
693
703
  sync_parser = subparsers.add_parser(
694
704
  "sync",
@@ -727,4 +737,59 @@ def get_parser() -> argparse.ArgumentParser:
727
737
  help="Show what would happen without actually syncing.",
728
738
  )
729
739
 
740
+ purge_parser = subparsers.add_parser(
741
+ "purge",
742
+ help="Total Wipeout: Completely delete the Codex home directory (Auth, Identity, and all State).",
743
+ )
744
+ purge_parser.add_argument(
745
+ "--source-dir",
746
+ default=str(_get_default("codex_home", str(DEFAULT_CODEX_HOME))),
747
+ help="Codex home directory to purge.",
748
+ )
749
+ purge_parser.add_argument(
750
+ "--dry-run",
751
+ action="store_true",
752
+ help="Show what would be removed without deleting anything.",
753
+ )
754
+ purge_parser.add_argument(
755
+ "--yes",
756
+ "-y",
757
+ action="store_true",
758
+ help="Confirm the purge without prompting.",
759
+ )
760
+
761
+ remove_parser = subparsers.add_parser(
762
+ "remove",
763
+ help="Account Cleanup: Delete all local (and optionally cloud) backups and registry entries for a specific email.",
764
+ )
765
+ remove_parser.add_argument(
766
+ "--email",
767
+ required=True,
768
+ help="The email address of the account to remove.",
769
+ )
770
+ remove_parser.add_argument(
771
+ "--backup-dir",
772
+ default=str(_get_default("backup_dir", str(DEFAULT_BACKUP_DIR))),
773
+ help="Directory containing backup archives and metadata.",
774
+ )
775
+ remove_parser.add_argument(
776
+ "--cloud",
777
+ action="store_true",
778
+ help="Also remove backups and registry entries from Cloud (B2).",
779
+ )
780
+ remove_parser.add_argument(
781
+ "--dry-run",
782
+ action="store_true",
783
+ help="Show what would be removed without deleting anything.",
784
+ )
785
+ remove_parser.add_argument(
786
+ "--yes",
787
+ "-y",
788
+ action="store_true",
789
+ help="Confirm the removal without prompting.",
790
+ )
791
+ remove_parser.add_argument("--bucket", help="B2 Bucket Name")
792
+ remove_parser.add_argument("--b2-id", help="B2 Key ID")
793
+ remove_parser.add_argument("--b2-key", help="B2 App Key")
794
+
730
795
  return parser