Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 1 addition & 32 deletions src/cli/config-manager.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, test, mock, beforeEach, 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", () => {
Expand Down Expand Up @@ -169,37 +169,6 @@ describe("fetchNpmDistTags", () => {
})
})

describe("config-manager ANTIGRAVITY_PROVIDER_CONFIG", () => {
test("Gemini models include full spec (limit + modalities)", () => {
const google = (ANTIGRAVITY_PROVIDER_CONFIG as any).google
expect(google).toBeTruthy()

const models = google.models as Record<string, any>
expect(models).toBeTruthy()

const required = [
"antigravity-gemini-3-pro-high",
"antigravity-gemini-3-pro-low",
"antigravity-gemini-3-flash",
]

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)
}
})
})

describe("generateOmoConfig - model fallback system", () => {
test("generates native sonnet models when Claude standard subscription", () => {
// #given user has Claude standard subscription (not max20)
Expand Down
123 changes: 0 additions & 123 deletions src/cli/config-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,46 +394,6 @@ export async function getOpenCodeVersion(): Promise<string | null> {
return result?.version ?? null
}

export async function addAuthPlugins(config: InstallConfig): Promise<ConfigMergeResult> {
try {
ensureConfigDir()
} 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 = parseConfigWithError(path)
if (parseResult.error && !parseResult.config) {
existingConfig = {}
} else {
existingConfig = parseResult.config
}
}

const plugins: string[] = existingConfig?.plugin ?? []

if (config.hasGemini) {
const version = await fetchLatestVersion("opencode-antigravity-auth")
const pluginEntry = version ? `opencode-antigravity-auth@${version}` : "opencode-antigravity-auth"
if (!plugins.some((p) => p.startsWith("opencode-antigravity-auth"))) {
plugins.push(pluginEntry)
}
}



const newConfig = { ...(existingConfig ?? {}), plugin: plugins }
writeFileSync(path, JSON.stringify(newConfig, null, 2) + "\n")
return { success: true, configPath: path }
} catch (err) {
return { success: false, configPath: path, error: formatErrorWithSuggestion(err, "add auth plugins to config") }
}
}

export interface BunInstallResult {
success: boolean
timedOut?: boolean
Expand Down Expand Up @@ -492,89 +452,6 @@ export async function runBunInstallWithDetails(): Promise<BunInstallResult> {
}
}

/**
* Antigravity Provider Configuration
*
* IMPORTANT: Model names MUST use `antigravity-` prefix for stability.
*
* The opencode-antigravity-auth plugin supports two naming conventions:
* - `antigravity-gemini-3-pro-high` (RECOMMENDED, explicit Antigravity quota routing)
* - `gemini-3-pro-high` (LEGACY, backward compatible but may break in future)
*
* Legacy names rely on Gemini CLI using `-preview` suffix for disambiguation.
* If Google removes `-preview`, legacy names may route to wrong quota.
*
* @see https://github.com/NoeFabris/opencode-antigravity-auth#migration-guide-v127
*/
export const ANTIGRAVITY_PROVIDER_CONFIG = {
google: {
name: "Google",
models: {
"antigravity-gemini-3-pro-high": {
name: "Gemini 3 Pro High (Antigravity)",
thinking: true,
attachment: true,
limit: { context: 1048576, output: 65535 },
modalities: { input: ["text", "image", "pdf"], output: ["text"] },
},
"antigravity-gemini-3-pro-low": {
name: "Gemini 3 Pro Low (Antigravity)",
thinking: true,
attachment: true,
limit: { context: 1048576, output: 65535 },
modalities: { input: ["text", "image", "pdf"], output: ["text"] },
},
"antigravity-gemini-3-flash": {
name: "Gemini 3 Flash (Antigravity)",
attachment: true,
limit: { context: 1048576, output: 65536 },
modalities: { input: ["text", "image", "pdf"], output: ["text"] },
},
},
},
}



export function addProviderConfig(config: InstallConfig): ConfigMergeResult {
try {
ensureConfigDir()
} 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 = parseConfigWithError(path)
if (parseResult.error && !parseResult.config) {
existingConfig = {}
} else {
existingConfig = parseResult.config
}
}

const newConfig = { ...(existingConfig ?? {}) }

const providers = (newConfig.provider ?? {}) as Record<string, unknown>

if (config.hasGemini) {
providers.google = ANTIGRAVITY_PROVIDER_CONFIG.google
}

if (Object.keys(providers).length > 0) {
newConfig.provider = providers
}

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") }
}
}

function detectProvidersFromOmoConfig(): { hasOpenAI: boolean; hasOpencodeZen: boolean; hasZaiCodingPlan: boolean } {
const omoConfigPath = getOmoConfig()
if (!existsSync(omoConfigPath)) {
Expand Down
48 changes: 3 additions & 45 deletions src/cli/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import {
writeOmoConfig,
isOpenCodeInstalled,
getOpenCodeVersion,
addAuthPlugins,
addProviderConfig,
detectCurrentConfig,
} from "./config-manager"
import { shouldShowChatGPTOnlyWarning } from "./model-fallback"
Expand Down Expand Up @@ -290,7 +288,7 @@ async function runNonTuiInstall(args: InstallArgs): Promise<number> {

printHeader(isUpdate)

const totalSteps = 6
const totalSteps = 4
let step = 1

printStep(step++, totalSteps, "Checking OpenCode installation...")
Expand Down Expand Up @@ -318,26 +316,6 @@ async function runNonTuiInstall(args: InstallArgs): Promise<number> {
}
printSuccess(`Plugin ${isUpdate ? "verified" : "added"} ${SYMBOLS.arrow} ${color.dim(pluginResult.configPath)}`)

if (config.hasGemini) {
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) {
Expand Down Expand Up @@ -387,7 +365,7 @@ async function runNonTuiInstall(args: InstallArgs): Promise<number> {
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("→ Install opencode-antigravity-auth first")}\n` : "") +
(config.hasCopilot ? ` ${SYMBOLS.bullet} GitHub ${color.gray("→ Copilot")}` : ""),
"Authenticate Your Providers"
)
Expand Down Expand Up @@ -436,26 +414,6 @@ export async function install(args: InstallArgs): Promise<number> {
}
s.stop(`Plugin added to ${color.cyan(pluginResult.configPath)}`)

if (config.hasGemini) {
s.start("Adding auth plugins (fetching latest versions)")
const authResult = await addAuthPlugins(config)
if (!authResult.success) {
s.stop(`Failed to add auth plugins: ${authResult.error}`)
p.outro(color.red("Installation failed."))
return 1
}
s.stop(`Auth plugins added to ${color.cyan(authResult.configPath)}`)

s.start("Adding provider configurations")
const providerResult = addProviderConfig(config)
if (!providerResult.success) {
s.stop(`Failed to add provider config: ${providerResult.error}`)
p.outro(color.red("Installation failed."))
return 1
}
s.stop(`Provider config added to ${color.cyan(providerResult.configPath)}`)
}

s.start("Writing oh-my-opencode configuration")
const omoResult = writeOmoConfig(config)
if (!omoResult.success) {
Expand Down Expand Up @@ -503,7 +461,7 @@ export async function install(args: InstallArgs): Promise<number> {
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("→ Install opencode-antigravity-auth first")}`)
if (config.hasCopilot) providers.push(`GitHub ${color.gray("→ Copilot")}`)

console.log()
Expand Down