From b91f28f95bec16e740e9d374bafa3ac4c8df8d9c Mon Sep 17 00:00:00 2001 From: Zyy Date: Thu, 3 Sep 2026 16:37:53 +0800 Subject: [PATCH] fix(frontend): align configuration and usage copy Generate the displayed Codex files and CCSwitch import payload from one source, carry the full settings through deep links, and clarify the quota percentage label. Co-authored-by: Codex --- .../AccountQuotaSummaryCell/index.vue | 2 +- .../api-keys/components/ApiKeyUseModal.vue | 36 ++++++------------ .../views/api-keys/utils/ccswitchImport.ts | 33 ++++++++++++++--- .../src/views/api-keys/utils/codexConfig.ts | 37 +++++++++++++++++++ 4 files changed, 78 insertions(+), 30 deletions(-) create mode 100644 frontend/src/views/api-keys/utils/codexConfig.ts diff --git a/frontend/src/views/accounts/components/AccountQuotaSummaryCell/index.vue b/frontend/src/views/accounts/components/AccountQuotaSummaryCell/index.vue index be144720f..5633c2ab9 100644 --- a/frontend/src/views/accounts/components/AccountQuotaSummaryCell/index.vue +++ b/frontend/src/views/accounts/components/AccountQuotaSummaryCell/index.vue @@ -50,7 +50,7 @@ const additionalEntryCount = computed(() => Math.max(summaryEntries.value.length class="flex shrink-0 items-baseline gap-1 text-[9px] font-emphasis text-cp-text-quaternary" title="最近使用额度的当前已用比例" > - 消耗比例 + 使用率 {{ currentUsageDisplay }} diff --git a/frontend/src/views/api-keys/components/ApiKeyUseModal.vue b/frontend/src/views/api-keys/components/ApiKeyUseModal.vue index e01dda302..04c83e7ca 100644 --- a/frontend/src/views/api-keys/components/ApiKeyUseModal.vue +++ b/frontend/src/views/api-keys/components/ApiKeyUseModal.vue @@ -8,6 +8,10 @@ import BaseModal from '@/components/base/BaseModal/index.vue' import BaseScrollbar from '@/components/base/BaseScrollbar.vue' import BaseSegmented from '@/components/base/BaseSegmented.vue' import BaseSwitch from '@/components/base/BaseSwitch.vue' +import { + buildCodexConfigFiles, + CODEX_WEBSOCKET_ENABLED_BY_DEFAULT, +} from '../utils/codexConfig' const props = defineProps<{ apiKey: { @@ -24,7 +28,7 @@ const emit = defineEmits<{ const open = defineModel({ default: false }) const activePlatform = shallowRef('unix') -const websocketEnabled = shallowRef(true) +const websocketEnabled = shallowRef(CODEX_WEBSOCKET_ENABLED_BY_DEFAULT) const platformOptions = [ { label: 'macOS / Linux', value: 'unix', icon: Apple }, @@ -40,31 +44,15 @@ const configPath = computed(() => const authPath = computed(() => activePlatform.value === 'windows' ? '%userprofile%\\.codex\\auth.json' : '~/.codex/auth.json', ) -const codexAuthJson = computed(() => JSON.stringify({ OPENAI_API_KEY: keyValue.value }, null, 2)) -const defaultModel = 'gpt-5.6-terra' - -const codexConfigToml = computed( - () => `model_provider = "OpenAI" -model = "${defaultModel}" -review_model = "${defaultModel}" -model_reasoning_effort = "max" -service_tier = "default" -disable_response_storage = true -network_access = "enabled" - -[model_providers.OpenAI] -name = "OpenAI" -base_url = "${props.apiBaseUrl}" -wire_api = "responses"${websocketEnabled.value ? '\nsupports_websockets = true' : ''} -requires_openai_auth = true - -[features]${websocketEnabled.value ? '\nresponses_websockets_v2 = true' : ''} -goals = true`, -) +const codexConfigFiles = computed(() => buildCodexConfigFiles({ + apiKey: keyValue.value, + baseUrl: props.apiBaseUrl, + websocketEnabled: websocketEnabled.value, +})) const visibleFiles = computed(() => [ - { path: configPath.value, content: codexConfigToml.value, scrollbarHeight: '360px' }, - { path: authPath.value, content: codexAuthJson.value, scrollbarHeight: undefined }, + { path: configPath.value, content: codexConfigFiles.value.configToml, scrollbarHeight: '360px' }, + { path: authPath.value, content: codexConfigFiles.value.authJson, scrollbarHeight: undefined }, ]) diff --git a/frontend/src/views/api-keys/utils/ccswitchImport.ts b/frontend/src/views/api-keys/utils/ccswitchImport.ts index faded9851..26df5b2fb 100644 --- a/frontend/src/views/api-keys/utils/ccswitchImport.ts +++ b/frontend/src/views/api-keys/utils/ccswitchImport.ts @@ -1,4 +1,8 @@ -const DEFAULT_CC_SWITCH_MODEL = 'gpt-5.5' +import { + buildCodexConfigFiles, + CODEX_DEFAULT_MODEL, + CODEX_WEBSOCKET_ENABLED_BY_DEFAULT, +} from './codexConfig' export interface CodexCcSwitchImportInput { apiKey: string @@ -7,18 +11,37 @@ export interface CodexCcSwitchImportInput { } export function buildCodexCcSwitchImportDeeplink(input: CodexCcSwitchImportInput): string { - const baseUrl = input.baseUrl.replace(/\/+$/, '') + const configFiles = buildCodexConfigFiles({ + apiKey: input.apiKey, + baseUrl: input.baseUrl, + websocketEnabled: CODEX_WEBSOCKET_ENABLED_BY_DEFAULT, + }) + // Carry the complete files shown in the UI; the explicit fields below keep + // compatibility with CCSwitch versions that only extract common settings. + const config = encodeBase64(JSON.stringify({ + auth: configFiles.auth, + config: configFiles.configToml, + })) const entries: [string, string][] = [ ['resource', 'provider'], ['app', 'codex'], - ['model', DEFAULT_CC_SWITCH_MODEL], + ['model', CODEX_DEFAULT_MODEL], ['name', input.providerName], - ['homepage', baseUrl], - ['endpoint', baseUrl], + ['homepage', configFiles.baseUrl], + ['endpoint', configFiles.baseUrl], ['apiKey', input.apiKey], ['configFormat', 'json'], + ['config', config], ['usageEnabled', 'false'], ] return `ccswitch://v1/import?${new URLSearchParams(entries).toString()}` } + +function encodeBase64(value: string) { + const bytes = new TextEncoder().encode(value) + let binary = '' + for (const byte of bytes) + binary += String.fromCharCode(byte) + return btoa(binary) +} diff --git a/frontend/src/views/api-keys/utils/codexConfig.ts b/frontend/src/views/api-keys/utils/codexConfig.ts new file mode 100644 index 000000000..1cb19229c --- /dev/null +++ b/frontend/src/views/api-keys/utils/codexConfig.ts @@ -0,0 +1,37 @@ +export const CODEX_DEFAULT_MODEL = 'gpt-5.6-terra' +export const CODEX_WEBSOCKET_ENABLED_BY_DEFAULT = true + +export interface CodexConfigInput { + apiKey: string + baseUrl: string + websocketEnabled?: boolean +} + +export function buildCodexConfigFiles(input: CodexConfigInput) { + const baseUrl = input.baseUrl.replace(/\/+$/, '') + const websocketEnabled = input.websocketEnabled ?? CODEX_WEBSOCKET_ENABLED_BY_DEFAULT + const auth = { OPENAI_API_KEY: input.apiKey } + const configToml = `model_provider = "OpenAI" +model = "${CODEX_DEFAULT_MODEL}" +review_model = "${CODEX_DEFAULT_MODEL}" +model_reasoning_effort = "max" +service_tier = "default" +disable_response_storage = true +network_access = "enabled" + +[model_providers.OpenAI] +name = "OpenAI" +base_url = "${baseUrl}" +wire_api = "responses"${websocketEnabled ? '\nsupports_websockets = true' : ''} +requires_openai_auth = true + +[features]${websocketEnabled ? '\nresponses_websockets_v2 = true' : ''} +goals = true` + + return { + auth, + authJson: JSON.stringify(auth, null, 2), + baseUrl, + configToml, + } +}