cdx-manager 0.9.12 → 0.9.14

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # CDX Manager
2
2
 
3
- [![License](https://img.shields.io/badge/license-MIT-4C8BF5)](LICENSE) ![Version](https://img.shields.io/badge/version-v0.9.12-4C8BF5) ![Python](https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white)
3
+ [![License](https://img.shields.io/badge/license-MIT-4C8BF5)](LICENSE) ![Version](https://img.shields.io/badge/version-v0.9.14-4C8BF5) ![Python](https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white)
4
4
 
5
5
  **Run multiple Codex, Claude, Antigravity, and Ollama sessions from one terminal. Switch between accounts instantly.**
6
6
 
@@ -359,8 +359,8 @@ cdx history --summary --from 2026-05-01 --to 2026-05-28
359
359
  | `cdx handoff <source> <target> [--json]` | Build shared context from the source session's latest launch transcript, install it into the target session, and launch the target unless `--json` is used; supports cross-provider handoff |
360
360
  | `cdx rmv <name> [--force] [--json]` | Remove a session and its auth data (prompts for confirmation unless `--force`) |
361
361
  | `cdx clean [name] [--json]` | Clear launch transcript logs for one session or all sessions |
362
- | `cdx export <file> [--include-auth] [--sessions a,b] [--passphrase-env VAR] [--force] [--json]` | Export sessions to a portable bundle; `--include-auth` encrypts auth data with a passphrase |
363
- | `cdx import <file> [--sessions a,b] [--passphrase-env VAR] [--force] [--json]` | Import sessions from a bundle into the current `CDX_HOME` |
362
+ | `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 |
363
+ | `cdx import <file> [--sessions a,b] [--passphrase-env VAR\|--passphrase-stdin] [--force] [--json]` | Import sessions from a bundle into the current `CDX_HOME` |
364
364
  | `cdx doctor [--json]` | Inspect CLI dependencies, CDX_HOME permissions, missing state, orphan profiles, and pending quarantines |
365
365
  | `cdx repair [--dry-run] [--force] [--json]` | Plan or apply safe repairs for missing state files, quarantines, and orphan profiles |
366
366
  | `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 |
@@ -510,7 +510,7 @@ cdx import backup-auth.cdx --passphrase-env CDX_BUNDLE_PASSPHRASE
510
510
  Notes:
511
511
 
512
512
  - `--include-auth` is encrypted, requires a passphrase, and exports only provider credential files rather than full profile caches or logs.
513
- - Without `--passphrase-env`, `cdx` prompts in an interactive terminal.
513
+ - 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.
514
514
  - `--sessions work,perso` exports or imports only a subset.
515
515
  - `--force` allows overwriting existing destination sessions during import or replacing an existing bundle file during export.
516
516
  - Auth bundles contain credentials. Treat them like secrets and delete them after transfer.
@@ -0,0 +1,27 @@
1
+ # CDX Manager 0.9.13
2
+
3
+ ## Highlights
4
+
5
+ - Fixed Claude `setup-token` capture on Linux by using the util-linux `script` flush flag.
6
+
7
+ ## Changes
8
+
9
+ ### Claude setup-token capture
10
+
11
+ Linux terminals now run transcript capture with `script -f` instead of the BSD/macOS-only `script -F` spelling.
12
+
13
+ This prevents util-linux `script` from exiting with `invalid option -- 'F'`, which previously made `cdx` fall back to running `claude setup-token` without transcript capture and miss the printed token.
14
+
15
+ macOS keeps the existing `script -F` behavior.
16
+
17
+ ## Validation
18
+
19
+ - `npm run release:validate`
20
+ - `npm run lint`
21
+ - `npm test`
22
+ - `logics-manager lint --require-status`
23
+ - `logics-manager audit`
24
+ - `git diff --check`
25
+ - `npm --cache /private/tmp/cdx-npm-cache pack --dry-run`
26
+ - `python -m build`
27
+ - `python -m twine check dist/*`
@@ -0,0 +1,27 @@
1
+ # CDX Manager 0.9.14
2
+
3
+ ## Highlights
4
+
5
+ - Added `--passphrase-stdin` for auth bundle export/import flows, avoiding passphrases in child process environments.
6
+
7
+ ## Changes
8
+
9
+ ### Auth bundle passphrase input
10
+
11
+ `cdx export --include-auth` and `cdx import` now accept `--passphrase-stdin` as an alternative to `--passphrase-env`.
12
+
13
+ This lets non-interactive callers provide the bundle passphrase through standard input instead of storing it in an environment variable. The new flag is mutually exclusive with `--passphrase-env`, and export still requires `--include-auth` before a passphrase is accepted.
14
+
15
+ Usage strings, command help, README examples, and regression coverage were updated for the new input mode.
16
+
17
+ ## Validation
18
+
19
+ - `npm run release:validate`
20
+ - `npm run lint`
21
+ - `npm test`
22
+ - `logics-manager lint --require-status`
23
+ - `logics-manager audit`
24
+ - `git diff --check`
25
+ - `npm --cache /private/tmp/cdx-npm-cache pack --dry-run`
26
+ - `python -m build`
27
+ - `python -m twine check dist/*`
@@ -112,6 +112,14 @@
112
112
  "v0.9.11": {
113
113
  "github_tarball_sha256": "8ab8f673af4ad9c0e4fd64110d47285a1de02c095fd0f6d79eb67e061255093d",
114
114
  "github_zip_sha256": "ee5fffa543b8a02c082a180c624d31951c5ff662d492132cd83d4f099da70441"
115
+ },
116
+ "v0.9.12": {
117
+ "github_tarball_sha256": "ed9de98e1b4df5e42a1e389580f03d4b4b2e09ee8f63d23110090e6f164b8ae6",
118
+ "github_zip_sha256": "55f9ead146411ae740db1cae3f91c56d6ebe33c03345f9d4bfd487e45962e412"
119
+ },
120
+ "v0.9.13": {
121
+ "github_tarball_sha256": "4541dcecbfc12705f8e50e61ab1cb1f53dd6a209cc4a6d33ec3a4e533a3da3eb",
122
+ "github_zip_sha256": "a8df11be1910a0562e0f97f69e9f35ee2fdf90ef50cb40689c85bf5ee5937027"
115
123
  }
116
124
  }
117
125
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdx-manager",
3
- "version": "0.9.12",
3
+ "version": "0.9.14",
4
4
  "description": "Terminal session manager for Codex and Claude accounts.",
5
5
  "license": "MIT",
6
6
  "author": "Alexandre Agostini",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cdx-manager"
7
- version = "0.9.12"
7
+ version = "0.9.14"
8
8
  description = "Terminal session manager for Codex and Claude accounts."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
package/src/cli.py CHANGED
@@ -66,7 +66,7 @@ from .status_view import (
66
66
  )
67
67
  from .update_check import check_for_update, check_logics_manager_for_update
68
68
 
69
- VERSION = "0.9.12"
69
+ VERSION = "0.9.14"
70
70
 
71
71
  # Public surface: this module is a facade. Names below are imported above
72
72
  # purely to be re-exported (consumed by tests and external callers); listing
@@ -192,8 +192,8 @@ def _print_help(use_color=False):
192
192
  f" {_style('cdx enable <name> [--json]', '36', use_color)}",
193
193
  f" {_style('cdx rmv <name> [--force] [--json]', '36', use_color)}",
194
194
  f" {_style('cdx clean [name] [--json]', '36', use_color)}",
195
- f" {_style('cdx export <file> [--include-auth] [--sessions a,b] [--passphrase-env VAR] [--force] [--json]', '36', use_color)}",
196
- f" {_style('cdx import <file> [--sessions a,b] [--passphrase-env VAR] [--force|--merge] [--json]', '36', use_color)}",
195
+ f" {_style('cdx export <file> [--include-auth] [--sessions a,b] [--passphrase-env VAR|--passphrase-stdin] [--force] [--json]', '36', use_color)}",
196
+ f" {_style('cdx import <file> [--sessions a,b] [--passphrase-env VAR|--passphrase-stdin] [--force|--merge] [--json]', '36', use_color)}",
197
197
  f" {_style('cdx doctor [--json]', '36', use_color)}",
198
198
  f" {_style('cdx repair [--dry-run] [--force] [--json]', '36', use_color)}",
199
199
  f" {_style('cdx view [--json]', '36', use_color)}",
package/src/cli_args.py CHANGED
@@ -15,8 +15,8 @@ STATUS_USAGE = "Usage: cdx status [--json] [--refresh|--cached] [--timeout SECON
15
15
  DOCTOR_USAGE = "Usage: cdx doctor [--json]"
16
16
  REPAIR_USAGE = "Usage: cdx repair [--dry-run] [--force] [--json]"
17
17
  UPDATE_USAGE = "Usage: cdx update [--check] [--yes] [--json] [--version TAG]"
18
- EXPORT_USAGE = "Usage: cdx export <file> [--include-auth] [--force] [--json] [--sessions name1,name2] [--passphrase-env VAR]"
19
- IMPORT_USAGE = "Usage: cdx import <file> [--force|--merge] [--json] [--sessions name1,name2] [--passphrase-env VAR]"
18
+ EXPORT_USAGE = "Usage: cdx export <file> [--include-auth] [--force] [--json] [--sessions name1,name2] [--passphrase-env VAR|--passphrase-stdin]"
19
+ IMPORT_USAGE = "Usage: cdx import <file> [--force|--merge] [--json] [--sessions name1,name2] [--passphrase-env VAR|--passphrase-stdin]"
20
20
  CONTEXT_USAGE = "Usage: cdx context show|path|init|edit|clear|set [text...] [--json]"
21
21
  HANDOFF_USAGE = "Usage: cdx handoff <name> [--json] | cdx handoff <source> <target> [--json]"
22
22
  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] [--model MODEL] [--priority 0..100] [--json]"
@@ -585,12 +585,15 @@ def _parse_export_args(args):
585
585
  "transform": _parse_session_names,
586
586
  },
587
587
  "--passphrase-env": {"key": "passphrase_env", "type": "str", "default": None},
588
+ "--passphrase-stdin": {"key": "passphrase_stdin", "type": "bool", "default": False},
588
589
  }, EXPORT_USAGE, positionals_key="positionals", max_positionals=1)
589
590
  parsed["file_path"] = parsed.pop("positionals")[0] if parsed["positionals"] else None
590
591
  if not parsed["file_path"]:
591
592
  raise CdxError(EXPORT_USAGE)
592
- if parsed["passphrase_env"] and not parsed["include_auth"]:
593
- raise CdxError("--passphrase-env requires --include-auth for export.")
593
+ if parsed["passphrase_env"] and parsed["passphrase_stdin"]:
594
+ raise CdxError("--passphrase-env and --passphrase-stdin are mutually exclusive.")
595
+ if (parsed["passphrase_env"] or parsed["passphrase_stdin"]) and not parsed["include_auth"]:
596
+ raise CdxError("--passphrase-env/--passphrase-stdin requires --include-auth for export.")
594
597
  return parsed
595
598
 
596
599
 
@@ -606,12 +609,15 @@ def _parse_import_args(args):
606
609
  "transform": _parse_session_names,
607
610
  },
608
611
  "--passphrase-env": {"key": "passphrase_env", "type": "str", "default": None},
612
+ "--passphrase-stdin": {"key": "passphrase_stdin", "type": "bool", "default": False},
609
613
  }, IMPORT_USAGE, positionals_key="positionals", max_positionals=1)
610
614
  parsed["file_path"] = parsed.pop("positionals")[0] if parsed["positionals"] else None
611
615
  if not parsed["file_path"]:
612
616
  raise CdxError(IMPORT_USAGE)
613
617
  if parsed["force"] and parsed["merge"]:
614
618
  raise CdxError("--force and --merge are mutually exclusive.")
619
+ if parsed["passphrase_env"] and parsed["passphrase_stdin"]:
620
+ raise CdxError("--passphrase-env and --passphrase-stdin are mutually exclusive.")
615
621
  return parsed
616
622
 
617
623
 
@@ -107,15 +107,22 @@ from .update_check import LatestReleaseCheckError, fetch_latest_release, fetch_l
107
107
  from .update_manager import build_update_plan, format_update_failure, run_update_plan, verify_updated_command
108
108
 
109
109
 
110
- def _resolve_bundle_passphrase(ctx, env_var, prompt, confirm=False):
110
+ def _resolve_bundle_passphrase(ctx, env_var, prompt, confirm=False, use_stdin=False):
111
111
  env = ctx.get("env", {})
112
112
  if env_var:
113
113
  passphrase = env.get(env_var)
114
114
  if not passphrase:
115
115
  raise CdxError(f"Environment variable {env_var} is empty or unset.")
116
116
  return passphrase
117
+ if use_stdin:
118
+ reader = ctx["options"].get("read_stdin")
119
+ raw = reader() if callable(reader) else sys.stdin.readline()
120
+ passphrase = (raw or "").rstrip("\r\n")
121
+ if not passphrase:
122
+ raise CdxError("Bundle passphrase from stdin is empty.")
123
+ return passphrase
117
124
  if not ctx["stdin_is_tty"]:
118
- raise CdxError("Encrypted bundle export/import requires an interactive terminal or --passphrase-env.")
125
+ raise CdxError("Encrypted bundle export/import requires an interactive terminal, --passphrase-env, or --passphrase-stdin.")
119
126
  getpass_fn = ctx["options"].get("getpass") or getpass.getpass
120
127
  passphrase = getpass_fn(prompt)
121
128
  if not passphrase:
@@ -1757,6 +1764,7 @@ def handle_export(rest, ctx):
1757
1764
  parsed["passphrase_env"],
1758
1765
  "Bundle passphrase: ",
1759
1766
  confirm=True,
1767
+ use_stdin=parsed["passphrase_stdin"],
1760
1768
  )
1761
1769
  result = ctx["service"]["export_bundle"](
1762
1770
  parsed["file_path"],
@@ -1796,6 +1804,7 @@ def handle_import(rest, ctx):
1796
1804
  parsed["passphrase_env"],
1797
1805
  "Bundle passphrase: ",
1798
1806
  confirm=False,
1807
+ use_stdin=parsed["passphrase_stdin"],
1799
1808
  )
1800
1809
  result = ctx["service"]["import_bundle"](
1801
1810
  parsed["file_path"],
@@ -375,8 +375,9 @@ def _wrap_launch_with_transcript(session, spec, capture_transcript=True, env=Non
375
375
 
376
376
  def _default_script_args(transcript_path, spec):
377
377
  if sys.platform.startswith("linux"):
378
+ # util-linux `script` flushes with -f (lowercase); -F is the BSD/macOS spelling and errors here.
378
379
  command = shlex.join([spec["command"]] + spec["args"])
379
- return ["-q", "-F", "-c", command, transcript_path]
380
+ return ["-q", "-f", "-c", command, transcript_path]
380
381
  return ["-q", "-F", transcript_path, spec["command"]] + spec["args"]
381
382
 
382
383