From 7d1b7168b30e563e7f39f1b250f433ac0263982f Mon Sep 17 00:00:00 2001 From: kvanzuijlen <8818390+kvanzuijlen@users.noreply.github.com> Date: Mon, 25 May 2026 01:08:30 +0200 Subject: [PATCH 1/3] fix(antigravity): fix support for antigravity ide (v2) --- plugins/antigravity/plugin.js | 52 ++++++++------ plugins/antigravity/plugin.test.js | 108 +++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 22 deletions(-) diff --git a/plugins/antigravity/plugin.js b/plugins/antigravity/plugin.js index 2b8d515b..3a228ff6 100644 --- a/plugins/antigravity/plugin.js +++ b/plugins/antigravity/plugin.js @@ -1,6 +1,7 @@ (function () { var LS_SERVICE = "exa.language_server_pb.LanguageServerService" - var STATE_DB = "~/Library/Application Support/Antigravity/User/globalStorage/state.vscdb" + var STATE_DB_V1 = "~/Library/Application Support/Antigravity/User/globalStorage/state.vscdb" + var STATE_DB_V2 = "~/Library/Application Support/Antigravity IDE/User/globalStorage/state.vscdb" var CLOUD_CODE_URLS = [ "https://daily-cloudcode-pa.googleapis.com", "https://cloudcode-pa.googleapis.com", @@ -93,29 +94,36 @@ } function loadOAuthTokens(ctx) { - try { - var rows = ctx.host.sqlite.query( - STATE_DB, - "SELECT value FROM ItemTable WHERE key = '" + OAUTH_TOKEN_KEY + "' LIMIT 1" - ) - var parsed = ctx.util.tryParseJson(rows) - if (!parsed || !parsed.length || !parsed[0].value) return null - var inner = unwrapOAuthSentinel(ctx, parsed[0].value) - if (!inner) return null - var fields = readFields(inner) - var accessToken = (fields[1] && fields[1].type === 2) ? fields[1].data : null - var refreshToken = (fields[3] && fields[3].type === 2) ? fields[3].data : null - var expirySeconds = null - if (fields[4] && fields[4].type === 2) { - var ts = readFields(fields[4].data) - if (ts[1] && ts[1].type === 0) expirySeconds = ts[1].value + var dbPaths = [STATE_DB_V2, STATE_DB_V1] + for (var i = 0; i < dbPaths.length; i++) { + var dbPath = dbPaths[i] + try { + if (!ctx.host.fs.exists(dbPath)) { + continue + } + var rows = ctx.host.sqlite.query( + dbPath, + "SELECT value FROM ItemTable WHERE key = '" + OAUTH_TOKEN_KEY + "' LIMIT 1" + ) + var parsed = ctx.util.tryParseJson(rows) + if (!parsed || !parsed.length || !parsed[0].value) continue + var inner = unwrapOAuthSentinel(ctx, parsed[0].value) + if (!inner) continue + var fields = readFields(inner) + var accessToken = (fields[1] && fields[1].type === 2) ? fields[1].data : null + var refreshToken = (fields[3] && fields[3].type === 2) ? fields[3].data : null + var expirySeconds = null + if (fields[4] && fields[4].type === 2) { + var ts = readFields(fields[4].data) + if (ts[1] && ts[1].type === 0) expirySeconds = ts[1].value + } + if (!accessToken && !refreshToken) continue + return { accessToken: accessToken, refreshToken: refreshToken, expirySeconds: expirySeconds } + } catch (e) { + ctx.host.log.warn("failed to read unified oauth token from " + dbPath + ": " + String(e)) } - if (!accessToken && !refreshToken) return null - return { accessToken: accessToken, refreshToken: refreshToken, expirySeconds: expirySeconds } - } catch (e) { - ctx.host.log.warn("failed to read unified oauth token: " + String(e)) - return null } + return null } // --- Google OAuth token refresh --- diff --git a/plugins/antigravity/plugin.test.js b/plugins/antigravity/plugin.test.js index d09e7fce..c07eceff 100644 --- a/plugins/antigravity/plugin.test.js +++ b/plugins/antigravity/plugin.test.js @@ -108,6 +108,8 @@ function setupLsMock(ctx, discovery, responseBody) { } function setupSqliteMock(ctx, oauthEnvelopeB64) { + ctx.host.fs.writeText("~/Library/Application Support/Antigravity/User/globalStorage/state.vscdb", "dummy-sqlite") + ctx.host.fs.writeText("~/Library/Application Support/Antigravity IDE/User/globalStorage/state.vscdb", "dummy-sqlite") ctx.host.sqlite.query.mockImplementation((db, sql) => { if (sql.includes(OAUTH_TOKEN_KEY) && oauthEnvelopeB64) { return JSON.stringify([{ value: oauthEnvelopeB64 }]) @@ -696,6 +698,112 @@ describe("antigravity plugin", () => { expect(result.lines.length).toBeGreaterThan(0) }) + it("queries the v2 database path if it exists", async () => { + const ctx = makeCtx() + const futureExpiry = Math.floor(Date.now() / 1000) + 3600 + const protoB64 = makeOAuthSentinelB64(ctx, { accessToken: "ya29.v2-access", refreshToken: "1//refresh-token", expirySeconds: futureExpiry }) + + // Simulate v2 DB file existence + const v2Path = "~/Library/Application Support/Antigravity IDE/User/globalStorage/state.vscdb" + ctx.host.fs.writeText(v2Path, "dummy-sqlite") + + let queriedDbPath = null + ctx.host.sqlite.query.mockImplementation((db, sql) => { + queriedDbPath = db + if (sql.includes(OAUTH_TOKEN_KEY)) { + return JSON.stringify([{ value: protoB64 }]) + } + return "[]" + }) + ctx.host.ls.discover.mockReturnValue(null) + + ctx.host.http.request.mockImplementation((opts) => { + if (String(opts.url).includes("fetchAvailableModels")) { + return { status: 200, bodyText: JSON.stringify(makeCloudCodeResponse()) } + } + return { status: 500, bodyText: "" } + }) + + const plugin = await loadPlugin() + plugin.probe(ctx) + + expect(queriedDbPath).toBe(v2Path) + }) + + it("falls back to pre-v2 database path if v2 path does not exist", async () => { + const ctx = makeCtx() + const futureExpiry = Math.floor(Date.now() / 1000) + 3600 + const protoB64 = makeOAuthSentinelB64(ctx, { accessToken: "ya29.v1-access", refreshToken: "1//refresh-token", expirySeconds: futureExpiry }) + + // Do NOT write to v2 path, so it doesn't exist. Simulate v1 DB file existence. + const v1Path = "~/Library/Application Support/Antigravity/User/globalStorage/state.vscdb" + ctx.host.fs.writeText(v1Path, "dummy-sqlite") + + let queriedDbPath = null + ctx.host.sqlite.query.mockImplementation((db, sql) => { + queriedDbPath = db + if (sql.includes(OAUTH_TOKEN_KEY)) { + return JSON.stringify([{ value: protoB64 }]) + } + return "[]" + }) + ctx.host.ls.discover.mockReturnValue(null) + + ctx.host.http.request.mockImplementation((opts) => { + if (String(opts.url).includes("fetchAvailableModels")) { + return { status: 200, bodyText: JSON.stringify(makeCloudCodeResponse()) } + } + return { status: 500, bodyText: "" } + }) + + const plugin = await loadPlugin() + plugin.probe(ctx) + + expect(queriedDbPath).toBe(v1Path) + }) + + it("cascades and falls back to pre-v2 database path if v2 path exists but is corrupt or has no token", async () => { + const ctx = makeCtx() + const futureExpiry = Math.floor(Date.now() / 1000) + 3600 + const protoB64 = makeOAuthSentinelB64(ctx, { accessToken: "ya29.v1-fallback-access", refreshToken: "1//refresh-token", expirySeconds: futureExpiry }) + + const v1Path = "~/Library/Application Support/Antigravity/User/globalStorage/state.vscdb" + const v2Path = "~/Library/Application Support/Antigravity IDE/User/globalStorage/state.vscdb" + + // Simulate both files existing on disk + ctx.host.fs.writeText(v1Path, "valid-db") + ctx.host.fs.writeText(v2Path, "corrupt-db") + + const queriedPaths = [] + ctx.host.sqlite.query.mockImplementation((db, sql) => { + queriedPaths.push(db) + if (db === v2Path) { + // Simulate SQLite query failure/throw or returning empty rows + throw new Error("database is locked or corrupt") + } + if (db === v1Path && sql.includes(OAUTH_TOKEN_KEY)) { + return JSON.stringify([{ value: protoB64 }]) + } + return "[]" + }) + ctx.host.ls.discover.mockReturnValue(null) + + ctx.host.http.request.mockImplementation((opts) => { + if (String(opts.url).includes("fetchAvailableModels")) { + return { status: 200, bodyText: JSON.stringify(makeCloudCodeResponse()) } + } + return { status: 500, bodyText: "" } + }) + + const plugin = await loadPlugin() + const result = plugin.probe(ctx) + + // Should have queried both paths + expect(queriedPaths).toContain(v2Path) + expect(queriedPaths).toContain(v1Path) + expect(result.lines.length).toBeGreaterThan(0) + }) + it("throws when unified oauth envelope is missing and no cache", async () => { const ctx = makeCtx() setupSqliteMock(ctx, null) From ae527e9b2246c0421b6ea1d349f16bae257ea0c3 Mon Sep 17 00:00:00 2001 From: kvanzuijlen <8818390+kvanzuijlen@users.noreply.github.com> Date: Mon, 25 May 2026 01:31:15 +0200 Subject: [PATCH 2/3] fix(antigravity): look for antigravity-ide marker when ide is running --- plugins/antigravity/plugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/antigravity/plugin.js b/plugins/antigravity/plugin.js index 3a228ff6..356f3800 100644 --- a/plugins/antigravity/plugin.js +++ b/plugins/antigravity/plugin.js @@ -197,7 +197,7 @@ function discoverLs(ctx) { return ctx.host.ls.discover({ processName: "language_server_macos", - markers: ["antigravity"], + markers: ["antigravity", "antigravity-ide"], csrfFlag: "--csrf_token", portFlag: "--extension_server_port", }) From ff48f556f4703e10c7d38875fc17630ab77f70a1 Mon Sep 17 00:00:00 2001 From: kvanzuijlen <8818390+kvanzuijlen@users.noreply.github.com> Date: Mon, 25 May 2026 14:06:40 +0200 Subject: [PATCH 3/3] docs(antigravity): document new db path --- docs/providers/antigravity.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/providers/antigravity.md b/docs/providers/antigravity.md index 7ba81dea..50865adb 100644 --- a/docs/providers/antigravity.md +++ b/docs/providers/antigravity.md @@ -158,7 +158,9 @@ Interestingly, non-Google models (Claude, GPT-OSS) are proxied through Codeium/W Antigravity stores auth credentials in a VS Code-compatible state database. -- **Path:** `~/Library/Application Support/Antigravity/User/globalStorage/state.vscdb` +- **Paths (tried in order):** + - `~/Library/Application Support/Antigravity IDE/User/globalStorage/state.vscdb` (v2 - renamed app) + - `~/Library/Application Support/Antigravity/User/globalStorage/state.vscdb` (v1 - pre-rename, retained for backwards compatibility) - **Table:** `ItemTable` (`key` TEXT, `value` TEXT) ### antigravityUnifiedStateSync.oauthToken (sentinel envelope → protobuf)