diff --git a/frontend/src/components/keys/UseKeyModal.vue b/frontend/src/components/keys/UseKeyModal.vue index 95bbc64226..604af3048c 100644 --- a/frontend/src/components/keys/UseKeyModal.vue +++ b/frontend/src/components/keys/UseKeyModal.vue @@ -275,6 +275,7 @@ interface Props { apiKey: string baseUrl: string platform: GroupPlatform | null + claudeCodeOnly?: boolean allowMessagesDispatch?: boolean } @@ -336,6 +337,7 @@ const codexManifestContext = computed(() => { // Reset tabs when platform changes const defaultClientTab = computed(() => { + if (props.claudeCodeOnly) return 'claude' switch (props.platform) { case 'openai': return 'codex' @@ -350,7 +352,7 @@ const defaultClientTab = computed(() => { } }) -watch(() => props.platform, () => { +watch(() => [props.platform, props.claudeCodeOnly], () => { activeTab.value = 'unix' activeClientTab.value = defaultClientTab.value codexAuthMode.value = 'legacy' @@ -440,6 +442,9 @@ const SparkleIcon = { const clientTabs = computed((): TabConfig[] => { if (!props.platform) return [] + if (props.claudeCodeOnly) { + return [{ id: 'claude', label: t('keys.useKeyModal.cliTabs.claudeCode'), icon: TerminalIcon }] + } switch (props.platform) { case 'openai': { const tabs: TabConfig[] = [ diff --git a/frontend/src/components/keys/__tests__/UseKeyModal.spec.ts b/frontend/src/components/keys/__tests__/UseKeyModal.spec.ts index 8a672aa918..b8bbbc7261 100644 --- a/frontend/src/components/keys/__tests__/UseKeyModal.spec.ts +++ b/frontend/src/components/keys/__tests__/UseKeyModal.spec.ts @@ -40,6 +40,44 @@ describe('UseKeyModal', () => { saveAsMock.mockClear() }) + it('shows only Claude Code for Claude Code-only groups', async () => { + const wrapper = mount(UseKeyModal, { + props: { + show: true, + apiKey: 'sk-anthropic-test', + baseUrl: 'https://example.com/v1', + platform: 'anthropic' + }, + global: { + stubs: { + BaseDialog: { template: '
' }, + Icon: { template: '' } + } + } + }) + + const clientTabs = () => wrapper.find('nav[aria-label="Client"]').text() + expect(clientTabs()).toContain('keys.useKeyModal.cliTabs.codexCli') + expect(clientTabs()).toContain('keys.useKeyModal.cliTabs.opencode') + + const codexTab = wrapper.find('nav[aria-label="Client"]').findAll('button').find( + (button) => button.text().includes('keys.useKeyModal.cliTabs.codexCli') + ) + await codexTab!.trigger('click') + await wrapper.setProps({ claudeCodeOnly: true }) + + expect(clientTabs()).toContain('keys.useKeyModal.cliTabs.claudeCode') + expect(clientTabs()).not.toContain('keys.useKeyModal.cliTabs.codexCli') + expect(clientTabs()).not.toContain('keys.useKeyModal.cliTabs.opencode') + expect(wrapper.find('pre code').text()).toContain('CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC') + + await wrapper.setProps({ platform: 'openai' }) + expect(clientTabs()).toContain('keys.useKeyModal.cliTabs.claudeCode') + expect(clientTabs()).not.toContain('keys.useKeyModal.cliTabs.codexCli') + expect(clientTabs()).not.toContain('keys.useKeyModal.cliTabs.opencode') + expect(wrapper.find('pre code').text()).toContain('ANTHROPIC_BASE_URL') + }) + it('omits the attribution override from every standard Claude Code setup form', async () => { const wrapper = mount(UseKeyModal, { props: { diff --git a/frontend/src/views/user/KeysView.vue b/frontend/src/views/user/KeysView.vue index 3011b52a11..66ac097cb1 100644 --- a/frontend/src/views/user/KeysView.vue +++ b/frontend/src/views/user/KeysView.vue @@ -1075,6 +1075,7 @@ :api-key="selectedKey?.key || ''" :base-url="publicSettings?.api_base_url || ''" :platform="selectedKey?.group?.platform || null" + :claude-code-only="selectedKey?.group?.claude_code_only || false" :allow-messages-dispatch="selectedKey?.group?.allow_messages_dispatch || false" @close="closeUseKeyModal" />