cdx-manager 0.12.1 → 0.12.3
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 +6 -4
- package/changelogs/CHANGELOGS_0_12_2.md +28 -0
- package/changelogs/CHANGELOGS_0_12_3.md +29 -0
- package/checksums/release-archives.json +4 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/cli.py +2 -2
- package/src/cli_args.py +64 -11
- package/src/cli_commands.py +9 -8
- package/src/health.py +13 -0
- package/src/notify.py +7 -0
- package/src/run_command.py +32 -1
- package/src/update_manager.py +2 -2
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
|
|
|
@@ -204,7 +204,9 @@ Release maintainer note:
|
|
|
204
204
|
- Before publishing npm or PyPI packages, run `npm run release:validate`.
|
|
205
205
|
- The release tag must match `package.json`, `pyproject.toml`, `src/cli.py`, and `VERSION`.
|
|
206
206
|
- `checksums/release-archives.json` must include the matching `vX.Y.Z` entry with both `github_tarball_sha256` and `github_zip_sha256`.
|
|
207
|
-
-
|
|
207
|
+
- Publishing a GitHub Release runs the checksum upload workflow: it regenerates `checksums/release-archives.json` for the release tag and uploads it as the `release-archives.json` asset with replacement enabled for safe reruns.
|
|
208
|
+
- If the asset is missing or stale, repair it with `python3 scripts/update_release_checksums.py --tag vX.Y.Z`, then `gh release upload vX.Y.Z checksums/release-archives.json --clobber`.
|
|
209
|
+
- Publish packages only after the checksum workflow succeeds and `npm run release:validate`, `npm run lint`, and `npm test` pass.
|
|
208
210
|
|
|
209
211
|
### Environment
|
|
210
212
|
|
|
@@ -274,7 +276,7 @@ cdx disk profiles --candidates
|
|
|
274
276
|
|
|
275
277
|
### Next-Ready Notifications
|
|
276
278
|
|
|
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.
|
|
279
|
+
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
280
|
|
|
279
281
|
```bash
|
|
280
282
|
cdx ready
|
|
@@ -372,7 +374,7 @@ cdx history --summary --from 2026-05-01 --to 2026-05-28
|
|
|
372
374
|
| `cdx reset <name> [--yes] [--json]` | Explicitly consume one available banked Codex rate-limit reset; confirmation is required unless `--yes` is supplied |
|
|
373
375
|
| `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
376
|
| `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 |
|
|
377
|
+
| `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
378
|
| `cdx repair [--dry-run] [--force] [--json]` | Plan or apply safe repairs for missing state files, quarantines, and orphan profiles |
|
|
377
379
|
| `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
380
|
| `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`
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# CDX Manager 0.12.3
|
|
2
|
+
|
|
3
|
+
## Highlights
|
|
4
|
+
|
|
5
|
+
- GitHub Releases now trigger an automatic checksum asset workflow so `release-archives.json` is regenerated and uploaded for the published tag.
|
|
6
|
+
- `cdx run` validation errors now explain what is wrong and how to list or create matching sessions.
|
|
7
|
+
- Provider capacity or runtime failures now keep failed run records queryable instead of dropping them from the run registry.
|
|
8
|
+
|
|
9
|
+
## Changes
|
|
10
|
+
|
|
11
|
+
### Automated release checksum asset
|
|
12
|
+
|
|
13
|
+
Publishing a GitHub Release now runs `.github/workflows/upload-release-checksums.yml`. The workflow checks out the release tag, regenerates `checksums/release-archives.json`, and uploads it as the `release-archives.json` release asset with replacement enabled for reruns.
|
|
14
|
+
|
|
15
|
+
### More actionable run validation
|
|
16
|
+
|
|
17
|
+
`cdx run` now returns specific guidance for unknown session references and invalid provider selections so users can recover with `cdx configs`, `cdx add`, or a corrected provider argument.
|
|
18
|
+
|
|
19
|
+
### Queryable failed provider runs
|
|
20
|
+
|
|
21
|
+
Provider run failures are now written to the run registry with failure metadata, preserving enough detail for later inspection even when the provider never starts successfully.
|
|
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`
|
|
@@ -156,6 +156,10 @@
|
|
|
156
156
|
"v0.12.1": {
|
|
157
157
|
"github_tarball_sha256": "87e3b11886a8e7fb10233d425f6a1986e62395c89049947678a50cfb20384278",
|
|
158
158
|
"github_zip_sha256": "249f75eb240de7cc421f17579e0040d35eb06e3213568673f743c92191dda0f0"
|
|
159
|
+
},
|
|
160
|
+
"v0.12.2": {
|
|
161
|
+
"github_tarball_sha256": "5214f9f85f6bdc2c53bded938c91e28411de3c573bbcff81ebecb20dea6d99c2",
|
|
162
|
+
"github_zip_sha256": "8f447cdea9e921f7428c1c72db5a19f40f447a877bb2bc8dc1c8bd5e34daf070"
|
|
159
163
|
}
|
|
160
164
|
}
|
|
161
165
|
}
|
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.3"
|
|
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]"
|
|
@@ -35,6 +35,13 @@ CAN_RESUME_USAGE = "Usage: cdx can-resume <name> [--json]"
|
|
|
35
35
|
SELECT_USAGE = "Usage: cdx select --provider PROVIDER [--min-reasoning-effort minimal|low|medium|high|xhigh] [--min-power minimal|low|medium|high|xhigh] [--require-ready] [--refresh] --json"
|
|
36
36
|
NEXT_USAGE = "Usage: cdx next [--json] [--refresh]"
|
|
37
37
|
RUN_USAGE = "Usage: cdx run [session] --cwd PATH (--prompt-file PATH|--prompt TEXT) [--provider PROVIDER] [--model MODEL] [--kind assistant|code-review] [--reasoning-effort minimal|low|medium|high|xhigh] [--power minimal|low|medium|high|xhigh] [--permission review|default|auto|full|workspace-write|read-only|danger-full-access] [--timeout-seconds N] --json"
|
|
38
|
+
RUN_JSON_REQUIRED = "cdx run: --json is required."
|
|
39
|
+
RUN_TARGET_REQUIRED = "cdx run: specify a session name or --provider PROVIDER."
|
|
40
|
+
RUN_SESSION_PROVIDER_CONFLICT = "cdx run: cannot specify both a session name and --provider."
|
|
41
|
+
RUN_CWD_REQUIRED = "cdx run: --cwd PATH is required."
|
|
42
|
+
RUN_PROMPT_SOURCE_REQUIRED = "cdx run: specify exactly one prompt source: --prompt TEXT or --prompt-file PATH."
|
|
43
|
+
RUN_KIND_VALUES = ("assistant", "code-review")
|
|
44
|
+
RUN_PERMISSION_VALUES = ("review", "default", "auto", "full", "workspace-write", "read-only", "danger-full-access")
|
|
38
45
|
RUNS_USAGE = "Usage: cdx runs [--limit N] --json"
|
|
39
46
|
RUN_STATUS_USAGE = "Usage: cdx run-status <run_id> --json"
|
|
40
47
|
RUN_REPORT_USAGE = "Usage: cdx run-report <run_id> --json"
|
|
@@ -46,6 +53,35 @@ def _parse_json_flag(args):
|
|
|
46
53
|
return json_flag, cleaned
|
|
47
54
|
|
|
48
55
|
|
|
56
|
+
def _parse_doctor_args(args):
|
|
57
|
+
parsed = {"json": False, "severity": None}
|
|
58
|
+
index = 0
|
|
59
|
+
while index < len(args):
|
|
60
|
+
arg = args[index]
|
|
61
|
+
if arg == "--json":
|
|
62
|
+
parsed["json"] = True
|
|
63
|
+
index += 1
|
|
64
|
+
continue
|
|
65
|
+
if arg == "--severity":
|
|
66
|
+
if parsed["severity"] is not None or index + 1 >= len(args):
|
|
67
|
+
raise CdxError(DOCTOR_USAGE)
|
|
68
|
+
value = args[index + 1]
|
|
69
|
+
index += 2
|
|
70
|
+
elif arg.startswith("--severity="):
|
|
71
|
+
if parsed["severity"] is not None:
|
|
72
|
+
raise CdxError(DOCTOR_USAGE)
|
|
73
|
+
value = arg.split("=", 1)[1]
|
|
74
|
+
index += 1
|
|
75
|
+
else:
|
|
76
|
+
raise CdxError(DOCTOR_USAGE)
|
|
77
|
+
|
|
78
|
+
values = [item.strip().upper() for item in value.split(",")]
|
|
79
|
+
if not values or any(item not in {"OK", "WARN", "FAIL"} for item in values):
|
|
80
|
+
raise CdxError(DOCTOR_USAGE)
|
|
81
|
+
parsed["severity"] = ",".join(dict.fromkeys(values))
|
|
82
|
+
return parsed
|
|
83
|
+
|
|
84
|
+
|
|
49
85
|
def _parse_flag_args(args, schema, usage, positionals_key=None, max_positionals=0):
|
|
50
86
|
parsed = {spec["key"]: spec.get("default") for spec in schema.values()}
|
|
51
87
|
positionals = []
|
|
@@ -298,6 +334,21 @@ def _parse_timeout_seconds(value, usage=RUN_USAGE):
|
|
|
298
334
|
return parsed
|
|
299
335
|
|
|
300
336
|
|
|
337
|
+
def _parse_run_timeout_seconds(value):
|
|
338
|
+
try:
|
|
339
|
+
return _parse_timeout_seconds(value, RUN_USAGE)
|
|
340
|
+
except CdxError as error:
|
|
341
|
+
raise CdxError(f"cdx run: --timeout-seconds must be a positive number; got {value!r}.") from error
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _parse_run_provider(value):
|
|
345
|
+
try:
|
|
346
|
+
return _parse_provider_filter(value, RUN_USAGE)
|
|
347
|
+
except CdxError as error:
|
|
348
|
+
allowed = "|".join(PROVIDERS)
|
|
349
|
+
raise CdxError(f"cdx run: invalid --provider {value!r}; allowed values: {allowed}.") from error
|
|
350
|
+
|
|
351
|
+
|
|
301
352
|
def _normalize_run_permission(value):
|
|
302
353
|
if value is None:
|
|
303
354
|
return None
|
|
@@ -309,7 +360,8 @@ def _normalize_run_permission(value):
|
|
|
309
360
|
}
|
|
310
361
|
text = aliases.get(text, text)
|
|
311
362
|
if text not in ("review", "default", "auto", "full"):
|
|
312
|
-
|
|
363
|
+
allowed = "|".join(RUN_PERMISSION_VALUES)
|
|
364
|
+
raise CdxError(f"cdx run: invalid --permission {value!r}; allowed values: {allowed}.")
|
|
313
365
|
return text
|
|
314
366
|
|
|
315
367
|
|
|
@@ -318,28 +370,29 @@ def _parse_run_args(args):
|
|
|
318
370
|
"--cwd": {"key": "cwd", "type": "str", "default": None},
|
|
319
371
|
"--prompt-file": {"key": "prompt_file", "type": "str", "default": None},
|
|
320
372
|
"--prompt": {"key": "prompt", "type": "str", "default": None},
|
|
321
|
-
"--provider": {"key": "provider", "type": "str", "default": None, "transform":
|
|
373
|
+
"--provider": {"key": "provider", "type": "str", "default": None, "transform": _parse_run_provider},
|
|
322
374
|
"--model": {"key": "model", "type": "str", "default": None},
|
|
323
375
|
"--kind": {"key": "kind", "type": "str", "default": "assistant"},
|
|
324
376
|
"--reasoning-effort": {"key": "reasoning_effort", "type": "str", "default": None},
|
|
325
377
|
"--power": {"key": "power", "type": "str", "default": None},
|
|
326
378
|
"--permission": {"key": "permission", "type": "str", "default": None, "transform": _normalize_run_permission},
|
|
327
|
-
"--timeout-seconds": {"key": "timeout_seconds", "type": "str", "default": None, "transform":
|
|
379
|
+
"--timeout-seconds": {"key": "timeout_seconds", "type": "str", "default": None, "transform": _parse_run_timeout_seconds},
|
|
328
380
|
"--json": {"key": "json", "type": "bool", "default": False},
|
|
329
381
|
}, RUN_USAGE, positionals_key="names", max_positionals=1)
|
|
330
382
|
if not parsed["json"]:
|
|
331
|
-
raise CdxError(
|
|
383
|
+
raise CdxError(RUN_JSON_REQUIRED)
|
|
332
384
|
name = parsed["names"][0] if parsed["names"] else None
|
|
333
385
|
if name and parsed["provider"]:
|
|
334
|
-
raise CdxError(
|
|
386
|
+
raise CdxError(RUN_SESSION_PROVIDER_CONFLICT)
|
|
335
387
|
if not name and not parsed["provider"]:
|
|
336
|
-
raise CdxError(
|
|
388
|
+
raise CdxError(RUN_TARGET_REQUIRED)
|
|
337
389
|
if not parsed["cwd"]:
|
|
338
|
-
raise CdxError(
|
|
390
|
+
raise CdxError(RUN_CWD_REQUIRED)
|
|
339
391
|
if bool(parsed["prompt_file"]) == bool(parsed["prompt"]):
|
|
340
|
-
raise CdxError(
|
|
341
|
-
if parsed["kind"] not in
|
|
342
|
-
|
|
392
|
+
raise CdxError(RUN_PROMPT_SOURCE_REQUIRED)
|
|
393
|
+
if parsed["kind"] not in RUN_KIND_VALUES:
|
|
394
|
+
allowed = "|".join(RUN_KIND_VALUES)
|
|
395
|
+
raise CdxError(f"cdx run: invalid --kind {parsed['kind']!r}; allowed values: {allowed}.")
|
|
343
396
|
effort = _normalize_reasoning_effort(
|
|
344
397
|
reasoning_effort=parsed["reasoning_effort"],
|
|
345
398
|
power=parsed["power"],
|
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 {
|
package/src/run_command.py
CHANGED
|
@@ -18,6 +18,8 @@ def run_cdx_error_code(error):
|
|
|
18
18
|
message = str(error)
|
|
19
19
|
if message.startswith("Usage:"):
|
|
20
20
|
return "invalid_request"
|
|
21
|
+
if message.startswith("cdx run:"):
|
|
22
|
+
return "invalid_request"
|
|
21
23
|
if message.startswith("Invalid cwd:"):
|
|
22
24
|
return "invalid_cwd"
|
|
23
25
|
if message.startswith("Session is disabled:"):
|
|
@@ -47,6 +49,30 @@ def run_payload_reasoning_effort(parsed, session):
|
|
|
47
49
|
)
|
|
48
50
|
|
|
49
51
|
|
|
52
|
+
def _tail_line(path, limit=4000):
|
|
53
|
+
if not path:
|
|
54
|
+
return None
|
|
55
|
+
try:
|
|
56
|
+
size = os.path.getsize(path)
|
|
57
|
+
with open(path, "rb") as handle:
|
|
58
|
+
handle.seek(max(0, size - limit))
|
|
59
|
+
text = handle.read().decode("utf-8", errors="replace")
|
|
60
|
+
except OSError:
|
|
61
|
+
return None
|
|
62
|
+
lines = [line.strip() for line in text.splitlines() if line.strip()]
|
|
63
|
+
if not lines:
|
|
64
|
+
return None
|
|
65
|
+
return lines[-1][:500]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _provider_failure_message(error, run_info, error_code):
|
|
69
|
+
base = str(error) if error else "Run failed."
|
|
70
|
+
if error_code != "provider_failed":
|
|
71
|
+
return base
|
|
72
|
+
detail = _tail_line(run_info.get("stderr_path")) or _tail_line(run_info.get("stdout_path"))
|
|
73
|
+
return f"{base}: {detail}" if detail else base
|
|
74
|
+
|
|
75
|
+
|
|
50
76
|
def run_result_payload(api_schema_version, ok, parsed, session, run_info=None,
|
|
51
77
|
error=None, error_source=None, error_code=None):
|
|
52
78
|
run_info = run_info or {}
|
|
@@ -55,6 +81,11 @@ def run_result_payload(api_schema_version, ok, parsed, session, run_info=None,
|
|
|
55
81
|
if session else
|
|
56
82
|
empty_usage()
|
|
57
83
|
)
|
|
84
|
+
message = None if ok else (
|
|
85
|
+
_provider_failure_message(error, run_info, error_code)
|
|
86
|
+
if error_source == "provider"
|
|
87
|
+
else (str(error) if error else "Run failed.")
|
|
88
|
+
)
|
|
58
89
|
return {
|
|
59
90
|
"schema_version": api_schema_version,
|
|
60
91
|
"ok": bool(ok),
|
|
@@ -77,7 +108,7 @@ def run_result_payload(api_schema_version, ok, parsed, session, run_info=None,
|
|
|
77
108
|
"error": None if ok else {
|
|
78
109
|
"source": error_source or "cdx",
|
|
79
110
|
"code": error_code or "cdx_error",
|
|
80
|
-
"message":
|
|
111
|
+
"message": message,
|
|
81
112
|
"provider_code": run_info.get("returncode") if error_source == "provider" else None,
|
|
82
113
|
},
|
|
83
114
|
}
|
package/src/update_manager.py
CHANGED
|
@@ -63,10 +63,10 @@ def detect_installation(package_root=None, prefix=None, base_prefix=None):
|
|
|
63
63
|
return {"mode": "standalone", "package_root": str(root)}
|
|
64
64
|
if _is_source_checkout(root):
|
|
65
65
|
return {"mode": "source", "package_root": str(root)}
|
|
66
|
-
if _is_python_env(prefix=prefix, base_prefix=base_prefix):
|
|
67
|
-
return {"mode": "python", "package_root": str(root)}
|
|
68
66
|
if (root / "package.json").exists():
|
|
69
67
|
return {"mode": "npm", "package_root": str(root)}
|
|
68
|
+
if _is_python_env(prefix=prefix, base_prefix=base_prefix):
|
|
69
|
+
return {"mode": "python", "package_root": str(root)}
|
|
70
70
|
return {"mode": "unknown", "package_root": str(root)}
|
|
71
71
|
|
|
72
72
|
|