chore: 保存 Agent 安全优化前基线
This commit is contained in:
1 parent
08696896f9
commit
ff016347cd
116 files changed
+3858
-2485
No files matched your search
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user