cdx-manager 0.12.1 → 0.12.2
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 +3 -3
- package/changelogs/CHANGELOGS_0_12_2.md +28 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/cli.py +2 -2
- package/src/cli_args.py +30 -1
- package/src/cli_commands.py +9 -8
- package/src/health.py +13 -0
- package/src/notify.py +7 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CDX Manager
|
|
2
2
|
|
|
3
|
-
[](LICENSE) ](LICENSE)  
|
|
4
4
|
|
|
5
5
|
**Run multiple Codex, Claude, Antigravity, and Ollama sessions from one terminal. Switch between accounts instantly.**
|
|
6
6
|
|
|
@@ -274,7 +274,7 @@ cdx disk profiles --candidates
|
|
|
274
274
|
|
|
275
275
|
### Next-Ready Notifications
|
|
276
276
|
|
|
277
|
-
Use `cdx ready` when every useful assistant is cooling down and you want your terminal back. It is shorthand for `cdx notify --schedule --next-ready`: cdx picks the next known reset, registers a native OS notification, and exits immediately.
|
|
277
|
+
Use `cdx ready` when every useful assistant is cooling down and you want your terminal back. It is shorthand for `cdx notify --schedule --next-ready`: cdx picks the next known reset, registers a native OS notification, and exits immediately. If no future reset is known, it exits successfully and explains that no notification was scheduled.
|
|
278
278
|
|
|
279
279
|
```bash
|
|
280
280
|
cdx ready
|
|
@@ -372,7 +372,7 @@ cdx history --summary --from 2026-05-01 --to 2026-05-28
|
|
|
372
372
|
| `cdx reset <name> [--yes] [--json]` | Explicitly consume one available banked Codex rate-limit reset; confirmation is required unless `--yes` is supplied |
|
|
373
373
|
| `cdx export <file> [--include-auth] [--sessions a,b] [--passphrase-env VAR\|--passphrase-stdin] [--force] [--json]` | Export sessions to a portable bundle; `--include-auth` encrypts auth data with a passphrase |
|
|
374
374
|
| `cdx import <file> [--sessions a,b] [--passphrase-env VAR\|--passphrase-stdin] [--force] [--json]` | Import sessions from a bundle into the current `CDX_HOME` |
|
|
375
|
-
| `cdx doctor [--json]` | Inspect CLI dependencies, provider CLI versions/capability hints, CDX_HOME permissions, missing state, orphan profiles, and pending quarantines |
|
|
375
|
+
| `cdx doctor [--severity OK|WARN|FAIL[,OK|WARN|FAIL...]] [--json]` | Inspect CLI dependencies, provider CLI versions/capability hints, CDX_HOME permissions, missing state, orphan profiles, and pending quarantines; optionally filter issue severities |
|
|
376
376
|
| `cdx repair [--dry-run] [--force] [--json]` | Plan or apply safe repairs for missing state files, quarantines, and orphan profiles |
|
|
377
377
|
| `cdx view [--json] [--lan] [--lan-rw] [--focus <ref>] [--read] [--port <port>] [--host <host>] [--refresh-interval <s>] [--tls] [--tls-cert <path>] [--tls-key <path>] [--open] [--no-open]` | Open the Logics browser/focus viewer by delegating to `logics-manager view`; all viewer flags are forwarded; JSON mode reports diagnostics without launching it |
|
|
378
378
|
| `cdx update [--check] [--yes] [--json] [--version TAG]` | Update cdx-manager using the installer that matches how it was installed |
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# CDX Manager 0.12.2
|
|
2
|
+
|
|
3
|
+
## Highlights
|
|
4
|
+
|
|
5
|
+
- `cdx ready` now exits successfully when no future reset is known, clearly reporting that no notification was scheduled.
|
|
6
|
+
- `cdx doctor` can filter diagnostics by severity with `--severity OK|WARN|FAIL`.
|
|
7
|
+
- Unknown `cdx config <name>` sessions now explain how to inspect existing sessions or create the requested one.
|
|
8
|
+
|
|
9
|
+
## Changes
|
|
10
|
+
|
|
11
|
+
### Safer ready notifications
|
|
12
|
+
|
|
13
|
+
When `cdx ready` cannot find an upcoming session reset, it returns a successful unscheduled result instead of failing. Named reset notifications with no known reset remain explicit errors.
|
|
14
|
+
|
|
15
|
+
### Filterable diagnostics
|
|
16
|
+
|
|
17
|
+
`cdx doctor --severity` accepts a comma-separated list of `OK`, `WARN`, and `FAIL` statuses. Text and JSON output retain accurate summaries for the filtered report.
|
|
18
|
+
|
|
19
|
+
### Actionable configuration errors
|
|
20
|
+
|
|
21
|
+
Unknown-session errors from `cdx config` now direct users to `cdx configs` to inspect available sessions and `cdx add <name>` to create one.
|
|
22
|
+
|
|
23
|
+
## Validation
|
|
24
|
+
|
|
25
|
+
- `npm run lint`
|
|
26
|
+
- `npm test`
|
|
27
|
+
- `logics-manager lint --require-status`
|
|
28
|
+
- `logics-manager audit --group-by-doc`
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
package/src/cli.py
CHANGED
|
@@ -78,7 +78,7 @@ from .status_view import (
|
|
|
78
78
|
)
|
|
79
79
|
from .update_check import check_for_update, check_logics_manager_for_update
|
|
80
80
|
|
|
81
|
-
VERSION = "0.12.
|
|
81
|
+
VERSION = "0.12.2"
|
|
82
82
|
|
|
83
83
|
# Public surface: this module is a facade. Names below are imported above
|
|
84
84
|
# purely to be re-exported (consumed by tests and external callers); listing
|
|
@@ -216,7 +216,7 @@ def _print_help(use_color=False):
|
|
|
216
216
|
f" {_style('cdx clean profiles (--tmp|--old-logs DAYS) [--yes] [--json]', '36', use_color)}",
|
|
217
217
|
f" {_style('cdx export <file> [--include-auth] [--sessions a,b] [--passphrase-env VAR|--passphrase-stdin] [--force] [--json]', '36', use_color)}",
|
|
218
218
|
f" {_style('cdx import <file> [--sessions a,b] [--passphrase-env VAR|--passphrase-stdin] [--force|--merge] [--json]', '36', use_color)}",
|
|
219
|
-
f" {_style('cdx doctor [--json]', '36', use_color)}",
|
|
219
|
+
f" {_style('cdx doctor [--severity OK|WARN|FAIL[,OK|WARN|FAIL...]] [--json]', '36', use_color)}",
|
|
220
220
|
f" {_style('cdx repair [--dry-run] [--force] [--json]', '36', use_color)}",
|
|
221
221
|
f" {_style('cdx view [--json]', '36', use_color)}",
|
|
222
222
|
f" {_style('cdx update [all] [--check] [--yes] [--json] [--version TAG]', '36', use_color)}",
|
package/src/cli_args.py
CHANGED
|
@@ -12,7 +12,7 @@ from .errors import CdxError
|
|
|
12
12
|
from .provider_runtime import _normalize_reasoning_effort
|
|
13
13
|
|
|
14
14
|
STATUS_USAGE = "Usage: cdx status [--json] [--refresh|--cached] [--timeout SECONDS] | cdx status --small|-s [--refresh|--cached] [--timeout SECONDS] | cdx status <name> [--json] [--refresh|--cached] [--timeout SECONDS]"
|
|
15
|
-
DOCTOR_USAGE = "Usage: cdx doctor [--json]"
|
|
15
|
+
DOCTOR_USAGE = "Usage: cdx doctor [--severity OK|WARN|FAIL[,OK|WARN|FAIL...]] [--json]"
|
|
16
16
|
DISK_USAGE = "Usage: cdx disk [profiles] [--candidates] [--json]"
|
|
17
17
|
REPAIR_USAGE = "Usage: cdx repair [--dry-run] [--force] [--json]"
|
|
18
18
|
UPDATE_USAGE = "Usage: cdx update [all] [--check] [--yes] [--json] [--version TAG]"
|
|
@@ -46,6 +46,35 @@ def _parse_json_flag(args):
|
|
|
46
46
|
return json_flag, cleaned
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
def _parse_doctor_args(args):
|
|
50
|
+
parsed = {"json": False, "severity": None}
|
|
51
|
+
index = 0
|
|
52
|
+
while index < len(args):
|
|
53
|
+
arg = args[index]
|
|
54
|
+
if arg == "--json":
|
|
55
|
+
parsed["json"] = True
|
|
56
|
+
index += 1
|
|
57
|
+
continue
|
|
58
|
+
if arg == "--severity":
|
|
59
|
+
if parsed["severity"] is not None or index + 1 >= len(args):
|
|
60
|
+
raise CdxError(DOCTOR_USAGE)
|
|
61
|
+
value = args[index + 1]
|
|
62
|
+
index += 2
|
|
63
|
+
elif arg.startswith("--severity="):
|
|
64
|
+
if parsed["severity"] is not None:
|
|
65
|
+
raise CdxError(DOCTOR_USAGE)
|
|
66
|
+
value = arg.split("=", 1)[1]
|
|
67
|
+
index += 1
|
|
68
|
+
else:
|
|
69
|
+
raise CdxError(DOCTOR_USAGE)
|
|
70
|
+
|
|
71
|
+
values = [item.strip().upper() for item in value.split(",")]
|
|
72
|
+
if not values or any(item not in {"OK", "WARN", "FAIL"} for item in values):
|
|
73
|
+
raise CdxError(DOCTOR_USAGE)
|
|
74
|
+
parsed["severity"] = ",".join(dict.fromkeys(values))
|
|
75
|
+
return parsed
|
|
76
|
+
|
|
77
|
+
|
|
49
78
|
def _parse_flag_args(args, schema, usage, positionals_key=None, max_positionals=0):
|
|
50
79
|
parsed = {spec["key"]: spec.get("default") for spec in schema.values()}
|
|
51
80
|
positionals = []
|
package/src/cli_commands.py
CHANGED
|
@@ -17,7 +17,6 @@ from .cli_args import (
|
|
|
17
17
|
CAN_RESUME_USAGE,
|
|
18
18
|
CONTEXT_USAGE,
|
|
19
19
|
DISK_USAGE,
|
|
20
|
-
DOCTOR_USAGE,
|
|
21
20
|
HANDOFF_USAGE,
|
|
22
21
|
LAST_USAGE,
|
|
23
22
|
MEMORY_USAGE,
|
|
@@ -33,6 +32,7 @@ from .cli_args import (
|
|
|
33
32
|
_parse_config_args,
|
|
34
33
|
_parse_configs_args,
|
|
35
34
|
_parse_copy_args,
|
|
35
|
+
_parse_doctor_args,
|
|
36
36
|
_parse_export_args,
|
|
37
37
|
_parse_flag_args,
|
|
38
38
|
_parse_history_args,
|
|
@@ -98,7 +98,7 @@ from .context_store import (
|
|
|
98
98
|
)
|
|
99
99
|
from .errors import CdxError
|
|
100
100
|
from .fs_utils import atomic_write
|
|
101
|
-
from .health import collect_health_report, format_health_report
|
|
101
|
+
from .health import collect_health_report, filter_health_report, format_health_report
|
|
102
102
|
from .notify import (
|
|
103
103
|
format_notify_event,
|
|
104
104
|
format_scheduled_notification,
|
|
@@ -1700,7 +1700,10 @@ def handle_config(rest, ctx):
|
|
|
1700
1700
|
parsed = _parse_config_args(rest)
|
|
1701
1701
|
session = ctx["service"]["get_session"](parsed["name"])
|
|
1702
1702
|
if not session:
|
|
1703
|
-
raise CdxError(
|
|
1703
|
+
raise CdxError(
|
|
1704
|
+
f"Unknown session: {parsed['name']}. Run cdx configs to inspect existing sessions or "
|
|
1705
|
+
f"cdx add {parsed['name']} to create it."
|
|
1706
|
+
)
|
|
1704
1707
|
message = f"Launch settings for {parsed['name']}"
|
|
1705
1708
|
if parsed["json"]:
|
|
1706
1709
|
_write_json(ctx, _json_success("config", message, session=session, launch=session.get("launch") or {}))
|
|
@@ -1893,17 +1896,15 @@ def handle_clean(rest, ctx):
|
|
|
1893
1896
|
|
|
1894
1897
|
|
|
1895
1898
|
def handle_doctor(rest, ctx):
|
|
1896
|
-
|
|
1897
|
-
unknown = [arg for arg in rest if arg != "--json"]
|
|
1898
|
-
if unknown:
|
|
1899
|
-
raise CdxError(DOCTOR_USAGE)
|
|
1899
|
+
parsed = _parse_doctor_args(rest)
|
|
1900
1900
|
report = collect_health_report(
|
|
1901
1901
|
ctx["service"],
|
|
1902
1902
|
ctx["service"]["base_dir"],
|
|
1903
1903
|
env=ctx.get("env"),
|
|
1904
1904
|
spawn_sync=ctx.get("spawn_sync"),
|
|
1905
1905
|
)
|
|
1906
|
-
|
|
1906
|
+
report = filter_health_report(report, parsed["severity"])
|
|
1907
|
+
if parsed["json"]:
|
|
1907
1908
|
_write_json(ctx, _json_success("doctor", "Collected health report", report=report))
|
|
1908
1909
|
else:
|
|
1909
1910
|
ctx["out"](f"{format_health_report(report, use_color=ctx['use_color'])}\n")
|
package/src/health.py
CHANGED
|
@@ -363,6 +363,19 @@ def summarize_health(issues):
|
|
|
363
363
|
}
|
|
364
364
|
|
|
365
365
|
|
|
366
|
+
def filter_health_report(report, severity):
|
|
367
|
+
if severity is None:
|
|
368
|
+
return report
|
|
369
|
+
severities = set(severity.split(","))
|
|
370
|
+
issues = [issue for issue in report["issues"] if issue["status"] in severities]
|
|
371
|
+
return {
|
|
372
|
+
**report,
|
|
373
|
+
"issues": issues,
|
|
374
|
+
"summary": summarize_health(issues),
|
|
375
|
+
"severity": severity,
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
|
|
366
379
|
def format_health_report(report, use_color=False):
|
|
367
380
|
rows = [["STATUS", "CHECK", "MESSAGE"]]
|
|
368
381
|
for issue in report["issues"]:
|
package/src/notify.py
CHANGED
|
@@ -221,6 +221,13 @@ def schedule_notification_event(base_dir, parsed, event, spawn_sync=None, env=No
|
|
|
221
221
|
now_fn = now_fn or time.time
|
|
222
222
|
target_timestamp = event.get("target_timestamp")
|
|
223
223
|
if target_timestamp is None:
|
|
224
|
+
if parsed["mode"] == "next-ready":
|
|
225
|
+
return {
|
|
226
|
+
"scheduled": False,
|
|
227
|
+
"backend": "none",
|
|
228
|
+
"message": event["message"],
|
|
229
|
+
"target_timestamp": None,
|
|
230
|
+
}
|
|
224
231
|
raise CdxError(f"Cannot schedule notification: {event['message']}")
|
|
225
232
|
if target_timestamp <= now_fn():
|
|
226
233
|
return {
|