Skip to content

Commit 8804b13

Browse files
committed
fix: backport upstream provider form updates
1 parent 4f6ff9b commit 8804b13

17 files changed

Lines changed: 470 additions & 313 deletions

src/components/providers/utils.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ export const getProviderPrimaryApiKey = (
162162
export const buildApiKeyEntry = (input?: Partial<ApiKeyEntry>): ApiKeyEntry => ({
163163
apiKey: input?.apiKey ?? '',
164164
proxyUrl: input?.proxyUrl ?? '',
165-
headers: input?.headers ?? {},
166165
});
167166

168167
export const ampcodeMappingsToEntries = (mappings?: AmpcodeModelMapping[]): ModelEntry[] => {

src/pages/AiProvidersAmpcodeEditPage.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,10 @@ export function AiProvidersAmpcodeEditPage() {
392392
type="password"
393393
value={form.upstreamApiKey}
394394
onChange={(e) => setForm((prev) => ({ ...prev, upstreamApiKey: e.target.value }))}
395+
autoComplete="new-password"
396+
data-1p-ignore="true"
397+
data-lpignore="true"
398+
data-bwignore="true"
395399
disabled={loading || saving || disableControls}
396400
hint={t('ai_providers.ampcode_upstream_api_key_hint')}
397401
/>
@@ -450,7 +454,9 @@ export function AiProvidersAmpcodeEditPage() {
450454
onClick={() => {
451455
setUpstreamApiKeysDirty(true);
452456
setForm((prev) => {
453-
const nextEntries = prev.upstreamApiKeyEntries.filter((_, entryIndex) => entryIndex !== index);
457+
const nextEntries = prev.upstreamApiKeyEntries.filter(
458+
(_, entryIndex) => entryIndex !== index
459+
);
454460
return {
455461
...prev,
456462
upstreamApiKeyEntries: nextEntries.length
@@ -469,6 +475,10 @@ export function AiProvidersAmpcodeEditPage() {
469475
placeholder={t('ai_providers.ampcode_upstream_api_keys_upstream_placeholder')}
470476
aria-label={t('ai_providers.ampcode_upstream_api_keys_upstream_placeholder')}
471477
value={entry.upstreamApiKey}
478+
autoComplete="new-password"
479+
data-1p-ignore="true"
480+
data-lpignore="true"
481+
data-bwignore="true"
472482
onChange={(e) => {
473483
const value = e.target.value;
474484
setUpstreamApiKeysDirty(true);

src/pages/AiProvidersClaudeEditLayout.tsx

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@ import { Outlet, useLocation, useNavigate, useParams } from 'react-router-dom';
44
import { useTranslation } from 'react-i18next';
55
import { useUnsavedChangesGuard } from '@/hooks/useUnsavedChangesGuard';
66
import { providersApi } from '@/services/api';
7-
import { useAuthStore, useClaudeEditDraftStore, useConfigStore, useNotificationStore } from '@/stores';
7+
import {
8+
useAuthStore,
9+
useClaudeEditDraftStore,
10+
useConfigStore,
11+
useNotificationStore,
12+
} from '@/stores';
813
import type { ProviderKeyConfig } from '@/types';
914
import type { ModelInfo } from '@/utils/models';
1015
import type { ModelEntry, ProviderFormState } from '@/components/providers/types';
1116
import { buildHeaderObject, headersToEntries, normalizeHeaderEntries } from '@/utils/headers';
12-
import { areKeyValueEntriesEqual, areModelEntriesEqual, areStringArraysEqual } from '@/utils/compare';
17+
import {
18+
areKeyValueEntriesEqual,
19+
areModelEntriesEqual,
20+
areStringArraysEqual,
21+
} from '@/utils/compare';
1322
import { excludedModelsToText, parseExcludedModels } from '@/components/providers/utils';
1423
import { modelsToEntries } from '@/components/ui/modelInputListUtils';
1524
import type { ClaudeEditBaseline } from '@/stores/useClaudeEditDraftStore';
@@ -80,7 +89,10 @@ const normalizeClaudeModelEntries = (entries: Array<{ name: string; alias: strin
8089

8190
const normalizeCloakConfig = (cloak: ProviderFormState['cloak']) => {
8291
if (!cloak) return null;
83-
const mode = String(cloak.mode ?? '').trim().toLowerCase() || 'auto';
92+
const mode =
93+
String(cloak.mode ?? '')
94+
.trim()
95+
.toLowerCase() || 'auto';
8496
const strictMode = Boolean(cloak.strictMode);
8597
const sensitiveWords = Array.isArray(cloak.sensitiveWords)
8698
? cloak.sensitiveWords.map((word) => String(word ?? '').trim()).filter(Boolean)
@@ -95,7 +107,9 @@ const normalizeCloakConfig = (cloak: ProviderFormState['cloak']) => {
95107
const buildClaudeBaseline = (form: ProviderFormState): ClaudeEditBaseline => ({
96108
apiKey: String(form.apiKey ?? '').trim(),
97109
priority:
98-
form.priority !== undefined && Number.isFinite(form.priority) ? Math.trunc(form.priority) : null,
110+
form.priority !== undefined && Number.isFinite(form.priority)
111+
? Math.trunc(form.priority)
112+
: null,
99113
prefix: String(form.prefix ?? '').trim(),
100114
baseUrl: String(form.baseUrl ?? '').trim(),
101115
proxyUrl: String(form.proxyUrl ?? '').trim(),
@@ -105,7 +119,10 @@ const buildClaudeBaseline = (form: ProviderFormState): ClaudeEditBaseline => ({
105119
cloak: normalizeCloakConfig(form.cloak),
106120
});
107121

108-
const areCloakConfigsEqual = (left: ClaudeEditBaseline['cloak'], right: ClaudeEditBaseline['cloak']) => {
122+
const areCloakConfigsEqual = (
123+
left: ClaudeEditBaseline['cloak'],
124+
right: ClaudeEditBaseline['cloak']
125+
) => {
109126
if (left === right) return true;
110127
if (!left || !right) return false;
111128
if (left.mode !== right.mode || left.strictMode !== right.strictMode) return false;
@@ -248,6 +265,7 @@ export function AiProvidersClaudeEditLayout() {
248265
if (initialData) {
249266
const seededForm: ProviderFormState = {
250267
...initialData,
268+
apiKey: '',
251269
headers: headersToEntries(initialData.headers),
252270
modelEntries: modelsToEntries(initialData.models),
253271
excludedText: excludedModelsToText(initialData.excludedModels),
@@ -332,8 +350,7 @@ export function AiProvidersClaudeEditLayout() {
332350
enabled: canGuard,
333351
shouldBlock: ({ nextLocation }) => {
334352
const nextPath = nextLocation.pathname;
335-
const isWithinRoot =
336-
nextPath === editorRootPath || nextPath.startsWith(`${editorRootPath}/`);
353+
const isWithinRoot = nextPath === editorRootPath || nextPath.startsWith(`${editorRootPath}/`);
337354
return isDirty && !isWithinRoot;
338355
},
339356
dialog: {
@@ -392,7 +409,10 @@ export function AiProvidersClaudeEditLayout() {
392409
});
393410

394411
if (addedCount > 0) {
395-
showNotification(t('ai_providers.claude_models_fetch_added', { count: addedCount }), 'success');
412+
showNotification(
413+
t('ai_providers.claude_models_fetch_added', { count: addedCount }),
414+
'success'
415+
);
396416
}
397417
},
398418
[setForm, showNotification, t]
@@ -406,7 +426,7 @@ export function AiProvidersClaudeEditLayout() {
406426
setSaving(true);
407427
try {
408428
const payload: ProviderKeyConfig = {
409-
apiKey: form.apiKey.trim(),
429+
apiKey: form.apiKey.trim() || initialData?.apiKey?.trim() || '',
410430
priority: form.priority !== undefined ? Math.trunc(form.priority) : undefined,
411431
prefix: form.prefix?.trim() || undefined,
412432
baseUrl: (form.baseUrl ?? '').trim() || undefined,
@@ -434,7 +454,9 @@ export function AiProvidersClaudeEditLayout() {
434454
updateConfigValue('claude-api-key', nextList);
435455
clearCache('claude-api-key');
436456
showNotification(
437-
editIndex !== null ? t('notification.claude_config_updated') : t('notification.claude_config_added'),
457+
editIndex !== null
458+
? t('notification.claude_config_updated')
459+
: t('notification.claude_config_added'),
438460
'success'
439461
);
440462
allowNextNavigation();
@@ -454,6 +476,7 @@ export function AiProvidersClaudeEditLayout() {
454476
editIndex,
455477
form,
456478
handleBack,
479+
initialData,
457480
invalidIndex,
458481
invalidIndexParam,
459482
resolvedLoading,
@@ -466,27 +489,29 @@ export function AiProvidersClaudeEditLayout() {
466489

467490
return (
468491
<Outlet
469-
context={{
470-
hasIndexParam,
471-
editIndex,
472-
invalidIndexParam,
473-
invalidIndex,
474-
disableControls,
475-
loading: resolvedLoading,
476-
saving,
477-
form,
478-
setForm,
479-
testModel,
480-
setTestModel,
481-
testStatus,
482-
setTestStatus,
483-
testMessage,
484-
setTestMessage,
485-
availableModels,
486-
handleBack,
487-
handleSave,
488-
mergeDiscoveredModels,
489-
} satisfies ClaudeEditOutletContext}
492+
context={
493+
{
494+
hasIndexParam,
495+
editIndex,
496+
invalidIndexParam,
497+
invalidIndex,
498+
disableControls,
499+
loading: resolvedLoading,
500+
saving,
501+
form,
502+
setForm,
503+
testModel,
504+
setTestModel,
505+
testStatus,
506+
setTestStatus,
507+
testMessage,
508+
setTestMessage,
509+
availableModels,
510+
handleBack,
511+
handleSave,
512+
mergeDiscoveredModels,
513+
} satisfies ClaudeEditOutletContext
514+
}
490515
/>
491516
);
492517
}

src/pages/AiProvidersClaudeEditPage.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,10 @@ export function AiProvidersClaudeEditPage() {
307307
label={t('ai_providers.claude_add_modal_key_label')}
308308
value={form.apiKey}
309309
onChange={(e) => setForm((prev) => ({ ...prev, apiKey: e.target.value }))}
310+
autoComplete="new-password"
311+
data-1p-ignore="true"
312+
data-lpignore="true"
313+
data-bwignore="true"
310314
disabled={saving || disableControls || isTesting}
311315
/>
312316
<Input
@@ -358,7 +362,9 @@ export function AiProvidersClaudeEditPage() {
358362

359363
<div className={styles.modelConfigSection}>
360364
<div className={styles.modelConfigHeader}>
361-
<label className={styles.modelConfigTitle}>{t('ai_providers.claude_models_label')}</label>
365+
<label className={styles.modelConfigTitle}>
366+
{t('ai_providers.claude_models_label')}
367+
</label>
362368
<div className={styles.modelConfigToolbar}>
363369
<Button
364370
variant="secondary"
@@ -403,7 +409,9 @@ export function AiProvidersClaudeEditPage() {
403409

404410
<div className={styles.modelTestPanel}>
405411
<div className={styles.modelTestMeta}>
406-
<label className={styles.modelTestLabel}>{t('ai_providers.claude_test_title')}</label>
412+
<label className={styles.modelTestLabel}>
413+
{t('ai_providers.claude_test_title')}
414+
</label>
407415
<span className={styles.modelTestHint}>{t('ai_providers.claude_test_hint')}</span>
408416
</div>
409417
<div className={styles.modelTestControls}>
@@ -479,7 +487,9 @@ export function AiProvidersClaudeEditPage() {
479487

480488
<div className={styles.modelConfigSection}>
481489
<div className={styles.modelConfigHeader}>
482-
<label className={styles.modelConfigTitle}>{t('ai_providers.claude_cloak_title')}</label>
490+
<label className={styles.modelConfigTitle}>
491+
{t('ai_providers.claude_cloak_title')}
492+
</label>
483493
<div className={styles.modelConfigToolbar}>
484494
<ToggleSwitch
485495
checked={Boolean(form.cloak)}
@@ -492,9 +502,12 @@ export function AiProvidersClaudeEditPage() {
492502
return { ...prev, cloak: undefined };
493503
}
494504

495-
const restored = prev.cloak
496-
?? lastCloakConfigRef.current
497-
?? { mode: 'auto', strictMode: false, sensitiveWords: [] };
505+
const restored = prev.cloak ??
506+
lastCloakConfigRef.current ?? {
507+
mode: 'auto',
508+
strictMode: false,
509+
sensitiveWords: [],
510+
};
498511
const mode = String(restored.mode ?? 'auto').trim() || 'auto';
499512
return {
500513
...prev,
@@ -574,7 +587,9 @@ export function AiProvidersClaudeEditPage() {
574587
rows={3}
575588
disabled={saving || disableControls || isTesting}
576589
/>
577-
<div className="hint">{t('ai_providers.claude_cloak_sensitive_words_hint')}</div>
590+
<div className="hint">
591+
{t('ai_providers.claude_cloak_sensitive_words_hint')}
592+
</div>
578593
</div>
579594
</>
580595
) : null}

src/pages/AiProvidersCodexEditPage.tsx

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ import { modelsApi, providersApi } from '@/services/api';
1616
import { useAuthStore, useConfigStore, useNotificationStore } from '@/stores';
1717
import type { ProviderKeyConfig } from '@/types';
1818
import { buildHeaderObject, headersToEntries, normalizeHeaderEntries } from '@/utils/headers';
19-
import { areKeyValueEntriesEqual, areModelEntriesEqual, areStringArraysEqual } from '@/utils/compare';
19+
import {
20+
areKeyValueEntriesEqual,
21+
areModelEntriesEqual,
22+
areStringArraysEqual,
23+
} from '@/utils/compare';
2024
import { entriesToModels, modelsToEntries } from '@/components/ui/modelInputListUtils';
2125
import { excludedModelsToText, parseExcludedModels } from '@/components/providers/utils';
2226
import type { ModelInfo } from '@/utils/models';
@@ -99,7 +103,9 @@ const buildCodexBaseline = (form: CodexFormState): CodexFormBaseline => ({
99103
apiKey: String(form.apiKey ?? '').trim(),
100104
disableCooling: Boolean(form.disableCooling),
101105
priority:
102-
form.priority !== undefined && Number.isFinite(form.priority) ? Math.trunc(form.priority) : null,
106+
form.priority !== undefined && Number.isFinite(form.priority)
107+
? Math.trunc(form.priority)
108+
: null,
103109
prefix: String(form.prefix ?? '').trim(),
104110
baseUrl: String(form.baseUrl ?? '').trim(),
105111
websockets: Boolean(form.websockets),
@@ -208,7 +214,7 @@ export function AiProvidersCodexEditPage() {
208214

209215
if (initialData) {
210216
const nextForm: CodexFormState = {
211-
apiKey: initialData.apiKey ?? '',
217+
apiKey: '',
212218
disableCooling: Boolean(initialData.disableCooling),
213219
priority: initialData.priority,
214220
prefix: initialData.prefix ?? '',
@@ -352,10 +358,10 @@ export function AiProvidersCodexEditPage() {
352358
const hasCustomAuthorization = Object.keys(headerObject).some(
353359
(key) => key.toLowerCase() === 'authorization'
354360
);
355-
const apiKey = form.apiKey?.trim() || undefined;
361+
const apiKey = form.apiKey?.trim() || initialData?.apiKey?.trim() || '';
356362
const list = await modelsApi.fetchV1ModelsViaApiCall(
357363
form.baseUrl ?? '',
358-
hasCustomAuthorization ? undefined : apiKey,
364+
hasCustomAuthorization ? undefined : apiKey || undefined,
359365
headerObject
360366
);
361367
if (modelDiscoveryRequestIdRef.current !== requestId) return;
@@ -370,7 +376,7 @@ export function AiProvidersCodexEditPage() {
370376
setModelDiscoveryFetching(false);
371377
}
372378
}
373-
}, [form.apiKey, form.baseUrl, form.headers, t]);
379+
}, [form.apiKey, form.baseUrl, form.headers, initialData, t]);
374380

375381
useEffect(() => {
376382
if (!modelDiscoveryOpen) {
@@ -393,7 +399,8 @@ export function AiProvidersCodexEditPage() {
393399
const hasCustomAuthorization = Object.keys(headerObject).some(
394400
(key) => key.toLowerCase() === 'authorization'
395401
);
396-
const hasApiKeyField = Boolean(form.apiKey?.trim());
402+
const apiKey = form.apiKey?.trim() || initialData?.apiKey?.trim() || '';
403+
const hasApiKeyField = Boolean(apiKey);
397404
const canAutoFetch = hasApiKeyField || hasCustomAuthorization;
398405

399406
if (!canAutoFetch) return;
@@ -402,12 +409,19 @@ export function AiProvidersCodexEditPage() {
402409
.sort(([a], [b]) => a.toLowerCase().localeCompare(b.toLowerCase()))
403410
.map(([key, value]) => `${key}:${value}`)
404411
.join('|');
405-
const signature = `${nextEndpoint}||${form.apiKey?.trim()}||${headerSignature}`;
412+
const signature = `${nextEndpoint}||${apiKey}||${headerSignature}`;
406413
if (autoFetchSignatureRef.current === signature) return;
407414
autoFetchSignatureRef.current = signature;
408415

409416
void fetchCodexModelDiscovery();
410-
}, [fetchCodexModelDiscovery, form.apiKey, form.baseUrl, form.headers, modelDiscoveryOpen]);
417+
}, [
418+
fetchCodexModelDiscovery,
419+
form.apiKey,
420+
form.baseUrl,
421+
form.headers,
422+
initialData,
423+
modelDiscoveryOpen,
424+
]);
411425

412426
useEffect(() => {
413427
const availableNames = new Set(discoveredModels.map((model) => model.name));
@@ -462,7 +476,7 @@ export function AiProvidersCodexEditPage() {
462476
const handleSave = useCallback(async () => {
463477
if (!canSave) return;
464478

465-
const trimmedApiKey = (form.apiKey ?? '').trim();
479+
const trimmedApiKey = (form.apiKey ?? '').trim() || initialData?.apiKey?.trim() || '';
466480
if (!trimmedApiKey) {
467481
showNotification(t('notification.codex_key_required'), 'error');
468482
return;
@@ -519,6 +533,7 @@ export function AiProvidersCodexEditPage() {
519533
editIndex,
520534
form,
521535
handleBack,
536+
initialData,
522537
showNotification,
523538
t,
524539
updateConfigValue,
@@ -579,6 +594,10 @@ export function AiProvidersCodexEditPage() {
579594
placeholder={t('ai_providers.codex_add_modal_key_placeholder')}
580595
value={form.apiKey}
581596
onChange={(e) => setForm((prev) => ({ ...prev, apiKey: e.target.value }))}
597+
autoComplete="new-password"
598+
data-1p-ignore="true"
599+
data-lpignore="true"
600+
data-bwignore="true"
582601
disabled={disableControls || saving}
583602
/>
584603
<Input

0 commit comments

Comments
 (0)