cdx-manager 0.25.5 → 0.25.6

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
@@ -4,7 +4,7 @@
4
4
 
5
5
  <br clear="left"/>
6
6
 
7
- [![License](https://img.shields.io/badge/license-MIT-4C8BF5)](LICENSE) ![Version](https://img.shields.io/badge/version-v0.25.5-4C8BF5) ![Python](https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white)
7
+ [![License](https://img.shields.io/badge/license-MIT-4C8BF5)](LICENSE) ![Version](https://img.shields.io/badge/version-v0.25.6-4C8BF5) ![Python](https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white)
8
8
 
9
9
  **The right AI coding account, when you need it.** CDX Manager keeps your Codex and Claude accounts in separate profiles, shows which ones have quota, and helps you pick the next usable session from your terminal.
10
10
 
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.25.5
1
+ 0.25.6
@@ -0,0 +1,15 @@
1
+ # CDX Manager 0.25.6
2
+
3
+ Release date: 2026-11-02
4
+
5
+ This patch release fixes provider update detection and verifies installed versions after updates.
6
+
7
+ ## Fixes
8
+
9
+ - Detect advertised provider updaters and version output on both stdout and stderr. After an update, verify the installed version and report failure if it is missing, unchanged, downgraded, or still below the published version. Successful text output shows the version transition, and JSON results include before, after, and latest versions. (#87)
10
+ - Fall back to the latest stable GitHub release when Ollama's vendor API returns an empty, invalid, or failed response. Keep Linux Ollama updates manual, with an explicit maintenance reason. (#87)
11
+
12
+ ## Maintenance
13
+
14
+ - Record tray companion checksums for v0.25.5. (`b64a372f9ab4abb61f1440943fca19703ad555eb`)
15
+ - Record release archive checksums for v0.25.5. (`1218145eae4201e40b8140e7a94cbcec18454e17`)
@@ -2,6 +2,9 @@
2
2
 
3
3
  ## Truthful workstation updates
4
4
 
5
+ - Detect native updater subcommands in both stdout and stderr, including Antigravity's indented `Available subcommands:` help. Verify the installed CLI version after updating, fail unchanged or unverified updates, and expose before/after versions without hiding major upgrades.
6
+ - Fall back from empty or failed Ollama vendor responses (including Linux HTTP 204) to the latest stable `ollama/ollama` GitHub release. Keep Linux updates manual with `update_available`, a null command, and a service-safety reason; never replace a potentially active binary or restart models automatically.
7
+
5
8
  - Verify latest versions from npm, RTK GitHub releases, and Antigravity/Ollama vendor update endpoints with short read-only probes. Report `unknown` on unavailable evidence; reserve `up_to_date` for identical known versions, including Ponytail revisions.
6
9
  - Preserve the existing JSON fields and `update_available` status, show uncertainty and manual-update reasons in text, and apply only supported native commands or verified Homebrew package upgrades. Cover matching/outdated versions, source failures, missing tools, and command execution with faked sources.
7
10
 
@@ -738,6 +738,17 @@
738
738
  "x86_64-pc-windows-msvc": "8e2c1925f025a7227ef909f27e8b4696c03bb5a86e0c61c989da6a942b5638a6",
739
739
  "x86_64-unknown-linux-musl": "9ae1164abc3ff4b30d25c44696569855138f8ac0e3b6e1c3451c716d20f221fa"
740
740
  }
741
+ },
742
+ "v0.25.5": {
743
+ "github_tarball_sha256": "4a2ece21e32f59a84d8bc7b2a565817c6c90bb314f179319bc19cf85a0c71a60",
744
+ "github_zip_sha256": "959de4dca501fd53c937f4045936b12acbea58d255a4e3cae510ec82a230bef8",
745
+ "tray_assets": {
746
+ "aarch64-apple-darwin": "727c4ded1ee4d9d8ed9853badcb10efd1cc76bd6dcd6012b6739920d42e6273c",
747
+ "aarch64-unknown-linux-musl": "9270224db6be83d4a4787f9b523a6fa059267f56ed1cf12f022f65bf68c0474f",
748
+ "x86_64-apple-darwin": "10c555671cdeb3ce4c0372f6f8a48e6cfeac9132e980c466fe5a8978dc1a2862",
749
+ "x86_64-pc-windows-msvc": "85945b30580cb82c64ece94652ae0633a6156dfd85e02108c0d4b7676cab9911",
750
+ "x86_64-unknown-linux-musl": "7c5a23938b84c5952be71b5956ed3167c218d05ce661940b0c86f2dde2d8221f"
751
+ }
741
752
  }
742
753
  }
743
754
  }
package/docs/cli.md CHANGED
@@ -567,19 +567,33 @@ an installed server, not the latest published release, and is not used.
567
567
  Antigravity Hub and IDE 2 have separate feeds and are not substituted for `agy`.
568
568
  HTTP and read-only CLI probes have a five-second timeout; source failures leave
569
569
  `latest_version: null` and `status: unknown`. Ponytail also reports `unknown`
570
- when its remote revision is unavailable. An empty Ollama response (including
571
- HTTP 204 without a published version) remains `unknown`; a different platform
572
- is never substituted to manufacture a match.
570
+ when its remote revision is unavailable. An empty, failed, or invalid Ollama
571
+ response (including Linux HTTP 204) falls back to the latest stable GitHub
572
+ release for `ollama/ollama`. If that source also fails, status is `unknown`;
573
+ a different platform's vendor response is never substituted.
573
574
 
574
575
  For an outdated tool, CDX uses a native update command advertised by its
575
- `--help` (including Claude and Antigravity when available), Codex's updater,
576
+ `--help` on either stdout or stderr (including Claude, Antigravity, and RTK
577
+ when available), Codex's updater,
576
578
  or Homebrew only when the resolved executable belongs to a Caskroom/Cellar
577
579
  package. An outdated tool without such a path retains `update_available`,
578
580
  `update_command: null`, and a human-readable `reason`; it requires the vendor's
579
- manual update mechanism. Source uncertainty never schedules a guessed install.
581
+ manual update mechanism. On Linux, Ollama always retains `update_available`
582
+ with `update_command: null` and an explicit maintenance reason: its installer
583
+ writes system paths and may restart a service. A binary in `~/.local/bin` can
584
+ also serve running models, so CDX neither replaces it nor restarts services.
585
+ Arrange a manual maintenance window. Source uncertainty never schedules a guessed install.
580
586
  The text table shows `UNKNOWN` and the reason, and an empty action list makes
581
587
  no claim that all tools are current. JSON consumers, including external
582
588
  remote-ops auto-update scripts, should inspect statuses separately from successful
583
589
  command execution: exit code zero means no attempted action failed, not that
584
590
  every installed tool was verified current. With `--yes`, the returned plan is the
585
591
  pre-update inventory; `results` records attempted actions and their exit codes.
592
+
593
+ After each successful tool update command, CDX probes the CLI version again.
594
+ An unchanged, unreadable, downgraded, or still older-than-published version
595
+ makes the action fail, even if the updater exited zero. Successful text output
596
+ shows the before/after versions, including major upgrades. JSON keeps the
597
+ pre-update plan and existing result fields; tool verification adds
598
+ `version_before`, `version_after`, and `latest_version` to successful-command
599
+ results, plus `reason` and a nonzero `returncode` if verification fails.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdx-manager",
3
- "version": "0.25.5",
3
+ "version": "0.25.6",
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.25.5"
7
+ version = "0.25.6"
8
8
  description = "Terminal session manager for Codex and Claude accounts."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9.2"
@@ -52,7 +52,8 @@ def _format_update_all_result(result, use_color=False):
52
52
  if result.get("skipped"):
53
53
  return _warn(f"skipped: {result['name']} (blocked by {result['blocked_by']})", use_color)
54
54
  if result.get("returncode") == 0:
55
- return _success(f"done: {result['name']}", use_color)
55
+ versions = f" ({result['version_before']} -> {result['version_after']})" if "version_after" in result else ""
56
+ return _success(f"done: {result['name']}{versions}", use_color)
56
57
  detail = str(result.get("stderr") or result.get("stdout") or "No error output.").strip().replace("\n", " ")
57
58
  detail = detail[-500:]
58
59
  command = shlex.join(result.get("command") or [])
package/src/update_all.py CHANGED
@@ -56,7 +56,7 @@ def _version(command, env, runner):
56
56
  result = _run([command, "--version"], env, runner, timeout=_SOURCE_TIMEOUT)
57
57
  if result["returncode"]:
58
58
  return None
59
- text = (result["stdout"] or result["stderr"] or "").strip()
59
+ text = "\n".join((result["stdout"] or "", result["stderr"] or "")).strip()
60
60
  # Ollama can print the server version before the CLI version.
61
61
  client = re.search(r"client version is (\S+)", text)
62
62
  match = client or re.search(r"\b(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)\b", text)
@@ -85,9 +85,19 @@ def _latest_version(name):
85
85
  if name == PROVIDER_OLLAMA:
86
86
  arch = {"x86_64": "amd64", "aarch64": "arm64"}.get(platform.machine().lower(), platform.machine().lower())
87
87
  query = urlencode({"os": platform.system().lower(), "arch": arch, "version": "0.0.0"})
88
- release = _fetch_json(f"https://ollama.com/api/update?{query}")
89
- # The vendor updater extracts the release tag from the artifact URL.
90
- return _parse_version(os.path.basename(os.path.dirname(urlparse(release["url"]).path)))
88
+ try:
89
+ release = _fetch_json(f"https://ollama.com/api/update?{query}")
90
+ # The vendor updater extracts the release tag from the artifact URL.
91
+ version = _parse_version(os.path.basename(os.path.dirname(urlparse(release["url"]).path)))
92
+ if version:
93
+ return version
94
+ except Exception:
95
+ # Linux can return HTTP 204 with no JSON body.
96
+ pass
97
+ release = _fetch_json("https://api.github.com/repos/ollama/ollama/releases/latest")
98
+ if release.get("draft") or release.get("prerelease"):
99
+ return None
100
+ return _parse_version(release["tag_name"])
91
101
  except Exception:
92
102
  # A failed or changed source is uncertainty, never evidence of currency.
93
103
  return None
@@ -101,6 +111,8 @@ def _newer(latest, current):
101
111
 
102
112
 
103
113
  def _native_update_command(name, command, path, env, runner):
114
+ if name == PROVIDER_OLLAMA and platform.system().lower() == "linux":
115
+ return None
104
116
  real_path = os.path.realpath(path)
105
117
  cask = re.search(r"/Caskroom/([^/]+)/", real_path)
106
118
  formula = re.search(r"/Cellar/([^/]+)/", real_path)
@@ -111,7 +123,7 @@ def _native_update_command(name, command, path, env, runner):
111
123
  return [command, "update"]
112
124
  help_result = _run([command, "--help"], env, runner, timeout=_SOURCE_TIMEOUT)
113
125
  if help_result["returncode"] == 0:
114
- help_text = help_result["stdout"]
126
+ help_text = "\n".join((help_result["stdout"] or "", help_result["stderr"] or ""))
115
127
  for subcommand in ("self-update", "update", "--update"):
116
128
  if re.search(r"(?m)^\s*" + re.escape(subcommand) + r"(?:\s|$)", help_text):
117
129
  return [command, subcommand]
@@ -153,6 +165,9 @@ def collect_update_all_plan(service, env=None, runner=None):
153
165
  steps.append({"name": name, "command": item["update_command"], "env": {}})
154
166
  else:
155
167
  item["reason"] = "No supported CLI updater or verified Homebrew installation; use the vendor's updater manually."
168
+ if name == PROVIDER_OLLAMA and platform.system().lower() == "linux":
169
+ item["reason"] = ("No supported safe automatic Linux update: Ollama's installer writes system paths and may restart a service. "
170
+ "Even a user-local binary may serve running models; arrange a manual maintenance window.")
156
171
  else:
157
172
  item["status"] = "unknown"
158
173
  item["reason"] = "Latest version unavailable from the official source." if not latest else "Installed version unavailable or does not match the published release."
@@ -214,6 +229,15 @@ def apply_update_all_plan(plan, service, env=None, runner=None, progress=None):
214
229
  continue
215
230
  result = _run(step["command"], {**env, **step.get("env", {})}, runner, timeout=120)
216
231
  result = {"name": step["name"], "command": step["command"], **result}
232
+ item = next((item for item in plan.get("items", []) if item["name"] == step["name"]), None)
233
+ if result["returncode"] == 0 and item:
234
+ after = _version(item["command"], env, runner)
235
+ result.update(version_before=item["version"], version_after=after, latest_version=item["latest_version"])
236
+ if not (_newer(after, item["version"]) and
237
+ (after == item["latest_version"] or _newer(after, item["latest_version"]))):
238
+ result["returncode"] = 1
239
+ result["reason"] = "Update verification failed: installed version did not advance to the published release."
240
+ result["stderr"] = (result["stderr"] + "\n" + result["reason"]).strip()
217
241
  results.append(result)
218
242
  if result["returncode"] == 0 and step.get("id"):
219
243
  completed.add(step["id"])