cdx-manager 0.21.6 → 0.22.0
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 +1 -1
- package/VERSION +1 -1
- package/changelogs/CHANGELOGS_0_22_0.md +25 -0
- package/checksums/release-archives.json +11 -0
- package/docs/cli.md +17 -7
- package/docs/operations.md +79 -0
- package/docs/sessions-and-notifications.md +1 -1
- package/docs/technical-overview.md +1 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/agent_notify.py +22 -6
- package/src/cli.py +9 -2
- package/src/cli_args.py +37 -8
- package/src/cli_helpers.py +22 -9
- package/src/commands/launch.py +41 -3
- package/src/commands/maintenance.py +1 -0
- package/src/commands/rescue.py +207 -0
- package/src/commands/runs.py +23 -0
- package/src/commands/status.py +56 -6
- package/src/handoff.py +22 -11
- package/src/interactive_usage.py +20 -5
- package/src/provider_background.py +12 -7
- package/src/provider_runtime.py +49 -3
- package/src/retro.py +131 -0
- package/src/rtk_savings.py +68 -0
- package/src/run_registry.py +27 -1
- package/src/run_usage.py +36 -9
- package/src/session_backup.py +16 -5
- package/src/session_helpers.py +4 -0
- package/src/session_ranking.py +10 -0
- package/src/session_service.py +22 -2
- package/src/session_status.py +86 -2
- package/src/shared_memory.py +102 -0
- package/src/status_source.py +2 -0
- package/src/status_view.py +23 -23
- package/src/tray_autostart.py +21 -2
- package/src/tray_instance.py +11 -2
- package/src/update_manager.py +10 -4
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<br clear="left"/>
|
|
6
6
|
|
|
7
|
-
[](LICENSE) ](LICENSE)  
|
|
8
8
|
|
|
9
9
|
**The right AI coding account, when you need it.** CDX Manager keeps your Codex and Claude accounts in separate profiles, shows which ones have quota, and helps you pick the next usable session from your terminal.
|
|
10
10
|
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.22.0
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# CDX Manager 0.22.0
|
|
2
|
+
|
|
3
|
+
This release adds account-aware session recovery, clearer run controls, and more useful usage reporting. It also hardens credential handling and fixes several run and status edge cases.
|
|
4
|
+
|
|
5
|
+
## Account choice and recovery
|
|
6
|
+
|
|
7
|
+
- Set an account's automatic selection policy with `cdx set <name> --policy use|ask-first|never`. The policy is respected when CDX chooses an account for a run or rescue.
|
|
8
|
+
- `cdx rescue` can hand a limit-stopped conversation to a ready account. It also detects limit stops in the transcript and avoids duplicate rescue attempts.
|
|
9
|
+
- `cdx status` reports when an account may run out at its current pace and includes ISO 8601 reset times in JSON output.
|
|
10
|
+
- Claude conversations keep their own names, and handoffs work when a session has used multiple workspaces.
|
|
11
|
+
|
|
12
|
+
## Run and usage tools
|
|
13
|
+
|
|
14
|
+
- `cdx run` accepts a label and a concurrency cap. Cap checks, hook ownership, and blocked-status reporting have been corrected.
|
|
15
|
+
- Interactive session launch accepts `--task` and an optional `--end-state` to describe the authorized goal.
|
|
16
|
+
- `cdx stats --by project|model` breaks down usage, and stats can show estimated time saved by RTK over the selected period.
|
|
17
|
+
- Read-only session retrospectives are available on demand for an account.
|
|
18
|
+
- Profiles can share Claude auto-memory, and RTK history can be shared across profiles.
|
|
19
|
+
- One-hour cache writes are priced at the correct 2× rate.
|
|
20
|
+
|
|
21
|
+
## Security and reliability
|
|
22
|
+
|
|
23
|
+
- Credential isolation, backup-bundle import, session-name handling, and setup-token logging are hardened.
|
|
24
|
+
- Runtime fixes cover reset-time parsing, run hooks and status, and small CLI and documentation inconsistencies.
|
|
25
|
+
- The security policy is documented in `SECURITY.md`.
|
|
@@ -540,6 +540,17 @@
|
|
|
540
540
|
"x86_64-pc-windows-msvc": "d107e75e7700c2fcc7138d6bc14cdf6929fb3ad3e3d7a5b8648f44828f4ab4bb",
|
|
541
541
|
"x86_64-unknown-linux-musl": "00d93b4f16b58c203fbdc8cf80b15f1402ebf3fef8568162505142a3adb20272"
|
|
542
542
|
}
|
|
543
|
+
},
|
|
544
|
+
"v0.21.6": {
|
|
545
|
+
"github_tarball_sha256": "fc6c59c4c1d8349b2755a5206b90d62ec3e629abcf39f27785051332d3a7beec",
|
|
546
|
+
"github_zip_sha256": "78ac691c4b70fe3f2f2f200c17838ee41522b78eda6b0c90c39f587ca9c1ed47",
|
|
547
|
+
"tray_assets": {
|
|
548
|
+
"aarch64-apple-darwin": "9d423ba9ddd17217fa36f8995e7cfc88b93a9f572600d56ce7f03fc183fe552f",
|
|
549
|
+
"aarch64-unknown-linux-musl": "4c24363ac6347a75cebe3c450ac34df36087da6c6864ddfcac3317a6d347f645",
|
|
550
|
+
"x86_64-apple-darwin": "80ca55843a7ae2519e3f714388ba058399d609643688cac2f51b38ae54e3ecee",
|
|
551
|
+
"x86_64-pc-windows-msvc": "65cb725a2ae55350b8d9500f4293cd52e709ee017c999d65de9a1e12b472f7ed",
|
|
552
|
+
"x86_64-unknown-linux-musl": "6ff5479d7d57f329e76ad633e963e0d45f059d28004a3777f406b5bdd0713abe"
|
|
553
|
+
}
|
|
543
554
|
}
|
|
544
555
|
}
|
|
545
556
|
}
|
package/docs/cli.md
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
| `cdx <name>` | Launch a session (checks auth first) |
|
|
10
10
|
| `cdx <name> [--json]` | Launch a session; `--json` returns a structured success payload after the interactive run ends |
|
|
11
11
|
| `cdx <name> --prompt TEXT` / `cdx <name> --prompt-file PATH` | Launch a session that starts on this first message (not with `--resume` or Ollama sessions; `--prompt-file -` is refused because the session needs standard input; a relative `--prompt-file` path is read from the current directory, not `--dir`) |
|
|
12
|
+
| `cdx <name> --dir PATH --task TEXT [--end-state TEXT]` | Start a session on a task. `--end-state` names what your go-ahead covers ("CI green on a PR, merged"): the session takes every step it needs, commits, pushes and PRs included, and stops only for a decision outside it. Without `--end-state` it stops and shows the result before any commit, push or PR. Replaces `--prompt`; the first message also tells it to read the repository's instructions and use its skills. |
|
|
12
13
|
| `cdx <name> -r` / `cdx <name> --resume` | Resume the provider-native conversation for a session when supported |
|
|
13
|
-
| `cdx add [provider] <name> [--model MODEL] [--json]` | Register a new session (`provider`: `codex`, `claude`, `antigravity`, or `ollama`; Ollama requires `--model`) |
|
|
14
|
+
| `cdx add [provider] <name> [--model MODEL] [--json]` | Register a new session (`provider`: `codex`, `claude`, `antigravity`, or `ollama`; Ollama requires `--model`). New session names (also for `cp`, `rename` and `import`) must match `[A-Za-z0-9][A-Za-z0-9._-]*`; existing sessions keep their names |
|
|
14
15
|
| `cdx cp <source> <dest> [--json]` | Copy a session into another session name, overwriting the destination if it exists |
|
|
15
16
|
| `cdx ren <source> <dest> [--json]` | Rename a session and move its auth data |
|
|
16
17
|
| `cdx label <name> <label> [--json]` | Attach one short label to a session; tables show `LABEL` only when at least one label exists |
|
|
@@ -23,8 +24,8 @@
|
|
|
23
24
|
| `cdx config <name> [--json]` | Show persistent launch settings for a session |
|
|
24
25
|
| `cdx configs [--json]` | Show persistent launch settings for all sessions in one table |
|
|
25
26
|
| `cdx power\|perm\|fast\|model <name\|all\|provider:PROVIDER\|a,b> <value\|default> [--json]` | Shortcut commands for setting or clearing one launch setting |
|
|
26
|
-
| `cdx set <name>\|--sessions all\|a,b\|--provider PROVIDER [--power minimal\|low\|medium\|high\|xhigh] [--permission review\|default\|auto\|full] [--fast on\|off] [--rtk on\|off] [--logics on\|off] [--notify on\|off] [--notify-preview on\|off] [--scrub-env on\|off] [--model MODEL] [--fallback-model MODEL[,MODEL...]] [--budget USD] [--extra-args 'ARGS'] [--priority 0..100] [--json]` | Persist launch settings for one or more sessions |
|
|
27
|
-
| `cdx unset <name>\|--sessions all\|a,b\|--provider PROVIDER (--power\|--reasoning-effort\|--permission\|--fast\|--rtk\|--logics\|--notify\|--notify-preview\|--scrub-env\|--model\|--fallback-model\|--budget\|--extra-args\|--priority\|--all) [--json]` | Remove persisted launch settings and fall back to provider defaults |
|
|
27
|
+
| `cdx set <name>\|--sessions all\|a,b\|--provider PROVIDER [--power minimal\|low\|medium\|high\|xhigh] [--permission review\|default\|auto\|full] [--fast on\|off] [--rtk on\|off] [--logics on\|off] [--notify on\|off] [--notify-preview on\|off] [--scrub-env on\|off] [--shared-memory on\|off] [--retro on\|off] [--policy use\|ask-first\|never] [--model MODEL] [--fallback-model MODEL[,MODEL...]] [--budget USD] [--extra-args 'ARGS'] [--priority 0..100] [--json]` | Persist launch settings for one or more sessions |
|
|
28
|
+
| `cdx unset <name>\|--sessions all\|a,b\|--provider PROVIDER (--power\|--reasoning-effort\|--permission\|--fast\|--rtk\|--logics\|--notify\|--notify-preview\|--scrub-env\|--shared-memory\|--retro\|--policy\|--model\|--fallback-model\|--budget\|--extra-args\|--priority\|--all) [--json]` | Remove persisted launch settings and fall back to provider defaults |
|
|
28
29
|
| `cdx history [name] [--limit N] [--summary] [--since 7d\|today\|DATE] [--from DATE] [--to DATE] [--json]` | Show recent launch history or aggregate total launch time per assistant, optionally filtered by period |
|
|
29
30
|
| `cdx last [--json]` | Launch the most recent existing session from launch history |
|
|
30
31
|
| `cdx resume <name> [--json]` | Resume the provider-native conversation for a session using the named command form |
|
|
@@ -33,6 +34,8 @@
|
|
|
33
34
|
| `cdx memory [--global\|--project NAME_OR_PATH] [show\|view\|path\|init\|edit\|clear\|set\|append\|list] [text...] [--json]` | Manage explicit user-controlled memory for the current workspace, global scope, or a named/path project |
|
|
34
35
|
| `cdx handoff <name> [--json]` | Reuse the prepared entry for this target/workspace, or use explicitly labelled notes-only recovery if none exists; launch unless `--json` |
|
|
35
36
|
| `cdx handoff <source> <target> [--source-conversation ID\|--source-native-transcript PATH\|--source-transcript PATH] [--json]` | Prepare an identity-checked transcript entry and launch the target in the verified workspace unless `--json`; exact native selection resolves duplicate IDs, while explicit terminal selection is degraded recovery |
|
|
37
|
+
| `cdx rescue [<session>] [--to <target>] [--yes] [--dry-run] [--json]` | Hand the newest conversation stopped by a usage limit (account at 0%, or a transcript that ends on the provider's usage-limit error; launched in the last 24 h) over to the best ready account of the same provider, in the folder it last ran in. Picks only `--policy use` accounts; falls back to an `ask-first` one with a confirmation; never `never`. Asks before handing off unless `--yes` (which does not cover `ask-first`). `--dry-run` and `--json` show the plan only. A conversation already handed off is not offered again until its session is launched again. |
|
|
38
|
+
| `cdx retro [--limit N] [--json]` | Show the latest session retrospectives, newest first (5 by default). Retros run only for sessions with `cdx set <name> --retro on`; see [operations](operations.md#session-retros). |
|
|
36
39
|
| `cdx rmv <name> [--force] [--json]` | Remove a session and its auth data (prompts for confirmation unless `--force`) |
|
|
37
40
|
| `cdx clean [name] [--yes] [--json]` | Clear launch transcript logs for one session or all sessions after confirmation |
|
|
38
41
|
| `cdx clean profiles (--tmp\|--old-logs DAYS) [--yes] [--json]` | Remove explicit profile cleanup candidates after confirmation: temporary marketplace/plugin staging caches or old `.log` files |
|
|
@@ -49,17 +52,17 @@
|
|
|
49
52
|
| `cdx notify` | Internal provider hook target for agent alerts; use `cdx set <name> --notify on` to enable it |
|
|
50
53
|
| `cdx next [--json] [--refresh]` | Select the best next assistant using the same priority logic as `cdx status` |
|
|
51
54
|
| `cdx select --provider PROVIDER [--min-reasoning-effort minimal\|low\|medium\|high\|xhigh] [--min-power minimal\|low\|medium\|high\|xhigh] [--require-ready] [--refresh] --json` | Select a suitable session for headless automation |
|
|
52
|
-
| `cdx run [session] --cwd PATH (--prompt-file PATH\|--prompt TEXT\|--prompt-file -) [--provider PROVIDER] [--model MODEL] [--reasoning-effort minimal\|low\|medium\|high\|xhigh] [--power minimal\|low\|medium\|high\|xhigh] [--permission MODE] [--timeout-seconds N] [--detach] [--failover] [--refresh] --json` | Run one headless task and return a stable JSON result; `--detach` returns the `run_id` at launch without waiting, `--failover` continues the task on the next account when this one hits its rate limit, `--prompt-file -` reads the prompt from stdin |
|
|
55
|
+
| `cdx run [session] --cwd PATH (--prompt-file PATH\|--prompt TEXT\|--prompt-file -) [--provider PROVIDER] [--model MODEL] [--kind assistant\|code-review\|retro] [--reasoning-effort minimal\|low\|medium\|high\|xhigh] [--power minimal\|low\|medium\|high\|xhigh] [--permission MODE] [--timeout-seconds N] [--label TEXT] [--max-parallel N] [--detach] [--failover] [--refresh] --json` | Run one headless task and return a stable JSON result; `--detach` returns the `run_id` at launch without waiting, `--failover` continues the task on the next account when this one hits its rate limit, `--prompt-file -` reads the prompt from stdin, `--label` names the run in `cdx runs`, `--max-parallel N` (default `CDX_MAX_RUNS`; unset, invalid, 0 or negative means no limit) refuses to start with `too_many_runs` when N runs are already running, `--kind` (default `assistant`) labels the run record and `code-review` adds a review report to its final payload |
|
|
53
56
|
| `cdx run-report <run_id> --json` | Full report, transcript metadata, and final payload for a run |
|
|
54
57
|
| `cdx run-status <run_id> --json` | Status of one run by id |
|
|
55
58
|
| `cdx run-tail <run_id> [--lines N] --json` | Last lines of a run's own output, while it is still running or after it finished |
|
|
56
59
|
| `cdx runs [--limit N] [--since 7d\|today\|DATE] --json` | List recent runs; `--since` returns every run completed after the cursor and ignores `--limit` |
|
|
57
60
|
| `cdx schema --json` | Publish the enums, mutually-exclusive argument groups, and error codes programmatic callers should validate against |
|
|
58
|
-
| `cdx stats [name] [--since 7d\|today\|DATE] [--from DATE] [--to DATE] [--json]` | Aggregate launch counts, duration, and known token usage by session, for headless and interactive runs alike |
|
|
61
|
+
| `cdx stats [name] [--since 7d\|today\|DATE] [--from DATE] [--to DATE] [--by session\|project\|model] [--json]` | Aggregate launch counts, duration, and known token usage by session, for headless and interactive runs alike. `--by project` groups runs by launch folder (labelled by the shortest trailing part that tells folders apart; `project` in JSON holds the full path), `--by model` by the model that served them (same totals, same pricing; default `session`). PROV. lists every provider a row saw, such as `claude+codex`. Without a session filter it also prints what rtk saved over the period (`rtk` in JSON), read with `rtk gain` from `RTK_DB_PATH` when set, else the shared history, and the older per-profile ones. |
|
|
59
62
|
| `cdx status [--json] [--refresh\|--cached] [--timeout SECONDS]` | Show token usage table for all sessions; empty credits, block, and reset columns are omitted; `--cached` skips live provider probes and returns only stored status; Claude rows also report the auth type (`setup-token` or `full-login`) in a TYPE column and as `auth_type` in JSON. |
|
|
60
63
|
| `cdx status --small [--refresh\|--cached] [--timeout SECONDS]` / `cdx status -s [--refresh\|--cached] [--timeout SECONDS]` | Show compact token usage table without provider, blocking quota, credits, and updated columns |
|
|
61
64
|
| `cdx status <name> [--json] [--refresh\|--cached] [--timeout SECONDS]` | Show detailed usage breakdown for one session; `--cached` avoids live provider refreshes |
|
|
62
|
-
| `cdx tray status [--json] [--refresh]` | Emit the tray snapshot: one icon state, one line per enabled session, and how fresh each figure is. Reads stored status by default; `--refresh` is the only path that probes a provider |
|
|
65
|
+
| `cdx tray status [--json] [--refresh] [--beat]` | Emit the tray snapshot: one icon state, one line per enabled session, and how fresh each figure is. Reads stored status by default; `--refresh` is the only path that probes a provider; `--beat` also records a heartbeat, as `cdx tray heartbeat` does, which is how a companion claims alert delivery |
|
|
63
66
|
| `cdx tray launch [--json]` | Start the installed tray companion, detached. Reports `tray_companion_not_installed` when nothing is recorded, or `tray_companion_missing` when the recorded companion is gone. `CDX_TRAY_BIN` points it at a locally built one |
|
|
64
67
|
| `cdx tray install [--json] [--yes] [--autostart\|--no-autostart] [--alerts\|--no-alerts]` | Download the companion for this OS and architecture, verify its published checksum before unpacking, record what was written, and start it. It asks whether to launch at login; provider-hook consent is requested separately at the first eligible interactive session launch. Refuses when no checksum is published for the asset |
|
|
65
68
|
| `cdx tray uninstall [--json]` | Remove exactly what `install` recorded, and nothing else |
|
|
@@ -165,6 +168,12 @@ Commands with machine-readable output:
|
|
|
165
168
|
- `cdx select ... --json`
|
|
166
169
|
- `cdx run ... --json`
|
|
167
170
|
|
|
171
|
+
Status rows also carry `runs_out_at_iso`: when the account empties if it keeps the pace it has used since its 5-hour or weekly window opened (the earlier of the two), or `null` when that pace lasts until the reset. `cdx status <name>` shows it as `Runs out: ~11:48 at this pace`. It is an average since the window opened, not a prediction of what you will do next.
|
|
172
|
+
|
|
173
|
+
Status rows also carry `rate_limit_reached`: the provider's typed block reason (Codex, for example `workspace_owner_credits_depleted`), or `null`. A row with a reason is never selected by `cdx run` and confirms exhaustion for `--failover`, even when its percentages still read healthy.
|
|
174
|
+
|
|
175
|
+
In `cdx status --json` rows, `reset_5h_at`, `reset_week_at` and `reset_at` are display strings in local time with no year (`Oct 20 15:00`). Read `reset_5h_at_iso`, `reset_week_at_iso` and `reset_at_iso` instead: ISO 8601 with the local offset, or a bare date when the provider gave no time of day.
|
|
176
|
+
|
|
168
177
|
Success payloads follow a shared envelope:
|
|
169
178
|
|
|
170
179
|
```json
|
|
@@ -234,6 +243,7 @@ Every usage record, whichever path produced it, uses one set of definitions:
|
|
|
234
243
|
| --- | --- |
|
|
235
244
|
| `input_tokens` | Uncached input **only**. Codex reports a cache-inclusive count natively; cdx reduces it to the remainder so the field means the same thing on every provider. |
|
|
236
245
|
| `cache_creation_tokens` | Tokens written to the provider's prompt cache. |
|
|
246
|
+
| `cache_creation_1h_tokens` | The part of `cache_creation_tokens` written with Anthropic's one-hour TTL (`cache_creation.ephemeral_1h_input_tokens`), which costs 2x instead of 1.25x. A subset, never added to a total; `null` when the provider does not report the split. |
|
|
237
247
|
| `cache_read_tokens` | Tokens served from it. Kept apart from cache creation because the two cost roughly 1.25x and 0.1x of uncached input, a difference no summed figure can recover. |
|
|
238
248
|
| `cached_input_tokens` | `cache_creation_tokens + cache_read_tokens`, derived. The single CACHE column in `cdx stats` shows this. |
|
|
239
249
|
| `output_tokens` | Generated tokens. |
|
|
@@ -242,7 +252,7 @@ Every usage record, whichever path produced it, uses one set of definitions:
|
|
|
242
252
|
|
|
243
253
|
Cached input is counted once: it is excluded from `input_tokens` and reported in the cache fields, never in both. `null` means the provider did not report the figure, which is not the same as zero.
|
|
244
254
|
|
|
245
|
-
`cdx stats` also shows a **`COST~`** column and ranks sessions by it. It weights each token class by what it costs relative to one uncached input token — cache read at the recorded model's ratio (0.1x by default), cache write 1.25x, and output at the recorded model's output/input ratio — and reports the result in uncached-input-equivalent tokens. These are ratios rather than prices. It is **not** a currency amount, and it is not comparable across providers whose own prices differ.
|
|
255
|
+
`cdx stats` also shows a **`COST~`** column and ranks sessions by it. It weights each token class by what it costs relative to one uncached input token — cache read at the recorded model's ratio (0.1x by default), cache write 1.25x (2x for one-hour writes), and output at the recorded model's output/input ratio — and reports the result in uncached-input-equivalent tokens. These are ratios rather than prices. It is **not** a currency amount, and it is not comparable across providers whose own prices differ. Writes reported as one-hour are weighted 2x; a record without that split keeps the five-minute 1.25x rate.
|
|
246
256
|
|
|
247
257
|
Ranking on the raw total would put a session that mostly replayed a cache above one that spent far more on generation, because cache reads are typically the overwhelming majority of tokens consumed while costing a fiftieth of an output token.
|
|
248
258
|
|
package/docs/operations.md
CHANGED
|
@@ -25,6 +25,9 @@ Notes:
|
|
|
25
25
|
- Without `--passphrase-env`, `cdx` prompts in an interactive terminal. Non-interactive callers can pass `--passphrase-stdin` to read the passphrase from stdin (one line) instead of exposing it in the child environment.
|
|
26
26
|
- `--sessions work,perso` exports or imports only a subset.
|
|
27
27
|
- `--force` allows overwriting existing destination sessions during import or replacing an existing bundle file during export. Import preserves detected local `plugins/` state when the bundle does not provide it, and refuses to overwrite existing sessions from a bundle without auth payloads unless `--allow-authless-force` is also passed.
|
|
28
|
+
- Import only writes the provider credential files listed above, and only from an encrypted `--include-auth` bundle; any other file in a bundle makes the import fail before anything changes.
|
|
29
|
+
- Import refuses a new session whose `profiles/<name>` folder already exists without a registered session; remove or rename the folder first (`cdx repair` lists orphan profiles).
|
|
30
|
+
- `--force` on a Claude session always replaces its login: the profile's keychain entry is removed even when the bundle carries no credential, and restored if the import fails.
|
|
28
31
|
- Auth bundles contain credentials. Treat them like secrets and delete them after transfer.
|
|
29
32
|
|
|
30
33
|
## Release checklist
|
|
@@ -113,6 +116,81 @@ All `cdx clean` actions require interactive confirmation before any deletion or
|
|
|
113
116
|
|
|
114
117
|
The cleanup action is selected only when `--tmp` or `--old-logs` is present. This keeps `cdx clean profiles` available for an existing session named `profiles`. If any candidate or old log cannot be removed, cleanup stops with a non-zero error that identifies the affected path; it does not report the operation as successful.
|
|
115
118
|
|
|
119
|
+
## When an account runs out
|
|
120
|
+
|
|
121
|
+
A conversation that hits a 5-hour or weekly limit can go on under another account.
|
|
122
|
+
|
|
123
|
+
1. Keep accounts you share or want to spare out of every automatic choice:
|
|
124
|
+
`cdx set shared --policy ask-first` (offered only with a typed confirmation),
|
|
125
|
+
`cdx set colleague --policy never` (never offered).
|
|
126
|
+
Before it happens, `cdx status <name>` shows `Runs out: ~HH:MM at this pace` when the
|
|
127
|
+
current pace empties the account before its reset: a good moment to move long work.
|
|
128
|
+
2. When a session stops on its limit, check the plan first:
|
|
129
|
+
`cdx rescue --dry-run` shows the stopped conversation, its folder, when the
|
|
130
|
+
account comes back, and the account that would take over.
|
|
131
|
+
3. Run `cdx rescue` and confirm. It runs `cdx handoff <source> <target>
|
|
132
|
+
--source-conversation <id>` in the folder the conversation last ran in; the new
|
|
133
|
+
session starts with a recovery prompt over the exact transcript. A successful
|
|
134
|
+
handoff stamps the source conversation `rescuedAt`, so `cdx rescue` does not
|
|
135
|
+
offer it again until that session is launched again.
|
|
136
|
+
4. Several stopped sessions: `cdx rescue <session>` picks one; `--to <account>`
|
|
137
|
+
forces the target.
|
|
138
|
+
|
|
139
|
+
Limits: only conversations launched in the last 24 hours, on an account at 0% or whose
|
|
140
|
+
transcript ends on the usage-limit error (caught before `cdx status` refreshes).
|
|
141
|
+
The automatic pick only takes a ready account of the same provider; `--to` checks
|
|
142
|
+
only that the account exists and its policy is not `never`, so a target of another
|
|
143
|
+
provider or without quota is yours to rule out. If nothing is ready,
|
|
144
|
+
`cdx status` shows when each account resets.
|
|
145
|
+
|
|
146
|
+
## Delegating work in the background
|
|
147
|
+
|
|
148
|
+
Hand a task to a background run and keep working, without starting more runs than your accounts can carry.
|
|
149
|
+
|
|
150
|
+
1. Set a cap once, in your shell profile: `export CDX_MAX_RUNS=2`. Every `cdx run` then refuses to
|
|
151
|
+
start a third concurrent run (`too_many_runs`) instead of piling work onto spent accounts. `0` or a
|
|
152
|
+
negative value means no limit. A refused run leaves the session untouched (no new conversation).
|
|
153
|
+
2. Start the run with a label you will recognise later:
|
|
154
|
+
```bash
|
|
155
|
+
cdx run --provider claude --cwd ~/Documents/cts --label "fix build cts" \
|
|
156
|
+
--prompt-file task.md --detach --json
|
|
157
|
+
```
|
|
158
|
+
3. Follow it: `cdx runs --json` lists runs with their label and status; `cdx run-status <run_id> --json`
|
|
159
|
+
and `cdx run-tail <run_id> --json` show one of them.
|
|
160
|
+
4. A run refused by the cap: wait for one to finish, or pass `--max-parallel N` for that run.
|
|
161
|
+
## Starting a session on a task in another repository
|
|
162
|
+
|
|
163
|
+
From a hub session or a terminal, open a session in another repository with the task and
|
|
164
|
+
what you already approve, so it neither stops too early nor goes further than you meant:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
cdx work --dir ~/Documents/cts --task "Fix the failing CI on main" \
|
|
168
|
+
--end-state "CI green on a pull request, merged"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
- Write the end state as the result you will accept, naming the steps it includes (commit, push,
|
|
172
|
+
PR, merge). A vague "show me first" makes the session stop after the commit and wait for a push.
|
|
173
|
+
- Leave `--end-state` out when you want to review before anything is published: the session then
|
|
174
|
+
stops before any commit, push or PR.
|
|
175
|
+
- Name the repository's skill in the task when one fits ("use the implement-task skill") instead of
|
|
176
|
+
restating its steps; the session is told to read the repository's instructions anyway.
|
|
177
|
+
|
|
178
|
+
## Session retros
|
|
179
|
+
|
|
180
|
+
A correction you make once should not have to be made again next session.
|
|
181
|
+
|
|
182
|
+
1. Turn retros on for the accounts you work with: `cdx set work --retro on` (off by default:
|
|
183
|
+
each retro is one model call).
|
|
184
|
+
2. When an interactive session with at least 3 prompts ends, cdx starts a detached
|
|
185
|
+
`cdx run --kind retro --permission read-only --power low` on a ready `use` account of the
|
|
186
|
+
same provider. It reads the session's transcript and writes nothing.
|
|
187
|
+
3. Read the reports with `cdx retro` (or `cdx retro --limit 10`). Each lists the corrections you
|
|
188
|
+
made, then lessons tagged `memory:`, `runbook:`, `skill:` or `CLAUDE.md:` with the text to add.
|
|
189
|
+
4. Apply the ones you agree with yourself, or ask the next session to. With
|
|
190
|
+
`--shared-memory on`, a lesson saved to memory reaches every account.
|
|
191
|
+
|
|
192
|
+
Retro runs also appear in `cdx runs`; a retro never triggers a retro of its own.
|
|
193
|
+
|
|
116
194
|
## Banked Codex resets
|
|
117
195
|
|
|
118
196
|
Eligible Codex accounts expose manually redeemable rate-limit resets through the Codex app-server. `cdx status` shows their available count in the `RESETS` column; `cdx status <name>` also shows the nearest known expiration, and JSON status includes the available reset details supplied by Codex.
|
|
@@ -153,6 +231,7 @@ credential sync.
|
|
|
153
231
|
- **`cdx <name>` fails with "not authenticated"** — run `cdx login <name>` first. `cdx doctor` uses the same Codex app-server probe as `cdx auth refresh`, so a local auth file alone is never reported as live authentication; a rejected refresh token is reported as `login_required` with the matching login command.
|
|
154
232
|
- **A Claude session has no claude.ai connectors (Microsoft 365, ...) in `/mcp`** — `cdx login <name>` stores a setup-token for Claude profiles, and Claude Code can only make model requests with it: it never loads claude.ai connectors. `cdx status` shows each Claude profile's auth type (`setup-token` or `full-login`; `auth_type` in `--json`). To switch, start the session and run `/login` inside it. Once the profile holds its own claude.ai login (its `.claude/.credentials.json`, or its profile-specific `Claude Code-credentials-<hash>` keychain entry on macOS), cdx stops passing the setup-token on later launches. If that login later expires, run `/login` again, or go back to a setup-token with `cdx login <name>`, which removes the profile's `/login`.
|
|
155
233
|
- **Keep credentials away from what a Claude agent runs** — by default Claude Code passes its credentials, including the setup-token cdx injects, to the Bash commands, hooks and stdio MCP servers it starts. `cdx set <name> --scrub-env on` launches that session with `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1`, which strips credential-looking variables from those subprocesses (GitHub tokens and proxy settings stay). It is off by default: it also removes other secrets an agent may need (`NPM_TOKEN`, a `DATABASE_URL` with a password), and on Linux it isolates Bash from host processes, so `ps` and `kill` no longer see them. Worth turning on for unattended sessions that read untrusted content.
|
|
234
|
+
- **Share Claude's memory across accounts** — Claude Code keeps its auto-memory (`.claude/projects/<project>/memory`) in the profile home, so each account starts without what the others learned. `cdx set <name> --shared-memory on` links that folder, for the project being launched, to `~/.cdx/shared-memory/<project>`, shared by every profile with the setting on. Existing memory is copied in and the old folder kept as `memory.pre-cdx-<time>`; `MEMORY.md` indexes are merged line by line. A file with the same name and different content on two accounts is a conflict: cdx leaves that profile alone and names the files to merge by hand. macOS and Linux only (symlinks).
|
|
156
235
|
- **One of two Codex accounts keeps asking for login** — run `cdx doctor --json` and inspect each Codex session's `codex_auth_file`, `codex_live_auth`, and `codex_stale_auth_logs` checks. If recent logs mention expired auth, repair only that isolated profile with `cdx login <name>`. Codex Business profiles can share the same `tokens.account_id`; `cdx doctor` reports `codex_shared_account_id` as a workspace-level clue, not proof that two profiles use the same user. Newly created sessions seed from the current global `~/.codex/auth.json` when one exists. For two separate Codex accounts, create or repair each session by running `cdx login <name>` for that session; `cdx login` does not log out first, so use `cdx logout <name>` explicitly only when you want to clear that isolated profile.
|
|
157
236
|
- **`cdx` says no compatible Python 3 interpreter was found** — install Python 3 and make `py -3`, `python`, or `python3` available on PATH.
|
|
158
237
|
- **`cdx add` succeeds but the session does not appear** — check that `CDX_HOME` is consistent between calls; a mismatch creates two separate registries.
|
|
@@ -111,7 +111,7 @@ cdx power all default
|
|
|
111
111
|
cdx model provider:ollama default
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
`--model` maps to Codex `--model`, Claude `--model`, and Ollama `ollama run <model>`. `--power` maps to Codex `model_reasoning_effort` and Claude `--effort`; supported values are `minimal`, `low`, `medium`, `high`, and `xhigh`. `--permission` maps to provider-native permission flags. Codex Fast mode is separate from reasoning effort: `--fast on` opts a Codex session into the Codex Fast service tier, while new sessions, `--fast off`, and default launches force the non-Fast `flex` tier. Use `--power low` when you want low reasoning effort without enabling Codex Fast credits. Existing legacy sessions that stored `fast=on` before this split continue to behave as low effort unless the user explicitly sets `--fast on` again. `--priority` is a 0..100 selector preference used as a tie-breaker after readiness and availability. `--rtk on` adds standing guidance that encourages assistants to use RTK (`rtk <command>`) for noisy terminal commands when RTK is available, while keeping raw commands for exact output. Logics guidance is auto-enabled when `logics-manager` is available; use `--logics off` to disable that guidance for a session, or `--logics on` to pin it explicitly. This guidance is passed as instructions, never as a first message: Claude receives it through `--append-system-prompt` and Codex through `-c developer_instructions=...`, appended after any `developer_instructions` already set in the profile's `config.toml` (on Python older than 3.11 without `tomli`, a profile that sets its own is left untouched and gets no cdx guidance). Launching or resuming a session therefore never submits a turn on your behalf. Antigravity and Ollama have no instruction channel and receive no guidance. If you also pass `--append-system-prompt` through `--extra-args`, Claude receives the option twice; prefer putting your own text in the profile's `CLAUDE.md`.
|
|
114
|
+
`--model` maps to Codex `--model`, Claude `--model`, and Ollama `ollama run <model>`. `--power` maps to Codex `model_reasoning_effort` and Claude `--effort`; supported values are `minimal`, `low`, `medium`, `high`, and `xhigh`. `--permission` maps to provider-native permission flags. Codex Fast mode is separate from reasoning effort: `--fast on` opts a Codex session into the Codex Fast service tier, while new sessions, `--fast off`, and default launches force the non-Fast `flex` tier. Use `--power low` when you want low reasoning effort without enabling Codex Fast credits. Existing legacy sessions that stored `fast=on` before this split continue to behave as low effort unless the user explicitly sets `--fast on` again. `--priority` is a 0..100 selector preference used as a tie-breaker after readiness and availability. `--policy use|ask-first|never` (default `use`) says whether cdx may pick an account on your behalf: `cdx next`, `cdx select`, `cdx run --provider` and run failover only ever choose `use` accounts; `ask-first` accounts are offered by `cdx rescue` with a confirmation, `never` accounts are not offered at all. Naming an account explicitly (`cdx <name>`, `cdx run <name>`) is always allowed. `--rtk on` adds standing guidance that encourages assistants to use RTK (`rtk <command>`) for noisy terminal commands when RTK is available, while keeping raw commands for exact output. Logics guidance is auto-enabled when `logics-manager` is available; use `--logics off` to disable that guidance for a session, or `--logics on` to pin it explicitly. This guidance is passed as instructions, never as a first message: Claude receives it through `--append-system-prompt` and Codex through `-c developer_instructions=...`, appended after any `developer_instructions` already set in the profile's `config.toml` (on Python older than 3.11 without `tomli`, a profile that sets its own is left untouched and gets no cdx guidance). Launching or resuming a session therefore never submits a turn on your behalf. Antigravity and Ollama have no instruction channel and receive no guidance. Whatever the `--rtk` setting, every launch, resume and `cdx run` sets `RTK_DB_PATH` to the rtk history of your real home (`~/Library/Application Support/rtk/history.db` on macOS, `~/.local/share/rtk/history.db` on Linux, `%LOCALAPPDATA%\rtk\history.db` on Windows), so `rtk gain` in your own shell counts what every account saved instead of each profile keeping its own; an `RTK_DB_PATH` you set yourself is kept. If you also pass `--append-system-prompt` through `--extra-args`, Claude receives the option twice; prefer putting your own text in the profile's `CLAUDE.md`.
|
|
115
115
|
|
|
116
116
|
## Terminal Titles
|
|
117
117
|
|
|
@@ -85,7 +85,7 @@ All session data lives under `CDX_HOME` (default: `~/.cdx/`):
|
|
|
85
85
|
cdx-session.log
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
Session names are URL-encoded when used as directory or file names. CLI command names such as `add`, `disk`, `status`, and `login` are reserved and cannot be used as new session names.
|
|
88
|
+
Session names are URL-encoded when used as directory or file names. CLI command names such as `add`, `disk`, `status`, and `login` are reserved and cannot be used as new session names. New session names must start with a letter or digit and contain only letters, digits, `.`, `_` and `-`, so a name never means anything to a shell; sessions created before this rule keep working.
|
|
89
89
|
|
|
90
90
|
`cdx memory` is the user-facing interface for explicit memory. `--project NAME` uses a stable free-form project name; `--project /path/to/repo` resolves to the same workspace-hash memory that repo would use as the current workspace. `append` adds the text as-is with a clean newline boundary; it does not add dates or headings. `list` reports global memory, named project memories, and the current workspace memory when present; it does not scan your filesystem for repos.
|
|
91
91
|
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
package/src/agent_notify.py
CHANGED
|
@@ -11,6 +11,7 @@ owns because it sets HOME/CODEX_HOME per session at launch.
|
|
|
11
11
|
|
|
12
12
|
import json
|
|
13
13
|
import os
|
|
14
|
+
import re
|
|
14
15
|
import shutil
|
|
15
16
|
import subprocess
|
|
16
17
|
|
|
@@ -356,9 +357,11 @@ def provision(auth_home, provider, enabled, env=None, spawn_sync=None, base_dir=
|
|
|
356
357
|
"""
|
|
357
358
|
env = env or os.environ
|
|
358
359
|
if enabled and not notification_channel(env):
|
|
359
|
-
# Nothing could be delivered on this host
|
|
360
|
-
#
|
|
361
|
-
|
|
360
|
+
# Nothing could be delivered on this host (an SSH login, say), so
|
|
361
|
+
# installing a hook would only buy an approval prompt for a feature that
|
|
362
|
+
# shows nothing. Removing existing hooks is no better: Codex would ask to
|
|
363
|
+
# approve them again on the next local launch. Leave everything as is.
|
|
364
|
+
return False
|
|
362
365
|
if not supports_agent_alerts(provider):
|
|
363
366
|
return False
|
|
364
367
|
command = resolve_hook_command(env)
|
|
@@ -525,16 +528,29 @@ def _apply_hooks(path, enabled, command):
|
|
|
525
528
|
|
|
526
529
|
|
|
527
530
|
def _is_ours(entry):
|
|
528
|
-
"""Ours if any of its commands is
|
|
531
|
+
"""Ours if any of its commands is exactly `<path to cdx> notify`.
|
|
532
|
+
|
|
533
|
+
Matched on the executable's basename, not a substring, so a user hook such
|
|
534
|
+
as `~/cdx-scripts/slack notify` is never mistaken for ours and deleted.
|
|
535
|
+
"""
|
|
529
536
|
if not isinstance(entry, dict):
|
|
530
537
|
return False
|
|
531
538
|
for hook in entry.get("hooks") or []:
|
|
532
|
-
|
|
533
|
-
if command.endswith(f" {HOOK_ARG}") and "cdx" in command:
|
|
539
|
+
if isinstance(hook, dict) and _is_cdx_notify(str(hook.get("command") or "")):
|
|
534
540
|
return True
|
|
535
541
|
return False
|
|
536
542
|
|
|
537
543
|
|
|
544
|
+
def _is_cdx_notify(command):
|
|
545
|
+
command = command.strip()
|
|
546
|
+
if command.startswith('"'):
|
|
547
|
+
program, _, rest = command[1:].partition('"')
|
|
548
|
+
else:
|
|
549
|
+
program, _, rest = command.partition(" ")
|
|
550
|
+
name = re.split(r"[\\/]", program)[-1].lower()
|
|
551
|
+
return name in {"cdx", "cdx.cmd", "cdx.exe"} and rest.strip() == HOOK_ARG
|
|
552
|
+
|
|
553
|
+
|
|
538
554
|
def _read_json(path):
|
|
539
555
|
"""Existing content, {} when absent, or None when we must not touch it.
|
|
540
556
|
|
package/src/cli.py
CHANGED
|
@@ -71,8 +71,10 @@ from .cli_render import (
|
|
|
71
71
|
_visible_len,
|
|
72
72
|
format_error,
|
|
73
73
|
)
|
|
74
|
+
from .commands.rescue import handle_rescue
|
|
74
75
|
from .config import PROVIDER_OLLAMA
|
|
75
76
|
from .errors import CdxError
|
|
77
|
+
from .retro import handle_retro
|
|
76
78
|
from .session_service import create_session_service
|
|
77
79
|
from .status_view import (
|
|
78
80
|
_format_blocking_quota,
|
|
@@ -175,6 +177,8 @@ _COMMAND_HANDLERS = {
|
|
|
175
177
|
"rename": handle_rename,
|
|
176
178
|
"resume": handle_resume,
|
|
177
179
|
"can-resume": handle_can_resume,
|
|
180
|
+
"rescue": handle_rescue,
|
|
181
|
+
"retro": handle_retro,
|
|
178
182
|
"rmv": handle_remove,
|
|
179
183
|
"run": handle_run,
|
|
180
184
|
"run-report": handle_run_report,
|
|
@@ -236,7 +240,7 @@ def _print_help(use_color=False):
|
|
|
236
240
|
f" {_style('cdx reset <name> [--yes] [--json]', '36', use_color)}",
|
|
237
241
|
f" {_style('cdx auth refresh <name|all> [--json]', '36', use_color)}",
|
|
238
242
|
f" {_style('cdx select --provider PROVIDER [--min-reasoning-effort minimal|low|medium|high|xhigh] [--min-power minimal|low|medium|high|xhigh] [--require-ready] [--refresh] --json', '36', use_color)}",
|
|
239
|
-
f" {_style('cdx run [session] --cwd PATH (--prompt-file PATH|--prompt TEXT|--prompt-file -) [--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] [--detach] [--refresh] --json', '36', use_color)}",
|
|
243
|
+
f" {_style('cdx run [session] --cwd PATH (--prompt-file PATH|--prompt TEXT|--prompt-file -) [--provider PROVIDER] [--model MODEL] [--kind assistant|code-review|retro] [--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] [--label TEXT] [--max-parallel N] [--detach] [--failover] [--refresh] --json', '36', use_color)}",
|
|
240
244
|
f" {_style('cdx runs [--limit N] [--since 7d|today|DATE] --json', '36', use_color)}",
|
|
241
245
|
f" {_style('cdx run-status <run_id> --json', '36', use_color)}",
|
|
242
246
|
f" {_style('cdx run-report <run_id> --json', '36', use_color)}",
|
|
@@ -251,10 +255,12 @@ def _print_help(use_color=False):
|
|
|
251
255
|
f" {_style(SET_USAGE[len('Usage: '):], '36', use_color)}",
|
|
252
256
|
f" {_style('cdx unset <name>|--sessions all|a,b|--provider PROVIDER (--power|--permission|--fast|--rtk|--logics|--notify|--notify-preview|--model|--priority|--all) [--json]', '36', use_color)}",
|
|
253
257
|
f" {_style('cdx history [name] [--limit N] [--summary] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--json]', '36', use_color)}",
|
|
254
|
-
f" {_style('cdx stats [name] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--json]', '36', use_color)}",
|
|
258
|
+
f" {_style('cdx stats [name] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--by session|project|model] [--json]', '36', use_color)}",
|
|
255
259
|
f" {_style('cdx last [--json]', '36', use_color)}",
|
|
256
260
|
f" {_style('cdx resume <name> [--json]', '36', use_color)}",
|
|
257
261
|
f" {_style('cdx can-resume <name> [--json]', '36', use_color)}",
|
|
262
|
+
f" {_style('cdx rescue [<session>] [--to <target>] [--yes] [--dry-run] [--json]', '36', use_color)}",
|
|
263
|
+
f" {_style('cdx retro [--limit N] [--json]', '36', use_color)}",
|
|
258
264
|
f" {_style('cdx handoff <name> [--json]', '36', use_color)}",
|
|
259
265
|
f" {_style('cdx handoff <source> <target> [--source-conversation ID | --source-native-transcript PATH | --source-transcript PATH] [--json]', '36', use_color)}",
|
|
260
266
|
f" {_style('cdx add [provider] <name> [--model MODEL] [--json]', '36', use_color)}",
|
|
@@ -285,6 +291,7 @@ def _print_help(use_color=False):
|
|
|
285
291
|
f" {_style('cdx ready', '36', use_color)} # next session to become ready",
|
|
286
292
|
f" {_style('cdx <name> [--json]', '36', use_color)}",
|
|
287
293
|
f" {_style('cdx <name> --prompt TEXT|--prompt-file PATH', '36', use_color)} # start with a first message",
|
|
294
|
+
f" {_style('cdx <name> --dir PATH --task TEXT --end-state TEXT', '36', use_color)} # start on a task, with what the go-ahead covers",
|
|
288
295
|
f" {_style('cdx --help', '36', use_color)}",
|
|
289
296
|
f" {_style('cdx --version', '36', use_color)}",
|
|
290
297
|
])
|
package/src/cli_args.py
CHANGED
|
@@ -33,26 +33,27 @@ CONTEXT_USAGE = "Usage: cdx context show|path|init|edit|clear|set|append [text..
|
|
|
33
33
|
MEMORY_USAGE = "Usage: cdx memory [--global|--project NAME_OR_PATH] [show|view|path|init|edit|clear|set|append|list] [text...] [--json]"
|
|
34
34
|
HANDOFF_USAGE = "Usage: cdx handoff <name> [--json] | cdx handoff <source> <target> [--source-conversation ID | --source-native-transcript PATH | --source-transcript PATH] [--json]"
|
|
35
35
|
LABEL_USAGE = "Usage: cdx label <name> <label> [--json] | cdx label <name> --clear [--json]"
|
|
36
|
-
SET_USAGE = "Usage: cdx set <name>|--sessions all|a,b|--provider PROVIDER [--power minimal|low|medium|high|xhigh] [--permission review|default|auto|full] [--fast on|off] [--rtk on|off] [--logics on|off] [--notify on|off] [--notify-preview on|off] [--scrub-env on|off] [--model MODEL] [--fallback-model MODEL[,MODEL...]] [--budget USD] [--extra-args 'ARGS'] [--priority 0..100] [--json]"
|
|
37
|
-
UNSET_USAGE = "Usage: cdx unset <name>|--sessions all|a,b|--provider PROVIDER (--power|--reasoning-effort|--permission|--fast|--rtk|--logics|--notify|--notify-preview|--scrub-env|--model|--fallback-model|--budget|--extra-args|--priority|--all) [--json]"
|
|
36
|
+
SET_USAGE = "Usage: cdx set <name>|--sessions all|a,b|--provider PROVIDER [--power minimal|low|medium|high|xhigh] [--permission review|default|auto|full] [--fast on|off] [--rtk on|off] [--logics on|off] [--notify on|off] [--notify-preview on|off] [--scrub-env on|off] [--shared-memory on|off] [--retro on|off] [--policy use|ask-first|never] [--model MODEL] [--fallback-model MODEL[,MODEL...]] [--budget USD] [--extra-args 'ARGS'] [--priority 0..100] [--json]"
|
|
37
|
+
UNSET_USAGE = "Usage: cdx unset <name>|--sessions all|a,b|--provider PROVIDER (--power|--reasoning-effort|--permission|--fast|--rtk|--logics|--notify|--notify-preview|--scrub-env|--shared-memory|--retro|--policy|--model|--fallback-model|--budget|--extra-args|--priority|--all) [--json]"
|
|
38
38
|
SETTING_ALIAS_USAGE = "Usage: cdx power|perm|fast|model <name|all|provider:PROVIDER|a,b> <value|default> [--json]"
|
|
39
39
|
CONFIG_USAGE = "Usage: cdx config <name> [--json]"
|
|
40
40
|
CONFIGS_USAGE = "Usage: cdx configs [--json]"
|
|
41
41
|
HISTORY_USAGE = "Usage: cdx history [name] [--limit N] [--summary] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--json]"
|
|
42
|
-
STATS_USAGE = "Usage: cdx stats [name] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--json]"
|
|
42
|
+
STATS_USAGE = "Usage: cdx stats [name] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--by session|project|model] [--json]"
|
|
43
|
+
STATS_GROUPS = ("session", "project", "model")
|
|
43
44
|
LAST_USAGE = "Usage: cdx last [--json]"
|
|
44
45
|
RESUME_USAGE = "Usage: cdx resume <name> [--json]"
|
|
45
46
|
CAN_RESUME_USAGE = "Usage: cdx can-resume <name> [--json]"
|
|
46
47
|
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"
|
|
47
48
|
NEXT_USAGE = "Usage: cdx next [--json] [--refresh]"
|
|
48
|
-
RUN_USAGE = "Usage: cdx run [session] (--cwd|--dir) PATH (--prompt-file PATH|--prompt TEXT|--prompt-file -) [--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] [--detach] [--failover] [--refresh] --json"
|
|
49
|
+
RUN_USAGE = "Usage: cdx run [session] (--cwd|--dir) PATH (--prompt-file PATH|--prompt TEXT|--prompt-file -) [--provider PROVIDER] [--model MODEL] [--kind assistant|code-review|retro] [--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] [--label TEXT] [--max-parallel N] [--detach] [--failover] [--refresh] --json"
|
|
49
50
|
RUN_JSON_REQUIRED = "cdx run: --json is required."
|
|
50
51
|
RUN_TARGET_REQUIRED = "cdx run: specify a session name or --provider PROVIDER."
|
|
51
52
|
RUN_SESSION_PROVIDER_CONFLICT = "cdx run: cannot specify both a session name and --provider."
|
|
52
53
|
RUN_CWD_REQUIRED = "cdx run: --cwd PATH is required."
|
|
53
54
|
RUN_PROMPT_SOURCE_REQUIRED = "cdx run: specify exactly one prompt source: --prompt TEXT or --prompt-file PATH."
|
|
54
55
|
RUN_DETACH_FAILOVER_CONFLICT = "cdx run: --failover needs to watch the run, which --detach gives up."
|
|
55
|
-
RUN_KIND_VALUES = ("assistant", "code-review")
|
|
56
|
+
RUN_KIND_VALUES = ("assistant", "code-review", "retro")
|
|
56
57
|
# Aliases of the shared definitions in config, kept only so the existing
|
|
57
58
|
# RUN_* spellings in this module keep reading naturally. They are references,
|
|
58
59
|
# not copies: config.py is the single owner.
|
|
@@ -253,11 +254,14 @@ def _parse_set_args(args):
|
|
|
253
254
|
"--notify": {"key": "notify", "type": "str", "default": None, "transform": _on_off("notify")},
|
|
254
255
|
"--notify-preview": {"key": "notify_preview", "type": "str", "default": None, "transform": _on_off("notify preview")},
|
|
255
256
|
"--scrub-env": {"key": "scrub_env", "type": "str", "default": None, "transform": _on_off("scrub env")},
|
|
257
|
+
"--shared-memory": {"key": "shared_memory", "type": "str", "default": None, "transform": _on_off("shared memory")},
|
|
258
|
+
"--retro": {"key": "retro", "type": "str", "default": None, "transform": _on_off("retro")},
|
|
256
259
|
"--model": {"key": "model", "type": "str", "default": None},
|
|
257
260
|
"--fallback-model": {"key": "fallback_model", "type": "str", "default": None},
|
|
258
261
|
"--budget": {"key": "budget", "type": "str", "default": None, "transform": _parse_budget_value},
|
|
259
262
|
"--extra-args": {"key": "extra_args", "type": "str", "default": None},
|
|
260
263
|
"--priority": {"key": "priority", "type": "str", "default": None, "transform": _parse_priority_value},
|
|
264
|
+
"--policy": {"key": "policy", "type": "str", "default": None},
|
|
261
265
|
"--sessions": {"key": "sessions", "type": "str", "default": None, "transform": _parse_set_unset_sessions},
|
|
262
266
|
"--provider": {"key": "provider", "type": "str", "default": None, "transform": lambda value: _parse_provider_filter(value, SET_USAGE)},
|
|
263
267
|
"--json": {"key": "json", "type": "bool", "default": False},
|
|
@@ -274,7 +278,7 @@ def _parse_set_args(args):
|
|
|
274
278
|
raise CdxError(SET_USAGE)
|
|
275
279
|
settings = {
|
|
276
280
|
key: parsed[key]
|
|
277
|
-
for key in ("power", "permission", "fast", "rtk", "logics", "notify", "notify_preview", "scrub_env", "model", "fallback_model", "budget", "extra_args", "priority")
|
|
281
|
+
for key in ("power", "permission", "fast", "rtk", "logics", "notify", "notify_preview", "scrub_env", "shared_memory", "retro", "model", "fallback_model", "budget", "extra_args", "priority", "policy")
|
|
278
282
|
if parsed[key] is not None
|
|
279
283
|
}
|
|
280
284
|
if not settings:
|
|
@@ -299,11 +303,14 @@ def _parse_unset_args(args):
|
|
|
299
303
|
"--notify": {"key": "notify", "type": "bool", "default": False},
|
|
300
304
|
"--notify-preview": {"key": "notify_preview", "type": "bool", "default": False},
|
|
301
305
|
"--scrub-env": {"key": "scrub_env", "type": "bool", "default": False},
|
|
306
|
+
"--shared-memory": {"key": "shared_memory", "type": "bool", "default": False},
|
|
307
|
+
"--retro": {"key": "retro", "type": "bool", "default": False},
|
|
302
308
|
"--model": {"key": "model", "type": "bool", "default": False},
|
|
303
309
|
"--fallback-model": {"key": "fallback_model", "type": "bool", "default": False},
|
|
304
310
|
"--budget": {"key": "budget", "type": "bool", "default": False},
|
|
305
311
|
"--extra-args": {"key": "extra_args", "type": "bool", "default": False},
|
|
306
312
|
"--priority": {"key": "priority", "type": "bool", "default": False},
|
|
313
|
+
"--policy": {"key": "policy", "type": "bool", "default": False},
|
|
307
314
|
"--all": {"key": "all", "type": "bool", "default": False},
|
|
308
315
|
"--sessions": {"key": "sessions", "type": "str", "default": None, "transform": _parse_set_unset_sessions},
|
|
309
316
|
"--provider": {"key": "provider", "type": "str", "default": None, "transform": lambda value: _parse_provider_filter(value, UNSET_USAGE)},
|
|
@@ -320,8 +327,8 @@ def _parse_unset_args(args):
|
|
|
320
327
|
if not parsed["names"] and not parsed["sessions"] and not parsed["provider"]:
|
|
321
328
|
raise CdxError(UNSET_USAGE)
|
|
322
329
|
_UNSET_KEYS = (
|
|
323
|
-
"power", "reasoning_effort", "permission", "fast", "rtk", "logics", "notify", "notify_preview", "scrub_env",
|
|
324
|
-
"model", "fallback_model", "budget", "extra_args", "priority",
|
|
330
|
+
"power", "reasoning_effort", "permission", "fast", "rtk", "logics", "notify", "notify_preview", "scrub_env", "shared_memory", "retro",
|
|
331
|
+
"model", "fallback_model", "budget", "extra_args", "priority", "policy",
|
|
325
332
|
)
|
|
326
333
|
keys = list(_UNSET_KEYS) if parsed["all"] else [key for key in _UNSET_KEYS if parsed[key]]
|
|
327
334
|
if not keys:
|
|
@@ -513,6 +520,9 @@ def cdx_schema():
|
|
|
513
520
|
"provider_failed",
|
|
514
521
|
"provider_timeout",
|
|
515
522
|
"run_cancelled",
|
|
523
|
+
"too_many_runs",
|
|
524
|
+
"failover_exhausted",
|
|
525
|
+
"failover_limit_reached",
|
|
516
526
|
"invalid_request",
|
|
517
527
|
"cdx_error",
|
|
518
528
|
],
|
|
@@ -611,6 +621,17 @@ def _parse_run_effort(reasoning_effort, power):
|
|
|
611
621
|
) from error
|
|
612
622
|
|
|
613
623
|
|
|
624
|
+
def _parse_max_parallel(value):
|
|
625
|
+
try:
|
|
626
|
+
parsed = int(value)
|
|
627
|
+
except (TypeError, ValueError):
|
|
628
|
+
parsed = 0
|
|
629
|
+
if parsed < 1:
|
|
630
|
+
raise CdxArgumentError("cdx run: --max-parallel takes a whole number of at least 1.",
|
|
631
|
+
code=ARG_CODE_INVALID_VALUE, arguments=["--max-parallel"])
|
|
632
|
+
return parsed
|
|
633
|
+
|
|
634
|
+
|
|
614
635
|
def _parse_run_args(args):
|
|
615
636
|
parsed = _parse_run_flag_args(args, {
|
|
616
637
|
"--cwd": {"key": "cwd", "type": "str", "default": None},
|
|
@@ -627,6 +648,8 @@ def _parse_run_args(args):
|
|
|
627
648
|
"--detach": {"key": "detach", "type": "bool", "default": False},
|
|
628
649
|
"--failover": {"key": "failover", "type": "bool", "default": False},
|
|
629
650
|
"--refresh": {"key": "refresh", "type": "bool", "default": False},
|
|
651
|
+
"--label": {"key": "label", "type": "str", "default": None},
|
|
652
|
+
"--max-parallel": {"key": "max_parallel", "type": "str", "default": None, "transform": _parse_max_parallel},
|
|
630
653
|
"--json": {"key": "json", "type": "bool", "default": False},
|
|
631
654
|
}, RUN_USAGE, positionals_key="names", max_positionals=1)
|
|
632
655
|
if not parsed["json"]:
|
|
@@ -681,6 +704,8 @@ def _parse_run_args(args):
|
|
|
681
704
|
"timeout_seconds": parsed["timeout_seconds"],
|
|
682
705
|
"detach": parsed["detach"],
|
|
683
706
|
"failover": parsed["failover"],
|
|
707
|
+
"label": parsed["label"],
|
|
708
|
+
"max_parallel": parsed["max_parallel"],
|
|
684
709
|
"refresh": parsed["refresh"],
|
|
685
710
|
"reasoning_effort": effort.get("reasoning_effort"),
|
|
686
711
|
"power": effort.get("power"),
|
|
@@ -845,12 +870,16 @@ def _parse_stats_args(args, now=None):
|
|
|
845
870
|
"--since": {"key": "since", "type": "str", "default": None},
|
|
846
871
|
"--from": {"key": "from", "type": "str", "default": None},
|
|
847
872
|
"--to": {"key": "to", "type": "str", "default": None},
|
|
873
|
+
"--by": {"key": "by", "type": "str", "default": "session"},
|
|
848
874
|
"--json": {"key": "json", "type": "bool", "default": False},
|
|
849
875
|
}, STATS_USAGE, positionals_key="names", max_positionals=1)
|
|
876
|
+
if parsed["by"] not in STATS_GROUPS:
|
|
877
|
+
raise CdxError(STATS_USAGE)
|
|
850
878
|
period = _parse_history_period(parsed, now, STATS_USAGE)
|
|
851
879
|
return {
|
|
852
880
|
"name": parsed["names"][0] if parsed["names"] else None,
|
|
853
881
|
"period": period,
|
|
882
|
+
"by": parsed["by"],
|
|
854
883
|
"json": parsed["json"],
|
|
855
884
|
}
|
|
856
885
|
|