diff --git a/frontend/src/components/account/BulkEditAccountModal.vue b/frontend/src/components/account/BulkEditAccountModal.vue index 682bb489b0..d24488b47a 100644 --- a/frontend/src/components/account/BulkEditAccountModal.vue +++ b/frontend/src/components/account/BulkEditAccountModal.vue @@ -1195,7 +1195,7 @@ const allOpenAIPassthroughCapable = computed(() => { targetSelectedPlatforms.value.length === 1 && targetSelectedPlatforms.value[0] === 'openai' && targetSelectedTypes.value.length > 0 && - targetSelectedTypes.value.every(t => t === 'oauth' || t === 'apikey') + targetSelectedTypes.value.every(t => t === 'oauth' || t === 'setup-token' || t === 'apikey') ) }) @@ -1204,7 +1204,7 @@ const allOpenAIOAuth = computed(() => { targetSelectedPlatforms.value.length === 1 && targetSelectedPlatforms.value[0] === 'openai' && targetSelectedTypes.value.length > 0 && - targetSelectedTypes.value.every(t => t === 'oauth') + targetSelectedTypes.value.every(t => t === 'oauth' || t === 'setup-token') ) }) diff --git a/frontend/src/components/account/EditAccountModal.vue b/frontend/src/components/account/EditAccountModal.vue index dff99a6bf4..86b60a5b82 100644 --- a/frontend/src/components/account/EditAccountModal.vue +++ b/frontend/src/components/account/EditAccountModal.vue @@ -1342,7 +1342,7 @@
@@ -1372,7 +1372,7 @@
@@ -1432,7 +1432,7 @@
@@ -1446,7 +1446,7 @@

-
@@ -1662,7 +1662,7 @@
@@ -1717,7 +1717,7 @@
@@ -2979,7 +2979,7 @@ const syncFormFromAccount = (newAccount: Account | null) => { autoPause5hDisabled.value = extra?.auto_pause_5h_disabled === true autoPause7dDisabled.value = extra?.auto_pause_7d_disabled === true - // Load OpenAI passthrough toggle (OpenAI OAuth/API Key) + // Load OpenAI passthrough toggle (OpenAI OAuth/SetupToken/API Key) openaiPassthroughEnabled.value = false openAICompactMode.value = 'auto' openAIResponsesMode.value = 'auto' @@ -2992,7 +2992,7 @@ const syncFormFromAccount = (newAccount: Account | null) => { codexImageGenerationBridgeMode.value = 'inherit' anthropicPassthroughEnabled.value = false webSearchEmulationMode.value = 'default' - if (newAccount.platform === 'openai' && (newAccount.type === 'oauth' || newAccount.type === 'apikey')) { + if (newAccount.platform === 'openai' && (newAccount.type === 'oauth' || newAccount.type === 'setup-token' || newAccount.type === 'apikey')) { openaiPassthroughEnabled.value = extra?.openai_passthrough === true || extra?.openai_oauth_passthrough === true openAICompactMode.value = (extra?.openai_compact_mode as OpenAICompactMode) || 'auto' if (newAccount.type === 'apikey') { @@ -3024,7 +3024,7 @@ const syncFormFromAccount = (newAccount: Account | null) => { fallbackEnabledKeys: ['responses_websockets_v2_enabled', 'openai_ws_enabled'], defaultMode: OPENAI_WS_MODE_OFF }) - if (newAccount.type === 'oauth') { + if (newAccount.type === 'oauth' || newAccount.type === 'setup-token') { codexCLIOnlyEnabled.value = extra?.codex_cli_only === true codexCLIOnlyAppServerEnabled.value = extra?.codex_cli_only_allow_app_server === true @@ -4101,12 +4101,12 @@ const handleSubmit = async () => { updatePayload.extra = newExtra } - // For OpenAI OAuth/API Key accounts, handle passthrough mode in extra - if (props.account.platform === 'openai' && (props.account.type === 'oauth' || props.account.type === 'apikey')) { - const currentExtra = (props.account.extra as Record) || {} - const newExtra: Record = { ...currentExtra } + // For OpenAI OAuth/SetupToken/API Key accounts, handle passthrough mode in extra + if (props.account.platform === 'openai' && (props.account.type === 'oauth' || props.account.type === 'setup-token' || props.account.type === 'apikey')) { + const currentExtra = (props.account.extra as Record) || {} + const newExtra: Record = { ...currentExtra } const hadCodexCLIOnlyEnabled = currentExtra.codex_cli_only === true - if (props.account.type === 'oauth') { + if (props.account.type === 'oauth' || props.account.type === 'setup-token') { newExtra.openai_oauth_responses_websockets_v2_mode = openaiOAuthResponsesWebSocketV2Mode.value newExtra.openai_oauth_responses_websockets_v2_enabled = isOpenAIWSModeEnabled(openaiOAuthResponsesWebSocketV2Mode.value) } else if (props.account.type === 'apikey') { @@ -4161,7 +4161,7 @@ const handleSubmit = async () => { newExtra.codex_image_generation_bridge = codexImageGenerationBridgeMode.value === 'enabled' } - if (props.account.type === 'oauth') { + if (props.account.type === 'oauth' || props.account.type === 'setup-token') { if (codexCLIOnlyEnabled.value) { newExtra.codex_cli_only = true } else if (hadCodexCLIOnlyEnabled) { diff --git a/frontend/src/components/account/__tests__/EditAccountModal.spec.ts b/frontend/src/components/account/__tests__/EditAccountModal.spec.ts index 77c333fb2d..8d28b2b003 100644 --- a/frontend/src/components/account/__tests__/EditAccountModal.spec.ts +++ b/frontend/src/components/account/__tests__/EditAccountModal.spec.ts @@ -192,6 +192,17 @@ function buildAntigravityAccount(projectId = 'configured-project') { } as any } +function buildOpenAISetupTokenAccount() { + return { + ...buildAccount(), + type: 'setup-token', + extra: { + openai_oauth_responses_websockets_v2_mode: 'ctx_pool', + openai_oauth_responses_websockets_v2_enabled: true + } + } as any +} + function mountModal(account = buildAccount()) { return mount(EditAccountModal, { props: { @@ -487,6 +498,23 @@ describe('EditAccountModal', () => { expect(updateAccountMock.mock.calls[0]?.[1]?.extra).not.toHaveProperty('codex_image_generation_bridge_enabled') }) + it('setup-token account can select and submit OAuth WS mode', async () => { + const account = buildOpenAISetupTokenAccount() + updateAccountMock.mockReset() + checkMixedChannelRiskMock.mockReset() + checkMixedChannelRiskMock.mockResolvedValue({ has_risk: false }) + updateAccountMock.mockResolvedValue(account) + + const wrapper = mountModal(account) + + await wrapper.get('[data-testid="edit-openai-ws-mode-select"]').setValue('http_bridge') + await wrapper.get('form#edit-account-form').trigger('submit.prevent') + + expect(updateAccountMock).toHaveBeenCalledTimes(1) + expect(updateAccountMock.mock.calls[0]?.[1]?.extra?.openai_oauth_responses_websockets_v2_mode).toBe('http_bridge') + expect(updateAccountMock.mock.calls[0]?.[1]?.extra?.openai_oauth_responses_websockets_v2_enabled).toBe(true) + }) + it('allows saving apikey account when backend redacted api_key but credentials_status reports it exists', async () => { // 新前端 + 新后端:响应已脱敏,credentials 里没有 api_key,credentials_status.has_api_key=true const account = buildAccount()