chore: 保存 Agent 安全优化前基线

This commit is contained in:
Logan.Li committed 2026-09-02 23:31:38 +08:00
1 parent 08696896f9
commit ff016347cd
116 files changed
+3858 -2485

No files matched your search

+68 -25
View File
@@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Check, CheckCircle2, ChevronDown, Loader2, Plus, Save, Trash2, Wifi } from 'lucide-react'
import { Check, CheckCircle2, ChevronDown, Plus, Save, Trash2, Wifi } from 'lucide-react'
import type { AiBillingMode, AiProviderConfig } from '../../../shared/types/ai.types'
import {
AI_PROVIDER_FAMILY_ORDER,
@@ -10,6 +10,7 @@ import {
resolveProviderFamily,
} from '../../ai/providerPresets'
import { useAiProviderStore } from '../../stores/aiProviderStore'
import Button from '../common/Button'
type TestState = 'idle' | 'testing' | 'success' | 'error'
@@ -133,7 +134,15 @@ export default function AiProviderSettings() {
}
const id = `manual-${Date.now().toString(36)}`
updateDraft({
models: [{ id, model: modelId, label: modelId || t('ai.customModel'), capability: 'chat', billing: defaultManualBilling() }],
models: [
{
id,
model: modelId,
label: modelId || t('ai.customModel'),
capability: 'chat',
billing: defaultManualBilling(),
},
],
defaultModelId: id,
})
}
@@ -208,13 +217,15 @@ export default function AiProviderSettings() {
</optgroup>
))}
</select>
<button
<Button
type="button"
variant="primary"
size="sm"
icon={<Plus className="h-3.5 w-3.5" />}
onClick={() => (presetId === 'custom' ? addCustom() : addPreset())}
className="ui-btn-primary flex items-center gap-1.5"
>
<Plus className="h-3.5 w-3.5" /> {t('ai.add')}
</button>
{t('ai.add')}
</Button>
</div>
<div className="grid min-h-[400px] grid-cols-[168px_minmax(0,1fr)] gap-6">
@@ -233,7 +244,7 @@ export default function AiProviderSettings() {
className={`w-full rounded-lg px-2.5 py-2 text-left transition-colors ${
selectedId === provider.id
? 'bg-accent-blue/12 text-accent-blue'
: 'text-content-secondary hover:bg-dark-700/50'
: 'hover:bg-dark-700/50 text-content-secondary'
}`}
>
<div className="flex items-center gap-1.5">
@@ -256,7 +267,11 @@ export default function AiProviderSettings() {
<div className="max-w-xl space-y-3.5">
<div className="grid grid-cols-2 gap-3">
<Field label={t('ai.name')}>
<input value={draft.name} onChange={(event) => updateDraft({ name: event.target.value })} className="ui-input-sm" />
<input
value={draft.name}
onChange={(event) => updateDraft({ name: event.target.value })}
className="ui-input-sm"
/>
</Field>
<Field label={t('ai.model')}>
{manualModel || draft.models.length === 0 ? (
@@ -286,7 +301,11 @@ export default function AiProviderSettings() {
</div>
{manualModel && draft.models.length > 0 && (
<button type="button" className="text-xs text-accent-blue hover:underline" onClick={() => setManualModel(false)}>
<button
type="button"
className="text-xs text-accent-blue hover:underline"
onClick={() => setManualModel(false)}
>
{t('ai.backToList')}
</button>
)}
@@ -298,7 +317,13 @@ export default function AiProviderSettings() {
value={apiKey}
disabled={draft.authStyle === 'none'}
onChange={(event) => setApiKey(event.target.value)}
placeholder={draft.hasApiKey ? t('ai.savedKeepBlank') : draft.authStyle === 'none' ? t('ai.notRequired') : 'sk-...'}
placeholder={
draft.hasApiKey
? t('ai.savedKeepBlank')
: draft.authStyle === 'none'
? t('ai.notRequired')
: 'sk-...'
}
className="ui-input-sm"
/>
</Field>
@@ -330,7 +355,9 @@ export default function AiProviderSettings() {
<Field label={t('ai.protocol')}>
<select
value={draft.protocol}
onChange={(event) => updateDraft({ protocol: event.target.value as AiProviderConfig['protocol'] })}
onChange={(event) =>
updateDraft({ protocol: event.target.value as AiProviderConfig['protocol'] })
}
className="ui-input-sm"
>
<option value="openai-chat">OpenAI Chat</option>
@@ -341,7 +368,9 @@ export default function AiProviderSettings() {
<Field label={t('ai.auth')}>
<select
value={draft.authStyle}
onChange={(event) => updateDraft({ authStyle: event.target.value as AiProviderConfig['authStyle'] })}
onChange={(event) =>
updateDraft({ authStyle: event.target.value as AiProviderConfig['authStyle'] })
}
className="ui-input-sm"
>
<option value="bearer">Bearer</option>
@@ -362,31 +391,45 @@ export default function AiProviderSettings() {
)}
<div className="flex flex-wrap items-center gap-2 pt-1">
<button
<Button
type="button"
onClick={() => void testConnection()}
variant="secondary"
size="sm"
disabled={!canTest}
className="ui-btn-secondary flex items-center gap-1.5"
loading={testState === 'testing'}
icon={testState === 'testing' ? undefined : <Wifi className="h-3.5 w-3.5" />}
onClick={() => void testConnection()}
>
{testState === 'testing' ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Wifi className="h-3.5 w-3.5" />}
{testState === 'testing' ? t('ai.testing') : t('ai.test')}
</button>
<button
</Button>
<Button
type="button"
onClick={() => void save()}
variant="primary"
size="sm"
disabled={!verified || saving}
className="ui-btn-primary flex items-center gap-1.5"
loading={saving}
icon={saving ? undefined : <Save className="h-3.5 w-3.5" />}
onClick={() => void save()}
>
{saving ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Save className="h-3.5 w-3.5" />}
{t('common:action.save')}
</button>
</Button>
{isStored && store.activeProviderId !== draft.id && (
<button type="button" onClick={() => void store.setActiveProvider(draft.id)} className="ui-btn-secondary">
<Button
type="button"
variant="secondary"
size="sm"
onClick={() => void store.setActiveProvider(draft.id)}
>
{t('ai.setActive')}
</button>
</Button>
)}
<div className="flex-1" />
<button type="button" title={t('ai.deleteTitle')} onClick={() => void remove()} className="rounded p-1.5 text-txt-faint hover:bg-dark-700 hover:text-red-300">
<button
type="button"
title={t('ai.deleteTitle')}
onClick={() => void remove()}
className="rounded p-1.5 text-txt-faint hover:bg-dark-700 hover:text-red-300"
>
<Trash2 className="h-3.5 w-3.5" />
</button>
</div>