cdx-manager 0.12.3 → 0.12.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/changelogs/CHANGELOGS_0_12_3.md +5 -0
- package/changelogs/CHANGELOGS_0_12_4.md +18 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/cli.py +2 -2
- package/src/session_service.py +24 -0
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,7 +373,7 @@ 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` |
|
|
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
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 |
|
|
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 |
|
|
@@ -527,10 +527,10 @@ cdx import backup-auth.cdx --passphrase-env CDX_BUNDLE_PASSPHRASE
|
|
|
527
527
|
|
|
528
528
|
Notes:
|
|
529
529
|
|
|
530
|
-
- `--include-auth` is encrypted, requires a passphrase, and exports only provider credential files rather than full profile caches or
|
|
530
|
+
- `--include-auth` is encrypted, requires a passphrase, and exports only provider credential files rather than full profile caches, logs, or installed plugins.
|
|
531
531
|
- 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
532
|
- `--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.
|
|
533
|
+
- `--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
534
|
- Auth bundles contain credentials. Treat them like secrets and delete them after transfer.
|
|
535
535
|
|
|
536
536
|
---
|
|
@@ -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`
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
package/src/cli.py
CHANGED
|
@@ -78,7 +78,7 @@ from .status_view import (
|
|
|
78
78
|
)
|
|
79
79
|
from .update_check import check_for_update, check_logics_manager_for_update
|
|
80
80
|
|
|
81
|
-
VERSION = "0.12.
|
|
81
|
+
VERSION = "0.12.4"
|
|
82
82
|
|
|
83
83
|
# Public surface: this module is a facade. Names below are imported above
|
|
84
84
|
# purely to be re-exported (consumed by tests and external callers); listing
|
|
@@ -215,7 +215,7 @@ def _print_help(use_color=False):
|
|
|
215
215
|
f" {_style('cdx clean [name] [--yes] [--json]', '36', use_color)}",
|
|
216
216
|
f" {_style('cdx clean profiles (--tmp|--old-logs DAYS) [--yes] [--json]', '36', use_color)}",
|
|
217
217
|
f" {_style('cdx export <file> [--include-auth] [--sessions a,b] [--passphrase-env VAR|--passphrase-stdin] [--force] [--json]', '36', use_color)}",
|
|
218
|
-
f" {_style('cdx import <file> [--sessions a,b] [--passphrase-env VAR|--passphrase-stdin] [--force|--merge] [--json]', '36', use_color)}",
|
|
218
|
+
f" {_style('cdx import <file> [--sessions a,b] [--passphrase-env VAR|--passphrase-stdin] [--force|--merge] [--allow-authless-force] [--json]', '36', use_color)}",
|
|
219
219
|
f" {_style('cdx doctor [--severity OK|WARN|FAIL[,OK|WARN|FAIL...]] [--json]', '36', use_color)}",
|
|
220
220
|
f" {_style('cdx repair [--dry-run] [--force] [--json]', '36', use_color)}",
|
|
221
221
|
f" {_style('cdx view [--json]', '36', use_color)}",
|
package/src/session_service.py
CHANGED
|
@@ -78,6 +78,7 @@ CLAUDE_STATUS_CACHE_TTL_SECONDS = 10 * 60
|
|
|
78
78
|
# Each live Codex probe spawns app-server, which can refresh+rotate the OAuth
|
|
79
79
|
# token. Cache longer so routine status calls don't keep triggering refreshes.
|
|
80
80
|
CODEX_STATUS_CACHE_TTL_SECONDS = 5 * 60
|
|
81
|
+
_FORCE_IMPORT_PRESERVED_PROFILE_PATHS = ("plugins",)
|
|
81
82
|
STATUS_PROBE_TIMEOUT_SECONDS = 5
|
|
82
83
|
MAX_STATUS_WORKERS = 8
|
|
83
84
|
LAUNCH_POWER_VALUES = {"minimal", "low", "medium", "high", "xhigh"}
|
|
@@ -1427,6 +1428,26 @@ def create_session_service(options=None):
|
|
|
1427
1428
|
else:
|
|
1428
1429
|
store["remove_session"](name)
|
|
1429
1430
|
|
|
1431
|
+
def _force_import_preserved_profile_paths(session_root):
|
|
1432
|
+
return [rel_path for rel_path in _FORCE_IMPORT_PRESERVED_PROFILE_PATHS if os.path.exists(os.path.join(session_root, rel_path))]
|
|
1433
|
+
|
|
1434
|
+
def _restore_force_import_profile_paths(name, backup_root, session_root, rel_paths):
|
|
1435
|
+
if not backup_root:
|
|
1436
|
+
return
|
|
1437
|
+
for rel_path in rel_paths:
|
|
1438
|
+
source_path = os.path.join(backup_root, rel_path)
|
|
1439
|
+
dest_path = os.path.join(session_root, rel_path)
|
|
1440
|
+
if not os.path.exists(source_path) or os.path.exists(dest_path):
|
|
1441
|
+
continue
|
|
1442
|
+
try:
|
|
1443
|
+
if os.path.isdir(source_path) and not os.path.islink(source_path):
|
|
1444
|
+
shutil.copytree(source_path, dest_path)
|
|
1445
|
+
else:
|
|
1446
|
+
_ensure_private_dir(os.path.dirname(dest_path))
|
|
1447
|
+
shutil.copy2(source_path, dest_path)
|
|
1448
|
+
except Exception as error:
|
|
1449
|
+
raise CdxError(f"Could not restore local plugin state for session {name}: {rel_path}") from error
|
|
1450
|
+
|
|
1430
1451
|
def import_bundle(
|
|
1431
1452
|
file_path,
|
|
1432
1453
|
passphrase=None,
|
|
@@ -1479,9 +1500,11 @@ def create_session_service(options=None):
|
|
|
1479
1500
|
backup_root = None
|
|
1480
1501
|
old_record = None
|
|
1481
1502
|
old_state = None
|
|
1503
|
+
preserved_profile_paths = []
|
|
1482
1504
|
if is_existing and force:
|
|
1483
1505
|
old_record = store["get_session"](name)
|
|
1484
1506
|
old_state = store["read_session_state"](name)
|
|
1507
|
+
preserved_profile_paths = _force_import_preserved_profile_paths(session_root)
|
|
1485
1508
|
if os.path.exists(session_root):
|
|
1486
1509
|
backup_root = tempfile.mkdtemp(prefix=f".{_encode(name)}.import.", dir=os.path.dirname(session_root))
|
|
1487
1510
|
os.rmdir(backup_root)
|
|
@@ -1535,6 +1558,7 @@ def create_session_service(options=None):
|
|
|
1535
1558
|
_ensure_private_dir(os.path.dirname(dest_path))
|
|
1536
1559
|
# Decrypted credentials: 0o600 from creation, no umask window.
|
|
1537
1560
|
atomic_write(dest_path, item["content"], mode=0o600)
|
|
1561
|
+
_restore_force_import_profile_paths(name, backup_root, session_root, preserved_profile_paths)
|
|
1538
1562
|
except Exception:
|
|
1539
1563
|
_restore_import_backup(name, backup_root, session_root, old_record, old_state)
|
|
1540
1564
|
raise
|