From bb069b944a260416f793117a1baf475401b01149 Mon Sep 17 00:00:00 2001 From: Maxim Esipov Date: Thu, 12 Feb 2026 01:10:30 +0300 Subject: [PATCH 1/3] docs: add Invalid SemVer beta troubleshooting steps --- docs/TROUBLESHOOTING.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 112e0d9..49af502 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -84,6 +84,39 @@ The correct key is `plugin` (singular): **Not** `"plugins"` (will cause "Unrecognized key" error). +### "Invalid SemVer: beta" + +**Error:** +``` +Invalid SemVer +{ + "name": "UnknownError", + "data": { + "message": "Error: Invalid SemVer: beta ... isOutdated (src/bun/registry.ts:...)" + } +} +``` + +**Why this happens:** OpenCode's cache may keep the plugin dependency as a dist-tag (`"beta"`) in `~/.cache/opencode/package.json` and `~/.cache/opencode/bun.lock`. Some OpenCode versions compare plugin versions as strict semver and fail on non-numeric tags. + +**Fix (recommended):** Re-resolve the dependency in OpenCode cache so it is pinned to a real version. + +**macOS / Linux:** +```bash +cd ~/.cache/opencode +bun add opencode-antigravity-auth@latest +``` + +**Windows (PowerShell):** +```powershell +Set-Location "$env:USERPROFILE\.cache\opencode" +bun add opencode-antigravity-auth@latest +``` + +Then restart OpenCode. + +> If you intentionally run beta channel, use `bun add opencode-antigravity-auth@beta` instead. + --- ## Gemini CLI Permission Error @@ -431,4 +464,4 @@ npx tsx script/test-regression.ts --dry-run # List tests ## Still stuck? -Open an issue on [GitHub](https://github.com/NoeFabris/opencode-antigravity-auth/issues). \ No newline at end of file +Open an issue on [GitHub](https://github.com/NoeFabris/opencode-antigravity-auth/issues). From 00e748a4c6b5ee578b054e72b74b9ace543f43b6 Mon Sep 17 00:00:00 2001 From: ndycode Date: Fri, 20 Feb 2026 03:22:40 +0800 Subject: [PATCH 2/3] fix: bump version fallback to 1.18.3 to unblock Gemini 3.1 Pro The hardcoded ANTIGRAVITY_VERSION_FALLBACK (1.15.8) predates Gemini 3.1 Pro support on the backend. When the live version fetch fails (firewall, WSL2 network restrictions, timeout), the stale fallback is used in User-Agent headers. The backend then rejects requests with 'not available on this version'. Bump the fallback to 1.18.3 (current stable) and add 9 regression tests covering the network-failure path. Closes #468 --- src/constants.ts | 2 +- src/plugin/version.test.ts | 107 +++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 src/plugin/version.test.ts diff --git a/src/constants.ts b/src/constants.ts index 8686dfd..61fa1db 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -70,7 +70,7 @@ export const GEMINI_CLI_ENDPOINT = ANTIGRAVITY_ENDPOINT_PROD; */ export const ANTIGRAVITY_DEFAULT_PROJECT_ID = "rising-fact-p41fc"; -const ANTIGRAVITY_VERSION_FALLBACK = "1.15.8"; +const ANTIGRAVITY_VERSION_FALLBACK = "1.18.3"; let antigravityVersion = ANTIGRAVITY_VERSION_FALLBACK; let versionLocked = false; diff --git a/src/plugin/version.test.ts b/src/plugin/version.test.ts new file mode 100644 index 0000000..ddd69a0 --- /dev/null +++ b/src/plugin/version.test.ts @@ -0,0 +1,107 @@ +import { describe, it, expect, vi, beforeEach } from "vitest" + +/** + * Regression tests for the version fallback mechanism. + * + * Issue #468: On WSL2/AlmaLinux with strict firewall rules, both the + * auto-updater API and changelog fetch fail. The plugin then uses the + * hardcoded fallback version in User-Agent headers. If the fallback is + * too old, the backend rejects requests for newer models (e.g., Gemini 3.1 Pro) + * with "not available on this version". + * + * These tests verify the fallback is current (1.18.3) and that the + * network-failure path correctly uses it. + */ + +// Reset module state between tests so versionLocked starts fresh +beforeEach(() => { + vi.resetModules() +}) + +describe("ANTIGRAVITY_VERSION_FALLBACK", () => { + it("defaults to 1.18.3 (current stable)", async () => { + const { getAntigravityVersion } = await import("../constants.ts") + expect(getAntigravityVersion()).toBe("1.18.3") + }) + + it("is at least 1.18.0 to support Gemini 3.1 Pro", async () => { + const { getAntigravityVersion } = await import("../constants.ts") + const [major, minor] = getAntigravityVersion().split(".").map(Number) + expect(major! * 100 + minor!).toBeGreaterThanOrEqual(118) + }) +}) + +describe("setAntigravityVersion", () => { + it("updates the version on first call", async () => { + const { getAntigravityVersion, setAntigravityVersion } = await import("../constants.ts") + setAntigravityVersion("2.0.0") + expect(getAntigravityVersion()).toBe("2.0.0") + }) + + it("locks after first call — subsequent calls are ignored", async () => { + const { getAntigravityVersion, setAntigravityVersion } = await import("../constants.ts") + setAntigravityVersion("2.0.0") + setAntigravityVersion("3.0.0") + expect(getAntigravityVersion()).toBe("2.0.0") + }) +}) + +describe("initAntigravityVersion — network failure path", () => { + it("falls back to hardcoded version when both fetches throw", async () => { + vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("network unreachable"))) + + const { getAntigravityVersion } = await import("../constants.ts") + const { initAntigravityVersion } = await import("./version.ts") + await initAntigravityVersion() + + expect(getAntigravityVersion()).toBe("1.18.3") + }) + + it("falls back to hardcoded version when both fetches return non-ok", async () => { + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue({ ok: false, status: 503, text: async () => "" }), + ) + + const { getAntigravityVersion } = await import("../constants.ts") + const { initAntigravityVersion } = await import("./version.ts") + await initAntigravityVersion() + + expect(getAntigravityVersion()).toBe("1.18.3") + }) + + it("uses API version when auto-updater responds", async () => { + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue({ ok: true, text: async () => "1.19.0" }), + ) + + const { getAntigravityVersion } = await import("../constants.ts") + const { initAntigravityVersion } = await import("./version.ts") + await initAntigravityVersion() + + expect(getAntigravityVersion()).toBe("1.19.0") + }) + + it("fallback version appears in User-Agent header", async () => { + vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("timeout"))) + + const { getAntigravityHeaders } = await import("../constants.ts") + const { initAntigravityVersion } = await import("./version.ts") + await initAntigravityVersion() + + const headers = getAntigravityHeaders() + expect(headers["User-Agent"]).toContain("Antigravity/1.18.3") + }) + + it("fallback version appears in randomized antigravity headers", async () => { + vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("timeout"))) + + const { getRandomizedHeaders } = await import("../constants.ts") + const { initAntigravityVersion } = await import("./version.ts") + await initAntigravityVersion() + + const headers = getRandomizedHeaders("antigravity") + expect(headers["User-Agent"]).toContain("1.18.3") + }) +}) From 2685b9737bd4deaf3661fafb8884fed86faef7d9 Mon Sep 17 00:00:00 2001 From: ndycode Date: Fri, 20 Feb 2026 03:33:48 +0800 Subject: [PATCH 3/3] address CodeRabbit review: export fallback constant, fix test hygiene - Export ANTIGRAVITY_VERSION_FALLBACK so tests import it instead of repeating the literal (single update surface on future bumps) - Add afterEach(vi.unstubAllGlobals) to prevent stub leaks between tests - Fix semver comparison to use explicit major/minor checks instead of major*100+minor encoding that breaks for minor >= 100 --- src/constants.ts | 2 +- src/plugin/version.test.ts | 33 +++++++++++++++++++-------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index 61fa1db..a8a21c8 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -70,7 +70,7 @@ export const GEMINI_CLI_ENDPOINT = ANTIGRAVITY_ENDPOINT_PROD; */ export const ANTIGRAVITY_DEFAULT_PROJECT_ID = "rising-fact-p41fc"; -const ANTIGRAVITY_VERSION_FALLBACK = "1.18.3"; +export const ANTIGRAVITY_VERSION_FALLBACK = "1.18.3"; let antigravityVersion = ANTIGRAVITY_VERSION_FALLBACK; let versionLocked = false; diff --git a/src/plugin/version.test.ts b/src/plugin/version.test.ts index ddd69a0..4445147 100644 --- a/src/plugin/version.test.ts +++ b/src/plugin/version.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, vi, beforeEach } from "vitest" +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest" /** * Regression tests for the version fallback mechanism. @@ -9,7 +9,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest" * too old, the backend rejects requests for newer models (e.g., Gemini 3.1 Pro) * with "not available on this version". * - * These tests verify the fallback is current (1.18.3) and that the + * These tests verify the fallback is current and that the * network-failure path correctly uses it. */ @@ -18,16 +18,21 @@ beforeEach(() => { vi.resetModules() }) +afterEach(() => { + vi.unstubAllGlobals() +}) + describe("ANTIGRAVITY_VERSION_FALLBACK", () => { - it("defaults to 1.18.3 (current stable)", async () => { - const { getAntigravityVersion } = await import("../constants.ts") - expect(getAntigravityVersion()).toBe("1.18.3") + it("defaults to the exported fallback constant", async () => { + const { ANTIGRAVITY_VERSION_FALLBACK, getAntigravityVersion } = await import("../constants.ts") + expect(getAntigravityVersion()).toBe(ANTIGRAVITY_VERSION_FALLBACK) }) it("is at least 1.18.0 to support Gemini 3.1 Pro", async () => { const { getAntigravityVersion } = await import("../constants.ts") const [major, minor] = getAntigravityVersion().split(".").map(Number) - expect(major! * 100 + minor!).toBeGreaterThanOrEqual(118) + expect(major).toBeGreaterThanOrEqual(1) + if (major === 1) expect(minor).toBeGreaterThanOrEqual(18) }) }) @@ -50,11 +55,11 @@ describe("initAntigravityVersion — network failure path", () => { it("falls back to hardcoded version when both fetches throw", async () => { vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("network unreachable"))) - const { getAntigravityVersion } = await import("../constants.ts") + const { ANTIGRAVITY_VERSION_FALLBACK, getAntigravityVersion } = await import("../constants.ts") const { initAntigravityVersion } = await import("./version.ts") await initAntigravityVersion() - expect(getAntigravityVersion()).toBe("1.18.3") + expect(getAntigravityVersion()).toBe(ANTIGRAVITY_VERSION_FALLBACK) }) it("falls back to hardcoded version when both fetches return non-ok", async () => { @@ -63,11 +68,11 @@ describe("initAntigravityVersion — network failure path", () => { vi.fn().mockResolvedValue({ ok: false, status: 503, text: async () => "" }), ) - const { getAntigravityVersion } = await import("../constants.ts") + const { ANTIGRAVITY_VERSION_FALLBACK, getAntigravityVersion } = await import("../constants.ts") const { initAntigravityVersion } = await import("./version.ts") await initAntigravityVersion() - expect(getAntigravityVersion()).toBe("1.18.3") + expect(getAntigravityVersion()).toBe(ANTIGRAVITY_VERSION_FALLBACK) }) it("uses API version when auto-updater responds", async () => { @@ -86,22 +91,22 @@ describe("initAntigravityVersion — network failure path", () => { it("fallback version appears in User-Agent header", async () => { vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("timeout"))) - const { getAntigravityHeaders } = await import("../constants.ts") + const { ANTIGRAVITY_VERSION_FALLBACK, getAntigravityHeaders } = await import("../constants.ts") const { initAntigravityVersion } = await import("./version.ts") await initAntigravityVersion() const headers = getAntigravityHeaders() - expect(headers["User-Agent"]).toContain("Antigravity/1.18.3") + expect(headers["User-Agent"]).toContain(`Antigravity/${ANTIGRAVITY_VERSION_FALLBACK}`) }) it("fallback version appears in randomized antigravity headers", async () => { vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("timeout"))) - const { getRandomizedHeaders } = await import("../constants.ts") + const { ANTIGRAVITY_VERSION_FALLBACK, getRandomizedHeaders } = await import("../constants.ts") const { initAntigravityVersion } = await import("./version.ts") await initAntigravityVersion() const headers = getRandomizedHeaders("antigravity") - expect(headers["User-Agent"]).toContain("1.18.3") + expect(headers["User-Agent"]).toContain(ANTIGRAVITY_VERSION_FALLBACK) }) })