fix(ccswitch): add codex model to import deeplink
This commit is contained in:
parent
33db04fb75
commit
65493df95a
67
frontend/src/utils/__tests__/ccswitchImport.spec.ts
Normal file
67
frontend/src/utils/__tests__/ccswitchImport.spec.ts
Normal 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)
|
||||||
|
})
|
||||||
|
})
|
||||||
72
frontend/src/utils/ccswitchImport.ts
Normal file
72
frontend/src/utils/ccswitchImport.ts
Normal 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()}`
|
||||||
|
}
|
||||||
@ -1073,6 +1073,10 @@ import type { Column } from '@/components/common/types'
|
|||||||
import type { BatchApiKeyUsageStats } from '@/api/usage'
|
import type { BatchApiKeyUsageStats } from '@/api/usage'
|
||||||
import { formatDateTime } from '@/utils/format'
|
import { formatDateTime } from '@/utils/format'
|
||||||
import { maskApiKey } from '@/utils/maskApiKey'
|
import { maskApiKey } from '@/utils/maskApiKey'
|
||||||
|
import {
|
||||||
|
buildCcSwitchImportDeeplink,
|
||||||
|
type CcSwitchClientType
|
||||||
|
} from '@/utils/ccswitchImport'
|
||||||
|
|
||||||
// Helper to format date for datetime-local input
|
// Helper to format date for datetime-local input
|
||||||
const formatDateTimeLocal = (isoDate: string): string => {
|
const formatDateTimeLocal = (isoDate: string): string => {
|
||||||
@ -1700,34 +1704,10 @@ const importToCcswitch = (row: ApiKey) => {
|
|||||||
executeCcsImport(row, platform === 'gemini' ? 'gemini' : 'claude')
|
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 baseUrl = publicSettings.value?.api_base_url || window.location.origin
|
||||||
const platform = row.group?.platform || 'anthropic'
|
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 = `({
|
const usageScript = `({
|
||||||
request: {
|
request: {
|
||||||
url: "{{baseUrl}}/v1/usage",
|
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 providerName = (publicSettings.value?.site_name || 'sub2api').trim() || 'sub2api'
|
||||||
|
const deeplink = buildCcSwitchImportDeeplink({
|
||||||
const params = new URLSearchParams({
|
baseUrl,
|
||||||
resource: 'provider',
|
platform,
|
||||||
app: app,
|
clientType,
|
||||||
name: providerName,
|
providerName,
|
||||||
homepage: baseUrl,
|
|
||||||
endpoint: endpoint,
|
|
||||||
apiKey: row.key,
|
apiKey: row.key,
|
||||||
configFormat: 'json',
|
usageScript
|
||||||
usageEnabled: 'true',
|
|
||||||
usageScript: btoa(usageScript),
|
|
||||||
usageAutoInterval: '30'
|
|
||||||
})
|
})
|
||||||
const deeplink = `ccswitch://v1/import?${params.toString()}`
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
window.open(deeplink, '_self')
|
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) {
|
if (pendingCcsRow.value) {
|
||||||
executeCcsImport(pendingCcsRow.value, clientType)
|
executeCcsImport(pendingCcsRow.value, clientType)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user