diff --git a/src/App.tsx b/src/App.tsx index d1497ffce..33aebf00e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1533,7 +1533,7 @@ function App() { }} onSubmit={handleEditProvider} appId={activeApp} - isProxyTakeover={isProxyRunning && isCurrentAppTakeoverActive} + isProxyTakeover={isCurrentAppTakeoverActive} /> {effectiveUsageProvider && ( diff --git a/src/components/providers/EditProviderDialog.tsx b/src/components/providers/EditProviderDialog.tsx index 2536eb9d6..688e2db6f 100644 --- a/src/components/providers/EditProviderDialog.tsx +++ b/src/components/providers/EditProviderDialog.tsx @@ -247,6 +247,7 @@ export function EditProviderDialog({ onSubmittingChange={setIsFormSubmitting} initialData={initialData} showButtons={false} + isProxyTakeover={isProxyTakeover} /> ); diff --git a/src/components/providers/forms/CodexConfigEditor.tsx b/src/components/providers/forms/CodexConfigEditor.tsx index a7a375292..8e23ae32b 100644 --- a/src/components/providers/forms/CodexConfigEditor.tsx +++ b/src/components/providers/forms/CodexConfigEditor.tsx @@ -1,6 +1,9 @@ import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Info } from "lucide-react"; import { CodexAuthSection, CodexConfigSection } from "./CodexConfigSections"; import { CodexCommonConfigModal } from "./CodexCommonConfigModal"; +import { Alert, AlertDescription } from "@/components/ui/alert"; interface CodexConfigEditorProps { authValue: string; @@ -11,6 +14,8 @@ interface CodexConfigEditorProps { showRemoteCompaction?: boolean; + isProxyTakeover?: boolean; + onAuthChange: (value: string) => void; onConfigChange: (value: string) => void; @@ -43,6 +48,7 @@ const CodexConfigEditor: React.FC = ({ configValue, providerName, showRemoteCompaction, + isProxyTakeover = false, onAuthChange, onConfigChange, onAuthBlur, @@ -57,6 +63,7 @@ const CodexConfigEditor: React.FC = ({ onExtract, isExtracting, }) => { + const { t } = useTranslation(); const [isCommonConfigModalOpen, setIsCommonConfigModalOpen] = useState(false); const handleCloseCommonConfigModal = () => { @@ -66,12 +73,22 @@ const CodexConfigEditor: React.FC = ({ return (
+ {isProxyTakeover && ( + + + + {t("codexConfig.proxyTakeoverStorageNotice")} + + + )} + {/* Auth JSON Section */} {/* Config TOML Section */} @@ -85,6 +102,7 @@ const CodexConfigEditor: React.FC = ({ onEditCommonConfig={() => setIsCommonConfigModalOpen(true)} commonConfigError={commonConfigError} configError={configError} + isProxyTakeover={isProxyTakeover} /> {/* Common Config Modal */} diff --git a/src/components/providers/forms/CodexConfigSections.tsx b/src/components/providers/forms/CodexConfigSections.tsx index ddfeada52..275eaa106 100644 --- a/src/components/providers/forms/CodexConfigSections.tsx +++ b/src/components/providers/forms/CodexConfigSections.tsx @@ -29,6 +29,7 @@ interface CodexAuthSectionProps { onChange: (value: string) => void; onBlur?: () => void; error?: string; + isProxyTakeover?: boolean; } /** @@ -39,6 +40,7 @@ export const CodexAuthSection: React.FC = ({ onChange, onBlur, error, + isProxyTakeover = false, }) => { const { t } = useTranslation(); const [isDarkMode, setIsDarkMode] = useState(false); @@ -90,7 +92,11 @@ export const CodexAuthSection: React.FC = ({ {!error && (

- {t("codexConfig.authJsonHint")} + {t( + isProxyTakeover + ? "codexConfig.authJsonStorageHint" + : "codexConfig.authJsonHint", + )}

)}
@@ -107,6 +113,7 @@ interface CodexConfigSectionProps { onEditCommonConfig: () => void; commonConfigError?: string; configError?: string; + isProxyTakeover?: boolean; } /** @@ -122,6 +129,7 @@ export const CodexConfigSection: React.FC = ({ onEditCommonConfig, commonConfigError, configError, + isProxyTakeover = false, }) => { const { t } = useTranslation(); const [isDarkMode, setIsDarkMode] = useState(false); @@ -367,7 +375,11 @@ export const CodexConfigSection: React.FC = ({ {!configError && (

- {t("codexConfig.configTomlHint")} + {t( + isProxyTakeover + ? "codexConfig.configTomlStorageHint" + : "codexConfig.configTomlHint", + )}

)} diff --git a/src/components/providers/forms/ProviderForm.tsx b/src/components/providers/forms/ProviderForm.tsx index c22572fdb..191b067ee 100644 --- a/src/components/providers/forms/ProviderForm.tsx +++ b/src/components/providers/forms/ProviderForm.tsx @@ -230,6 +230,7 @@ export interface ProviderFormProps { iconColor?: string; }; showButtons?: boolean; + isProxyTakeover?: boolean; } export function ProviderForm(props: ProviderFormProps) { @@ -251,6 +252,7 @@ function ProviderFormFull({ onSubmittingChange, initialData, showButtons = true, + isProxyTakeover = false, }: ProviderFormProps) { if (appId === "claude-desktop") { throw new Error("ProviderFormFull should not receive claude-desktop"); @@ -2165,6 +2167,7 @@ function ProviderFormFull({ configValue={codexConfig} providerName={form.watch("name")} showRemoteCompaction={category !== "official"} + isProxyTakeover={isProxyTakeover} onAuthChange={setCodexAuth} onConfigChange={handleCodexConfigChange} useCommonConfig={useCodexCommonConfigFlag} diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index f4499ce70..ec1e3d3b1 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1104,8 +1104,11 @@ "authJson": "auth.json (JSON) *", "authJsonPlaceholder": "{\n \"OPENAI_API_KEY\": \"sk-your-api-key-here\"\n}", "authJsonHint": "Codex auth.json configuration content", + "authJsonStorageHint": "Proxy takeover is showing provider-stored auth, not the live ~/.codex/auth.json; OAuth access tokens remain in the live auth.json.", "configToml": "config.toml (TOML)", "configTomlHint": "Codex config.toml configuration content", + "configTomlStorageHint": "Proxy takeover is showing the provider upstream config; the live config.toml is temporarily managed with the local proxy URL and PROXY_MANAGED.", + "proxyTakeoverStorageNotice": "This app is under proxy takeover. You are editing the provider's stored config; the live config is temporarily managed by the proxy, so this form does not need to show 127.0.0.1 or PROXY_MANAGED.", "writeCommonConfig": "Write Common Config", "editCommonConfig": "Edit Common Config", "editCommonConfigTitle": "Edit Codex Common Config Snippet", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 92cbc4f6d..d73694696 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1104,8 +1104,11 @@ "authJson": "auth.json (JSON) *", "authJsonPlaceholder": "{\n \"OPENAI_API_KEY\": \"sk-your-api-key-here\"\n}", "authJsonHint": "Codex の auth.json 設定内容", + "authJsonStorageHint": "プロキシ引き継ぎ中は、live の ~/.codex/auth.json ではなくプロバイダー保存 auth を表示しています。OAuth アクセストークンは live auth.json に保持されます。", "configToml": "config.toml (TOML)", "configTomlHint": "Codex の config.toml 設定内容", + "configTomlStorageHint": "プロキシ引き継ぎ中はプロバイダーの upstream config を表示しています。live config.toml は一時的にローカルプロキシ URL と PROXY_MANAGED で管理されます。", + "proxyTakeoverStorageNotice": "このアプリはプロキシ引き継ぎ中です。ここで編集しているのはプロバイダーの保存設定で、実際の live 設定は一時的にプロキシが管理します。このフォームに 127.0.0.1 や PROXY_MANAGED が表示される必要はありません。", "writeCommonConfig": "共通設定を書き込む", "editCommonConfig": "共通設定を編集", "editCommonConfigTitle": "Codex 共通設定スニペットを編集", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 7d2410c38..071f30724 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -1076,8 +1076,11 @@ "authJson": "auth.json (JSON) *", "authJsonPlaceholder": "{\n \"OPENAI_API_KEY\": \"sk-your-api-key-here\"\n}", "authJsonHint": "Codex auth.json 設定內容", + "authJsonStorageHint": "代理接管中顯示的是供應商儲存 auth,不是即時 ~/.codex/auth.json;OAuth Access Token 會保留在 live auth.json 中。", "configToml": "config.toml (TOML)", "configTomlHint": "Codex config.toml 設定內容", + "configTomlStorageHint": "代理接管中顯示的是供應商上游 config;即時 config.toml 會由代理暫時寫為本機位址和 PROXY_MANAGED。", + "proxyTakeoverStorageNotice": "目前應用處於代理接管狀態。這裡編輯的是供應商儲存配置,真實 live 配置會由代理暫時接管;儲存不需要這裡顯示 127.0.0.1 或 PROXY_MANAGED。", "writeCommonConfig": "寫入通用設定", "editCommonConfig": "編輯通用設定", "editCommonConfigTitle": "編輯 Codex 通用設定片段", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 0afb08854..b38ec6a43 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1104,8 +1104,11 @@ "authJson": "auth.json (JSON) *", "authJsonPlaceholder": "{\n \"OPENAI_API_KEY\": \"sk-your-api-key-here\"\n}", "authJsonHint": "Codex auth.json 配置内容", + "authJsonStorageHint": "代理接管中显示的是供应商存储 auth,不是实时 ~/.codex/auth.json;OAuth Access Token 会保留在 live auth.json 中。", "configToml": "config.toml (TOML)", "configTomlHint": "Codex config.toml 配置内容", + "configTomlStorageHint": "代理接管中显示的是供应商上游 config;实时 config.toml 会由代理临时写为本地地址和 PROXY_MANAGED。", + "proxyTakeoverStorageNotice": "当前应用处于代理接管状态。这里编辑的是供应商存储配置,真实 live 配置会由代理临时接管;保存不会要求这里显示 127.0.0.1 或 PROXY_MANAGED。", "writeCommonConfig": "写入通用配置", "editCommonConfig": "编辑通用配置", "editCommonConfigTitle": "编辑 Codex 通用配置片段", diff --git a/tests/components/EditProviderDialog.test.tsx b/tests/components/EditProviderDialog.test.tsx index b4b35a29d..43a5fcb58 100644 --- a/tests/components/EditProviderDialog.test.tsx +++ b/tests/components/EditProviderDialog.test.tsx @@ -42,6 +42,7 @@ vi.mock("@/components/providers/forms/ProviderForm", () => ({ ProviderForm: ({ initialData, onSubmit, + isProxyTakeover, }: { initialData: { name?: string; @@ -61,6 +62,7 @@ vi.mock("@/components/providers/forms/ProviderForm", () => ({ icon?: string; iconColor?: string; }) => void; + isProxyTakeover?: boolean; }) => (
({ {JSON.stringify(initialData.settingsConfig ?? {})} + + {isProxyTakeover ? "true" : "false"} +
), })); @@ -153,4 +158,48 @@ describe("EditProviderDialog", () => { modelCatalog: dbModelCatalog, }); }); + + it("代理接管中编辑 Codex 供应商时展示数据库配置而不是读取 live 代理配置", async () => { + const provider: Provider = { + id: "deepseek", + name: "DeepSeek", + category: "custom", + settingsConfig: { + auth: { + OPENAI_API_KEY: "db-key", + }, + config: + 'model_provider = "custom"\n[model_providers.custom]\nbase_url = "https://api.deepseek.com/v1"\n', + }, + }; + + apiMocks.getCurrent.mockResolvedValue(provider.id); + apiMocks.getLiveProviderSettings.mockResolvedValue({ + auth: { + OPENAI_API_KEY: "PROXY_MANAGED", + }, + config: + 'model_provider = "custom"\n[model_providers.custom]\nbase_url = "http://127.0.0.1:15721/v1"\nexperimental_bearer_token = "PROXY_MANAGED"\n', + }); + + render( + , + ); + + await waitFor(() => { + expect(screen.getByTestId("is-proxy-takeover").textContent).toBe("true"); + }); + + expect(apiMocks.getLiveProviderSettings).not.toHaveBeenCalled(); + expect( + JSON.parse(screen.getByTestId("settings-config").textContent ?? "{}"), + ).toEqual(provider.settingsConfig); + }); });