cdx-manager 0.18.1 → 0.18.2
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 +2 -1
- package/changelogs/CHANGELOGS_0_18_2.md +43 -0
- package/checksums/release-archives.json +11 -0
- package/package.json +1 -1
- package/pyproject.toml +9 -2
- package/src/tray_install.py +47 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CDX Manager
|
|
2
2
|
|
|
3
|
-
[](LICENSE) ](LICENSE)  
|
|
4
4
|
|
|
5
5
|
**Stop guessing which AI account still has quota.** `cdx` tracks the rate-limit window of every Codex and Claude account you own, tells you which one is usable right now, and launches it with isolated auth — in one command.
|
|
6
6
|
|
|
@@ -560,6 +560,7 @@ They are deliberately hard to become noise. One hint per invocation at most, not
|
|
|
560
560
|
|
|
561
561
|
The companion is a native binary distributed as a release asset, not part of the pip package. Its trust model is worth stating plainly rather than leaving you to assume the usual one.
|
|
562
562
|
|
|
563
|
+
- **Where the checksum comes from.** Releases older than the one you are running are vouched for by `checksums/release-archives.json`, committed to the repository before those assets existed. Your own release is a special case: its tray assets are built after the tag, so no package built at tag time can carry their checksums. For that one version CDX reads the ledger the release publishes beside the assets — the publisher's own assertion over the same channel, which catches a truncated download or a wrong-architecture asset but does not defend against someone who can rewrite the release itself.
|
|
563
564
|
- **The checksum is the vouching, not Apple or Microsoft.** The macOS bundle is signed with a *self-signed* certificate. That signature exists because Apple Silicon refuses to execute unsigned arm64 code at all — it is not a claim that Apple reviewed anything. What actually vouches for the asset is the SHA-256 published in `checksums/release-archives.json`, verified before a single byte is unpacked. An asset with no published checksum is refused rather than installed hopefully.
|
|
564
565
|
- **Install through `cdx tray install`, and no download link is offered.** This is a security property, not a convenience. A file fetched by CDX carries no macOS quarantine attribute and no Windows Mark-of-the-Web, so nothing puts Gatekeeper or SmartScreen between you and a companion you asked for. The same file fetched through a browser would carry both. That is why no direct download URL is published: it would be a worse path wearing the same clothes.
|
|
565
566
|
- **Nothing is silently replaced.** An update downloads, verifies, and *proves the replacement starts* before the working companion is touched. A corrupt asset or one built for the wrong architecture costs an error message, not a tray you can no longer launch. An interrupted update is a named, recoverable state that `cdx tray doctor` reports.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# CDX Manager 0.18.2
|
|
2
|
+
|
|
3
|
+
## Fixes
|
|
4
|
+
|
|
5
|
+
### `cdx tray install` can install the companion for the release you are running
|
|
6
|
+
|
|
7
|
+
No release could ever install its own tray companion, and the cause was
|
|
8
|
+
structural rather than a bug. Tray assets are built after the tag exists, so
|
|
9
|
+
their checksums are recorded afterwards — which means a package built at tag
|
|
10
|
+
time ships a checksum ledger that stops at the previous release. Installing
|
|
11
|
+
refused with "No published checksum for … of CDX <this version>".
|
|
12
|
+
|
|
13
|
+
The release already publishes `release-archives.json` beside the assets. A
|
|
14
|
+
missing local entry now falls through to it.
|
|
15
|
+
|
|
16
|
+
What that is worth, stated plainly: it is the publisher's own assertion fetched
|
|
17
|
+
over the same HTTPS channel as the asset, so it does not defend against someone
|
|
18
|
+
able to rewrite the release. It does defend against a truncated download and an
|
|
19
|
+
asset built for another architecture, which are the failures that happen. A
|
|
20
|
+
checksum committed to the repository before the asset existed is the stronger
|
|
21
|
+
claim, so a local entry always wins and fetches nothing.
|
|
22
|
+
|
|
23
|
+
### The PyPI package can start
|
|
24
|
+
|
|
25
|
+
`pyproject.toml` declared only the root package, so `cdx_manager.commands` was
|
|
26
|
+
absent from every wheel since it was extracted and `pip install cdx-manager`
|
|
27
|
+
produced a CLI that failed at import. setuptools does not warn about a
|
|
28
|
+
subpackage it was never told about: the wheel builds cleanly and breaks on the
|
|
29
|
+
user's machine.
|
|
30
|
+
|
|
31
|
+
The npm package and the standalone installer were unaffected — both ship the
|
|
32
|
+
source tree wholesale.
|
|
33
|
+
|
|
34
|
+
A test now asserts every package under `src/` is declared for the wheel.
|
|
35
|
+
|
|
36
|
+
## Validation
|
|
37
|
+
|
|
38
|
+
- `npm run lint`
|
|
39
|
+
- `npm test` — 775 Python tests
|
|
40
|
+
- A wheel built from this tree installs into a clean virtualenv and runs
|
|
41
|
+
`cdx --version` and `cdx tray doctor`
|
|
42
|
+
- `cdx tray install` exercised against a checksum ledger with no entry for the
|
|
43
|
+
version, which is the shape every shipped package has
|
|
@@ -202,6 +202,17 @@
|
|
|
202
202
|
"x86_64-pc-windows-msvc": "86e29cbdbcf070aec7fd34e5596343a43f2b5092e634ca094aba96abf1d49884",
|
|
203
203
|
"x86_64-unknown-linux-musl": "36ef879c0e75f3c367ed48f1f5a67e25698ec5a04c70958b48ae0885f87487d8"
|
|
204
204
|
}
|
|
205
|
+
},
|
|
206
|
+
"v0.18.1": {
|
|
207
|
+
"github_tarball_sha256": "260529247233abca840ecba03c2948234425b4fe0d27f2e5c008bdeb89fb0bd5",
|
|
208
|
+
"github_zip_sha256": "bc14fc2fd27641a56089a023b51427abac854eaf527a17d9bc0571698fb19b9f",
|
|
209
|
+
"tray_assets": {
|
|
210
|
+
"aarch64-apple-darwin": "8a1cd7f819d21097d63f8c452479bae7bfbfe3468473c7e8f557043c7b7ab47c",
|
|
211
|
+
"aarch64-unknown-linux-musl": "a4a757bfc0f048aa24439b675cb9f269d70328a98be88efbe5cc3fea96cbd78d",
|
|
212
|
+
"x86_64-apple-darwin": "1003b7051d6dfbf1a19c1e82eca1c648d11363171a77ad0cd85822a167921573",
|
|
213
|
+
"x86_64-pc-windows-msvc": "738eeb165c1c2fde58bd952cdc0a59561ca96856a3fb8d0cffc024bfb78b9ca6",
|
|
214
|
+
"x86_64-unknown-linux-musl": "0b3c02b007aa0655a5b95c7794eba49a37bdb9868c447bb0cfbdfbd9d371d094"
|
|
215
|
+
}
|
|
205
216
|
}
|
|
206
217
|
}
|
|
207
218
|
}
|
package/package.json
CHANGED
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.18.
|
|
7
|
+
version = "0.18.2"
|
|
8
8
|
description = "Terminal session manager for Codex and Claude accounts."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -45,10 +45,17 @@ Changelog = "https://github.com/AlexAgo83/cdx-manager/tree/main/changelogs"
|
|
|
45
45
|
cdx = "cdx_manager.cli:cli_entry"
|
|
46
46
|
|
|
47
47
|
[tool.setuptools]
|
|
48
|
-
|
|
48
|
+
# Every package under src/, not just the root. Subpackages have to be named
|
|
49
|
+
# explicitly here, and one that is missing is not a build error: the wheel is
|
|
50
|
+
# produced happily and fails at import on the user's machine. src/commands has
|
|
51
|
+
# been absent from every wheel since it was extracted, so `pip install
|
|
52
|
+
# cdx-manager` gave an unusable CLI while the npm and standalone installs — both
|
|
53
|
+
# of which ship the source tree wholesale — worked fine.
|
|
54
|
+
packages = ["cdx_manager", "cdx_manager.commands"]
|
|
49
55
|
|
|
50
56
|
[tool.setuptools.package-dir]
|
|
51
57
|
cdx_manager = "src"
|
|
58
|
+
"cdx_manager.commands" = "src/commands"
|
|
52
59
|
|
|
53
60
|
[tool.pytest.ini_options]
|
|
54
61
|
testpaths = ["test"]
|
package/src/tray_install.py
CHANGED
|
@@ -60,19 +60,61 @@ def asset_name(version, target):
|
|
|
60
60
|
return f"cdx-tray-{version}-{target}.tar.gz"
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
RELEASE_LEDGER_URL = (
|
|
64
|
+
"https://github.com/AlexAgo83/cdx-manager/releases/download/v{version}/release-archives.json"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _ledger_entry(ledger, version, target):
|
|
69
|
+
release = (ledger.get("releases") or {}).get(f"v{version}") or {}
|
|
70
|
+
return (release.get("tray_assets") or {}).get(target)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def expected_checksum(version, target, ledger_path=CHECKSUM_LEDGER, download=None):
|
|
64
74
|
"""The published sha256 for this asset, or None when none is recorded.
|
|
65
75
|
|
|
66
76
|
None is a refusal, not a fallback. An asset with no published checksum is
|
|
67
77
|
exactly the case this gate exists for.
|
|
78
|
+
|
|
79
|
+
The shipped ledger cannot contain the entry for its own release, and that is
|
|
80
|
+
structural rather than an oversight: the tray assets are built after the tag
|
|
81
|
+
exists, so their checksums are recorded afterwards. A package built at tag
|
|
82
|
+
time therefore ships a ledger that stops at the previous release, and
|
|
83
|
+
`cdx tray install` would refuse the companion for the very version the user
|
|
84
|
+
is running.
|
|
85
|
+
|
|
86
|
+
So a missing local entry falls through to the ledger the release publishes
|
|
87
|
+
alongside the assets. Be clear about what that is worth: it is the
|
|
88
|
+
publisher's own assertion, fetched over the same HTTPS channel as the asset,
|
|
89
|
+
so it does not defend against someone who can rewrite the release. It does
|
|
90
|
+
defend against a truncated or corrupted download and against an asset built
|
|
91
|
+
for another architecture, which are the failures that actually happen. The
|
|
92
|
+
strong guarantee — a checksum committed before the asset existed — is what
|
|
93
|
+
the local ledger gives for every release older than this one.
|
|
68
94
|
"""
|
|
69
95
|
try:
|
|
70
96
|
with open(ledger_path, encoding="utf-8") as handle:
|
|
71
97
|
ledger = json.load(handle)
|
|
72
98
|
except (OSError, ValueError):
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
99
|
+
ledger = {}
|
|
100
|
+
local = _ledger_entry(ledger, version, target)
|
|
101
|
+
if local:
|
|
102
|
+
return local
|
|
103
|
+
return _published_checksum(version, target, download=download)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _published_checksum(version, target, download=None):
|
|
107
|
+
"""The entry from the ledger attached to the release, or None."""
|
|
108
|
+
fetch = download or _download
|
|
109
|
+
with tempfile.TemporaryDirectory(prefix="cdx-tray-ledger-") as scratch:
|
|
110
|
+
destination = os.path.join(scratch, "release-archives.json")
|
|
111
|
+
try:
|
|
112
|
+
fetch(RELEASE_LEDGER_URL.format(version=version), destination)
|
|
113
|
+
with open(destination, encoding="utf-8") as handle:
|
|
114
|
+
published = json.load(handle)
|
|
115
|
+
except (OSError, ValueError):
|
|
116
|
+
return None
|
|
117
|
+
return _ledger_entry(published, version, target)
|
|
76
118
|
|
|
77
119
|
|
|
78
120
|
def _sha256(path):
|
|
@@ -147,7 +189,7 @@ def install(base_dir, version, download=None, ledger_path=CHECKSUM_LEDGER, targe
|
|
|
147
189
|
raise TrayInstallError(
|
|
148
190
|
f"No tray companion is published for {platform.system()} {platform.machine()}."
|
|
149
191
|
)
|
|
150
|
-
expected = expected_checksum(version, target, ledger_path=ledger_path)
|
|
192
|
+
expected = expected_checksum(version, target, ledger_path=ledger_path, download=download)
|
|
151
193
|
if not expected:
|
|
152
194
|
raise TrayInstallError(
|
|
153
195
|
f"No published checksum for the {target} tray companion of CDX {version}. "
|