fix(ccswitch): add codex model to import deeplink

This commit is contained in:
wucm667 2026-05-08 17:31:36 +08:00
parent 33db04fb75
commit 65493df95a
3 changed files with 151 additions and 38 deletions

View File

@ -0,0 +1,67 @@
import { describe, expect, it } from 'vitest'
import {
OPENAI_CC_SWITCH_CODEX_MODEL,
buildCcSwitchImportDeeplink
} from '@/utils/ccswitchImport'
import type { GroupPlatform } from '@/types'
function paramsFromDeeplink(deeplink: string): URLSearchParams {
const query = deeplink.split('?')[1] || ''
return new URLSearchParams(query)
}
describe('ccswitchImport utils', () => {
const baseInput = {
baseUrl: 'https://api.example.com',
providerName: 'Sub2API',
apiKey: 'sk-test',
usageScript: 'return true'
}
it('adds the Codex model parameter for OpenAI imports', () => {
const params = paramsFromDeeplink(
buildCcSwitchImportDeeplink({
...baseInput,
platform: 'openai',
clientType: 'claude'
})
)
expect(params.get('resource')).toBe('provider')
expect(params.get('app')).toBe('codex')
expect(params.get('endpoint')).toBe(baseInput.baseUrl)
expect(params.get('model')).toBe(OPENAI_CC_SWITCH_CODEX_MODEL)
expect(atob(params.get('usageScript') || '')).toBe(baseInput.usageScript)
})
it.each([
{ platform: 'anthropic' as GroupPlatform, clientType: 'claude' as const, app: 'claude' },
{ platform: 'gemini' as GroupPlatform, clientType: 'gemini' as const, app: 'gemini' }
])('does not add a model parameter for $platform imports', ({ platform, clientType, app }) => {
const params = paramsFromDeeplink(
buildCcSwitchImportDeeplink({
...baseInput,
platform,
clientType
})
)
expect(params.get('app')).toBe(app)
expect(params.get('endpoint')).toBe(baseInput.baseUrl)
expect(params.has('model')).toBe(false)
})
it('keeps Antigravity imports on the selected client endpoint without a model parameter', () => {
const params = paramsFromDeeplink(
buildCcSwitchImportDeeplink({
...baseInput,
platform: 'antigravity',
clientType: 'gemini'
})
)
expect(params.get('app')).toBe('gemini')
expect(params.get('endpoint')).toBe(`${baseInput.baseUrl}/antigravity`)
expect(params.has('model')).toBe(false)
})
})

View File

@ -0,0 +1,72 @@
import type { GroupPlatform } from '@/types'
export const OPENAI_CC_SWITCH_CODEX_MODEL = 'gpt-5.4'
export type CcSwitchClientType = 'claude' | 'gemini'
export interface CcSwitchImportConfig {
app: string
endpoint: string
model?: string
}
export interface CcSwitchImportDeeplinkInput {
baseUrl: string
platform?: GroupPlatform | null
clientType: CcSwitchClientType
providerName: string
apiKey: string
usageScript: string
}
export function resolveCcSwitchImportConfig(
platform: GroupPlatform | undefined | null,
clientType: CcSwitchClientType,
baseUrl: string
): CcSwitchImportConfig {
switch (platform || 'anthropic') {
case 'antigravity':
return {
app: clientType === 'gemini' ? 'gemini' : 'claude',
endpoint: `${baseUrl}/antigravity`
}
case 'openai':
return {
app: 'codex',
endpoint: baseUrl,
model: OPENAI_CC_SWITCH_CODEX_MODEL
}
case 'gemini':
return {
app: 'gemini',
endpoint: baseUrl
}
default:
return {
app: 'claude',
endpoint: baseUrl
}
}
}
export function buildCcSwitchImportDeeplink(input: CcSwitchImportDeeplinkInput): string {
const config = resolveCcSwitchImportConfig(input.platform, input.clientType, input.baseUrl)
const entries: [string, string][] = [
['resource', 'provider'],
['app', config.app],
['name', input.providerName],
['homepage', input.baseUrl],
['endpoint', config.endpoint],
['apiKey', input.apiKey],
['configFormat', 'json'],
['usageEnabled', 'true'],
['usageScript', btoa(input.usageScript)],
['usageAutoInterval', '30']
]
if (config.model) {
entries.splice(2, 0, ['model', config.model])
}
return `ccswitch://v1/import?${new URLSearchParams(entries).toString()}`
}

View File

@ -1073,6 +1073,10 @@ import type { Column } from '@/components/common/types'
import type { BatchApiKeyUsageStats } from '@/api/usage'
import { formatDateTime } from '@/utils/format'
import { maskApiKey } from '@/utils/maskApiKey'
import {
buildCcSwitchImportDeeplink,
type CcSwitchClientType
} from '@/utils/ccswitchImport'
// Helper to format date for datetime-local input
const formatDateTimeLocal = (isoDate: string): string => {
@ -1700,34 +1704,10 @@ const importToCcswitch = (row: ApiKey) => {
executeCcsImport(row, platform === 'gemini' ? 'gemini' : 'claude')
}
const executeCcsImport = (row: ApiKey, clientType: 'claude' | 'gemini') => {
const executeCcsImport = (row: ApiKey, clientType: CcSwitchClientType) => {
const baseUrl = publicSettings.value?.api_base_url || window.location.origin
const platform = row.group?.platform || 'anthropic'
// Determine app name and endpoint based on platform and client type
let app: string
let endpoint: string
if (platform === 'antigravity') {
// Antigravity always uses /antigravity suffix
app = clientType === 'gemini' ? 'gemini' : 'claude'
endpoint = `${baseUrl}/antigravity`
} else {
switch (platform) {
case 'openai':
app = 'codex'
endpoint = baseUrl
break
case 'gemini':
app = 'gemini'
endpoint = baseUrl
break
default: // anthropic
app = 'claude'
endpoint = baseUrl
}
}
const usageScript = `({
request: {
url: "{{baseUrl}}/v1/usage",
@ -1745,20 +1725,14 @@ const executeCcsImport = (row: ApiKey, clientType: 'claude' | 'gemini') => {
}
})`
const providerName = (publicSettings.value?.site_name || 'sub2api').trim() || 'sub2api'
const params = new URLSearchParams({
resource: 'provider',
app: app,
name: providerName,
homepage: baseUrl,
endpoint: endpoint,
const deeplink = buildCcSwitchImportDeeplink({
baseUrl,
platform,
clientType,
providerName,
apiKey: row.key,
configFormat: 'json',
usageEnabled: 'true',
usageScript: btoa(usageScript),
usageAutoInterval: '30'
usageScript
})
const deeplink = `ccswitch://v1/import?${params.toString()}`
try {
window.open(deeplink, '_self')
@ -1775,7 +1749,7 @@ const executeCcsImport = (row: ApiKey, clientType: 'claude' | 'gemini') => {
}
}
const handleCcsClientSelect = (clientType: 'claude' | 'gemini') => {
const handleCcsClientSelect = (clientType: CcSwitchClientType) => {
if (pendingCcsRow.value) {
executeCcsImport(pendingCcsRow.value, clientType)
}