From cd6ec2a212ed2c9c1cdba6f45398eaf3258d54f5 Mon Sep 17 00:00:00 2001 From: dtzp555-max Date: Mon, 11 May 2026 07:01:49 +1000 Subject: [PATCH] fix(doctor): dynamic latest_version from origin/main; release v3.15.1 (#92) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v3.15.0 doctor used a hard-coded `latest = "v3.14.0"` fallback, causing any v3.15.0+ install to report kind=upgrade against a stale value. `ocp update` would then attempt `git checkout v3.14.0` — a downgrade. Doctor now fetches `git -C ~/ocp show origin/main:package.json` to determine the actual latest. On failure (offline, fresh clone, no remote), falls back to currentVersion so kind=noop instead of recommending a downgrade. Regression test added: doctor with unreachable ocpDir falls back to currentVersion as latest (not the old hardcoded v3.14.0). Caught during v3.15.0 post-deploy verification on home-mac: ./ocp doctor reported `kind=upgrade` immediately after v3.15.0 install, which would have been a critical user-facing bug. No cli.js citation needed: this is OCP-internal doctor logic with no corresponding cli.js operation. Co-authored-by: dtzp555 Co-authored-by: Claude Opus 4.7 --- CHANGELOG.md | 8 +++++++- package.json | 2 +- scripts/doctor.mjs | 14 +++++++++++++- test-features.mjs | 12 ++++++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ea0093..946243a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog -## v3.15.0 — 2026-XX-XX (release date filled at tag time) +## v3.15.1 — 2026-05-10 + +### Fixes + +- **doctor: dynamic `latest_version` from `origin/main:package.json`** — v3.15.0 doctor used a hard-coded `latest = "v3.14.0"` fallback, which made any v3.15.0+ install report `kind = upgrade` (against a stale value). `ocp update` would then attempt `git checkout v3.14.0` — a downgrade. Doctor now fetches `git -C ~/ocp show origin/main:package.json` to determine the actual latest version; on failure (offline, fresh clone with no remote), falls back to `currentVersion` so `kind = noop` instead of recommending a downgrade. + +## v3.15.0 — 2026-05-10 ### Features diff --git a/package.json b/package.json index 8451dd6..fe69e2a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-claude-proxy", - "version": "3.15.0", + "version": "3.15.1", "description": "OCP (Open Claude Proxy) — use your Claude Pro/Max subscription as an OpenAI-compatible API for any IDE. Works with Cline, OpenCode, Aider, Continue.dev, OpenClaw, and more.", "type": "module", "bin": { diff --git a/scripts/doctor.mjs b/scripts/doctor.mjs index 737bce1..9928e9f 100644 --- a/scripts/doctor.mjs +++ b/scripts/doctor.mjs @@ -48,7 +48,19 @@ export async function runDoctor(opts = {}) { currentVersion = "unknown"; } } - const latestVersion = opts.mockLatest || "v3.14.0"; + // Resolve latest from origin/main (cheap: `git show origin/main:package.json`). + // Falls back to current_version when network/git unavailable, so kind = noop instead + // of recommending a downgrade against a stale hardcoded value. + let latestVersion = opts.mockLatest; + if (!latestVersion) { + try { + const out = execSync(`git -C ${ocpDir} show origin/main:package.json 2>/dev/null`, { stdio: ["pipe", "pipe", "pipe"] }).toString(); + const remotePkg = JSON.parse(out); + latestVersion = `v${remotePkg.version}`; + } catch { + latestVersion = currentVersion; + } + } push("current_version", "PASS", `current=${currentVersion}`); // --- from-version supported? --- diff --git a/test-features.mjs b/test-features.mjs index 34603dd..404f4fc 100644 --- a/test-features.mjs +++ b/test-features.mjs @@ -655,6 +655,18 @@ test("doctor empty health body → fix_service (not fix_oauth)", async () => { assert.equal(result.next_action.kind, "fix_service"); }); +test("doctor falls back to currentVersion when origin/main unreachable (no stale latest)", async () => { + // Use a non-existent ocpDir so git command fails; without the fix this would still + // hard-code v3.14.0 as latest and recommend a downgrade for a future v3.15.0+ user. + const result = await runDoctor({ + skipNetwork: true, + mockVersion: "v3.15.0", + ocpDir: "/nonexistent-ocp-dir-for-test" + }); + assert.equal(result.latest_version, "v3.15.0"); + assert.equal(result.next_action.kind, "noop"); +}); + // ── Upgrade Tests ── import { runUpgrade } from "./scripts/upgrade.mjs";