diff --git a/README.md b/README.md index 366d4df2..954e51da 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ The dashboard provides visual management for all account types: | **Azure Foundry** | API Key | `ccs foundry` | Claude via Microsoft Azure | | **Minimax** | API Key | `ccs mm` | M2 series, 1M context | | **DeepSeek** | API Key | `ccs deepseek` | V3.2 and R1 reasoning | +| **Novita AI** | API Key | `ccs api create --preset novita` | Anthropic-compatible Novita endpoint for Claude Code | | **Qwen (OAuth)** | OAuth | `ccs qwen` | Qwen Code via CLIProxy | | **Qwen API** | API Key | `ccs api create --preset qwen` | DashScope Anthropic-compatible API | | **Alibaba Coding Plan** | API Key | `ccs api create --preset alibaba-coding-plan` | Model Studio Coding Plan endpoint | diff --git a/src/shared/provider-preset-catalog.ts b/src/shared/provider-preset-catalog.ts index b64d22d4..0ca546a0 100644 --- a/src/shared/provider-preset-catalog.ts +++ b/src/shared/provider-preset-catalog.ts @@ -258,16 +258,15 @@ const RAW_PROVIDER_PRESET_DEFINITIONS: readonly ProviderPresetDefinition[] = [ { id: 'novita', name: 'Novita AI', - description: 'OpenAI-compatible API (Llama, Mistral, Qwen, and more)', - baseUrl: 'https://api.novita.ai/openai', + description: 'Anthropic-compatible API for Claude Code and CCS profiles', + baseUrl: 'https://api.novita.ai/anthropic', defaultProfileName: 'novita', defaultModel: 'deepseek/deepseek-v3.2', apiKeyPlaceholder: 'YOUR_NOVITA_API_KEY', apiKeyHint: 'Get your API key at novita.ai', category: 'alternative', requiresApiKey: true, - badge: 'OpenAI-compatible', - icon: '/icons/novita.svg', + badge: 'Anthropic-compatible', }, ]; diff --git a/tests/unit/api/provider-presets-novita.test.ts b/tests/unit/api/provider-presets-novita.test.ts index 976737b5..33557503 100644 --- a/tests/unit/api/provider-presets-novita.test.ts +++ b/tests/unit/api/provider-presets-novita.test.ts @@ -5,7 +5,7 @@ describe('provider-presets-novita', () => { it('resolves novita preset id', () => { const preset = getPresetById('novita'); expect(preset?.id).toBe('novita'); - expect(preset?.baseUrl).toBe('https://api.novita.ai/openai'); + expect(preset?.baseUrl).toBe('https://api.novita.ai/anthropic'); expect(preset?.defaultProfileName).toBe('novita'); }); diff --git a/tests/unit/api/provider-presets.test.ts b/tests/unit/api/provider-presets.test.ts index 2246ed6e..9f5d03e2 100644 --- a/tests/unit/api/provider-presets.test.ts +++ b/tests/unit/api/provider-presets.test.ts @@ -1,5 +1,11 @@ +import { existsSync } from 'fs'; +import { resolve } from 'path'; import { describe, expect, it } from 'bun:test'; -import { getPresetById, isValidPresetId } from '../../../src/api/services/provider-presets'; +import { + PROVIDER_PRESETS, + getPresetById, + isValidPresetId, +} from '../../../src/api/services/provider-presets'; describe('provider-presets', () => { it('resolves Alibaba Coding Plan preset id', () => { @@ -54,4 +60,13 @@ describe('provider-presets', () => { const preset = getPresetById('qwen'); expect(preset?.defaultProfileName).toBe('qwen-api'); }); + + it('only references provider preset icons that exist in ui/public', () => { + for (const preset of PROVIDER_PRESETS) { + if (!preset.icon) continue; + + const iconPath = resolve(import.meta.dir, '../../../ui/public', preset.icon.replace(/^\/+/, '')); + expect(existsSync(iconPath)).toBe(true); + } + }); });