From 7eb406243ef64fd14aeb1997041fd3726d53f6a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Sun, 16 Aug 2026 00:33:26 +0800 Subject: [PATCH] fix(desktop): warn on proxy-only settings takeover #1219 --- .../src/__tests__/generalSettings.test.tsx | 12 +++++++ desktop/src/i18n/locales/en.ts | 1 + desktop/src/i18n/locales/jp.ts | 1 + desktop/src/i18n/locales/kr.ts | 1 + desktop/src/i18n/locales/zh-TW.ts | 1 + desktop/src/i18n/locales/zh.ts | 1 + .../src/pages/settings/GeneralSettings.tsx | 9 ++++++ desktop/src/stores/settingsStore.ts | 16 ++++++++++ .../settingsStoreProxyManagedWarning.test.ts | 32 +++++++++++++++++++ 9 files changed, 74 insertions(+) create mode 100644 desktop/src/stores/settingsStoreProxyManagedWarning.test.ts diff --git a/desktop/src/__tests__/generalSettings.test.tsx b/desktop/src/__tests__/generalSettings.test.tsx index a1eec2fa..bfe20833 100644 --- a/desktop/src/__tests__/generalSettings.test.tsx +++ b/desktop/src/__tests__/generalSettings.test.tsx @@ -246,6 +246,7 @@ describe('Settings > General tab', () => { traceCapture: { enabled: true, storageDir: '/Users/test/.claude/cc-haha/traces' }, chatSendBehavior: 'enter', responseLanguage: '', + proxyManagedSettingsWarning: false, uiZoom: 1, webSearch: { mode: 'auto', tavilyApiKey: '', braveApiKey: '' }, network: { @@ -1710,6 +1711,17 @@ describe('Settings > General tab', () => { expect(screen.getByText('MCP')).toBeInTheDocument() expect(screen.getByText('Plugins')).toBeInTheDocument() }) + + it('warns when the user settings contain only a proxy-managed placeholder', async () => { + useSettingsStore.setState({ proxyManagedSettingsWarning: true }) + + render() + fireEvent.click(screen.getByRole('button', { name: 'General' })) + + expect(await screen.findByRole('alert')).toHaveTextContent( + 'Your user settings contain only a PROXY_MANAGED proxy placeholder', + ) + }) }) describe('Settings > Providers tab', () => { diff --git a/desktop/src/i18n/locales/en.ts b/desktop/src/i18n/locales/en.ts index 374b3895..ae36990a 100644 --- a/desktop/src/i18n/locales/en.ts +++ b/desktop/src/i18n/locales/en.ts @@ -1443,6 +1443,7 @@ Row 9, all 8 cells: continuing from straight down, turning left through lower-le // Settings > General 'settings.general.appearanceTitle': 'Appearance', + 'settings.general.proxyManagedSettingsWarning': 'Your user settings contain only a PROXY_MANAGED proxy placeholder, so another proxy tool may have replaced the remaining settings. If this was not intentional, disable CC Switch routing takeover and restore settings.json from a backup. Otherwise, you can ignore this notice.', 'settings.general.appearanceDescription': 'Switch between the six paper and ink palettes.', 'settings.general.appearance.paper': 'Paper', 'settings.general.appearance.warmClassic': 'Warm Classic', diff --git a/desktop/src/i18n/locales/jp.ts b/desktop/src/i18n/locales/jp.ts index d645b6f1..9c70a35b 100644 --- a/desktop/src/i18n/locales/jp.ts +++ b/desktop/src/i18n/locales/jp.ts @@ -1445,6 +1445,7 @@ export const jp: Record = { // Settings > General 'settings.general.appearanceTitle': '外観', + 'settings.general.proxyManagedSettingsWarning': 'ユーザー設定が PROXY_MANAGED プロキシのプレースホルダーだけになっており、他の設定がプロキシツールに上書きされた可能性があります。意図した設定でなければ、CC Switch のルーティング引き継ぎを無効にして、バックアップから settings.json を復元してください。意図した設定なら、この通知は無視できます。', 'settings.general.appearanceDescription': '紙と墨の 6 つの配色を切り替えます。', 'settings.general.appearance.paper': '紙墨', 'settings.general.appearance.warmClassic': 'クラシックウォーム', diff --git a/desktop/src/i18n/locales/kr.ts b/desktop/src/i18n/locales/kr.ts index 7442f3f7..24e8c35e 100644 --- a/desktop/src/i18n/locales/kr.ts +++ b/desktop/src/i18n/locales/kr.ts @@ -1445,6 +1445,7 @@ export const kr: Record = { // Settings > General 'settings.general.appearanceTitle': '모양', + 'settings.general.proxyManagedSettingsWarning': '사용자 설정에 PROXY_MANAGED 프록시 자리표시자만 남아 있어 다른 설정이 프록시 도구에 의해 덮어쓰였을 수 있습니다. 의도한 설정이 아니라면 CC Switch 라우팅 인계를 끄고 백업에서 settings.json을 복원하세요. 직접 설정한 경우에는 이 알림을 무시해도 됩니다.', 'settings.general.appearanceDescription': '종이와 먹의 여섯 가지 배색을 전환합니다.', 'settings.general.appearance.paper': '지묵', 'settings.general.appearance.warmClassic': '따뜻한 클래식', diff --git a/desktop/src/i18n/locales/zh-TW.ts b/desktop/src/i18n/locales/zh-TW.ts index ad14f6a0..76c2128e 100644 --- a/desktop/src/i18n/locales/zh-TW.ts +++ b/desktop/src/i18n/locales/zh-TW.ts @@ -1444,6 +1444,7 @@ export const zh: Record = { // Settings > General 'settings.general.appearanceTitle': '配色主題', + 'settings.general.proxyManagedSettingsWarning': '偵測到使用者設定只剩 PROXY_MANAGED 代理占位資訊,其他設定可能被代理工具覆寫。若這不是你的主動設定,請先關閉 CC Switch 路由接管並從備份還原 settings.json;主動使用此設定可忽略。', 'settings.general.appearanceDescription': '在紙、墨、印六套配色之間切換。', 'settings.general.appearance.paper': '紙墨', 'settings.general.appearance.warmClassic': '經典暖色', diff --git a/desktop/src/i18n/locales/zh.ts b/desktop/src/i18n/locales/zh.ts index aaea23c8..5452e5f5 100644 --- a/desktop/src/i18n/locales/zh.ts +++ b/desktop/src/i18n/locales/zh.ts @@ -1444,6 +1444,7 @@ export const zh: Record = { // Settings > General 'settings.general.appearanceTitle': '配色主题', + 'settings.general.proxyManagedSettingsWarning': '检测到用户配置仅剩 PROXY_MANAGED 代理占位信息,其他设置可能被代理工具覆盖。若这不是你的主动配置,请先关闭 CC Switch 路由接管并从备份恢复 settings.json;主动使用该配置可忽略。', 'settings.general.appearanceDescription': '在纸、墨、印六套配色之间切换。', 'settings.general.appearance.paper': '纸墨', 'settings.general.appearance.warmClassic': '经典暖色', diff --git a/desktop/src/pages/settings/GeneralSettings.tsx b/desktop/src/pages/settings/GeneralSettings.tsx index 9bbb91f1..3885b907 100644 --- a/desktop/src/pages/settings/GeneralSettings.tsx +++ b/desktop/src/pages/settings/GeneralSettings.tsx @@ -88,6 +88,7 @@ export function GeneralSettings() { setAppMode: setAppModeAction, uiZoom, setUiZoom, + proxyManagedSettingsWarning, } = useSettingsStore() // Read the theme from the store that owns it. settingsStore keeps a copy for // its own consumers, but that copy is only refreshed on an explicit setTheme @@ -616,6 +617,14 @@ export function GeneralSettings() { return (
+ {proxyManagedSettingsWarning && ( +
+ {t('settings.general.proxyManagedSettingsWarning')} +
+ )} {/* No page header here on purpose: the only title it could carry is the nav label verbatim, with no description to add. The pane opens on its first section instead. */} diff --git a/desktop/src/stores/settingsStore.ts b/desktop/src/stores/settingsStore.ts index 385b02b8..b8cd27ee 100644 --- a/desktop/src/stores/settingsStore.ts +++ b/desktop/src/stores/settingsStore.ts @@ -21,6 +21,7 @@ import { type ThemeMode, type UpdateProxyMode, type UpdateProxySettings, + type UserSettings, type WebSearchSettings, } from '../types/settings' import type { TraceCaptureSettings } from '../types/trace' @@ -85,6 +86,7 @@ type SettingsStore = { uiZoom: number isLoading: boolean error: string | null + proxyManagedSettingsWarning: boolean appMode: AppModeConfig appModeRequiresRestart: boolean @@ -210,6 +212,7 @@ export const useSettingsStore = create((set, get) => ({ uiZoom: readStoredAppZoomLevel(), isLoading: false, error: null, + proxyManagedSettingsWarning: false, appMode: { mode: 'default', @@ -274,6 +277,7 @@ export const useSettingsStore = create((set, get) => ({ h5AccessDiagnostics: h5AccessResult.diagnostics, h5AccessError: h5AccessResult.error, responseLanguage: typeof userSettings.language === 'string' ? userSettings.language : '', + proxyManagedSettingsWarning: hasProxyManagedOnlyUserSettings(userSettings), isLoading: false, error: null, }) @@ -647,6 +651,18 @@ export const useSettingsStore = create((set, get) => ({ }, })) +export function hasProxyManagedOnlyUserSettings(settings: UserSettings): boolean { + const keys = Object.keys(settings) + if (keys.length !== 1 || keys[0] !== 'env') return false + + const env = settings.env + if (typeof env !== 'object' || env === null || Array.isArray(env)) return false + + const values = env as Record + return values.ANTHROPIC_API_KEY === 'PROXY_MANAGED' + || values.ANTHROPIC_AUTH_TOKEN === 'PROXY_MANAGED' +} + subscribeLocaleChanges((locale) => { useSettingsStore.setState({ locale }) applyDocumentLocale(locale) diff --git a/desktop/src/stores/settingsStoreProxyManagedWarning.test.ts b/desktop/src/stores/settingsStoreProxyManagedWarning.test.ts new file mode 100644 index 00000000..2f380ac1 --- /dev/null +++ b/desktop/src/stores/settingsStoreProxyManagedWarning.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from 'vitest' +import { hasProxyManagedOnlyUserSettings } from './settingsStore' + +describe('proxy-managed user settings warning', () => { + it('recognizes a proxy-only takeover placeholder', () => { + expect(hasProxyManagedOnlyUserSettings({ + env: { + ANTHROPIC_BASE_URL: 'http://127.0.0.1:15721', + ANTHROPIC_API_KEY: 'PROXY_MANAGED', + }, + })).toBe(true) + }) + + it('does not flag a normal local model endpoint', () => { + expect(hasProxyManagedOnlyUserSettings({ + env: { + ANTHROPIC_BASE_URL: 'http://127.0.0.1:11434', + ANTHROPIC_API_KEY: 'local-model-key', + }, + })).toBe(false) + }) + + it('does not flag a proxy placeholder when other user settings remain', () => { + expect(hasProxyManagedOnlyUserSettings({ + env: { + ANTHROPIC_BASE_URL: 'http://127.0.0.1:15721', + ANTHROPIC_AUTH_TOKEN: 'PROXY_MANAGED', + }, + language: 'Chinese', + })).toBe(false) + }) +})