From aa1a3b9a74a505eb4362a346dfc9026dae8629d3 Mon Sep 17 00:00:00 2001 From: cyhhao Date: Sun, 11 Jan 2026 20:29:32 +0800 Subject: [PATCH] fix: update OpenCode use-key examples --- frontend/src/components/keys/UseKeyModal.vue | 178 ++++++++++++++++--- frontend/src/i18n/locales/en.ts | 11 ++ frontend/src/i18n/locales/zh.ts | 11 ++ 3 files changed, 178 insertions(+), 22 deletions(-) diff --git a/frontend/src/components/keys/UseKeyModal.vue b/frontend/src/components/keys/UseKeyModal.vue index 546a53ab..58f42ae6 100644 --- a/frontend/src/components/keys/UseKeyModal.vue +++ b/frontend/src/components/keys/UseKeyModal.vue @@ -28,8 +28,8 @@ {{ platformDescription }}
{{ platformNote }} @@ -173,17 +173,28 @@ const { copyToClipboard: clipboardCopy } = useClipboard() const copiedIndex = ref(null) const activeTab = ref('unix') -const activeClientTab = ref('claude') // Level 1 tab for antigravity platform +const activeClientTab = ref('claude') // Reset tabs when platform changes -watch(() => props.platform, (newPlatform) => { - activeTab.value = 'unix' - if (newPlatform === 'antigravity') { - activeClientTab.value = 'claude' +const defaultClientTab = computed(() => { + switch (props.platform) { + case 'openai': + return 'codex' + case 'gemini': + return 'gemini' + case 'antigravity': + return 'claude' + default: + return 'claude' } }) -// Reset shell tab when client changes (for antigravity) +watch(() => props.platform, () => { + activeTab.value = 'unix' + activeClientTab.value = defaultClientTab.value +}, { immediate: true }) + +// Reset shell tab when client changes watch(activeClientTab, () => { activeTab.value = 'unix' }) @@ -251,11 +262,32 @@ const SparkleIcon = { } } -// Client tabs for Antigravity platform (Level 1) -const clientTabs = computed((): TabConfig[] => [ - { id: 'claude', label: t('keys.useKeyModal.antigravity.claudeCode'), icon: TerminalIcon }, - { id: 'gemini', label: t('keys.useKeyModal.antigravity.geminiCli'), icon: SparkleIcon } -]) +const clientTabs = computed((): TabConfig[] => { + if (!props.platform) return [] + switch (props.platform) { + case 'openai': + return [ + { id: 'codex', label: t('keys.useKeyModal.cliTabs.codexCli'), icon: TerminalIcon }, + { id: 'opencode', label: t('keys.useKeyModal.cliTabs.opencode'), icon: TerminalIcon } + ] + case 'gemini': + return [ + { id: 'gemini', label: t('keys.useKeyModal.cliTabs.geminiCli'), icon: SparkleIcon }, + { id: 'opencode', label: t('keys.useKeyModal.cliTabs.opencode'), icon: TerminalIcon } + ] + case 'antigravity': + return [ + { id: 'claude', label: t('keys.useKeyModal.cliTabs.claudeCode'), icon: TerminalIcon }, + { id: 'gemini', label: t('keys.useKeyModal.cliTabs.geminiCli'), icon: SparkleIcon }, + { id: 'opencode', label: t('keys.useKeyModal.cliTabs.opencode'), icon: TerminalIcon } + ] + default: + return [ + { id: 'claude', label: t('keys.useKeyModal.cliTabs.claudeCode'), icon: TerminalIcon }, + { id: 'opencode', label: t('keys.useKeyModal.cliTabs.opencode'), icon: TerminalIcon } + ] + } +}) // Shell tabs (3 types for environment variable based configs) const shellTabs: TabConfig[] = [ @@ -270,11 +302,13 @@ const openaiTabs: TabConfig[] = [ { id: 'windows', label: 'Windows', icon: WindowsIcon } ] +const showShellTabs = computed(() => activeClientTab.value !== 'opencode') + const currentTabs = computed(() => { + if (!showShellTabs.value) return [] if (props.platform === 'openai') { - return openaiTabs // 2 tabs: unix, windows + return openaiTabs } - // All other platforms (anthropic, gemini, antigravity) use shell tabs return shellTabs }) @@ -308,6 +342,8 @@ const platformNote = computed(() => { } }) +const showPlatformNote = computed(() => activeClientTab.value !== 'opencode') + const escapeHtml = (value: string) => value .replace(/&/g, '&') .replace(/ wrapToken('text-slate-500', value) const currentFiles = computed((): FileConfig[] => { const baseUrl = props.baseUrl || window.location.origin const apiKey = props.apiKey + const baseRoot = baseUrl.replace(/\/v1\/?$/, '').replace(/\/+$/, '') + const ensureV1 = (value: string) => { + const trimmed = value.replace(/\/+$/, '') + return trimmed.endsWith('/v1') ? trimmed : `${trimmed}/v1` + } + const apiBase = ensureV1(baseRoot) + const antigravityBase = ensureV1(`${baseRoot}/antigravity`) + const antigravityGeminiBase = (() => { + const trimmed = `${baseRoot}/antigravity`.replace(/\/+$/, '') + return trimmed.endsWith('/v1beta') ? trimmed : `${trimmed}/v1beta` + })() + + if (activeClientTab.value === 'opencode') { + switch (props.platform) { + case 'anthropic': + return [generateOpenCodeConfig('anthropic', apiBase, apiKey)] + case 'openai': + return [generateOpenCodeConfig('openai', apiBase, apiKey)] + case 'gemini': + return [generateOpenCodeConfig('gemini', apiBase, apiKey)] + case 'antigravity': + return [ + generateOpenCodeConfig('antigravity-claude', antigravityBase, apiKey, 'opencode.json (Claude)'), + generateOpenCodeConfig('antigravity-gemini', antigravityGeminiBase, apiKey, 'opencode.json (Gemini)') + ] + default: + return [generateOpenCodeConfig('openai', apiBase, apiKey)] + } + } switch (props.platform) { case 'openai': @@ -336,12 +401,11 @@ const currentFiles = computed((): FileConfig[] => { case 'gemini': return [generateGeminiCliContent(baseUrl, apiKey)] case 'antigravity': - // Both Claude Code and Gemini CLI need /antigravity suffix for antigravity platform - if (activeClientTab.value === 'claude') { - return generateAnthropicFiles(`${baseUrl}/antigravity`, apiKey) + if (activeClientTab.value === 'gemini') { + return [generateGeminiCliContent(`${baseUrl}/antigravity`, apiKey)] } - return [generateGeminiCliContent(`${baseUrl}/antigravity`, apiKey)] - default: // anthropic + return generateAnthropicFiles(`${baseUrl}/antigravity`, apiKey) + default: return generateAnthropicFiles(baseUrl, apiKey) } }) @@ -456,6 +520,76 @@ requires_openai_auth = true` ] } +function generateOpenCodeConfig(platform: string, baseUrl: string, apiKey: string, pathLabel?: string): FileConfig { + const provider: Record = { + [platform]: { + options: { + baseURL: baseUrl, + apiKey, + ...(platform === 'openai' ? { store: false } : {}) + } + } + } + const openaiModels = { + 'gpt-5.2-codex': { + name: 'GPT-5.2 Codex', + variants: { + low: {}, + medium: {}, + high: {}, + xhigh: {} + } + } + } + const geminiModels = { + 'gemini-3-pro-high': { name: 'Gemini 3 Pro High' }, + 'gemini-3-pro-low': { name: 'Gemini 3 Pro Low' }, + 'gemini-3-pro-preview': { name: 'Gemini 3 Pro Preview' }, + 'gemini-3-pro-image': { name: 'Gemini 3 Pro Image' }, + 'gemini-3-flash': { name: 'Gemini 3 Flash' }, + 'gemini-2.5-flash-thinking': { name: 'Gemini 2.5 Flash Thinking' }, + 'gemini-2.5-flash': { name: 'Gemini 2.5 Flash' }, + 'gemini-2.5-flash-lite': { name: 'Gemini 2.5 Flash Lite' } + } + const claudeModels = { + 'claude-opus-4-5-thinking': { name: 'Claude Opus 4.5 Thinking' }, + 'claude-sonnet-4-5-thinking': { name: 'Claude Sonnet 4.5 Thinking' }, + 'claude-sonnet-4-5': { name: 'Claude Sonnet 4.5' } + } + + if (platform === 'gemini') { + provider[platform].npm = '@ai-sdk/google' + provider[platform].models = geminiModels + } else if (platform === 'anthropic') { + provider[platform].npm = '@ai-sdk/anthropic' + } else if (platform === 'antigravity-claude') { + provider[platform].npm = '@ai-sdk/anthropic' + provider[platform].name = 'Antigravity (Claude)' + provider[platform].models = claudeModels + } else if (platform === 'antigravity-gemini') { + provider[platform].npm = '@ai-sdk/google' + provider[platform].name = 'Antigravity (Gemini)' + provider[platform].models = geminiModels + } else if (platform === 'openai') { + provider[platform].models = openaiModels + } + + const content = JSON.stringify( + { + provider, + $schema: 'https://opencode.ai/config.json' + }, + null, + 2 + ) + + return { + path: pathLabel ?? 'opencode.json', + content, + hint: t('keys.useKeyModal.opencode.hint') + } +} + const copyContent = async (content: string, index: number) => { const success = await clipboardCopy(content, t('keys.copied')) if (success) { diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index c9633e38..29a1e18a 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -364,6 +364,12 @@ export default { note: 'Make sure the config directory exists. macOS/Linux users can run mkdir -p ~/.codex to create it.', noteWindows: 'Press Win+R and enter %userprofile%\\.codex to open the config directory. Create it manually if it does not exist.', }, + cliTabs: { + claudeCode: 'Claude Code', + geminiCli: 'Gemini CLI', + codexCli: 'Codex CLI', + opencode: 'OpenCode', + }, antigravity: { description: 'Configure API access for Antigravity group. Select the configuration method based on your client.', claudeCode: 'Claude Code', @@ -376,6 +382,11 @@ export default { modelComment: 'If you have Gemini 3 access, you can use: gemini-3-pro-preview', note: 'These environment variables will be active in the current terminal session. For permanent configuration, add them to ~/.bashrc, ~/.zshrc, or the appropriate configuration file.', }, + opencode: { + title: 'OpenCode Example', + subtitle: 'opencode.json', + hint: 'This is a group configuration example. Adjust model and options as needed.', + }, }, customKeyLabel: 'Custom Key', customKeyPlaceholder: 'Enter your custom key (min 16 chars)', diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index 6571d0e5..adb2b2c2 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -361,6 +361,12 @@ export default { note: '请确保配置目录存在。macOS/Linux 用户可运行 mkdir -p ~/.codex 创建目录。', noteWindows: '按 Win+R,输入 %userprofile%\\.codex 打开配置目录。如目录不存在,请先手动创建。', }, + cliTabs: { + claudeCode: 'Claude Code', + geminiCli: 'Gemini CLI', + codexCli: 'Codex CLI', + opencode: 'OpenCode', + }, antigravity: { description: '为 Antigravity 分组配置 API 访问。请根据您使用的客户端选择对应的配置方式。', claudeCode: 'Claude Code', @@ -373,6 +379,11 @@ export default { modelComment: '如果你有 Gemini 3 权限可以填:gemini-3-pro-preview', note: '这些环境变量将在当前终端会话中生效。如需永久配置,请将其添加到 ~/.bashrc、~/.zshrc 或相应的配置文件中。', }, + opencode: { + title: 'OpenCode 配置示例', + subtitle: 'opencode.json', + hint: '示例仅用于演示分组配置,模型与选项可按需调整。', + }, }, customKeyLabel: '自定义密钥', customKeyPlaceholder: '输入自定义密钥(至少16个字符)',