cdx-manager 0.12.3 → 0.14.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 +135 -6
- package/changelogs/CHANGELOGS_0_12_3.md +5 -0
- package/changelogs/CHANGELOGS_0_12_4.md +18 -0
- package/changelogs/CHANGELOGS_0_14_0.md +146 -0
- package/package.json +3 -3
- package/pyproject.toml +1 -1
- package/src/cli.py +65 -5
- package/src/cli_args.py +274 -33
- package/src/cli_commands.py +251 -3049
- package/src/cli_helpers.py +177 -2
- package/src/commands/__init__.py +6 -0
- package/src/commands/auth.py +224 -0
- package/src/commands/backup.py +112 -0
- package/src/commands/context_memory.py +341 -0
- package/src/commands/launch.py +218 -0
- package/src/commands/lifecycle.py +154 -0
- package/src/commands/maintenance.py +723 -0
- package/src/commands/runs.py +562 -0
- package/src/commands/settings.py +107 -0
- package/src/commands/status.py +681 -0
- package/src/config.py +41 -0
- package/src/errors.py +17 -0
- package/src/health.py +151 -3
- package/src/notify.py +11 -5
- package/src/provider_runtime.py +46 -11
- package/src/run_command.py +112 -4
- package/src/run_registry.py +53 -2
- package/src/session_backup.py +343 -0
- package/src/session_helpers.py +179 -0
- package/src/session_ranking.py +259 -0
- package/src/session_service.py +574 -1402
- package/src/session_status.py +534 -0
- package/src/status_view.py +15 -34
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
|
|
|
@@ -373,8 +373,8 @@ cdx history --summary --from 2026-05-01 --to 2026-05-28
|
|
|
373
373
|
| `cdx disk [profiles] [--candidates] [--json]` | Measure `CDX_HOME`; `profiles` includes per-profile breakdown, and `--candidates` reports reclaimable temporary caches and old logs with evidence |
|
|
374
374
|
| `cdx reset <name> [--yes] [--json]` | Explicitly consume one available banked Codex rate-limit reset; confirmation is required unless `--yes` is supplied |
|
|
375
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 |
|
|
376
|
-
| `cdx import <file> [--sessions a,b] [--passphrase-env VAR\|--passphrase-stdin] [--force] [--json]` | Import sessions from a bundle into the current `CDX_HOME` |
|
|
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
|
+
| `cdx import <file> [--sessions a,b] [--passphrase-env VAR\|--passphrase-stdin] [--force\|--merge] [--allow-authless-force] [--json]` | Import sessions from a bundle into the current `CDX_HOME` |
|
|
377
|
+
| `cdx doctor [--severity OK|WARN|FAIL[,OK|WARN|FAIL...]] [--check-provider-flags] [--json]` | Inspect CLI dependencies, provider CLI versions/capability hints, CDX_HOME permissions, missing state, orphan profiles, and pending quarantines; optionally filter issue severities. `--check-provider-flags` additionally verifies that the CLI flags cdx maps for each permission are actually accepted by each provider's installed CLI |
|
|
378
378
|
| `cdx repair [--dry-run] [--force] [--json]` | Plan or apply safe repairs for missing state files, quarantines, and orphan profiles |
|
|
379
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 |
|
|
380
380
|
| `cdx update [--check] [--yes] [--json] [--version TAG]` | Update cdx-manager using the installer that matches how it was installed |
|
|
@@ -384,7 +384,12 @@ cdx history --summary --from 2026-05-01 --to 2026-05-28
|
|
|
384
384
|
| `cdx notify --next-ready [--poll seconds] [--once] [--schedule] [--refresh] [--json]` | Wait until the recommended session is usable, or schedule the next known reset notification |
|
|
385
385
|
| `cdx next [--json] [--refresh]` | Select the best next assistant using the same priority logic as `cdx status` |
|
|
386
386
|
| `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 |
|
|
387
|
-
| `cdx run [session] --cwd PATH (--prompt-file PATH\|--prompt TEXT) [--provider PROVIDER] [--model MODEL] [--reasoning-effort minimal\|low\|medium\|high\|xhigh] [--power minimal\|low\|medium\|high\|xhigh] [--permission MODE] [--timeout-seconds N] --json` | Run one headless task and return a stable JSON result |
|
|
387
|
+
| `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] [--refresh] --json` | Run one headless task and return a stable JSON result; `--detach` returns the `run_id` at launch without waiting, `--prompt-file -` reads the prompt from stdin |
|
|
388
|
+
| `cdx run-report <run_id> --json` | Full report, transcript metadata, and final payload for a run |
|
|
389
|
+
| `cdx run-status <run_id> --json` | Status of one run by id |
|
|
390
|
+
| `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 |
|
|
391
|
+
| `cdx runs [--limit N] [--since 7d\|today\|DATE] --json` | List recent runs; `--since` returns every run completed after the cursor and ignores `--limit` |
|
|
392
|
+
| `cdx schema --json` | Publish the enums, mutually-exclusive argument groups, and error codes programmatic callers should validate against |
|
|
388
393
|
| `cdx stats [name] [--since 7d\|today\|DATE] [--from DATE] [--to DATE] [--json]` | Aggregate launch counts, duration, and known headless token usage by session |
|
|
389
394
|
| `cdx status [--json] [--refresh\|--cached] [--timeout SECONDS]` | Show token usage table for all sessions; `--cached` skips live provider probes and returns only stored status |
|
|
390
395
|
| `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 |
|
|
@@ -506,6 +511,130 @@ cdx stats work
|
|
|
506
511
|
cdx select --provider codex --min-reasoning-effort low --require-ready --json
|
|
507
512
|
```
|
|
508
513
|
|
|
514
|
+
### How cdx picks a session
|
|
515
|
+
|
|
516
|
+
`cdx select`, `cdx run --provider`, `cdx next`, the `cdx status` recommendation, and `cdx ready` all use one ranking. They used to use two that disagreed, so the answer depended on which command you asked.
|
|
517
|
+
|
|
518
|
+
**Candidate filters** exclude sessions before any ordering happens: disabled sessions, logged-out sessions (they cannot serve work), sessions of another provider when one is requested, sessions below a requested `--min-reasoning-effort`, and — when readiness is required — sessions with no availability left.
|
|
519
|
+
|
|
520
|
+
**Ordering** compares the remaining candidates factor by factor:
|
|
521
|
+
|
|
522
|
+
1. **Usability** — usable now, then blocked with a known upcoming reset, then reset known, then nothing known.
|
|
523
|
+
2. **Priority** — the `--priority` you set with `cdx set <name> --priority 0..100`, highest first.
|
|
524
|
+
3. Then, for a session you can use now: **credits** (sessions *without* credits first, so included quota is spent before paid credits), **availability**, **reset time**. For a session you cannot use now the order is **reset time** first, then credits and availability — when you cannot use it, what matters is when it comes back.
|
|
525
|
+
4. **Reasoning effort** — the lowest configured effort that still clears any requested floor, leaving stronger sessions free for work that needs them.
|
|
526
|
+
5. **Session name**, so the order is deterministic.
|
|
527
|
+
|
|
528
|
+
`--priority` ranks sessions *within* a usability class, never across one. A high-priority session with no quota left does not outrank a usable one: priority expresses which usable session to prefer, not a way to route work somewhere it will fail.
|
|
529
|
+
|
|
530
|
+
`cdx select --json` reports `selection_policy` (built from the ranking itself, so it cannot describe an order the code does not apply) and `deciding_factor`/`reason`, naming the factor that actually separated the winner from the runner-up for that call — or reporting that it was the only candidate. Do not treat `selection_policy` as a stable identifier; it changes when the ranking changes, which is the point.
|
|
531
|
+
|
|
532
|
+
`cdx run --provider` selects from cached status by default. Pass `--refresh` to fetch status first. When a session is auto-selected without any recorded availability, the run payload carries a `session_selected_without_status` warning — that is different from a session known to be low, and on a freshly imported or long-idle set of sessions it is the ordinary case.
|
|
533
|
+
|
|
534
|
+
### Verifying provider flag mappings
|
|
535
|
+
|
|
536
|
+
cdx translates each `--permission` value into concrete provider CLI flags. Those mappings are declarations about someone else's CLI, and a provider can drop or rename a flag without cdx noticing — which is exactly what happened with an `--experimental-yolo` flag mapped for ollama that the ollama CLI never had.
|
|
537
|
+
|
|
538
|
+
`cdx doctor --check-provider-flags` verifies them against the installed provider CLIs:
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
cdx doctor --check-provider-flags --json
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
For each configured provider it reports, per permission, whether every mapped flag is accepted. A rejected flag is a `FAIL` naming the provider, the permission, and the flag. A provider whose CLI is not installed, or whose help cannot be read, is a `WARN` — never an `OK`: "could not verify" is precisely the state a stale mapping hides in. A provider that maps no flags at all, such as ollama, reports `OK` with an empty mapping, so having nothing to check is distinguishable from having failed to check.
|
|
545
|
+
|
|
546
|
+
It is opt-in because it costs one provider CLI invocation per configured provider. When it is not requested, the default `cdx doctor` report says so with a `provider_permission_flags_unchecked` warning rather than omitting it, so a green report never implies the mappings were verified.
|
|
547
|
+
|
|
548
|
+
### Programmatic Callers
|
|
549
|
+
|
|
550
|
+
`cdx run` blocks until the provider finishes, which suits a supervisor that can wait. Agents, MCP servers, and watchdogs usually cannot, so the surface below exists specifically for them: launch without waiting, watch progress, react to failures by code, and discover valid values instead of hard-coding them.
|
|
551
|
+
|
|
552
|
+
**Launch without waiting.** `--detach` returns as soon as the run is registered, with the `run_id` already assigned:
|
|
553
|
+
|
|
554
|
+
```bash
|
|
555
|
+
cdx run work --cwd /path/to/repo --prompt-file task.md --detach --json
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
The payload carries `detached: true`, `run_id`, `pid`, and the artifact paths — but no `exit_code`, `duration_seconds`, or `usage`, since the run has not finished. The detached process is put in its own session, so it survives the launcher exiting, including when cdx was invoked over an SSH command that returns immediately. It transitions its own registry entry to a terminal status, so `cdx runs` and `cdx run-status` stay accurate with nobody supervising.
|
|
559
|
+
|
|
560
|
+
Authentication is still resolved before the launch returns, so a login problem surfaces synchronously rather than silently in the background.
|
|
561
|
+
|
|
562
|
+
**Watch a run in progress.** `cdx run-status` reports only running/succeeded/failed; `run-tail` shows what the run is actually doing:
|
|
563
|
+
|
|
564
|
+
```bash
|
|
565
|
+
cdx run-tail <run_id> --lines 50 --json
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
It returns the last lines of the run's own `stdout_path` plus its current status, whether the run is still going or already finished. Lines are raw provider output, not summarized. Undecodable bytes come back with replacement characters rather than failing the call.
|
|
569
|
+
|
|
570
|
+
**Pipe an untrusted prompt.** `--prompt-file -` reads the prompt from standard input, so arbitrary text never has to reach a command line or a temporary file the caller must clean up:
|
|
571
|
+
|
|
572
|
+
```bash
|
|
573
|
+
echo "$UNTRUSTED_PROMPT" | cdx run work --cwd /path/to/repo --prompt-file - --json
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
It fails immediately if standard input is a terminal, rather than blocking at a silent prompt.
|
|
577
|
+
|
|
578
|
+
**Poll for completions with a cursor.** `cdx runs --since` returns every run that completed after the cursor, using the same cursor forms as `cdx history --since`:
|
|
579
|
+
|
|
580
|
+
```bash
|
|
581
|
+
cdx runs --since 15m --json
|
|
582
|
+
cdx runs --since 2026-08-07T09:00:00Z --json
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
A cursor is bounded by time, not by row count: `--limit` is ignored when `--since` is given (and says so in `warnings`). That is deliberate — capping a cursor query by row count would silently drop completions a caller had not yet seen, which is exactly the miss the cursor removes. Runs still in flight are not returned; they appear once they complete. Callers do not need to keep their own set of already-reported run ids.
|
|
586
|
+
|
|
587
|
+
**React to failures by code, not by message.** Argument failures report a specific `error.code` and name the offending arguments as data:
|
|
588
|
+
|
|
589
|
+
| `error.code` | Raised when |
|
|
590
|
+
| --- | --- |
|
|
591
|
+
| `missing_required_argument` | A required argument is absent |
|
|
592
|
+
| `mutually_exclusive_arguments` | Two arguments that cannot be combined were both given |
|
|
593
|
+
| `invalid_argument_value` | A constrained option got a value outside its accepted set |
|
|
594
|
+
| `argument_value_out_of_range` | A numeric argument fell outside its accepted range |
|
|
595
|
+
| `unknown_argument` | An unrecognized flag |
|
|
596
|
+
|
|
597
|
+
```json
|
|
598
|
+
{
|
|
599
|
+
"ok": false,
|
|
600
|
+
"error": {
|
|
601
|
+
"code": "mutually_exclusive_arguments",
|
|
602
|
+
"message": "cdx run: cannot specify both a session name and --provider.",
|
|
603
|
+
"arguments": ["session", "--provider"],
|
|
604
|
+
"allowed_values": null
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
Never branch on `error.message`; it is written for a human reading a terminal and may be reworded.
|
|
610
|
+
|
|
611
|
+
**Discover valid values.** `cdx schema --json` publishes the enums and argument constraints the parser itself validates against:
|
|
612
|
+
|
|
613
|
+
```bash
|
|
614
|
+
cdx schema --json
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
It returns accepted values for `permission` (with its aliases and canonical forms), `reasoning_effort`/`power`, `kind`, and `provider`, plus the declared mutually-exclusive argument groups and the error codes above. Validate against this rather than copying the lists into your own code — a hand-maintained copy drifts, and cdx will not tell you when it does.
|
|
618
|
+
|
|
619
|
+
**Surface run warnings.** The run payload's `warnings` list reports degradations a zero exit code would otherwise hide, on the successful path as much as the failing one:
|
|
620
|
+
|
|
621
|
+
```json
|
|
622
|
+
{
|
|
623
|
+
"ok": true,
|
|
624
|
+
"exit_code": 0,
|
|
625
|
+
"warnings": [
|
|
626
|
+
{
|
|
627
|
+
"code": "network_disabled_by_permission",
|
|
628
|
+
"message": "codex runs at permission 'review' inside a sandbox with no network access, ...",
|
|
629
|
+
"provider": "codex",
|
|
630
|
+
"permission": "review"
|
|
631
|
+
}
|
|
632
|
+
]
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
Codex ties network access to its sandbox, so any permission below `full` leaves the run unable to resolve DNS — `gh`, `curl`, and package installs fail inside an otherwise successful run. Show these warnings to whoever reads your output rather than discarding them; a run that succeeds while quietly doing less is the case they exist for.
|
|
637
|
+
|
|
509
638
|
---
|
|
510
639
|
|
|
511
640
|
## Backup And Restore
|
|
@@ -527,10 +656,10 @@ cdx import backup-auth.cdx --passphrase-env CDX_BUNDLE_PASSPHRASE
|
|
|
527
656
|
|
|
528
657
|
Notes:
|
|
529
658
|
|
|
530
|
-
- `--include-auth` is encrypted, requires a passphrase, and exports only provider credential files rather than full profile caches or
|
|
659
|
+
- `--include-auth` is encrypted, requires a passphrase, and exports only provider credential files rather than full profile caches, logs, or installed plugins.
|
|
531
660
|
- 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.
|
|
532
661
|
- `--sessions work,perso` exports or imports only a subset.
|
|
533
|
-
- `--force` allows overwriting existing destination sessions during import or replacing an existing bundle file during export. Import refuses to overwrite existing sessions from a bundle without auth payloads unless `--allow-authless-force` is also passed.
|
|
662
|
+
- `--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.
|
|
534
663
|
- Auth bundles contain credentials. Treat them like secrets and delete them after transfer.
|
|
535
664
|
|
|
536
665
|
---
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
## Highlights
|
|
4
4
|
|
|
5
5
|
- GitHub Releases now trigger an automatic checksum asset workflow so `release-archives.json` is regenerated and uploaded for the published tag.
|
|
6
|
+
- `cdx import --force` now preserves detected local Codex plugin state while refreshing sessions from auth-bearing bundles.
|
|
6
7
|
- `cdx run` validation errors now explain what is wrong and how to list or create matching sessions.
|
|
7
8
|
- Provider capacity or runtime failures now keep failed run records queryable instead of dropping them from the run registry.
|
|
8
9
|
|
|
@@ -12,6 +13,10 @@
|
|
|
12
13
|
|
|
13
14
|
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
|
|
|
16
|
+
### Force import plugin preservation
|
|
17
|
+
|
|
18
|
+
Auth-bearing force imports now keep local `plugins/` state when the incoming bundle does not include plugin files, so refreshing credentials does not silently remove locally installed Codex plugins.
|
|
19
|
+
|
|
15
20
|
### More actionable run validation
|
|
16
21
|
|
|
17
22
|
`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.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# CDX Manager 0.12.4
|
|
2
|
+
|
|
3
|
+
## Highlights
|
|
4
|
+
|
|
5
|
+
- `cdx import --force` now preserves local Codex plugins when refreshing from bundles that do not include plugin files.
|
|
6
|
+
|
|
7
|
+
## Changes
|
|
8
|
+
|
|
9
|
+
### Force import plugin preservation
|
|
10
|
+
|
|
11
|
+
Force imports of auth-bearing bundles keep detected local `plugins/` state instead of deleting it when the incoming bundle omits plugin files. This keeps locally installed Codex plugins available after credential refresh imports.
|
|
12
|
+
|
|
13
|
+
## Validation
|
|
14
|
+
|
|
15
|
+
- `npm run lint`
|
|
16
|
+
- `npm test`
|
|
17
|
+
- `logics-manager lint --require-status`
|
|
18
|
+
- `logics-manager audit`
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# CDX Manager 0.14.0
|
|
2
|
+
|
|
3
|
+
> 0.13.0 was prepared but never published. Everything it contained ships here,
|
|
4
|
+
> so this release covers two versions' worth of work: the programmatic CLI
|
|
5
|
+
> surface for non-interactive callers, and the unification of session selection
|
|
6
|
+
> and accepted values.
|
|
7
|
+
|
|
8
|
+
## Highlights
|
|
9
|
+
|
|
10
|
+
- `cdx run --detach` launches a run without waiting and returns its `run_id` immediately; `cdx run-tail` shows a run's live output.
|
|
11
|
+
- `cdx schema --json` publishes the enums, constraints, and error codes callers should validate against, instead of forcing them to copy lists that drift.
|
|
12
|
+
- Every command that picks a session now uses one ranking, and `--priority` finally counts in all of them.
|
|
13
|
+
- `cdx set --permission workspace-write` works, matching what `cdx run --permission` always accepted.
|
|
14
|
+
- New `cdx doctor --check-provider-flags` verifies that the CLI flags cdx maps for each permission actually exist in the provider CLIs.
|
|
15
|
+
|
|
16
|
+
## Changes
|
|
17
|
+
|
|
18
|
+
### Detached runs
|
|
19
|
+
|
|
20
|
+
`cdx run --detach --json` registers the run, starts the provider detached from the invoking process, and returns straight away with `detached: true`, the assigned `run_id`, `pid`, and the artifact paths. The child is placed in its own session so it outlives the launcher, including when cdx was invoked over an SSH command that returns immediately, and it transitions its own registry entry to a terminal status so `cdx runs` and `cdx run-status` stay accurate with nobody supervising. Authentication is still resolved before the launch returns, so a login problem surfaces synchronously rather than in the background.
|
|
21
|
+
|
|
22
|
+
Callers no longer have to poll `cdx runs` and guess which row belongs to the run they just started.
|
|
23
|
+
|
|
24
|
+
### Live run output
|
|
25
|
+
|
|
26
|
+
`cdx run-tail <run_id> [--lines N] --json` returns the last lines of a run's recorded `stdout_path` plus its current status, while the run is still going or after it finished. Undecodable provider bytes are returned with replacement characters rather than failing the call. A run with no recorded output path reports `run_output_unavailable`, distinct from an unreadable one.
|
|
27
|
+
|
|
28
|
+
### Specific argument error codes
|
|
29
|
+
|
|
30
|
+
Argument and usage failures previously collapsed into `invalid_request` carrying the full human usage line, so a missing `--cwd` was indistinguishable from passing a session name and `--provider` together. They now report `missing_required_argument`, `mutually_exclusive_arguments`, `invalid_argument_value`, `argument_value_out_of_range`, or `unknown_argument`, and the error object gained `arguments` and `allowed_values` fields naming the offending arguments as data. The human-readable message and the exit codes are unchanged.
|
|
31
|
+
|
|
32
|
+
`invalid_reasoning_effort` is kept for an unsupported `--reasoning-effort`/`--power` value; supplying two conflicting values now reports `mutually_exclusive_arguments`, since the two flags are aliases of one setting.
|
|
33
|
+
|
|
34
|
+
### Schema discovery
|
|
35
|
+
|
|
36
|
+
`cdx schema --json` publishes the accepted values for `permission` (with aliases and canonical forms), `reasoning_effort`/`power`, `kind`, and `provider`, the declared mutually-exclusive argument groups, and the argument error codes. Values are read from the same definitions the parser validates against, and a test fails if the two disagree, so downstream callers can stop maintaining copies that drift.
|
|
37
|
+
|
|
38
|
+
### Prompts on standard input
|
|
39
|
+
|
|
40
|
+
`cdx run --prompt-file -` reads the prompt from standard input, so a caller relaying arbitrary text no longer has to stage and clean up a temporary file or place the text on a command line. It fails immediately with `invalid_argument_value` if standard input is a terminal.
|
|
41
|
+
|
|
42
|
+
### Completion cursor for runs
|
|
43
|
+
|
|
44
|
+
`cdx runs --since <cursor> --json` returns every run that completed after the cursor, accepting the same cursor forms as `cdx history --since`. The cursor bounds the result by time rather than row count: `--limit` is ignored when `--since` is given, and the payload says so in `warnings`. Capping a cursor query by row count would silently drop completions a polling caller had not yet seen. Runs still in flight are excluded until they complete.
|
|
45
|
+
|
|
46
|
+
### Run warnings
|
|
47
|
+
|
|
48
|
+
The run payload's `warnings` list was declared in the contract but hardcoded empty. It is now populated, on the successful path as much as the failing one. The first warning, `network_disabled_by_permission`, fires when a Codex-backed run uses a permission below `full`: Codex ties network access to its sandbox, so the run cannot resolve DNS and network-dependent tooling fails inside a run that still exits zero. The condition is read from the same permission-to-flag mapping the launch spec uses, so adding a permission cannot leave it behind.
|
|
49
|
+
|
|
50
|
+
### One session selection rule
|
|
51
|
+
|
|
52
|
+
`cdx select`, `cdx run --provider`, `cdx next`, the `cdx status` recommendation, and `cdx ready` used two independent rankings that disagreed. Each knew something the other did not: the headless one filtered by provider and honored `--priority`, the recommendation one understood credits and reset scheduling. Which session you got depended on which command you asked.
|
|
53
|
+
|
|
54
|
+
They are now one function in `src/session_ranking.py`, parameterized by what each caller needs. Candidate filters (disabled, logged out, provider, effort floor, readiness) are kept separate from ordering, because describing a filter as a sort stage is part of what made the old published policy wrong.
|
|
55
|
+
|
|
56
|
+
The merged ordering is: usability class, then `--priority`, then — for a usable session — credits, availability and reset; for a session that is not usable now, reset first, since when it comes back is what matters. Reasoning effort and session name break the remaining ties.
|
|
57
|
+
|
|
58
|
+
### The priority setting counts everywhere
|
|
59
|
+
|
|
60
|
+
`cdx set <name> --priority 90` previously affected only `cdx select` and `cdx run --provider`. Status rows now carry the configured priority and effort, so `cdx next`, `cdx status`, and `cdx ready` honor it too.
|
|
61
|
+
|
|
62
|
+
Priority ranks sessions *within* a usability class, never across one: a high-priority session with no quota left does not outrank a usable one. Priority says which usable session to prefer, not where to send work that will fail there.
|
|
63
|
+
|
|
64
|
+
The `cdx status` recommendation line is now labelled `Recommended:` rather than `Priority:`. The old label named the whole recommendation after one of its inputs, so tuning `--priority` and seeing no change read as the setting being broken when availability or resets had decided instead.
|
|
65
|
+
|
|
66
|
+
### Truthful selection reporting
|
|
67
|
+
|
|
68
|
+
`cdx select --json` built its `selection_policy` from a hardcoded string that omitted the reasoning-effort tie-break and described the readiness filter as a sort stage, and a `reason` that claimed availability decided even when priority or the alphabetical fallback did.
|
|
69
|
+
|
|
70
|
+
Both are now derived. `selection_policy` is built from the ranking itself and reports factors and filters separately; `deciding_factor` and `reason` name the factor that actually separated the winner from the runner-up for that call, or report that it was the only candidate. Do not treat `selection_policy` as a stable identifier — it changes when the ranking does, which is the point.
|
|
71
|
+
|
|
72
|
+
### Selecting on fresh or missing status
|
|
73
|
+
|
|
74
|
+
`cdx run --provider` now accepts `--refresh` to fetch status before auto-selecting. Cached status remains the default, so ordinary runs do not each pay for a provider probe.
|
|
75
|
+
|
|
76
|
+
When auto-selection picks a session with no recorded availability, the run payload carries a `session_selected_without_status` warning. That is distinct from a session known to be low: on a freshly imported or long-idle set of sessions, choosing with no data at all is the ordinary case, and it was previously invisible.
|
|
77
|
+
|
|
78
|
+
### One definition of the values cdx accepts
|
|
79
|
+
|
|
80
|
+
The reasoning-effort enum was defined in four places (two of them identical, one line apart in the same module) and the permission enum in two, with `cdx set` validating against one pair and `cdx run` against another. `config.py` now owns both, and every validator, normalizer, and `cdx schema --json` derives from them. One test asserts identity rather than equality; another fails if any module restates one of these sets.
|
|
81
|
+
|
|
82
|
+
### Permission aliases work in cdx set
|
|
83
|
+
|
|
84
|
+
`cdx run --permission workspace-write` was accepted and normalized to `default`, while `cdx set <name> --permission workspace-write` failed with `Unsupported permission`. The same applied to `read-only` and `danger-full-access`, which are the provider-native spellings a user is most likely to have in hand, and `cdx schema --json` published the aliases without saying they applied to one command only.
|
|
85
|
+
|
|
86
|
+
`cdx set` and the `perm` alias command now accept them, storing the canonical value so nothing downstream has to learn aliases exist. They were added to `set` rather than removed from `run`, since removing them would break callers passing provider-native spellings today.
|
|
87
|
+
|
|
88
|
+
### Verifying provider flag mappings
|
|
89
|
+
|
|
90
|
+
cdx maps each permission to concrete provider CLI flags, and nothing verified those mappings. GitHub issue #8 was exactly that: an `--experimental-yolo` flag mapped for ollama that the ollama CLI never had, undetected until a user hit it.
|
|
91
|
+
|
|
92
|
+
`cdx doctor --check-provider-flags` checks each configured provider's installed CLI for the flags cdx maps. A rejected flag is a `FAIL` naming the provider, the permission, and the flag. An uninstalled CLI or unreadable help is a `WARN`, never an `OK` — "could not verify" is the state that bug lived in. A provider that maps nothing, such as ollama, reports `OK` with an empty mapping, so having nothing to check is distinguishable from having failed to check.
|
|
93
|
+
|
|
94
|
+
It is opt-in because it costs a provider CLI invocation per provider, and the default report carries a `provider_permission_flags_unchecked` warning rather than omitting it, so a green doctor never implies the mappings were verified.
|
|
95
|
+
|
|
96
|
+
Antigravity and ollama permission mappings also moved out of inline branches into `LAUNCH_PERMISSION_ARGS`, so every provider's mapping is declared in one table the check can read. Ollama's entry is deliberately empty.
|
|
97
|
+
|
|
98
|
+
## Internal structure
|
|
99
|
+
|
|
100
|
+
No behavior change, listed because the source layout is different if you have a patch in flight.
|
|
101
|
+
|
|
102
|
+
`cli_commands.py` (3313 lines) became nine per-domain modules under `src/commands/`, and `session_service.py` (1690 lines) became `session_service`, `session_helpers`, `session_status`, and `session_backup`. `create_session_service` went from a 1153-line factory holding 46 closures to 56 lines of assembly, with dependencies passed explicitly rather than captured.
|
|
103
|
+
|
|
104
|
+
Both original modules remain the import location for everything they exposed, so no caller changed. The split followed a coupling measurement in each case, and stopped where the measurement stopped: the session service's runtime and auth groups have no helpers of their own, so they were left in place rather than turned into files that would only re-import from elsewhere. That reasoning is recorded in `session_service`'s docstring, with a test that fails if someone finishes the job by instinct.
|
|
105
|
+
|
|
106
|
+
## Behavior changes
|
|
107
|
+
|
|
108
|
+
- **`--priority` now outranks availability.** It previously sat below availability in the headless sort, so it only broke exact ties and was effectively inert. A session with lower availability but higher priority now wins within the same usability class. This is the change that makes the setting mean anything.
|
|
109
|
+
- **Ties on session name now sort ascending everywhere.** The recommendation ranking sorted names descending as a side effect of reversing its whole sort tuple, disagreeing with the headless ranking. The ascending order is kept.
|
|
110
|
+
- **Logged-out sessions are never selection candidates.** `cdx select` without `--require-ready` could previously return one. `--require-ready` remains stricter still: it requires a confirmed `authenticated` state, so a session whose auth has never been checked is not offered to `cdx run --provider`.
|
|
111
|
+
- `cdx status`'s recommendation line is labelled `Recommended:` instead of `Priority:`.
|
|
112
|
+
- `cdx select --json`'s `selection_policy` is now a structured object rather than an underscore-joined string.
|
|
113
|
+
|
|
114
|
+
## Fixes
|
|
115
|
+
|
|
116
|
+
- `cdx --version` reported a stale release. `src/cli.py` restated the version as a fourth hardcoded copy alongside `VERSION`, `package.json`, and `pyproject.toml`, and nothing kept the four in step — it had been left at 0.12.4. The version is now resolved from the `VERSION` file in a checkout and from installed package metadata otherwise, and a test fails if any of the four (plus the README badge) disagree.
|
|
117
|
+
- The release validator was left scraping a `VERSION = "..."` literal out of `src/cli.py`, which the fix above had removed. It resolved an empty string and rejected every release with "Missing release version in: src/cli.py" — inside the publish workflow, after the tag was already public. It now reads the resolved version, and a test fails if any declaration site stops yielding one.
|
|
118
|
+
- Two tests still asserted the `--experimental-yolo` flag removed from ollama launches in 0.12.4 (GitHub issue #8); the launch behavior was already correct, the stale expectations are now aligned.
|
|
119
|
+
- `format_json_error` honors an error's declared code instead of always re-deriving one from the message prefix, so structured argument errors keep their code when they reach the CLI entry point.
|
|
120
|
+
- The detached-run test asserted the POSIX `start_new_session` unconditionally, so the Windows CI job failed on a code path that was correct. The assertion is now platform-aware, and Windows CI exercises the `creationflags` branch for the first time.
|
|
121
|
+
|
|
122
|
+
## Review fixes
|
|
123
|
+
|
|
124
|
+
Found by a review pass over the programmatic CLI work, before release:
|
|
125
|
+
|
|
126
|
+
- The detached child's `CDX_RUN_ID` is consumed rather than read, so it is no longer inherited by the provider process. Left set, an agent making its own nested `cdx run` would have claimed the outer run's id, deleting its registry record and truncating its stdout, stderr, and transcript files.
|
|
127
|
+
- The detached child receives the resolved absolute `--cwd`; a relative path would have resolved against the child's own working directory.
|
|
128
|
+
- `--detach` now detaches correctly on Windows (`DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP`), and always re-invokes through `sys.executable -m` rather than `sys.argv[0]`, which is not reliably executable there.
|
|
129
|
+
- `network_disabled_by_permission` now fires when no `--permission` is given at all — the most common invocation, where `codex exec` still applies its own sandboxed default. It previously exempted exactly the case it was written for.
|
|
130
|
+
- `cdx run-tail` on a run that has not written its first byte returns `lines: []` instead of failing, so the launch-then-tail flow does not report a fatal error during that window. A missing output file on a *finished* run is still an error.
|
|
131
|
+
- `run-tail` drops a partial leading line when the read window opens mid-line, rather than returning a fragment as if it were a whole line.
|
|
132
|
+
- `cdx schema --json` publishes every error code the run commands emit, grouped by area, plus the warning codes; and `--reasoning-effort`/`--power` moved from `mutually_exclusive` to a new `must_match` group, since cdx accepts both when they agree.
|
|
133
|
+
- An empty `--power ""` blames `--power` instead of `--reasoning-effort`.
|
|
134
|
+
- `format_json_error` emits the same error field set as the run payload, so callers do not need two shapes.
|
|
135
|
+
- The prompt a detached launch stages for its child is deleted once the child reads it, instead of leaving a permanent cleartext copy in the session log directory.
|
|
136
|
+
|
|
137
|
+
## Validation
|
|
138
|
+
|
|
139
|
+
- `npm run lint`
|
|
140
|
+
- `npm test` — 565 tests.
|
|
141
|
+
- CI green on ubuntu-latest and windows-latest.
|
|
142
|
+
- End-to-end: detached launch returning `run_id`, `run-tail` against the in-flight run after the launcher exited, terminal status reached without a supervisor, and `runs --since` reporting the completion.
|
|
143
|
+
- End-to-end: with one session at 80% availability and priority 0 and another at 40% and priority 90, `cdx select`, `cdx next`, and `cdx status` all name the second, and `cdx select` reports `decided on priority`.
|
|
144
|
+
- The issue #8 condition reproduced against the new provider flag check: mapping `--experimental-yolo` for codex fails the check by name. All four provider CLIs verified against their real binaries.
|
|
145
|
+
- `logics-manager lint --require-status`
|
|
146
|
+
- `logics-manager audit --group-by-doc` — 0 blocking, 0 warnings.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cdx-manager",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Terminal session manager for Codex and Claude accounts.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alexandre Agostini",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"checksums",
|
|
31
31
|
"changelogs",
|
|
32
32
|
"src",
|
|
33
|
-
"!src
|
|
33
|
+
"!src/**/__pycache__",
|
|
34
34
|
"!src/**/*.pyc",
|
|
35
35
|
"install.sh",
|
|
36
36
|
"install.ps1",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"test": "npm run test:py",
|
|
47
47
|
"test:py": "node bin/python-runner.js -m pytest",
|
|
48
48
|
"test:coverage": "node bin/python-runner.js -m pytest --cov=src --cov-report=term-missing",
|
|
49
|
-
"lint": "node --check bin/cdx.js && node --check bin/python-runner.js && node bin/python-runner.js scripts/verify_project_docs.py && node bin/python-runner.js -m ruff check . && node bin/python-runner.js -m py_compile bin/cdx src/*.py scripts/*.py test
|
|
49
|
+
"lint": "node --check bin/cdx.js && node --check bin/python-runner.js && node bin/python-runner.js scripts/verify_project_docs.py && node bin/python-runner.js -m ruff check . && node bin/python-runner.js -m py_compile bin/cdx src/*.py src/commands/*.py scripts/*.py test/*.py",
|
|
50
50
|
"release:validate": "node bin/python-runner.js scripts/verify_release_checksums.py",
|
|
51
51
|
"prepublishOnly": "npm run release:validate && npm run lint && npm test",
|
|
52
52
|
"link": "npm link",
|
package/pyproject.toml
CHANGED
package/src/cli.py
CHANGED
|
@@ -43,7 +43,9 @@ from .cli_commands import (
|
|
|
43
43
|
handle_run,
|
|
44
44
|
handle_run_report,
|
|
45
45
|
handle_run_status,
|
|
46
|
+
handle_run_tail,
|
|
46
47
|
handle_runs,
|
|
48
|
+
handle_schema,
|
|
47
49
|
handle_select,
|
|
48
50
|
handle_set,
|
|
49
51
|
handle_stats,
|
|
@@ -78,7 +80,38 @@ from .status_view import (
|
|
|
78
80
|
)
|
|
79
81
|
from .update_check import check_for_update, check_logics_manager_for_update
|
|
80
82
|
|
|
81
|
-
|
|
83
|
+
|
|
84
|
+
def _resolve_version():
|
|
85
|
+
"""The version, resolved rather than restated.
|
|
86
|
+
|
|
87
|
+
This was a hardcoded string, a fourth copy alongside VERSION, package.json,
|
|
88
|
+
and pyproject.toml, with nothing keeping the four in step — so
|
|
89
|
+
`cdx --version` could report a release it was not.
|
|
90
|
+
|
|
91
|
+
The VERSION file next to the package wins when it exists, because that only
|
|
92
|
+
happens in a checkout, where it is the thing being edited and the installed
|
|
93
|
+
metadata is whatever was last `pip install`ed. Installed packages have no
|
|
94
|
+
such file and fall through to their own metadata.
|
|
95
|
+
"""
|
|
96
|
+
version_file = os.path.join(
|
|
97
|
+
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "VERSION"
|
|
98
|
+
)
|
|
99
|
+
try:
|
|
100
|
+
with open(version_file, encoding="utf-8") as handle:
|
|
101
|
+
text = handle.read().strip()
|
|
102
|
+
if text:
|
|
103
|
+
return text
|
|
104
|
+
except OSError:
|
|
105
|
+
pass
|
|
106
|
+
try:
|
|
107
|
+
from importlib.metadata import version
|
|
108
|
+
|
|
109
|
+
return version("cdx-manager")
|
|
110
|
+
except Exception: # noqa: BLE001 - any resolution failure falls back below
|
|
111
|
+
return "0.0.0"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
VERSION = _resolve_version()
|
|
82
115
|
|
|
83
116
|
# Public surface: this module is a facade. Names below are imported above
|
|
84
117
|
# purely to be re-exported (consumed by tests and external callers); listing
|
|
@@ -130,7 +163,9 @@ _COMMAND_HANDLERS = {
|
|
|
130
163
|
"run": handle_run,
|
|
131
164
|
"run-report": handle_run_report,
|
|
132
165
|
"run-status": handle_run_status,
|
|
166
|
+
"run-tail": handle_run_tail,
|
|
133
167
|
"runs": handle_runs,
|
|
168
|
+
"schema": handle_schema,
|
|
134
169
|
"select": handle_select,
|
|
135
170
|
"set": handle_set,
|
|
136
171
|
"stats": handle_stats,
|
|
@@ -183,10 +218,12 @@ def _print_help(use_color=False):
|
|
|
183
218
|
f" {_style('cdx next [--json] [--refresh]', '36', use_color)}",
|
|
184
219
|
f" {_style('cdx reset <name> [--yes] [--json]', '36', use_color)}",
|
|
185
220
|
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)}",
|
|
186
|
-
f" {_style('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', '36', use_color)}",
|
|
187
|
-
f" {_style('cdx runs [--limit N] --json', '36', use_color)}",
|
|
221
|
+
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)}",
|
|
222
|
+
f" {_style('cdx runs [--limit N] [--since 7d|today|DATE] --json', '36', use_color)}",
|
|
188
223
|
f" {_style('cdx run-status <run_id> --json', '36', use_color)}",
|
|
189
224
|
f" {_style('cdx run-report <run_id> --json', '36', use_color)}",
|
|
225
|
+
f" {_style('cdx run-tail <run_id> [--lines N] --json', '36', use_color)}",
|
|
226
|
+
f" {_style('cdx schema --json', '36', use_color)}",
|
|
190
227
|
f" {_style('cdx context show|path|init|edit|clear|set|append [text...] [--json]', '36', use_color)}",
|
|
191
228
|
f" {_style('cdx memory [--global|--project NAME_OR_PATH] [show|view|path|init|edit|clear|set|append|list] [text...] [--json]', '36', use_color)}",
|
|
192
229
|
f" {_style('cdx disk [profiles] [--candidates] [--json]', '36', use_color)}",
|
|
@@ -215,8 +252,8 @@ def _print_help(use_color=False):
|
|
|
215
252
|
f" {_style('cdx clean [name] [--yes] [--json]', '36', use_color)}",
|
|
216
253
|
f" {_style('cdx clean profiles (--tmp|--old-logs DAYS) [--yes] [--json]', '36', use_color)}",
|
|
217
254
|
f" {_style('cdx export <file> [--include-auth] [--sessions a,b] [--passphrase-env VAR|--passphrase-stdin] [--force] [--json]', '36', use_color)}",
|
|
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 [--severity OK|WARN|FAIL[,OK|WARN|FAIL...]] [--json]', '36', use_color)}",
|
|
255
|
+
f" {_style('cdx import <file> [--sessions a,b] [--passphrase-env VAR|--passphrase-stdin] [--force|--merge] [--allow-authless-force] [--json]', '36', use_color)}",
|
|
256
|
+
f" {_style('cdx doctor [--severity OK|WARN|FAIL[,OK|WARN|FAIL...]] [--check-provider-flags] [--json]', '36', use_color)}",
|
|
220
257
|
f" {_style('cdx repair [--dry-run] [--force] [--json]', '36', use_color)}",
|
|
221
258
|
f" {_style('cdx view [--json]', '36', use_color)}",
|
|
222
259
|
f" {_style('cdx update [all] [--check] [--yes] [--json] [--version TAG]', '36', use_color)}",
|
|
@@ -239,6 +276,25 @@ def wants_json(argv):
|
|
|
239
276
|
|
|
240
277
|
def format_json_error(error):
|
|
241
278
|
message = str(error)
|
|
279
|
+
# An error that already knows its own code keeps it. Only errors that never
|
|
280
|
+
# declared one fall back to sniffing the message prefix below.
|
|
281
|
+
declared = getattr(error, "code", None)
|
|
282
|
+
if declared:
|
|
283
|
+
allowed = getattr(error, "allowed", None)
|
|
284
|
+
# Same field set as run_result_payload's error object, so a caller does
|
|
285
|
+
# not need two shapes depending on which command raised.
|
|
286
|
+
return json.dumps({
|
|
287
|
+
"schema_version": API_SCHEMA_VERSION,
|
|
288
|
+
"ok": False,
|
|
289
|
+
"error": {
|
|
290
|
+
"source": "cdx",
|
|
291
|
+
"code": declared,
|
|
292
|
+
"message": message,
|
|
293
|
+
"arguments": list(getattr(error, "arguments", ()) or []),
|
|
294
|
+
"allowed_values": list(allowed) if allowed else None,
|
|
295
|
+
"exit_code": error.exit_code,
|
|
296
|
+
},
|
|
297
|
+
}, indent=2)
|
|
242
298
|
code = "cdx_error"
|
|
243
299
|
if message.startswith("Usage:"):
|
|
244
300
|
code = "invalid_usage"
|
|
@@ -506,7 +562,11 @@ def main(argv, options=None):
|
|
|
506
562
|
"service": service,
|
|
507
563
|
"signal_emitter": signal_emitter,
|
|
508
564
|
"spawn": spawn,
|
|
565
|
+
"spawn_detached": options.get("spawn_detached"),
|
|
509
566
|
"spawn_headless": options.get("spawn_headless"),
|
|
567
|
+
# Stream `--prompt-file -` reads from. Distinct from "stdin", which is
|
|
568
|
+
# the {"isTTY": ...} descriptor the interactive paths use.
|
|
569
|
+
"prompt_stdin": options.get("prompt_stdin"),
|
|
510
570
|
"spawn_sync": spawn_sync,
|
|
511
571
|
"stdin_is_tty": stdin_is_tty,
|
|
512
572
|
"version": VERSION,
|