From ee3d88af9d09673f363623ea756b231d5870c016 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 6 Mar 2026 10:59:41 +0900 Subject: [PATCH] refactor(installer): remove dead Antigravity auth plugin code The installer was writing Antigravity provider config and calling a no-op addAuthPlugins function. Since opencode-antigravity-auth is no longer auto-installed and OpenCode supports native Google/Gemini auth, all Antigravity-related installer code is dead. Gemini detection now checks for native google provider instead. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- src/cli/cli-installer.test.ts | 14 +- src/cli/cli-installer.ts | 28 +-- src/cli/config-manager.test.ts | 72 +----- src/cli/config-manager.ts | 3 - .../add-provider-config.test.ts | 205 ---------------- src/cli/config-manager/add-provider-config.ts | 82 ------- .../antigravity-provider-configuration.ts | 64 ----- src/cli/config-manager/auth-plugins.test.ts | 230 ------------------ src/cli/config-manager/auth-plugins.ts | 140 ----------- .../config-manager/detect-current-config.ts | 3 +- .../config-manager/jsonc-provider-editor.ts | 11 - src/cli/tui-installer.ts | 24 +- 12 files changed, 7 insertions(+), 869 deletions(-) delete mode 100644 src/cli/config-manager/add-provider-config.test.ts delete mode 100644 src/cli/config-manager/add-provider-config.ts delete mode 100644 src/cli/config-manager/antigravity-provider-configuration.ts delete mode 100644 src/cli/config-manager/auth-plugins.test.ts delete mode 100644 src/cli/config-manager/auth-plugins.ts delete mode 100644 src/cli/config-manager/jsonc-provider-editor.ts diff --git a/src/cli/cli-installer.test.ts b/src/cli/cli-installer.test.ts index 2320d9511..5d5fd0ca5 100644 --- a/src/cli/cli-installer.test.ts +++ b/src/cli/cli-installer.test.ts @@ -21,19 +21,9 @@ describe("runCliInstaller", () => { console.error = originalConsoleError }) - it("runs auth and provider setup steps when openai or copilot are enabled without gemini", async () => { + it("completes installation without auth plugin or provider config steps", async () => { //#given - const addAuthPluginsSpy = spyOn(configManager, "addAuthPlugins").mockResolvedValue({ - success: true, - configPath: "/tmp/opencode.jsonc", - }) - const addProviderConfigSpy = spyOn(configManager, "addProviderConfig").mockReturnValue({ - success: true, - configPath: "/tmp/opencode.jsonc", - }) const restoreSpies = [ - addAuthPluginsSpy, - addProviderConfigSpy, spyOn(configManager, "detectCurrentConfig").mockReturnValue({ isInstalled: false, hasClaude: false, @@ -73,8 +63,6 @@ describe("runCliInstaller", () => { //#then expect(result).toBe(0) - expect(addAuthPluginsSpy).toHaveBeenCalledTimes(1) - expect(addProviderConfigSpy).toHaveBeenCalledTimes(1) for (const spy of restoreSpies) { spy.mockRestore() diff --git a/src/cli/cli-installer.ts b/src/cli/cli-installer.ts index cdcbfc4d4..278c07df0 100644 --- a/src/cli/cli-installer.ts +++ b/src/cli/cli-installer.ts @@ -1,9 +1,7 @@ import color from "picocolors" import type { InstallArgs } from "./types" import { - addAuthPlugins, addPluginToOpenCodeConfig, - addProviderConfig, detectCurrentConfig, getOpenCodeVersion, isOpenCodeInstalled, @@ -45,7 +43,7 @@ export async function runCliInstaller(args: InstallArgs, version: string): Promi printHeader(isUpdate) - const totalSteps = 6 + const totalSteps = 4 let step = 1 printStep(step++, totalSteps, "Checking OpenCode installation...") @@ -77,28 +75,6 @@ export async function runCliInstaller(args: InstallArgs, version: string): Promi `Plugin ${isUpdate ? "verified" : "added"} ${SYMBOLS.arrow} ${color.dim(pluginResult.configPath)}`, ) - const needsProviderSetup = config.hasGemini || config.hasOpenAI || config.hasCopilot - - if (needsProviderSetup) { - printStep(step++, totalSteps, "Adding auth plugins...") - const authResult = await addAuthPlugins(config) - if (!authResult.success) { - printError(`Failed: ${authResult.error}`) - return 1 - } - printSuccess(`Auth plugins configured ${SYMBOLS.arrow} ${color.dim(authResult.configPath)}`) - - printStep(step++, totalSteps, "Adding provider configurations...") - const providerResult = addProviderConfig(config) - if (!providerResult.success) { - printError(`Failed: ${providerResult.error}`) - return 1 - } - printSuccess(`Providers configured ${SYMBOLS.arrow} ${color.dim(providerResult.configPath)}`) - } else { - step += 2 - } - printStep(step++, totalSteps, "Writing oh-my-opencode configuration...") const omoResult = writeOmoConfig(config) if (!omoResult.success) { @@ -156,7 +132,7 @@ export async function runCliInstaller(args: InstallArgs, version: string): Promi printBox( `Run ${color.cyan("opencode auth login")} and select your provider:\n` + (config.hasClaude ? ` ${SYMBOLS.bullet} Anthropic ${color.gray("→ Claude Pro/Max")}\n` : "") + - (config.hasGemini ? ` ${SYMBOLS.bullet} Google ${color.gray("→ OAuth with Antigravity")}\n` : "") + + (config.hasGemini ? ` ${SYMBOLS.bullet} Google ${color.gray("→ Gemini")}\n` : "") + (config.hasCopilot ? ` ${SYMBOLS.bullet} GitHub ${color.gray("→ Copilot")}` : ""), "Authenticate Your Providers", ) diff --git a/src/cli/config-manager.test.ts b/src/cli/config-manager.test.ts index be28c5e6d..d98576812 100644 --- a/src/cli/config-manager.test.ts +++ b/src/cli/config-manager.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test, mock, afterEach } from "bun:test" -import { ANTIGRAVITY_PROVIDER_CONFIG, getPluginNameWithVersion, fetchNpmDistTags, generateOmoConfig } from "./config-manager" +import { getPluginNameWithVersion, fetchNpmDistTags, generateOmoConfig } from "./config-manager" import type { InstallConfig } from "./types" describe("getPluginNameWithVersion", () => { @@ -169,76 +169,6 @@ describe("fetchNpmDistTags", () => { }) }) -describe("config-manager ANTIGRAVITY_PROVIDER_CONFIG", () => { - test("all models include full spec (limit + modalities + Antigravity label)", () => { - const google = (ANTIGRAVITY_PROVIDER_CONFIG as any).google - expect(google).toBeTruthy() - - const models = google.models as Record - expect(models).toBeTruthy() - - const required = [ - "antigravity-gemini-3.1-pro", - "antigravity-gemini-3-flash", - "antigravity-claude-sonnet-4-6", - "antigravity-claude-sonnet-4-6-thinking", - "antigravity-claude-opus-4-5-thinking", - ] - - for (const key of required) { - const model = models[key] - expect(model).toBeTruthy() - expect(typeof model.name).toBe("string") - expect(model.name.includes("(Antigravity)")).toBe(true) - - expect(model.limit).toBeTruthy() - expect(typeof model.limit.context).toBe("number") - expect(typeof model.limit.output).toBe("number") - - expect(model.modalities).toBeTruthy() - expect(Array.isArray(model.modalities.input)).toBe(true) - expect(Array.isArray(model.modalities.output)).toBe(true) - } - }) - - test("Gemini models have variant definitions", () => { - // #given the antigravity provider config - const models = (ANTIGRAVITY_PROVIDER_CONFIG as any).google.models as Record - - // #when checking Gemini Pro variants - const pro = models["antigravity-gemini-3.1-pro"] - // #then should have low and high variants - expect(pro.variants).toBeTruthy() - expect(pro.variants.low).toBeTruthy() - expect(pro.variants.high).toBeTruthy() - - // #when checking Gemini Flash variants - const flash = models["antigravity-gemini-3-flash"] - // #then should have minimal, low, medium, high variants - expect(flash.variants).toBeTruthy() - expect(flash.variants.minimal).toBeTruthy() - expect(flash.variants.low).toBeTruthy() - expect(flash.variants.medium).toBeTruthy() - expect(flash.variants.high).toBeTruthy() - }) - - test("Claude thinking models have variant definitions", () => { - // #given the antigravity provider config - const models = (ANTIGRAVITY_PROVIDER_CONFIG as any).google.models as Record - - // #when checking Claude thinking variants - const sonnetThinking = models["antigravity-claude-sonnet-4-6-thinking"] - const opusThinking = models["antigravity-claude-opus-4-5-thinking"] - - // #then both should have low and max variants - for (const model of [sonnetThinking, opusThinking]) { - expect(model.variants).toBeTruthy() - expect(model.variants.low).toBeTruthy() - expect(model.variants.max).toBeTruthy() - } - }) -}) - describe("generateOmoConfig - model fallback system", () => { test("uses github-copilot sonnet fallback when only copilot available", () => { // #given user has only copilot (no max plan) diff --git a/src/cli/config-manager.ts b/src/cli/config-manager.ts index cfb6a9178..73a81ad6a 100644 --- a/src/cli/config-manager.ts +++ b/src/cli/config-manager.ts @@ -14,9 +14,6 @@ export { writeOmoConfig } from "./config-manager/write-omo-config" export { isOpenCodeInstalled, getOpenCodeVersion } from "./config-manager/opencode-binary" -export { fetchLatestVersion, addAuthPlugins } from "./config-manager/auth-plugins" -export { ANTIGRAVITY_PROVIDER_CONFIG } from "./config-manager/antigravity-provider-configuration" -export { addProviderConfig } from "./config-manager/add-provider-config" export { detectCurrentConfig } from "./config-manager/detect-current-config" export type { BunInstallResult } from "./config-manager/bun-install" diff --git a/src/cli/config-manager/add-provider-config.test.ts b/src/cli/config-manager/add-provider-config.test.ts deleted file mode 100644 index 53a7cc335..000000000 --- a/src/cli/config-manager/add-provider-config.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { describe, expect, it } from "bun:test" -import { modifyProviderInJsonc } from "./jsonc-provider-editor" -import { parseJsonc } from "../../shared/jsonc-parser" - -describe("modifyProviderInJsonc", () => { - describe("Test 1: Basic JSONC with existing provider", () => { - it("replaces provider value, preserves comments and other keys", () => { - // given - const content = `{ - // my config - "provider": { "openai": {} }, - "plugin": ["foo"] -}` - const newProviderValue = { google: { name: "Google" } } - - // when - const result = modifyProviderInJsonc(content, newProviderValue) - - // then - expect(result).toContain('"google"') - expect(result).toContain('"plugin": ["foo"]') - expect(result).toContain('// my config') - - // Post-write validation - const parsed = parseJsonc>(result) - expect(parsed).toHaveProperty('plugin') - expect(parsed).toHaveProperty('provider') - }) - }) - - describe("Test 2: Comment containing '}' inside provider block", () => { - it("must NOT corrupt file", () => { - // given - const content = `{ - "provider": { - // } this brace should be ignored - "openai": {} - }, - "other": 1 -}` - const newProviderValue = { google: { name: "Google" } } - - // when - const result = modifyProviderInJsonc(content, newProviderValue) - - // then - expect(result).toContain('"other"') - - // Post-write validation - const parsed = parseJsonc>(result) - expect(parsed).toHaveProperty('other') - expect(parsed.other).toBe(1) - }) - }) - - describe("Test 3: Comment containing '\"provider\"' before real key", () => { - it("must NOT match wrong location", () => { - // given - const content = `{ - // "provider": { "example": true } - "provider": { "openai": {} }, - "other": 1 -}` - const newProviderValue = { google: { name: "Google" } } - - // when - const result = modifyProviderInJsonc(content, newProviderValue) - - // then - expect(result).toContain('"other"') - - // Post-write validation - const parsed = parseJsonc>(result) - expect(parsed).toHaveProperty('other') - expect(parsed.other).toBe(1) - expect(parsed.provider).toHaveProperty('google') - }) - }) - - describe("Test 4: Comment containing '{' inside provider", () => { - it("must NOT mess up depth", () => { - // given - const content = `{ - "provider": { - // { unmatched brace in comment - "openai": {} - }, - "other": 1 -}` - const newProviderValue = { google: { name: "Google" } } - - // when - const result = modifyProviderInJsonc(content, newProviderValue) - - // then - expect(result).toContain('"other"') - - // Post-write validation - const parsed = parseJsonc>(result) - expect(parsed).toHaveProperty('other') - expect(parsed.other).toBe(1) - }) - }) - - describe("Test 5: No existing provider key", () => { - it("inserts provider without corrupting", () => { - // given - const content = `{ - // config comment - "plugin": ["foo"] -}` - const newProviderValue = { google: { name: "Google" } } - - // when - const result = modifyProviderInJsonc(content, newProviderValue) - - // then - expect(result).toContain('"provider"') - expect(result).toContain('"plugin"') - expect(result).toContain('foo') - expect(result).toContain('// config comment') - - // Post-write validation - const parsed = parseJsonc>(result) - expect(parsed).toHaveProperty('provider') - expect(parsed).toHaveProperty('plugin') - expect(parsed.plugin).toEqual(['foo']) - }) - }) - - describe("Test 6: String value exactly 'provider' before real key", () => { - it("must NOT match wrong location", () => { - // given - const content = `{ - "note": "provider", - "provider": { "openai": {} }, - "other": 1 -}` - const newProviderValue = { google: { name: "Google" } } - - // when - const result = modifyProviderInJsonc(content, newProviderValue) - - // then - expect(result).toContain('"other"') - expect(result).toContain('"note": "provider"') - - // Post-write validation - const parsed = parseJsonc>(result) - expect(parsed).toHaveProperty('other') - expect(parsed.other).toBe(1) - expect(parsed.note).toBe('provider') - }) - }) - - describe("Test 7: Post-write validation", () => { - it("result file must be valid JSONC for all cases", () => { - // Test Case 1 - const content1 = `{ - "provider": { "openai": {} }, - "plugin": ["foo"] -}` - const result1 = modifyProviderInJsonc(content1, { google: {} }) - expect(() => parseJsonc(result1)).not.toThrow() - - // Test Case 2 - const content2 = `{ - "provider": { - // } comment - "openai": {} - } -}` - const result2 = modifyProviderInJsonc(content2, { google: {} }) - expect(() => parseJsonc(result2)).not.toThrow() - - // Test Case 3 - const content3 = `{ - "plugin": ["foo"] -}` - const result3 = modifyProviderInJsonc(content3, { google: {} }) - expect(() => parseJsonc(result3)).not.toThrow() - }) - }) - - describe("Test 8: Trailing commas preserved", () => { - it("file is valid JSONC with trailing commas", () => { - // given - const content = `{ - "provider": { "openai": {}, }, - "plugin": ["foo",], -}` - const newProviderValue = { google: { name: "Google" } } - - // when - const result = modifyProviderInJsonc(content, newProviderValue) - - // then - expect(() => parseJsonc(result)).not.toThrow() - - const parsed = parseJsonc>(result) - expect(parsed).toHaveProperty('plugin') - expect(parsed.plugin).toEqual(['foo']) - }) - }) -}) diff --git a/src/cli/config-manager/add-provider-config.ts b/src/cli/config-manager/add-provider-config.ts deleted file mode 100644 index daef30cc2..000000000 --- a/src/cli/config-manager/add-provider-config.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { readFileSync, writeFileSync, copyFileSync } from "node:fs" -import type { ConfigMergeResult, InstallConfig } from "../types" -import { getConfigDir } from "./config-context" -import { ensureConfigDirectoryExists } from "./ensure-config-directory-exists" -import { formatErrorWithSuggestion } from "./format-error-with-suggestion" -import { detectConfigFormat } from "./opencode-config-format" -import { parseOpenCodeConfigFileWithError, type OpenCodeConfig } from "./parse-opencode-config-file" -import { ANTIGRAVITY_PROVIDER_CONFIG } from "./antigravity-provider-configuration" -import { modifyProviderInJsonc } from "./jsonc-provider-editor" -import { parseJsonc } from "../../shared/jsonc-parser" - -export function addProviderConfig(config: InstallConfig): ConfigMergeResult { - try { - ensureConfigDirectoryExists() - } catch (err) { - return { - success: false, - configPath: getConfigDir(), - error: formatErrorWithSuggestion(err, "create config directory"), - } - } - - const { format, path } = detectConfigFormat() - - try { - let existingConfig: OpenCodeConfig | null = null - if (format !== "none") { - const parseResult = parseOpenCodeConfigFileWithError(path) - if (parseResult.error && !parseResult.config) { - return { - success: false, - configPath: path, - error: `Failed to parse config file: ${parseResult.error}`, - } - } - existingConfig = parseResult.config - } - - const newConfig = { ...(existingConfig ?? {}) } - const providers = (newConfig.provider ?? {}) as Record - - if (config.hasGemini) { - providers.google = ANTIGRAVITY_PROVIDER_CONFIG.google - } - - if (Object.keys(providers).length > 0) { - newConfig.provider = providers - } - - if (format === "jsonc") { - const content = readFileSync(path, "utf-8") - - // Backup original file - copyFileSync(path, `${path}.bak`) - - const providerValue = (newConfig.provider ?? {}) as Record - const newContent = modifyProviderInJsonc(content, providerValue) - - // Post-write validation - try { - parseJsonc(newContent) - } catch (error) { - return { - success: false, - configPath: path, - error: `Generated JSONC is invalid: ${error instanceof Error ? error.message : String(error)}`, - } - } - - writeFileSync(path, newContent) - } else { - writeFileSync(path, JSON.stringify(newConfig, null, 2) + "\n") - } - return { success: true, configPath: path } - } catch (err) { - return { - success: false, - configPath: path, - error: formatErrorWithSuggestion(err, "add provider config"), - } - } -} diff --git a/src/cli/config-manager/antigravity-provider-configuration.ts b/src/cli/config-manager/antigravity-provider-configuration.ts deleted file mode 100644 index 6d847ac5d..000000000 --- a/src/cli/config-manager/antigravity-provider-configuration.ts +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Antigravity Provider Configuration - * - * IMPORTANT: Model names MUST use `antigravity-` prefix for stability. - * - * Since opencode-antigravity-auth v1.3.0, models use a variant system: - * - `antigravity-gemini-3.1-pro` with variants: low, high - * - `antigravity-gemini-3-flash` with variants: minimal, low, medium, high - * - * Legacy tier-suffixed names (e.g., `antigravity-gemini-3.1-pro-high`) still work - * but variants are the recommended approach. - * - * @see https://github.com/NoeFabris/opencode-antigravity-auth#models - */ -export const ANTIGRAVITY_PROVIDER_CONFIG = { - google: { - name: "Google", - models: { - "antigravity-gemini-3.1-pro": { - name: "Gemini 3 Pro (Antigravity)", - limit: { context: 1048576, output: 65535 }, - modalities: { input: ["text", "image", "pdf"], output: ["text"] }, - variants: { - low: { thinkingLevel: "low" }, - high: { thinkingLevel: "high" }, - }, - }, - "antigravity-gemini-3-flash": { - name: "Gemini 3 Flash (Antigravity)", - limit: { context: 1048576, output: 65536 }, - modalities: { input: ["text", "image", "pdf"], output: ["text"] }, - variants: { - minimal: { thinkingLevel: "minimal" }, - low: { thinkingLevel: "low" }, - medium: { thinkingLevel: "medium" }, - high: { thinkingLevel: "high" }, - }, - }, - "antigravity-claude-sonnet-4-6": { - name: "Claude Sonnet 4.6 (Antigravity)", - limit: { context: 200000, output: 64000 }, - modalities: { input: ["text", "image", "pdf"], output: ["text"] }, - }, - "antigravity-claude-sonnet-4-6-thinking": { - name: "Claude Sonnet 4.6 Thinking (Antigravity)", - limit: { context: 200000, output: 64000 }, - modalities: { input: ["text", "image", "pdf"], output: ["text"] }, - variants: { - low: { thinkingConfig: { thinkingBudget: 8192 } }, - max: { thinkingConfig: { thinkingBudget: 32768 } }, - }, - }, - "antigravity-claude-opus-4-5-thinking": { - name: "Claude Opus 4.5 Thinking (Antigravity)", - limit: { context: 200000, output: 64000 }, - modalities: { input: ["text", "image", "pdf"], output: ["text"] }, - variants: { - low: { thinkingConfig: { thinkingBudget: 8192 } }, - max: { thinkingConfig: { thinkingBudget: 32768 } }, - }, - }, - }, - }, -} diff --git a/src/cli/config-manager/auth-plugins.test.ts b/src/cli/config-manager/auth-plugins.test.ts deleted file mode 100644 index c7d5649cf..000000000 --- a/src/cli/config-manager/auth-plugins.test.ts +++ /dev/null @@ -1,230 +0,0 @@ -import { describe, expect, it, beforeEach, afterEach, spyOn } from "bun:test" -import { tmpdir } from "node:os" -import { join } from "node:path" -import { writeFileSync, readFileSync, existsSync, rmSync, mkdirSync } from "node:fs" -import { parseJsonc } from "../../shared/jsonc-parser" -import type { InstallConfig } from "../types" -import { resetConfigContext } from "./config-context" - -let testConfigPath: string -let testConfigDir: string -let testCounter = 0 -let fetchVersionSpy: unknown - -beforeEach(async () => { - testCounter++ - testConfigDir = join(tmpdir(), `test-opencode-${Date.now()}-${testCounter}`) - testConfigPath = join(testConfigDir, "opencode.jsonc") - mkdirSync(testConfigDir, { recursive: true }) - - process.env.OPENCODE_CONFIG_DIR = testConfigDir - resetConfigContext() - - const module = await import("./auth-plugins") - fetchVersionSpy = spyOn(module, "fetchLatestVersion").mockResolvedValue("1.2.3") -}) - -afterEach(() => { - try { - rmSync(testConfigDir, { recursive: true, force: true }) - } catch {} -}) - -const testConfig: InstallConfig = { - hasClaude: false, - isMax20: false, - hasOpenAI: false, - hasGemini: true, - hasCopilot: false, - hasOpencodeZen: false, - hasZaiCodingPlan: false, - hasKimiForCoding: false, -} - -describe("addAuthPlugins", () => { - describe("Test 1: JSONC with commented plugin line", () => { - it("preserves comment, does NOT add antigravity plugin", async () => { - const content = `{ - // "plugin": ["old-plugin"] - "plugin": ["existing-plugin"], - "provider": {} -}` - writeFileSync(testConfigPath, content, "utf-8") - - const { addAuthPlugins } = await import("./auth-plugins") - const result = await addAuthPlugins(testConfig) - - expect(result.success).toBe(true) - - const newContent = readFileSync(result.configPath, "utf-8") - expect(newContent).toContain('// "plugin": ["old-plugin"]') - expect(newContent).toContain('existing-plugin') - // antigravity plugin should NOT be auto-added anymore - expect(newContent).not.toContain('opencode-antigravity-auth') - - const parsed = parseJsonc>(newContent) - const plugins = parsed.plugin as string[] - expect(plugins).toContain('existing-plugin') - expect(plugins.some((p) => p.startsWith('opencode-antigravity-auth'))).toBe(false) - }) - }) - - describe("Test 2: Plugin array already contains antigravity", () => { - it("preserves existing antigravity, does not add another", async () => { - const content = `{ - "plugin": ["existing-plugin", "opencode-antigravity-auth"], - "provider": {} -}` - writeFileSync(testConfigPath, content, "utf-8") - - const { addAuthPlugins } = await import("./auth-plugins") - const result = await addAuthPlugins(testConfig) - - expect(result.success).toBe(true) - - const newContent = readFileSync(testConfigPath, "utf-8") - const parsed = parseJsonc>(newContent) - const plugins = parsed.plugin as string[] - - const antigravityCount = plugins.filter((p) => p.startsWith('opencode-antigravity-auth')).length - expect(antigravityCount).toBe(1) - expect(plugins).toContain('existing-plugin') - }) - }) - - describe("Test 3: Backup created before write", () => { - it("creates .bak file", async () => { - const originalContent = `{ - "plugin": ["existing-plugin"], - "provider": {} -}` - writeFileSync(testConfigPath, originalContent, "utf-8") - readFileSync(testConfigPath, "utf-8") - - const { addAuthPlugins } = await import("./auth-plugins") - const result = await addAuthPlugins(testConfig) - - expect(result.success).toBe(true) - expect(existsSync(`${result.configPath}.bak`)).toBe(true) - - const backupContent = readFileSync(`${result.configPath}.bak`, "utf-8") - expect(backupContent).toBe(originalContent) - }) - }) - - describe("Test 4: Comment with } character", () => { - it("preserves comments with special characters", async () => { - const content = `{ - // This comment has } special characters - "plugin": ["existing-plugin"], - "provider": {} -}` - writeFileSync(testConfigPath, content, "utf-8") - - const { addAuthPlugins } = await import("./auth-plugins") - const result = await addAuthPlugins(testConfig) - - expect(result.success).toBe(true) - - const newContent = readFileSync(testConfigPath, "utf-8") - expect(newContent).toContain('// This comment has } special characters') - - expect(() => parseJsonc(newContent)).not.toThrow() - }) - }) - - describe("Test 5: Comment containing 'plugin' string", () => { - it("must NOT match comment location", async () => { - const content = `{ - // "plugin": ["fake"] - "plugin": ["existing-plugin"], - "provider": {} -}` - writeFileSync(testConfigPath, content, "utf-8") - - const { addAuthPlugins } = await import("./auth-plugins") - const result = await addAuthPlugins(testConfig) - - expect(result.success).toBe(true) - - const newContent = readFileSync(testConfigPath, "utf-8") - expect(newContent).toContain('// "plugin": ["fake"]') - - const parsed = parseJsonc>(newContent) - const plugins = parsed.plugin as string[] - expect(plugins).toContain('existing-plugin') - expect(plugins).not.toContain('fake') - }) - }) - - describe("Test 6: No existing plugin array", () => { - it("creates empty plugin array when none exists, does NOT add antigravity", async () => { - const content = `{ - "provider": {} -}` - writeFileSync(testConfigPath, content, "utf-8") - - const { addAuthPlugins } = await import("./auth-plugins") - const result = await addAuthPlugins(testConfig) - - expect(result.success).toBe(true) - - const newContent = readFileSync(result.configPath, "utf-8") - - const parsed = parseJsonc>(newContent) - expect(parsed).toHaveProperty('plugin') - const plugins = parsed.plugin as string[] - // antigravity plugin should NOT be auto-added anymore - expect(plugins.some((p) => p.startsWith('opencode-antigravity-auth'))).toBe(false) - expect(plugins.length).toBe(0) - }) - }) - - describe("Test 7: Post-write validation ensures valid JSONC", () => { - it("result file must be valid JSONC", async () => { - const content = `{ - "plugin": ["existing-plugin"], - "provider": {} -}` - writeFileSync(testConfigPath, content, "utf-8") - - const { addAuthPlugins } = await import("./auth-plugins") - const result = await addAuthPlugins(testConfig) - - expect(result.success).toBe(true) - - const newContent = readFileSync(testConfigPath, "utf-8") - expect(() => parseJsonc(newContent)).not.toThrow() - - const parsed = parseJsonc>(newContent) - expect(parsed).toHaveProperty('plugin') - expect(parsed).toHaveProperty('provider') - }) - }) - - describe("Test 8: Multiple plugins in array", () => { - it("preserves existing plugins, does NOT add antigravity", async () => { - const content = `{ - "plugin": ["plugin-1", "plugin-2", "plugin-3"], - "provider": {} -}` - writeFileSync(testConfigPath, content, "utf-8") - - const { addAuthPlugins } = await import("./auth-plugins") - const result = await addAuthPlugins(testConfig) - - expect(result.success).toBe(true) - - const newContent = readFileSync(result.configPath, "utf-8") - const parsed = parseJsonc>(newContent) - const plugins = parsed.plugin as string[] - - expect(plugins).toContain('plugin-1') - expect(plugins).toContain('plugin-2') - expect(plugins).toContain('plugin-3') - // antigravity plugin should NOT be auto-added anymore - expect(plugins.some((p) => p.startsWith('opencode-antigravity-auth'))).toBe(false) - expect(plugins.length).toBe(3) - }) - }) -}) diff --git a/src/cli/config-manager/auth-plugins.ts b/src/cli/config-manager/auth-plugins.ts deleted file mode 100644 index c68cd4e99..000000000 --- a/src/cli/config-manager/auth-plugins.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { readFileSync, writeFileSync, copyFileSync, existsSync } from "node:fs" -import { modify, applyEdits } from "jsonc-parser" -import type { ConfigMergeResult, InstallConfig } from "../types" -import { getConfigDir } from "./config-context" -import { ensureConfigDirectoryExists } from "./ensure-config-directory-exists" -import { formatErrorWithSuggestion } from "./format-error-with-suggestion" -import { detectConfigFormat } from "./opencode-config-format" -import { parseOpenCodeConfigFileWithError, type OpenCodeConfig } from "./parse-opencode-config-file" -import { parseJsonc } from "../../shared/jsonc-parser" - -export async function fetchLatestVersion(packageName: string): Promise { - try { - const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`) - if (!res.ok) return null - const data = (await res.json()) as { version: string } - return data.version - } catch { - return null - } -} - -export async function addAuthPlugins(config: InstallConfig): Promise { - try { - ensureConfigDirectoryExists() - } catch (err) { - return { - success: false, - configPath: getConfigDir(), - error: formatErrorWithSuggestion(err, "create config directory"), - } - } - - const { format, path } = detectConfigFormat() - const backupPath = `${path}.bak` - - try { - let existingConfig: OpenCodeConfig | null = null - if (format !== "none") { - const parseResult = parseOpenCodeConfigFileWithError(path) - if (parseResult.error && !parseResult.config) { - return { - success: false, - configPath: path, - error: `Failed to parse config file: ${parseResult.error}`, - } - } - existingConfig = parseResult.config - } - - const rawPlugins = existingConfig?.plugin - const plugins: string[] = Array.isArray(rawPlugins) ? rawPlugins : [] - - // Note: opencode-antigravity-auth plugin auto-installation has been removed - // Users can manually add auth plugins if needed - - const newConfig = { ...(existingConfig ?? {}), plugin: plugins } - - if (format !== "none" && existsSync(path)) { - copyFileSync(path, backupPath) - } - - if (format === "jsonc") { - const content = readFileSync(path, "utf-8") - - const newContent = applyEdits( - content, - modify(content, ["plugin"], plugins, { - formattingOptions: { tabSize: 2, insertSpaces: true }, - }) - ) - - try { - parseJsonc(newContent) - } catch (error) { - if (existsSync(backupPath)) { - copyFileSync(backupPath, path) - } - throw new Error(`Generated JSONC is invalid: ${error instanceof Error ? error.message : String(error)}`) - } - - try { - writeFileSync(path, newContent) - } catch (error) { - const hasBackup = existsSync(backupPath) - try { - if (hasBackup) { - copyFileSync(backupPath, path) - } - } catch (restoreError) { - return { - success: false, - configPath: path, - error: `Failed to write config file, and restore from backup failed: ${String(error)}; restore error: ${String(restoreError)}`, - } - } - - return { - success: false, - configPath: path, - error: hasBackup - ? `Failed to write config file. Restored from backup: ${String(error)}` - : `Failed to write config file. No backup was available: ${String(error)}`, - } - } - } else { - const nextContent = JSON.stringify(newConfig, null, 2) + "\n" - try { - writeFileSync(path, nextContent) - } catch (error) { - const hasBackup = existsSync(backupPath) - try { - if (hasBackup) { - copyFileSync(backupPath, path) - } - } catch (restoreError) { - return { - success: false, - configPath: path, - error: `Failed to write config file, and restore from backup failed: ${String(error)}; restore error: ${String(restoreError)}`, - } - } - - return { - success: false, - configPath: path, - error: hasBackup - ? `Failed to write config file. Restored from backup: ${String(error)}` - : `Failed to write config file. No backup was available: ${String(error)}`, - } - } - } - return { success: true, configPath: path } - } catch (err) { - return { - success: false, - configPath: path, - error: formatErrorWithSuggestion(err, "add auth plugins to config"), - } - } -} diff --git a/src/cli/config-manager/detect-current-config.ts b/src/cli/config-manager/detect-current-config.ts index fd7855336..c61924101 100644 --- a/src/cli/config-manager/detect-current-config.ts +++ b/src/cli/config-manager/detect-current-config.ts @@ -66,7 +66,8 @@ export function detectCurrentConfig(): DetectedConfig { return result } - result.hasGemini = plugins.some((p) => p.startsWith("opencode-antigravity-auth")) + const providers = openCodeConfig.provider as Record | undefined + result.hasGemini = providers ? "google" in providers : false const { hasOpenAI, hasOpencodeZen, hasZaiCodingPlan, hasKimiForCoding } = detectProvidersFromOmoConfig() result.hasOpenAI = hasOpenAI diff --git a/src/cli/config-manager/jsonc-provider-editor.ts b/src/cli/config-manager/jsonc-provider-editor.ts deleted file mode 100644 index 3f53c6d13..000000000 --- a/src/cli/config-manager/jsonc-provider-editor.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { modify, applyEdits } from "jsonc-parser" - -export function modifyProviderInJsonc( - content: string, - newProviderValue: Record -): string { - const edits = modify(content, ["provider"], newProviderValue, { - formattingOptions: { tabSize: 2, insertSpaces: true }, - }) - return applyEdits(content, edits) -} diff --git a/src/cli/tui-installer.ts b/src/cli/tui-installer.ts index a2246a25a..f74500d2d 100644 --- a/src/cli/tui-installer.ts +++ b/src/cli/tui-installer.ts @@ -2,9 +2,7 @@ import * as p from "@clack/prompts" import color from "picocolors" import type { InstallArgs } from "./types" import { - addAuthPlugins, addPluginToOpenCodeConfig, - addProviderConfig, detectCurrentConfig, getOpenCodeVersion, isOpenCodeInstalled, @@ -54,26 +52,6 @@ export async function runTuiInstaller(args: InstallArgs, version: string): Promi } spinner.stop(`Plugin added to ${color.cyan(pluginResult.configPath)}`) - if (config.hasGemini) { - spinner.start("Adding auth plugins (fetching latest versions)") - const authResult = await addAuthPlugins(config) - if (!authResult.success) { - spinner.stop(`Failed to add auth plugins: ${authResult.error}`) - p.outro(color.red("Installation failed.")) - return 1 - } - spinner.stop(`Auth plugins added to ${color.cyan(authResult.configPath)}`) - - spinner.start("Adding provider configurations") - const providerResult = addProviderConfig(config) - if (!providerResult.success) { - spinner.stop(`Failed to add provider config: ${providerResult.error}`) - p.outro(color.red("Installation failed.")) - return 1 - } - spinner.stop(`Provider config added to ${color.cyan(providerResult.configPath)}`) - } - spinner.start("Writing oh-my-opencode configuration") const omoResult = writeOmoConfig(config) if (!omoResult.success) { @@ -123,7 +101,7 @@ export async function runTuiInstaller(args: InstallArgs, version: string): Promi if ((config.hasClaude || config.hasGemini || config.hasCopilot) && !args.skipAuth) { const providers: string[] = [] if (config.hasClaude) providers.push(`Anthropic ${color.gray("→ Claude Pro/Max")}`) - if (config.hasGemini) providers.push(`Google ${color.gray("→ OAuth with Antigravity")}`) + if (config.hasGemini) providers.push(`Google ${color.gray("→ Gemini")}`) if (config.hasCopilot) providers.push(`GitHub ${color.gray("→ Copilot")}`) console.log()