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 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](https://img.shields.io/badge/license-MIT-4C8BF5)](LICENSE) ![Version](https://img.shields.io/badge/version-v0.2.1-4C8BF5) ![Python](https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white)
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
+ }
@@ -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}