cdx-manager 0.7.2 → 0.7.4

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # CDX Manager
2
2
 
3
- [![License](https://img.shields.io/badge/license-MIT-4C8BF5)](LICENSE) ![Version](https://img.shields.io/badge/version-v0.7.2-4C8BF5) ![Python](https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white)
3
+ [![License](https://img.shields.io/badge/license-MIT-4C8BF5)](LICENSE) ![Version](https://img.shields.io/badge/version-v0.7.4-4C8BF5) ![Python](https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white)
4
4
 
5
5
  **Run multiple Codex, Claude, Antigravity, and Ollama sessions from one terminal. Switch between accounts instantly.**
6
6
 
@@ -118,7 +118,8 @@ With the standalone PowerShell installer:
118
118
 
119
119
  ```powershell
120
120
  Invoke-WebRequest https://raw.githubusercontent.com/AlexAgo83/cdx-manager/main/install.ps1 -OutFile install.ps1
121
- # Optional: set CDX_SHA256 before running if you have a trusted checksum
121
+ # Optional: set CDX_SHA256 before running if you have a trusted checksum.
122
+ # Set CDX_ALLOW_UNVERIFIED=1 only if you intentionally accept an unverified archive.
122
123
  powershell -ExecutionPolicy Bypass -File .\install.ps1
123
124
  ```
124
125
 
@@ -126,7 +127,8 @@ With the standalone GitHub installer:
126
127
 
127
128
  ```bash
128
129
  curl -fsSL https://raw.githubusercontent.com/AlexAgo83/cdx-manager/main/install.sh -o install.sh
129
- # Optional: set CDX_SHA256 before running if you have a trusted checksum
130
+ # Optional: set CDX_SHA256 before running if you have a trusted checksum.
131
+ # Set CDX_ALLOW_UNVERIFIED=1 only if you intentionally accept an unverified archive.
130
132
  sh install.sh
131
133
  ```
132
134
 
@@ -134,7 +136,7 @@ For a specific version:
134
136
 
135
137
  ```bash
136
138
  curl -fsSL https://raw.githubusercontent.com/AlexAgo83/cdx-manager/main/install.sh -o install.sh
137
- CDX_VERSION=v0.7.2 sh install.sh
139
+ CDX_VERSION=v0.7.4 sh install.sh
138
140
  ```
139
141
 
140
142
  From source:
@@ -183,6 +185,7 @@ Security note:
183
185
 
184
186
  - The standalone installers try to resolve official release checksums from `checksums/release-archives.json`.
185
187
  - You can still override verification explicitly through `CDX_SHA256`.
188
+ - If no checksum is available, standalone installers fail closed unless `CDX_ALLOW_UNVERIFIED=1` is set.
186
189
  - Prefer `npm`, `pipx`, or `uv` when you want registry-backed install flows.
187
190
  - If you use the standalone script, download it first, inspect it, and prefer a release with an official checksum entry.
188
191
 
@@ -241,6 +244,9 @@ cdx work
241
244
  # Check usage across all sessions
242
245
  cdx status
243
246
 
247
+ # Pick the best session using the same priority logic as status
248
+ cdx next
249
+
244
250
  # Notify when the next cooling-down assistant is ready
245
251
  cdx ready
246
252
  ```
@@ -256,7 +262,7 @@ cdx ready --refresh
256
262
 
257
263
  ### Persistent Launch Settings
258
264
 
259
- By default, `cdx` launches provider CLIs without forcing model effort, permission mode, or fast behavior. Set only the values you want to pin:
265
+ New sessions start with `power=medium` and `fast=off`, so launches are predictable without enabling fast mode. Set or override only the values you want to pin:
260
266
 
261
267
  ```bash
262
268
  cdx set work --power medium --permission full --fast off
@@ -269,9 +275,10 @@ cdx power all low
269
275
  cdx perm provider:claude review
270
276
  cdx model provider:ollama llama3.2
271
277
  cdx config work
278
+ cdx configs
272
279
  ```
273
280
 
274
- Those values are stored on the session and reapplied every time you run `cdx work`. Remove overrides to return to provider defaults:
281
+ Those values are stored on the session and reapplied every time you run `cdx work`. Remove overrides to return to provider-native defaults:
275
282
 
276
283
  ```bash
277
284
  cdx unset work --power
@@ -320,6 +327,7 @@ cdx history --summary --from 2026-05-01 --to 2026-05-28
320
327
  | `cdx disable <name> [--json]` | Disable a session without deleting it; disabled sessions stay visible and cannot launch |
321
328
  | `cdx enable <name> [--json]` | Re-enable a disabled session |
322
329
  | `cdx config <name> [--json]` | Show persistent launch settings for a session |
330
+ | `cdx configs [--json]` | Show persistent launch settings for all sessions in one table |
323
331
  | `cdx power\|perm\|fast\|model <name\|all\|provider:PROVIDER\|a,b> <value\|default> [--json]` | Shortcut commands for setting or clearing one launch setting |
324
332
  | `cdx set <name>\|--sessions all\|a,b\|--provider PROVIDER [--power low\|medium\|high\|xhigh\|max] [--permission review\|default\|auto\|full] [--fast on\|off] [--rtk on\|off] [--model MODEL] [--priority 0..100] [--json]` | Persist launch settings for one or more sessions |
325
333
  | `cdx unset <name>\|--sessions all\|a,b\|--provider PROVIDER (--power\|--permission\|--fast\|--rtk\|--model\|--priority\|--all) [--json]` | Remove persisted launch settings and fall back to provider defaults |
@@ -338,6 +346,7 @@ cdx history --summary --from 2026-05-01 --to 2026-05-28
338
346
  | `cdx ready [--refresh] [--json]` | Schedule an OS notification for the next cooling-down assistant that becomes ready, then return immediately |
339
347
  | `cdx notify <name> --at-reset [--poll seconds] [--once] [--schedule] [--refresh] [--json]` | Wait for a session reset time or schedule an OS wake-up notification when due |
340
348
  | `cdx notify --next-ready [--poll seconds] [--once] [--schedule] [--refresh] [--json]` | Wait until the recommended session is usable, or schedule the next known reset notification |
349
+ | `cdx next [--json] [--refresh]` | Select the best next assistant using the same priority logic as `cdx status` |
341
350
  | `cdx select --provider PROVIDER [--min-reasoning-effort low\|medium\|high] [--min-power low\|medium\|high] [--require-ready] [--refresh] --json` | Select a suitable session for headless automation |
342
351
  | `cdx run [session] --cwd PATH (--prompt-file PATH\|--prompt TEXT) [--provider PROVIDER] [--model MODEL] [--reasoning-effort low\|medium\|high] [--power low\|medium\|high] [--permission MODE] [--timeout-seconds N] --json` | Run one headless task and return a stable JSON result |
343
352
  | `cdx stats [name] [--since 7d\|today\|DATE] [--from DATE] [--to DATE] [--json]` | Aggregate launch counts, duration, and known headless token usage by session |
@@ -0,0 +1,37 @@
1
+ # Changelog (`0.7.2 -> 0.7.3`)
2
+
3
+ Release date: 2026-05-31
4
+
5
+ ## Status Display
6
+
7
+ - Rounded Codex credit balances in the `cdx status` `CR` column to two decimal places.
8
+ - Applied the same credit formatting to the single-session status detail view.
9
+ - Parallelized status resolution across sessions with a bounded worker pool while preserving cache behavior and final row ordering.
10
+ - Added visible per-session progress for text status checks, including `Checked <session> (x/y)` messages for refreshed sessions.
11
+
12
+ ## Stats and History Output
13
+
14
+ - Improved `cdx stats` text output with colorized headings, sessions, token totals, durations, and metadata when color is enabled.
15
+ - Reformatted long duration values into readable day/hour/minute forms such as `2h 05m`.
16
+ - Marked active sessions with `*` in `cdx stats`, matching `cdx status`.
17
+ - Improved `cdx history` and `cdx history --summary` with colorized headings, success/failure states, durations, and metadata.
18
+ - Marked active sessions with `*` in `cdx history` text output without changing JSON payloads.
19
+
20
+ ## Coverage and Tests
21
+
22
+ - Added regression coverage for status credit rounding, parallel status refresh, status progress messages, colorized stats/history output, readable duration formatting, and active-session markers.
23
+ - Increased the Python test suite to 262 tests.
24
+
25
+ ## Release Metadata
26
+
27
+ - Updated package metadata, CLI version output, README badge, pinned installer example, and release changelog to `v0.7.3`.
28
+
29
+ ## Validation and Regression Evidence
30
+
31
+ - `npm run lint`
32
+ - `npm test`
33
+ - `npm pack --dry-run`
34
+ - `node bin/cdx.js -v`
35
+ - `python3 -m unittest discover -s test`
36
+ - `python3 -m build`
37
+ - `python3 -m twine check dist/cdx_manager-0.7.3*`
@@ -0,0 +1,45 @@
1
+ # Changelog (`0.7.3 -> 0.7.4`)
2
+
3
+ Release date: 2026-06-02
4
+
5
+ ## Security and Privacy
6
+
7
+ - Rejected `.` and `..` as session names so profile and state paths cannot escape their managed directories.
8
+ - Added import-bundle regression coverage for unsafe dot-path session names.
9
+ - Redacted headless `cdx run` prompt arguments before persisting launch history while keeping provider execution unchanged.
10
+ - Made headless `cdx run` perform a live provider auth probe instead of trusting only local credential files.
11
+ - Hardened Claude auth invalidation so invalid Claude credentials are recorded as logged out during refresh and status handling.
12
+
13
+ ## Installer Integrity
14
+
15
+ - Changed standalone Unix and PowerShell installers to fail closed when no official checksum is available.
16
+ - Added the explicit `CDX_ALLOW_UNVERIFIED=1` override for users who intentionally accept an unverified archive.
17
+ - Updated README security guidance for standalone installer checksum behavior.
18
+
19
+ ## Headless and Selection Behavior
20
+
21
+ - Added the next-assistant recommendation command and improved resolved run reasoning-effort reporting.
22
+ - Preserved stable provider-specific launch metadata while adding stricter headless auth validation.
23
+
24
+ ## Maintainability
25
+
26
+ - Extracted headless run prompt, error-code, and JSON payload helpers into `src/run_command.py`.
27
+ - Reduced the size of `src/cli_commands.py` without changing the public `cdx run --json` contract.
28
+
29
+ ## Coverage and Tests
30
+
31
+ - Added regression coverage for dot-path session names, unsafe imported sessions, redacted headless history prompts, forced live auth probes, and unauthenticated headless runs.
32
+ - Increased the Python test suite to 276 tests.
33
+
34
+ ## Release Metadata
35
+
36
+ - Updated package metadata, CLI version output, README badge, pinned installer example, and release changelog to `v0.7.4`.
37
+
38
+ ## Validation and Regression Evidence
39
+
40
+ - `npm run lint`
41
+ - `npm test`
42
+ - `npm audit --omit=dev --json`
43
+ - `sh -n install.sh`
44
+ - PowerShell parser check for `install.ps1`
45
+ - `python3 -m logics_manager lint --require-status`
@@ -36,6 +36,14 @@
36
36
  "v0.7.1": {
37
37
  "github_tarball_sha256": "276cf2f094405bef674290ff8d1f2401f99afc10981c97efe4fe8293801715c8",
38
38
  "github_zip_sha256": "5d5cdefec3ebcd61d4149b71895f4d5b79e604cf874b2d567910e578a8164670"
39
+ },
40
+ "v0.7.2": {
41
+ "github_tarball_sha256": "9e993b15386c7b47895cfaca9c7e92165967ef34ecf8337c64823a5b0f9eb9e0",
42
+ "github_zip_sha256": "1e5fd926a16811f84137636830e0b27776b9f8f8d2eb8953b620023c8bfe6fdd"
43
+ },
44
+ "v0.7.3": {
45
+ "github_tarball_sha256": "f68ac90a51c723eef9ae2b66106baa954f2fff8129fb5353e8eeb26b258b06c9",
46
+ "github_zip_sha256": "dde2512701808450b39c5c4431317d95d017f9944435db83ac4497b645feb719"
39
47
  }
40
48
  }
41
49
  }
package/install.ps1 CHANGED
@@ -62,7 +62,11 @@ try {
62
62
  throw "cdx install: checksum mismatch for $tag`nexpected: $Sha256`nactual: $actualSha256"
63
63
  }
64
64
  } else {
65
- Write-Warning "No official checksum available for $tag; continuing without verification."
65
+ if ($env:CDX_ALLOW_UNVERIFIED -eq "1") {
66
+ Write-Warning "No official checksum available for $tag; continuing because CDX_ALLOW_UNVERIFIED=1."
67
+ } else {
68
+ throw "cdx install: no official checksum available for $tag. Set CDX_ALLOW_UNVERIFIED=1 to install without checksum verification."
69
+ }
66
70
  }
67
71
  Expand-Archive -Path $archivePath -DestinationPath $extractRoot -Force
68
72
 
package/install.sh CHANGED
@@ -86,7 +86,13 @@ if [ -n "$EXPECTED_SHA256" ]; then
86
86
  exit 1
87
87
  fi
88
88
  else
89
- echo "cdx install: warning: no official checksum available for $TAG; continuing without verification" >&2
89
+ if [ "${CDX_ALLOW_UNVERIFIED:-}" = "1" ]; then
90
+ echo "cdx install: warning: no official checksum available for $TAG; continuing because CDX_ALLOW_UNVERIFIED=1" >&2
91
+ else
92
+ echo "cdx install: no official checksum available for $TAG" >&2
93
+ echo "Set CDX_ALLOW_UNVERIFIED=1 to install without checksum verification." >&2
94
+ exit 1
95
+ fi
90
96
  fi
91
97
 
92
98
  tar -xzf "$TMP_DIR/cdx-manager.tar.gz" -C "$TMP_DIR"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdx-manager",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "description": "Terminal session manager for Codex and Claude accounts.",
5
5
  "license": "MIT",
6
6
  "author": "Alexandre Agostini",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cdx-manager"
7
- version = "0.7.2"
7
+ version = "0.7.4"
8
8
  description = "Terminal session manager for Codex and Claude accounts."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -2,7 +2,7 @@ import inspect
2
2
  import threading
3
3
  from datetime import datetime, timezone
4
4
 
5
- from .claude_usage import refresh_claude_session_status
5
+ from .claude_usage import ClaudeAuthInvalidError, refresh_claude_session_status
6
6
  from .config import PROVIDER_CLAUDE
7
7
  from .errors import CdxError
8
8
 
@@ -83,6 +83,24 @@ def _refresh_claude_sessions(service, refresh_fn=None, target_names=None, force=
83
83
  for t in threads:
84
84
  t.join(timeout=10)
85
85
 
86
+ invalid_auth = sorted({
87
+ item.get("session")
88
+ for item in errors
89
+ if item.get("session") and isinstance(item.get("error"), ClaudeAuthInvalidError)
90
+ })
91
+ if invalid_auth and service.get("update_auth_state"):
92
+ now = datetime.now(timezone.utc).astimezone().isoformat()
93
+ for name in invalid_auth:
94
+ try:
95
+ service["update_auth_state"](name, lambda auth: {
96
+ **auth,
97
+ "status": "logged_out",
98
+ "lastCheckedAt": now,
99
+ "lastLoggedOutAt": now,
100
+ })
101
+ except CdxError:
102
+ pass
103
+
86
104
  for name, usage in results.items():
87
105
  try:
88
106
  service["record_status"](name, usage)
@@ -15,6 +15,10 @@ CLAUDE_STATUS_PROBE_MODEL = os.environ.get("CDX_CLAUDE_STATUS_MODEL", "claude-ha
15
15
  CLAUDE_AUTH_STATUS_TIMEOUT_SECONDS = 15
16
16
 
17
17
 
18
+ class ClaudeAuthInvalidError(CdxError):
19
+ pass
20
+
21
+
18
22
  def _clean_oauth_token(token):
19
23
  if not token:
20
24
  return None
@@ -142,6 +146,10 @@ def fetch_claude_rate_limit_headers(access_token):
142
146
  headers = {k.lower(): v for k, v in resp.getheaders()}
143
147
  except urllib.error.HTTPError as e:
144
148
  headers = {k.lower(): v for k, v in e.headers.items()}
149
+ if e.code == 401:
150
+ message = _read_http_error_message(e)
151
+ suffix = f": {message}" if message else ""
152
+ raise ClaudeAuthInvalidError(f"Claude usage unavailable (HTTP {e.code}{suffix})") from e
145
153
  if (
146
154
  "anthropic-ratelimit-unified-5h-utilization" not in headers
147
155
  and "anthropic-ratelimit-unified-7d-utilization" not in headers
package/src/cli.py CHANGED
@@ -10,6 +10,7 @@ from .cli_commands import (
10
10
  handle_add,
11
11
  handle_clean,
12
12
  handle_config,
13
+ handle_configs,
13
14
  handle_context,
14
15
  handle_copy,
15
16
  handle_doctor,
@@ -25,6 +26,7 @@ from .cli_commands import (
25
26
  handle_logout,
26
27
  handle_launch_setting_alias,
27
28
  handle_notify,
29
+ handle_next,
28
30
  handle_remove,
29
31
  handle_repair,
30
32
  handle_rename,
@@ -58,7 +60,7 @@ from .status_view import (
58
60
  )
59
61
  from .update_check import check_for_update
60
62
 
61
- VERSION = "0.7.2"
63
+ VERSION = "0.7.4"
62
64
 
63
65
 
64
66
  # ---------------------------------------------------------------------------
@@ -75,10 +77,12 @@ def _print_help(use_color=False):
75
77
  f" {_style('cdx status [--json] [--refresh]', '36', use_color)}",
76
78
  f" {_style('cdx status --small|-s [--refresh]', '36', use_color)}",
77
79
  f" {_style('cdx status <name> [--json] [--refresh]', '36', use_color)}",
80
+ f" {_style('cdx next [--json] [--refresh]', '36', use_color)}",
78
81
  f" {_style('cdx select --provider PROVIDER [--min-reasoning-effort low|medium|high] [--min-power low|medium|high] [--require-ready] [--refresh] --json', '36', use_color)}",
79
82
  f" {_style('cdx run [session] --cwd PATH (--prompt-file PATH|--prompt TEXT) [--provider PROVIDER] [--model MODEL] [--reasoning-effort low|medium|high] [--power low|medium|high] [--permission review|default|auto|full|workspace-write|read-only|danger-full-access] [--timeout-seconds N] --json', '36', use_color)}",
80
83
  f" {_style('cdx context show|path|init|edit|clear|set [text...] [--json]', '36', use_color)}",
81
84
  f" {_style('cdx config <name> [--json]', '36', use_color)}",
85
+ f" {_style('cdx configs [--json]', '36', use_color)}",
82
86
  f" {_style('cdx power|perm|fast|model <name|all|provider:PROVIDER|a,b> <value|default> [--json]', '36', use_color)}",
83
87
  f" {_style('cdx set <name>|--sessions all|a,b|--provider PROVIDER [--power low|medium|high|xhigh|max] [--permission review|default|auto|full] [--fast on|off] [--rtk on|off] [--model MODEL] [--priority 0..100] [--json]', '36', use_color)}",
84
88
  f" {_style('cdx unset <name>|--sessions all|a,b|--provider PROVIDER (--power|--permission|--fast|--rtk|--model|--priority|--all) [--json]', '36', use_color)}",
@@ -240,7 +244,7 @@ def main(argv, options=None):
240
244
  "version": VERSION,
241
245
  "cwd": options.get("cwd") or os.getcwd(),
242
246
  "update_notice": _get_update_notice(service, env, options) if command not in (
243
- "add", "cp", "ren", "rename", "mv", "rmv", "clean", "doctor", "repair", "update", "ready", "notify", "context", "config", "set", "unset", "power", "perm", "fast", "model", "history", "stats", "handoff", "login", "logout", "disable", "enable", "export", "import", "select", "run", "help", "version"
247
+ "add", "cp", "ren", "rename", "mv", "rmv", "clean", "doctor", "repair", "update", "ready", "notify", "next", "context", "config", "configs", "set", "unset", "power", "perm", "fast", "model", "history", "stats", "handoff", "login", "logout", "disable", "enable", "export", "import", "select", "run", "help", "version"
244
248
  ) else None,
245
249
  "use_color": use_color,
246
250
  }
@@ -289,12 +293,18 @@ def main(argv, options=None):
289
293
  if command == "notify":
290
294
  return handle_notify(rest, ctx)
291
295
 
296
+ if command == "next":
297
+ return handle_next(rest, ctx)
298
+
292
299
  if command == "context":
293
300
  return handle_context(rest, ctx)
294
301
 
295
302
  if command == "config":
296
303
  return handle_config(rest, ctx)
297
304
 
305
+ if command == "configs":
306
+ return handle_configs(rest, ctx)
307
+
298
308
  if command == "set":
299
309
  return handle_set(rest, ctx)
300
310