cdx-manager 0.9.9 → 0.9.10

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.9.9-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.9.10-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
 
@@ -0,0 +1,33 @@
1
+ # CDX Manager 0.9.10
2
+
3
+ ## Highlights
4
+
5
+ - Clarified Claude status warnings when quota refresh fails but local Claude auth is still valid.
6
+
7
+ ## Changes
8
+
9
+ ### Claude status diagnostics
10
+
11
+ `cdx status` now distinguishes a Claude quota refresh failure from a local Claude login failure.
12
+
13
+ When Anthropic quota probing returns an invalid-credentials response while `claude auth status` still reports an authenticated session, the JSON warning keeps the existing `claude_refresh_failed` code for compatibility and adds:
14
+
15
+ - `auth_status: authenticated`
16
+ - `status_freshness: stale`
17
+ - a message explaining that cached quota values may be stale
18
+
19
+ The text-mode warning now says that local auth is still valid instead of implying that the Claude account itself is down.
20
+
21
+ Regression coverage verifies both the JSON payload and terminal warning for this mixed auth/quota-refresh state.
22
+
23
+ ## Validation
24
+
25
+ - `npm run release:validate`
26
+ - `npm run lint`
27
+ - `npm test`
28
+ - `logics-manager lint --require-status`
29
+ - `logics-manager audit`
30
+ - `git diff --check`
31
+ - `npm --cache /private/tmp/cdx-npm-cache pack --dry-run`
32
+ - `python -m build`
33
+ - `python -m twine check dist/*`
@@ -100,6 +100,10 @@
100
100
  "v0.9.8": {
101
101
  "github_tarball_sha256": "8f05d052b7890d74de7ac510c20376430515ad2616e139941cb9bb8edc4fb680",
102
102
  "github_zip_sha256": "3bc3834ecf84784c77cf603fcd4f6f439e42005612f82fabab588886519179eb"
103
+ },
104
+ "v0.9.9": {
105
+ "github_tarball_sha256": "092c564803dd9a674250040ac308055de3701aa993237f257676403c24eb1318",
106
+ "github_zip_sha256": "0fbd8de7a97cde5aef14f23036def2e34dd690c2aa78ef93ddfeef0bb3e9547b"
103
107
  }
104
108
  }
105
109
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdx-manager",
3
- "version": "0.9.9",
3
+ "version": "0.9.10",
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.9.9"
7
+ version = "0.9.10"
8
8
  description = "Terminal session manager for Codex and Claude accounts."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
package/src/cli.py CHANGED
@@ -66,7 +66,7 @@ from .status_view import (
66
66
  )
67
67
  from .update_check import check_for_update, check_logics_manager_for_update
68
68
 
69
- VERSION = "0.9.9"
69
+ VERSION = "0.9.10"
70
70
 
71
71
 
72
72
  _COMMAND_HANDLERS = {
@@ -2533,16 +2533,6 @@ def handle_status(rest, ctx):
2533
2533
  }
2534
2534
  for item in refresh_result.get("errors", [])
2535
2535
  ]
2536
- warnings.extend([
2537
- {
2538
- "code": "claude_refresh_failed",
2539
- "session": item.get("session") or "unknown",
2540
- "message": item.get("error") or "unknown error",
2541
- }
2542
- for item in refresh_errors
2543
- ])
2544
- warnings.extend(_update_notice_warnings(ctx))
2545
-
2546
2536
  status_progress = None if parsed["json"] else _make_status_progress(ctx)
2547
2537
  if len(args) == 1:
2548
2538
  row = ctx["service"]["get_status_row"](
@@ -2552,11 +2542,16 @@ def handle_status(rest, ctx):
2552
2542
  cache_only=parsed["cached"],
2553
2543
  status_timeout_seconds=parsed["timeout"],
2554
2544
  )
2545
+ output_warnings = [
2546
+ *warnings,
2547
+ *_refresh_warning_payloads(refresh_errors, [row]),
2548
+ *_update_notice_warnings(ctx),
2549
+ ]
2555
2550
  if parsed["json"]:
2556
- _write_json(ctx, _json_success("status", f"Collected status for {args[0]}", warnings=warnings, session=row))
2551
+ _write_json(ctx, _json_success("status", f"Collected status for {args[0]}", warnings=output_warnings, session=row))
2557
2552
  return 0
2558
2553
  ctx["out"](f"{_format_status_detail(row, use_color=ctx['use_color'])}\n")
2559
- _write_refresh_warnings(refresh_errors, ctx)
2554
+ _write_refresh_warnings(refresh_errors, ctx, rows=[row])
2560
2555
  _write_update_notice(ctx)
2561
2556
  return 0
2562
2557
 
@@ -2566,11 +2561,16 @@ def handle_status(rest, ctx):
2566
2561
  cache_only=parsed["cached"],
2567
2562
  status_timeout_seconds=parsed["timeout"],
2568
2563
  )
2564
+ output_warnings = [
2565
+ *warnings,
2566
+ *_refresh_warning_payloads(refresh_errors, rows),
2567
+ *_update_notice_warnings(ctx),
2568
+ ]
2569
2569
  if parsed["json"]:
2570
- _write_json(ctx, _json_success("status", "Collected session status rows", warnings=warnings, rows=rows))
2570
+ _write_json(ctx, _json_success("status", "Collected session status rows", warnings=output_warnings, rows=rows))
2571
2571
  return 0
2572
2572
  ctx["out"](f"{_format_status_rows(rows, use_color=ctx['use_color'], small=parsed['small'])}\n")
2573
- _write_refresh_warnings(refresh_errors, ctx)
2573
+ _write_refresh_warnings(refresh_errors, ctx, rows=rows)
2574
2574
  _write_update_notice(ctx)
2575
2575
  return 0
2576
2576
 
@@ -2607,12 +2607,65 @@ def _refresh_claude_auth_states(service, target_names=None, spawn_sync=None, env
2607
2607
  return {"updated": updated, "errors": errors}
2608
2608
 
2609
2609
 
2610
- def _write_refresh_warnings(refresh_errors, ctx, stream="out"):
2611
- write = ctx["err"] if stream == "err" and "err" in ctx else ctx["out"]
2610
+ def _rows_by_session(rows):
2611
+ return {
2612
+ row.get("session_name"): row
2613
+ for row in (rows or [])
2614
+ if row.get("session_name")
2615
+ }
2616
+
2617
+
2618
+ def _is_invalid_claude_usage_auth(error):
2619
+ text = str(error or "").lower()
2620
+ return "http 401" in text and "invalid authentication credentials" in text
2621
+
2622
+
2623
+ def _has_valid_local_claude_auth(item, rows_by_name):
2624
+ session = item.get("session")
2625
+ row = rows_by_name.get(session)
2626
+ return bool(row and row.get("provider") == PROVIDER_CLAUDE and row.get("auth_status") == "authenticated")
2627
+
2628
+
2629
+ def _refresh_warning_payloads(refresh_errors, rows=None):
2630
+ rows_by_name = _rows_by_session(rows)
2631
+ warnings = []
2612
2632
  for item in refresh_errors:
2613
2633
  session = item.get("session") or "unknown"
2614
2634
  error = item.get("error") or "unknown error"
2615
- write(f"{_warn(f'Warning: Claude refresh failed for {session}: {error}', ctx['use_color'])}\n")
2635
+ warning = {
2636
+ "code": "claude_refresh_failed",
2637
+ "session": session,
2638
+ "message": error,
2639
+ }
2640
+ if _is_invalid_claude_usage_auth(error) and _has_valid_local_claude_auth(item, rows_by_name):
2641
+ warning.update({
2642
+ "auth_status": "authenticated",
2643
+ "status_freshness": "stale",
2644
+ "message": (
2645
+ "Claude quota refresh failed, but local Claude auth is still valid; "
2646
+ f"cached quota may be stale. Original error: {error}"
2647
+ ),
2648
+ })
2649
+ warnings.append(warning)
2650
+ return warnings
2651
+
2652
+
2653
+ def _format_refresh_warning(item, rows_by_name):
2654
+ session = item.get("session") or "unknown"
2655
+ error = item.get("error") or "unknown error"
2656
+ if _is_invalid_claude_usage_auth(error) and _has_valid_local_claude_auth(item, rows_by_name):
2657
+ return (
2658
+ f"Warning: Claude quota refresh failed for {session}, but local auth is still valid; "
2659
+ f"cached quota may be stale. ({error})"
2660
+ )
2661
+ return f"Warning: Claude refresh failed for {session}: {error}"
2662
+
2663
+
2664
+ def _write_refresh_warnings(refresh_errors, ctx, stream="out", rows=None):
2665
+ write = ctx["err"] if stream == "err" and "err" in ctx else ctx["out"]
2666
+ rows_by_name = _rows_by_session(rows)
2667
+ for item in refresh_errors:
2668
+ write(f"{_warn(_format_refresh_warning(item, rows_by_name), ctx['use_color'])}\n")
2616
2669
 
2617
2670
 
2618
2671
  def handle_export(rest, ctx):