Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
564d27b
feat(web-ui): enable claude share command button
ymkiux May 17, 2026
a9e567a
feat(web-ui): move version to brand header and simplify layout
ymkiux May 17, 2026
7e13a4e
fix(web-ui): make language switcher always visible at sidebar bottom
ymkiux May 17, 2026
2701016
fix(web-ui): fix language switcher visibility at sidebar bottom
ymkiux May 17, 2026
ae446ef
feat(web-ui): add context window utilization warning for sessions
ymkiux May 17, 2026
12b3038
test: update test to match enabled share command button
ymkiux May 17, 2026
b839fc4
test: allow sessionContextUtilization in parity test
ymkiux May 17, 2026
3d896b2
fix(search): CJK token matching + E2E stability fixes
ymkiux May 17, 2026
4ca5164
feat(web-ui): Codex provider UI improvements + edit key eye toggle
ymkiux May 18, 2026
707462f
feat(web-ui): add eye toggle for Claude edit config API key
ymkiux May 18, 2026
cde92c3
test(web-ui): update parity and share command tests for provider UI c…
ymkiux May 18, 2026
c746613
feat(claude): add local bridge load balancing for Claude providers
ymkiux May 18, 2026
9c3e6c9
fix(claude): add local provider card to Claude config panel
ymkiux May 18, 2026
55322e5
feat(cli): add Windows system tray icon for running indicator
ymkiux May 18, 2026
db99a4f
fix(claude): remove provider health check section from config panel
ymkiux May 18, 2026
2bac697
fix(dashboard): remove providers health section from Doctor panel
ymkiux May 18, 2026
3ea1e0d
fix(claude): remove provider health check section from Claude config …
ymkiux May 18, 2026
b43af44
fix(claude): remove health check results display, keep button and hint
ymkiux May 18, 2026
82123ea
fix(codex): remove health check results display, keep button and hint
ymkiux May 18, 2026
dec373a
feat(cli): add update command
ymkiux May 18, 2026
58999a5
fix(tray): fix pid variable bug and improve robustness
ymkiux May 18, 2026
75ad600
fix(tray): fix powershell heredoc indentation syntax error
ymkiux May 18, 2026
e3854bf
fix(tray): improve UI compatibility and add fallback icon
ymkiux May 18, 2026
3f54c4b
refactor(tray): use pure powershell with STA mode and hidden form for…
ymkiux May 18, 2026
f9011e0
refactor(cli): remove experimental windows tray icon feature
ymkiux May 18, 2026
6a5b08f
docs(readme): modernize style and fix consistency tests
ymkiux May 18, 2026
655b04e
docs(readme): remove sub-projects and ecosystem section
ymkiux May 18, 2026
32489f5
docs(readme): fix heading spacing and remove sub-projects section
ymkiux May 18, 2026
5b4ee20
chore: bump version to 0.0.33
ymkiux May 18, 2026
dd28a6b
docs(readme): restore missing badges with flat-square style
ymkiux May 18, 2026
52d577f
docs(readme): add Live Agent Sync to progress table
ymkiux May 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ const BUILTIN_PROXY_SETTINGS_FILE = path.join(CONFIG_DIR, 'codexmate-proxy.json'
const BUILTIN_CLAUDE_PROXY_SETTINGS_FILE = path.join(CONFIG_DIR, 'codexmate-claude-proxy.json');
const OPENAI_BRIDGE_SETTINGS_FILE = path.join(CONFIG_DIR, 'codexmate-openai-bridge.json');
const LOCAL_BRIDGE_SETTINGS_FILE = path.join(CONFIG_DIR, 'codexmate-local-bridge.json');
const CLAUDE_LOCAL_BRIDGE_SETTINGS_FILE = path.join(CONFIG_DIR, 'codexmate-claude-local-bridge.json');
const CLAUDE_LOCAL_PROVIDERS_FILE = path.join(CONFIG_DIR, 'codexmate-claude-bridge.json');
const CODEX_SESSIONS_DIR = path.join(CONFIG_DIR, 'sessions');
const SESSION_TRASH_DIR = path.join(CONFIG_DIR, 'codexmate-session-trash');
const SESSION_TRASH_FILES_DIR = path.join(SESSION_TRASH_DIR, 'files');
Expand Down Expand Up @@ -338,6 +340,7 @@ const openaiBridgeHandler = createOpenaiBridgeHttpHandler({
const localBridgeHandler = createLocalBridgeHttpHandler({
readConfigFn: readConfig,
openaiBridgeFile: OPENAI_BRIDGE_SETTINGS_FILE,
claudeProvidersFile: CLAUDE_LOCAL_PROVIDERS_FILE,
localBridgeSettingsFile: LOCAL_BRIDGE_SETTINGS_FILE,
expectedToken: typeof process.env.CODEXMATE_HTTP_TOKEN === 'string' ? process.env.CODEXMATE_HTTP_TOKEN.trim() : '',
maxBodySize: MAX_API_BODY_SIZE,
Expand Down Expand Up @@ -2202,6 +2205,30 @@ function updateProviderInConfig(params = {}) {
}
}

function getProviderKey(params = {}) {
const name = typeof params.name === 'string' ? params.name.trim() : '';
if (!name) return { error: '名称不能为空' };
try {
const config = readConfig();
const provider = config.model_providers && config.model_providers[name];
if (!provider) return { error: '提供商不存在' };

const bridge = typeof provider.codexmate_bridge === 'string' ? provider.codexmate_bridge.trim() : '';
const isTransform = bridge === 'openai' || String(provider.base_url || '').includes('/bridge/openai/');
if (isTransform) {
const settings = readOpenaiBridgeSettings(OPENAI_BRIDGE_SETTINGS_FILE);
const entry = settings.providers ? settings.providers[name] : null;
const key = entry && typeof entry.apiKey === 'string' ? entry.apiKey : '';
return { key };
Comment on lines +2217 to +2223

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Narrow the transform-provider detection.

Line 2214 treats any base_url containing /bridge/openai/ as a managed transform provider. That can misclassify a normal upstream URL and make this endpoint return settings.providers[name].apiKey instead of the provider’s real preferred_auth_method, so the edit modal loads the wrong secret.

Suggested fix
-        const bridge = typeof provider.codexmate_bridge === 'string' ? provider.codexmate_bridge.trim() : '';
-        const isTransform = bridge === 'openai' || String(provider.base_url || '').includes('/bridge/openai/');
+        const bridge = typeof provider.codexmate_bridge === 'string' ? provider.codexmate_bridge.trim() : '';
+        const baseUrl = typeof provider.base_url === 'string' ? provider.base_url.trim() : '';
+        const isLocalOpenaiBridgeUrl = /^https?:\/\/(?:127\.0\.0\.1|localhost|\[::1\])(?::\d+)?\/bridge\/openai\/[^/]+\/v1\/?$/i.test(baseUrl);
+        const isTransform = bridge === 'openai' || isLocalOpenaiBridgeUrl;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const bridge = typeof provider.codexmate_bridge === 'string' ? provider.codexmate_bridge.trim() : '';
const isTransform = bridge === 'openai' || String(provider.base_url || '').includes('/bridge/openai/');
if (isTransform) {
const settings = readOpenaiBridgeSettings(OPENAI_BRIDGE_SETTINGS_FILE);
const entry = settings.providers ? settings.providers[name] : null;
const key = entry && typeof entry.apiKey === 'string' ? entry.apiKey : '';
return { key };
const bridge = typeof provider.codexmate_bridge === 'string' ? provider.codexmate_bridge.trim() : '';
const baseUrl = typeof provider.base_url === 'string' ? provider.base_url.trim() : '';
const isLocalOpenaiBridgeUrl = /^https?:\/\/(?:127\.0\.0\.1|localhost|\[::1\])(?::\d+)?\/bridge\/openai\/[^/]+\/v1\/?$/i.test(baseUrl);
const isTransform = bridge === 'openai' || isLocalOpenaiBridgeUrl;
if (isTransform) {
const settings = readOpenaiBridgeSettings(OPENAI_BRIDGE_SETTINGS_FILE);
const entry = settings.providers ? settings.providers[name] : null;
const key = entry && typeof entry.apiKey === 'string' ? entry.apiKey : '';
return { key };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli.js` around lines 2213 - 2219, The detection for a transform provider is
too broad; instead of treating any base_url that contains '/bridge/openai/' as a
transform, parse provider.base_url (use new URL(...)) and only mark isTransform
true when either provider.codexmate_bridge === 'openai' OR the parsed URL's
pathname strictly startsWith '/bridge/openai/' AND its hostname matches the
bridge host from readOpenaiBridgeSettings(OPENAI_BRIDGE_SETTINGS_FILE) (e.g.,
settings.host or settings.bridge_host); update the isTransform logic and then
continue to read settings.providers[name].apiKey only when that narrower
isTransform is true so preferred_auth_method from the real provider is not
overridden.

}

const key = typeof provider.preferred_auth_method === 'string' ? provider.preferred_auth_method : '';
return { key };
} catch (e) {
return { error: e.message || '读取失败' };
}
}
Comment on lines +2209 to +2231

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Lock down plaintext key reads before exposing them over /api.

This new action returns raw provider secrets, but /api still fully trusts loopback requests and does not validate Host/Origin. That means a DNS-rebinding page can read local API keys once the Web UI is running. Please add an explicit anti-rebinding/secret-read gate here instead of relying on the current loopback shortcut.

Also applies to: 10632-10634

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli.js` around lines 2205 - 2227, Add an explicit anti-rebinding gate at the
start of getProviderKey so raw provider secrets are never returned unless the
request context is validated: implement or call a helper like
isRequestFromLocalTrustedSource(params.request, params.headers) (or check
Host/Origin and that the connection is loopback + matches an allowlist or an
explicit env/flag such as ALLOW_SECRET_READ) and return { error: 'not allowed' }
when the check fails; then proceed to use
readConfig/readOpenaiBridgeSettings/OPENAI_BRIDGE_SETTINGS_FILE only after the
gate passes. Apply the same gate to the other plaintext-secret readers
referenced (the same logic used around the code at the other provider-secret
reads).


function deleteProviderFromConfig(params = {}) {
const name = typeof params.name === 'string' ? params.name.trim() : '';
if (!name) return { error: '名称不能为空' };
Expand Down Expand Up @@ -5549,6 +5576,156 @@ function getLocalBridgeExcludedProviders() {
return { excludedProviders: settings.excludedProviders };
}

// ============================================================================
// Claude Local Bridge
// ============================================================================

function readClaudeLocalBridgeSettings() {
const defaults = { enabled: false, lastActiveBaseUrl: '', lastModel: '', excludedProviders: [] };
try {
if (!fs.existsSync(CLAUDE_LOCAL_BRIDGE_SETTINGS_FILE)) return defaults;
const raw = JSON.parse(fs.readFileSync(CLAUDE_LOCAL_BRIDGE_SETTINGS_FILE, 'utf-8'));
return {
enabled: !!raw.enabled,
lastActiveBaseUrl: typeof raw.lastActiveBaseUrl === 'string' ? raw.lastActiveBaseUrl.trim() : '',
lastModel: typeof raw.lastModel === 'string' ? raw.lastModel.trim() : '',
excludedProviders: Array.isArray(raw.excludedProviders) ? raw.excludedProviders.filter(p => typeof p === 'string') : []
};
} catch (e) {
return defaults;
}
}

function writeClaudeLocalBridgeSettings(settings) {
fs.writeFileSync(CLAUDE_LOCAL_BRIDGE_SETTINGS_FILE, JSON.stringify(settings, null, 2), 'utf-8');
}

function readClaudeLocalProvidersFile() {
try {
if (!fs.existsSync(CLAUDE_LOCAL_PROVIDERS_FILE)) return { providers: {} };
const raw = JSON.parse(fs.readFileSync(CLAUDE_LOCAL_PROVIDERS_FILE, 'utf-8'));
return { providers: (raw && typeof raw.providers === 'object') ? raw.providers : {} };
} catch (e) {
Comment on lines +5604 to +5609

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve excludedProviders when syncing Claude bridge providers.

readClaudeLocalProvidersFile() drops excludedProviders, then Line 5724 falls back to []. Every provider sync therefore wipes the exclusion list, so the load-balancing opt-out state is lost on the next UI sync.

Suggested fix
 function readClaudeLocalProvidersFile() {
     try {
-        if (!fs.existsSync(CLAUDE_LOCAL_PROVIDERS_FILE)) return { providers: {} };
+        if (!fs.existsSync(CLAUDE_LOCAL_PROVIDERS_FILE)) {
+            return { providers: {}, excludedProviders: [] };
+        }
         const raw = JSON.parse(fs.readFileSync(CLAUDE_LOCAL_PROVIDERS_FILE, 'utf-8'));
-        return { providers: (raw && typeof raw.providers === 'object') ? raw.providers : {} };
+        return {
+            providers: raw && raw.providers && typeof raw.providers === 'object' && !Array.isArray(raw.providers)
+                ? raw.providers
+                : {},
+            excludedProviders: Array.isArray(raw && raw.excludedProviders)
+                ? raw.excludedProviders.filter((name) => typeof name === 'string' && name.trim())
+                : []
+        };
     } catch (e) {
-        return { providers: {} };
+        return { providers: {}, excludedProviders: [] };
     }
 }

Also applies to: 5721-5725

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli.js` around lines 5603 - 5608, The readClaudeLocalProvidersFile function
currently returns only a providers object and drops the excludedProviders list,
causing syncs to wipe exclusions; modify readClaudeLocalProvidersFile to also
read and return excludedProviders from the CLAUDE_LOCAL_PROVIDERS_FILE
(defaulting to an empty array only if the file value is missing or not an array)
so callers (the Claude bridge provider sync logic) receive and preserve
excludedProviders instead of falling back to [] and losing the opt-out state.

return { providers: {} };
}
}

function writeClaudeLocalProvidersFile(data) {
ensureDir(CONFIG_DIR);
fs.writeFileSync(CLAUDE_LOCAL_PROVIDERS_FILE, JSON.stringify(data, null, 2), 'utf-8');
}

function syncClaudeProvidersToBridgeFile() {
// Sync Claude configs from localStorage-equivalent (browser) to bridge file
// Called when providers are added/updated/deleted via web UI
const existing = readClaudeLocalProvidersFile();
const providers = existing.providers || {};
// Preserve existing entries, update from params
return { providers };
}

function toggleClaudeLocalBridge(params = {}) {
const enable = !!params.enable;
const settings = readClaudeLocalBridgeSettings();

try {
const readResult = readJsonObjectFromFile(CLAUDE_SETTINGS_FILE, {});
if (!readResult.ok) {
return { success: false, error: readResult.error || '读取 Claude settings.json 失败' };
}
const currentSettings = readResult.data || {};
const currentEnv = (currentSettings.env && typeof currentSettings.env === 'object' && !Array.isArray(currentSettings.env))
? currentSettings.env : {};
const currentBaseUrl = typeof currentEnv.ANTHROPIC_BASE_URL === 'string' ? currentEnv.ANTHROPIC_BASE_URL.trim() : '';

if (enable) {
if (currentBaseUrl.includes('/bridge/claude-local/')) {
return { success: true, enabled: true, notice: '已启用 Claude 本地负载均衡' };
}
settings.lastActiveBaseUrl = currentBaseUrl;
settings.lastModel = typeof currentEnv.ANTHROPIC_MODEL === 'string' ? currentEnv.ANTHROPIC_MODEL.trim() : '';
settings.enabled = true;
writeClaudeLocalBridgeSettings(settings);

const localPort = resolveWebPort();
const localBaseUrl = `http://127.0.0.1:${localPort}/bridge/claude-local/v1`;
const nextEnv = { ...currentEnv, ANTHROPIC_BASE_URL: localBaseUrl };
const nextSettings = { ...currentSettings, env: nextEnv };
ensureDir(CLAUDE_DIR);
backupFileIfNeededOnce(CLAUDE_SETTINGS_FILE);
writeJsonAtomic(CLAUDE_SETTINGS_FILE, nextSettings);
return { success: true, enabled: true, previousBaseUrl: currentBaseUrl };
} else {
if (!currentBaseUrl.includes('/bridge/claude-local/')) {
settings.enabled = false;
writeClaudeLocalBridgeSettings(settings);
return { success: true, enabled: false, notice: 'Claude 本地负载均衡未启用' };
}
const restoreBaseUrl = settings.lastActiveBaseUrl || '';
if (!restoreBaseUrl) {
settings.enabled = false;
writeClaudeLocalBridgeSettings(settings);
return { success: true, enabled: false, notice: '已关闭 Claude 本地负载均衡(无历史配置可恢复)' };
}
const nextEnv = { ...currentEnv, ANTHROPIC_BASE_URL: restoreBaseUrl };
if (settings.lastModel) {
nextEnv.ANTHROPIC_MODEL = settings.lastModel;
}
const nextSettings = { ...currentSettings, env: nextEnv };
ensureDir(CLAUDE_DIR);
backupFileIfNeededOnce(CLAUDE_SETTINGS_FILE);
writeJsonAtomic(CLAUDE_SETTINGS_FILE, nextSettings);
settings.enabled = false;
writeClaudeLocalBridgeSettings(settings);
Comment on lines +5665 to +5680

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear bridge env vars when disabling without a previous base URL.

If Claude had no ANTHROPIC_BASE_URL before enable, lastActiveBaseUrl stays empty. This branch returns success without touching settings.json, so Claude keeps using /bridge/claude-local/ even though the API now reports the bridge as disabled.

Suggested fix
             const restoreBaseUrl = settings.lastActiveBaseUrl || '';
-            if (!restoreBaseUrl) {
-                settings.enabled = false;
-                writeClaudeLocalBridgeSettings(settings);
-                return { success: true, enabled: false, notice: '已关闭 Claude 本地负载均衡(无历史配置可恢复)' };
-            }
-            const nextEnv = { ...currentEnv, ANTHROPIC_BASE_URL: restoreBaseUrl };
-            if (settings.lastModel) {
-                nextEnv.ANTHROPIC_MODEL = settings.lastModel;
-            }
+            const nextEnv = { ...currentEnv };
+            if (restoreBaseUrl) {
+                nextEnv.ANTHROPIC_BASE_URL = restoreBaseUrl;
+            } else {
+                delete nextEnv.ANTHROPIC_BASE_URL;
+            }
+            if (settings.lastModel) {
+                nextEnv.ANTHROPIC_MODEL = settings.lastModel;
+            } else {
+                delete nextEnv.ANTHROPIC_MODEL;
+            }
             const nextSettings = { ...currentSettings, env: nextEnv };
             ensureDir(CLAUDE_DIR);
             backupFileIfNeededOnce(CLAUDE_SETTINGS_FILE);
             writeJsonAtomic(CLAUDE_SETTINGS_FILE, nextSettings);
             settings.enabled = false;
             writeClaudeLocalBridgeSettings(settings);
-            return { success: true, enabled: false, restoredBaseUrl: restoreBaseUrl, restoredModel: settings.lastModel };
+            return restoreBaseUrl
+                ? { success: true, enabled: false, restoredBaseUrl: restoreBaseUrl, restoredModel: settings.lastModel }
+                : { success: true, enabled: false, notice: '已关闭 Claude 本地负载均衡(无历史配置可恢复)' };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli.js` around lines 5664 - 5679, The branch that handles disabling when
restoreBaseUrl is empty returns early without updating the persisted Claude
settings, leaving ANTHROPIC_BASE_URL/ANTHROPIC_MODEL still pointing to the
bridge; modify this branch to clear those env vars in the stored settings: load
currentSettings/currentEnv, set ANTHROPIC_BASE_URL = '' (and remove or clear
ANTHROPIC_MODEL if present), update nextSettings.env accordingly, call
ensureDir(CLAUDE_DIR), backupFileIfNeededOnce(CLAUDE_SETTINGS_FILE),
writeJsonAtomic(CLAUDE_SETTINGS_FILE, nextSettings), then set settings.enabled =
false and call writeClaudeLocalBridgeSettings(settings) before returning the
success result so the bridge env is actually removed.

return { success: true, enabled: false, restoredBaseUrl: restoreBaseUrl, restoredModel: settings.lastModel };
}
} catch (e) {
return { error: e && e.message ? e.message : '操作失败' };
}
}

function getClaudeLocalBridgeStatus() {
const settings = readClaudeLocalBridgeSettings();
let currentBaseUrl = '';
try {
const readResult = readJsonObjectFromFile(CLAUDE_SETTINGS_FILE, {});
if (readResult.ok && readResult.data && readResult.data.env) {
currentBaseUrl = typeof readResult.data.env.ANTHROPIC_BASE_URL === 'string' ? readResult.data.env.ANTHROPIC_BASE_URL.trim() : '';
}
} catch (e) { /* ignore */ }
const providersData = readClaudeLocalProvidersFile();
const providerNames = Object.keys(providersData.providers || {});
return {
enabled: settings.enabled,
active: currentBaseUrl.includes('/bridge/claude-local/'),
excludedProviders: settings.excludedProviders,
lastActiveBaseUrl: settings.lastActiveBaseUrl,
lastModel: settings.lastModel,
providers: providerNames
};
}

function setClaudeLocalBridgeExcludedProviders(params = {}) {
const names = Array.isArray(params.names) ? params.names.filter(n => typeof n === 'string' && n.trim()) : [];
const settings = readClaudeLocalBridgeSettings();
settings.excludedProviders = names;
writeClaudeLocalBridgeSettings(settings);
return { success: true, excludedProviders: names };
}

function getClaudeLocalBridgeExcludedProviders() {
const settings = readClaudeLocalBridgeSettings();
return { excludedProviders: settings.excludedProviders };
}

function syncClaudeBridgeProviders(params = {}) {
const providers = (params.providers && typeof params.providers === 'object') ? params.providers : {};
const existing = readClaudeLocalProvidersFile();
const excluded = existing.excludedProviders || [];
writeClaudeLocalProvidersFile({ providers, excludedProviders: excluded });
return { success: true, count: Object.keys(providers).length };
}

function removeClaudeSessionIndexEntry(indexPath, sessionFilePath, sessionId) {
if (!indexPath || !fs.existsSync(indexPath)) {
return { removed: false, entry: null };
Expand Down Expand Up @@ -10605,6 +10782,9 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
case 'update-provider':
result = updateProviderInConfig(params || {});
break;
case 'get-provider-key':
result = getProviderKey(params || {});
break;
case 'delete-provider':
result = deleteProviderFromConfig(params || {});
break;
Expand Down Expand Up @@ -10980,6 +11160,21 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
case 'local-bridge-get-excluded':
result = getLocalBridgeExcludedProviders();
break;
case 'claude-local-bridge-toggle':
result = toggleClaudeLocalBridge(params || {});
break;
case 'claude-local-bridge-status':
result = getClaudeLocalBridgeStatus();
break;
case 'claude-local-bridge-set-excluded':
result = setClaudeLocalBridgeExcludedProviders(params || {});
break;
case 'claude-local-bridge-get-excluded':
result = getClaudeLocalBridgeExcludedProviders();
break;
case 'claude-local-bridge-sync-providers':
result = syncClaudeBridgeProviders(params || {});
break;
case 'workflow-list':
result = listWorkflowDefinitions();
break;
Expand Down
Loading
Loading