diff --git a/tests/unit/web-ui-behavior-parity.test.mjs b/tests/unit/web-ui-behavior-parity.test.mjs index 57c637fb..0df77ac6 100644 --- a/tests/unit/web-ui-behavior-parity.test.mjs +++ b/tests/unit/web-ui-behavior-parity.test.mjs @@ -411,6 +411,7 @@ test('captured bundled app skeleton only exposes expected data key drift versus ]; allowedExtraCurrentKeys.push( 'lang', + 'configTemplateContext', 'configTemplateDiffVisible', 'configTemplateDiffLoading', 'configTemplateDiffError', @@ -424,6 +425,8 @@ test('captured bundled app skeleton only exposes expected data key drift versus 'healthCheckBatchDone', 'healthCheckBatchFailed', 'showHealthCheckModal', + 'showCodexBridgePoolModal', + 'showClaudeBridgePoolModal', 'pluginsActiveId', 'pluginsLoading', 'pluginsError', @@ -571,6 +574,8 @@ test('captured bundled app skeleton only exposes expected data key drift versus 'normalizeConfigTemplateDiffConfirmEnabled', 'setConfigTemplateDiffConfirmEnabled', 'extractClaudeResumeKeyFromFilePath', + 'openClaudeConfigTemplateEditor', + 'applyClaudeLocalBridge', 'loadPluginsOverview', 'selectPlugin', 'createPromptTemplate', diff --git a/web-ui/app.js b/web-ui/app.js index f89a21c6..a0520cff 100644 --- a/web-ui/app.js +++ b/web-ui/app.js @@ -71,6 +71,8 @@ document.addEventListener('DOMContentLoaded', () => { showAgentsModal: false, showSkillsModal: false, showHealthCheckModal: false, + showCodexBridgePoolModal: false, + showClaudeBridgePoolModal: false, // Plugins pluginsActiveId: 'prompt-templates', pluginsLoading: false, @@ -98,6 +100,7 @@ document.addEventListener('DOMContentLoaded', () => { confirmDialogResolver: null, configTemplateContent: '', configTemplateApplying: false, + configTemplateContext: 'codex', configTemplateDiffVisible: false, configTemplateDiffLoading: false, configTemplateDiffError: '', diff --git a/web-ui/logic.claude.mjs b/web-ui/logic.claude.mjs index 52d4d2ed..254157ac 100644 --- a/web-ui/logic.claude.mjs +++ b/web-ui/logic.claude.mjs @@ -111,6 +111,10 @@ export function matchClaudeConfigFromSettings(claudeConfigs = {}, env = {}) { if (!normalizedSettings.baseUrl || !normalizedSettings.model || !hasClaudeCredential(normalizedSettings)) { return ''; } + // 检测本地桥接 URL + if (typeof normalizedSettings.baseUrl === 'string' && normalizedSettings.baseUrl.includes('/bridge/claude-local/')) { + return 'claude-local'; + } const comparableSettingsUrl = normalizeClaudeComparableUrl(normalizedSettings.baseUrl); const entries = Object.entries(claudeConfigs || {}); for (const [name, config] of entries) { diff --git a/web-ui/modules/app.methods.claude-config.mjs b/web-ui/modules/app.methods.claude-config.mjs index b216fb15..41e698ea 100644 --- a/web-ui/modules/app.methods.claude-config.mjs +++ b/web-ui/modules/app.methods.claude-config.mjs @@ -264,6 +264,43 @@ export function createClaudeConfigMethods(options = {}) { claudeLocalBridgeConfigured() { return this.claudeLocalBridgeCandidateProviders().some(p => p.hasKey); + }, + + async applyClaudeLocalBridge() { + this.currentClaudeConfig = 'claude-local'; + try { localStorage.setItem('currentClaudeConfig', 'claude-local'); } catch (_) {} + this.refreshClaudeModelContext(); + + const candidates = this.claudeLocalBridgeCandidateProviders(); + if (candidates.length === 0) { + return this.showMessage('请先添加并配置至少一个 Claude 提供商', 'error'); + } + + try { + const res = await api('claude-local-bridge-toggle', { enable: true }); + if (res.error) { + this.showMessage(res.error || '启用本地负载均衡失败', 'error'); + return; + } + this.showMessage('Claude 本地负载均衡已启用', 'success'); + } catch (e) { + this.showMessage('启用本地负载均衡失败', 'error'); + } + }, + + async openClaudeConfigTemplateEditor() { + try { + const res = await api('get-claude-settings-raw'); + if (res.error) { + this.showMessage(res.error, 'error'); + return; + } + this.configTemplateContent = res.content || '{}'; + this.configTemplateContext = 'claude'; + this.showConfigTemplateModal = true; + } catch (e) { + this.showMessage('加载 Claude settings 失败', 'error'); + } } }; } diff --git a/web-ui/modules/app.methods.codex-config.mjs b/web-ui/modules/app.methods.codex-config.mjs index bc6b4505..3f0faded 100644 --- a/web-ui/modules/app.methods.codex-config.mjs +++ b/web-ui/modules/app.methods.codex-config.mjs @@ -558,6 +558,7 @@ export function createCodexConfigMethods(options = {}) { template = `${template.trimEnd()}\n\n${appendBlock}\n`; } this.configTemplateContent = template; + this.configTemplateContext = 'codex'; this.showConfigTemplateModal = true; } catch (e) { this.showMessage('加载模板失败', 'error'); @@ -807,9 +808,16 @@ export function createCodexConfigMethods(options = {}) { const performApply = async () => { this.configTemplateApplying = true; try { - const res = await api('apply-config-template', { - template: this.configTemplateContent - }); + let res; + if (this.configTemplateContext === 'claude') { + res = await api('apply-claude-settings-raw', { + content: this.configTemplateContent + }); + } else { + res = await api('apply-config-template', { + template: this.configTemplateContent + }); + } if (res.error) { this.showMessage(res.error, 'error'); return; diff --git a/web-ui/partials/index/modals-basic.html b/web-ui/partials/index/modals-basic.html index 86c0e7da..70c50dc6 100644 --- a/web-ui/partials/index/modals-basic.html +++ b/web-ui/partials/index/modals-basic.html @@ -171,3 +171,29 @@ + + +
+ diff --git a/web-ui/partials/index/panel-config-claude.html b/web-ui/partials/index/panel-config-claude.html index 449dabf2..a93c7072 100644 --- a/web-ui/partials/index/panel-config-claude.html +++ b/web-ui/partials/index/panel-config-claude.html @@ -82,6 +82,7 @@ :placeholder="t('claude.model.placeholder')" >