cdx-manager 0.2.1
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 +243 -0
- package/bin/cdx +20 -0
- package/changelogs/CHANGELOGS_0_1_1.md +98 -0
- package/changelogs/CHANGELOGS_0_2_0.md +68 -0
- package/changelogs/CHANGELOGS_0_2_1.md +29 -0
- package/package.json +44 -0
- package/src/__init__.py +17 -0
- package/src/claude_refresh.py +72 -0
- package/src/claude_usage.py +84 -0
- package/src/cli.py +188 -0
- package/src/cli_commands.py +369 -0
- package/src/cli_render.py +131 -0
- package/src/config.py +8 -0
- package/src/errors.py +4 -0
- package/src/health.py +125 -0
- package/src/notify.py +138 -0
- package/src/provider_runtime.py +290 -0
- package/src/repair.py +121 -0
- package/src/session_service.py +563 -0
- package/src/session_store.py +244 -0
- package/src/status_source.py +572 -0
- package/src/status_view.py +270 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alexandre Agostini
|
|
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,243 @@
|
|
|
1
|
+
# CDX Manager
|
|
2
|
+
|
|
3
|
+
**Run multiple Codex and Claude sessions from one terminal. Switch between accounts instantly.**
|
|
4
|
+
|
|
5
|
+
If you use AI coding tools at scale ; multiple accounts, multiple providers : you know the friction: re-authenticating, losing context, juggling environment variables. `cdx` removes all of that.
|
|
6
|
+
|
|
7
|
+
One command to launch any session. Zero auth juggling.
|
|
8
|
+
|
|
9
|
+
[](LICENSE)  
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Table of Contents
|
|
14
|
+
|
|
15
|
+
- [What it does](#what-it-does)
|
|
16
|
+
- [Technical Overview](#technical-overview)
|
|
17
|
+
- [Getting Started](#getting-started)
|
|
18
|
+
- [All Commands](#all-commands)
|
|
19
|
+
- [Available Scripts](#available-scripts)
|
|
20
|
+
- [Project Structure](#project-structure)
|
|
21
|
+
- [Data Layout](#data-layout)
|
|
22
|
+
- [Troubleshooting](#troubleshooting)
|
|
23
|
+
- [Contributing](#contributing)
|
|
24
|
+
- [License](#license)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## What it does
|
|
29
|
+
|
|
30
|
+
- **Multiple accounts, one tool.** Register as many Codex or Claude sessions as you need. Each one gets its own isolated auth environment — no cross-contamination between accounts.
|
|
31
|
+
- **Instant launch.** `cdx work` opens your "work" session. `cdx personal` opens another. No config files to edit mid-flow.
|
|
32
|
+
- **Auth guardrails.** `cdx` checks authentication before launching. If a session is not logged in, it tells you exactly what to run — no silent failures.
|
|
33
|
+
- **Usage at a glance.** `cdx status` shows token usage, 5-hour window quota, weekly quota, and last-updated timestamps for every session in one aligned table.
|
|
34
|
+
- **Passive status resolution.** If a session has no recorded status, `cdx` reads it directly from the provider's session logs and JSONL history — no manual sync required.
|
|
35
|
+
- **Session transcript capture.** Every launch is recorded to a local log file via `script`, giving you a full terminal transcript for each session.
|
|
36
|
+
- **Clean removal.** `cdx rmv` wipes a session and its entire auth directory. No orphaned files, no stale credentials.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Technical Overview
|
|
41
|
+
|
|
42
|
+
- Python 3.9+, zero runtime dependencies.
|
|
43
|
+
- Environment isolation per session:
|
|
44
|
+
- Codex sessions override `CODEX_HOME` to a dedicated profile directory.
|
|
45
|
+
- Claude sessions override `HOME` to a dedicated profile directory.
|
|
46
|
+
- Persistence:
|
|
47
|
+
- Session registry at `~/.cdx/sessions.json` (versioned JSON store).
|
|
48
|
+
- Per-session state at `~/.cdx/state/<name>.json`.
|
|
49
|
+
- Auth and provider data under `~/.cdx/profiles/<name>/`.
|
|
50
|
+
- All paths are URL-encoded to support arbitrary session names.
|
|
51
|
+
- Status resolution pipeline:
|
|
52
|
+
- Primary source: recorded status fields on the session record.
|
|
53
|
+
- Fallback: `status-source` scans provider JSONL history files and terminal log transcripts, strips ANSI/OSC sequences, and extracts `usage%`, `5h remaining%`, and `week remaining%` via pattern matching.
|
|
54
|
+
- Claude status refreshes are cached briefly by default; pass `--refresh` to force a live rate-limit probe.
|
|
55
|
+
- If `script` is unavailable, Codex launch falls back to running without transcript capture.
|
|
56
|
+
- Auth probe: synchronous subprocess call to `codex login status` or `claude auth status` before any interactive launch.
|
|
57
|
+
- Signal forwarding: `SIGINT`, `SIGTERM`, and `SIGHUP` are forwarded to the child process and produce clean exit codes.
|
|
58
|
+
- Test stack: Python built-in `unittest` runner with no test framework dependency.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Getting Started
|
|
63
|
+
|
|
64
|
+
### Prerequisites
|
|
65
|
+
|
|
66
|
+
- Python 3.9+
|
|
67
|
+
- npm
|
|
68
|
+
- `codex` and/or `claude` CLI installed and available in your PATH
|
|
69
|
+
|
|
70
|
+
### Install
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
git clone <repo>
|
|
74
|
+
cd cdx-manager
|
|
75
|
+
make install
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`cdx` is now available globally. Changes to the source take effect immediately — no reinstall needed.
|
|
79
|
+
|
|
80
|
+
To uninstall:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
make uninstall
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Alternatively, for a non-symlinked global install:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm install -g .
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Once published to npm:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npm install -g cdx-manager
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Environment
|
|
99
|
+
|
|
100
|
+
By default, `cdx` stores all data under `~/.cdx/`. Override with:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
export CDX_HOME=/path/to/custom/dir
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Optional runtime knobs:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
export CDX_CLAUDE_STATUS_MODEL=claude-haiku-4-5-20251001
|
|
110
|
+
export CDX_SCRIPT_BIN=script
|
|
111
|
+
export CDX_SCRIPT_ARGS='-q -F {transcript}'
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Quick Start
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# Register a Codex session
|
|
118
|
+
cdx add work
|
|
119
|
+
|
|
120
|
+
# Register a Claude session
|
|
121
|
+
cdx add claude personal
|
|
122
|
+
|
|
123
|
+
# List all sessions
|
|
124
|
+
cdx
|
|
125
|
+
|
|
126
|
+
# Launch a session
|
|
127
|
+
cdx work
|
|
128
|
+
|
|
129
|
+
# Check usage across all sessions
|
|
130
|
+
cdx status
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## All Commands
|
|
136
|
+
|
|
137
|
+
| Command | Description |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `cdx` | List all sessions with last-updated timestamps |
|
|
140
|
+
| `cdx <name>` | Launch a session (checks auth first) |
|
|
141
|
+
| `cdx add [provider] <name>` | Register a new session (`provider`: `codex` or `claude`, default: `codex`) |
|
|
142
|
+
| `cdx cp <source> <dest>` | Copy a session into another session name, overwriting the destination if it exists |
|
|
143
|
+
| `cdx ren <source> <dest>` | Rename a session and move its auth data |
|
|
144
|
+
| `cdx login <name>` | Re-authenticate a session (logout + login) |
|
|
145
|
+
| `cdx logout <name>` | Log out of a session |
|
|
146
|
+
| `cdx rmv <name> [--force]` | Remove a session and its auth data (prompts for confirmation unless `--force`) |
|
|
147
|
+
| `cdx clean [name]` | Clear launch transcript logs for one session or all sessions |
|
|
148
|
+
| `cdx doctor [--json]` | Inspect CLI dependencies, CDX_HOME permissions, missing state, orphan profiles, and pending quarantines |
|
|
149
|
+
| `cdx repair [--dry-run] [--force] [--json]` | Plan or apply safe repairs for missing state files, quarantines, and orphan profiles |
|
|
150
|
+
| `cdx notify <name> --at-reset [--poll seconds] [--once]` | Wait for a session reset time and send a desktop notification when due |
|
|
151
|
+
| `cdx notify --next-ready [--poll seconds] [--once]` | Wait until the recommended session is usable or needs a refresh after reset |
|
|
152
|
+
| `cdx status [--json] [--refresh]` | Show token usage table for all sessions; JSON keeps the same row-array shape and writes live Claude refresh warnings to stderr |
|
|
153
|
+
| `cdx status --small [--refresh]` / `cdx status -s [--refresh]` | Show compact token usage table without provider, blocking quota, credits, and updated columns |
|
|
154
|
+
| `cdx status <name> [--json] [--refresh]` | Show detailed usage breakdown for one session |
|
|
155
|
+
| `cdx --help` | Show usage |
|
|
156
|
+
| `cdx --version` | Show version |
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Available Scripts
|
|
161
|
+
|
|
162
|
+
- `npm test`: run the Python test suite
|
|
163
|
+
- `npm run test:py`: run the Python unit tests directly
|
|
164
|
+
- `npm run lint`: byte-compile the Python sources and tests
|
|
165
|
+
- `npm run link`: link `cdx` globally for local development (`npm link`)
|
|
166
|
+
- `npm run unlink`: remove the global link
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Project Structure
|
|
171
|
+
|
|
172
|
+
```text
|
|
173
|
+
bin/
|
|
174
|
+
cdx # Entry point — shebang + main() call
|
|
175
|
+
|
|
176
|
+
src/
|
|
177
|
+
cli.py # Top-level command router
|
|
178
|
+
cli_commands.py # Command handlers and argument handling
|
|
179
|
+
cli_render.py # Terminal formatting, tables, colors, and errors
|
|
180
|
+
status_view.py # Status table/detail rendering and priority ranking
|
|
181
|
+
provider_runtime.py # Provider launch/auth commands, transcripts, signals
|
|
182
|
+
claude_refresh.py # Claude usage refresh orchestration
|
|
183
|
+
session_service.py # Session lifecycle: create, copy, rename, launch, remove, status
|
|
184
|
+
# resolution, auth state management
|
|
185
|
+
session_store.py # JSON persistence layer: sessions.json + per-session
|
|
186
|
+
# state files
|
|
187
|
+
status_source.py # Status artifact discovery: scans JSONL history files
|
|
188
|
+
# and terminal log transcripts, strips ANSI sequences,
|
|
189
|
+
# extracts usage metrics via pattern matching
|
|
190
|
+
config.py # CDX_HOME resolution (env override or ~/.cdx)
|
|
191
|
+
errors.py # CdxError with optional exit code
|
|
192
|
+
__init__.py # Public Python exports
|
|
193
|
+
|
|
194
|
+
test/
|
|
195
|
+
test_cli_py.py # CLI command dispatch tests
|
|
196
|
+
test_session_service_py.py # Session service unit tests
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Data Layout
|
|
202
|
+
|
|
203
|
+
All session data lives under `CDX_HOME` (default: `~/.cdx/`):
|
|
204
|
+
|
|
205
|
+
```text
|
|
206
|
+
~/.cdx/
|
|
207
|
+
sessions.json # Session registry (versioned, all sessions)
|
|
208
|
+
state/
|
|
209
|
+
<encoded-name>.json # Per-session rehydration state
|
|
210
|
+
profiles/
|
|
211
|
+
<encoded-name>/ # Codex session: CODEX_HOME points here
|
|
212
|
+
log/
|
|
213
|
+
cdx-session.log # Terminal transcript (written by script(1))
|
|
214
|
+
<encoded-name>/
|
|
215
|
+
claude-home/ # Claude session: HOME points here
|
|
216
|
+
log/
|
|
217
|
+
cdx-session.log
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Session names are URL-encoded when used as directory or file names. CLI command names such as `add`, `status`, and `login` are reserved and cannot be used as session names.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Troubleshooting
|
|
225
|
+
|
|
226
|
+
- **`cdx <name>` fails with "not authenticated"** — run `cdx login <name>` first.
|
|
227
|
+
- **`cdx add` succeeds but the session does not appear** — check that `CDX_HOME` is consistent between calls; a mismatch creates two separate registries.
|
|
228
|
+
- **Status shows `n/a` for all fields** — the session has not been launched yet, or the provider has not written any status output to its history files. Launch the session and run `/status` inside it at least once.
|
|
229
|
+
- **`cdx rmv` says "Removal requires confirmation in an interactive terminal"** — pass `--force` to bypass the prompt in non-interactive environments (scripts, CI).
|
|
230
|
+
- **`cdx login` hangs** — the provider's login flow requires a browser or device code. Follow the on-screen instructions in the terminal that opened.
|
|
231
|
+
- **`make install` says `npm link` is not found** — ensure Node.js and npm are installed and in your PATH.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Contributing
|
|
236
|
+
|
|
237
|
+
Contribution guidelines are available in [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## License
|
|
242
|
+
|
|
243
|
+
This project is licensed under the MIT License. See [`LICENSE`](LICENSE).
|
package/bin/cdx
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
8
|
+
if str(ROOT) not in sys.path:
|
|
9
|
+
sys.path.insert(0, str(ROOT))
|
|
10
|
+
|
|
11
|
+
from src.cli import format_error, main # noqa: E402
|
|
12
|
+
from src.errors import CdxError # noqa: E402
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
try:
|
|
17
|
+
raise SystemExit(main(sys.argv[1:]))
|
|
18
|
+
except CdxError as error:
|
|
19
|
+
sys.stderr.write(f"{format_error(error)}\n")
|
|
20
|
+
raise SystemExit(error.exit_code)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# CHANGELOGS_0_1_1
|
|
2
|
+
|
|
3
|
+
Release date: 2026-04-16
|
|
4
|
+
|
|
5
|
+
## CDX Manager 0.1.1
|
|
6
|
+
|
|
7
|
+
CDX Manager 0.1.1 is the first packaged release of the multi-provider terminal session manager for Codex and Claude accounts.
|
|
8
|
+
|
|
9
|
+
It turns the initial Logics-scoped idea into a usable CLI: isolated per-session auth homes, interactive provider launch, status extraction from local artifacts, Claude usage refresh through Anthropic rate-limit headers, log capture, cleanup commands, and release-ready package metadata.
|
|
10
|
+
|
|
11
|
+
### At a glance
|
|
12
|
+
|
|
13
|
+
- Added the `cdx` CLI entrypoint with conventional `--help`, `--version`, session listing, creation, launch, copy, removal, login, logout, clean, and status commands
|
|
14
|
+
- Added persistent session storage under `CDX_HOME`, with URL-encoded session names and per-session rehydration state
|
|
15
|
+
- Added explicit Codex and Claude provider support with isolated `CODEX_HOME` / `HOME` environments
|
|
16
|
+
- Added auth bootstrap and guardrails before launch, including provider-specific status checks and interactive login/logout commands
|
|
17
|
+
- Added terminal signal forwarding so launched provider CLIs receive interrupts and exits cleanly
|
|
18
|
+
- Added global and per-session status views with 5-hour, weekly, available, reset, and updated fields
|
|
19
|
+
- Added fallback status parsing from provider JSONL history and terminal transcript logs
|
|
20
|
+
- Added Claude automatic usage refresh from Anthropic API rate-limit headers
|
|
21
|
+
- Added terminal transcript capture through `script`, rotated launch logs, and the `cdx clean` command
|
|
22
|
+
- Migrated the implementation to Python while keeping npm-based install and validation scripts
|
|
23
|
+
- Added root packaging metadata, MIT license, README, contribution guidance, and release notes
|
|
24
|
+
|
|
25
|
+
### Session management
|
|
26
|
+
|
|
27
|
+
- `cdx add <name>` creates Codex sessions by default.
|
|
28
|
+
- `cdx add claude <name>` creates Claude sessions with a dedicated `claude-home`.
|
|
29
|
+
- `cdx <name>` launches the selected provider in an isolated environment.
|
|
30
|
+
- `cdx cp <source> <dest>` copies a session while preserving provider isolation.
|
|
31
|
+
- `cdx rmv <name> [--force]` removes the session registry entry and its auth directory.
|
|
32
|
+
- Session state is stored in a versioned JSON registry and per-session state files under `CDX_HOME`.
|
|
33
|
+
|
|
34
|
+
### Authentication and launch flow
|
|
35
|
+
|
|
36
|
+
- Codex sessions use `codex login status`, `codex login`, and `codex logout`.
|
|
37
|
+
- Claude sessions use `claude auth status`, `claude auth login`, and `claude auth logout`.
|
|
38
|
+
- Launches are blocked when a session is not authenticated, with clear recovery instructions.
|
|
39
|
+
- Signals from the wrapper process are forwarded to the child provider process.
|
|
40
|
+
- Codex launches show a reminder to run `/status` inside Codex when usage data needs refreshing.
|
|
41
|
+
|
|
42
|
+
### Usage and status reporting
|
|
43
|
+
|
|
44
|
+
- `cdx status` renders an aligned table for all sessions.
|
|
45
|
+
- `cdx status <name>` renders a detailed view for one session.
|
|
46
|
+
- `--json` is supported for both global and detail status views.
|
|
47
|
+
- Status rows include `AVAILABLE`, `5H LEFT`, `WEEK LEFT`, `RESET 5H`, `RESET WEEK`, and `UPDATED`.
|
|
48
|
+
- Available percentage is computed from the stricter remaining quota window.
|
|
49
|
+
- Cached statuses are enriched from newer or more detailed local artifacts when available.
|
|
50
|
+
|
|
51
|
+
### Codex status extraction
|
|
52
|
+
|
|
53
|
+
- Parses Codex `/status` blocks from logs and JSONL history.
|
|
54
|
+
- Handles ANSI / terminal control sequences and narrow terminal layouts.
|
|
55
|
+
- Extracts 5-hour and weekly reset values independently.
|
|
56
|
+
- Uses account identity in Codex status blocks to avoid accepting pasted status output from another account.
|
|
57
|
+
- Prefers direct launch logs over noisier conversational rollout JSONL artifacts.
|
|
58
|
+
|
|
59
|
+
### Claude status extraction
|
|
60
|
+
|
|
61
|
+
- Parses `Current session` and `Current week` blocks from Claude transcripts.
|
|
62
|
+
- Extracts 5-hour reset values from `Current session`, including AM/PM formats such as `Resets at 5:00 AM`.
|
|
63
|
+
- Extracts weekly reset values from `Current week`.
|
|
64
|
+
- Keeps Claude 5-hour and weekly reset values separate in status output.
|
|
65
|
+
- Refreshes Claude usage automatically from Anthropic rate-limit headers when credentials are available.
|
|
66
|
+
|
|
67
|
+
### Local time behavior
|
|
68
|
+
|
|
69
|
+
- Claude API reset timestamps are formatted in the machine's local timezone.
|
|
70
|
+
- Log-derived time-only reset values are inferred in the local timezone.
|
|
71
|
+
- Status `updated_at` values from API, JSONL, and file metadata are normalized to local ISO timestamps.
|
|
72
|
+
|
|
73
|
+
### Logging and cleanup
|
|
74
|
+
|
|
75
|
+
- Provider launches are captured to local transcript logs via `script`.
|
|
76
|
+
- Launch logs include unique timestamped filenames to avoid overwriting active or recent transcripts.
|
|
77
|
+
- Logs are rotated around the 10 MB threshold.
|
|
78
|
+
- `cdx clean [name]` clears launch transcript logs for one session or all sessions.
|
|
79
|
+
|
|
80
|
+
### Packaging
|
|
81
|
+
|
|
82
|
+
- Published package version is `0.1.1`.
|
|
83
|
+
- The npm tarball is restricted to the CLI entrypoint, source package, README, license, and release changelogs.
|
|
84
|
+
- Local project-only files such as `.claude`, Logics workflow docs, and tests are excluded from the package tarball.
|
|
85
|
+
|
|
86
|
+
### Validation
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm run lint
|
|
90
|
+
npm test
|
|
91
|
+
python3 bin/cdx --version
|
|
92
|
+
npm --cache /tmp/cdx-npm-cache pack --dry-run
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Notes
|
|
96
|
+
|
|
97
|
+
- This is the first release changelog, so it summarizes the full history from the initial Logics bootstrap through the 0.1.1 release preparation.
|
|
98
|
+
- The package remains marked `private` in `package.json`; publication requires intentionally changing that release policy.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# CHANGELOGS_0_2_0
|
|
2
|
+
|
|
3
|
+
Release date: 2026-04-16
|
|
4
|
+
|
|
5
|
+
## CDX Manager 0.2.0
|
|
6
|
+
|
|
7
|
+
CDX Manager 0.2.0 focuses on operational reliability. It adds health checks, safe repair workflows, reset notifications, stronger session-store behavior, and clearer status output for choosing the best account.
|
|
8
|
+
|
|
9
|
+
### At a glance
|
|
10
|
+
|
|
11
|
+
- Added `cdx doctor` to inspect local CLI dependencies, `CDX_HOME` writability, missing session state, orphan profiles, and pending quarantines.
|
|
12
|
+
- Added `cdx repair` with dry-run-by-default behavior and `--force` for safe repairs.
|
|
13
|
+
- Added `cdx notify` to wait for reset events or the next recommended usable session.
|
|
14
|
+
- Added compact status output with `cdx status --small` / `cdx status -s`.
|
|
15
|
+
- Improved `cdx status` ranking so rows are sorted by practical availability.
|
|
16
|
+
- Added priority guidance that explains which session to use first and which one comes next.
|
|
17
|
+
- Added countdown reset formatting for reset times under 24 hours.
|
|
18
|
+
- Decorated CLI output with terminal-native color support while respecting `NO_COLOR`, `CLICOLOR`, and TTY behavior.
|
|
19
|
+
- Split the CLI implementation into smaller modules for status rendering, provider runtime, commands, and storage.
|
|
20
|
+
|
|
21
|
+
### Doctor and repair
|
|
22
|
+
|
|
23
|
+
- `cdx doctor [--json]` reports installation and data-layout health.
|
|
24
|
+
- `cdx repair [--dry-run] [--force] [--json]` plans repairs by default.
|
|
25
|
+
- Missing per-session state files can be recreated safely.
|
|
26
|
+
- Pending quarantine profile directories can be cleaned up.
|
|
27
|
+
- Orphan profiles are moved to quarantine with `--force` instead of being deleted directly.
|
|
28
|
+
|
|
29
|
+
### Notifications
|
|
30
|
+
|
|
31
|
+
- `cdx notify <name> --at-reset` waits for the selected session reset time.
|
|
32
|
+
- `cdx notify --next-ready` waits for the recommended session to become usable or due for refresh.
|
|
33
|
+
- `--poll seconds`, `--once`, and `--json` are supported.
|
|
34
|
+
- macOS desktop notifications are sent through `osascript` when available, with terminal output as the consistent fallback.
|
|
35
|
+
|
|
36
|
+
### Status improvements
|
|
37
|
+
|
|
38
|
+
- `cdx status` now includes a direct priority line before the usage tip.
|
|
39
|
+
- The priority line prefers immediately usable accounts, accounts with earlier relevant resets, and accounts without credit fallback when appropriate.
|
|
40
|
+
- Reset columns use countdowns such as `in 2h 30m` under 24 hours.
|
|
41
|
+
- The main `cdx` session list now formats updated timestamps as relative ages.
|
|
42
|
+
- JSON status output keeps a stable shape even when live Claude refresh fails; refresh warnings are written to stderr.
|
|
43
|
+
|
|
44
|
+
### Reliability and safety
|
|
45
|
+
|
|
46
|
+
- Session names now reject reserved command names including `doctor`, `repair`, and `notify`.
|
|
47
|
+
- Session removal uses a quarantine flow and surfaces cleanup failures instead of silently ignoring them.
|
|
48
|
+
- Session-store writes fsync the containing directory after atomic replacement.
|
|
49
|
+
- Session-store locking now fails explicitly when file locks cannot be acquired.
|
|
50
|
+
- Codex transcript launch now supports quoted `CDX_SCRIPT_ARGS` with a `{transcript}` placeholder.
|
|
51
|
+
- Codex launch falls back without transcript capture when the `script` wrapper is unavailable or fails before producing a transcript.
|
|
52
|
+
- Signal interruptions no longer trigger transcript fallback relaunches.
|
|
53
|
+
|
|
54
|
+
### Validation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm run lint
|
|
58
|
+
npm test
|
|
59
|
+
./bin/cdx doctor
|
|
60
|
+
./bin/cdx repair --dry-run
|
|
61
|
+
./bin/cdx notify --next-ready --once
|
|
62
|
+
./bin/cdx --version
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Notes
|
|
66
|
+
|
|
67
|
+
- The package remains marked `private` in `package.json`; npm publication still requires an explicit policy change.
|
|
68
|
+
- No Git remote is configured in the current local repository, so this release is prepared locally and tagged locally only.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# CHANGELOGS_0_2_1
|
|
2
|
+
|
|
3
|
+
Release date: 2026-04-16
|
|
4
|
+
|
|
5
|
+
## CDX Manager 0.2.1
|
|
6
|
+
|
|
7
|
+
CDX Manager 0.2.1 prepares the package for npm publication.
|
|
8
|
+
|
|
9
|
+
### Packaging
|
|
10
|
+
|
|
11
|
+
- Removed the private package flag so npm publication is possible.
|
|
12
|
+
- Added npm package metadata: license, author, repository, bugs, homepage, keywords, and Node engine declaration.
|
|
13
|
+
- Added `prepublishOnly` so `npm publish` runs lint and tests before publication.
|
|
14
|
+
- Normalized the `bin.cdx` path with `npm pkg fix`.
|
|
15
|
+
- Documented the future npm install command in the README.
|
|
16
|
+
|
|
17
|
+
### Validation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm run lint
|
|
21
|
+
npm test
|
|
22
|
+
npm --cache /tmp/cdx-npm-cache publish --dry-run
|
|
23
|
+
npm view cdx-manager name version --registry https://registry.npmjs.org/
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Notes
|
|
27
|
+
|
|
28
|
+
- `npm view cdx-manager` currently returns `E404`, so the package name appears available on the npm registry.
|
|
29
|
+
- This release is prepared for npm publication but has not been published yet.
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cdx-manager",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Terminal session manager for Codex and Claude accounts.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Alexandre Agostini",
|
|
7
|
+
"homepage": "https://github.com/AlexAgo83/cdx-manager#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/AlexAgo83/cdx-manager.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/AlexAgo83/cdx-manager/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"codex",
|
|
17
|
+
"claude",
|
|
18
|
+
"cli",
|
|
19
|
+
"terminal",
|
|
20
|
+
"session-manager",
|
|
21
|
+
"ai-tools"
|
|
22
|
+
],
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin",
|
|
28
|
+
"changelogs",
|
|
29
|
+
"src",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"bin": {
|
|
34
|
+
"cdx": "bin/cdx"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"test": "npm run test:py",
|
|
38
|
+
"test:py": "PYTHONPYCACHEPREFIX=/tmp/pycache python3 -m unittest discover -s test -p 'test_*_py.py'",
|
|
39
|
+
"lint": "PYTHONPYCACHEPREFIX=/tmp/pycache python3 -m py_compile bin/cdx src/*.py test/test_*_py.py",
|
|
40
|
+
"prepublishOnly": "npm run lint && npm test",
|
|
41
|
+
"link": "npm link",
|
|
42
|
+
"unlink": "npm unlink -g cdx-manager"
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from .cli import main
|
|
2
|
+
from .errors import CdxError
|
|
3
|
+
from .session_service import (
|
|
4
|
+
ALLOWED_PROVIDERS,
|
|
5
|
+
DEFAULT_PROVIDER,
|
|
6
|
+
create_session_service,
|
|
7
|
+
)
|
|
8
|
+
from .session_store import create_session_store
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"ALLOWED_PROVIDERS",
|
|
12
|
+
"CdxError",
|
|
13
|
+
"DEFAULT_PROVIDER",
|
|
14
|
+
"create_session_service",
|
|
15
|
+
"create_session_store",
|
|
16
|
+
"main",
|
|
17
|
+
]
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import threading
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
|
|
5
|
+
from .claude_usage import refresh_claude_session_status
|
|
6
|
+
from .errors import CdxError
|
|
7
|
+
|
|
8
|
+
CLAUDE_REFRESH_TTL_SECONDS = 10 * 60
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _parse_timestamp(value):
|
|
12
|
+
if not value:
|
|
13
|
+
return None
|
|
14
|
+
try:
|
|
15
|
+
parsed = datetime.fromisoformat(str(value).replace("Z", "+00:00"))
|
|
16
|
+
except (TypeError, ValueError):
|
|
17
|
+
return None
|
|
18
|
+
if parsed.tzinfo is None:
|
|
19
|
+
parsed = parsed.replace(tzinfo=timezone.utc)
|
|
20
|
+
return parsed
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _is_stale(session, now=None, ttl_seconds=CLAUDE_REFRESH_TTL_SECONDS):
|
|
24
|
+
status = session.get("lastStatus") or {}
|
|
25
|
+
updated_at = _parse_timestamp(status.get("updated_at") or session.get("lastStatusAt"))
|
|
26
|
+
if not updated_at:
|
|
27
|
+
return True
|
|
28
|
+
now = now or datetime.now(timezone.utc).astimezone()
|
|
29
|
+
return (now - updated_at.astimezone(now.tzinfo)).total_seconds() >= ttl_seconds
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _refresh_claude_sessions(service, refresh_fn=None, target_names=None, force=False, ttl_seconds=CLAUDE_REFRESH_TTL_SECONDS):
|
|
33
|
+
refresh_fn = refresh_fn or refresh_claude_session_status
|
|
34
|
+
target_names = set(target_names or [])
|
|
35
|
+
sessions = service["list_sessions"]()
|
|
36
|
+
claude_sessions = [
|
|
37
|
+
s for s in sessions
|
|
38
|
+
if s["provider"] == "claude"
|
|
39
|
+
and (not target_names or s["name"] in target_names)
|
|
40
|
+
and (force or _is_stale(s, ttl_seconds=ttl_seconds))
|
|
41
|
+
]
|
|
42
|
+
if not claude_sessions:
|
|
43
|
+
return {"refreshed": [], "errors": []}
|
|
44
|
+
|
|
45
|
+
errors = []
|
|
46
|
+
results = {}
|
|
47
|
+
threads = []
|
|
48
|
+
|
|
49
|
+
def fetch(s):
|
|
50
|
+
try:
|
|
51
|
+
usage = refresh_fn(s)
|
|
52
|
+
if inspect.isawaitable(usage):
|
|
53
|
+
import asyncio
|
|
54
|
+
usage = asyncio.run(usage)
|
|
55
|
+
if usage:
|
|
56
|
+
results[s["name"]] = usage
|
|
57
|
+
except Exception as e:
|
|
58
|
+
errors.append({"session": s["name"], "error": e})
|
|
59
|
+
|
|
60
|
+
for s in claude_sessions:
|
|
61
|
+
t = threading.Thread(target=fetch, args=(s,))
|
|
62
|
+
threads.append(t)
|
|
63
|
+
t.start()
|
|
64
|
+
for t in threads:
|
|
65
|
+
t.join()
|
|
66
|
+
|
|
67
|
+
for name, usage in results.items():
|
|
68
|
+
try:
|
|
69
|
+
service["record_status"](name, usage)
|
|
70
|
+
except CdxError:
|
|
71
|
+
errors.append({"session": name, "error": CdxError(f"Failed to record Claude status for {name}")})
|
|
72
|
+
return {"refreshed": sorted(results), "errors": errors}
|