cdx-manager 0.9.6 → 0.9.7
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/changelogs/CHANGELOGS_0_9_7.md +25 -0
- package/checksums/release-archives.json +4 -0
- package/package.json +2 -1
- package/pyproject.toml +1 -1
- package/src/cli.py +1 -1
- package/src/fs_utils.py +50 -0
- package/src/provider_runtime.py +2 -2
- package/src/repair.py +2 -2
- package/src/session_service.py +5 -4
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
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# CDX Manager 0.9.7
|
|
2
|
+
|
|
3
|
+
## Highlights
|
|
4
|
+
|
|
5
|
+
- Hardened profile cleanup so read-only provider cache files do not block session removal or repair cleanup.
|
|
6
|
+
|
|
7
|
+
## Changes
|
|
8
|
+
|
|
9
|
+
### Read-only profile cleanup
|
|
10
|
+
|
|
11
|
+
`cdx rmv`, session overwrite rollback cleanup, and repair quarantine cleanup now make profile trees user-writable before deleting them.
|
|
12
|
+
|
|
13
|
+
This fixes failures seen when provider-managed caches contain read-only files, including nested Go module cache paths created under session-specific provider homes.
|
|
14
|
+
|
|
15
|
+
## Validation
|
|
16
|
+
|
|
17
|
+
- `npm run release:validate`
|
|
18
|
+
- `npm run lint`
|
|
19
|
+
- `npm test`
|
|
20
|
+
- `logics-manager lint --require-status`
|
|
21
|
+
- `logics-manager audit`
|
|
22
|
+
- `git diff --check`
|
|
23
|
+
- `npm --cache /private/tmp/cdx-npm-cache pack --dry-run`
|
|
24
|
+
- `python -m build`
|
|
25
|
+
- `python -m twine check dist/*`
|
|
@@ -88,6 +88,10 @@
|
|
|
88
88
|
"v0.9.5": {
|
|
89
89
|
"github_tarball_sha256": "29c512600ed366e06bcaa8ae12f105cc198f85c6887d46c9032d677c5335203b",
|
|
90
90
|
"github_zip_sha256": "a7be652ba39b929eae6e90c48410ce8b7c9c122625da55bfcd50edb755d6d609"
|
|
91
|
+
},
|
|
92
|
+
"v0.9.6": {
|
|
93
|
+
"github_tarball_sha256": "7706ba127f247dfab50404ca175921c7874624e835c920799b3b122a45f7dc4b",
|
|
94
|
+
"github_zip_sha256": "be11246f65eae006512247067bff22863a6ebe8d5738b056fe17f654fd5d9b5c"
|
|
91
95
|
}
|
|
92
96
|
}
|
|
93
97
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cdx-manager",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.7",
|
|
4
4
|
"description": "Terminal session manager for Codex and Claude accounts.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alexandre Agostini",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"cdx": "bin/cdx.js"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
|
+
"status": "node bin/cdx.js status",
|
|
45
46
|
"test": "npm run test:py",
|
|
46
47
|
"test:py": "node bin/python-runner.js -m unittest discover -s test -p test_*_py.py",
|
|
47
48
|
"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 py_compile bin/cdx src/*.py scripts/*.py test/test_*_py.py",
|
package/pyproject.toml
CHANGED
package/src/cli.py
CHANGED
package/src/fs_utils.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import stat
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _make_user_writable(path):
|
|
7
|
+
if os.path.islink(path):
|
|
8
|
+
return
|
|
9
|
+
try:
|
|
10
|
+
current = os.stat(path).st_mode
|
|
11
|
+
except OSError:
|
|
12
|
+
return
|
|
13
|
+
mode = current | stat.S_IRUSR | stat.S_IWUSR
|
|
14
|
+
if stat.S_ISDIR(current):
|
|
15
|
+
mode |= stat.S_IXUSR
|
|
16
|
+
try:
|
|
17
|
+
os.chmod(path, mode)
|
|
18
|
+
except OSError:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _make_tree_user_writable(path):
|
|
23
|
+
if not os.path.exists(path):
|
|
24
|
+
return
|
|
25
|
+
for root, dirs, files in os.walk(path, topdown=False):
|
|
26
|
+
for name in files:
|
|
27
|
+
file_path = os.path.join(root, name)
|
|
28
|
+
if not os.path.islink(file_path):
|
|
29
|
+
_make_user_writable(file_path)
|
|
30
|
+
for name in dirs:
|
|
31
|
+
dir_path = os.path.join(root, name)
|
|
32
|
+
if not os.path.islink(dir_path):
|
|
33
|
+
_make_user_writable(dir_path)
|
|
34
|
+
_make_user_writable(root)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def remove_tree(path, ignore_errors=False):
|
|
38
|
+
def onerror(func, failing_path, _exc_info):
|
|
39
|
+
parent = os.path.dirname(failing_path)
|
|
40
|
+
if parent:
|
|
41
|
+
_make_user_writable(parent)
|
|
42
|
+
_make_user_writable(failing_path)
|
|
43
|
+
try:
|
|
44
|
+
func(failing_path)
|
|
45
|
+
except OSError:
|
|
46
|
+
if not ignore_errors:
|
|
47
|
+
raise
|
|
48
|
+
|
|
49
|
+
_make_tree_user_writable(path)
|
|
50
|
+
shutil.rmtree(path, ignore_errors=ignore_errors, onerror=onerror)
|
package/src/provider_runtime.py
CHANGED
|
@@ -335,8 +335,6 @@ def _launch_config_args(session):
|
|
|
335
335
|
args += ["--dangerously-skip-permissions"]
|
|
336
336
|
return args
|
|
337
337
|
if provider == PROVIDER_OLLAMA:
|
|
338
|
-
if power:
|
|
339
|
-
args += ["--think", power if power in ("low", "medium", "high") else "high"]
|
|
340
338
|
if permission == "full":
|
|
341
339
|
args += ["--experimental-yolo"]
|
|
342
340
|
return args
|
|
@@ -424,6 +422,8 @@ def _logics_enabled(session, env=None):
|
|
|
424
422
|
|
|
425
423
|
|
|
426
424
|
def _with_launch_preferences(session, initial_prompt=None, env=None):
|
|
425
|
+
if session["provider"] == PROVIDER_OLLAMA:
|
|
426
|
+
return initial_prompt
|
|
427
427
|
prompts = []
|
|
428
428
|
if _rtk_enabled(session):
|
|
429
429
|
prompts.append(RTK_PROMPT)
|
package/src/repair.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
-
import shutil
|
|
4
3
|
|
|
4
|
+
from .fs_utils import remove_tree
|
|
5
5
|
from .health import collect_health_report
|
|
6
6
|
|
|
7
7
|
|
|
@@ -69,7 +69,7 @@ def _apply_recreate_state(service, name):
|
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
def _apply_remove_path(path):
|
|
72
|
-
|
|
72
|
+
remove_tree(path)
|
|
73
73
|
return "applied"
|
|
74
74
|
|
|
75
75
|
|
package/src/session_service.py
CHANGED
|
@@ -13,6 +13,7 @@ from .backup_bundle import decode_bundle, encode_bundle
|
|
|
13
13
|
from .config import PROVIDER_ANTIGRAVITY, PROVIDER_CLAUDE, PROVIDER_CODEX, PROVIDERS, get_cdx_home
|
|
14
14
|
from .codex_usage import fetch_codex_rate_limits
|
|
15
15
|
from .errors import CdxError
|
|
16
|
+
from .fs_utils import remove_tree
|
|
16
17
|
from .session_store import create_session_store
|
|
17
18
|
from .status_source import find_latest_status_artifact
|
|
18
19
|
|
|
@@ -641,7 +642,7 @@ def create_session_service(options=None):
|
|
|
641
642
|
raise CdxError(f"Unknown session: {name}")
|
|
642
643
|
if quarantine_root:
|
|
643
644
|
try:
|
|
644
|
-
|
|
645
|
+
remove_tree(quarantine_root)
|
|
645
646
|
except OSError as error:
|
|
646
647
|
raise CdxError(
|
|
647
648
|
f"Removed session {name}, but failed to delete archived profile {quarantine_root}: {error}"
|
|
@@ -701,14 +702,14 @@ def create_session_service(options=None):
|
|
|
701
702
|
overwritten = bool(existing)
|
|
702
703
|
except Exception:
|
|
703
704
|
if moved_temp and os.path.exists(dest_root):
|
|
704
|
-
|
|
705
|
+
remove_tree(dest_root, ignore_errors=True)
|
|
705
706
|
if backup_root and os.path.exists(backup_root) and not os.path.exists(dest_root):
|
|
706
707
|
os.rename(backup_root, dest_root)
|
|
707
708
|
raise
|
|
708
709
|
finally:
|
|
709
710
|
if backup_root and os.path.exists(backup_root):
|
|
710
|
-
|
|
711
|
-
|
|
711
|
+
remove_tree(backup_root, ignore_errors=True)
|
|
712
|
+
remove_tree(temp_parent, ignore_errors=True)
|
|
712
713
|
if not result["ok"]:
|
|
713
714
|
raise CdxError(f"Failed to create session: {dest_name}")
|
|
714
715
|
return {"session": result["session"], "overwritten": overwritten}
|