feat(codex): add OAuth fingerprint convergence

This commit is contained in:
elky
2026-08-13 09:57:17 +08:00
parent 654c4f6978
commit 8cf381b0c3
16 changed files with 1111 additions and 4 deletions
@@ -333,6 +333,30 @@
/>
</div>
<div
v-if="form.provider_type === 'codex'"
class="flex items-center justify-between gap-4 p-3 border rounded-lg bg-muted/50"
data-testid="codex-fingerprint-convergence-setting"
>
<div class="space-y-0.5">
<Label
for="codex-fingerprint-convergence"
class="text-sm font-medium"
>
{{ legacyT('Codex OAuth 指纹收敛') }}
</Label>
<p class="text-xs text-muted-foreground leading-relaxed">
{{ legacyT('统一同一 OAuth 账号的设备与会话标识;关闭时保持现有透传行为。') }}
</p>
</div>
<Switch
id="codex-fingerprint-convergence"
:model-value="form.codex_fingerprint_convergence_enabled"
:aria-label="legacyT('Codex OAuth 指纹收敛')"
@update:model-value="(v: boolean) => form.codex_fingerprint_convergence_enabled = v"
/>
</div>
<div class="flex items-center justify-between gap-4 p-3 border rounded-lg bg-muted/50">
<div class="space-y-0.5">
<span class="text-sm font-medium">{{ legacyT('敏感信息保护') }}</span>
@@ -453,6 +477,8 @@ const form = ref({
request_timeout: undefined as number | undefined,
// 号池模式
pool_mode_enabled: false,
// Codex 专属配置
codex_fingerprint_convergence_enabled: false,
// Kiro 专属配置
kiro_simulated_cache_enabled: false,
})
@@ -483,6 +509,8 @@ function resetForm() {
request_timeout: undefined,
// 号池模式
pool_mode_enabled: false,
// Codex 专属配置
codex_fingerprint_convergence_enabled: false,
// Kiro 专属配置
kiro_simulated_cache_enabled: false,
}
@@ -517,6 +545,8 @@ function loadProviderData() {
request_timeout: props.provider.request_timeout ?? undefined,
// 号池模式
pool_mode_enabled: poolAdvanced !== null,
// Codex 专属配置
codex_fingerprint_convergence_enabled: props.provider.codex_fingerprint_convergence_enabled ?? false,
// Kiro 专属配置
kiro_simulated_cache_enabled: props.provider.kiro_simulated_cache_enabled ?? false,
}
@@ -540,6 +570,9 @@ watch(() => form.value.provider_type, () => {
if (form.value.provider_type !== 'kiro') {
form.value.kiro_simulated_cache_enabled = false
}
if (form.value.provider_type !== 'codex') {
form.value.codex_fingerprint_convergence_enabled = false
}
})
// 提交表单
@@ -586,6 +619,11 @@ const handleSubmit = async () => {
pool_advanced: form.value.pool_mode_enabled
? (currentPoolAdvanced ?? {})
: null,
...(form.value.provider_type === 'codex'
? {
codex_fingerprint_convergence_enabled: form.value.codex_fingerprint_convergence_enabled,
}
: {}),
...(form.value.provider_type === 'kiro'
? {
config: {
@@ -256,4 +256,93 @@ describe('ProviderFormDialog provider types', () => {
}),
)
})
it('shows fingerprint convergence only for Codex and submits the enabled setting', async () => {
mountDialog(null)
await settle()
expect(document.body.querySelector('[data-testid="codex-fingerprint-convergence-setting"]')).toBeNull()
const providerTypeSelect = [...document.body.querySelectorAll<HTMLSelectElement>('select')]
.find(select => select.querySelector('option[value="codex"]'))
if (!providerTypeSelect) throw new Error('Missing provider type select')
providerTypeSelect.value = 'codex'
providerTypeSelect.dispatchEvent(new Event('change', { bubbles: true }))
await nextTick()
const convergenceSwitch = document.body.querySelector<HTMLButtonElement>(
'#codex-fingerprint-convergence',
)
expect(convergenceSwitch).not.toBeNull()
expect(convergenceSwitch?.getAttribute('aria-checked')).toBe('false')
convergenceSwitch?.click()
await nextTick()
expect(convergenceSwitch?.getAttribute('aria-checked')).toBe('true')
await setInput('#name', 'Codex Provider')
clickButton('创建')
await settle()
expect(endpointMocks.createProvider).toHaveBeenCalledWith(
expect.objectContaining({
name: 'Codex Provider',
provider_type: 'codex',
codex_fingerprint_convergence_enabled: true,
}),
)
})
it('loads and can disable fingerprint convergence for an existing Codex provider', async () => {
mountDialog(makeProvider({
provider_type: 'codex',
codex_fingerprint_convergence_enabled: true,
}))
await settle()
const convergenceSwitch = document.body.querySelector<HTMLButtonElement>(
'#codex-fingerprint-convergence',
)
expect(convergenceSwitch?.getAttribute('aria-checked')).toBe('true')
convergenceSwitch?.click()
clickButton('保存')
await settle()
expect(endpointMocks.updateProvider).toHaveBeenCalledWith(
'provider-1',
expect.objectContaining({
provider_type: 'codex',
codex_fingerprint_convergence_enabled: false,
}),
)
})
it('clears and does not submit the setting after switching away from Codex', async () => {
mountDialog(null)
await settle()
const providerTypeSelect = [...document.body.querySelectorAll<HTMLSelectElement>('select')]
.find(select => select.querySelector('option[value="codex"]'))
if (!providerTypeSelect) throw new Error('Missing provider type select')
providerTypeSelect.value = 'codex'
providerTypeSelect.dispatchEvent(new Event('change', { bubbles: true }))
await nextTick()
document.body.querySelector<HTMLButtonElement>('#codex-fingerprint-convergence')?.click()
await nextTick()
providerTypeSelect.value = 'custom'
providerTypeSelect.dispatchEvent(new Event('change', { bubbles: true }))
await nextTick()
expect(document.body.querySelector('[data-testid="codex-fingerprint-convergence-setting"]')).toBeNull()
await setInput('#name', 'Custom Provider')
clickButton('创建')
await settle()
const payload = endpointMocks.createProvider.mock.calls[0]?.[0]
expect(payload).not.toHaveProperty('codex_fingerprint_convergence_enabled')
})
})