cdx-manager 0.12.2 → 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 +4 -2
- 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 +1 -1
- package/src/cli_args.py +34 -10
- 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
|
|
|
@@ -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
|
package/src/cli_args.py
CHANGED
|
@@ -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"
|
|
@@ -327,6 +334,21 @@ def _parse_timeout_seconds(value, usage=RUN_USAGE):
|
|
|
327
334
|
return parsed
|
|
328
335
|
|
|
329
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
|
+
|
|
330
352
|
def _normalize_run_permission(value):
|
|
331
353
|
if value is None:
|
|
332
354
|
return None
|
|
@@ -338,7 +360,8 @@ def _normalize_run_permission(value):
|
|
|
338
360
|
}
|
|
339
361
|
text = aliases.get(text, text)
|
|
340
362
|
if text not in ("review", "default", "auto", "full"):
|
|
341
|
-
|
|
363
|
+
allowed = "|".join(RUN_PERMISSION_VALUES)
|
|
364
|
+
raise CdxError(f"cdx run: invalid --permission {value!r}; allowed values: {allowed}.")
|
|
342
365
|
return text
|
|
343
366
|
|
|
344
367
|
|
|
@@ -347,28 +370,29 @@ def _parse_run_args(args):
|
|
|
347
370
|
"--cwd": {"key": "cwd", "type": "str", "default": None},
|
|
348
371
|
"--prompt-file": {"key": "prompt_file", "type": "str", "default": None},
|
|
349
372
|
"--prompt": {"key": "prompt", "type": "str", "default": None},
|
|
350
|
-
"--provider": {"key": "provider", "type": "str", "default": None, "transform":
|
|
373
|
+
"--provider": {"key": "provider", "type": "str", "default": None, "transform": _parse_run_provider},
|
|
351
374
|
"--model": {"key": "model", "type": "str", "default": None},
|
|
352
375
|
"--kind": {"key": "kind", "type": "str", "default": "assistant"},
|
|
353
376
|
"--reasoning-effort": {"key": "reasoning_effort", "type": "str", "default": None},
|
|
354
377
|
"--power": {"key": "power", "type": "str", "default": None},
|
|
355
378
|
"--permission": {"key": "permission", "type": "str", "default": None, "transform": _normalize_run_permission},
|
|
356
|
-
"--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},
|
|
357
380
|
"--json": {"key": "json", "type": "bool", "default": False},
|
|
358
381
|
}, RUN_USAGE, positionals_key="names", max_positionals=1)
|
|
359
382
|
if not parsed["json"]:
|
|
360
|
-
raise CdxError(
|
|
383
|
+
raise CdxError(RUN_JSON_REQUIRED)
|
|
361
384
|
name = parsed["names"][0] if parsed["names"] else None
|
|
362
385
|
if name and parsed["provider"]:
|
|
363
|
-
raise CdxError(
|
|
386
|
+
raise CdxError(RUN_SESSION_PROVIDER_CONFLICT)
|
|
364
387
|
if not name and not parsed["provider"]:
|
|
365
|
-
raise CdxError(
|
|
388
|
+
raise CdxError(RUN_TARGET_REQUIRED)
|
|
366
389
|
if not parsed["cwd"]:
|
|
367
|
-
raise CdxError(
|
|
390
|
+
raise CdxError(RUN_CWD_REQUIRED)
|
|
368
391
|
if bool(parsed["prompt_file"]) == bool(parsed["prompt"]):
|
|
369
|
-
raise CdxError(
|
|
370
|
-
if parsed["kind"] not in
|
|
371
|
-
|
|
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}.")
|
|
372
396
|
effort = _normalize_reasoning_effort(
|
|
373
397
|
reasoning_effort=parsed["reasoning_effort"],
|
|
374
398
|
power=parsed["power"],
|
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
|
|