Showing preview only (5,358K chars total). Download the full file or copy to clipboard to get everything.
Repository: farion1231/cc-switch
Branch: main
Commit: 8ccfbd36d6cb
Files: 606
Total size: 5.0 MB
Directory structure:
gitextract_y5v1mptu/
├── .gitattributes
├── .gitignore
├── .node-version
├── CHANGELOG.md
├── LICENSE
├── README.md
├── README_JA.md
├── README_ZH.md
├── cc-switch-main/
│ └── src/
│ └── config/
│ └── universalProviderPresets.ts
├── components.json
├── deplink.html
├── docs/
│ ├── proxy-guide-zh.md
│ ├── release-notes/
│ │ ├── v3.10.0-en.md
│ │ ├── v3.10.0-ja.md
│ │ ├── v3.10.0-zh.md
│ │ ├── v3.11.0-en.md
│ │ ├── v3.11.0-ja.md
│ │ ├── v3.11.0-zh.md
│ │ ├── v3.11.1-en.md
│ │ ├── v3.11.1-ja.md
│ │ ├── v3.11.1-zh.md
│ │ ├── v3.12.0-en.md
│ │ ├── v3.12.0-ja.md
│ │ ├── v3.12.0-zh.md
│ │ ├── v3.12.1-en.md
│ │ ├── v3.12.1-ja.md
│ │ ├── v3.12.1-zh.md
│ │ ├── v3.12.2-en.md
│ │ ├── v3.12.2-ja.md
│ │ ├── v3.12.2-zh.md
│ │ ├── v3.12.3-en.md
│ │ ├── v3.12.3-ja.md
│ │ ├── v3.12.3-zh.md
│ │ ├── v3.6.0-en.md
│ │ ├── v3.6.0-zh.md
│ │ ├── v3.6.1-en.md
│ │ ├── v3.6.1-zh.md
│ │ ├── v3.7.0-en.md
│ │ ├── v3.7.0-zh.md
│ │ ├── v3.7.1-en.md
│ │ ├── v3.7.1-zh.md
│ │ ├── v3.8.0-en.md
│ │ ├── v3.8.0-ja.md
│ │ ├── v3.8.0-zh.md
│ │ ├── v3.9.0-en.md
│ │ ├── v3.9.0-ja.md
│ │ └── v3.9.0-zh.md
│ └── user-manual/
│ ├── README.md
│ ├── en/
│ │ ├── 1-getting-started/
│ │ │ ├── 1.1-introduction.md
│ │ │ ├── 1.2-installation.md
│ │ │ ├── 1.3-interface.md
│ │ │ ├── 1.4-quickstart.md
│ │ │ └── 1.5-settings.md
│ │ ├── 2-providers/
│ │ │ ├── 2.1-add.md
│ │ │ ├── 2.2-switch.md
│ │ │ ├── 2.3-edit.md
│ │ │ ├── 2.4-sort-duplicate.md
│ │ │ └── 2.5-usage-query.md
│ │ ├── 3-extensions/
│ │ │ ├── 3.1-mcp.md
│ │ │ ├── 3.2-prompts.md
│ │ │ └── 3.3-skills.md
│ │ ├── 4-proxy/
│ │ │ ├── 4.1-service.md
│ │ │ ├── 4.2-takeover.md
│ │ │ ├── 4.3-failover.md
│ │ │ ├── 4.4-usage.md
│ │ │ └── 4.5-model-test.md
│ │ ├── 5-faq/
│ │ │ ├── 5.1-config-files.md
│ │ │ ├── 5.2-questions.md
│ │ │ ├── 5.3-deeplink.md
│ │ │ └── 5.4-env-conflict.md
│ │ └── README.md
│ ├── ja/
│ │ ├── 1-getting-started/
│ │ │ ├── 1.1-introduction.md
│ │ │ ├── 1.2-installation.md
│ │ │ ├── 1.3-interface.md
│ │ │ ├── 1.4-quickstart.md
│ │ │ └── 1.5-settings.md
│ │ ├── 2-providers/
│ │ │ ├── 2.1-add.md
│ │ │ ├── 2.2-switch.md
│ │ │ ├── 2.3-edit.md
│ │ │ ├── 2.4-sort-duplicate.md
│ │ │ └── 2.5-usage-query.md
│ │ ├── 3-extensions/
│ │ │ ├── 3.1-mcp.md
│ │ │ ├── 3.2-prompts.md
│ │ │ └── 3.3-skills.md
│ │ ├── 4-proxy/
│ │ │ ├── 4.1-service.md
│ │ │ ├── 4.2-takeover.md
│ │ │ ├── 4.3-failover.md
│ │ │ ├── 4.4-usage.md
│ │ │ └── 4.5-model-test.md
│ │ ├── 5-faq/
│ │ │ ├── 5.1-config-files.md
│ │ │ ├── 5.2-questions.md
│ │ │ ├── 5.3-deeplink.md
│ │ │ └── 5.4-env-conflict.md
│ │ └── README.md
│ └── zh/
│ ├── 1-getting-started/
│ │ ├── 1.1-introduction.md
│ │ ├── 1.2-installation.md
│ │ ├── 1.3-interface.md
│ │ ├── 1.4-quickstart.md
│ │ └── 1.5-settings.md
│ ├── 2-providers/
│ │ ├── 2.1-add.md
│ │ ├── 2.2-switch.md
│ │ ├── 2.3-edit.md
│ │ ├── 2.4-sort-duplicate.md
│ │ └── 2.5-usage-query.md
│ ├── 3-extensions/
│ │ ├── 3.1-mcp.md
│ │ ├── 3.2-prompts.md
│ │ └── 3.3-skills.md
│ ├── 4-proxy/
│ │ ├── 4.1-service.md
│ │ ├── 4.2-takeover.md
│ │ ├── 4.3-failover.md
│ │ ├── 4.4-usage.md
│ │ └── 4.5-model-test.md
│ ├── 5-faq/
│ │ ├── 5.1-config-files.md
│ │ ├── 5.2-questions.md
│ │ ├── 5.3-deeplink.md
│ │ └── 5.4-env-conflict.md
│ └── README.md
├── flatpak/
│ ├── README.md
│ ├── com.ccswitch.desktop.desktop
│ ├── com.ccswitch.desktop.metainfo.xml
│ └── com.ccswitch.desktop.yml
├── package.json
├── pnpm-workspace.yaml
├── postcss.config.cjs
├── scripts/
│ ├── extract-icons.js
│ ├── filter-icons.js
│ └── generate-icon-index.js
├── session-manager.md
├── src/
│ ├── App.tsx
│ ├── components/
│ │ ├── AppSwitcher.tsx
│ │ ├── BrandIcons.tsx
│ │ ├── ColorPicker.tsx
│ │ ├── ConfirmDialog.tsx
│ │ ├── DeepLinkImportDialog.tsx
│ │ ├── IconPicker.tsx
│ │ ├── JsonEditor.tsx
│ │ ├── MarkdownEditor.tsx
│ │ ├── ProviderIcon.tsx
│ │ ├── UpdateBadge.tsx
│ │ ├── UsageFooter.tsx
│ │ ├── UsageScriptModal.tsx
│ │ ├── agents/
│ │ │ └── AgentsPanel.tsx
│ │ ├── common/
│ │ │ ├── AppCountBar.tsx
│ │ │ ├── AppToggleGroup.tsx
│ │ │ ├── FullScreenPanel.tsx
│ │ │ └── ListItemRow.tsx
│ │ ├── deeplink/
│ │ │ ├── McpConfirmation.tsx
│ │ │ ├── PromptConfirmation.tsx
│ │ │ └── SkillConfirmation.tsx
│ │ ├── env/
│ │ │ └── EnvWarningBanner.tsx
│ │ ├── icons/
│ │ │ └── TerminalIcons.tsx
│ │ ├── mcp/
│ │ │ ├── McpFormModal.tsx
│ │ │ ├── McpWizardModal.tsx
│ │ │ ├── UnifiedMcpPanel.tsx
│ │ │ └── useMcpValidation.ts
│ │ ├── mode-toggle.tsx
│ │ ├── openclaw/
│ │ │ ├── AgentsDefaultsPanel.tsx
│ │ │ ├── EnvPanel.tsx
│ │ │ ├── OpenClawHealthBanner.tsx
│ │ │ ├── ToolsPanel.tsx
│ │ │ ├── hooks/
│ │ │ │ └── useOpenClawModelOptions.ts
│ │ │ └── utils.ts
│ │ ├── prompts/
│ │ │ ├── PromptFormModal.tsx
│ │ │ ├── PromptFormPanel.tsx
│ │ │ ├── PromptListItem.tsx
│ │ │ ├── PromptPanel.tsx
│ │ │ └── PromptToggle.tsx
│ │ ├── providers/
│ │ │ ├── AddProviderDialog.tsx
│ │ │ ├── EditProviderDialog.tsx
│ │ │ ├── FailoverPriorityBadge.tsx
│ │ │ ├── HealthStatusIndicator.tsx
│ │ │ ├── ProviderActions.tsx
│ │ │ ├── ProviderCard.tsx
│ │ │ ├── ProviderEmptyState.tsx
│ │ │ ├── ProviderHealthBadge.tsx
│ │ │ ├── ProviderList.tsx
│ │ │ └── forms/
│ │ │ ├── ApiKeyInput.tsx
│ │ │ ├── BasicFormFields.tsx
│ │ │ ├── ClaudeFormFields.tsx
│ │ │ ├── CodexCommonConfigModal.tsx
│ │ │ ├── CodexConfigEditor.tsx
│ │ │ ├── CodexConfigSections.tsx
│ │ │ ├── CodexFormFields.tsx
│ │ │ ├── CommonConfigEditor.tsx
│ │ │ ├── CopilotAuthSection.tsx
│ │ │ ├── EndpointSpeedTest.tsx
│ │ │ ├── GeminiCommonConfigModal.tsx
│ │ │ ├── GeminiConfigEditor.tsx
│ │ │ ├── GeminiConfigSections.tsx
│ │ │ ├── GeminiFormFields.tsx
│ │ │ ├── OmoFormFields.tsx
│ │ │ ├── OpenClawFormFields.tsx
│ │ │ ├── OpenCodeFormFields.tsx
│ │ │ ├── ProviderAdvancedConfig.tsx
│ │ │ ├── ProviderForm.tsx
│ │ │ ├── ProviderPresetSelector.tsx
│ │ │ ├── helpers/
│ │ │ │ └── opencodeFormUtils.ts
│ │ │ ├── hooks/
│ │ │ │ ├── index.ts
│ │ │ │ ├── useApiKeyLink.ts
│ │ │ │ ├── useApiKeyState.ts
│ │ │ │ ├── useBaseUrlState.ts
│ │ │ │ ├── useCodexCommonConfig.ts
│ │ │ │ ├── useCodexConfigState.ts
│ │ │ │ ├── useCodexTomlValidation.ts
│ │ │ │ ├── useCommonConfigSnippet.ts
│ │ │ │ ├── useCopilotAuth.ts
│ │ │ │ ├── useCustomEndpoints.ts
│ │ │ │ ├── useGeminiCommonConfig.ts
│ │ │ │ ├── useGeminiConfigState.ts
│ │ │ │ ├── useManagedAuth.ts
│ │ │ │ ├── useModelState.ts
│ │ │ │ ├── useOmoDraftState.ts
│ │ │ │ ├── useOmoModelSource.ts
│ │ │ │ ├── useOpenclawFormState.ts
│ │ │ │ ├── useOpencodeFormState.ts
│ │ │ │ ├── useProviderCategory.ts
│ │ │ │ ├── useSpeedTestEndpoints.ts
│ │ │ │ └── useTemplateValues.ts
│ │ │ └── shared/
│ │ │ ├── ApiKeySection.tsx
│ │ │ ├── EndpointField.tsx
│ │ │ └── index.ts
│ │ ├── proxy/
│ │ │ ├── AutoFailoverConfigPanel.tsx
│ │ │ ├── CircuitBreakerConfigPanel.tsx
│ │ │ ├── FailoverQueueManager.tsx
│ │ │ ├── FailoverToggle.tsx
│ │ │ ├── ProxyPanel.tsx
│ │ │ ├── ProxyToggle.tsx
│ │ │ └── index.ts
│ │ ├── sessions/
│ │ │ ├── SessionItem.tsx
│ │ │ ├── SessionManagerPage.tsx
│ │ │ ├── SessionMessageItem.tsx
│ │ │ ├── SessionToc.tsx
│ │ │ └── utils.ts
│ │ ├── settings/
│ │ │ ├── AboutSection.tsx
│ │ │ ├── AppVisibilitySettings.tsx
│ │ │ ├── AuthCenterPanel.tsx
│ │ │ ├── BackupListSection.tsx
│ │ │ ├── DirectorySettings.tsx
│ │ │ ├── GlobalProxySettings.tsx
│ │ │ ├── ImportExportSection.tsx
│ │ │ ├── LanguageSettings.tsx
│ │ │ ├── LogConfigPanel.tsx
│ │ │ ├── ProxyTabContent.tsx
│ │ │ ├── RectifierConfigPanel.tsx
│ │ │ ├── SettingsPage.tsx
│ │ │ ├── SkillSyncMethodSettings.tsx
│ │ │ ├── TerminalSettings.tsx
│ │ │ ├── ThemeSettings.tsx
│ │ │ ├── WebdavSyncSection.tsx
│ │ │ └── WindowSettings.tsx
│ │ ├── skills/
│ │ │ ├── RepoManager.tsx
│ │ │ ├── RepoManagerPanel.tsx
│ │ │ ├── SkillCard.tsx
│ │ │ ├── SkillsPage.tsx
│ │ │ └── UnifiedSkillsPanel.tsx
│ │ ├── theme-provider.tsx
│ │ ├── ui/
│ │ │ ├── accordion.tsx
│ │ │ ├── alert.tsx
│ │ │ ├── badge.tsx
│ │ │ ├── button.tsx
│ │ │ ├── card.tsx
│ │ │ ├── checkbox.tsx
│ │ │ ├── collapsible.tsx
│ │ │ ├── command.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── dropdown-menu.tsx
│ │ │ ├── form.tsx
│ │ │ ├── input.tsx
│ │ │ ├── label.tsx
│ │ │ ├── popover.tsx
│ │ │ ├── scroll-area.tsx
│ │ │ ├── select.tsx
│ │ │ ├── sonner.tsx
│ │ │ ├── switch.tsx
│ │ │ ├── table.tsx
│ │ │ ├── tabs.tsx
│ │ │ ├── textarea.tsx
│ │ │ ├── toggle-row.tsx
│ │ │ └── tooltip.tsx
│ │ ├── universal/
│ │ │ ├── UniversalProviderCard.tsx
│ │ │ ├── UniversalProviderFormModal.tsx
│ │ │ ├── UniversalProviderPanel.tsx
│ │ │ └── index.ts
│ │ ├── usage/
│ │ │ ├── ModelStatsTable.tsx
│ │ │ ├── ModelTestConfigPanel.tsx
│ │ │ ├── PricingConfigPanel.tsx
│ │ │ ├── PricingEditModal.tsx
│ │ │ ├── ProviderStatsTable.tsx
│ │ │ ├── RequestDetailPanel.tsx
│ │ │ ├── RequestLogTable.tsx
│ │ │ ├── UsageDashboard.tsx
│ │ │ ├── UsageSummaryCards.tsx
│ │ │ ├── UsageTrendChart.tsx
│ │ │ └── format.ts
│ │ └── workspace/
│ │ ├── DailyMemoryPanel.tsx
│ │ ├── WorkspaceFileEditor.tsx
│ │ └── WorkspaceFilesPanel.tsx
│ ├── config/
│ │ ├── appConfig.tsx
│ │ ├── claudeProviderPresets.ts
│ │ ├── codexProviderPresets.ts
│ │ ├── codexTemplates.ts
│ │ ├── constants.ts
│ │ ├── geminiProviderPresets.ts
│ │ ├── iconInference.ts
│ │ ├── mcpPresets.ts
│ │ ├── openclawProviderPresets.ts
│ │ ├── opencodeProviderPresets.ts
│ │ └── universalProviderPresets.ts
│ ├── contexts/
│ │ └── UpdateContext.tsx
│ ├── hooks/
│ │ ├── useAutoCompact.ts
│ │ ├── useBackupManager.ts
│ │ ├── useDirectorySettings.ts
│ │ ├── useDragSort.ts
│ │ ├── useGlobalProxy.ts
│ │ ├── useImportExport.ts
│ │ ├── useLastValidValue.ts
│ │ ├── useMcp.ts
│ │ ├── useOpenClaw.ts
│ │ ├── usePromptActions.ts
│ │ ├── useProviderActions.ts
│ │ ├── useProxyConfig.ts
│ │ ├── useProxyStatus.ts
│ │ ├── useSessionSearch.ts
│ │ ├── useSettings.ts
│ │ ├── useSettingsForm.ts
│ │ ├── useSettingsMetadata.ts
│ │ ├── useSkills.ts
│ │ └── useStreamCheck.ts
│ ├── i18n/
│ │ ├── index.ts
│ │ └── locales/
│ │ ├── en.json
│ │ ├── ja.json
│ │ └── zh.json
│ ├── icons/
│ │ └── extracted/
│ │ ├── index.ts
│ │ └── metadata.ts
│ ├── index.css
│ ├── index.html
│ ├── lib/
│ │ ├── api/
│ │ │ ├── auth.ts
│ │ │ ├── config.ts
│ │ │ ├── copilot.ts
│ │ │ ├── deeplink.ts
│ │ │ ├── env.ts
│ │ │ ├── failover.ts
│ │ │ ├── globalProxy.ts
│ │ │ ├── index.ts
│ │ │ ├── mcp.ts
│ │ │ ├── model-test.ts
│ │ │ ├── omo.ts
│ │ │ ├── openclaw.ts
│ │ │ ├── prompts.ts
│ │ │ ├── providers.ts
│ │ │ ├── proxy.ts
│ │ │ ├── sessions.ts
│ │ │ ├── settings.ts
│ │ │ ├── skills.ts
│ │ │ ├── types.ts
│ │ │ ├── usage.ts
│ │ │ ├── vscode.ts
│ │ │ └── workspace.ts
│ │ ├── authBinding.ts
│ │ ├── errors/
│ │ │ └── skillErrorParser.ts
│ │ ├── platform.ts
│ │ ├── query/
│ │ │ ├── failover.ts
│ │ │ ├── index.ts
│ │ │ ├── mutations.ts
│ │ │ ├── omo.ts
│ │ │ ├── proxy.ts
│ │ │ ├── queries.ts
│ │ │ ├── queryClient.ts
│ │ │ └── usage.ts
│ │ ├── schemas/
│ │ │ ├── common.ts
│ │ │ ├── mcp.ts
│ │ │ ├── provider.ts
│ │ │ └── settings.ts
│ │ ├── updater.ts
│ │ ├── utils/
│ │ │ └── base64.ts
│ │ └── utils.ts
│ ├── main.tsx
│ ├── types/
│ │ ├── env.ts
│ │ ├── icon.ts
│ │ ├── omo.ts
│ │ ├── proxy.ts
│ │ └── usage.ts
│ ├── types.ts
│ ├── utils/
│ │ ├── domUtils.ts
│ │ ├── errorUtils.ts
│ │ ├── formatters.ts
│ │ ├── postChangeSync.ts
│ │ ├── providerConfigUtils.ts
│ │ ├── providerMetaUtils.ts
│ │ ├── textNormalization.ts
│ │ ├── tomlUtils.ts
│ │ └── uuid.ts
│ └── vite-env.d.ts
├── src-tauri/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── Info.plist
│ ├── build.rs
│ ├── capabilities/
│ │ └── default.json
│ ├── common-controls.manifest
│ ├── icons/
│ │ └── icon.icns
│ ├── src/
│ │ ├── app_config.rs
│ │ ├── app_store.rs
│ │ ├── auto_launch.rs
│ │ ├── claude_mcp.rs
│ │ ├── claude_plugin.rs
│ │ ├── codex_config.rs
│ │ ├── commands/
│ │ │ ├── auth.rs
│ │ │ ├── config.rs
│ │ │ ├── copilot.rs
│ │ │ ├── deeplink.rs
│ │ │ ├── env.rs
│ │ │ ├── failover.rs
│ │ │ ├── global_proxy.rs
│ │ │ ├── import_export.rs
│ │ │ ├── mcp.rs
│ │ │ ├── misc.rs
│ │ │ ├── mod.rs
│ │ │ ├── omo.rs
│ │ │ ├── openclaw.rs
│ │ │ ├── plugin.rs
│ │ │ ├── prompt.rs
│ │ │ ├── provider.rs
│ │ │ ├── proxy.rs
│ │ │ ├── session_manager.rs
│ │ │ ├── settings.rs
│ │ │ ├── skill.rs
│ │ │ ├── stream_check.rs
│ │ │ ├── sync_support.rs
│ │ │ ├── usage.rs
│ │ │ ├── webdav_sync.rs
│ │ │ └── workspace.rs
│ │ ├── config.rs
│ │ ├── database/
│ │ │ ├── backup.rs
│ │ │ ├── dao/
│ │ │ │ ├── failover.rs
│ │ │ │ ├── mcp.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── prompts.rs
│ │ │ │ ├── providers.rs
│ │ │ │ ├── proxy.rs
│ │ │ │ ├── settings.rs
│ │ │ │ ├── skills.rs
│ │ │ │ ├── stream_check.rs
│ │ │ │ ├── universal_providers.rs
│ │ │ │ └── usage_rollup.rs
│ │ │ ├── migration.rs
│ │ │ ├── mod.rs
│ │ │ ├── schema.rs
│ │ │ └── tests.rs
│ │ ├── deeplink/
│ │ │ ├── mcp.rs
│ │ │ ├── mod.rs
│ │ │ ├── parser.rs
│ │ │ ├── prompt.rs
│ │ │ ├── provider.rs
│ │ │ ├── skill.rs
│ │ │ ├── tests.rs
│ │ │ └── utils.rs
│ │ ├── error.rs
│ │ ├── gemini_config.rs
│ │ ├── gemini_mcp.rs
│ │ ├── init_status.rs
│ │ ├── lib.rs
│ │ ├── main.rs
│ │ ├── mcp/
│ │ │ ├── claude.rs
│ │ │ ├── codex.rs
│ │ │ ├── gemini.rs
│ │ │ ├── mod.rs
│ │ │ ├── opencode.rs
│ │ │ └── validation.rs
│ │ ├── openclaw_config.rs
│ │ ├── opencode_config.rs
│ │ ├── panic_hook.rs
│ │ ├── prompt.rs
│ │ ├── prompt_files.rs
│ │ ├── provider.rs
│ │ ├── provider_defaults.rs
│ │ ├── proxy/
│ │ │ ├── body_filter.rs
│ │ │ ├── cache_injector.rs
│ │ │ ├── circuit_breaker.rs
│ │ │ ├── error.rs
│ │ │ ├── error_mapper.rs
│ │ │ ├── failover_switch.rs
│ │ │ ├── forwarder.rs
│ │ │ ├── handler_config.rs
│ │ │ ├── handler_context.rs
│ │ │ ├── handlers.rs
│ │ │ ├── health.rs
│ │ │ ├── http_client.rs
│ │ │ ├── log_codes.rs
│ │ │ ├── mod.rs
│ │ │ ├── model_mapper.rs
│ │ │ ├── provider_router.rs
│ │ │ ├── providers/
│ │ │ │ ├── adapter.rs
│ │ │ │ ├── auth.rs
│ │ │ │ ├── claude.rs
│ │ │ │ ├── codex.rs
│ │ │ │ ├── copilot_auth.rs
│ │ │ │ ├── gemini.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── models/
│ │ │ │ │ ├── anthropic.rs
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ └── openai.rs
│ │ │ │ ├── streaming.rs
│ │ │ │ ├── streaming_responses.rs
│ │ │ │ ├── transform.rs
│ │ │ │ └── transform_responses.rs
│ │ │ ├── response_handler.rs
│ │ │ ├── response_processor.rs
│ │ │ ├── server.rs
│ │ │ ├── session.rs
│ │ │ ├── thinking_budget_rectifier.rs
│ │ │ ├── thinking_optimizer.rs
│ │ │ ├── thinking_rectifier.rs
│ │ │ ├── types.rs
│ │ │ └── usage/
│ │ │ ├── calculator.rs
│ │ │ ├── logger.rs
│ │ │ ├── mod.rs
│ │ │ └── parser.rs
│ │ ├── services/
│ │ │ ├── config.rs
│ │ │ ├── env_checker.rs
│ │ │ ├── env_manager.rs
│ │ │ ├── mcp.rs
│ │ │ ├── mod.rs
│ │ │ ├── omo.rs
│ │ │ ├── prompt.rs
│ │ │ ├── provider/
│ │ │ │ ├── endpoints.rs
│ │ │ │ ├── gemini_auth.rs
│ │ │ │ ├── live.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── usage.rs
│ │ │ ├── proxy.rs
│ │ │ ├── skill.rs
│ │ │ ├── speedtest.rs
│ │ │ ├── stream_check.rs
│ │ │ ├── usage_stats.rs
│ │ │ ├── webdav.rs
│ │ │ ├── webdav_auto_sync.rs
│ │ │ ├── webdav_sync/
│ │ │ │ └── archive.rs
│ │ │ └── webdav_sync.rs
│ │ ├── session_manager/
│ │ │ ├── mod.rs
│ │ │ ├── providers/
│ │ │ │ ├── claude.rs
│ │ │ │ ├── codex.rs
│ │ │ │ ├── gemini.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── openclaw.rs
│ │ │ │ ├── opencode.rs
│ │ │ │ └── utils.rs
│ │ │ └── terminal/
│ │ │ └── mod.rs
│ │ ├── settings.rs
│ │ ├── store.rs
│ │ ├── tray.rs
│ │ └── usage_script.rs
│ ├── tauri.conf.json
│ ├── tauri.windows.conf.json
│ ├── tests/
│ │ ├── app_config_load.rs
│ │ ├── app_type_parse.rs
│ │ ├── deeplink_import.rs
│ │ ├── import_export_sync.rs
│ │ ├── mcp_commands.rs
│ │ ├── provider_commands.rs
│ │ ├── provider_service.rs
│ │ ├── proxy_commands.rs
│ │ ├── skill_sync.rs
│ │ └── support.rs
│ └── wix/
│ └── per-user-main.wxs
├── tailwind.config.cjs
├── tests/
│ ├── components/
│ │ ├── AddProviderDialog.test.tsx
│ │ ├── CommonConfigModalBehavior.test.tsx
│ │ ├── GlobalProxySettings.test.tsx
│ │ ├── ImportExportSection.test.tsx
│ │ ├── McpFormModal.test.tsx
│ │ ├── OmoFormFields.mergeCustomModelsIntoStore.test.ts
│ │ ├── ProviderList.test.tsx
│ │ ├── SessionManagerPage.test.tsx
│ │ ├── SettingsDialog.test.tsx
│ │ ├── UnifiedSkillsPanel.test.tsx
│ │ ├── WebdavSyncSection.test.tsx
│ │ └── openclaw.utils.test.ts
│ ├── config/
│ │ ├── claudeProviderPresets.test.ts
│ │ └── opencodeProviderPresets.test.ts
│ ├── hooks/
│ │ ├── useCommonConfigSave.test.tsx
│ │ ├── useDirectorySettings.test.tsx
│ │ ├── useDragSort.test.tsx
│ │ ├── useImportExport.extra.test.tsx
│ │ ├── useImportExport.test.tsx
│ │ ├── useMcpValidation.test.tsx
│ │ ├── useProviderActions.test.tsx
│ │ ├── useProxyStatus.test.tsx
│ │ ├── useSettings.test.tsx
│ │ ├── useSettingsForm.test.tsx
│ │ └── useSettingsMetadata.test.tsx
│ ├── integration/
│ │ ├── App.test.tsx
│ │ └── SettingsDialog.test.tsx
│ ├── msw/
│ │ ├── handlers.ts
│ │ ├── server.ts
│ │ ├── state.ts
│ │ └── tauriMocks.ts
│ ├── setupGlobals.ts
│ ├── setupTests.ts
│ └── utils/
│ ├── omoConfig.test.ts
│ ├── providerConfigUtils.codex.test.ts
│ ├── providerMetaUtils.test.ts
│ └── testQueryClient.ts
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── vitest.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.rs text eol=lf
*.toml text eol=lf
*.json text eol=lf
*.md text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.txt text eol=lf
# TypeScript/JavaScript files
*.ts text eol=lf
*.tsx text eol=lf
*.js text eol=lf
*.jsx text eol=lf
# HTML/CSS files
*.html text eol=lf
*.css text eol=lf
*.scss text eol=lf
# Shell scripts
*.sh text eol=lf
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.woff binary
*.woff2 binary
*.ttf binary
*.exe binary
*.dll binary
================================================
FILE: .gitignore
================================================
node_modules/
dist/
release/
.DS_Store
*.log
.env
.env.local
*.tsbuildinfo
.npmrc
CLAUDE.md
# AGENTS.md
GEMINI.md
/.claude
/.codex
/.gemini
/.cc-switch
/.idea
/.vscode
vitest-report.json
nul
# Flatpak build artifacts
flatpak/cc-switch.deb
flatpak-build/
flatpak-repo/
.worktrees/
.spec-workflow/
copilot-api
.history
CODEBUDDY.md
.github
================================================
FILE: .node-version
================================================
22.12.0
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to CC Switch will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.12.3] - 2026-03-15
Post-v3.12.2 work adds a Tool Search domain restriction bypass, skill backup/restore lifecycle, proxy compatibility for OpenAI o-series models and gzip compression, and robustness fixes for Skills import, provider forms, and terminal session restore.
**Stats**: 17 commits | 61 files changed | +3,335 insertions | -194 deletions
### Added
- **Tool Search Domain Bypass**: Added setting to bypass Claude CLI Tool Search domain whitelist via equal-length binary patching; backups stored in `~/.cc-switch/toolsearch-backups/` with auto-reapply on startup when enabled
- **Skill Auto-Backup**: Skill files are automatically backed up to `~/.cc-switch/skill-backups/` before uninstall, with metadata preserved in `meta.json`; old backups pruned to keep at most 20
- **Skill Backup Restore & Delete**: Added list/restore/delete commands for skill backups; restore copies files back to SSOT, saves the DB record, and syncs to the current app with rollback on failure
### Changed
- **Proxy Gzip Compression**: Non-streaming proxy requests now auto-negotiate gzip compression instead of forcing `identity`; streaming requests conservatively keep `identity` to avoid SSE decompression errors
- **o1/o3 Model Compatibility**: Chat Completions proxy forwarding now correctly uses `max_completion_tokens` instead of `max_tokens` for OpenAI o-series models such as o1/o3/o4-mini (#1451)
- **OpenCode Model Variants**: Placed OpenCode model variants at top level instead of inside options for better discoverability (#1317)
- **Skills Import Flow**: Replaced implicit filesystem-based app inference with explicit `ImportSkillSelection` to prevent incorrect multi-app activation; added reconciliation to remove disabled/orphaned symlinks and MCP servers from live config
### Fixed
- **o-series Responses API Tokens**: Kept Responses API on the correct `max_output_tokens` field for o-series models instead of incorrectly injecting `max_completion_tokens`
- **Provider Form Double Submit**: Prevented duplicate submissions on rapid button clicks in provider add/edit forms (#1352)
- **Ghostty Session Restore**: Fixed Claude session restore in Ghostty terminal (#1506, thanks @canyonsehun)
- **Skill ZIP Import Extension**: Added `.skill` file extension support in ZIP import dialog (#1240, #1455)
- **Skill ZIP Install Target App**: ZIP skill installs now use the currently active app instead of always defaulting to Claude
- **OpenClaw Active Card Highlight**: Fixed active OpenClaw provider card not being highlighted (#1419)
- **Responsive Layout with TOC**: Improved responsive design when TOC title exists (#1491)
- **Import Skills Dialog White Screen**: Added missing TooltipProvider in ImportSkillsDialog to prevent runtime crash when opening the dialog
- **Panel Bottom Blank Area**: Replaced hardcoded `h-[calc(100vh-8rem)]` with `flex-1 min-h-0` across all content panels to eliminate bottom gap caused by mismatched offset values
---
## [3.12.2] - 2026-03-12
Post-v3.12.1 work focuses on Common Config safety during proxy takeover and more reliable Codex TOML editing.
**Stats**: 5 commits | 22 files changed | +1,716 insertions | -288 deletions
### Added
- **Empty State Guidance**: Improved first-run experience with detailed import instructions and a conditional Common Config snippet hint for Claude/Codex/Gemini providers
### Changed
- **Proxy Takeover Restore Flow**: Proxy takeover hot-switch and provider sync now refresh the restore backup instead of overwriting live config files, rebuilding effective provider settings with Common Config applied so rollback preserves the real user configuration
- **Codex TOML Editing Engine**: Refactored Codex `config.toml` updates onto shared section-aware TOML helpers in Rust and TypeScript, covering `base_url` and `model` field edits across provider forms and takeover cleanup
- **Common Config Initialization Lifecycle**: Startup now auto-extracts Common Config snippets from clean live configs before takeover restoration, tracks explicit "snippet cleared" state, and persists a one-time legacy migration flag to avoid repeated backfills
### Fixed
- **Common Config Loss During Takeover**: Fixed cases where proxy takeover could drop Common Config changes, overwrite live configs during sync, or produce incomplete restore snapshots when switching providers
- **Codex Restore Snapshot Preservation**: Fixed Codex takeover restore backups so existing `mcp_servers` blocks survive provider hot-switches instead of being discarded; changed MCP backup preservation from wholesale table replacement to per-server-id merge so provider/common-config MCP updates win on conflict while live-only servers are retained
- **Cleared Snippet Resurrection**: Fixed startup auto-extraction recreating Common Config snippets that users had intentionally cleared
- **Codex `base_url` Misplacement**: Fixed Codex `base_url` extraction and editing to target the active `[model_providers.<name>]` section instead of appending to the file tail or confusing `mcp_servers.*.base_url` entries for provider endpoints
---
## [3.12.1] - 2026-03-12
### Patch Release
Stability-focused patch release fixing the Common Config modal infinite reopen loop, a WebDAV sync foreign key constraint failure, several i18n interpolation issues, and a Windows toolbar compact mode bug. Also adds **StepFun** provider presets, **OpenClaw input type selection** and **authHeader** support, upgrades Gemini to **3.1-pro**, and welcomes four new sponsor partners.
**Stats**: 19 commits | 56 files changed | +1,429 insertions | -396 deletions
### Added
#### Provider Presets
- **StepFun**: Added StepFun (阶跃星辰) provider presets including the step-3.5-flash model across supported applications (#1369, thanks @hengm3467)
#### OpenClaw Enhancements
- **Input Type Selection**: Added input type selection dropdown for model Advanced Options in OpenClaw configuration form (#1368, thanks @liuxxxu)
- **authHeader Field**: Added optional `authHeader` boolean to OpenClawProviderConfig for vendor-specific auth header support (e.g. Longcat), and refactored form state to reuse the shared type
#### Sponsor Partners
- **Micu API**: Added Micu API as sponsor partner with affiliate links
- **XCodeAPI**: Added XCodeAPI as sponsor partner
- **SiliconFlow**: Added SiliconFlow (硅基流动) as sponsor partner with affiliate links
- **CTok**: Added CTok as sponsor partner
### Changed
- **UCloud → Compshare**: Renamed UCloud provider to Compshare (优云智算) with full i18n support across all three locales (EN/ZH/JA)
- **Compshare Links**: Updated Compshare sponsor registration links to coding-plan page
- **Gemini Model Upgrade**: Upgraded default Gemini model from 2.5-pro to 3.1-pro in provider presets
### Fixed
#### Common Config & UI
- **Common Config Modal Loop**: Fixed an infinite reopen loop in the Common Config modal and added draft editing support to prevent data loss during edits
- **Toolbar Compact Mode (Windows)**: Fixed toolbar compact mode not triggering on Windows due to left-side overflow (#1375, thanks @zuoliangyu)
- **Session Search Index**: Fixed session search index not syncing with query data, causing stale list display after session deletion
#### Sync & Data
- **WebDAV Provider Health FK**: Fixed foreign key constraint failure when restoring `provider_health` table during WebDAV sync
#### Provider & Preset
- **Longcat authHeader**: Added missing `authHeader: true` to Longcat provider preset (#1377, thanks @wavever)
- **OpenClaw Tool Permissions**: Aligned OpenClaw tool permission profiles with upstream schema (#1355, thanks @bigsongeth)
- **X-Code API URL**: Corrected X-Code API URL from `www.x-code.cn` to `x-code.cc`
#### i18n & Localization
- **Stream Check Toast**: Fixed stream check toast i18n interpolation keys not matching translation placeholders
- **Proxy Startup Toast**: Fixed proxy startup toast not interpolating address and port values (#1399, thanks @Mason-mengze)
- **OpenCode API Format Label**: Renamed OpenCode API format label from "OpenAI" to "OpenAI Responses" for accuracy
---
## [3.12.0] - 2026-03-09
### Feature Release
This release restores the **Model Health Check (Stream Check)** UI, adds **OpenAI Responses API** format conversion, introduces the **Bedrock Optimizer** for thinking + cache injection, expands provider presets (Ucloud, Micu, X-Code API, Novita, Bailian For Coding), overhauls **OpenClaw config panels** with a JSON5 round-trip write engine, enhances **WebDAV sync** with dual-layer versioning, and delivers a comprehensive **i18n audit** fixing 69 missing keys alongside 20+ bug fixes.
**Stats**: 56 commits | 221 files changed | +20,582 insertions | -8,026 deletions
### Added
#### Stream Check (Model Health Check)
- **Restore Stream Check UI**: Brought back the model health check (Stream Check) panel for testing provider endpoint availability with live streaming validation
- **First-Run Confirmation**: Added a confirmation dialog on first use of Stream Check to inform users about the feature's purpose and network requests
- **OpenAI Chat Format Support**: Stream Check now supports `openai_chat` api_format, enabling health checks for providers using OpenAI-compatible endpoints
#### OpenAI Responses API
- **Responses API Format Conversion**: New `api_format = "openai_responses"` option enabling Anthropic Messages ↔ OpenAI Responses API bidirectional conversion for providers that implement the Responses API
- **Responses API Deduplication**: Deduplicated and improved the Responses API conversion logic, consolidating shared transformation code
#### Bedrock Optimizer
- **Bedrock Request Optimizer**: PRE-SEND optimizer that injects thinking parameters and cache control blocks into AWS Bedrock requests, enabling extended thinking and prompt caching on Bedrock endpoints (#1301)
#### OpenClaw Enhancements
- **JSON5 Round-Trip Write Engine**: Overhauled OpenClaw config panels with a JSON5 round-trip write engine that preserves comments, formatting, and ordering when saving configuration changes
- **Config Panel Improvements**: Redesigned EnvPanel as a full JSON editor, added `tools.profile` selection to ToolsPanel, introduced OpenClawHealthBanner for config validation warnings, and added legacy timeout migration support in Agents Defaults
- **Agent Model Dropdown**: Replaced text inputs with dropdown selects for OpenClaw agent model configuration, offering a curated list of available models
- **User-Agent Toggle**: Added a User-Agent header toggle for OpenClaw, defaulting to off to avoid potential compatibility issues with certain providers
#### Provider Presets
- **Ucloud**: Added Ucloud partner provider preset for Claude, Codex, and OpenClaw with endpointCandidates, unified apiKeyUrl, refreshed model defaults, and OpenClaw `templateValues` / `suggestedDefaults`
- **Micu**: Added Micu partner provider preset for Claude, Codex, OpenClaw, and OpenCode with OpenClaw `templateValues` / `suggestedDefaults`
- **X-Code API**: Added X-Code API partner provider preset for Claude, Codex, and OpenCode with endpointCandidates
- **Novita**: Added Novita provider presets and icon across all supported apps (#1192)
- **Bailian For Coding**: Added Bailian For Coding preset configuration (#1263)
- **SiliconFlow Partner Badge**: Added partner badge designation for SiliconFlow provider presets
- **Model Role Badges**: Added model role badges (e.g., Opus, Sonnet) to provider presets and reordered presets to prioritize Opus models
#### WebDAV Sync
- **Dual-Layer Versioning**: Added protocol v2 + db-v6 dual-layer versioning to WebDAV sync, enabling backward-compatible sync format evolution and automatic migration detection
- **Auto-Sync Confirmation**: Added a confirmation dialog when toggling WebDAV auto-sync on/off to prevent accidental changes
#### Usage & Data
- **Daily Rollups & Auto-Vacuum**: Added usage daily rollups for aggregated statistics, incremental auto-vacuum for storage management, and sync-aware backup that coordinates with WebDAV sync cycles
- **UsageFooter Extra Fields**: Added extra field display in UsageFooter component for normal mode, showing additional usage metadata (#1137)
#### Session Management
- **Session Deletion**: Added session deletion with per-provider cleanup and path safety validation, allowing users to remove individual conversation sessions
#### UI & Config
- **Auth Field Selector**: Restored Claude provider auth field selector supporting both AUTH_TOKEN and API_KEY authentication modes
- **Failover Toggle**: Moved failover toggle to display independently on the main page with a confirmation dialog for enabling/disabling
- **Common Config Auto-Extract**: Auto-extract Common Config Snippets from live configuration files on first run, seeding initial common config without manual setup
- **New Provider Page Improvements**: Improved the new provider page with API endpoint and model name fields (#1155)
### Changed
#### Architecture
- **Common Config Runtime Overlay**: Common Config is now applied as a runtime overlay during provider switching instead of being materialized (merged) into each provider's stored config. This preserves the original provider config in the database and applies common settings dynamically at write time
- **First-Run Auto-Extract**: On first run, Common Config Snippets are automatically extracted from the current live configuration files, eliminating the need for manual initial setup
### Fixed
#### Proxy & Streaming
- **OpenAI Streaming Conversion**: Fixed OpenAI ChatCompletion → Anthropic Messages streaming conversion that could produce malformed events under certain response structures
- **Codex /responses/compact Route**: Added support for Codex `/responses/compact` route in proxy forwarding (#1194)
- **Codex Common Config TOML Merge**: Fixed Codex Common Config to use structural TOML merge/subset instead of raw string comparison, correctly handling key ordering and formatting differences
- **Proxy Forwarder Failure Logs**: Improved proxy forwarder failure logging with more descriptive error messages
#### Provider & Preset
- **X-Code Rename**: Renamed "X-Code" provider to "X-Code API" for consistency with the official branding
- **SSSAiCode Missing /v1**: Added missing `/v1` path to SSSAiCode default endpoint for Codex and OpenCode
- **AICoding URL Fix**: Removed `www` prefix from aicoding.sh provider URLs to match the correct domain
- **New Provider Page Input Handling**: Fixed the new provider page so API endpoint / model fields handle line-break deletion correctly and added the missing `codexConfig.modelNameHint` i18n key for zh/en/ja
#### Platform
- **Cache Hit Token Statistics**: Fixed missing token statistics for cache hits in streaming responses (#1244)
- **Minimize-to-Tray Auto Exit**: Fixed issue where the application would automatically exit after being minimized to the system tray for a period of time (#1245)
#### i18n & Localization
- **Comprehensive i18n Audit**: Added 69 missing i18n keys and fixed hardcoded Chinese strings across the application, improving localization coverage for all three languages (zh/en/ja)
- **Model Test Panel i18n**: Corrected i18n key paths for model test panel title and description
- **JSON5 Slash Escaping**: Normalized JSON5 slash escaping and added i18n support for OpenClaw panel labels
#### UI
- **Skills Count Display**: Fixed skills count not displaying correctly when adding new skills (#1295)
- **Endpoint Speed Test**: Removed HTTP status code display from endpoint speed test results to reduce visual noise
- **Outline Button Text Tone**: Aligned outline button text color tone with usage refresh control for visual consistency (#1222)
### Performance
- **OpenClaw Config Write Skip**: Skip backup and atomic write when OpenClaw configuration content is unchanged, avoiding unnecessary I/O operations
### Documentation
- **User Manual i18n**: Restructured user manual for internationalization and added complete EN/JA translations alongside the existing ZH documentation
- **User Manual OpenClaw**: Added OpenClaw coverage and completed settings documentation for the user manual
- **UCloud CompShare Sponsor**: Added UCloud CompShare as a sponsor partner
- **Docs Directory Reorganization**: Reorganized docs directory structure, added user manual links to all three README files, removed cross-language links from user manual sections, and synced README features across EN/ZH/JA
### Maintenance
- **Periodic Maintenance Timer**: Consolidated periodic maintenance timers into a unified scheduler, combining vacuum and rollup operations into a single timer
- **OpenClaw Save Toast**: Removed backup path display from OpenClaw save toasts for cleaner notification messages
---
## [3.11.1] - 2026-02-28
### Hotfix Release
This release reverts the Partial Key-Field Merging architecture introduced in v3.11.0, restoring the proven "full config overwrite + Common Config Snippet" mechanism, and fixes several UI and platform compatibility issues.
**Stats**: 8 commits | 52 files changed | +3,948 insertions | -1,411 deletions
### Reverted
- **Restore Full Config Overwrite + Common Config Snippet** (revert 992dda5c): Reverted the partial key-field merging refactoring from v3.11.0 due to critical issues — non-whitelisted custom fields were lost during provider switching, backfill permanently stripped non-key fields from the database, and the whitelist required constant maintenance. Restores full config snapshot write, Common Config Snippet UI and backend commands, and 6 frontend components/hooks
### Changed
- **Proxy Panel Layout**: Moved proxy on/off toggle from accordion header into panel content area, placed directly above app takeover options, ensuring users see takeover configuration immediately after enabling the proxy
- **Manual Import for OpenCode/OpenClaw**: Removed auto-import on startup; empty state now shows an "Import Current Config" button, consistent with Claude/Codex/Gemini behavior
### Fixed
- **"Follow System" Theme Not Auto-Updating**: Delegated to Tauri's native theme tracking (`set_window_theme(None)`) so the WebView's `prefers-color-scheme` media query stays in sync with OS theme changes
- **Compact Mode Cannot Exit**: Restored `flex-1` on `toolbarRef` so `useAutoCompact`'s exit condition triggers correctly based on available width instead of content width
- **Proxy Takeover Toast Shows {{app}}**: Added missing `app` interpolation parameter to i18next `t()` calls for proxy takeover enabled/disabled messages
- **Windows Protocol Handler Side Effects**: Disabled environment check and one-click install on Windows to prevent unintended protocol handler registration
---
## [3.11.0] - 2026-02-26
### Feature Release
This release introduces **OpenClaw** as the fifth supported application, a full **Session Manager** for browsing conversation history across all apps, an independent **Backup Management** panel, **Oh My OpenCode (OMO)** integration, and 50+ other features, fixes, and improvements across 147 commits.
**Stats**: 147 commits | 274 files changed | +32,179 insertions | -5,467 deletions
### Added
#### OpenClaw Support (New Application)
- **OpenClaw Integration**: Full management support for OpenClaw as the fifth application in CC Switch, including provider switching, configuration panels (Env / Tools / Agents Defaults), Workspace file management (HEARTBEAT / BOOTSTRAP / BOOT), daily memory files, and additive overlay mode
- **OpenClaw Provider Presets**: 13+ built-in provider presets with brand icon and complete i18n (zh/en/ja)
- **OpenClaw Form Fields**: Dedicated provider form with providerKey input, model allowlist auto-registration, and default model button
- **OpenClaw Config Panels**: Env editor, Tools editor, and Agents Defaults editor backed by JSON5 read/write (`openclaw_config.rs`)
#### Session Manager
- **Session Manager**: Browse and search conversation history for Claude Code, Codex, Gemini CLI, OpenCode, and OpenClaw with table-of-contents navigation and in-session search
- **Session App Filter**: Auto-filter sessions by current app when entering the session page
- **Session Performance**: Parallel directory scanning and head-tail JSONL reading for faster session list loading
#### Backup Management
- **Backup Panel**: Independent backup management panel with configurable backup policy (max count, auto-cleanup) and backup rename support
- **Periodic Backup**: Hourly automatic backup timer during runtime
- **Pre-Migration Backup**: Automatic backup before database schema migrations with backfill warning
- **Delete Backup**: Delete individual backup files with confirmation dialog
- **Backup Time Fix**: Use local time instead of UTC for backup file names
#### Oh My OpenCode (OMO)
- **OMO Integration**: Full Oh My OpenCode config file management with agent model selection, category configuration, and recommended model fill
- **OMO Slim**: Lightweight oh-my-opencode-slim mode support with OmoVariant parameterization
- **OMO Cross-Exclusion**: Enforce OMO ↔ OMO Slim mutual exclusion at the database level
#### Workspace
- **Daily Memory Search**: Full-text search across daily memory files with date-sorted display
- **Clickable Paths**: Directory paths in workspace panels are now clickable; renamed “Today's Note” to “Add Memory”
- **Workspace Files Panel**: Manage bootstrap markdown files for OpenClaw (HEARTBEAT / BOOTSTRAP / BOOT types)
#### Provider Presets
- **AWS Bedrock**: Support for AKSK and API Key authentication modes (Claude and OpenCode)
- **SSAI Code**: Partner provider preset across all five apps
- **CrazyRouter**: Partner provider preset with custom icon
- **AICoding**: Partner provider preset with i18n promotion text
- **Bailian**: Renamed from Qwen Coder with new icon; updated domestic model providers to latest versions
#### Proxy & Network
- **Thinking Budget Rectifier**: New rectifier for thinking budget parameters with dedicated module (`thinking_budget_rectifier.rs`)
- **WebDAV Auto Sync**: Automatic periodic sync with large file protection mechanism
#### UI & UX
- **Theme Animation**: Circular reveal animation when toggling between light and dark themes
- **Claude Quick Toggles**: Quick toggle switches in the Claude config JSON editor for common settings
- **Dynamic Endpoint Hint**: Context-aware hint text in endpoint input based on API format selection
- **AppSwitcher Auto Compact**: Automatically collapse to compact mode based on available width, with smooth transition animation
- **App Transition**: Fade-in/fade-out animation when switching between OpenClaw and other apps
- **Silent Startup Conditional**: Show silent startup option only when launch-on-startup is enabled
#### Settings & Environment
- **First-Run Confirmation**: Confirmation dialogs for proxy and usage features on first use
- **Local Proxy Toggle**: `enableLocalProxy` setting to control proxy UI visibility on the home page
- **Environment Check**: More granular local environment detection (installed CLI tool versions, Volta path detection)
#### Usage & Pricing
- **Usage Dashboard Enhancement**: Auto-refresh control, robust formatting, and request log table improvements
- **New Model Pricing**: Added pricing data for claude-opus-4-6 and gpt-5.3-codex with incremental data seeding
### Changed
#### Architecture
- **Partial Key-Field Merging (⚠️ Breaking, reverted in v3.11.1)**: Provider switching now uses partial key-field merging instead of full config overwrite, preserving user's non-provider settings (plugins, MCP, permissions). The "Common Config Snippet" feature has been removed as it is no longer needed. Removes 6 frontend files and ~150 lines of backend dead code (#1098)
- **Manual Import**: Replaced auto-import on startup with manual “Import Current Config” button in empty state, reducing ~47 lines of startup code
- **OMO Variant Parameterization**: Eliminated ~250 lines of OMO/OMO Slim code duplication via `OmoVariant` struct with STANDARD/SLIM constants
- **OMO Common Config Removal**: Removed the two-layer merge system for OMO common config (-1,733 lines across 21 files)
#### Code Quality
- **ProviderForm Decomposition**: Extracted ProviderForm.tsx from 2,227 lines to 1,526 lines by splitting into 5 focused modules (opencodeFormUtils, useOmoModelSource, useOpencodeFormState, useOmoDraftState, useOpenclawFormState)
- **Shared MCP/Skills Components**: Extracted AppCountBar, AppToggleGroup, and ListItemRow shared components to eliminate duplication across MCP and Skills panels
- **OpenClaw TanStack Query Migration**: Migrated Env, Tools, and AgentsDefaults panels from manual useState/useEffect to centralized TanStack Query hooks
#### Settings Layout
- **Proxy Tab**: Split Advanced tab into dedicated Proxy tab (local proxy, failover, rectifiers, global outbound proxy); moved pricing config to Usage dashboard as collapsible accordion. SettingsPage reduced from ~716 to ~426 lines with 5-tab layout: General | Proxy | Advanced | Usage | About
- **Data Section Split**: Split data accordion into Import/Export and Cloud Sync sections for better discoverability
#### Terminal & Config
- **Unified Terminal Selection**: Consolidated terminal preference to global settings; added WezTerm support and terminal name mapping (iterm2 → iterm)
- **OpenClaw Agents Panel**: Primary model field set to read-only; detailed model fields (context window, max tokens, reasoning, cost) moved to advanced options
- **Claude Model Update**: Updated Claude model references from 4.5 to 4.6 across all provider presets
### Fixed
#### Critical
- **Windows Home Dir Regression**: Restored default home directory resolution on Windows to prevent providers/settings “disappearing” when `HOME` env var differs from the real user profile directory (Git/MSYS environments); auto-detects v3.10.3 legacy database location
- **Linux White Screen**: Disabled WebKitGTK hardware acceleration on AMD GPUs (Cezanne/Radeon Vega) to prevent EGL initialization failure causing blank screen on startup
- **OpenAI Beta Parameter**: Stopped appending `?beta=true` to OpenAI Chat Completions endpoints, fixing request failures for Nvidia and other `apiFormat=”openai_chat”` providers
- **Health Check Auth Mode**: Health check now respects provider's auth_mode setting instead of always using x-api-key header
#### Provider & Preset
- **OpenClaw /v1 Prefix**: Removed /v1 prefix from OpenClaw anthropic-messages presets to prevent double path (/v1/v1/messages) with Anthropic SDK auto-append
- **Opus Pricing**: Corrected Opus pricing from $15/$75 to $5/$25 and upgraded model ID to claude-opus-4-6
- **AIGoCode URLs**: Unified API base URL to https://api.aigocode.com across all apps; removed trailing /v1 suffix
- **Zhipu GLM**: Removed outdated partner status from Claude, OpenCode, and OpenClaw presets
- **API Key Visibility**: Restored API Key input field when creating new Claude providers (was incorrectly hidden for non-cloud_provider categories)
#### OMO / OMO Slim
- **OMO Slim Category Checks**: Added missing omo-slim category checks across add/form/mutation paths
- **OMO Slim Cache Invalidation**: Invalidate OMO Slim query cache after provider mutations to prevent stale UI state
- **OMO Recommended Models**: Synced agent/category recommended models with upstream sources; fixed provider/model format to pure model IDs
- **OMO Fill Feedback**: Added toast feedback when “Fill Recommended” button silently fails
- **OMO Last-Provider Restriction**: Removed last-provider deletion restriction for OMO/OMO Slim plugins
- **OpenCode Model Validation**: Reject saving OpenCode providers without at least one configured model
#### OpenClaw
- **OpenClaw P0-P3 Fixes**: Fixed 25 missing i18n keys, replaced key={index} with stable crypto.randomUUID(), excluded openclaw from ProxyToggle/FailoverToggle, added deep link merge_additive_config(), unified serde(flatten) naming, added directory existence checks, removed dead code, added duplicate key validation
- **OpenClaw Robustness**: Fixed EnvPanel visibleKeys using entry key names instead of array indices; added NaN guards; validated provider ID and model before import
- **OpenClaw i18n Dedup**: Merged duplicate openclaw i18n keys to restore provider form translations
#### Platform
- **Window Flash**: Prevented window flicker on silent startup (Windows)
- **Title Bar Theme**: Title bar now follows dark/light mode theme changes
- **Skills Path Separator**: Fixed path separator matching for skill installation status on Windows (supports both `/` and `\`)
- **WSL Conditional Compilation**: Added `#[cfg(target_os = “windows”)]` to WSL helper functions to eliminate dead_code warnings on non-Windows platforms
#### UI
- **Toolbar Clipping**: Removed toolbar height limit that was clipping AppSwitcher
- **Update Badge**: Show update badge instead of green check when a newer version is available
- **Session Button Visibility**: Only show Session Manager button for Claude and Codex apps
- **Directory Spacing**: Added vertical spacing between directory setting sections
- **Dark Mode Cards**: Unified SQL import/export card styling in dark mode
- **OpenClaw Scroll**: Enabled scrolling for OpenClaw configuration panel content
#### i18n & Localization
- **Session Manager i18n**: Replaced hardcoded Chinese strings with i18n keys for relative time, role labels, and UI elements
- **OpenClaw Default Model Label**: Renamed “Enable/Default” to “Set as Default / Current Default” with wider button
- **Daily Memory Sort**: Sort daily memory files by filename date (YYYY-MM-DD.md) instead of modification time
- **Backup Name i18n**: Use local time for backup file names
#### Other
- **Skill Doc URL**: Use actual branch from download_repo for documentation URL; switched from /tree/ to /blob/ pointing to SKILL.md
- **OpenCode Install Detection**: Added install.sh priority paths (OPENCODE_INSTALL_DIR > XDG_BIN_DIR > ~/bin > ~/.opencode/bin) with path dedup and cross-platform executable candidates
- **Provider Auto-Import**: Removed auto-import side effect from useProvidersQuery queryFn; users now trigger import manually via empty state button
- **Manual Backup Validation**: Treat missing database file as error during manual backup to prevent false success toast
### Performance
- **Session Panel Loading**: Parallel directory scanning and head-tail JSONL reading for Codex, OpenClaw, and OpenCode session providers
- **Query Cache Cleanup**: Removed unnecessary TanStack Query cache overhead for Tauri local IPC calls
### Documentation
- **Sponsors**: Added/updated SSSAiCode, Crazyrouter, AICoding, Right Code, and MiniMax sponsor entries across all README languages
- **User Manual**: Added user manual documentation (#979)
### Maintenance
- **Pre-Release Cleanup**: Removed debug logs, fixed clippy warnings, added missing Japanese translations, and formatted code
- **UI Exclusions**: Hidden MCP, Skills, proxy/pricing, stream check, and model test panels for OpenClaw where not applicable
---
## [3.10.3] - 2026-01-30
### Feature Release
This release introduces a generic API format selector, pricing configuration enhancements, and multiple UX improvements.
### Added
- **API Key Link for OpenCode**: API key link support for OpenCode provider form, enabling quick access to provider key management pages
- **AICodeMirror Partner Preset**: Added AICodeMirror partner preset for all apps (Claude, Codex, Gemini, OpenCode)
- **API Format Selector**: Generic API format chooser for Claude providers, replacing the OpenRouter-specific toggle. Supports Anthropic Messages (native) and OpenAI Chat Completions format
- **API Format Presets**: Allow preset providers to specify API format (anthropic or openai_chat) for third-party proxy services
- **Proxy Hint**: Display info toast when switching to OpenAI Chat format provider, reminding users to enable proxy
- **Pricing Config Enhancement**: Per-provider cost multiplier, pricing model source (request/response), request model logging, and enriched usage UI (#781)
- **Skills ZIP Install**: Install skills directly from local ZIP files with recursive scanning support
- **Preferred Terminal**: Choose preferred terminal app per platform (macOS: Terminal.app/iTerm2/Alacritty/Kitty/Ghostty; Windows: cmd/PowerShell/Windows Terminal; Linux: GNOME Terminal/Konsole/Xfce4/Alacritty/Kitty/Ghostty)
- **Silent Startup**: Option to prevent window popup on launch (#713)
- **OpenCode Environment Check**: Version detection with Go path scanning and one-click install from GitHub Releases
- **OpenCode Directory Sync**: Auto-sync all providers to live config on directory change with additive mode support
- **NVIDIA NIM Preset**: New provider preset for Claude and OpenCode with nvidia.svg icon
- **n1n.ai Preset**: New provider preset (#667)
- **Update Badge Icon**: Replace update badge dot with ArrowUpCircle icon
- **Linux ARM64**: CI build support for Linux ARM64 architecture
### Changed
- **API Format Migration**: Migrate api_format from settings_config to ProviderMeta to prevent polluting ~/.claude/settings.json
- **DeepSeek max_tokens**: Remove max_tokens clamp from proxy transform layer
- **Terminal Functions**: Consolidate redundant terminal launch functions
- **Home Dir Utility**: Consolidate get_home_dir into single public function
- **Kimi/Moonshot**: Upgrade provider presets to k2.5 model
### Fixed
- **Codex 404 & Timeout**: Fix 404 errors and connection timeout with custom base_url; improve /v1 prefix handling and system proxy detection (#760)
- **Proxy URL Building**: Fix duplicate /v1/v1 in URL; extend ?beta=true to /v1/chat/completions endpoint
- **OpenRouter Compat Mode**: Improve backward compatibility supporting number and string types
- **Gemini Visibility**: Correct Gemini default visibility to true (#818)
- **Footer Layout**: Correct footer layout in advanced settings tab
- **Claude Code Detection**: Prioritize native install path for detection
- **Tray Menu**: Simplify title labels and optimize menu separators (#796)
- **Duplicate Skills**: Prevent duplicate skill installation from different repos (#778)
- **Windows Tests**: Stabilize test environment (#644)
- **i18n**: Update apiFormatOpenAIChat label to mention proxy requirement
- **Error Display**: Use extractErrorMessage for complete error display in mutations
- **Sponsors**: Add AICodeMirror and reorder sponsor list
---
## [3.10.2] - 2026-01-24
### Patch Release
This maintenance release adds skill sync options and includes important bug fixes.
### Added
- **Skills**: Add skill sync method setting with symlink/copy options
- **Partners**: Add RightCode as official partner
### Fixed
- **Prompts**: Clear prompt file when all prompts are disabled
- **OpenCode**: Preserve extra model fields during serialization
- **Provider Form**: Backfill model fields when editing Claude provider
---
## [3.10.1] - 2026-01-23
### Patch Release
This maintenance release includes important bug fixes for Windows platform, UI improvements, and code quality enhancements.
### Added
- **Provider Icons**: Updated RightCode provider icon with improved visual design
### Changed
- **Proxy Rectifier**: Changed rectifier default state to disabled for better stability
- **Window Settings**: Reordered window settings and updated default values for improved UX
- **UI Layout**: Increased app icon collapse threshold from 3 to 4 icons
- **Code Quality**: Simplified `RectifierConfig` implementation using `#[derive(Default)]`
### Fixed
- **Windows Platform**:
- Fixed terminal window closing immediately after execution on Windows
- Corrected OpenCode config path resolution on Windows
- **UI Improvements**:
- Fixed ProviderIcon color validation to prevent black icons from appearing
- Unified layout padding across all panels for consistent spacing
- Fixed panel content alignment with header constraints
- **Code Quality**: Resolved Rust Clippy warnings and applied consistent formatting
---
## [3.10.0] - 2026-01-21
### Feature Release
This release introduces OpenCode support and brings improvements across proxy, usage tracking, and overall UX.
### Added
- **OpenCode Support** - Manage OpenCode providers, MCP servers, and Skills, with first-launch import and full internationalization (#695)
- **Global Proxy** - Add global proxy settings for outbound network requests (#596)
- **Claude Rectifier** - Add thinking signature rectifier for Claude API (#595)
- **Health Check Enhancements** - Configurable prompt and CLI-compatible requests for stream health check (#623)
- **Per-Provider Config** - Support provider-specific configuration and persistence (#663)
- **App Visibility Controls** - Show/hide apps and keep tray menu in sync (Gemini hidden by default)
- **Takeover Compact Mode** - Use a compact AppSwitcher layout when showing 3+ visible apps
- **Keyboard Shortcut** - Press `ESC` to quickly go back/close panels (#670)
- **Terminal Improvements** - Provider-specific terminal button, `fnm` path support, and safer cross-platform launching (#564)
- **WSL Tool Detection** - Detect tool versions in WSL with additional security hardening (#627)
- **Skills Presets** - Add `baoyu-skills` preset repo and auto-supplement missing default repos
### Changed
- **Proxy Logging** - Simplify proxy log output (#585)
- **Pricing Editor UX** - Unify pricing edit modal with `FullScreenPanel`
- **Advanced Settings Layout** - Move rectifier section below failover for better flow
- **OpenRouter Compat Mode** - Disable OpenRouter compatibility mode by default and hide UI toggle
### Fixed
- **Auto Failover** - Switch to P1 immediately when enabling auto failover
- **Provider Edit Dialog** - Fix stale data when reopening provider editor after save (#654)
- **Deeplink** - Support multiple endpoints and prioritize `GOOGLE_GEMINI_BASE_URL` over `GEMINI_BASE_URL` (#597)
- **MCP (WSL)** - Skip `cmd /c` wrapper for WSL target paths (#592)
- **Usage Templates** - Add variable hints and validation fixes; prevent config leaking between providers (#628)
- **Gemini Timeout Format** - Convert timeout params to Gemini CLI format (#580)
- **UI** - Fix Select dropdown rendering in `FullScreenPanel`; auto-apply default icon color when unset
- **Usage UI** - Auto-adapt usage block offset based on action buttons width (#613)
- **Provider Endpoint** - Persist endpoint auto-select state (#611)
- **Provider Form** - Reset baseUrl and apiKey states when switching presets
---
## [3.9.1] - 2026-01-09
### Bug Fix Release
This release focuses on stability improvements and crash prevention.
### Added
- **Crash Logging** - Panic hook captures crash info to `~/.cc-switch/crash.log` with full stack traces (#562)
- **Release Logging** - Enable logging for release builds with automatic rotation (keeps 2 most recent files)
- **AIGoCode Icon** - Added colored icon for AIGoCode provider preset
### Fixed
- **Proxy Panic Prevention** - Graceful degradation when HTTP client initialization fails due to invalid proxy settings; falls back to no_proxy mode (#560)
- **UTF-8 Safety** - Fix potential panic when masking API keys or truncating logs containing multi-byte characters (Chinese, emoji, etc.) (#560)
- **Default Proxy Port** - Change default port from 5000 to 15721 to avoid conflict with macOS AirPlay Receiver (#560)
- **Windows Title** - Display "CC Switch" instead of default "Tauri app" in window title
- **Windows/Linux Spacing** - Remove extra 28px blank space below native titlebar introduced in v3.9.0
- **Flatpak Tray Icon** - Bundle libayatana-appindicator for tray icon support on Flatpak (#556)
- **Provider Preset** - Correct casing from "AiGoCode" to "AIGoCode" to match official branding
---
## [3.9.0] - 2026-01-07
### Stable Release
This stable release includes all changes from `3.9.0-1`, `3.9.0-2`, and `3.9.0-3`.
### Added
- **Local API Proxy** - High-performance local HTTP proxy for Claude Code, Codex, and Gemini CLI (Axum-based)
- **Per-App Takeover** - Independently route each app through the proxy with automatic live-config backup/redirect
- **Auto Failover** - Circuit breaker + smart failover with independent queues and health tracking per app
- **Universal Provider** - Shared provider configurations that can sync to Claude/Codex/Gemini (ideal for API gateways like NewAPI)
- **Provider Search Filter** - Quick filter to find providers by name (#435)
- **Keyboard Shortcut** - Open settings with Command+comma / Ctrl+comma (#436)
- **Deeplink Usage Config** - Import usage query config via deeplink (#400)
- **Provider Icon Colors** - Customize provider icon colors (#385)
- **Skills Multi-App Support** - Skills now support both Claude Code and Codex (#365)
- **Closable Toasts** - Close button for switch toast and all success toasts (#350)
- **Skip First-Run Confirmation** - Option to skip Claude Code first-run confirmation dialog
- **MCP Import** - Import MCP servers from installed apps
- **Common Config Snippet Extraction** - Extract reusable common config snippets from the current provider or editor content (Claude/Codex/Gemini)
- **Usage Enhancements** - Model extraction, request logging improvements, cache hit/creation metrics, and auto-refresh (#455, #508)
- **Error Request Logging** - Detailed logging for proxy requests (#401)
- **Linux Packaging** - Added RPM and Flatpak packaging targets
- **Provider Presets & Icons** - Added/updated partner presets and icons (e.g., MiMo, DMXAPI, Cubence)
### Changed
- **Usage Terminology** - Rename "Cache Read/Write" to "Cache Hit/Creation" across all languages (#508)
- **Model Pricing Data** - Refresh built-in model pricing table (Claude full version IDs, GPT-5 series, Gemini ID formats, and Chinese models) (#508)
- **Proxy Header Forwarding** - Switch to a blacklist approach and improve header passthrough compatibility (#508)
- **Failover Behavior** - Bypass timeout/retry configs when failover is disabled; update default failover timeout and circuit breaker values (#508, #521)
- **Provider Presets** - Update default model versions and change the default Qwen base URL (#517)
- **Skills Management** - Unify Skills management architecture with SSOT + React Query; improve caching for discoverable skills
- **Settings UX** - Reorder items in the Advanced tab for better discoverability
- **Proxy Active Theme** - Apply emerald theme when proxy takeover is active
### Fixed
- **Security** - Security fixes for JavaScript executor and usage script (#151)
- **Usage Timezone & Parsing** - Fix datetime picker timezone handling; improve token parsing/billing for Gemini and Codex formats (#508)
- **Windows Compatibility** - Improve MCP export and version check behavior to avoid terminal popups
- **Windows Startup** - Use system titlebar to prevent black screen on startup
- **WebView Compatibility** - Add fallback for crypto.randomUUID() on older WebViews
- **macOS Autostart** - Use `.app` bundle path to prevent terminal window popups
- **Database** - Add missing schema migrations; show an error dialog on initialization failure with a retry option
- **Import/Export** - Restrict SQL import to CC Switch exported backups only; refresh providers immediately after import
- **Prompts** - Allow saving prompts with empty content
- **MCP Sync** - Skip sync when the target CLI app is not installed
- **Common Config (Codex)** - Preserve MCP server `base_url` during extraction and remove provider-specific `model_providers` blocks
- **Proxy** - Improve takeover detection and stability; clean up model override env vars when switching providers in takeover mode (#508)
- **Skills** - Skip hidden directories during discovery; fix wrong skill repo branch
- **Settings Navigation** - Navigate to About tab when clicking update badge
- **UI** - Fix dialogs not opening on first click and improve window dragging area in `FullScreenPanel`
---
## [3.9.0-3] - 2025-12-29
### Beta Release
Third beta release with important bug fixes for Windows compatibility, UI improvements, and new features.
### Added
- **Universal Provider** - Support for universal provider configurations (#348)
- **Provider Search Filter** - Quick filter to find providers by name (#435)
- **Keyboard Shortcut** - Open settings with Command+comma / Ctrl+comma (#436)
- **Xiaomi MiMo Icon** - Added MiMo icon and Claude provider configuration (#470)
- **Usage Model Extraction** - Extract model info from usage statistics (#455)
- **Skip First-Run Confirmation** - Option to skip Claude Code first-run confirmation dialog
- **Exit Animations** - Added exit animation to FullScreenPanel dialogs
- **Fade Transitions** - Smooth fade transitions for app/view/panel switching
### Fixed
#### Windows
- Wrap npx/npm commands with `cmd /c` for MCP export
- Prevent terminal windows from appearing during version check
#### macOS
- Use .app bundle path for autostart to prevent terminal window popup
#### UI
- Resolve Dialog/Modal not opening on first click (#492)
- Improve dark mode text contrast for form labels
- Reduce header spacing and fix layout shift on view switch
- Prevent header layout shift when switching views
#### Database & Schema
- Add missing base columns migration for proxy_config
- Add backward compatibility check for proxy_config seed insert
#### Other
- Use local timezone and robust DST handling in usage stats (#500)
- Remove deprecated `sync_enabled_to_codex` call
- Gracefully handle invalid Codex config.toml during MCP sync
- Add missing translations for reasoning model and OpenRouter compat mode
### Improved
- **macOS Tray** - Use macOS tray template icon
- **Header Alignment** - Remove macOS titlebar tint, align custom header
- **Shadow Removal** - Cleaner UI by removing shadow styles
- **Code Inspector** - Added code-inspector-plugin for development
- **i18n** - Complete internationalization for usage panel and settings
- **Sponsor Logos** - Made sponsor logos clickable
### Stats
- 35 commits since v3.9.0-2
- 5 files changed in test/lint fixes
---
## [3.9.0-2] - 2025-12-20
### Beta Release
Second beta release focusing on proxy stability, import safety, and provider preset polish.
### Added
- **DMXAPI Partner** - Added DMXAPI as an official partner provider preset
- **Provider Icons** - Added provider icons for OpenRouter, LongCat, ModelScope, and AiHubMix
### Changed
- **Proxy (OpenRouter)** - Switched OpenRouter to passthrough mode for native Claude API
### Fixed
- **Import/Export** - Restrict SQL import to CC Switch exported backups only; refresh providers immediately after import
- **Proxy** - Respect existing Claude token when syncing; add fallback recovery for orphaned takeover state; remove global auto-start flag
- **Windows** - Add minimum window size to Windows platform config
- **UI** - Improve About section UI (#419) and unify header toolbar styling
### Stats
- 13 commits since v3.9.0-1
---
## [3.9.0-1] - 2025-12-18
### Beta Release
This beta release introduces the **Local API Proxy** feature, along with Skills multi-app support, UI improvements, and numerous bug fixes.
### Major Features
#### Local Proxy Server
- **Local HTTP Proxy** - High-performance proxy server built on Axum framework
- **Multi-app Support** - Unified proxy for Claude Code, Codex, and Gemini CLI API requests
- **Per-app Takeover** - Independent control over which apps route through the proxy
- **Live Config Takeover** - Automatically backs up and redirects CLI configurations to local proxy
#### Auto Failover
- **Circuit Breaker** - Automatically detects provider failures and triggers protection
- **Smart Failover** - Automatically switches to backup provider when current one is unavailable
- **Health Tracking** - Real-time monitoring of provider availability
- **Independent Failover Queues** - Each app maintains its own failover queue
#### Monitoring
- **Request Logging** - Detailed logging of all proxy requests
- **Usage Statistics** - Token consumption, latency, success rate metrics
- **Real-time Status** - Frontend displays proxy status and statistics
#### Skills Multi-App Support
- **Multi-app Support** - Skills now support both Claude and Codex (#365)
- **Multi-app Migration** - Existing Skills auto-migrate to multi-app structure (#378)
- **Installation Path Fix** - Use directory basename for skill installation path (#358)
### Added
- **Provider Icon Colors** - Customize provider icon colors (#385)
- **Deeplink Usage Config** - Import usage query config via deeplink (#400)
- **Error Request Logging** - Detailed logging for proxy requests (#401)
- **Closable Toast** - Added close button to switch notification toast (#350)
- **Icon Color Component** - ProviderIcon component supports color prop (#384)
### Fixed
#### Proxy Related
- Takeover Codex base_url via model_provider
- Harden crash recovery with fallback detection
- Sync UI when active provider differs from current setting
- Resolve circuit breaker race condition and error classification
- Stabilize live takeover and provider editing
- Reset health badges when proxy stops
- Retry failover for all HTTP errors including 4xx
- Fix HalfOpen counter underflow and config field inconsistencies
- Resolve circuit breaker state persistence and HalfOpen deadlock
- Auto-recover live config after abnormal exit
- Update live backup when hot-switching provider in proxy mode
- Wait for server shutdown before exiting app
- Disable auto-start on app launch by resetting enabled flag on stop
- Sync live config tokens to database before takeover
- Resolve 404 error and auto-setup proxy targets
#### MCP Related
- Skip sync when target CLI app is not installed
- Improve upsert and import robustness
- Use browser-compatible platform detection for MCP presets
#### UI Related
- Restore fade transition for Skills button
- Add close button to all success toasts
- Prevent card jitter when health badge appears
- Update SettingsPage tab styles (#342)
#### Other
- Fix Azure website link (#407)
- Add fallback to provider config for usage credentials (#360)
- Fix Windows black screen on startup (use system titlebar)
- Add fallback for crypto.randomUUID() on older WebViews
- Use correct npm package for Codex CLI version check
- Security fixes for JavaScript executor and usage script (#151)
### Improved
- **Proxy Active Theme** - Apply emerald theme when proxy takeover is active
- **Card Animation** - Improved provider card hover animation
- **Remove Restart Prompt** - No longer prompts restart when switching providers
### Technical
- Implement per-app takeover mode
- Proxy module contains 20+ Rust files with complete layered architecture
- Add 5 new database tables for proxy functionality
- Modularize handlers.rs to reduce code duplication
- Remove is_proxy_target in favor of failover_queue
### Stats
- 55 commits since v3.8.2
- 164 files changed
- +22,164 / -570 lines
---
## [3.8.0] - 2025-11-28
### Major Updates
- **Persistence architecture upgrade** - Moved from single JSON storage to SQLite + JSON dual-layer; added schema versioning, transactions, and SQL import/export; first launch auto-migrates `config.json` to SQLite while keeping originals safe.
- **Brand new UI** - Full layout redesign, unified component/ConfirmDialog styles, smoother animations, overscroll disabled; Tailwind CSS downgraded to v3.4 for compatibility.
- **Japanese language support** - UI now localized in Chinese/English/Japanese.
### Added
- **Skills recursive scanning** - Discovers nested `SKILL.md` files across multi-level directories; same-name skills allowed by full-path dedup.
- **Provider icons** - Presets ship with default icons; custom icon colors; icons retained when duplicating providers.
- **Auto launch on startup** - One-click enable/disable using Registry/LaunchAgent/XDG autostart.
- **Provider preset** - Added MiniMax partner preset.
- **Form validation** - Required fields get real-time validation and unified toast messaging.
### Fixed
- **Custom endpoints loss** - Switched provider updates to `UPDATE` to avoid cascade deletes from `INSERT OR REPLACE`.
- **Gemini config writing** - Correctly writes custom env vars to `.env` and keeps auth configs isolated.
- **Provider validation** - Handles missing current provider IDs and preserves icon fields on duplicate.
- **Linux rendering** - Fixed WebKitGTK DMA-BUF rendering and preserved user `.desktop` customizations.
- **Misc** - Removed redundant usage queries; corrected DMXAPI auth token field; restored missing deeplink translations; fixed usage script template init.
### Technical
- **Database modules** - Added `schema`, `backup`, `migration`, and DAO layers for providers/MCP/prompts/skills/settings.
- **Service modularization** - Split provider service into live/auth/endpoints/usage modules; deeplink parsing/import logic modularized.
- **Code cleanup** - Removed legacy JSON-era import/export, unused MCP types; unified error handling; tests migrated to SQLite backend and MSW handlers updated.
### Migration Notes
- First launch auto-migrates data from `config.json` to SQLite and device settings to `settings.json`; originals kept; error dialog on failure; dry-run supported.
### Stats
- 51 commits since v3.7.1; 207 files changed; +17,297 / -6,870 lines. See [release-note-v3.8.0](docs/release-notes/v3.8.0-en.md) for details.
---
## [3.7.1] - 2025-11-22
### Fixed
- **Skills third-party repository installation** (#268) - Fixed installation failure for skills repositories with custom subdirectories (e.g., `ComposioHQ/awesome-claude-skills`)
- **Gemini configuration persistence** - Resolved issue where settings.json edits were lost when switching providers
- **Dialog overlay click protection** - Prevented dialogs from closing when clicking outside, avoiding accidental form data loss (affects 11 dialog components)
### Added
- **Gemini configuration directory support** (#255) - Added custom configuration directory option for Gemini in settings
- **ArchLinux installation support** (#259) - Added AUR installation via `paru -S cc-switch-bin`
### Improved
- **Skills error messages i18n** - Added 28+ detailed error messages (English & Chinese) with specific resolution suggestions
- **Download timeout** - Extended from 15s to 60s to reduce network-related false positives
- **Code formatting** - Applied unified Rust (`cargo fmt`) and TypeScript (`prettier`) formatting standards
### Reverted
- **Auto-launch on system startup** - Temporarily reverted feature pending further testing and optimization
---
## [3.7.0] - 2025-11-19
### Major Features
#### Gemini CLI Integration
- **Complete Gemini CLI support** - Third major application added alongside Claude Code and Codex
- **Dual-file configuration** - Support for both `.env` and `settings.json` file formats
- **Environment variable detection** - Auto-detect `GOOGLE_GEMINI_BASE_URL`, `GEMINI_MODEL`, etc.
- **MCP management** - Full MCP configuration capabilities for Gemini
- **Provider presets**
- Google Official (OAuth authentication)
- PackyCode (partner integration)
- Custom endpoint support
- **Deep link support** - Import Gemini providers via `ccswitch://` protocol
- **System tray integration** - Quick-switch Gemini providers from tray menu
- **Backend modules** - New `gemini_config.rs` (20KB) and `gemini_mcp.rs`
#### MCP v3.7.0 Unified Architecture
- **Unified management panel** - Single interface for Claude/Codex/Gemini MCP servers
- **SSE transport type** - New Server-Sent Events support alongside stdio/http
- **Smart JSON parser** - Fault-tolerant parsing of various MCP config formats
- **Extended field support** - Preserve custom fields in Codex TOML conversion
- **Codex format correction** - Proper `[mcp_servers]` format (auto-cleanup of incorrect `[mcp.servers]`)
- **Import/export system** - Unified import from Claude/Codex/Gemini live configs
- **UX improvements**
- Default app selection in forms
- JSON formatter for config validation
- Improved layout and visual hierarchy
- Better validation error messages
#### Claude Skills Management System
- **GitHub repository integration** - Auto-scan and discover skills from GitHub repos
- **Pre-configured repositories**
- `ComposioHQ/awesome-claude-skills` (curated collection)
- `anthropics/skills` (official Anthropic skills)
- `cexll/myclaude` (community, with subdirectory scanning)
- **Lifecycle management**
- One-click install to `~/.claude/skills/`
- Safe uninstall with state tracking
- Update checking (infrastructure ready)
- **Custom repository support** - Add any GitHub repo as a skill source
- **Subdirectory scanning** - Optional `skillsPath` for repos with nested skill directories
- **Backend architecture** - `SkillService` (526 lines) with GitHub API integration
- **Frontend interface**
- SkillsPage: Browse and manage skills
- SkillCard: Visual skill presentation
- RepoManager: Repository management dialog
- **State persistence** - Installation state stored in `skills.json`
- **Full i18n support** - Complete Chinese/English translations (47+ keys)
#### Prompts (System Prompts) Management
- **Multi-preset management** - Create, edit, and switch between multiple system prompts
- **Cross-app support**
- Claude: `~/.claude/CLAUDE.md`
- Codex: `~/.codex/AGENTS.md`
- Gemini: `~/.gemini/GEMINI.md`
- **Markdown editor** - Full-featured CodeMirror 6 editor with syntax highlighting
- **Smart synchronization**
- Auto-write to live files on enable
- Content backfill protection (save current before switching)
- First-launch auto-import from live files
- **Single-active enforcement** - Only one prompt can be active at a time
- **Delete protection** - Cannot delete active prompts
- **Backend service** - `PromptService` (213 lines) with CRUD operations
- **Frontend components**
- PromptPanel: Main management interface (177 lines)
- PromptFormModal: Edit dialog with validation (160 lines)
- MarkdownEditor: CodeMirror integration (159 lines)
- usePromptActions: Business logic hook (152 lines)
- **Full i18n support** - Complete Chinese/English translations (41+ keys)
#### Deep Link Protocol (ccswitch://)
- **Protocol registration** - `ccswitch://` URL scheme for one-click imports
- **Provider import** - Import provider configurations from URLs or shared links
- **Lifecycle integration** - Deep link handling integrated into app startup
- **Cross-platform support** - Works on Windows, macOS, and Linux
#### Environment Variable Conflict Detection
- **Claude & Codex detection** - Identify conflicting environment variables
- **Gemini auto-detection** - Automatic environment variable discovery
- **Conflict management** - UI for resolving configuration conflicts
- **Prevention system** - Warn before overwriting existing configurations
### New Features
#### Provider Management
- **DouBaoSeed preset** - Added ByteDance's DouBao provider
- **Kimi For Coding** - Moonshot AI coding assistant
- **BaiLing preset** - BaiLing AI integration
- **Removed AnyRouter preset** - Discontinued provider
- **Model configuration** - Support for custom model names in Codex and Gemini
- **Provider notes field** - Add custom notes to providers for better organization
#### Configuration Management
- **Common config migration** - Moved Claude common config snippets from localStorage to `config.json`
- **Unified persistence** - Common config snippets now shared across all apps
- **Auto-import on first launch** - Automatically import configs from live files on first run
- **Backfill priority fix** - Correct priority handling when enabling prompts
#### UI/UX Improvements
- **macOS native design** - Migrated color scheme to macOS native design system
- **Window centering** - Default window position centered on screen
- **Password input fixes** - Disabled Edge/IE reveal and clear buttons
- **URL overflow prevention** - Fixed overflow in provider cards
- **Error notification enhancement** - Copy-to-clipboard for error messages
- **Tray menu sync** - Real-time sync after drag-and-drop sorting
### Improvements
#### Architecture
- **MCP v3.7.0 cleanup** - Removed legacy code and warnings
- **Unified structure** - Default initialization with v3.7.0 unified structure
- **Backward compatibility** - Compilation fixes for older configs
- **Code formatting** - Applied consistent formatting across backend and frontend
#### Platform Compatibility
- **Windows fix** - Resolved winreg API compatibility issue (v0.52)
- **Safe pattern matching** - Replaced `unwrap()` with safe patterns in tray menu
#### Configuration
- **MCP sync on switch** - Sync MCP configs for all apps when switching providers
- **Gemini form sync** - Fixed form fields syncing with environment editor
- **Gemini config reading** - Read from both `.env` and `settings.json`
- **Validation improvements** - Enhanced input validation and boundary checks
#### Internationalization
- **JSON syntax fixes** - Resolved syntax errors in locale files
- **App name i18n** - Added internationalization support for app names
- **Deduplicated labels** - Reused providerForm keys to reduce duplication
- **Gemini MCP title** - Added missing Gemini MCP panel title
### Bug Fixes
#### Critical Fixes
- **Usage script validation** - Added input validation and boundary checks
- **Gemini validation** - Relaxed validation when adding providers
- **TOML quote normalization** - Handle CJK quotes to prevent parsing errors
- **MCP field preservation** - Preserve custom fields in Codex TOML editor
- **Password input** - Fixed white screen crash (FormLabel → Label)
#### Stability
- **Tray menu safety** - Replaced unwrap with safe pattern matching
- **Error isolation** - Tray menu update failures don't block main operations
- **Import classification** - Set category to custom for imported default configs
#### UI Fixes
- **Model placeholders** - Removed misleading model input placeholders
- **Base URL population** - Auto-fill base URL for non-official providers
- **Drag sort sync** - Fixed tray menu order after drag-and-drop
### Technical Improvements
#### Code Quality
- **Type safety** - Complete TypeScript type coverage across codebase
- **Test improvements** - Simplified boolean assertions in tests
- **Clippy warnings** - Fixed `uninlined_format_args` warnings
- **Code refactoring** - Extracted templates, optimized logic flows
#### Dependencies
- **Tauri** - Updated to 2.8.x series
- **Rust dependencies** - Added `anyhow`, `zip`, `serde_yaml`, `tempfile` for Skills
- **Frontend dependencies** - Added CodeMirror 6 packages for Markdown editor
- **winreg** - Updated to v0.52 (Windows compatibility)
#### Performance
- **Startup optimization** - Removed legacy migration scanning
- **Lock management** - Improved RwLock usage to prevent deadlocks
- **Background query** - Enabled background mode for usage polling
### Statistics
- **Total commits**: 85 commits from v3.6.0 to v3.7.0
- **Code changes**: 152 files changed, 18,104 insertions(+), 3,732 deletions(-)
- **New modules**:
- Skills: 2,034 lines (21 files)
- Prompts: 1,302 lines (20 files)
- Gemini: ~1,000 lines (multiple files)
- MCP refactor: ~3,000 lines (refactored)
### Strategic Positioning
v3.7.0 represents a major evolution from "Provider Switcher" to **"All-in-One AI CLI Management Platform"**:
1. **Capability Extension** - Skills provide external ability integration
2. **Behavior Customization** - Prompts enable AI personality presets
3. **Configuration Unification** - MCP v3.7.0 eliminates app silos
4. **Ecosystem Openness** - Deep links enable community sharing
5. **Multi-AI Support** - Claude/Codex/Gemini trinity
6. **Intelligent Detection** - Auto-discovery of environment conflicts
### Notes
- Users upgrading from v3.1.0 or earlier should first upgrade to v3.2.x for one-time migration
- Skills and Prompts management are new features requiring no migration
- Gemini CLI support requires Gemini CLI to be installed separately
- MCP v3.7.0 unified structure is backward compatible with previous configs
## [3.6.0] - 2025-11-07
### ✨ New Features
- **Provider Duplicate** - Quick duplicate existing provider configurations for easy variant creation
- **Edit Mode Toggle** - Show/hide drag handles to optimize editing experience
- **Custom Endpoint Management** - Support multi-endpoint configuration for aggregator providers
- **Usage Query Enhancements**
- Auto-refresh interval: Support periodic automatic usage query
- Test Script API: Validate JavaScript scripts before execution
- Template system expansion: Custom blank template, support for access token and user ID parameters
- **Configuration Editor Improvements**
- Add JSON format button
- Real-time TOML syntax validation for Codex configuration
- **Auto-sync on Directory Change** - When switching Claude/Codex config directories (e.g., WSL environment), automatically sync current provider to new directory without manual operation
- **Load Live Config When Editing Active Provider** - When editing the currently active provider, prioritize displaying the actual effective configuration to protect user manual modifications
- **New Provider Presets** - DMXAPI, Azure Codex, AnyRouter, AiHubMix, MiniMax
- **Partner Promotion Mechanism** - Support ecosystem partner promotion (e.g., Zhipu GLM Z.ai)
### 🔧 Improvements
- **Configuration Directory Switching**
- Introduced unified post-change sync utility (`postChangeSync.ts`)
- Auto-sync current providers to new directory when changing Claude/Codex config directories
- Perfect support for WSL environment switching
- Auto-sync after config import to ensure immediate effectiveness
- Use Result pattern for graceful error handling without blocking main flow
- Distinguish "fully successful" and "partially successful" states for precise user feedback
- **UI/UX Enhancements**
- Provider cards: Unique icons and color identification
- Unified border design system across all components
- Drag interaction optimization: Push effect animation, improved handle icons
- Enhanced current provider visual feedback
- Dialog size standardization and layout consistency
- Form experience: Optimized model placeholders, simplified provider hints, category-specific hints
- **Complete Internationalization Coverage**
- Error messages internationalization
- Tray menu internationalization
- All UI components internationalization
- **Usage Display Moved Inline** - Usage display moved next to enable button
### 🐛 Bug Fixes
- **Configuration Sync**
- Fixed `apiKeyUrl` priority issue
- Fixed MCP sync-to-other-side functionality failure
- Fixed sync issues after config import
- Prevent silent fallback and data loss on config error
- **Usage Query**
- Fixed auto-query interval timing issue
- Ensure refresh button shows loading animation on click
- **UI Issues**
- Fixed name collision error (`get_init_error` command)
- Fixed language setting rollback after successful save
- Fixed language switch state reset (dependency cycle)
- Fixed edit mode button alignment
- **Configuration Management**
- Fixed Codex API Key auto-sync
- Fixed endpoint speed test functionality
- Fixed provider duplicate insertion position (next to original provider)
- Fixed custom endpoint preservation in edit mode
- **Startup Issues**
- Force exit on config error (no silent fallback)
- Eliminate code duplication causing initialization errors
### 🏗️ Technical Improvements (For Developers)
**Backend Refactoring (Rust)** - Completed 5-phase refactoring:
- **Phase 1**: Unified error handling (`AppError` + i18n error messages)
- **Phase 2**: Command layer split by domain (`commands/{provider,mcp,config,settings,plugin,misc}.rs`)
- **Phase 3**: Integration tests and transaction mechanism (config snapshot + failure rollback)
- **Phase 4**: Extracted Service layer (`services/{provider,mcp,config,speedtest}.rs`)
- **Phase 5**: Concurrency optimization (`RwLock` instead of `Mutex`, scoped guard to avoid deadlock)
**Frontend Refactoring (React + TypeScript)** - Completed 4-stage refactoring:
- **Stage 1**: Test infrastructure (vitest + MSW + @testing-library/react)
- **Stage 2**: Extracted custom hooks (`useProviderActions`, `useMcpActions`, `useSettings`, `useImportExport`, etc.)
- **Stage 3**: Component splitting and business logic extraction
- **Stage 4**: Code cleanup and formatting unification
**Testing System**:
- Hooks unit tests 100% coverage
- Integration tests covering key processes (App, SettingsDialog, MCP Panel)
- MSW mocking backend API to ensure test independence
**Code Quality**:
- Unified parameter format: All Tauri commands migrated to camelCase (Tauri 2 specification)
- `AppType` renamed to `AppId`: Semantically clearer
- Unified parsing with `FromStr` trait: Centralized `app` parameter parsing
- Eliminate code duplication: DRY violations cleanup
- Remove unused code: `missing_param` helper function, deprecated `tauri-api.ts`, redundant `KimiModelSelector` component
**Internal Optimizations**:
- **Removed Legacy Migration Logic**: v3.6 removed v1 config auto-migration and copy file scanning logic
- ✅ **Impact**: Improved startup performance, cleaner code
- ✅ **Compatibility**: v2 format configs fully compatible, no action required
- ⚠️ **Note**: Users upgrading from v3.1.0 or earlier should first upgrade to v3.2.x or v3.5.x for one-time migration, then upgrade to v3.6
- **Command Parameter Standardization**: Backend unified to use `app` parameter (values: `claude` or `codex`)
- ✅ **Impact**: More standardized code, friendlier error prompts
- ✅ **Compatibility**: Frontend fully adapted, users don't need to care about this change
### 📦 Dependencies
- Updated to Tauri 2.8.x
- Updated to TailwindCSS 4.x
- Updated to TanStack Query v5.90.x
- Maintained React 18.2.x and TypeScript 5.3.x
## [3.5.0] - 2025-01-15
### ⚠ Breaking Changes
- Tauri commands only accept the `app` parameter (`claude`/`codex`); removed `app_type`/`appType` compatibility.
- Frontend types are standardized to `AppId` (removed `AppType` export); variable naming is standardized to `appId`.
### ✨ New Features
- **MCP (Model Context Protocol) Management** - Complete MCP server configuration management system
- Add, edit, delete, and toggle MCP servers in `~/.claude.json`
- Support for stdio and http server types with command validation
- Built-in templates for popular MCP servers (mcp-fetch, etc.)
- Real-time enable/disable toggle for MCP servers
- Atomic file writing to prevent configuration corruption
- **Configuration Import/Export** - Backup and restore your provider configurations
- Export all configurations to JSON file with one click
- Import configurations with validation and automatic backup
- Automatic backup rotation (keeps 10 most recent backups)
- Progress modal with detailed status feedback
- **Endpoint Speed Testing** - Test API endpoint response times
- Measure latency to different provider endpoints
- Visual indicators for connection quality
- Help users choose the fastest provider
### 🔧 Improvements
- Complete internationalization (i18n) coverage for all UI components
- Enhanced error handling and user feedback throughout the application
- Improved configuration file management with better validation
- Added new provider presets: Longcat, kat-coder
- Updated GLM provider configurations with latest models
- Refined UI/UX with better spacing, icons, and visual feedback
- Enhanced tray menu functionality and responsiveness
- **Standardized release artifact naming** - All platform releases now use consistent version-tagged filenames:
- macOS: `CC-Switch-v{version}-macOS.tar.gz` / `.zip`
- Windows: `CC-Switch-v{version}-Windows.msi` / `-Portable.zip`
- Linux: `CC-Switch-v{version}-Linux.AppImage` / `.deb`
### 🐛 Bug Fixes
- Fixed layout shifts during provider switching
- Improved config file path handling across different platforms
- Better error messages for configuration validation failures
- Fixed various edge cases in configuration import/export
### 📦 Technical Details
- Enhanced `import_export.rs` module with backup management
- New `claude_mcp.rs` module for MCP configuration handling
- Improved state management and lock handling in Rust backend
- Better TypeScript type safety across the codebase
## [3.4.0] - 2025-10-01
### ✨ Features
- Enable internationalization via i18next with a Chinese default and English fallback, plus an in-app language switcher
- Add Claude plugin sync while retiring the legacy VS Code integration controls (Codex no longer requires settings.json edits)
- Extend provider presets with optional API key URLs and updated models, including DeepSeek-V3.1-Terminus and Qwen3-Max
- Support portable mode launches and enforce a single running instance to avoid conflicts
### 🔧 Improvements
- Allow minimizing the window to the system tray and add macOS Dock visibility management for tray workflows
- Refresh the Settings modal with a scrollable layout, save icon, and cleaner language section
- Smooth provider toggle states with consistent button widths/icons and prevent layout shifts when switching between Claude and Codex
- Adjust the Windows MSI installer to target per-user LocalAppData and improve component tracking reliability
### 🐛 Fixes
- Remove the unnecessary OpenAI auth requirement from third-party provider configurations
- Fix layout shifts while switching app types with Claude plugin sync enabled
- Align Enable/In Use button states to avoid visual jank across app views
## [3.3.0] - 2025-09-22
### ✨ Features
- Add “Apply to VS Code / Remove from VS Code” actions on provider cards, writing settings for Code/Insiders/VSCodium variants _(Removed in 3.4.x)_
- Enable VS Code auto-sync by default with window broadcast and tray hooks so Codex switches sync silently _(Removed in 3.4.x)_
- Extend the Codex provider wizard with display name, dedicated API key URL, and clearer guidance
- Introduce shared common config snippets with JSON/TOML reuse, validation, and consistent error surfaces
### 🔧 Improvements
- Keep the tray menu responsive when the window is hidden and standardize button styling and copy
- Disable modal backdrop blur on Linux (WebKitGTK/Wayland) to avoid freezes; restore the window when clicking the macOS Dock icon
- Support overriding config directories on WSL, refine placeholders/descriptions, and fix VS Code button wrapping on Windows
- Add a `created_at` timestamp to provider records for future sorting and analytics
### 🐛 Fixes
- Correct regex escapes and common snippet trimming in the Codex wizard to prevent validation issues
- Harden the VS Code sync flow with more reliable TOML/JSON parsing while reducing layout jank
- Bundle `@codemirror/lint` to reinstate live linting in config editors
## [3.2.0] - 2025-09-13
### ✨ New Features
- System tray provider switching with dynamic menu for Claude/Codex
- Frontend receives `provider-switched` events and refreshes active app
- Built-in update flow via Tauri Updater plugin with dismissible UpdateBadge
### 🔧 Improvements
- Single source of truth for provider configs; no duplicate copy files
- One-time migration imports existing copies into `config.json` and archives originals
- Duplicate provider de-duplication by name + API key at startup
- Atomic writes for Codex `auth.json` + `config.toml` with rollback on failure
- Logging standardized (Rust): use `log::{info,warn,error}` instead of stdout prints
- Tailwind v4 integration and refined dark mode handling
### 🐛 Fixes
- Remove/minimize debug console logs in production builds
- Fix CSS minifier warnings for scrollbar pseudo-elements
- Prettier formatting across codebase for consistent style
### 📦 Dependencies
- Tauri: 2.8.x (core, updater, process, opener, log plugins)
- React: 18.2.x · TypeScript: 5.3.x · Vite: 5.x
### 🔄 Notes
- `connect-src` CSP remains permissive for compatibility; can be tightened later as needed
## [3.1.1] - 2025-09-03
### 🐛 Bug Fixes
- Fixed the default codex config.toml to match the latest modifications
- Improved provider configuration UX with custom option
### 📝 Documentation
- Updated README with latest information
## [3.1.0] - 2025-09-01
### ✨ New Features
- **Added Codex application support** - Now supports both Claude Code and Codex configuration management
- Manage auth.json and config.toml for Codex
- Support for backup and restore operations
- Preset providers for Codex (Official, PackyCode)
- API Key auto-write to auth.json when using presets
- **New UI components**
- App switcher with segmented control design
- Dual editor form for Codex configuration
- Pills-style app switcher with consistent button widths
- **Enhanced configuration management**
- Multi-app config v2 structure (claude/codex)
- Automatic v1→v2 migration with backup
- OPENAI_API_KEY validation for non-official presets
- TOML syntax validation for config.toml
### 🔧 Technical Improvements
- Unified Tauri command API with app_type parameter
- Backward compatibility for app/appType parameters
- Added get_config_status/open_config_folder/open_external commands
- Improved error handling for empty config.toml
### 🐛 Bug Fixes
- Fixed config path reporting and folder opening for Codex
- Corrected default import behavior when main config is missing
- Fixed non_snake_case warnings in commands.rs
## [3.0.0] - 2025-08-27
### 🚀 Major Changes
- **Complete migration from Electron to Tauri 2.0** - The application has been completely rewritten using Tauri, resulting in:
- **90% reduction in bundle size** (from ~150MB to ~15MB)
- **Significantly improved startup performance**
- **Native system integration** without Chromium overhead
- **Enhanced security** with Rust backend
### ✨ New Features
- **Native window controls** with transparent title bar on macOS
- **Improved file system operations** using Rust for better performance
- **Enhanced security model** with explicit permission declarations
- **Better platform detection** using Tauri's native APIs
### 🔧 Technical Improvements
- Migrated from Electron IPC to Tauri command system
- Replaced Node.js file operations with Rust implementations
- Implemented proper CSP (Content Security Policy) for enhanced security
- Added TypeScript strict mode for better type safety
- Integrated Rust cargo fmt and clippy for code quality
### 🐛 Bug Fixes
- Fixed bundle identifier conflict on macOS (changed from .app to .desktop)
- Resolved platform detection issues
- Improved error handling in configuration management
### 📦 Dependencies
- **Tauri**: 2.8.2
- **React**: 18.2.0
- **TypeScript**: 5.3.0
- **Vite**: 5.0.0
### 🔄 Migration Notes
For users upgrading from v2.x (Electron version):
- Configuration files remain compatible - no action required
- The app will automatically migrate your existing provider configurations
- Window position and size preferences have been reset to defaults
#### Backup on v1→v2 Migration (cc-switch internal config)
- When the app detects an old v1 config structure at `~/.cc-switch/config.json`, it now creates a timestamped backup before writing the new v2 structure.
- Backup location: `~/.cc-switch/config.v1.backup.<timestamp>.json`
- This only concerns cc-switch's own metadata file; your actual provider files under `~/.claude/` and `~/.codex/` are untouched.
### 🛠️ Development
- Added `pnpm typecheck` command for TypeScript validation
- Added `pnpm format` and `pnpm format:check` for code formatting
- Rust code now uses cargo fmt for consistent formatting
## [2.0.0] - Previous Electron Release
### Features
- Multi-provider configuration management
- Quick provider switching
- Import/export configurations
- Preset provider templates
---
## [1.0.0] - Initial Release
### Features
- Basic provider management
- Claude Code integration
- Configuration file handling
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2025 Jason Young
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<div align="center">
# CC Switch
### The All-in-One Manager for Claude Code, Codex, Gemini CLI, OpenCode & OpenClaw
[](https://github.com/farion1231/cc-switch/releases)
[](https://github.com/farion1231/cc-switch/releases)
[](https://tauri.app/)
[](https://github.com/farion1231/cc-switch/releases/latest)
<a href="https://trendshift.io/repositories/15372" target="_blank"><img src="https://trendshift.io/api/badge/repositories/15372" alt="farion1231%2Fcc-switch | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
English | [中文](README_ZH.md) | [日本語](README_JA.md) | [Changelog](CHANGELOG.md)
</div>
## ❤️Sponsor
<details open>
<summary>Click to collapse</summary>
[](https://platform.minimax.io/subscribe/coding-plan?code=ClLhgxr2je&source=link)
MiniMax-M2.5 is a SOTA large language model designed for real-world productivity. Trained in a diverse range of complex real-world digital working environments, M2.5 builds upon the coding expertise of M2.1 to extend into general office work, reaching fluency in generating and operating Word, Excel, and Powerpoint files, context switching between diverse software environments, and working across different agent and human teams. Scoring 80.2% on SWE-Bench Verified, 51.3% on Multi-SWE-Bench, and 76.3% on BrowseComp, M2.5 is also more token efficient than previous generations, having been trained to optimize its actions and output through planning.
[Click](https://platform.minimax.io/subscribe/coding-plan?code=ClLhgxr2je&source=link) to get an exclusive 12% off the MiniMax Coding Plan!
---
<table>
<tr>
<td width="180"><a href="https://www.packyapi.com/register?aff=cc-switch"><img src="assets/partners/logos/packycode.png" alt="PackyCode" width="150"></a></td>
<td>Thanks to PackyCode for sponsoring this project! PackyCode is a reliable and efficient API relay service provider, offering relay services for Claude Code, Codex, Gemini, and more. PackyCode provides special discounts for our software users: register using <a href="https://www.packyapi.com/register?aff=cc-switch">this link</a> and enter the "cc-switch" promo code during first recharge to get 10% off.</td>
</tr>
<tr>
<td width="180"><a href="https://cloud.siliconflow.cn/i/drGuwc9k"><img src="assets/partners/logos/silicon_en.jpg" alt="SiliconFlow" width="150"></a></td>
<td>Thanks to SiliconFlow for sponsoring this project! SiliconFlow is a high-performance AI infrastructure and model API platform, providing fast and reliable access to language, speech, image, and video models in one place. With pay-as-you-go billing, broad multimodal model support, high-speed inference, and enterprise-grade stability, SiliconFlow helps developers and teams build and scale AI applications more efficiently. Register via <a href="https://cloud.siliconflow.cn/i/drGuwc9k">this link</a> and complete real-name verification to receive ¥20 in bonus credit, usable across models on the platform. SiliconFlow is also now compatible with OpenClaw, allowing users to connect a SiliconFlow API key and call major AI models for free.</td>
</tr>
<tr>
<td width="180"><a href="https://aigocode.com/invite/CC-SWITCH"><img src="assets/partners/logos/aigocode.png" alt="AIGoCode" width="150"></a></td>
<td>Thanks to AIGoCode for sponsoring this project! AIGoCode is an all-in-one platform that integrates Claude Code, Codex, and the latest Gemini models, providing you with stable, efficient, and highly cost-effective AI coding services. The platform offers flexible subscription plans, zero risk of account suspension, direct access with no VPN required, and lightning-fast responses. AIGoCode has prepared a special benefit for CC Switch users: if you register via <a href="https://aigocode.com/invite/CC-SWITCH">this link</a>, you'll receive an extra 10% bonus credit on your first top-up!</td>
</tr>
<tr>
<td width="180"><a href="https://www.aicodemirror.com/register?invitecode=9915W3"><img src="assets/partners/logos/aicodemirror.jpg" alt="AICodeMirror" width="150"></a></td>
<td>Thanks to AICodeMirror for sponsoring this project! AICodeMirror provides official high-stability relay services for Claude Code / Codex / Gemini CLI, with enterprise-grade concurrency, fast invoicing, and 24/7 dedicated technical support.
Claude Code / Codex / Gemini official channels at 38% / 2% / 9% of original price, with extra discounts on top-ups! AICodeMirror offers special benefits for CC Switch users: register via <a href="https://www.aicodemirror.com/register?invitecode=9915W3">this link</a> to enjoy 20% off your first top-up, and enterprise customers can get up to 25% off!</td>
</tr>
<tr>
<td width="180"><a href="https://cubence.com/signup?code=CCSWITCH&source=ccs"><img src="assets/partners/logos/cubence.png" alt="Cubence" width="150"></a></td>
<td>Thanks to Cubence for sponsoring this project! Cubence is a reliable and efficient API relay service provider, offering relay services for Claude Code, Codex, Gemini, and more with flexible billing options including pay-as-you-go and monthly plans. Cubence provides special discounts for CC Switch users: register using <a href="https://cubence.com/signup?code=CCSWITCH&source=ccs">this link</a> and enter the "CCSWITCH" promo code during recharge to get 10% off every top-up!</td>
</tr>
<tr>
<td width="180"><a href="https://www.dmxapi.cn/register?aff=bUHu"><img src="assets/partners/logos/dmx-en.jpg" alt="DMXAPI" width="150"></a></td>
<td>Thanks to DMXAPI for sponsoring this project! DMXAPI provides global large model API services to 200+ enterprise users. One API key for all global models. Features include: instant invoicing, unlimited concurrency, starting from $0.15, 24/7 technical support. GPT/Claude/Gemini all at 32% off, domestic models 20-50% off, Claude Code exclusive models at 66% off! <a href="https://www.dmxapi.cn/register?aff=bUHu">Register here</a></td>
</tr>
<tr>
<td width="180"><a href="https://www.compshare.cn/coding-plan?ytag=GPU_YY_YX_git_cc-switch"><img src="assets/partners/logos/ucloud.png" alt="Compshare" width="150"></a></td>
<td>Thanks to Compshare for sponsoring this project! Compshare is UCloud's AI cloud platform, providing stable and comprehensive domestic and international model APIs with just one key. Featuring cost-effective monthly and pay-as-you-go Coding Plan packages at 60-80% off official prices. Supports Claude Code, Codex, and API access. Enterprise-grade high concurrency, 24/7 technical support, and self-service invoicing. Users who register via <a href="https://www.compshare.cn/coding-plan?ytag=GPU_YY_YX_git_cc-switch">this link</a> will receive a free 5 CNY platform trial credit!</td>
</tr>
<tr>
<td width="180"><a href="https://www.right.codes/register?aff=CCSWITCH"><img src="assets/partners/logos/rightcode.jpg" alt="RightCode" width="150"></a></td>
<td>Thank you to Right Code for sponsoring this project! Right Code reliably provides routing services for models such as Claude Code, Codex, and Gemini. It features a highly cost-effective Codex monthly subscription plan and <strong>supports quota rollovers—unused quota from one day can be carried over and used the next day.</strong> Invoices are available upon top-up. Enterprise and team users can receive dedicated one-on-one support. Right Code also offers an exclusive discount for CC Switch users: register via <a href="https://www.right.codes/register?aff=CCSWITCH">this link</a>, and with every top-up you will receive pay-as-you-go credit equivalent to 25% of the amount paid.</td>
</tr>
<tr>
<td width="180"><a href="https://aicoding.sh/i/CCSWITCH"><img src="assets/partners/logos/aicoding.jpg" alt="AICoding" width="150"></a></td>
<td>Thanks to AICoding.sh for sponsoring this project! AICoding.sh — Global AI Model API Relay Service at Unbeatable Prices! Claude Code at 19% of original price, GPT at just 1%! Trusted by hundreds of enterprises for cost-effective AI services. Supports Claude Code, GPT, Gemini and major domestic models, with enterprise-grade high concurrency, fast invoicing, and 24/7 dedicated technical support. CC Switch users who register via <a href="https://aicoding.sh/i/CCSWITCH">this link</a> get 10% off their first top-up!</td>
</tr>
<tr>
<td width="180"><a href="https://crazyrouter.com/register?aff=OZcm&ref=cc-switch"><img src="assets/partners/logos/crazyrouter.jpg" alt="Crazyrouter" width="150"></a></td>
<td>Thanks to Crazyrouter for sponsoring this project! Crazyrouter is a high-performance AI API aggregation platform — one API key for 300+ models including Claude Code, Codex, Gemini CLI, and more. All models at 55% of official pricing with auto-failover, smart routing, and unlimited concurrency. Crazyrouter offers an exclusive deal for CC Switch users: register via <a href="https://crazyrouter.com/register?aff=OZcm&ref=cc-switch">this link</a> to get <strong>$2 free credit</strong> instantly, plus enter promo code `CCSWITCH` on your first top-up for an extra <strong>30% bonus credit</strong>! </td>
</tr>
<tr>
<td width="180"><a href="https://www.sssaicode.com/register?ref=DCP0SM"><img src="assets/partners/logos/sssaicode.png" alt="SSSAiCode" width="150"></a></td>
<td>Thanks to SSSAiCode for sponsoring this project! SSSAiCode is a stable and reliable API relay service, dedicated to providing stable, reliable, and affordable Claude and Codex model services, <strong>offering high cost-effective official Claude service at just ¥0.5/$ equivalent</strong>, supporting monthly and pay-as-you-go billing plans with same-day fast invoicing. SSSAiCode offers a special deal for CC Switch users: register via <a href="https://www.sssaicode.com/register?ref=DCP0SM">this link</a> to enjoy $10 extra credit on every top-up!</td>
</tr>
<tr>
<td width="180"><a href="https://www.openclaudecode.cn/register?aff=aOYQ"><img src="assets/partners/logos/mikubanner.svg" alt="Micu" width="150"></a></td>
<td>Thanks to Micu API for sponsoring this project! Micu API is a global LLM relay service provider dedicated to delivering the best cost-performance ratio with high stability. Backed by a registered enterprise for core assurance, eliminating any risk of service discontinuation, with fast official invoicing support! We champion "zero cost to try": top up from as low as ¥1 with no minimum, and get fee-free refunds anytime! Micu API offers an exclusive deal for CC Switch users: register via <a href="https://www.openclaudecode.cn/register?aff=aOYQ">this link</a> and enter promo code "ccswitch" when topping up to enjoy a <strong>10% discount</strong>!</td>
</tr>
<tr>
<td width="180"><a href="https://x-code.cc/register?aff=IbPp"><img src="assets/partners/logos/xcodeapi.png" alt="XCodeAPI" width="150"></a></td>
<td>Thanks to XCodeAPI for sponsoring this project! XCodeAPI offers a special benefit for CC Switch users: register via <a href="https://x-code.cc/register?aff=IbPp">this link</a> and get an extra 10% credit bonus on your first order! (Contact the site admin to claim)</td>
</tr>
<tr>
<td width="180"><a href="https://ctok.ai"><img src="assets/partners/logos/ctok.png" alt="CTok" width="150"></a></td>
<td>Thanks to CTok.ai for sponsoring this project! CTok.ai is dedicated to building a one-stop AI programming tool service platform. We offer professional Claude Code packages and technical community services, with support for Google Gemini and OpenAI Codex. Through carefully designed plans and a professional tech community, we provide developers with reliable service guarantees and continuous technical support, making AI-assisted programming a true productivity tool. Click <a href="https://ctok.ai">here</a> to register!</td>
</tr>
</table>
</details>
## Why CC Switch?
Modern AI-powered coding relies on CLI tools like Claude Code, Codex, Gemini CLI, OpenCode, and OpenClaw — but each has its own configuration format. Switching API providers means manually editing JSON, TOML, or `.env` files, and there is no unified way to manage MCP and Skills across multiple tools.
**CC Switch** gives you a single desktop app to manage all five CLI tools. Instead of editing config files by hand, you get a visual interface to import providers with one click, switch between them instantly, with 50+ built-in provider presets, unified MCP and Skills management, and system tray quick switching — all backed by a reliable SQLite database with atomic writes that protect your configs from corruption.
- **One App, Five CLI Tools** — Manage Claude Code, Codex, Gemini CLI, OpenCode, and OpenClaw from a single interface
- **No More Manual Editing** — 50+ provider presets including AWS Bedrock, NVIDIA NIM, and community relays; just pick and switch
- **Unified MCP & Skills Management** — One panel to manage MCP servers and Skills across four apps with bidirectional sync
- **System Tray Quick Switch** — Switch providers instantly from the tray menu, no need to open the full app
- **Cloud Sync** — Sync provider data across devices via Dropbox, OneDrive, iCloud, or WebDAV servers
- **Cross-Platform** — Native desktop app for Windows, macOS, and Linux, built with Tauri 2
- **Built-in Utilities** — Includes various utilities for first-launch login confirmation, signature bypass, plugin extension sync, and more
## Screenshots
| Main Interface | Add Provider |
| :-----------------------------------------------: | :--------------------------------------------: |
|  |  |
## Features
[Full Changelog](CHANGELOG.md) | [Release Notes](docs/release-notes/v3.12.3-en.md)
### Provider Management
- **5 CLI tools, 50+ presets** — Claude Code, Codex, Gemini CLI, OpenCode, OpenClaw; copy your key and import with one click
- **Universal providers** — One config syncs to multiple apps (OpenCode, OpenClaw)
- One-click switching, system tray quick access, drag-and-drop sorting, import/export
### Proxy & Failover
- **Local proxy with hot-switching** — Format conversion, auto-failover, circuit breaker, provider health monitoring, and request rectifier
- **App-level takeover** — Independently proxy Claude, Codex, or Gemini, down to individual providers
### MCP, Prompts & Skills
- **Unified MCP panel** — Manage MCP servers across 4 apps with bidirectional sync and Deep Link import
- **Prompts** — Markdown editor with cross-app sync (CLAUDE.md / AGENTS.md / GEMINI.md) and backfill protection
- **Skills** — One-click install from GitHub repos or ZIP files, custom repository management, with symlink and file copy support
### Usage & Cost Tracking
- **Usage dashboard** — Track spending, requests, and tokens with trend charts, detailed request logs, and custom per-model pricing
### Session Manager & Workspace
- Browse, search, and restore conversation history across all apps
- **Workspace editor** (OpenClaw) — Edit agent files (AGENTS.md, SOUL.md, etc.) with Markdown preview
### System & Platform
- **Cloud sync** — Custom config directory (Dropbox, OneDrive, iCloud, NAS) and WebDAV server sync
- **Deep Link** (`ccswitch://`) — Import providers, MCP servers, prompts, and skills via URL
- Dark / Light / System theme, auto-launch, auto-updater, atomic writes, auto-backups, i18n (zh/en/ja)
## FAQ
<details>
<summary><strong>Which AI CLI tools does CC Switch support?</strong></summary>
CC Switch supports five tools: **Claude Code**, **Codex**, **Gemini CLI**, **OpenCode**, and **OpenClaw**. Each tool has dedicated provider presets and configuration management.
</details>
<details>
<summary><strong>Do I need to restart the terminal after switching providers?</strong></summary>
For most tools, yes — restart your terminal or the CLI tool for changes to take effect. The exception is **Claude Code**, which currently supports hot-switching of provider data without a restart.
</details>
<details>
<summary><strong>My plugin configuration disappeared after switching providers — what happened?</strong></summary>
CC Switch provides a "Shared Config Snippet" feature to pass common data (beyond API keys and endpoints) between providers. Go to "Edit Provider" → "Shared Config Panel" → click "Extract from Current Provider" to save all common data. When creating a new provider, check "Write Shared Config" (enabled by default) to include plugin data in the new provider. All your configuration items are preserved in the default provider imported when you first launched the app.
</details>
<details>
<summary><strong>macOS shows "unidentified developer" warning — how do I fix it?</strong></summary>
The author doesn't have an Apple Developer account yet (registration in progress). Close the warning, then go to **System Settings → Privacy & Security → Open Anyway**. After that, the app will open normally.
</details>
<details>
<summary><strong>Why can't I delete the currently active provider?</strong></summary>
CC Switch follows a "minimal intrusion" design principle — even if you uninstall the app, your CLI tools will continue to work normally. The system always keeps one active configuration, because deleting all configurations would make the corresponding CLI tool unusable. If you rarely use a specific CLI tool, you can hide it in Settings. To switch back to official login, see the next question.
</details>
<details>
<summary><strong>How do I switch back to official login?</strong></summary>
Add an official provider from the preset list. After switching to it, run the Log out / Log in flow, and then you can freely switch between the official provider and third-party providers. Codex supports switching between different official providers, making it easy to switch between multiple Plus or Team accounts.
</details>
<details>
<summary><strong>Where is my data stored?</strong></summary>
- **Database**: `~/.cc-switch/cc-switch.db` (SQLite — providers, MCP, prompts, skills)
- **Local settings**: `~/.cc-switch/settings.json` (device-level UI preferences)
- **Backups**: `~/.cc-switch/backups/` (auto-rotated, keeps 10 most recent)
- **Skills**: `~/.cc-switch/skills/` (symlinked to corresponding apps by default)
- **Skill Backups**: `~/.cc-switch/skill-backups/` (created automatically before uninstall, keeps 20 most recent)
</details>
## Documentation
For detailed guides on every feature, check out the **[User Manual](docs/user-manual/en/README.md)** — covering provider management, MCP/Prompts/Skills, proxy & failover, and more.
## Quick Start
### Basic Usage
1. **Add Provider**: Click "Add Provider" → Choose a preset or create custom configuration
2. **Switch Provider**:
- Main UI: Select provider → Click "Enable"
- System Tray: Click provider name directly (instant effect)
3. **Takes Effect**: Restart your terminal or the corresponding CLI tool to apply changes (Claude Code does not require a restart)
4. **Back to Official**: Add an "Official Login" preset, restart the CLI tool, then follow its login/OAuth flow
### MCP, Prompts, Skills & Sessions
- **MCP**: Click the "MCP" button → Add servers via templates or custom config → Toggle per-app sync
- **Prompts**: Click "Prompts" → Create presets with Markdown editor → Activate to sync to live files
- **Skills**: Click "Skills" → Browse GitHub repos → One-click install to all apps
- **Sessions**: Click "Sessions" → Browse, search, and restore conversation history across all apps
> **Note**: On first launch, you can manually import existing CLI tool configs as the default provider.
## Download & Installation
### System Requirements
- **Windows**: Windows 10 and above
- **macOS**: macOS 12 (Monterey) and above
- **Linux**: Ubuntu 22.04+ / Debian 11+ / Fedora 34+ and other mainstream distributions
### Windows Users
Download the latest `CC-Switch-v{version}-Windows.msi` installer or `CC-Switch-v{version}-Windows-Portable.zip` portable version from the [Releases](../../releases) page.
### macOS Users
**Method 1: Install via Homebrew (Recommended)**
```bash
brew tap farion1231/ccswitch
brew install --cask cc-switch
```
Update:
```bash
brew upgrade --cask cc-switch
```
**Method 2: Manual Download**
Download `CC-Switch-v{version}-macOS.zip` from the [Releases](../../releases) page and extract to use.
> **Note**: Since the author doesn't have an Apple Developer account, you may see an "unidentified developer" warning on first launch. Please close it first, then go to "System Settings" → "Privacy & Security" → click "Open Anyway", and you'll be able to open it normally afterwards.
### Arch Linux Users
**Install via paru (Recommended)**
```bash
paru -S cc-switch-bin
```
### Linux Users
Download the latest Linux build from the [Releases](../../releases) page:
- `CC-Switch-v{version}-Linux.deb` (Debian/Ubuntu)
- `CC-Switch-v{version}-Linux.rpm` (Fedora/RHEL/openSUSE)
- `CC-Switch-v{version}-Linux.AppImage` (Universal)
- `CC-Switch-v{version}-Linux.flatpak` (Flatpak)
Flatpak install & run:
```bash
flatpak install --user ./CC-Switch-v{version}-Linux.flatpak
flatpak run com.ccswitch.desktop
```
<details>
<summary><strong>Architecture Overview</strong></summary>
### Design Principles
```
┌─────────────────────────────────────────────────────────────┐
│ Frontend (React + TS) │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Components │ │ Hooks │ │ TanStack Query │ │
│ │ (UI) │──│ (Bus. Logic) │──│ (Cache/Sync) │ │
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
└────────────────────────┬────────────────────────────────────┘
│ Tauri IPC
┌────────────────────────▼────────────────────────────────────┐
│ Backend (Tauri + Rust) │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Commands │ │ Services │ │ Models/Config │ │
│ │ (API Layer) │──│ (Bus. Layer) │──│ (Data) │ │
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
**Core Design Patterns**
- **SSOT** (Single Source of Truth): All data stored in `~/.cc-switch/cc-switch.db` (SQLite)
- **Dual-layer Storage**: SQLite for syncable data, JSON for device-level settings
- **Dual-way Sync**: Write to live files on switch, backfill from live when editing active provider
- **Atomic Writes**: Temp file + rename pattern prevents config corruption
- **Concurrency Safe**: Mutex-protected database connection avoids race conditions
- **Layered Architecture**: Clear separation (Commands → Services → DAO → Database)
**Key Components**
- **ProviderService**: Provider CRUD, switching, backfill, sorting
- **McpService**: MCP server management, import/export, live file sync
- **ProxyService**: Local proxy mode with hot-switching and format conversion
- **SessionManager**: Claude Code conversation history browsing
- **ConfigService**: Config import/export, backup rotation
- **SpeedtestService**: API endpoint latency measurement
</details>
<details>
<summary><strong>Development Guide</strong></summary>
### Environment Requirements
- Node.js 18+
- pnpm 8+
- Rust 1.85+
- Tauri CLI 2.8+
### Development Commands
```bash
# Install dependencies
pnpm install
# Dev mode (hot reload)
pnpm dev
# Type check
pnpm typecheck
# Format code
pnpm format
# Check code format
pnpm format:check
# Run frontend unit tests
pnpm test:unit
# Run tests in watch mode (recommended for development)
pnpm test:unit:watch
# Build application
pnpm build
# Build debug version
pnpm tauri build --debug
```
### Rust Backend Development
```bash
cd src-tauri
# Format Rust code
cargo fmt
# Run clippy checks
cargo clippy
# Run backend tests
cargo test
# Run specific tests
cargo test test_name
# Run tests with test-hooks feature
cargo test --features test-hooks
```
### Testing Guide
**Frontend Testing**:
- Uses **vitest** as test framework
- Uses **MSW (Mock Service Worker)** to mock Tauri API calls
- Uses **@testing-library/react** for component testing
**Running Tests**:
```bash
# Run all tests
pnpm test:unit
# Watch mode (auto re-run)
pnpm test:unit:watch
# With coverage report
pnpm test:unit --coverage
```
### Tech Stack
**Frontend**: React 18 · TypeScript · Vite · TailwindCSS 3.4 · TanStack Query v5 · react-i18next · react-hook-form · zod · shadcn/ui · @dnd-kit
**Backend**: Tauri 2.8 · Rust · serde · tokio · thiserror · tauri-plugin-updater/process/dialog/store/log
**Testing**: vitest · MSW · @testing-library/react
</details>
<details>
<summary><strong>Project Structure</strong></summary>
```
├── src/ # Frontend (React + TypeScript)
│ ├── components/
│ │ ├── providers/ # Provider management
│ │ ├── mcp/ # MCP panel
│ │ ├── prompts/ # Prompts management
│ │ ├── skills/ # Skills management
│ │ ├── sessions/ # Session Manager
│ │ ├── proxy/ # Proxy mode panel
│ │ ├── openclaw/ # OpenClaw config panels
│ │ ├── settings/ # Settings (Terminal/Backup/About)
│ │ ├── deeplink/ # Deep Link import
│ │ ├── env/ # Environment variable management
│ │ ├── universal/ # Cross-app configuration
│ │ ├── usage/ # Usage statistics
│ │ └── ui/ # shadcn/ui component library
│ ├── hooks/ # Custom hooks (business logic)
│ ├── lib/
│ │ ├── api/ # Tauri API wrapper (type-safe)
│ │ └── query/ # TanStack Query config
│ ├── locales/ # Translations (zh/en/ja)
│ ├── config/ # Presets (providers/mcp)
│ └── types/ # TypeScript definitions
├── src-tauri/ # Backend (Rust)
│ └── src/
│ ├── commands/ # Tauri command layer (by domain)
│ ├── services/ # Business logic layer
│ ├── database/ # SQLite DAO layer
│ ├── proxy/ # Proxy module
│ ├── session_manager/ # Session management
│ ├── deeplink/ # Deep Link handling
│ └── mcp/ # MCP sync module
├── tests/ # Frontend tests
└── assets/ # Screenshots & partner resources
```
</details>
## Contributing
Issues and suggestions are welcome!
Before submitting PRs, please ensure:
- Pass type check: `pnpm typecheck`
- Pass format check: `pnpm format:check`
- Pass unit tests: `pnpm test:unit`
For new features, please open an issue for discussion before submitting a PR. PRs for features that are not a good fit for the project may be closed.
## Star History
[](https://www.star-history.com/#farion1231/cc-switch&Date)
## License
MIT © Jason Young
================================================
FILE: README_JA.md
================================================
<div align="center">
# CC Switch
### Claude Code、Codex、Gemini CLI、OpenCode、OpenClaw のオールインワン管理ツール
[](https://github.com/farion1231/cc-switch/releases)
[](https://github.com/farion1231/cc-switch/releases)
[](https://tauri.app/)
[](https://github.com/farion1231/cc-switch/releases/latest)
<a href="https://trendshift.io/repositories/15372" target="_blank"><img src="https://trendshift.io/api/badge/repositories/15372" alt="farion1231%2Fcc-switch | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
[English](README.md) | [中文](README_ZH.md) | 日本語 | [Changelog](CHANGELOG.md)
</div>
## ❤️スポンサー
<details open>
<summary>クリックで折りたたむ</summary>
[](https://platform.minimax.io/subscribe/coding-plan?code=ClLhgxr2je&source=link)
MiniMax-M2.5 は、実際の生産性向上のために設計された最先端の大規模言語モデルです。多様で複雑な実環境のデジタルワークスペースでトレーニングされた M2.5 は、M2.1 のコーディング能力をベースに一般的なオフィス業務へと拡張し、Word・Excel・PowerPoint ファイルの生成と操作、多様なソフトウェア環境間のコンテキスト切り替え、異なるエージェントや人間チーム間での協働を流暢にこなします。SWE-Bench Verified で 80.2%、Multi-SWE-Bench で 51.3%、BrowseComp で 76.3% を達成し、計画的な行動と出力の最適化トレーニングにより、前世代よりもトークン効率に優れています。
[こちら](https://platform.minimax.io/subscribe/coding-plan?code=ClLhgxr2je&source=link)から MiniMax Coding Plan の限定 12% オフを入手!
---
<table>
<tr>
<td width="180"><a href="https://www.packyapi.com/register?aff=cc-switch"><img src="assets/partners/logos/packycode.png" alt="PackyCode" width="150"></a></td>
<td>PackyCode のご支援に感謝します!PackyCode は Claude Code、Codex、Gemini などのリレーサービスを提供する信頼性の高い API 中継プラットフォームです。本ソフト利用者向けに特別割引があります:<a href="https://www.packyapi.com/register?aff=cc-switch">このリンク</a>で登録し、チャージ時に「cc-switch」クーポンを入力すると 10% オフになります。</td>
</tr>
<tr>
<td width="180"><a href="https://cloud.siliconflow.cn/i/drGuwc9k"><img src="assets/partners/logos/silicon_en.jpg" alt="SiliconFlow" width="150"></a></td>
<td>SiliconFlow のご支援に感謝します!SiliconFlow は高性能 AI インフラストラクチャおよびモデル API プラットフォームで、言語・音声・画像・動画モデルへの高速かつ信頼性の高いアクセスをワンストップで提供します。従量課金制、豊富なマルチモーダルモデル対応、高速推論、エンタープライズグレードの安定性を備え、開発者やチームがより効率的に AI アプリケーションを構築・拡張できるようサポートします。<a href="https://cloud.siliconflow.cn/i/drGuwc9k">このリンク</a>から登録し、本人確認を完了すると、プラットフォーム内の全モデルで利用可能な ¥20 のボーナスクレジットが付与されます。SiliconFlow は OpenClaw にも対応しており、SiliconFlow の API キーを接続することで主要な AI モデルを無料で呼び出すことができます。</td>
</tr>
<tr>
<td width="180"><a href="https://aigocode.com/invite/CC-SWITCH"><img src="assets/partners/logos/aigocode.png" alt="AIGoCode" width="150"></a></td>
<td>本プロジェクトは AIGoCode のスポンサー提供でお届けしています。AIGoCode は、Claude Code・Codex・最新の Gemini モデルを統合したオールインワンのAIコーディングプラットフォームで、安定性・高速性・コストパフォーマンスに優れた開発サービスを提供します。柔軟なサブスクリプションプランを備え、レスポンスも非常に高速です。さらに、CC Switch ユーザー向けの特典として、<a href="https://aigocode.com/invite/CC-SWITCH">このリンク</a>から登録すると、初回チャージ時に10%分のボーナスクレジットが付与されます!</td>
</tr>
<tr>
<td width="180"><a href="https://www.aicodemirror.com/register?invitecode=9915W3"><img src="assets/partners/logos/aicodemirror.jpg" alt="AICodeMirror" width="150"></a></td>
<td>AICodeMirror のご支援に感謝します!AICodeMirror は Claude Code / Codex / Gemini CLI の公式高安定リレーサービスを提供しており、エンタープライズ級の同時接続、迅速な請求書発行、24時間年中無休の専用テクニカルサポートを備えています。
Claude Code / Codex / Gemini 公式チャンネルが最安で元価格の 38% / 2% / 9%、チャージ時にはさらに割引!AICodeMirror は CC Switch ユーザー向けに特別特典を用意:<a href="https://www.aicodemirror.com/register?invitecode=9915W3">このリンク</a>から登録すると初回チャージ 20% オフ、法人のお客様は最大 25% オフ!</td>
</tr>
<tr>
<td width="180"><a href="https://cubence.com/signup?code=CCSWITCH&source=ccs"><img src="assets/partners/logos/cubence.png" alt="Cubence" width="150"></a></td>
<td>Cubence のご支援に感謝します!Cubence は Claude Code、Codex、Gemini などのリレーサービスを提供する信頼性の高い API 中継プラットフォームで、従量課金や月額プランなど柔軟な料金体系を提供しています。CC Switch ユーザー向けの特別割引:<a href="https://cubence.com/signup?code=CCSWITCH&source=ccs">このリンク</a>で登録し、チャージ時に「CCSWITCH」クーポンを入力すると、毎回 10% オフになります!</td>
</tr>
<tr>
<td width="180"><a href="https://www.dmxapi.cn/register?aff=bUHu"><img src="assets/partners/logos/dmx-en.jpg" alt="DMXAPI" width="150"></a></td>
<td>DMXAPI のご支援に感謝します!DMXAPI は 200 社以上の企業ユーザーにグローバル大規模モデル API サービスを提供しています。1 つの API キーで全世界のモデルにアクセス可能。即時請求書発行、同時接続数無制限、最低 $0.15 から、24 時間年中無休のテクニカルサポート。GPT/Claude/Gemini が全て 32% オフ、国内モデルは 20〜50% オフ、Claude Code 専用モデルは 66% オフ実施中!<a href="https://www.dmxapi.cn/register?aff=bUHu">登録はこちら</a></td>
</tr>
<tr>
<td width="180"><a href="https://www.compshare.cn/coding-plan?ytag=GPU_YY_YX_git_cc-switch"><img src="assets/partners/logos/ucloud.png" alt="Compshare" width="150"></a></td>
<td>Compshare のご支援に感謝します!Compshare は UCloud 傘下の AI クラウドプラットフォームで、国内外の安定した包括的なモデル API を 1 つのキーだけで利用可能。月額・従量課金のコストパフォーマンスに優れた Coding Plan パッケージを提供し、公式価格の 60〜80% オフで利用できます。Claude Code、Codex および API アクセスに対応。エンタープライズ級の高同時接続、24 時間年中無休のテクニカルサポート、セルフサービス請求書発行に対応。<a href="https://www.compshare.cn/coding-plan?ytag=GPU_YY_YX_git_cc-switch">こちらのリンク</a>から登録すると、無料で 5 元分のプラットフォーム体験クレジットがもらえます!</td>
</tr>
<tr>
<td width="180"><a href="https://www.right.codes/register?aff=CCSWITCH"><img src="assets/partners/logos/rightcode.jpg" alt="RightCode" width="150"></a></td>
<td>本プロジェクトへのご支援として、Right Code にご協賛いただき誠にありがとうございます。Right Code は、Claude Code、Codex、Gemini などのモデルに対応した中継(プロキシ)サービスを安定して提供しています。特に高いコストパフォーマンスを誇る Codex の月額プランを主力としており、<strong>未使用分の利用枠を翌日に繰り越して利用できる(繰越対応)</strong>点が特長です。チャージ(入金)後に請求書の発行が可能で、企業・チーム向けには専任担当による個別対応も行っています。さらに CC Switch ユーザー向けの特別優待として、<a href="https://www.right.codes/register?aff=CCSWITCH">こちらのリンク</a>からご登録いただくと、チャージのたびに実支払額の 25% 相当の従量課金クレジットが付与されます。</td>
</tr>
<tr>
<td width="180"><a href="https://aicoding.sh/i/CCSWITCH"><img src="assets/partners/logos/aicoding.jpg" alt="AICoding" width="150"></a></td>
<td>AICoding.sh のご支援に感謝します!AICoding.sh —— グローバル AI モデル API 超お得な中継サービス!Claude Code 81% オフ、GPT 99% オフ!数百社の企業に高コストパフォーマンスの AI サービスを提供。Claude Code、GPT、Gemini および国内主要モデルに対応、エンタープライズ級の高同時接続、迅速な請求書発行、24 時間年中無休の専属テクニカルサポート。<a href="https://aicoding.sh/i/CCSWITCH">こちらのリンク</a>から登録した CC Switch ユーザーは、初回チャージ 10% オフ!</td>
</tr>
<tr>
<td width="180"><a href="https://crazyrouter.com/register?aff=OZcm&ref=cc-switch"><img src="assets/partners/logos/crazyrouter.jpg" alt="Crazyrouter" width="150"></a></td>
<td>Crazyrouter のご支援に感謝します!Crazyrouter は高性能 AI API アグリゲーションプラットフォームです。1 つの API キーで Claude Code、Codex、Gemini CLI など 300 以上のモデルにアクセス可能。全モデルが公式価格の 55% で利用でき、自動フェイルオーバー、スマートルーティング、無制限同時接続に対応。CC Switch ユーザー向けの限定特典:<a href="https://crazyrouter.com/register?aff=OZcm&ref=cc-switch">こちらのリンク</a>から登録すると <strong>$2 の無料クレジット</strong> を即時進呈。さらに初回チャージ時にプロモコード `CCSWITCH` を入力すると <strong>30% のボーナスクレジット</strong> が追加されます!</td>
</tr>
<tr>
<td width="180"><a href="https://www.sssaicode.com/register?ref=DCP0SM"><img src="assets/partners/logos/sssaicode.png" alt="SSSAiCode" width="150"></a></td>
<td>SSSAiCode のご支援に感謝します!SSSAiCode は安定性と信頼性に優れた API 中継サービスで、安定的で信頼性が高く、手頃な価格の Claude・Codex モデルサービスを提供しています。<strong>高コストパフォーマンスの公式 Claude サービスを 0.5¥/$ 換算で提供</strong>、月額制・Paygo など多様な課金方式に対応し、当日の迅速な請求書発行をサポート。CC Switch ユーザー向けの特別特典:<a href="https://www.sssaicode.com/register?ref=DCP0SM">こちらのリンク</a>から登録すると、毎回のチャージで $10 の追加ボーナスを受けられます!</td>
</tr>
<tr>
<td width="180"><a href="https://www.openclaudecode.cn/register?aff=aOYQ"><img src="assets/partners/logos/mikubanner.svg" alt="Micu" width="150"></a></td>
<td>Micu API のご支援に感謝します!Micu API は、最高のコストパフォーマンスと高い安定性を追求するグローバル大規模言語モデル中継サービスプロバイダーです。法人企業がバックアップしており、サービス停止のリスクを排除、迅速な正規請求書発行に対応!「試行コストゼロ」をモットーに、最低 1 元からチャージ可能で手数料無料、いつでも返金可能!CC Switch ユーザー向けの限定特典:<a href="https://www.openclaudecode.cn/register?aff=aOYQ">こちらのリンク</a>から登録し、チャージ時にプロモコード「ccswitch」を入力すると <strong>10% 割引</strong> が適用されます!</td>
</tr>
<tr>
<td width="180"><a href="https://x-code.cc/register?aff=IbPp"><img src="assets/partners/logos/xcodeapi.png" alt="XCodeAPI" width="150"></a></td>
<td>XCodeAPI のご支援に感謝します!CC Switch ユーザー向けの特別特典:<a href="https://x-code.cc/register?aff=IbPp">こちらのリンク</a>から登録すると、初回注文で 10% の追加クレジットボーナスがもらえます!(サイト管理者に連絡して受け取りください)</td>
</tr>
<tr>
<td width="180"><a href="https://ctok.ai"><img src="assets/partners/logos/ctok.png" alt="CTok" width="150"></a></td>
<td>CTok.ai のご支援に感謝します!CTok.ai はワンストップ AI プログラミングツールサービスプラットフォームの構築に取り組んでいます。Claude Code のプロフェッショナルプランと技術コミュニティサービスを提供し、Google Gemini や OpenAI Codex にも対応しています。丁寧に設計されたプランと専門的な技術コミュニティを通じて、開発者に安定したサービス保証と継続的な技術サポートを提供し、AI アシストプログラミングを真の生産性ツールにします。<a href="https://ctok.ai">こちら</a>から登録してください!</td>
</tr>
</table>
</details>
## CC Switch を選ぶ理由
最新の AI コーディングは Claude Code、Codex、Gemini CLI、OpenCode、OpenClaw などの CLI ツールに依存していますが、各ツールの設定形式はバラバラです。API プロバイダを切り替えるたびに JSON、TOML、`.env` ファイルを手動で編集する必要があり、複数ツール間で MCP や Skills を統一的に管理する手段もありません。
**CC Switch** は、5 つの CLI ツールを 1 つのデスクトップアプリで一元管理できます。設定ファイルを手作業で編集する代わりに、ワンクリックでプロバイダをインポートし、瞬時に切り替えられるビジュアルインターフェースを提供します。50 以上の組み込みプリセット、統一 MCP・Skills 管理、システムトレイからの即時切り替え機能を搭載。すべてはアトミック書き込みによる信頼性の高い SQLite データベースに支えられており、設定の破損を防ぎます。
- **1 つのアプリで 5 つの CLI ツール** -- Claude Code、Codex、Gemini CLI、OpenCode、OpenClaw を単一インターフェースで管理
- **手動編集は不要** -- AWS Bedrock、NVIDIA NIM、コミュニティリレーなど 50 以上のプロバイダプリセットを内蔵。選んで切り替えるだけ
- **統一 MCP・Skills 管理** -- 1 つのパネルで 4 つのアプリの MCP サーバーと Skills を双方向同期で管理
- **システムトレイでクイック切り替え** -- トレイメニューから即座にプロバイダを切り替え。アプリを開く必要なし
- **クラウド同期** -- Dropbox、OneDrive、iCloud、または WebDAV サーバー経由でデバイス間のプロバイダデータを同期
- **クロスプラットフォーム** -- Tauri 2 で構築された Windows、macOS、Linux 対応のネイティブデスクトップアプリ
- **便利ツール内蔵** -- 初回起動時のログイン確認、署名バイパス、プラグイン拡張の同期など、さまざまなユーティリティを搭載
## スクリーンショット
| メイン画面 | プロバイダ追加 |
| :-------------------------------------------: | :----------------------------------------------: |
|  |  |
## 特長
[完全な更新履歴](CHANGELOG.md) | [リリースノート](docs/release-notes/v3.12.3-ja.md)
### プロバイダ管理
- **5 つの CLI ツール、50 以上のプリセット** -- Claude Code、Codex、Gemini CLI、OpenCode、OpenClaw。キーをコピーしてワンクリックでインポート
- **ユニバーサルプロバイダ** -- 1 つの設定を複数アプリに同期(OpenCode、OpenClaw)
- ワンクリック切り替え、システムトレイクイックアクセス、ドラッグ&ドロップ並び替え、インポート/エクスポート
### プロキシ & フェイルオーバー
- **ローカルプロキシのホットスイッチ** -- フォーマット変換、自動フェイルオーバー、サーキットブレーカー、プロバイダヘルスモニタリング、リクエストレクティファイア
- **アプリレベルのテイクオーバー** -- Claude、Codex、Gemini を個別にプロキシ経由でルーティング、プロバイダ単位で設定可能
### MCP、Prompts & Skills
- **統一 MCP パネル** -- 4 つのアプリの MCP サーバーを管理、双方向同期、Deep Link インポート対応
- **Prompts** -- Markdown エディタ、クロスアプリ同期(CLAUDE.md / AGENTS.md / GEMINI.md)、バックフィル保護
- **Skills** -- GitHub リポジトリまたは ZIP ファイルからワンクリックインストール、カスタムリポジトリ管理、シンボリックリンクとファイルコピーに対応
### 使用量 & コストトラッキング
- **使用量ダッシュボード** -- プロバイダ横断で支出・リクエスト数・トークン使用量を追跡、トレンドチャート、詳細リクエストログ、カスタムモデル価格設定
### Session Manager & ワークスペース
- すべてのアプリの会話履歴を閲覧・検索・復元
- **ワークスペースエディタ**(OpenClaw)-- エージェントファイル(AGENTS.md、SOUL.md など)を Markdown プレビュー付きで編集
### システム & プラットフォーム
- **クラウド同期** -- カスタム設定ディレクトリ(Dropbox、OneDrive、iCloud、NAS)および WebDAV サーバー同期
- **Deep Link** (`ccswitch://`) -- URL 経由でプロバイダ、MCP サーバー、Prompts、Skills をワンクリックインポート
- ダーク / ライト / システムテーマ、自動起動、自動アップデーター、アトミック書き込み、自動バックアップ、多言語対応(中/英/日)
## よくある質問
<details>
<summary><strong>CC Switch はどの AI CLI ツールに対応していますか?</strong></summary>
CC Switch は **Claude Code**、**Codex**、**Gemini CLI**、**OpenCode**、**OpenClaw** の 5 つのツールに対応しています。各ツールに専用のプロバイダプリセットと設定管理が用意されています。
</details>
<details>
<summary><strong>プロバイダを切り替えた後、ターミナルの再起動は必要ですか?</strong></summary>
ほとんどのツールでは、はい。変更を反映するにはターミナルまたは CLI ツールを再起動してください。ただし **Claude Code** は例外で、現在プロバイダデータのホットスイッチに対応しており、再起動は不要です。
</details>
<details>
<summary><strong>プロバイダを切り替えた後、プラグイン設定が消えてしまいました。どうすればよいですか?</strong></summary>
CC Switch には「共有設定スニペット」機能があり、APIキーやエンドポイント以外の共通データをプロバイダ間で引き継ぐことができます。「プロバイダ編集」→「共有設定パネル」→「現在のプロバイダから抽出」をクリックして、すべての共通データを保存してください。新しいプロバイダを作成する際に「共有設定を書き込む」にチェック(デフォルトで有効)を入れれば、プラグインなどのデータが新しいプロバイダ設定に含まれます。すべての設定項目は、アプリ初回起動時にインポートされたデフォルトプロバイダに保存されており、失われることはありません。
</details>
<details>
<summary><strong>macOS で「開発元を確認できません」と表示されます。どうすればよいですか?</strong></summary>
開発者が Apple Developer アカウントをまだ取得していないためです(登録手続き中)。警告を閉じてから、**システム設定 → プライバシーとセキュリティ → このまま開く**をクリックしてください。以降は通常通り起動できます。
</details>
<details>
<summary><strong>現在アクティブなプロバイダを削除できないのはなぜですか?</strong></summary>
CC Switch は「最小限の介入」という設計原則に従っています。アプリをアンインストールしても、CLI ツールは正常に動作し続けます。すべての設定を削除すると対応する CLI ツールが使用できなくなるため、システムは常にアクティブな設定を 1 つ保持します。特定の CLI ツールをあまり使用しない場合は、設定で非表示にできます。公式ログインに戻す方法は、次の質問をご覧ください。
</details>
<details>
<summary><strong>公式ログインに戻すにはどうすればよいですか?</strong></summary>
プリセットリストから公式プロバイダを追加してください。切り替え後、ログアウト/ログインのフローを実行すれば、以降は公式プロバイダとサードパーティプロバイダを自由に切り替えられます。Codex では異なる公式プロバイダ間の切り替えに対応しており、複数の Plus アカウントや Team アカウントの切り替えに便利です。
</details>
<details>
<summary><strong>データはどこに保存されますか?</strong></summary>
- **データベース**: `~/.cc-switch/cc-switch.db`(SQLite -- プロバイダ、MCP、Prompts、Skills)
- **ローカル設定**: `~/.cc-switch/settings.json`(デバイスレベルの UI 設定)
- **バックアップ**: `~/.cc-switch/backups/`(自動ローテーション、最新 10 件を保持)
- **Skills**: `~/.cc-switch/skills/`(デフォルトでシンボリックリンクにより対応アプリに接続)
- **Skill バックアップ**: `~/.cc-switch/skill-backups/`(アンインストール前に自動作成、最新 20 件を保持)
</details>
## ドキュメント
各機能の詳しい使い方については、**[ユーザーマニュアル](docs/user-manual/ja/README.md)** をご覧ください。プロバイダ管理、MCP/Prompts/Skills、プロキシとフェイルオーバーなど、すべての機能を網羅しています。
## クイックスタート
### 基本的な使い方
1. **プロバイダ追加**: 「Add Provider」をクリック → プリセットを選ぶかカスタム設定を作成
2. **プロバイダ切り替え**:
- メイン UI: プロバイダを選択 → 「Enable」をクリック
- システムトレイ: プロバイダ名をクリック(即時反映)
3. **反映**: ターミナルまたは対応する CLI ツールを再起動して適用(Claude Code は再起動不要)
4. **公式設定に戻す**: 「Official Login」プリセットを追加し、CLI ツールを再起動してログイン/OAuth フローを実行
### MCP、Prompts、Skills & Sessions
- **MCP**: 「MCP」ボタンをクリック → テンプレートまたはカスタム設定でサーバーを追加 → アプリごとの同期をトグルで切り替え
- **Prompts**: 「Prompts」をクリック → Markdown エディタでプリセットを作成 → 有効化してライブファイルに同期
- **Skills**: 「Skills」をクリック → GitHub リポジトリを閲覧 → ワンクリックですべてのアプリにインストール
- **Sessions**: 「Sessions」をクリック → すべてのアプリの会話履歴を閲覧・検索・復元
> **補足**: 初回起動時に、既存の CLI ツール設定を手動でインポートしてデフォルトプロバイダとして使用できます。
## ダウンロード & インストール
### システム要件
- **Windows**: Windows 10 以上
- **macOS**: macOS 12 (Monterey) 以上
- **Linux**: Ubuntu 22.04+ / Debian 11+ / Fedora 34+ など主要ディストリビューション
### Windows ユーザー
[Releases](../../releases) ページから最新版の `CC-Switch-v{version}-Windows.msi` インストーラー、またはポータブル版 `CC-Switch-v{version}-Windows-Portable.zip` をダウンロード。
### macOS ユーザー
**方法 1: Homebrew でインストール(推奨)**
```bash
brew tap farion1231/ccswitch
brew install --cask cc-switch
```
アップデート:
```bash
brew upgrade --cask cc-switch
```
**方法 2: 手動ダウンロード**
[Releases](../../releases) から `CC-Switch-v{version}-macOS.zip` をダウンロードして展開。
> **注意**: 開発者アカウント未登録のため、初回起動時に「開発元を確認できません」と表示される場合があります。一度閉じてから「システム設定」→「プライバシーとセキュリティ」→「このまま開く」をクリックしてください。以降は通常通り起動できます。
### Arch Linux ユーザー
**paru でインストール(推奨)**
```bash
paru -S cc-switch-bin
```
### Linux ユーザー
[Releases](../../releases) から最新版の Linux ビルドをダウンロード:
- `CC-Switch-v{version}-Linux.deb`(Debian/Ubuntu)
- `CC-Switch-v{version}-Linux.rpm`(Fedora/RHEL/openSUSE)
- `CC-Switch-v{version}-Linux.AppImage`(汎用)
- `CC-Switch-v{version}-Linux.flatpak`(Flatpak)
Flatpak のインストールと起動:
```bash
flatpak install --user ./CC-Switch-v{version}-Linux.flatpak
flatpak run com.ccswitch.desktop
```
<details>
<summary><strong>アーキテクチャ概要</strong></summary>
### 設計原則
```
┌─────────────────────────────────────────────────────────────┐
│ Frontend (React + TS) │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Components │ │ Hooks │ │ TanStack Query │ │
│ │ (UI) │──│ (Bus. Logic) │──│ (Cache/Sync) │ │
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
└────────────────────────┬────────────────────────────────────┘
│ Tauri IPC
┌────────────────────────▼────────────────────────────────────┐
│ Backend (Tauri + Rust) │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Commands │ │ Services │ │ Models/Config │ │
│ │ (API Layer) │──│ (Bus. Layer) │──│ (Data) │ │
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
**コア設計パターン**
- **SSOT** (Single Source of Truth): すべてのデータを `~/.cc-switch/cc-switch.db`(SQLite)に集約
- **二層ストレージ**: 同期データは SQLite、デバイスデータは JSON
- **双方向同期**: 切り替え時はライブファイルへ書き込み、編集時はアクティブプロバイダから逆同期
- **アトミック書き込み**: 一時ファイル + rename パターンで設定破損を防止
- **並行安全**: Mutex で保護された DB 接続でレースコンディションを防止
- **レイヤードアーキテクチャ**: Commands → Services → DAO → Database を明確に分離
**主要コンポーネント**
- **ProviderService**: プロバイダの CRUD、切り替え、バックフィル、ソート
- **McpService**: MCP サーバー管理、インポート/エクスポート、ライブファイル同期
- **ProxyService**: ローカル Proxy モードのホットスイッチとフォーマット変換
- **SessionManager**: Claude Code の会話履歴閲覧
- **ConfigService**: 設定のインポート/エクスポート、バックアップローテーション
- **SpeedtestService**: API エンドポイントの遅延計測
</details>
<details>
<summary><strong>開発ガイド</strong></summary>
### 開発環境
- Node.js 18+
- pnpm 8+
- Rust 1.85+
- Tauri CLI 2.8+
### 開発コマンド
```bash
# 依存関係をインストール
pnpm install
# ホットリロード付き開発モード
pnpm dev
# 型チェック
pnpm typecheck
# コード整形
pnpm format
# フォーマット検証
pnpm format:check
# フロントエンド単体テスト
pnpm test:unit
# ウォッチモード(開発に推奨)
pnpm test:unit:watch
# アプリをビルド
pnpm build
# デバッグビルド
pnpm tauri build --debug
```
### Rust バックエンド開発
```bash
cd src-tauri
# Rust コード整形
cargo fmt
# clippy チェック
cargo clippy
# バックエンドテスト
cargo test
# 特定テストのみ実行
cargo test test_name
# test-hooks フィーチャー付きでテスト
cargo test --features test-hooks
```
### テストガイド
**フロントエンドテスト**:
- テストフレームワークに **vitest** を使用
- **MSW (Mock Service Worker)** で Tauri API 呼び出しをモック
- コンポーネントテストに **@testing-library/react** を採用
**テスト実行**:
```bash
# 全テストを実行
pnpm test:unit
# ウォッチモード(自動再実行)
pnpm test:unit:watch
# カバレッジレポート付き
pnpm test:unit --coverage
```
### 技術スタック
**フロントエンド**: React 18 · TypeScript · Vite · TailwindCSS 3.4 · TanStack Query v5 · react-i18next · react-hook-form · zod · shadcn/ui · @dnd-kit
**バックエンド**: Tauri 2.8 · Rust · serde · tokio · thiserror · tauri-plugin-updater/process/dialog/store/log
**テスト**: vitest · MSW · @testing-library/react
</details>
<details>
<summary><strong>プロジェクト構成</strong></summary>
```
├── src/ # フロントエンド (React + TypeScript)
│ ├── components/
│ │ ├── providers/ # プロバイダ管理
│ │ ├── mcp/ # MCP パネル
│ │ ├── prompts/ # Prompts 管理
│ │ ├── skills/ # Skills 管理
│ │ ├── sessions/ # Session Manager
│ │ ├── proxy/ # Proxy モードパネル
│ │ ├── openclaw/ # OpenClaw 設定パネル
│ │ ├── settings/ # 設定 (Terminal/Backup/About)
│ │ ├── deeplink/ # Deep Link インポート
│ │ ├── env/ # 環境変数管理
│ │ ├── universal/ # クロスアプリ設定
│ │ ├── usage/ # 使用量統計
│ │ └── ui/ # shadcn/ui コンポーネントライブラリ
│ ├── hooks/ # カスタムフック(ビジネスロジック)
│ ├── lib/
│ │ ├── api/ # Tauri API ラッパー(型安全)
│ │ └── query/ # TanStack Query 設定
│ ├── locales/ # 翻訳 (zh/en/ja)
│ ├── config/ # プリセット (providers/mcp)
│ └── types/ # TypeScript 型定義
├── src-tauri/ # バックエンド (Rust)
│ └── src/
│ ├── commands/ # Tauri コマンド層(ドメイン別)
│ ├── services/ # ビジネスロジック層
│ ├── database/ # SQLite DAO 層
│ ├── proxy/ # Proxy モジュール
│ ├── session_manager/ # セッション管理
│ ├── deeplink/ # Deep Link 処理
│ └── mcp/ # MCP 同期モジュール
├── tests/ # フロントエンドテスト
└── assets/ # スクリーンショット & パートナーリソース
```
</details>
## 貢献
Issue や提案を歓迎します!
PR を送る前に以下をご確認ください:
- 型チェック: `pnpm typecheck`
- フォーマットチェック: `pnpm format:check`
- 単体テスト: `pnpm test:unit`
新機能の場合は、PR を送る前に Issue でディスカッションしてください。プロジェクトに合わない機能の PR はクローズされる場合があります。
## Star History
[](https://www.star-history.com/#farion1231/cc-switch&Date)
## ライセンス
MIT © Jason Young
================================================
FILE: README_ZH.md
================================================
<div align="center">
# CC Switch
### Claude Code、Codex、Gemini CLI、OpenCode 和 OpenClaw 的全方位管理工具
[](https://github.com/farion1231/cc-switch/releases)
[](https://github.com/farion1231/cc-switch/releases)
[](https://tauri.app/)
[](https://github.com/farion1231/cc-switch/releases/latest)
<a href="https://trendshift.io/repositories/15372" target="_blank"><img src="https://trendshift.io/api/badge/repositories/15372" alt="farion1231%2Fcc-switch | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
[English](README.md) | 中文 | [日本語](README_JA.md) | [更新日志](CHANGELOG.md)
</div>
## ❤️赞助商
<details open>
<summary>点击折叠</summary>
[](https://platform.minimaxi.com/subscribe/coding-plan?code=7kYF2VoaCn&source=link)
MiniMax M2.5 在编程、工具调用与搜索、办公等核心生产力场景均达到或刷新行业 SOTA,拥有架构师级代码能力与高效任务拆解能力,推理速度较上一代提升 37%、token 消耗更优;100 token/s 连续工作一小时仅需 1 美金,让复杂 Agent 规模化部署经济可行,已在企业多职能场景深度落地,加速全民 Agent 时代到来。
[点击](https://platform.minimaxi.com/subscribe/coding-plan?code=7kYF2VoaCn&source=link)即可领取 MiniMax Coding Plan 专属 88 折优惠!
---
<table>
<tr>
<td width="180"><a href="https://www.packyapi.com/register?aff=cc-switch"><img src="assets/partners/logos/packycode.png" alt="PackyCode" width="150"></a></td>
<td>感谢 PackyCode 赞助了本项目!PackyCode 是一家稳定、高效的API中转服务商,提供 Claude Code、Codex、Gemini 等多种中转服务。PackyCode 为本软件的用户提供了特别优惠,使用<a href="https://www.packyapi.com/register?aff=cc-switch">此链接</a>注册并在充值时填写"cc-switch"优惠码,首次充值可以享受9折优惠!</td>
</tr>
<tr>
<td width="180"><a href="https://cloud.siliconflow.cn/i/drGuwc9k"><img src="assets/partners/logos/silicon_zh.jpg" alt="SiliconFlow" width="150"></a></td>
<td>感谢硅基流动赞助了本项目!硅基流动是一个高性能 AI 基础设施与模型 API 平台,一站式提供语言、语音、图像、视频等多模态模型的快速、可靠访问。平台支持按量计费、丰富的多模态模型选择、高速推理和企业级稳定性,帮助开发者和团队更高效地构建和扩展 AI 应用。通过<a href="https://cloud.siliconflow.cn/i/drGuwc9k">此链接</a>注册并完成实名认证,即可获得 ¥20 奖励金,可在平台内跨模型使用。硅基流动现已兼容 OpenClaw,用户可接入硅基流动 API Key 免费调用主流 AI 模型。</td>
</tr>
<tr>
<td width="180"><a href="https://aigocode.com/invite/CC-SWITCH"><img src="assets/partners/logos/aigocode.png" alt="AIGoCode" width="150"></a></td>
<td>感谢 AIGoCode 赞助了本项目!AIGoCode 是一个集成了 Claude Code、Codex 以及 Gemini 最新模型的一站式平台,为你提供稳定、高效且高性价比的AI编程服务。本站提供灵活的订阅计划,零封号风险,国内直连,无需魔法,极速响应。AIGoCode 为 CC Switch 的用户提供了特别福利,通过<a href="https://aigocode.com/invite/CC-SWITCH">此链接</a>注册的用户首次充值可以获得额外10%奖励额度!</td>
</tr>
<tr>
<td width="180"><a href="https://www.aicodemirror.com/register?invitecode=9915W3"><img src="assets/partners/logos/aicodemirror.jpg" alt="AICodeMirror" width="150"></a></td>
<td>感谢 AICodeMirror 赞助了本项目!AICodeMirror 提供 Claude Code / Codex / Gemini CLI 官方高稳定中转服务,支持企业级高并发、极速开票、7×24 专属技术支持。
Claude Code / Codex / Gemini 官方渠道低至 3.8 / 0.2 / 0.9 折,充值更有折上折!AICodeMirror 为 CCSwitch 的用户提供了特别福利,通过<a href="https://www.aicodemirror.com/register?invitecode=9915W3">此链接</a>注册的用户,可享受首充8折,企业客户最高可享 7.5 折!</td>
</tr>
<tr>
<td width="180"><a href="https://cubence.com/signup?code=CCSWITCH&source=ccs"><img src="assets/partners/logos/cubence.png" alt="Cubence" width="150"></a></td>
<td>感谢 Cubence 赞助本项目!Cubence 是一家可靠高效的 API 中继服务提供商,提供对 Claude Code、Codex、Gemini 等模型的中继服务,并提供按量、包月等灵活的计费方式。Cubence 为 CC Switch 的用户提供了特别优惠:使用 <a href="https://cubence.com/signup?code=CCSWITCH&source=ccs">此链接</a> 注册,并在充值时输入 "CCSWITCH" 优惠码,每次充值均可享受九折优惠!</td>
</tr>
<tr>
<td width="180"><a href="https://www.dmxapi.cn/register?aff=bUHu"><img src="assets/partners/logos/dmx-zh.jpeg" alt="DMXAPI" width="150"></a></td>
<td>感谢 DMXAPI(大模型API)赞助了本项目! DMXAPI,一个Key用全球大模型。
为200多家企业用户提供全球大模型API服务。· 充值即开票 ·当天开票 ·并发不限制 ·1元起充 · 7x24 在线技术辅导,GPT/Claude/Gemini全部6.8折,国内模型5~8折,Claude Code 专属模型3.4折进行中!<a href="https://www.dmxapi.cn/register?aff=bUHu">点击这里注册</a></td>
</tr>
<tr>
<td width="180"><a href="https://www.compshare.cn/coding-plan?ytag=GPU_YY_YX_git_cc-switch"><img src="assets/partners/logos/ucloud.png" alt="优云智算" width="150"></a></td>
<td>感谢优云智算赞助了本项目!优云智算是UCloud旗下AI云平台,提供稳定、全面的国内外模型API,仅一个key即可调用。主打包月、按量的高性价比 Coding Plan 套餐,基于官方2~5折优惠。支持接入 Claude Code、Codex 及 API 调用。支持企业高并发、7*24技术支持、自助开票。通过<a href="https://www.compshare.cn/coding-plan?ytag=GPU_YY_YX_git_cc-switch">此链接</a>注册的用户,可得免费5元平台体验金!</td>
</tr>
<tr>
<td width="180"><a href="https://www.right.codes/register?aff=CCSWITCH"><img src="assets/partners/logos/rightcode.jpg" alt="RightCode" width="150"></a></td>
<td>感谢 Right Code 赞助了本项目!Right Code 稳定提供 Claude Code、Codex、Gemini 等模型的中转服务。主打<strong>极高性价比</strong>的Codex包月套餐,<strong>提供额度转结,套餐当天用不完的额度,第二天还能接着用!</strong>充值即可开票,企业、团队用户一对一对接。同时为 CC Switch 的用户提供了特别优惠:通过<a href="https://www.right.codes/register?aff=CCSWITCH">此链接</a>注册,每次充值均可获得实付金额25%的按量额度!</td>
</tr>
<tr>
<td width="180"><a href="https://aicoding.sh/i/CCSWITCH"><img src="assets/partners/logos/aicoding.jpg" alt="AICoding" width="150"></a></td>
<td>感谢 AICoding.sh 赞助了本项目!AICoding.sh —— 全球大模型 API 超值中转服务!Claude Code 1.9 折,GPT 0.1 折,已为数百家企业提供高性价比 AI 服务。支持 Claude Code、GPT、Gemini 及国内主流模型,企业级高并发、极速开票、7×24 专属技术支持,通过<a href="https://aicoding.sh/i/CCSWITCH">此链接</a> 注册的 CC Switch 用户,首充可享受九折优惠!</td>
</tr>
<tr>
<td width="180"><a href="https://crazyrouter.com/register?aff=OZcm&ref=cc-switch"><img src="assets/partners/logos/crazyrouter.jpg" alt="Crazyrouter" width="150"></a></td>
<td>感谢 Crazyrouter 赞助了本项目!Crazyrouter 是一个高性能 AI API 聚合平台——一个 API Key 即可访问 300+ 模型,包括 Claude Code、Codex、Gemini CLI 等。全部模型低至官方定价的 55%,支持自动故障转移、智能路由和无限并发。Crazyrouter 为 CC Switch 用户提供了专属优惠:通过<a href="https://crazyrouter.com/register?aff=OZcm&ref=cc-switch">此链接</a>注册即可获得 <strong>$2 免费额度</strong>,首次充值时输入优惠码 `CCSWITCH` 还可获得额外 <strong>30% 奖励额度</strong>!</td>
</tr>
<tr>
<td width="180"><a href="https://www.sssaicode.com/register?ref=DCP0SM"><img src="assets/partners/logos/sssaicode.png" alt="SSSAiCode" width="150"></a></td>
<td>感谢 SSSAiCode 赞助了本项目!SSSAiCode 是一家稳定可靠的API中转站,致力于提供稳定、可靠、平价的Claude、CodeX模型服务,<strong>提供高性价比折合0.5¥/$的官方Claude服务</strong>,支持包月、Paygo多种计费方式、支持当日快速开票,SSSAiCode为本软件的用户提供特别优惠,使用<a href="https://www.sssaicode.com/register?ref=DCP0SM">此链接</a>注册每次充值均可享受10$的额外奖励!</td>
</tr>
<tr>
<td width="180"><a href="https://www.openclaudecode.cn/register?aff=aOYQ"><img src="assets/partners/logos/mikubanner.svg" alt="Micu" width="150"></a></td>
<td>感谢 米醋API 赞助了本项目!米醋API 是一家致力于提供极致性价比与高稳定性的全球大模型中转服务商。米醋API 背后有实体企业做核心保障,杜绝跑路风险,支持极速正规开票!我们主打“试错零成本”:1 元起充低门槛,0 手续费随时退款!米醋API 为本软件的用户提供了特别优惠,使用<a href="https://www.openclaudecode.cn/register?aff=aOYQ">此链接</a>注册并在充值时填写"ccswitch"优惠码可享九折优惠!</td>
</tr>
<tr>
<td width="180"><a href="https://x-code.cc/register?aff=IbPp"><img src="assets/partners/logos/xcodeapi.png" alt="XCodeAPI" width="150"></a></td>
<td>感谢 XCodeAPI 赞助了本项目!XCodeAPI 为本软件的用户提供特别福利,使用<a href="https://x-code.cc/register?aff=IbPp">此链接</a>注册后首单加赠10%的额度!(联系站长领取)</td>
</tr>
<tr>
<td width="180"><a href="https://ctok.ai"><img src="assets/partners/logos/ctok.png" alt="CTok" width="150"></a></td>
<td>感谢 CTok.ai 赞助了本项目!CTok.ai 致力于打造一站式 AI 编程工具服务平台。我们提供 Claude Code 专业套餐及技术社群服务,同时支持 Google Gemini 和 OpenAI Codex。通过精心设计的套餐方案和专业的技术社群,为开发者提供稳定的服务保障和持续的技术支持,让 AI 辅助编程真正成为开发者的生产力工具。点击<a href="https://ctok.ai">这里</a>注册!</td>
</tr>
</table>
</details>
## 为什么选择 CC Switch?
现代 AI 编程依赖于 Claude Code、Codex、Gemini CLI、OpenCode 和 OpenClaw 等 CLI 工具——但每个工具都有自己的配置格式。切换 API 供应商意味着手动编辑 JSON、TOML 或 `.env` 文件,而在多个工具之间缺乏一个统一管理 MCP, SKILLS 的方式。
**CC Switch** 为你提供一个桌面应用来管理所有五个 CLI 工具。无需手动编辑配置文件,你将获得一个可视化界面,一键将供应商导入应用,一键在不同的供应商之间进行切换,内置 50+ 供应商预设、统一的 MCP, SKILLS 管理以及系统托盘即时切换功能——所有操作都基于可靠的 SQLite 数据库和原子写入机制,保护你的配置不被损坏。
- **一个应用,五个 CLI 工具** — 在单一界面中管理 Claude Code、Codex、Gemini CLI、OpenCode 和 OpenClaw
- **告别手动编辑** — 50+ 供应商预设,包括 AWS Bedrock、NVIDIA NIM 和社区中转服务;一键即可切换
- **统一 MCP, SKILLS 管理** — 一个面板管理四个应用的 MCP, SKILLS, 支持双向同步
- **系统托盘快速切换** — 从托盘菜单即时切换供应商,无需打开完整应用
- **云同步** — 通过 Dropbox、OneDrive、iCloud 或 WebDAV 服务器在不同设备之间同步供应商数据
- **跨平台** — 基于 Tauri 2 构建的原生桌面应用,支持 Windows、macOS 和 Linux
- **小工具** - 内置了多种小工具来解决首次安装登录确认、禁止签名、插件拓展同步等多种功能
## 界面预览
| 主界面 | 添加供应商 |
| :---------------------------------------: | :------------------------------------------: |
|  |  |
## 功能特性
[完整更新日志](CHANGELOG.md) | [发布说明](docs/release-notes/v3.12.3-zh.md)
### 供应商管理
- **5 个 CLI 工具,50+ 预设** — Claude Code、Codex、Gemini CLI、OpenCode、OpenClaw;复制 key 即可一键导入
- **通用供应商** — 一份配置同步到多个应用(OpenCode、OpenClaw)
- 一键切换、系统托盘快速访问、拖拽排序、导入导出
### 代理与故障转移
- **本地代理热切换** — 格式转换、自动故障转移、熔断器、供应商健康监控和整流器
- **应用级代理接管** — 独立为 Claude、Codex 或 Gemini 配置代理,具体到单个供应商
### MCP、Prompts 与 Skills
- **统一 MCP 面板** — 管理 4 个应用的 MCP 服务器,双向同步,支持 Deep Link 导入
- **Prompts** — Markdown 编辑器,跨应用同步(CLAUDE.md / AGENTS.md / GEMINI.md),回填保护
- **Skills** — 从 GitHub 仓库或 ZIP 文件一键安装,自定义仓库管理,支持软连接和文件复制
### 用量与成本追踪
- **用量仪表盘** — 跨供应商追踪支出、请求数和 Token 用量,趋势图表、详细请求日志和自定义模型定价
### 会话管理器与工作区
- 浏览、搜索和恢复全部应用对话历史
- **工作区编辑器**(OpenClaw)— 编辑 Agent 文件(AGENTS.md、SOUL.md 等),支持 Markdown 预览
### 系统与平台
- **云同步** — 自定义配置目录(Dropbox、OneDrive、iCloud、坚果云、NAS)及 WebDAV 服务器同步
- **Deep Link** (`ccswitch://`) — 通过 URL 一键导入供应商、MCP 服务器、提示词和技能
- 深色 / 浅色 / 跟随系统主题、开机自启、自动更新、原子写入、自动备份、国际化(中/英/日)
## 常见问题
<details>
<summary><strong>CC Switch 支持哪些 AI CLI 工具?</strong></summary>
CC Switch 支持五个工具:**Claude Code**、**Codex**、**Gemini CLI**、**OpenCode** 和 **OpenClaw**。每个工具都有专属的供应商预设和配置管理。
</details>
<details>
<summary><strong>切换供应商后需要重启终端吗?</strong></summary>
大多数工具需要重启终端或 CLI 工具才能使更改生效。例外的是 **Claude Code**,它目前支持供应商数据的热切换,无需重启。
</details>
<details>
<summary><strong>切换供应商之后我的插件配置怎么不见了?</strong></summary>
CC Switch 使用“通用配置片段”功能,在不同的供应商之间传递 Key 和请求地址之外的通用数据,您可以在“编辑供应商”菜单的“通用配置面板”里,点击“从当前供应商提取”,把所有的通用数据提取到通用配置中,之后在新建“供应商”的时候,只要勾选“写入通用配置”(默认勾选),就会把插件等数据写入到新的供应商配置中。您的所有配置项都会保存在运行本软件的时候,第一次导入的默认供应商里面,不会丢失。
</details>
<details>
<summary><strong>macOS 提示"未知开发者"警告 — 如何解决?</strong></summary>
这是由于作者没有苹果开发者账号(正在注册中)。关闭警告后,前往**系统设置 → 隐私与安全性 → 仍要打开**。之后应用即可正常打开。
</details>
<details>
<summary><strong>为什么总有一个正在激活中的供应商无法删除?</strong></summary>
本软件的设计原则是“最小侵入性”,即使卸载本软件,也不会影响应用的正常使用。
所以系统总会保留一个正在激活中的配置,因为如果将所有配置全部删除,该应用将无法正常使用。如果你不经常使用某个对应的应用,可以在设置中关掉该应用的显示。如果你想切换回官方登录,可以参考下条。
</details>
<details>
<summary><strong>如何切换回官方登录?</strong></summary>
可以在预设供应商里面添加一个官方供应商。切换过去之后,执行一遍 Log out / Log in 流程,之后便可以在官方供应商和第三方供应商之间随意切换。CodeX 可以在不同官方供应商之间进行切换,方便多个 Plus 或者 Team 账号之间切换。
</details>
<details>
<summary><strong>我的数据存储在哪里?</strong></summary>
- **数据库**:`~/.cc-switch/cc-switch.db`(SQLite — 供应商、MCP、提示词、技能)
- **本地设置**:`~/.cc-switch/settings.json`(设备级 UI 偏好设置)
- **备份**:`~/.cc-switch/backups/`(自动轮换,保留最近 10 个)
- **SKILLS**:`~/.cc-switch/skills/`(默认通过软链接连接到对应应用)
- **技能备份**:`~/.cc-switch/skill-backups/`(卸载前自动创建,保留最近 20 个)
</details>
## 文档
如需了解各项功能的详细使用方法,请查阅 **[用户手册](docs/user-manual/zh/README.md)** — 涵盖供应商管理、MCP/Prompts/Skills、代理与故障转移等全部功能。
## 快速开始
### 基本使用
1. **添加供应商**:点击"添加供应商" → 选择预设或创建自定义配置
2. **切换供应商**:
- 主界面:选择供应商 → 点击"启用"
- 系统托盘:直接点击供应商名称(立即生效)
3. **生效方式**:重启终端或对应的 CLI 工具以应用更改(CLaude Code 无需重启)
4. **恢复官方登录**:添加"官方登录"预设,重启 CLI 工具后按照其登录/OAuth 流程操作
### MCP、Prompts、Skills 与会话
- **MCP**:点击"MCP"按钮 → 通过模板或自定义配置添加服务器 → 切换各应用同步开关
- **Prompts**:点击"Prompts" → 使用 Markdown 编辑器创建预设 → 激活后同步到 live 文件
- **Skills**:点击"Skills" → 浏览 GitHub 仓库 → 一键安装到全部应用
- **会话**:点击"Sessions" → 浏览和搜索和恢复全部应用对话历史
> **注意**:首次启动可以手动导入现有 CLI 工具配置作为默认供应商。
## 下载安装
### 系统要求
- **Windows**:Windows 10 及以上
- **macOS**:macOS 12 (Monterey) 及以上
- **Linux**:Ubuntu 22.04+ / Debian 11+ / Fedora 34+ 等主流发行版
### Windows 用户
从 [Releases](../../releases) 页面下载最新版本的 `CC-Switch-v{版本号}-Windows.msi` 安装包或 `CC-Switch-v{版本号}-Windows-Portable.zip` 绿色版。
### macOS 用户
**方式一:通过 Homebrew 安装(推荐)**
```bash
brew tap farion1231/ccswitch
brew install --cask cc-switch
```
更新:
```bash
brew upgrade --cask cc-switch
```
**方式二:手动下载**
从 [Releases](../../releases) 页面下载 `CC-Switch-v{版本号}-macOS.zip` 解压使用。
> **注意**:由于作者没有苹果开发者账号,首次打开可能出现"未知开发者"警告,请先关闭,然后前往"系统设置" → "隐私与安全性" → 点击"仍要打开",之后便可以正常打开。
### Arch Linux 用户
**通过 paru 安装(推荐)**
```bash
paru -S cc-switch-bin
```
### Linux 用户
从 [Releases](../../releases) 页面下载最新版本的 Linux 安装包:
- `CC-Switch-v{版本号}-Linux.deb`(Debian/Ubuntu)
- `CC-Switch-v{版本号}-Linux.rpm`(Fedora/RHEL/openSUSE)
- `CC-Switch-v{版本号}-Linux.AppImage`(通用)
- `CC-Switch-v{版本号}-Linux.flatpak`(Flatpak)
Flatpak 安装与运行:
```bash
flatpak install --user ./CC-Switch-v{版本号}-Linux.flatpak
flatpak run com.ccswitch.desktop
```
<details>
<summary><strong>架构总览</strong></summary>
### 设计原则
```
┌─────────────────────────────────────────────────────────────┐
│ 前端 (React + TS) │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Components │ │ Hooks │ │ TanStack Query │ │
│ │ (UI) │──│ (业务逻辑) │──│ (缓存/同步) │ │
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
└────────────────────────┬────────────────────────────────────┘
│ Tauri IPC
┌────────────────────────▼────────────────────────────────────┐
│ 后端 (Tauri + Rust) │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Commands │ │ Services │ │ Models/Config │ │
│ │ (API 层) │──│ (业务层) │──│ (数据) │ │
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
**核心设计模式**
- **SSOT**(单一事实源):所有数据存储在 `~/.cc-switch/cc-switch.db`(SQLite)
- **双层存储**:SQLite 存储可同步数据,JSON 存储设备级设置
- **双向同步**:切换时写入 live 文件,编辑当前供应商时从 live 回填
- **原子写入**:临时文件 + 重命名模式防止配置损坏
- **并发安全**:Mutex 保护的数据库连接避免竞态条件
- **分层架构**:清晰分离(Commands → Services → DAO → Database)
**核心组件**
- **ProviderService**:供应商增删改查、切换、回填、排序
- **McpService**:MCP 服务器管理、导入导出、live 文件同步
- **ProxyService**:本地 Proxy 模式,支持热切换和格式转换
- **SessionManager**:Claude Code 对话历史浏览
- **ConfigService**:配置导入导出、备份轮换
- **SpeedtestService**:API 端点延迟测量
</details>
<details>
<summary><strong>开发指南</strong></summary>
### 环境要求
- Node.js 18+
- pnpm 8+
- Rust 1.85+
- Tauri CLI 2.8+
### 开发命令
```bash
# 安装依赖
pnpm install
# 开发模式(热重载)
pnpm dev
# 类型检查
pnpm typecheck
# 代码格式化
pnpm format
# 检查代码格式
pnpm format:check
# 运行前端单元测试
pnpm test:unit
# 监听模式运行测试(推荐开发时使用)
pnpm test:unit:watch
# 构建应用
pnpm build
# 构建调试版本
pnpm tauri build --debug
```
### Rust 后端开发
```bash
cd src-tauri
# 格式化 Rust 代码
cargo fmt
# 运行 clippy 检查
cargo clippy
# 运行后端测试
cargo test
# 运行特定测试
cargo test test_name
# 运行带测试 hooks 的测试
cargo test --features test-hooks
```
### 测试说明
**前端测试**:
- 使用 **vitest** 作为测试框架
- 使用 **MSW (Mock Service Worker)** 模拟 Tauri API 调用
- 使用 **@testing-library/react** 进行组件测试
**运行测试**:
```bash
# 运行所有测试
pnpm test:unit
# 监听模式(自动重跑)
pnpm test:unit:watch
# 带覆盖率报告
pnpm test:unit --coverage
```
### 技术栈
**前端**:React 18 · TypeScript · Vite · TailwindCSS 3.4 · TanStack Query v5 · react-i18next · react-hook-form · zod · shadcn/ui · @dnd-kit
**后端**:Tauri 2.8 · Rust · serde · tokio · thiserror · tauri-plugin-updater/process/dialog/store/log
**测试**:vitest · MSW · @testing-library/react
</details>
<details>
<summary><strong>项目结构</strong></summary>
```
├── src/ # 前端 (React + TypeScript)
│ ├── components/
│ │ ├── providers/ # 供应商管理
│ │ ├── mcp/ # MCP 面板
│ │ ├── prompts/ # Prompts 管理
│ │ ├── skills/ # Skills 管理
│ │ ├── sessions/ # 会话管理器
│ │ ├── proxy/ # Proxy 模式面板
│ │ ├── openclaw/ # OpenClaw 配置面板
│ │ ├── settings/ # 设置(终端/备份/关于)
│ │ ├── deeplink/ # Deep Link 导入
│ │ ├── env/ # 环境变量管理
│ │ ├── universal/ # 跨应用配置
│ │ ├── usage/ # 用量统计
│ │ └── ui/ # shadcn/ui 组件库
│ ├── hooks/ # 自定义 hooks(业务逻辑)
│ ├── lib/
│ │ ├── api/ # Tauri API 封装(类型安全)
│ │ └── query/ # TanStack Query 配置
│ ├── locales/ # 翻译 (zh/en/ja)
│ ├── config/ # 预设 (providers/mcp)
│ └── types/ # TypeScript 类型定义
├── src-tauri/ # 后端 (Rust)
│ └── src/
│ ├── commands/ # Tauri 命令层(按领域)
│ ├── services/ # 业务逻辑层
│ ├── database/ # SQLite DAO 层
│ ├── proxy/ # Proxy 模块
│ ├── session_manager/ # 会话管理
│ ├── deeplink/ # Deep Link 处理
│ └── mcp/ # MCP 同步模块
├── tests/ # 前端测试
└── assets/ # 截图 & 合作商资源
```
</details>
## 贡献
欢迎提交 Issue 反馈问题和建议!
提交 PR 前请确保:
- 通过类型检查:`pnpm typecheck`
- 通过格式检查:`pnpm format:check`
- 通过单元测试:`pnpm test:unit`
新功能开发前,欢迎先开 Issue 讨论实现方案,不适合项目的功能性 PR 有可能会被关闭。
## Star History
[](https://www.star-history.com/#farion1231/cc-switch&Date)
## License
MIT © Jason Young
================================================
FILE: cc-switch-main/src/config/universalProviderPresets.ts
================================================
/**
* 统一供应商(Universal Provider)预设配置
*
* 统一供应商是跨应用共享的配置,修改后会自动同步到 Claude、Codex、Gemini 三个应用。
* 适用于 NewAPI 等支持多种协议的 API 网关。
*/
import type {
UniversalProvider,
UniversalProviderApps,
UniversalProviderModels,
} from "@/types";
/**
* 统一供应商预设接口
*/
export interface UniversalProviderPreset {
/** 预设名称 */
name: string;
/** 供应商类型标识 */
providerType: string;
/** 默认启用的应用 */
defaultApps: UniversalProviderApps;
/** 默认模型配置 */
defaultModels: UniversalProviderModels;
/** 网站链接 */
websiteUrl?: string;
/** 图标名称 */
icon?: string;
/** 图标颜色 */
iconColor?: string;
/** 描述 */
description?: string;
/** 是否为自定义模板(允许用户完全自定义) */
isCustomTemplate?: boolean;
}
/**
* NewAPI 默认模型配置
*/
const NEWAPI_DEFAULT_MODELS: UniversalProviderModels = {
claude: {
model: "claude-sonnet-4-20250514",
haikuModel: "claude-haiku-4-20250514",
sonnetModel: "claude-sonnet-4-20250514",
opusModel: "claude-sonnet-4-20250514",
},
codex: {
model: "gpt-4o",
reasoningEffort: "high",
},
gemini: {
model: "gemini-2.5-pro",
},
};
const N1N_DEFAULT_MODELS: UniversalProviderModels = {
claude: {
model: "claude-3-5-sonnet-20240620",
haikuModel: "claude-3-haiku-20240307",
sonnetModel: "claude-3-5-sonnet-20240620",
opusModel: "claude-3-opus-20240229",
},
codex: {
model: "gpt-4o",
reasoningEffort: "high",
},
gemini: {
model: "gemini-1.5-pro-latest",
},
};
/**
* 统一供应商预设列表
*/
export const universalProviderPresets: UniversalProviderPreset[] = [
{
name: "n1n.ai",
providerType: "n1n",
defaultApps: {
claude: true,
codex: true,
gemini: true,
},
defaultModels: N1N_DEFAULT_MODELS,
websiteUrl: "https://n1n.ai",
icon: "openai",
iconColor: "#000000",
description:
"n1n.ai - 聚合 OpenAI, Anthropic, Google 等主流大模型的一站式 AI 服务平台",
},
{
name: "NewAPI",
providerType: "newapi",
defaultApps: {
claude: true,
codex: true,
gemini: true,
},
defaultModels: NEWAPI_DEFAULT_MODELS,
websiteUrl: "https://www.newapi.pro",
icon: "newapi",
iconColor: "#00A67E",
description:
"NewAPI 是一个可自部署的 API 网关,支持 Anthropic、OpenAI、Gemini 等多种协议",
},
{
name: "自定义网关",
providerType: "custom_gateway",
defaultApps: {
claude: true,
codex: true,
gemini: true,
},
defaultModels: NEWAPI_DEFAULT_MODELS,
icon: "openai",
iconColor: "#6366F1",
description: "自定义配置的 API 网关",
isCustomTemplate: true,
},
];
/**
* 根据预设创建统一供应商
*/
export function createUniversalProviderFromPreset(
preset: UniversalProviderPreset,
id: string,
baseUrl: string,
apiKey: string,
customName?: string,
): UniversalProvider {
return {
id,
name: customName || preset.name,
providerType: preset.providerType,
apps: { ...preset.defaultApps },
baseUrl,
apiKey,
models: JSON.parse(JSON.stringify(preset.defaultModels)), // Deep copy
websiteUrl: preset.websiteUrl,
icon: preset.icon,
iconColor: preset.iconColor,
createdAt: Date.now(),
};
}
/**
* 获取预设的显示名称(用于 UI)
*/
export function getPresetDisplayName(preset: UniversalProviderPreset): string {
return preset.name;
}
/**
* 根据类型查找预设
*/
export function findPresetByType(
providerType: string,
): UniversalProviderPreset | undefined {
return universalProviderPresets.find((p) => p.providerType === providerType);
}
================================================
FILE: components.json
================================================
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.cjs",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}
================================================
FILE: deplink.html
================================================
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CC Switch 深链接测试</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 900px;
margin: 0 auto;
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
color: white;
padding: 40px;
text-align: center;
}
.header h1 {
font-size: 32px;
margin-bottom: 10px;
}
.header p {
font-size: 16px;
opacity: 0.9;
}
.content {
padding: 40px;
}
.section {
margin-bottom: 40px;
}
.section h2 {
color: #2c3e50;
font-size: 24px;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #ecf0f1;
}
.version-badge {
display: inline-block;
background: linear-gradient(135deg, #f39c12 0%, #e67e22 100%);
color: white;
padding: 4px 12px;
border-radius: 12px;
font-size: 14px;
font-weight: 600;
margin-left: 8px;
vertical-align: middle;
}
.link-card {
background: #f8f9fa;
border-radius: 12px;
padding: 24px;
margin-bottom: 20px;
border: 2px solid #e9ecef;
transition: all 0.3s ease;
}
.link-card:hover {
border-color: #3498db;
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.15);
transform: translateY(-2px);
}
.link-card h3 {
color: #2c3e50;
font-size: 20px;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 8px;
}
.link-card .description {
color: #7f8c8d;
font-size: 14px;
margin-bottom: 16px;
line-height: 1.6;
}
.deep-link {
display: inline-flex;
align-items: center;
gap: 8px;
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
color: white;
padding: 12px 24px;
text-decoration: none;
border-radius: 8px;
font-weight: 500;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(52, 152, 219, 0.3);
}
.deep-link:hover {
background: linear-gradient(135deg, #2980b9 0%, #1f6391 100%);
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.4);
transform: translateY(-1px);
}
.deep-link:active {
transform: translateY(0);
}
.info-box {
background: #fff3cd;
border-left: 4px solid #ffc107;
padding: 16px;
border-radius: 8px;
margin-top: 20px;
}
.info-box h4 {
color: #856404;
margin-bottom: 8px;
font-size: 16px;
}
.info-box ul {
list-style: disc;
margin-left: 20px;
color: #856404;
font-size: 14px;
line-height: 1.8;
padding-left: 20px;
}
.generator-section {
background: #e8f4f8;
border-radius: 12px;
padding: 30px;
margin-top: 40px;
}
.generator-section h2 {
color: #2c3e50;
margin-bottom: 24px;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: #2c3e50;
font-weight: 500;
font-size: 14px;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 12px;
border: 2px solid #dee2e6;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s ease;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: #3498db;
}
.btn {
background: linear-gradient(135deg, #27ae60 0%, #229954 100%);
color: white;
padding: 14px 28px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(39, 174, 96, 0.3);
}
.btn:hover {
background: linear-gradient(135deg, #229954 0%, #1e8449 100%);
box-shadow: 0 4px 12px rgba(39, 174, 96, 0.4);
transform: translateY(-1px);
}
.btn:active {
transform: translateY(0);
}
.result-box {
background: white;
border-radius: 8px;
padding: 20px;
margin-top: 20px;
border: 2px solid #3498db;
}
.result-box strong {
color: #2c3e50;
font-size: 16px;
}
.result-text {
background: #f8f9fa;
padding: 12px;
border-radius: 6px;
margin: 12px 0;
word-break: break-all;
font-family: monospace;
font-size: 13px;
color: #2c3e50;
border: 1px solid #dee2e6;
}
.btn-copy {
background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%);
margin-right: 10px;
}
.btn-copy:hover {
background: linear-gradient(135deg, #8e44ad 0%, #7d3c98 100%);
}
.app-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
}
.badge-claude {
background: #e8f4f8;
color: #3498db;
}
.badge-codex {
background: #fef5e7;
color: #f39c12;
}
.badge-gemini {
background: #fdeef4;
color: #e91e63;
}
.param-list {
background: #f8f9fa;
border-left: 3px solid #3498db;
padding: 12px;
border-radius: 6px;
margin: 12px 0;
font-size: 13px;
line-height: 1.8;
color: #495057;
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
}
.param-tag {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
margin-right: 6px;
background: #3498db;
color: white;
}
.param-tag.optional {
background: #95a5a6;
}
@media (max-width: 768px) {
.header h1 {
font-size: 24px;
}
.content {
padding: 20px;
}
.generator-section {
padding: 20px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🔗 CC Switch 深链接测试</h1>
<p>点击下方链接测试深链接导入功能</p>
</div>
<div class="content">
<!-- 配置文件导入示例 (v3.8+) -->
<div class="section">
<h2>📦 配置文件导入示例 <span class="version-badge">v3.8+</span></h2>
<!-- Claude 配置文件导入 -->
<div class="link-card">
<h3>
<span class="app-badge badge-claude">Claude</span>
完整 JSON 配置导入
</h3>
<p class="description">
通过 Base64 编码的 JSON 配置文件导入完整配置,包含所有四种模型和端点信息。
</p>
<div class="param-list">
<span class="param-tag">必需</span> resource=provider, app=claude, name<br>
<span class="param-tag optional">可选</span> <strong>config</strong> (Base64 JSON),
<strong>configFormat=json</strong>
</div>
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
<a href="ccswitch://v1/import?resource=provider&app=claude&name=Claude%20Complete&configFormat=json&config=eyJlbnYiOnsiQU5USFJPUElDX0FVVEhfVE9LRU4iOiJzay1hbnQtdGVzdC1rZXkxMjMiLCJBTlRIUk9QSUNfQkFTRV9VUkwiOiJodHRwczovL2FwaS5hbnRocm9waWMuY29tL3YxIiwiQU5USFJPUElDX01PREVMIjoiY2xhdWRlLXNvbm5ldC00LjUiLCJBTlRIUk9QSUNfREVGQVVMVF9IQUlLVV9NT0RFTCI6ImNsYXVkZS1oYWlrdS00LjEiLCJBTlRIUk9QSUNfREVGQVVMVF9TT05ORVRfTU9ERUwiOiJjbGF1ZGUtc29ubmV0LTQuNSIsIkFOVEhST1BJQ19ERUZBVUxUX09QVVNfTU9ERUwiOiJjbGF1ZGUtb3B1cy00In19"
class="deep-link">
📥 导入完整配置
</a>
<button class="deep-link"
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=claude&name=Claude%20Complete&configFormat=json&config=eyJlbnYiOnsiQU5USFJPUElDX0FVVEhfVE9LRU4iOiJzay1hbnQtdGVzdC1rZXkxMjMiLCJBTlRIUk9QSUNfQkFTRV9VUkwiOiJodHRwczovL2FwaS5hbnRocm9waWMuY29tL3YxIiwiQU5USFJPUElDX01PREVMIjoiY2xhdWRlLXNvbm5ldC00LjUiLCJBTlRIUk9QSUNfREVGQVVMVF9IQUlLVV9NT0RFTCI6ImNsYXVkZS1oYWlrdS00LjEiLCJBTlRIUk9QSUNfREVGQVVMVF9TT05ORVRfTU9ERUwiOiJjbGF1ZGUtc29ubmV0LTQuNSIsIkFOVEhST1BJQ19ERUZBVUxUX09QVVNfTU9ERUwiOiJjbGF1ZGUtb3B1cy00In19', this)">
📋 复制链接
</button>
</div>
<div class="code-block"
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
<div style="color: #95a5a6; margin-bottom: 8px;">// 解码后的配置内容:</div>
{<br>
"env": {<br>
"ANTHROPIC_AUTH_TOKEN": "sk-ant-test-key123",<br>
"ANTHROPIC_BASE_URL": "https://api.anthropic.com/v1",<br>
"ANTHROPIC_MODEL": "claude-sonnet-4.5",<br>
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4.1",<br>
"ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4.5",<br>
"ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4"<br>
}<br>
}
</div>
</div>
<!-- URL 参数覆盖配置文件 -->
<div class="link-card">
<h3>
<span class="app-badge badge-claude">Claude</span>
配置 + URL 参数覆盖
</h3>
<p class="description">
配置文件提供基础设置,URL 参数覆盖 API Key。URL 参数优先级最高。
</p>
<div class="param-list">
<span class="param-tag">必需</span> name, config<br>
<span class="param-tag optional">覆盖</span> <strong>apiKey</strong> (覆盖配置文件中的值)
</div>
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
<a href="ccswitch://v1/import?resource=provider&app=claude&name=My%20Custom&apiKey=sk-ant-my-new-key&configFormat=json&config=eyJlbnYiOnsiQU5USFJPUElDX0JBU0VfVVJMIjoiaHR0cHM6Ly9hcGkuYW50aHJvcGljLmNvbS92MSIsIkFOVEhST1BJQ19NT0RFTCI6ImNsYXVkZS1zb25uZXQtNC41In19"
class="deep-link">
📥 导入混合配置
</a>
<button class="deep-link"
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=claude&name=My%20Custom&apiKey=sk-ant-my-new-key&configFormat=json&config=eyJlbnYiOnsiQU5USFJPUElDX0JBU0VfVVJMIjoiaHR0cHM6Ly9hcGkuYW50aHJvcGljLmNvbS92MSIsIkFOVEhST1BJQ19NT0RFTCI6ImNsYXVkZS1zb25uZXQtNC41In19', this)">
📋 复制链接
</button>
</div>
<div class="code-block"
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
<div style="color: #95a5a6; margin-bottom: 8px;">// 解码后的配置内容:</div>
{<br>
"env": {<br>
"ANTHROPIC_BASE_URL": "https://api.anthropic.com/v1",<br>
"ANTHROPIC_MODEL": "claude-sonnet-4.5"<br>
}<br>
}<br>
<div style="color: #f39c12; margin-top: 8px;">// URL 参数覆盖: apiKey=sk-ant-my-new-key</div>
</div>
<div
style="margin-top: 12px; padding: 10px; background: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px; font-size: 13px;">
<strong>优先级规则:</strong> URL 参数 (apiKey) > 配置文件 (endpoint, model)
</div>
</div>
<!-- Codex TOML 配置导入 -->
<div class="link-card">
<h3>
<span class="app-badge badge-codex">Codex</span>
TOML 格式配置导入
</h3>
<p class="description">
Codex 使用 TOML 格式的配置文件,包含 auth 和 config 两部分。
</p>
<div class="param-list">
<span class="param-tag">必需</span> name, config<br>
<span class="param-tag optional">可选</span> <strong>configFormat=json</strong> (Codex 配置为 JSON
包装的 TOML)
</div>
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
<a href="ccswitch://v1/import?resource=provider&app=codex&name=OpenAI%20Complete&configFormat=json&config=eyJhdXRoIjp7Ik9QRU5BSV9BUElfS0VZIjoic2stcHJvai10ZXN0LWtleTEyMyJ9LCJjb25maWciOiJbbW9kZWxfcHJvdmlkZXJzLm9wZW5haV1cbmJhc2VfdXJsID0gXCJodHRwczovL2FwaS5vcGVuYWkuY29tL3YxXCJcblxuW2dlbmVyYWxdXG5tb2RlbCA9IFwiZ3B0LTUuMVwiIn0="
class="deep-link">
📥 导入 Codex 配置
</a>
<button class="deep-link"
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=codex&name=OpenAI%20Complete&configFormat=json&config=eyJhdXRoIjp7Ik9QRU5BSV9BUElfS0VZIjoic2stcHJvai10ZXN0LWtleTEyMyJ9LCJjb25maWciOiJbbW9kZWxfcHJvdmlkZXJzLm9wZW5haV1cbmJhc2VfdXJsID0gXCJodHRwczovL2FwaS5vcGVuYWkuY29tL3YxXCJcblxuW2dlbmVyYWxdXG5tb2RlbCA9IFwiZ3B0LTUuMVwiIn0=', this)">
📋 复制链接
</button>
</div>
<div class="code-block"
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
<div style="color: #95a5a6; margin-bottom: 8px;">// 解码后的配置内容:</div>
{<br>
"auth": {<br>
"OPENAI_API_KEY": "sk-proj-test-key123"<br>
},<br>
"config": "[model_providers.openai]\nbase_url =
\"https://api.openai.com/v1\"\n\n[general]\nmodel = \"gpt-5.1\""<br>
}
<div style="color: #95a5a6; margin-top: 12px; margin-bottom: 4px;">// config 字段解析 (TOML):</div>
<div style="color: #a8d08d;">[model_providers.openai]</div>
<div style="color: #a8d08d;">base_url = "https://api.openai.com/v1"</div>
<div style="color: #a8d08d; margin-top: 8px;">[general]</div>
<div style="color: #a8d08d;">model = "gpt-5.1"</div>
</div>
</div>
<!-- Gemini 配置导入 -->
<div class="link-card">
<h3>
<span class="app-badge badge-gemini">Gemini</span>
Gemini 配置导入
</h3>
<p class="description">
Gemini 使用扁平的环境变量格式,简洁明了。
</p>
<div class="param-list">
<span class="param-tag">必需</span> name, config<br>
<span class="param-tag optional">可选</span> <strong>configFormat=json</strong>
</div>
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
<a href="ccswitch://v1/import?resource=provider&app=gemini&name=Google%20Gemini&configFormat=json&config=eyJHRU1JTklfQVBJX0tFWSI6IkFJemFTeUR0ZXN0a2V5MTIzIiwiR0VNSU5JX0JBU0VfVVJMIjoiaHR0cHM6Ly9nZW5lcmF0aXZlbGFuZ3VhZ2UuZ29vZ2xlYXBpcy5jb20vdjFiZXRhIiwiR0VNSU5JX01PREVMIjoiZ2VtaW5pLTMtcHJvLXByZXZpZXcifQ=="
class="deep-link">
📥 导入 Gemini 配置
</a>
<button class="deep-link"
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=gemini&name=Google%20Gemini&configFormat=json&config=eyJHRU1JTklfQVBJX0tFWSI6IkFJemFTeUR0ZXN0a2V5MTIzIiwiR0VNSU5JX0JBU0VfVVJMIjoiaHR0cHM6Ly9nZW5lcmF0aXZlbGFuZ3VhZ2UuZ29vZ2xlYXBpcy5jb20vdjFiZXRhIiwiR0VNSU5JX01PREVMIjoiZ2VtaW5pLTMtcHJvLXByZXZpZXcifQ==', this)">
📋 复制链接
</button>
</div>
<div class="code-block"
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
<div style="color: #95a5a6; margin-bottom: 8px;">// 解码后的配置内容:</div>
{<br>
"GEMINI_API_KEY": "AIzaSyDtestkey123",<br>
"GEMINI_BASE_URL": "https://generativelanguage.googleapis.com/v1beta",<br>
"GEMINI_MODEL": "gemini-3-pro-preview"<br>
}
</div>
</div>
</div>
<!-- MCP、Prompt、Skill 示例 -->
<div class="section">
<h2>🔌 MCP Servers 导入 <span class="version-badge">v3.8+</span></h2>
<div class="link-card">
<h3>📦📦 JSON 配置示例 - 批量导入多个 MCP Servers</h3>
<p class="description">
一次性导入多个 MCP 服务器 (Context7 + Sequential-thinking)。
</p>
<div class="param-list">
<span class="param-tag">必需</span> resource=mcp, apps, config (Base64)<br>
<span class="param-tag optional">可选</span> enabled
</div>
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
<a href="ccswitch://v1/import?resource=mcp&apps=claude,codex&config=eyJtY3BTZXJ2ZXJzIjp7ImNvbnRleHQ3Ijp7ImNvbW1hbmQiOiJidW54IiwiYXJncyI6WyIteSIsIkB1cHN0YXNoL2NvbnRleHQ3LW1jcCIsIi0tYXBpLWtleSIsImN0eDdzay00ZGRkNGY2Ni1lNzUyLTQwMjItYjFmNi1jOGNmNjI3OWI4MGQiXSwiZW52Ijp7fX0sInNlcXVlbnRpYWwtdGhpbmtpbmciOnsiY29tbWFuZCI6Im5weCIsImFyZ3MiOlsiLXkiLCJAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2VydmVyLXNlcXVlbnRpYWwtdGhpbmtpbmciXSwiZW52Ijp7fX19fQ==&enabled=true"
class="deep-link">📥 批量导入 2 个 MCP Servers</a>
<button class="deep-link"
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
onclick="copyDeepLink('ccswitch://v1/import?resource=mcp&apps=claude,codex&config=eyJtY3BTZXJ2ZXJzIjp7ImNvbnRleHQ3Ijp7ImNvbW1hbmQiOiJidW54IiwiYXJncyI6WyIteSIsIkB1cHN0YXNoL2NvbnRleHQ3LW1jcCIsIi0tYXBpLWtleSIsImN0eDdzay00ZGRkNGY2Ni1lNzUyLTQwMjItYjFmNi1jOGNmNjI3OWI4MGQiXSwiZW52Ijp7fX0sInNlcXVlbnRpYWwtdGhpbmtpbmciOnsiY29tbWFuZCI6Im5weCIsImFyZ3MiOlsiLXkiLCJAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2VydmVyLXNlcXVlbnRpYWwtdGhpbmtpbmciXSwiZW52Ijp7fX19fQ==&enabled=true', this)">📋
复制</button>
</div>
<!-- JSON 配置展示 -->
<div class="code-block"
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
<div style="color: #95a5a6; margin-bottom: 8px;">📦 批量 MCP 配置 JSON:</div>
<pre style="margin: 0; color: #a8d08d; line-height: 1.6;">{
"mcpServers": {
"context7": {
"command": "bunx",
"args": [
"-y",
"@upstash/context7-mcp",
"--api-key",
"ctx7sk-4ddd4f66-e752-4022-b1f6-c8cf6279b80d"
],
"env": {}
},
"sequential-thinking": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-sequential-thinking"
],
"env": {}
}
}
}</pre>
<div style="color: #95a5a6; margin-top: 8px; padding-top: 8px; border-top: 1px solid #34495e;">
💡 <strong>批量导入说明</strong>: 一次性导入 2 个 MCP 服务器<br>
📦 <strong>服务器 1</strong>: context7 - Upstash Context7 MCP 服务器<br>
📦 <strong>服务器 2</strong>: sequential-thinking - 结构化思维推理服务器<br>
🎯 <strong>目标应用</strong>: Claude 和 Codex<br>
🔄 <strong>智能合并</strong>: 如果服务器已存在,只更新应用启用状态,不覆盖配置
</div>
</div>
</div>
</div>
<!-- Prompt 导入示例 -->
<div class="section">
<h2>💬 Prompt 导入 <span class="version-badge">v3.8+</span></h2>
<div class="link-card">
<h3><span class="app-badge badge-claude">Claude</span> 代码审查专家</h3>
<p class="description">为 Claude 导入代码审查提示词。</p>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<a href="ccswitch://v1/import?resource=prompt&app=claude&name=代码审查专家&content=IyDku6PnoIHlrqHmn6XkuJPlrrYKCuS9oOaYr+S4gOS9jeaciee7j+mqjOeahOS7o+eggeWuoeafpeWRmO+8jOivt+WcqOS7o+eggeWuoeafpeWbnuWkjeeahOaXtuWAmeWBmuWQr+S4i+OAgg==&description=专注代码质量&enabled=true"
class="deep-link">📥 导入</a>
<button class="deep-link"
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
onclick="copyDeepLink('ccswitch://v1/import?resource=prompt&app=claude&name=代码审查专家&content=IyDku6PnoIHlrqHmn6XkuJPlrrYKCuS9oOaYr+S4gOS9jeaciee7j+mqjOeahOS7o+eggeWuoeafpeWRmO+8jOivt+WcqOS7o+eggeWuoeafpeWbnuWkjeeahOaXtuWAmeWBmuWQr+S4i+OAgg==&description=专注代码质量&enabled=true', this)">📋
复制</button>
</div>
<!-- 内容解释 -->
<div class="code-block"
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
<div style="color: #95a5a6; margin-bottom: 8px;">📝 Prompt 内容:</div>
<div style="color: #a8d08d; white-space: pre-wrap; line-height: 1.6;"># 代码审查专家
你是一位有经验的代码审查员,请在代码审查回复的时候做启下。</div>
<div style="color: #95a5a6; margin-top: 12px; padding-top: 8px; border-top: 1px solid #34495e;">
• <strong>应用</strong>: Claude<br>
• <strong>描述</strong>: 专注代码质量<br>
• <strong>状态</strong>: 导入后立即启用
</div>
</div>
</div>
</div>
<!-- Skill 导入示例 -->
<div class="section">
<h2>🛠️ Skill 仓库导入 <span class="version-badge">v3.8+</span></h2>
<div class="link-card">
<h3>添加 Claude Skill 仓库</h3>
<p class="description">
从 GitHub 仓库导入 Claude Skills,支持指定分支和子目录路径。
</p>
<div class="param-list">
<span class="param-tag">必需</span> resource=skill, repo (owner/name)<br>
<span class="param-tag optional">可选</span> branch, skills_path, directory
</div>
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
<a href="ccswitch://v1/import?resource=skill&repo=example/claude-skills&branch=main&skills_path=skills&directory=my-skills"
class="deep-link">
📥 导入 Skill 仓库示例
</a>
<button class="deep-link"
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
onclick="copyDeepLink('ccswitch://v1/import?resource=skill&repo=example/claude-skills&branch=main&skills_path=skills&directory=my-skills', this)">
📋 复制链接
</button>
</div>
<!-- 内容解释 -->
<div class="code-block"
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
<div style="color: #95a5a6; margin-bottom: 8px;">🗂️ 将添加以下 Skill 仓库:</div>
<div style="color: #52c41a; margin-bottom: 4px;">• <strong>GitHub 仓库</strong>:
example/claude-skills</div>
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>分支</strong>: main (默认分支)</div>
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>Skills 路径</strong>: skills
(仓库中技能文件所在的子目录)</div>
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>本地目录</strong>: my-skills (克隆到本地的目录名)
</div>
<div style="color: #95a5a6; margin-top: 12px; padding-top: 8px; border-top: 1px solid #34495e;">
💡 <strong>说明</strong>: 此操作会把仓库添加到 Skill 列表中。添加后,您可以在 Skills 管理界面选择安装具体的技能文件。<br>
🔧 <strong>应用</strong>: Claude (Skills 功能仅支持 Claude)
</div>
</div>
</div>
</div>
<!-- 深链接生成器 -->
<div class="section">
<h2>🚀 深链接生成器</h2>
<p style="color: #7f8c8d; margin-bottom: 24px;">
填写参数信息,自动生成深链接并导入到 CC Switch
</p>
<!-- MCP Servers 生成器 -->
<div class="generator-section">
<h3 style="color: #2c3e50; margin-bottom: 16px;">🔌 MCP Servers 导入生成器</h3>
<div class="form-group">
<label>目标应用 *</label>
<input type="text" id="mcpApps" placeholder="例如: claude,codex,gemini 或 claude" />
<small style="color: #7f8c8d;">多个应用用逗号分隔</small>
</div>
<div class="form-group">
<label>MCP 配置 (JSON) *</label>
<textarea id="mcpConfig" rows="8" placeholder='{
"mcpServers": {
"server-name": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-xxx"],
"type": "stdio"
}
}
}'></textarea>
<small style="color: #7f8c8d;">完整的 MCP 配置 JSON</small>
</div>
<div class="form-group">
<label>是否启用</label>
<select id="mcpEnabled">
<option value="true">是 (enabled=true)</option>
<option value="false">否 (enabled=false)</option>
</select>
</div>
<button class="btn" onclick="generateMcpLink()">🎯 生成 MCP 深链接</button>
<div id="mcpResult" class="result-section" style="display: none;">
<label>生成的深链接:</label>
<div class="result-url" id="mcpUrl" onclick="selectText(this)"></div>
<div style="display: flex; gap: 10px; margin-top: 12px;">
<button class="btn" onclick="copyGeneratedLink('mcpUrl')">📋 复制链接</button>
<a id="mcpImportBtn" class="btn"
style="background: linear-gradient(135deg, #27ae60 0%, #229954 100%); text-decoration: none;">
📥 立即导入
</a>
</div>
</div>
</div>
<!-- Prompt 生成器 -->
<div class="generator-section">
<h3 style="color: #2c3e50; margin-bottom: 16px;">💬 Prompt 导入生成器</h3>
<div class="form-group">
<label>目标应用 *</label>
<select id="promptApp">
<option value="claude">Claude</option>
<option value="codex">Codex</option>
<option value="gemini">Gemini</option>
</select>
</div>
<div class="form-group">
<label>提示词名称 *</label>
<input type="text" id="promptName" placeholder="例如: 代码审查专家" />
</div>
<div class="form-group">
<label>提示词内容 *</label>
<textarea id="promptContent" rows="6" placeholder="# 角色定义
你是一位专业的..."></textarea>
<small style="color: #7f8c8d;">支持 Markdown 格式,自动 Base64 编码</small>
</div>
<div class="form-group">
<label>描述</label>
<input type="text" id="promptDescription" placeholder="例如: 专注代码质量" />
</div>
<div class="form-group">
<label>导入后是否启用</label>
<select id="promptEnabled">
<option value="true">是 (将禁用其他提示词)</option>
<option value="false">否 (保持禁用状态)</option>
</select>
</div>
<button class="btn" onclick="generatePromptLink()">🎯 生成 Prompt 深链接</button>
<div id="promptResult" class="result-section" style="display: none;">
<label>生成的深链接:</label>
<div class="result-url" id="promptUrl" onclick="selectText(this)"></div>
<div style="display: flex; gap: 10px; margin-top: 12px;">
<button class="btn" onclick="copyGeneratedLink('promptUrl')">📋 复制链接</button>
<a id="promptImportBtn" class="btn"
style="background: linear-gradient(135deg, #27ae60 0%, #229954 100%); text-decoration: none;">
📥 立即导入
</a>
</div>
</div>
</div>
<!-- Skill 生成器 -->
<div class="generator-section">
<h3 style="color: #2c3e50; margin-bottom: 16px;">🛠️ Skill 仓库导入生成器</h3>
<div class="form-group">
<label>GitHub 仓库 *</label>
<input type="text" id="skillRepo" placeholder="例如: owner/repo-name" />
<small style="color: #7f8c8d;">格式: 所有者/仓库名</small>
</div>
<div class="form-group">
<label>分支</label>
<input type="text" id="skillBranch" placeholder="main" value="main" />
</div>
<div class="form-group">
<label>Skills 路径</label>
<input type="text" id="skillPath" placeholder="skills" value="skills" />
<small style="color: #7f8c8d;">仓库中技能文件所在的子目录</small>
</div>
<div class="form-group">
<label>本地目录名</label>
<input type="text" id="skillDirectory" placeholder="my-skills" />
<small style="color: #7f8c8d;">克隆到本地的目录名(可选)</small>
</div>
<button class="btn" onclick="generateSkillLink()">🎯 生成 Skill 深链接</button>
<div id="skillResult" class="result-section" style="display: none;">
<label>生成的深链接:</label>
<div class="result-url" id="skillUrl" onclick="selectText(this)"></div>
<div style="display: flex; gap: 10px; margin-top: 12px;">
<button class="btn" onclick="copyGeneratedLink('skillUrl')">📋 复制链接</button>
<a id="skillImportBtn" class="btn"
style="background: linear-gradient(135deg, #27ae60 0%, #229954 100%); text-decoration: none;">
📥 立即导入
</a>
</div>
</div>
</div>
</div>
<!-- Base64 编解码器 -->
<div class="section">
<h2>🔐 Base64 编解码器</h2>
<div class="generator-section" style="background: #f5f5f5; border: 2px solid #95a5a6;">
<h3 style="color: #2c3e50; margin-bottom: 16px;">编码器 (UTF-8 → Base64)</h3>
<div class="form-group">
<label>原始内容(UTF-8 文本)</label>
<textarea id="encodeInput" rows="6" placeholder="输入要编码的文本... 支持中文、JSON、TOML 等所有 UTF-8 字符"
style="font-family: monospace; font-size: 13px;"></textarea>
</div>
<button class="btn" style="background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);"
onclick="encodeToBase64()">
🔒 编码为 Base64
</button>
<div id="encodeResult" style="display: none;" class="result-box">
<strong>✅ 编码结果:</strong>
<div class="result-text" id="encodeOutput"></div>
<button class="btn btn-copy" onclick="copyEncoded()">📋 复制结果</button>
<small style="color: #7f8c8d; display: block; margin-top: 8px;">
💡 可直接用于深链接的 config 或 content 参数
</small>
</div>
</div>
<div class="generator-section"
style="background: #f0f9ff; border: 2px solid #3498db; margin-top: 24px;">
<h3 style="color: #2c3e50; margin-bottom: 16px;">解码器 (Base64 → UTF-8)</h3>
<div class="form-group">
<label>Base64 编码内容</label>
<textarea id="decodeInput" rows="6"
placeholder="粘贴 Base64 编码的文本... 例如: eyJlbnYiOnsiaGVsbG8iOiJ3b3JsZCJ9fQ=="
style="font-family: monospace; font-size: 13px;"></textarea>
</div>
<button class="btn" style="background: linear-gradient(135deg, #27ae60 0%, #229954 100%);"
onclick="decodeFromBase64()">
🔓 解码为文本
</button>
<div id="decodeResult" style="display: none;" class="result-box">
<strong>✅ 解码结果:</strong>
<div class="result-text" id="decodeOutput" style="white-space: pre-wrap;"></div>
<button class="btn btn-copy" onclick="copyDecoded()">📋 复制结果</button>
<div id="jsonFormat" style="display: none; margin-top: 12px;">
<button class="btn" style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%);"
onclick="formatJson()">
✨ 格式化 JSON
</button>
</div>
</div>
</div>
<div class="info-box" style="margin-top: 24px; background: #e8f4f8; border-left: 4px solid #3498db;">
<h4 style="color: #2c3e50;">💡 使用建议</h4>
<ul style="color: #2c3e50;">
<li><strong>编码配置文件</strong>:将 JSON 或 TOML 内容编码后用于 config 参数</li>
<li><strong>编码 Prompt</strong>:将 Markdown 提示词内容编码后用于 content 参数</li>
<li><strong>验证深链接</strong>:解码验证深链接中的配置内容是否正确</li>
<li><strong>UTF-8 支持</strong>:完整支持中文及其他 Unicode 字符</li>
</ul>
</div>
</div>
<!-- 注意事项 -->
<div class="info-box">
<h4>⚠️ 使用注意事项</h4>
<ul>
<li><strong>首次点击</strong>:浏览器会询问是否允许打开 CC Switch,请点击"允许"或"打开"</li>
<li><strong>macOS 用户</strong>:可能需要在"系统设置" → "隐私与安全性"中允许应用</li>
<li><strong>测试 API Key</strong>:示例中的 API Key 仅用于测试格式,无法实际使用</li>
<li><strong>导入确认</strong>:点击链接后会弹出确认对话框,API Key 会被掩码显示(前4位+****)</li>
<li><strong>编辑配置</strong>:导入后可以在 CC Switch 中随时编辑或删除配置</li>
</ul>
</div>
<!-- 深链接解析器 -->
<div class="generator-section" style="background: #f0f9ff; border: 2px solid #3498db;">
<h2>🔍 深链接解析器</h2>
<p style="color: #7f8c8d; margin-bottom: 24px;">粘贴深链接 URL,查看解析结果</p>
<div class="form-group">
<label>深链接 URL</label>
<textarea id="parseUrl" rows="3" placeholder="粘贴完整的 ccswitch:// 深链接..."
style="font-family: monospace; font-size: 13px;"></textarea>
</div>
<button class="btn" style="background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);"
onclick="parseDeepLink()">
🔍 解析深链接
</button>
<!-- 解析结果 -->
<div id="parseResult" style="display: none;">
<div class="result-box" style="margin-top: 20px;">
<strong>✅ 解析结果:</strong>
<!-- 基本信息 -->
<div id="parseBasicInfo" style="margin-top: 16px;"></div>
<!-- URL 参数 -->
<div id="parseUrlParams" style="margin-top: 16px;"></div>
<!-- 配置文件解析 -->
<div id="parseConfigContent" style="margin-top: 16px;"></div>
</div>
</div>
</div>
<!-- 深链接生成器 -->
<div class="generator-section">
<h2>🛠️ 深链接生成器</h2>
<p style="color: #7f8c8d; margin-bottom: 24px;">填写下方表单,生成您自己的深链接</p>
<!-- 导入模式切换 -->
<div class="form-group">
<label>导入模式</label>
<select id="importMode" onchange="toggleImportMode()">
<option value="url">URL 参数模式(传统)</option>
<option value="config">配置文件模式(v3.8+)</option>
</select>
<small style="color: #7f8c8d; font-size: 12px; display: block; margin-top: 4px;">
URL 参数模式:直接在 URL 中传递参数 | 配置文件模式:使用 Base64 编码的 JSON/TOML
</small>
</div>
<div class="form-group">
<label>应用类型 *</label>
<select id="app" onchange="updateModelFields()">
<option value="claude">Claude Code</option>
<option value="codex">Codex</option>
<option value="gemini">Gemini</option>
</select>
</div>
<div class="form-group">
<label>供应商名称 *</label>
<input type="text" id="name" placeholder="例如: Claude Official">
<small style="color: #e74c3c; font-size: 12px; display: block; margin-top: 4px;">
⚠️ 唯一必填项
</small>
</div>
<!-- URL 参数模式字段 -->
<div id="urlModeFields">
<div class="form-group">
<label>官网地址</label>
<input type="url" id="homepage" placeholder="https://example.com(可选,配置文件模式可自动推断)">
</div>
<div class="form-group">
<label>API 端点</label>
<input type="url" id="endpoint" placeholder="https://api.example.com/v1(配置文件模式可从配置提取)">
<small style="color: #7f8c8d; font-size: 11px; display: block; margin-top: 4px;">
主 API 端点地址
</small>
</div>
<div class="form-group">
<label>备用 API 端点(可选,逗号分隔)</label>
<input type="text" id="extraEndpoints" placeholder="https://api2.example.com/v1, https://api3.example.com/v1">
<small style="color: #7f8c8d; font-size: 11px; display: block; margin-top: 4px;">
多个备用端点用逗号分隔,导入后自动添加为自定义端点
</small>
</div>
<div class="form-group">
<label>API Key</label>
<input type="text" id="apiKey" placeholder="sk-xxxxx 或 AIzaSyXXXXX(配置文件模式可从配置提取)">
</div>
</div>
<!-- 配置文件模式字段 -->
<div id="configModeFields" style="display: none;">
<!-- 通用/Claude/Gemini JSON 配置 -->
<div id="generalConfigGroup" class="form-group">
<label id="configJsonLabel">配置文件内容(JSON)</label>
<textarea id="configJson" rows="12"
placeholder='输入 JSON 配置,例如: { "env": { "ANTHROPIC_AUTH_TOKEN": "sk-ant-xxx", "ANTHROPIC_BASE_URL": "https://api.anthropic.com/v1", "ANTHROPIC_MODEL": "claude-sonnet-4.5" } }'></textarea>
<small style="color: #7f8c8d; font-size: 12px; display: block; margin-top: 4px;">
配置文件将自动进行 Base64 编码
</small>
</div>
<!-- Codex 专用配置字段 -->
<div id="codexConfigGroup" style="display: none;">
<div class="form-group">
<label>Codex 认证信息 (JSON)</label>
<textarea id="codexAuthJson" rows="5"
placeholder='{ "auth": { "OPENAI_API_KEY": "sk-..." } }'></textarea>
<small style="color: #7f8c8d; font-size: 12px; display: block; margin-top: 4px;">
包含 API Key 的认证信息 JSON 对象
</small>
</div>
<div class="form-group">
<label>Codex 配置文件 (TOML)</label>
<textarea id="codexConfigToml" rows="10"
placeholder='[model_providers.openai] base_url = "..." [general] model = "..."'
style="font-family: monospace;"></textarea>
<small style="color: #7f8c8d; font-size: 12px; display: block; margin-top: 4px;">
config.toml 的原始内容
</small>
</div>
</div>
<div class="form-group">
<label>URL 参数覆盖(可选)</label>
<div style="background: #fff3cd; padding: 12px; border-radius: 8px; margin-bottom: 8px;">
<p style="font-size: 12px; color: #856404; margin: 0;">
💡 可以在下方填写 API Key、端点等参数来覆盖配置文件中的值。留空则完全使用配置文件。
</p>
</div>
<input type="text" id="overrideApiKey" placeholder="覆盖配置文件中的 API Key(可选)">
<input type="url" id="overrideEndpoint" placeholder="覆盖配置文件中的端点(可选)" style="margin-top: 8px;">
</div>
</div>
<div class="form-group">
<label>默认模型(可选)</label>
<input type="text" id="model" placeholder="例如: claude-haiku-4.1, gpt-5.1, gemini-3-pro-preview">
<small style="color: #7f8c8d; font-size: 12px; display: block; margin-top: 4px;">
通用模型字段,适用于所有应用类型
</small>
</div>
<!-- Claude 专用字段 -->
<div id="claudeFields" style="display: block;">
<div style="background: #e8f4f8; padding: 16px; border-radius: 8px; margin: 16px 0;">
<h4 style="color: #2c3e50; margin-bottom: 12px; font-size: 14px;">
📋 Claude 专用模型字段(可选)
gitextract_y5v1mptu/ ├── .gitattributes ├── .gitignore ├── .node-version ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README_JA.md ├── README_ZH.md ├── cc-switch-main/ │ └── src/ │ └── config/ │ └── universalProviderPresets.ts ├── components.json ├── deplink.html ├── docs/ │ ├── proxy-guide-zh.md │ ├── release-notes/ │ │ ├── v3.10.0-en.md │ │ ├── v3.10.0-ja.md │ │ ├── v3.10.0-zh.md │ │ ├── v3.11.0-en.md │ │ ├── v3.11.0-ja.md │ │ ├── v3.11.0-zh.md │ │ ├── v3.11.1-en.md │ │ ├── v3.11.1-ja.md │ │ ├── v3.11.1-zh.md │ │ ├── v3.12.0-en.md │ │ ├── v3.12.0-ja.md │ │ ├── v3.12.0-zh.md │ │ ├── v3.12.1-en.md │ │ ├── v3.12.1-ja.md │ │ ├── v3.12.1-zh.md │ │ ├── v3.12.2-en.md │ │ ├── v3.12.2-ja.md │ │ ├── v3.12.2-zh.md │ │ ├── v3.12.3-en.md │ │ ├── v3.12.3-ja.md │ │ ├── v3.12.3-zh.md │ │ ├── v3.6.0-en.md │ │ ├── v3.6.0-zh.md │ │ ├── v3.6.1-en.md │ │ ├── v3.6.1-zh.md │ │ ├── v3.7.0-en.md │ │ ├── v3.7.0-zh.md │ │ ├── v3.7.1-en.md │ │ ├── v3.7.1-zh.md │ │ ├── v3.8.0-en.md │ │ ├── v3.8.0-ja.md │ │ ├── v3.8.0-zh.md │ │ ├── v3.9.0-en.md │ │ ├── v3.9.0-ja.md │ │ └── v3.9.0-zh.md │ └── user-manual/ │ ├── README.md │ ├── en/ │ │ ├── 1-getting-started/ │ │ │ ├── 1.1-introduction.md │ │ │ ├── 1.2-installation.md │ │ │ ├── 1.3-interface.md │ │ │ ├── 1.4-quickstart.md │ │ │ └── 1.5-settings.md │ │ ├── 2-providers/ │ │ │ ├── 2.1-add.md │ │ │ ├── 2.2-switch.md │ │ │ ├── 2.3-edit.md │ │ │ ├── 2.4-sort-duplicate.md │ │ │ └── 2.5-usage-query.md │ │ ├── 3-extensions/ │ │ │ ├── 3.1-mcp.md │ │ │ ├── 3.2-prompts.md │ │ │ └── 3.3-skills.md │ │ ├── 4-proxy/ │ │ │ ├── 4.1-service.md │ │ │ ├── 4.2-takeover.md │ │ │ ├── 4.3-failover.md │ │ │ ├── 4.4-usage.md │ │ │ └── 4.5-model-test.md │ │ ├── 5-faq/ │ │ │ ├── 5.1-config-files.md │ │ │ ├── 5.2-questions.md │ │ │ ├── 5.3-deeplink.md │ │ │ └── 5.4-env-conflict.md │ │ └── README.md │ ├── ja/ │ │ ├── 1-getting-started/ │ │ │ ├── 1.1-introduction.md │ │ │ ├── 1.2-installation.md │ │ │ ├── 1.3-interface.md │ │ │ ├── 1.4-quickstart.md │ │ │ └── 1.5-settings.md │ │ ├── 2-providers/ │ │ │ ├── 2.1-add.md │ │ │ ├── 2.2-switch.md │ │ │ ├── 2.3-edit.md │ │ │ ├── 2.4-sort-duplicate.md │ │ │ └── 2.5-usage-query.md │ │ ├── 3-extensions/ │ │ │ ├── 3.1-mcp.md │ │ │ ├── 3.2-prompts.md │ │ │ └── 3.3-skills.md │ │ ├── 4-proxy/ │ │ │ ├── 4.1-service.md │ │ │ ├── 4.2-takeover.md │ │ │ ├── 4.3-failover.md │ │ │ ├── 4.4-usage.md │ │ │ └── 4.5-model-test.md │ │ ├── 5-faq/ │ │ │ ├── 5.1-config-files.md │ │ │ ├── 5.2-questions.md │ │ │ ├── 5.3-deeplink.md │ │ │ └── 5.4-env-conflict.md │ │ └── README.md │ └── zh/ │ ├── 1-getting-started/ │ │ ├── 1.1-introduction.md │ │ ├── 1.2-installation.md │ │ ├── 1.3-interface.md │ │ ├── 1.4-quickstart.md │ │ └── 1.5-settings.md │ ├── 2-providers/ │ │ ├── 2.1-add.md │ │ ├── 2.2-switch.md │ │ ├── 2.3-edit.md │ │ ├── 2.4-sort-duplicate.md │ │ └── 2.5-usage-query.md │ ├── 3-extensions/ │ │ ├── 3.1-mcp.md │ │ ├── 3.2-prompts.md │ │ └── 3.3-skills.md │ ├── 4-proxy/ │ │ ├── 4.1-service.md │ │ ├── 4.2-takeover.md │ │ ├── 4.3-failover.md │ │ ├── 4.4-usage.md │ │ └── 4.5-model-test.md │ ├── 5-faq/ │ │ ├── 5.1-config-files.md │ │ ├── 5.2-questions.md │ │ ├── 5.3-deeplink.md │ │ └── 5.4-env-conflict.md │ └── README.md ├── flatpak/ │ ├── README.md │ ├── com.ccswitch.desktop.desktop │ ├── com.ccswitch.desktop.metainfo.xml │ └── com.ccswitch.desktop.yml ├── package.json ├── pnpm-workspace.yaml ├── postcss.config.cjs ├── scripts/ │ ├── extract-icons.js │ ├── filter-icons.js │ └── generate-icon-index.js ├── session-manager.md ├── src/ │ ├── App.tsx │ ├── components/ │ │ ├── AppSwitcher.tsx │ │ ├── BrandIcons.tsx │ │ ├── ColorPicker.tsx │ │ ├── ConfirmDialog.tsx │ │ ├── DeepLinkImportDialog.tsx │ │ ├── IconPicker.tsx │ │ ├── JsonEditor.tsx │ │ ├── MarkdownEditor.tsx │ │ ├── ProviderIcon.tsx │ │ ├── UpdateBadge.tsx │ │ ├── UsageFooter.tsx │ │ ├── UsageScriptModal.tsx │ │ ├── agents/ │ │ │ └── AgentsPanel.tsx │ │ ├── common/ │ │ │ ├── AppCountBar.tsx │ │ │ ├── AppToggleGroup.tsx │ │ │ ├── FullScreenPanel.tsx │ │ │ └── ListItemRow.tsx │ │ ├── deeplink/ │ │ │ ├── McpConfirmation.tsx │ │ │ ├── PromptConfirmation.tsx │ │ │ └── SkillConfirmation.tsx │ │ ├── env/ │ │ │ └── EnvWarningBanner.tsx │ │ ├── icons/ │ │ │ └── TerminalIcons.tsx │ │ ├── mcp/ │ │ │ ├── McpFormModal.tsx │ │ │ ├── McpWizardModal.tsx │ │ │ ├── UnifiedMcpPanel.tsx │ │ │ └── useMcpValidation.ts │ │ ├── mode-toggle.tsx │ │ ├── openclaw/ │ │ │ ├── AgentsDefaultsPanel.tsx │ │ │ ├── EnvPanel.tsx │ │ │ ├── OpenClawHealthBanner.tsx │ │ │ ├── ToolsPanel.tsx │ │ │ ├── hooks/ │ │ │ │ └── useOpenClawModelOptions.ts │ │ │ └── utils.ts │ │ ├── prompts/ │ │ │ ├── PromptFormModal.tsx │ │ │ ├── PromptFormPanel.tsx │ │ │ ├── PromptListItem.tsx │ │ │ ├── PromptPanel.tsx │ │ │ └── PromptToggle.tsx │ │ ├── providers/ │ │ │ ├── AddProviderDialog.tsx │ │ │ ├── EditProviderDialog.tsx │ │ │ ├── FailoverPriorityBadge.tsx │ │ │ ├── HealthStatusIndicator.tsx │ │ │ ├── ProviderActions.tsx │ │ │ ├── ProviderCard.tsx │ │ │ ├── ProviderEmptyState.tsx │ │ │ ├── ProviderHealthBadge.tsx │ │ │ ├── ProviderList.tsx │ │ │ └── forms/ │ │ │ ├── ApiKeyInput.tsx │ │ │ ├── BasicFormFields.tsx │ │ │ ├── ClaudeFormFields.tsx │ │ │ ├── CodexCommonConfigModal.tsx │ │ │ ├── CodexConfigEditor.tsx │ │ │ ├── CodexConfigSections.tsx │ │ │ ├── CodexFormFields.tsx │ │ │ ├── CommonConfigEditor.tsx │ │ │ ├── CopilotAuthSection.tsx │ │ │ ├── EndpointSpeedTest.tsx │ │ │ ├── GeminiCommonConfigModal.tsx │ │ │ ├── GeminiConfigEditor.tsx │ │ │ ├── GeminiConfigSections.tsx │ │ │ ├── GeminiFormFields.tsx │ │ │ ├── OmoFormFields.tsx │ │ │ ├── OpenClawFormFields.tsx │ │ │ ├── OpenCodeFormFields.tsx │ │ │ ├── ProviderAdvancedConfig.tsx │ │ │ ├── ProviderForm.tsx │ │ │ ├── ProviderPresetSelector.tsx │ │ │ ├── helpers/ │ │ │ │ └── opencodeFormUtils.ts │ │ │ ├── hooks/ │ │ │ │ ├── index.ts │ │ │ │ ├── useApiKeyLink.ts │ │ │ │ ├── useApiKeyState.ts │ │ │ │ ├── useBaseUrlState.ts │ │ │ │ ├── useCodexCommonConfig.ts │ │ │ │ ├── useCodexConfigState.ts │ │ │ │ ├── useCodexTomlValidation.ts │ │ │ │ ├── useCommonConfigSnippet.ts │ │ │ │ ├── useCopilotAuth.ts │ │ │ │ ├── useCustomEndpoints.ts │ │ │ │ ├── useGeminiCommonConfig.ts │ │ │ │ ├── useGeminiConfigState.ts │ │ │ │ ├── useManagedAuth.ts │ │ │ │ ├── useModelState.ts │ │ │ │ ├── useOmoDraftState.ts │ │ │ │ ├── useOmoModelSource.ts │ │ │ │ ├── useOpenclawFormState.ts │ │ │ │ ├── useOpencodeFormState.ts │ │ │ │ ├── useProviderCategory.ts │ │ │ │ ├── useSpeedTestEndpoints.ts │ │ │ │ └── useTemplateValues.ts │ │ │ └── shared/ │ │ │ ├── ApiKeySection.tsx │ │ │ ├── EndpointField.tsx │ │ │ └── index.ts │ │ ├── proxy/ │ │ │ ├── AutoFailoverConfigPanel.tsx │ │ │ ├── CircuitBreakerConfigPanel.tsx │ │ │ ├── FailoverQueueManager.tsx │ │ │ ├── FailoverToggle.tsx │ │ │ ├── ProxyPanel.tsx │ │ │ ├── ProxyToggle.tsx │ │ │ └── index.ts │ │ ├── sessions/ │ │ │ ├── SessionItem.tsx │ │ │ ├── SessionManagerPage.tsx │ │ │ ├── SessionMessageItem.tsx │ │ │ ├── SessionToc.tsx │ │ │ └── utils.ts │ │ ├── settings/ │ │ │ ├── AboutSection.tsx │ │ │ ├── AppVisibilitySettings.tsx │ │ │ ├── AuthCenterPanel.tsx │ │ │ ├── BackupListSection.tsx │ │ │ ├── DirectorySettings.tsx │ │ │ ├── GlobalProxySettings.tsx │ │ │ ├── ImportExportSection.tsx │ │ │ ├── LanguageSettings.tsx │ │ │ ├── LogConfigPanel.tsx │ │ │ ├── ProxyTabContent.tsx │ │ │ ├── RectifierConfigPanel.tsx │ │ │ ├── SettingsPage.tsx │ │ │ ├── SkillSyncMethodSettings.tsx │ │ │ ├── TerminalSettings.tsx │ │ │ ├── ThemeSettings.tsx │ │ │ ├── WebdavSyncSection.tsx │ │ │ └── WindowSettings.tsx │ │ ├── skills/ │ │ │ ├── RepoManager.tsx │ │ │ ├── RepoManagerPanel.tsx │ │ │ ├── SkillCard.tsx │ │ │ ├── SkillsPage.tsx │ │ │ └── UnifiedSkillsPanel.tsx │ │ ├── theme-provider.tsx │ │ ├── ui/ │ │ │ ├── accordion.tsx │ │ │ ├── alert.tsx │ │ │ ├── badge.tsx │ │ │ ├── button.tsx │ │ │ ├── card.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── command.tsx │ │ │ ├── dialog.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── form.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── popover.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── sonner.tsx │ │ │ ├── switch.tsx │ │ │ ├── table.tsx │ │ │ ├── tabs.tsx │ │ │ ├── textarea.tsx │ │ │ ├── toggle-row.tsx │ │ │ └── tooltip.tsx │ │ ├── universal/ │ │ │ ├── UniversalProviderCard.tsx │ │ │ ├── UniversalProviderFormModal.tsx │ │ │ ├── UniversalProviderPanel.tsx │ │ │ └── index.ts │ │ ├── usage/ │ │ │ ├── ModelStatsTable.tsx │ │ │ ├── ModelTestConfigPanel.tsx │ │ │ ├── PricingConfigPanel.tsx │ │ │ ├── PricingEditModal.tsx │ │ │ ├── ProviderStatsTable.tsx │ │ │ ├── RequestDetailPanel.tsx │ │ │ ├── RequestLogTable.tsx │ │ │ ├── UsageDashboard.tsx │ │ │ ├── UsageSummaryCards.tsx │ │ │ ├── UsageTrendChart.tsx │ │ │ └── format.ts │ │ └── workspace/ │ │ ├── DailyMemoryPanel.tsx │ │ ├── WorkspaceFileEditor.tsx │ │ └── WorkspaceFilesPanel.tsx │ ├── config/ │ │ ├── appConfig.tsx │ │ ├── claudeProviderPresets.ts │ │ ├── codexProviderPresets.ts │ │ ├── codexTemplates.ts │ │ ├── constants.ts │ │ ├── geminiProviderPresets.ts │ │ ├── iconInference.ts │ │ ├── mcpPresets.ts │ │ ├── openclawProviderPresets.ts │ │ ├── opencodeProviderPresets.ts │ │ └── universalProviderPresets.ts │ ├── contexts/ │ │ └── UpdateContext.tsx │ ├── hooks/ │ │ ├── useAutoCompact.ts │ │ ├── useBackupManager.ts │ │ ├── useDirectorySettings.ts │ │ ├── useDragSort.ts │ │ ├── useGlobalProxy.ts │ │ ├── useImportExport.ts │ │ ├── useLastValidValue.ts │ │ ├── useMcp.ts │ │ ├── useOpenClaw.ts │ │ ├── usePromptActions.ts │ │ ├── useProviderActions.ts │ │ ├── useProxyConfig.ts │ │ ├── useProxyStatus.ts │ │ ├── useSessionSearch.ts │ │ ├── useSettings.ts │ │ ├── useSettingsForm.ts │ │ ├── useSettingsMetadata.ts │ │ ├── useSkills.ts │ │ └── useStreamCheck.ts │ ├── i18n/ │ │ ├── index.ts │ │ └── locales/ │ │ ├── en.json │ │ ├── ja.json │ │ └── zh.json │ ├── icons/ │ │ └── extracted/ │ │ ├── index.ts │ │ └── metadata.ts │ ├── index.css │ ├── index.html │ ├── lib/ │ │ ├── api/ │ │ │ ├── auth.ts │ │ │ ├── config.ts │ │ │ ├── copilot.ts │ │ │ ├── deeplink.ts │ │ │ ├── env.ts │ │ │ ├── failover.ts │ │ │ ├── globalProxy.ts │ │ │ ├── index.ts │ │ │ ├── mcp.ts │ │ │ ├── model-test.ts │ │ │ ├── omo.ts │ │ │ ├── openclaw.ts │ │ │ ├── prompts.ts │ │ │ ├── providers.ts │ │ │ ├── proxy.ts │ │ │ ├── sessions.ts │ │ │ ├── settings.ts │ │ │ ├── skills.ts │ │ │ ├── types.ts │ │ │ ├── usage.ts │ │ │ ├── vscode.ts │ │ │ └── workspace.ts │ │ ├── authBinding.ts │ │ ├── errors/ │ │ │ └── skillErrorParser.ts │ │ ├── platform.ts │ │ ├── query/ │ │ │ ├── failover.ts │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ ├── omo.ts │ │ │ ├── proxy.ts │ │ │ ├── queries.ts │ │ │ ├── queryClient.ts │ │ │ └── usage.ts │ │ ├── schemas/ │ │ │ ├── common.ts │ │ │ ├── mcp.ts │ │ │ ├── provider.ts │ │ │ └── settings.ts │ │ ├── updater.ts │ │ ├── utils/ │ │ │ └── base64.ts │ │ └── utils.ts │ ├── main.tsx │ ├── types/ │ │ ├── env.ts │ │ ├── icon.ts │ │ ├── omo.ts │ │ ├── proxy.ts │ │ └── usage.ts │ ├── types.ts │ ├── utils/ │ │ ├── domUtils.ts │ │ ├── errorUtils.ts │ │ ├── formatters.ts │ │ ├── postChangeSync.ts │ │ ├── providerConfigUtils.ts │ │ ├── providerMetaUtils.ts │ │ ├── textNormalization.ts │ │ ├── tomlUtils.ts │ │ └── uuid.ts │ └── vite-env.d.ts ├── src-tauri/ │ ├── .gitignore │ ├── Cargo.toml │ ├── Info.plist │ ├── build.rs │ ├── capabilities/ │ │ └── default.json │ ├── common-controls.manifest │ ├── icons/ │ │ └── icon.icns │ ├── src/ │ │ ├── app_config.rs │ │ ├── app_store.rs │ │ ├── auto_launch.rs │ │ ├── claude_mcp.rs │ │ ├── claude_plugin.rs │ │ ├── codex_config.rs │ │ ├── commands/ │ │ │ ├── auth.rs │ │ │ ├── config.rs │ │ │ ├── copilot.rs │ │ │ ├── deeplink.rs │ │ │ ├── env.rs │ │ │ ├── failover.rs │ │ │ ├── global_proxy.rs │ │ │ ├── import_export.rs │ │ │ ├── mcp.rs │ │ │ ├── misc.rs │ │ │ ├── mod.rs │ │ │ ├── omo.rs │ │ │ ├── openclaw.rs │ │ │ ├── plugin.rs │ │ │ ├── prompt.rs │ │ │ ├── provider.rs │ │ │ ├── proxy.rs │ │ │ ├── session_manager.rs │ │ │ ├── settings.rs │ │ │ ├── skill.rs │ │ │ ├── stream_check.rs │ │ │ ├── sync_support.rs │ │ │ ├── usage.rs │ │ │ ├── webdav_sync.rs │ │ │ └── workspace.rs │ │ ├── config.rs │ │ ├── database/ │ │ │ ├── backup.rs │ │ │ ├── dao/ │ │ │ │ ├── failover.rs │ │ │ │ ├── mcp.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── prompts.rs │ │ │ │ ├── providers.rs │ │ │ │ ├── proxy.rs │ │ │ │ ├── settings.rs │ │ │ │ ├── skills.rs │ │ │ │ ├── stream_check.rs │ │ │ │ ├── universal_providers.rs │ │ │ │ └── usage_rollup.rs │ │ │ ├── migration.rs │ │ │ ├── mod.rs │ │ │ ├── schema.rs │ │ │ └── tests.rs │ │ ├── deeplink/ │ │ │ ├── mcp.rs │ │ │ ├── mod.rs │ │ │ ├── parser.rs │ │ │ ├── prompt.rs │ │ │ ├── provider.rs │ │ │ ├── skill.rs │ │ │ ├── tests.rs │ │ │ └── utils.rs │ │ ├── error.rs │ │ ├── gemini_config.rs │ │ ├── gemini_mcp.rs │ │ ├── init_status.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── mcp/ │ │ │ ├── claude.rs │ │ │ ├── codex.rs │ │ │ ├── gemini.rs │ │ │ ├── mod.rs │ │ │ ├── opencode.rs │ │ │ └── validation.rs │ │ ├── openclaw_config.rs │ │ ├── opencode_config.rs │ │ ├── panic_hook.rs │ │ ├── prompt.rs │ │ ├── prompt_files.rs │ │ ├── provider.rs │ │ ├── provider_defaults.rs │ │ ├── proxy/ │ │ │ ├── body_filter.rs │ │ │ ├── cache_injector.rs │ │ │ ├── circuit_breaker.rs │ │ │ ├── error.rs │ │ │ ├── error_mapper.rs │ │ │ ├── failover_switch.rs │ │ │ ├── forwarder.rs │ │ │ ├── handler_config.rs │ │ │ ├── handler_context.rs │ │ │ ├── handlers.rs │ │ │ ├── health.rs │ │ │ ├── http_client.rs │ │ │ ├── log_codes.rs │ │ │ ├── mod.rs │ │ │ ├── model_mapper.rs │ │ │ ├── provider_router.rs │ │ │ ├── providers/ │ │ │ │ ├── adapter.rs │ │ │ │ ├── auth.rs │ │ │ │ ├── claude.rs │ │ │ │ ├── codex.rs │ │ │ │ ├── copilot_auth.rs │ │ │ │ ├── gemini.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── models/ │ │ │ │ │ ├── anthropic.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── openai.rs │ │ │ │ ├── streaming.rs │ │ │ │ ├── streaming_responses.rs │ │ │ │ ├── transform.rs │ │ │ │ └── transform_responses.rs │ │ │ ├── response_handler.rs │ │ │ ├── response_processor.rs │ │ │ ├── server.rs │ │ │ ├── session.rs │ │ │ ├── thinking_budget_rectifier.rs │ │ │ ├── thinking_optimizer.rs │ │ │ ├── thinking_rectifier.rs │ │ │ ├── types.rs │ │ │ └── usage/ │ │ │ ├── calculator.rs │ │ │ ├── logger.rs │ │ │ ├── mod.rs │ │ │ └── parser.rs │ │ ├── services/ │ │ │ ├── config.rs │ │ │ ├── env_checker.rs │ │ │ ├── env_manager.rs │ │ │ ├── mcp.rs │ │ │ ├── mod.rs │ │ │ ├── omo.rs │ │ │ ├── prompt.rs │ │ │ ├── provider/ │ │ │ │ ├── endpoints.rs │ │ │ │ ├── gemini_auth.rs │ │ │ │ ├── live.rs │ │ │ │ ├── mod.rs │ │ │ │ └── usage.rs │ │ │ ├── proxy.rs │ │ │ ├── skill.rs │ │ │ ├── speedtest.rs │ │ │ ├── stream_check.rs │ │ │ ├── usage_stats.rs │ │ │ ├── webdav.rs │ │ │ ├── webdav_auto_sync.rs │ │ │ ├── webdav_sync/ │ │ │ │ └── archive.rs │ │ │ └── webdav_sync.rs │ │ ├── session_manager/ │ │ │ ├── mod.rs │ │ │ ├── providers/ │ │ │ │ ├── claude.rs │ │ │ │ ├── codex.rs │ │ │ │ ├── gemini.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── openclaw.rs │ │ │ │ ├── opencode.rs │ │ │ │ └── utils.rs │ │ │ └── terminal/ │ │ │ └── mod.rs │ │ ├── settings.rs │ │ ├── store.rs │ │ ├── tray.rs │ │ └── usage_script.rs │ ├── tauri.conf.json │ ├── tauri.windows.conf.json │ ├── tests/ │ │ ├── app_config_load.rs │ │ ├── app_type_parse.rs │ │ ├── deeplink_import.rs │ │ ├── import_export_sync.rs │ │ ├── mcp_commands.rs │ │ ├── provider_commands.rs │ │ ├── provider_service.rs │ │ ├── proxy_commands.rs │ │ ├── skill_sync.rs │ │ └── support.rs │ └── wix/ │ └── per-user-main.wxs ├── tailwind.config.cjs ├── tests/ │ ├── components/ │ │ ├── AddProviderDialog.test.tsx │ │ ├── CommonConfigModalBehavior.test.tsx │ │ ├── GlobalProxySettings.test.tsx │ │ ├── ImportExportSection.test.tsx │ │ ├── McpFormModal.test.tsx │ │ ├── OmoFormFields.mergeCustomModelsIntoStore.test.ts │ │ ├── ProviderList.test.tsx │ │ ├── SessionManagerPage.test.tsx │ │ ├── SettingsDialog.test.tsx │ │ ├── UnifiedSkillsPanel.test.tsx │ │ ├── WebdavSyncSection.test.tsx │ │ └── openclaw.utils.test.ts │ ├── config/ │ │ ├── claudeProviderPresets.test.ts │ │ └── opencodeProviderPresets.test.ts │ ├── hooks/ │ │ ├── useCommonConfigSave.test.tsx │ │ ├── useDirectorySettings.test.tsx │ │ ├── useDragSort.test.tsx │ │ ├── useImportExport.extra.test.tsx │ │ ├── useImportExport.test.tsx │ │ ├── useMcpValidation.test.tsx │ │ ├── useProviderActions.test.tsx │ │ ├── useProxyStatus.test.tsx │ │ ├── useSettings.test.tsx │ │ ├── useSettingsForm.test.tsx │ │ └── useSettingsMetadata.test.tsx │ ├── integration/ │ │ ├── App.test.tsx │ │ └── SettingsDialog.test.tsx │ ├── msw/ │ │ ├── handlers.ts │ │ ├── server.ts │ │ ├── state.ts │ │ └── tauriMocks.ts │ ├── setupGlobals.ts │ ├── setupTests.ts │ └── utils/ │ ├── omoConfig.test.ts │ ├── providerConfigUtils.codex.test.ts │ ├── providerMetaUtils.test.ts │ └── testQueryClient.ts ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── vitest.config.ts
Showing preview only (342K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (3512 symbols across 396 files)
FILE: cc-switch-main/src/config/universalProviderPresets.ts
type UniversalProviderPreset (line 17) | interface UniversalProviderPreset {
constant NEWAPI_DEFAULT_MODELS (line 41) | const NEWAPI_DEFAULT_MODELS: UniversalProviderModels = {
constant N1N_DEFAULT_MODELS (line 57) | const N1N_DEFAULT_MODELS: UniversalProviderModels = {
function createUniversalProviderFromPreset (line 126) | function createUniversalProviderFromPreset(
function getPresetDisplayName (line 151) | function getPresetDisplayName(preset: UniversalProviderPreset): string {
function findPresetByType (line 158) | function findPresetByType(
FILE: scripts/extract-icons.js
constant ICONS_TO_EXTRACT (line 5) | const ICONS_TO_EXTRACT = {
constant ALL_ICONS (line 31) | const ALL_ICONS = [
constant OUTPUT_DIR (line 39) | const OUTPUT_DIR = path.join(__dirname, '../src/icons/extracted');
constant SOURCE_DIR (line 40) | const SOURCE_DIR = path.join(__dirname, '../node_modules/@lobehub/icons-...
FILE: scripts/filter-icons.js
constant ICONS_DIR (line 4) | const ICONS_DIR = path.join(__dirname, '../src/icons/extracted');
constant KEEP_LIST (line 8) | const KEEP_LIST = [
FILE: scripts/generate-icon-index.js
constant ICONS_DIR (line 4) | const ICONS_DIR = path.join(__dirname, '../src/icons/extracted');
constant INDEX_FILE (line 5) | const INDEX_FILE = path.join(ICONS_DIR, 'index.ts');
constant METADATA_FILE (line 6) | const METADATA_FILE = path.join(ICONS_DIR, 'metadata.ts');
constant KNOWN_METADATA (line 9) | const KNOWN_METADATA = {
FILE: src-tauri/build.rs
function main (line 1) | fn main() {
FILE: src-tauri/src/app_config.rs
type McpApps (line 9) | pub struct McpApps {
method is_enabled_for (line 22) | pub fn is_enabled_for(&self, app: &AppType) -> bool {
method set_enabled_for (line 33) | pub fn set_enabled_for(&mut self, app: &AppType, enabled: bool) {
method enabled_apps (line 44) | pub fn enabled_apps(&self) -> Vec<AppType> {
method is_empty (line 62) | pub fn is_empty(&self) -> bool {
type SkillApps (line 69) | pub struct SkillApps {
method is_enabled_for (line 82) | pub fn is_enabled_for(&self, app: &AppType) -> bool {
method set_enabled_for (line 93) | pub fn set_enabled_for(&mut self, app: &AppType, enabled: bool) {
method enabled_apps (line 104) | pub fn enabled_apps(&self) -> Vec<AppType> {
method is_empty (line 122) | pub fn is_empty(&self) -> bool {
method only (line 127) | pub fn only(app: &AppType) -> Self {
method from_labels (line 137) | pub fn from_labels(labels: &[String]) -> Self {
type InstalledSkill (line 151) | pub struct InstalledSkill {
type UnmanagedSkill (line 182) | pub struct UnmanagedSkill {
type McpServer (line 198) | pub struct McpServer {
type McpConfig (line 215) | pub struct McpConfig {
method is_empty (line 223) | pub fn is_empty(&self) -> bool {
type McpRoot (line 230) | pub struct McpRoot {
method default (line 251) | fn default() -> Self {
type PromptConfig (line 267) | pub struct PromptConfig {
type PromptRoot (line 274) | pub struct PromptRoot {
type AppType (line 295) | pub enum AppType {
method as_str (line 304) | pub fn as_str(&self) -> &str {
method is_additive_mode (line 318) | pub fn is_additive_mode(&self) -> bool {
method all (line 323) | pub fn all() -> impl Iterator<Item = AppType> {
type Err (line 336) | type Err = AppError;
method from_str (line 338) | fn from_str(s: &str) -> Result<Self, Self::Err> {
type CommonConfigSnippets (line 357) | pub struct CommonConfigSnippets {
method get (line 376) | pub fn get(&self, app: &AppType) -> Option<&String> {
method set (line 387) | pub fn set(&mut self, app: &AppType, snippet: Option<String>) {
type MultiAppConfig (line 400) | pub struct MultiAppConfig {
method load (line 450) | pub fn load() -> Result<Self, AppError> {
method save (line 555) | pub fn save(&self) -> Result<(), AppError> {
method get_manager (line 570) | pub fn get_manager(&self, app: &AppType) -> Option<&ProviderManager> {
method get_manager_mut (line 575) | pub fn get_manager_mut(&mut self, app: &AppType) -> Option<&mut Provid...
method ensure_app (line 580) | pub fn ensure_app(&mut self, app: &AppType) {
method mcp_for (line 588) | pub fn mcp_for(&self, app: &AppType) -> &McpConfig {
method mcp_for_mut (line 599) | pub fn mcp_for_mut(&mut self, app: &AppType) -> &mut McpConfig {
method default_with_auto_import (line 610) | fn default_with_auto_import() -> Result<Self, AppError> {
method maybe_auto_import_prompts_for_existing_config (line 635) | fn maybe_auto_import_prompts_for_existing_config(&mut self) -> Result<...
method auto_import_prompt_if_exists (line 670) | fn auto_import_prompt_if_exists(config: &mut Self, app: AppType) -> Re...
method migrate_mcp_to_unified (line 741) | pub fn migrate_mcp_to_unified(&mut self) -> Result<bool, AppError> {
function default_version (line 423) | fn default_version() -> u32 {
method default (line 428) | fn default() -> Self {
type TempHome (line 881) | struct TempHome {
method new (line 889) | fn new() -> Self {
method drop (line 906) | fn drop(&mut self) {
function write_prompt_file (line 919) | fn write_prompt_file(app: AppType, content: &str) {
function auto_imports_existing_prompt_when_config_missing (line 929) | fn auto_imports_existing_prompt_when_config_missing() {
function skips_empty_prompt_files_during_import (line 955) | fn skips_empty_prompt_files_during_import() {
function auto_import_happens_only_once (line 968) | fn auto_import_happens_only_once() {
function auto_imports_gemini_prompt_on_first_launch (line 1005) | fn auto_imports_gemini_prompt_on_first_launch() {
function auto_imports_all_three_apps_prompts (line 1029) | fn auto_imports_all_three_apps_prompts() {
FILE: src-tauri/src/app_store.rs
constant STORE_KEY_APP_CONFIG_DIR (line 9) | const STORE_KEY_APP_CONFIG_DIR: &str = "app_config_dir_override";
function override_cache (line 14) | fn override_cache() -> &'static RwLock<Option<PathBuf>> {
function update_cached_override (line 18) | fn update_cached_override(value: Option<PathBuf>) {
function get_app_config_dir_override (line 25) | pub fn get_app_config_dir_override() -> Option<PathBuf> {
function read_override_from_store (line 29) | fn read_override_from_store(app: &tauri::AppHandle) -> Option<PathBuf> {
function refresh_app_config_dir_override (line 67) | pub fn refresh_app_config_dir_override(app: &tauri::AppHandle) -> Option...
function set_app_config_dir_to_store (line 74) | pub fn set_app_config_dir_to_store(
function resolve_path (line 109) | fn resolve_path(raw: &str) -> PathBuf {
function migrate_app_config_dir_from_settings (line 128) | pub fn migrate_app_config_dir_from_settings(app: &tauri::AppHandle) -> R...
FILE: src-tauri/src/auto_launch.rs
function get_macos_app_bundle_path (line 7) | fn get_macos_app_bundle_path(exe_path: &std::path::Path) -> Option<std::...
function get_auto_launch (line 19) | fn get_auto_launch() -> Result<AutoLaunch, AppError> {
function enable_auto_launch (line 44) | pub fn enable_auto_launch() -> Result<(), AppError> {
function disable_auto_launch (line 54) | pub fn disable_auto_launch() -> Result<(), AppError> {
function is_auto_launch_enabled (line 64) | pub fn is_auto_launch_enabled() -> Result<bool, AppError> {
function test_get_macos_app_bundle_path_valid (line 77) | fn test_get_macos_app_bundle_path_valid() {
function test_get_macos_app_bundle_path_with_spaces (line 88) | fn test_get_macos_app_bundle_path_with_spaces() {
function test_get_macos_app_bundle_path_not_in_bundle (line 102) | fn test_get_macos_app_bundle_path_not_in_bundle() {
function test_get_macos_app_bundle_path_dev_build (line 110) | fn test_get_macos_app_bundle_path_dev_build() {
FILE: src-tauri/src/claude_mcp.rs
constant WINDOWS_WRAP_COMMANDS (line 13) | const WINDOWS_WRAP_COMMANDS: &[&str] = &["npx", "npm", "yarn", "pnpm", "...
function wrap_command_for_windows (line 18) | fn wrap_command_for_windows(obj: &mut Map<String, Value>) {
function wrap_command_for_windows (line 64) | fn wrap_command_for_windows(_obj: &mut Map<String, Value>) {
function is_wsl_path (line 72) | fn is_wsl_path(path: &Path) -> bool {
function is_wsl_path (line 88) | fn is_wsl_path(_path: &Path) -> bool {
type McpStatus (line 94) | pub struct McpStatus {
function user_config_path (line 100) | fn user_config_path() -> PathBuf {
function ensure_mcp_override_migrated (line 105) | fn ensure_mcp_override_migrated() {
function read_json_value (line 146) | fn read_json_value(path: &Path) -> Result<Value, AppError> {
function write_json_value (line 155) | fn write_json_value(path: &Path, value: &Value) -> Result<(), AppError> {
function get_mcp_status (line 164) | pub fn get_mcp_status() -> Result<McpStatus, AppError> {
function read_mcp_json (line 181) | pub fn read_mcp_json() -> Result<Option<String>, AppError> {
function set_has_completed_onboarding (line 192) | pub fn set_has_completed_onboarding() -> Result<bool, AppError> {
function clear_has_completed_onboarding (line 219) | pub fn clear_has_completed_onboarding() -> Result<bool, AppError> {
function upsert_mcp_server (line 239) | pub fn upsert_mcp_server(id: &str, spec: Value) -> Result<bool, AppError> {
function delete_mcp_server (line 311) | pub fn delete_mcp_server(id: &str) -> Result<bool, AppError> {
function validate_command_in_path (line 331) | pub fn validate_command_in_path(cmd: &str) -> Result<bool, AppError> {
function read_mcp_servers_map (line 369) | pub fn read_mcp_servers_map() -> Result<std::collections::HashMap<String...
function set_mcp_servers_map (line 387) | pub fn set_mcp_servers_map(
function test_wrap_command_for_windows_npx (line 456) | fn test_wrap_command_for_windows_npx() {
function test_wrap_command_for_windows_npm (line 480) | fn test_wrap_command_for_windows_npm() {
function test_wrap_command_for_windows_already_cmd (line 495) | fn test_wrap_command_for_windows_already_cmd() {
function test_wrap_command_for_windows_http_type_skipped (line 509) | fn test_wrap_command_for_windows_http_type_skipped() {
function test_wrap_command_for_windows_other_command_skipped (line 522) | fn test_wrap_command_for_windows_other_command_skipped() {
function test_wrap_command_for_windows_no_args (line 536) | fn test_wrap_command_for_windows_no_args() {
function test_wrap_command_for_windows_with_cmd_suffix (line 549) | fn test_wrap_command_for_windows_with_cmd_suffix() {
function test_wrap_command_for_windows_case_insensitive (line 565) | fn test_wrap_command_for_windows_case_insensitive() {
function test_is_wsl_path_wsl_dollar (line 582) | fn test_is_wsl_path_wsl_dollar() {
function test_is_wsl_path_wsl_localhost (line 605) | fn test_is_wsl_path_wsl_localhost() {
function test_is_wsl_path_case_insensitive (line 620) | fn test_is_wsl_path_case_insensitive() {
function test_is_wsl_path_non_wsl (line 632) | fn test_is_wsl_path_non_wsl() {
FILE: src-tauri/src/claude_plugin.rs
constant CLAUDE_DIR (line 6) | const CLAUDE_DIR: &str = ".claude";
constant CLAUDE_CONFIG_FILE (line 7) | const CLAUDE_CONFIG_FILE: &str = "config.json";
function claude_dir (line 9) | fn claude_dir() -> Result<PathBuf, AppError> {
function claude_config_path (line 18) | pub fn claude_config_path() -> Result<PathBuf, AppError> {
function ensure_claude_dir_exists (line 22) | pub fn ensure_claude_dir_exists() -> Result<PathBuf, AppError> {
function read_claude_config (line 30) | pub fn read_claude_config() -> Result<Option<String>, AppError> {
function is_managed_config (line 40) | fn is_managed_config(content: &str) -> bool {
function write_claude_config (line 51) | pub fn write_claude_config() -> Result<bool, AppError> {
function clear_claude_config (line 90) | pub fn clear_claude_config() -> Result<bool, AppError> {
function claude_config_status (line 121) | pub fn claude_config_status() -> Result<(bool, PathBuf), AppError> {
function is_claude_config_applied (line 126) | pub fn is_claude_config_applied() -> Result<bool, AppError> {
FILE: src-tauri/src/codex_config.rs
function get_codex_config_dir (line 15) | pub fn get_codex_config_dir() -> PathBuf {
function get_codex_auth_path (line 24) | pub fn get_codex_auth_path() -> PathBuf {
function get_codex_config_path (line 29) | pub fn get_codex_config_path() -> PathBuf {
function get_codex_provider_paths (line 35) | pub fn get_codex_provider_paths(
function delete_codex_provider_config (line 51) | pub fn delete_codex_provider_config(
function write_codex_live_atomic (line 64) | pub fn write_codex_live_atomic(
function read_codex_config_text (line 114) | pub fn read_codex_config_text() -> Result<String, AppError> {
function validate_config_toml (line 124) | pub fn validate_config_toml(text: &str) -> Result<(), AppError> {
function read_and_validate_codex_config_text (line 134) | pub fn read_and_validate_codex_config_text() -> Result<String, AppError> {
function update_codex_toml_field (line 148) | pub fn update_codex_toml_field(toml_str: &str, field: &str, value: &str)...
function remove_codex_toml_base_url_if (line 208) | pub fn remove_codex_toml_base_url_if(toml_str: &str, predicate: impl Fn(...
function base_url_writes_into_correct_model_provider_section (line 258) | fn base_url_writes_into_correct_model_provider_section() {
function base_url_creates_section_when_missing (line 291) | fn base_url_creates_section_when_missing() {
function base_url_falls_back_to_top_level_without_model_provider (line 309) | fn base_url_falls_back_to_top_level_without_model_provider() {
function clearing_base_url_removes_only_from_correct_section (line 324) | fn clearing_base_url_removes_only_from_correct_section() {
function model_field_operates_on_top_level (line 357) | fn model_field_operates_on_top_level() {
function preserves_comments_and_whitespace (line 376) | fn preserves_comments_and_whitespace() {
function does_not_misplace_when_profiles_section_follows (line 395) | fn does_not_misplace_when_profiles_section_follows() {
function remove_base_url_if_predicate (line 427) | fn remove_base_url_if_predicate() {
function remove_base_url_if_keeps_non_matching (line 452) | fn remove_base_url_if_keeps_non_matching() {
FILE: src-tauri/src/commands/auth.rs
constant AUTH_PROVIDER_GITHUB_COPILOT (line 6) | const AUTH_PROVIDER_GITHUB_COPILOT: &str = "github_copilot";
type ManagedAuthAccount (line 9) | pub struct ManagedAuthAccount {
type ManagedAuthStatus (line 19) | pub struct ManagedAuthStatus {
type ManagedAuthDeviceCodeResponse (line 28) | pub struct ManagedAuthDeviceCodeResponse {
function ensure_auth_provider (line 37) | fn ensure_auth_provider(auth_provider: &str) -> Result<&str, String> {
function map_account (line 44) | fn map_account(
function map_device_code_response (line 59) | fn map_device_code_response(
function auth_start_login (line 74) | pub async fn auth_start_login(
function auth_poll_for_account (line 88) | pub async fn auth_poll_for_account(
function auth_list_accounts (line 109) | pub async fn auth_list_accounts(
function auth_get_status (line 125) | pub async fn auth_get_status(
function auth_remove_account (line 147) | pub async fn auth_remove_account(
function auth_set_default_account (line 161) | pub async fn auth_set_default_account(
function auth_logout (line 175) | pub async fn auth_logout(
FILE: src-tauri/src/commands/config.rs
function get_claude_config_status (line 13) | pub async fn get_claude_config_status() -> Result<ConfigStatus, String> {
function invalid_json_format_error (line 19) | fn invalid_json_format_error(error: serde_json::Error) -> String {
function invalid_toml_format_error (line 31) | fn invalid_toml_format_error(error: toml_edit::TomlError) -> String {
function validate_common_config_snippet (line 43) | fn validate_common_config_snippet(app_type: &str, snippet: &str) -> Resu...
function get_config_status (line 65) | pub async fn get_config_status(app: String) -> Result<ConfigStatus, Stri...
function get_claude_code_config_path (line 108) | pub async fn get_claude_code_config_path() -> Result<String, String> {
function get_config_dir (line 113) | pub async fn get_config_dir(app: String) -> Result<String, String> {
function open_config_folder (line 126) | pub async fn open_config_folder(handle: AppHandle, app: String) -> Resul...
function pick_directory (line 148) | pub async fn pick_directory(
function get_app_config_path (line 179) | pub async fn get_app_config_path() -> Result<String, String> {
function open_app_config_folder (line 185) | pub async fn open_app_config_folder(handle: AppHandle) -> Result<bool, S...
function get_claude_common_config_snippet (line 201) | pub async fn get_claude_common_config_snippet(
function set_claude_common_config_snippet (line 211) | pub async fn set_claude_common_config_snippet(
function get_common_config_snippet (line 235) | pub async fn get_common_config_snippet(
function set_common_config_snippet (line 246) | pub async fn set_common_config_snippet(
function validate_common_config_snippet_accepts_comment_only_codex_snippet (line 328) | fn validate_common_config_snippet_accepts_comment_only_codex_snippet() {
function validate_common_config_snippet_rejects_invalid_codex_snippet (line 334) | fn validate_common_config_snippet_rejects_invalid_codex_snippet() {
function extract_common_config_snippet (line 345) | pub async fn extract_common_config_snippet(
FILE: src-tauri/src/commands/copilot.rs
type CopilotAuthState (line 14) | pub struct CopilotAuthState(pub Arc<RwLock<CopilotAuthManager>>);
function copilot_start_device_flow (line 22) | pub async fn copilot_start_device_flow(
function copilot_poll_for_auth (line 37) | pub async fn copilot_poll_for_auth(
function copilot_poll_for_account (line 62) | pub async fn copilot_poll_for_account(
function copilot_list_accounts (line 83) | pub async fn copilot_list_accounts(
function copilot_remove_account (line 92) | pub async fn copilot_remove_account(
function copilot_set_default_account (line 105) | pub async fn copilot_set_default_account(
function copilot_get_auth_status (line 120) | pub async fn copilot_get_auth_status(
function copilot_is_authenticated (line 129) | pub async fn copilot_is_authenticated(state: State<'_, CopilotAuthState>...
function copilot_logout (line 136) | pub async fn copilot_logout(state: State<'_, CopilotAuthState>) -> Resul...
function copilot_get_token (line 147) | pub async fn copilot_get_token(state: State<'_, CopilotAuthState>) -> Re...
function copilot_get_token_for_account (line 157) | pub async fn copilot_get_token_for_account(
function copilot_get_models (line 172) | pub async fn copilot_get_models(
function copilot_get_models_for_account (line 181) | pub async fn copilot_get_models_for_account(
function copilot_get_usage (line 194) | pub async fn copilot_get_usage(
function copilot_get_usage_for_account (line 203) | pub async fn copilot_get_usage_for_account(
FILE: src-tauri/src/commands/deeplink.rs
function parse_deeplink (line 10) | pub fn parse_deeplink(url: String) -> Result<DeepLinkImportRequest, Stri...
function merge_deeplink_config (line 18) | pub fn merge_deeplink_config(
function import_from_deeplink (line 27) | pub fn import_from_deeplink(
function import_from_deeplink_unified (line 46) | pub async fn import_from_deeplink_unified(
FILE: src-tauri/src/commands/env.rs
function check_env_conflicts (line 8) | pub fn check_env_conflicts(app: String) -> Result<Vec<EnvConflict>, Stri...
function delete_env_vars (line 14) | pub fn delete_env_vars(conflicts: Vec<EnvConflict>) -> Result<BackupInfo...
function restore_env_backup (line 20) | pub fn restore_env_backup(backup_path: String) -> Result<(), String> {
FILE: src-tauri/src/commands/failover.rs
function get_failover_queue (line 13) | pub async fn get_failover_queue(
function get_available_providers_for_failover (line 25) | pub async fn get_available_providers_for_failover(
function add_to_failover_queue (line 37) | pub async fn add_to_failover_queue(
function remove_from_failover_queue (line 50) | pub async fn remove_from_failover_queue(
function get_auto_failover_enabled (line 63) | pub async fn get_auto_failover_enabled(
function set_auto_failover_enabled (line 79) | pub async fn set_auto_failover_enabled(
FILE: src-tauri/src/commands/global_proxy.rs
function get_global_proxy_url (line 15) | pub fn get_global_proxy_url(state: tauri::State<'_, AppState>) -> Result...
function set_global_proxy_url (line 35) | pub fn set_global_proxy_url(state: tauri::State<'_, AppState>, url: Stri...
type ProxyTestResult (line 75) | pub struct ProxyTestResult {
function test_proxy_url (line 89) | pub async fn test_proxy_url(url: String) -> Result<ProxyTestResult, Stri...
function get_upstream_proxy_status (line 164) | pub fn get_upstream_proxy_status() -> UpstreamProxyStatus {
type UpstreamProxyStatus (line 175) | pub struct UpstreamProxyStatus {
type DetectedProxy (line 185) | pub struct DetectedProxy {
constant PROXY_PORTS (line 197) | const PROXY_PORTS: &[(u16, &str, bool)] = &[
function scan_local_proxies (line 213) | pub async fn scan_local_proxies() -> Vec<DetectedProxy> {
FILE: src-tauri/src/commands/import_export.rs
function export_config_to_file (line 21) | pub async fn export_config_to_file(
function import_config_from_file (line 42) | pub async fn import_config_from_file(
function sync_current_providers_live (line 63) | pub async fn sync_current_providers_live(state: State<'_, AppState>) -> ...
function save_file_dialog (line 82) | pub async fn save_file_dialog<R: tauri::Runtime>(
function open_file_dialog (line 98) | pub async fn open_file_dialog<R: tauri::Runtime>(
function open_zip_file_dialog (line 112) | pub async fn open_zip_file_dialog<R: tauri::Runtime>(
function create_db_backup (line 128) | pub async fn create_db_backup(state: State<'_, AppState>) -> Result<Stri...
function list_db_backups (line 146) | pub fn list_db_backups() -> Result<Vec<BackupEntry>, String> {
function restore_db_backup (line 152) | pub async fn restore_db_backup(
function rename_db_backup (line 165) | pub fn rename_db_backup(
function delete_db_backup (line 174) | pub fn delete_db_backup(filename: String) -> Result<(), String> {
FILE: src-tauri/src/commands/mcp.rs
function get_claude_mcp_status (line 16) | pub async fn get_claude_mcp_status() -> Result<claude_mcp::McpStatus, St...
function read_claude_mcp_config (line 22) | pub async fn read_claude_mcp_config() -> Result<Option<String>, String> {
function upsert_claude_mcp_server (line 28) | pub async fn upsert_claude_mcp_server(id: String, spec: serde_json::Valu...
function delete_claude_mcp_server (line 34) | pub async fn delete_claude_mcp_server(id: String) -> Result<bool, String> {
function validate_mcp_command (line 40) | pub async fn validate_mcp_command(cmd: String) -> Result<bool, String> {
type McpConfigResponse (line 45) | pub struct McpConfigResponse {
function get_mcp_config (line 55) | pub async fn get_mcp_config(
function upsert_mcp_server_in_config (line 73) | pub async fn upsert_mcp_server_in_config(
function delete_mcp_server_in_config (line 135) | pub async fn delete_mcp_server_in_config(
function set_mcp_enabled (line 146) | pub async fn set_mcp_enabled(
function get_mcp_servers (line 164) | pub async fn get_mcp_servers(
function upsert_mcp_server (line 172) | pub async fn upsert_mcp_server(
function delete_mcp_server (line 181) | pub async fn delete_mcp_server(state: State<'_, AppState>, id: String) -...
function toggle_mcp_app (line 187) | pub async fn toggle_mcp_app(
function import_mcp_from_apps (line 199) | pub async fn import_mcp_from_apps(state: State<'_, AppState>) -> Result<...
FILE: src-tauri/src/commands/misc.rs
constant CREATE_NO_WINDOW (line 19) | const CREATE_NO_WINDOW: u32 = 0x08000000;
function open_external (line 23) | pub async fn open_external(app: AppHandle, url: String) -> Result<bool, ...
function check_for_updates (line 39) | pub async fn check_for_updates(handle: AppHandle) -> Result<bool, String> {
function is_portable_mode (line 53) | pub async fn is_portable_mode() -> Result<bool, String> {
function get_init_error (line 65) | pub async fn get_init_error() -> Result<Option<InitErrorPayload>, String> {
function get_migration_result (line 72) | pub async fn get_migration_result() -> Result<bool, String> {
function get_skills_migration_result (line 79) | pub async fn get_skills_migration_result() -> Result<Option<SkillsMigrat...
type ToolVersion (line 84) | pub struct ToolVersion {
constant VALID_TOOLS (line 95) | const VALID_TOOLS: [&str; 4] = ["claude", "codex", "gemini", "opencode"];
type WslShellPreferenceInput (line 99) | pub struct WslShellPreferenceInput {
function tool_env_type_and_wsl_distro (line 108) | fn tool_env_type_and_wsl_distro(tool: &str) -> (String, Option<String>) {
function tool_env_type_and_wsl_distro (line 117) | fn tool_env_type_and_wsl_distro(_tool: &str) -> (String, Option<String>) {
function tool_env_type_and_wsl_distro (line 122) | fn tool_env_type_and_wsl_distro(_tool: &str) -> (String, Option<String>) {
function tool_env_type_and_wsl_distro (line 127) | fn tool_env_type_and_wsl_distro(_tool: &str) -> (String, Option<String>) {
function get_tool_versions (line 132) | pub async fn get_tool_versions(
function get_single_tool_version_impl (line 173) | async fn get_single_tool_version_impl(
function fetch_npm_latest_version (line 221) | async fn fetch_npm_latest_version(client: &reqwest::Client, package: &st...
function fetch_github_latest_version (line 239) | async fn fetch_github_latest_version(client: &reqwest::Client, repo: &st...
function extract_version (line 266) | fn extract_version(raw: &str) -> String {
function try_get_version (line 274) | fn try_get_version(tool: &str) -> (Option<String>, Option<String>) {
function is_valid_wsl_distro_name (line 323) | fn is_valid_wsl_distro_name(name: &str) -> bool {
function is_valid_shell (line 333) | fn is_valid_shell(shell: &str) -> bool {
function is_valid_shell_flag (line 342) | fn is_valid_shell_flag(flag: &str) -> bool {
function default_flag_for_shell (line 348) | fn default_flag_for_shell(shell: &str) -> &'static str {
function try_get_version_wsl (line 357) | fn try_get_version_wsl(
function try_get_version_wsl (line 457) | fn try_get_version_wsl(
function push_unique_path (line 469) | fn push_unique_path(paths: &mut Vec<std::path::PathBuf>, path: std::path...
function push_env_single_dir (line 479) | fn push_env_single_dir(paths: &mut Vec<std::path::PathBuf>, value: Optio...
function extend_from_path_list (line 485) | fn extend_from_path_list(
function opencode_extra_search_paths (line 504) | fn opencode_extra_search_paths(
function tool_executable_candidates (line 526) | fn tool_executable_candidates(tool: &str, dir: &Path) -> Vec<std::path::...
function scan_cli_version (line 543) | fn scan_cli_version(tool: &str) -> (Option<String>, Option<String>) {
function wsl_distro_for_tool (line 674) | fn wsl_distro_for_tool(tool: &str) -> Option<String> {
function wsl_distro_from_path (line 689) | fn wsl_distro_from_path(path: &Path) -> Option<String> {
function open_provider_terminal (line 717) | pub async fn open_provider_terminal(
function extract_env_vars_from_config (line 743) | fn extract_env_vars_from_config(
function launch_terminal_with_env (line 794) | fn launch_terminal_with_env(
function write_claude_config (line 831) | fn write_claude_config(
function launch_macos_terminal (line 852) | fn launch_macos_terminal(config_file: &std::path::Path) -> Result<(), St...
function launch_macos_terminal_app (line 907) | fn launch_macos_terminal_app(script_file: &std::path::Path) -> Result<()...
function launch_macos_iterm2 (line 938) | fn launch_macos_iterm2(script_file: &std::path::Path) -> Result<(), Stri...
function launch_macos_open_app (line 974) | fn launch_macos_open_app(
function launch_linux_terminal (line 1008) | fn launch_linux_terminal(config_file: &std::path::Path) -> Result<(), St...
function which_command (line 1105) | fn which_command(cmd: &str) -> bool {
function launch_windows_terminal (line 1116) | fn launch_windows_terminal(
function run_windows_start_command (line 1167) | fn run_windows_start_command(args: &[&str], terminal_name: &str) -> Resu...
function set_window_theme (line 1195) | pub async fn set_window_theme(window: tauri::Window, theme: String) -> R...
function test_extract_version (line 1213) | fn test_extract_version() {
function test_is_valid_shell (line 1224) | fn test_is_valid_shell() {
function test_is_valid_shell_flag (line 1238) | fn test_is_valid_shell_flag() {
function test_default_flag_for_shell (line 1248) | fn test_default_flag_for_shell() {
function test_is_valid_wsl_distro_name (line 1259) | fn test_is_valid_wsl_distro_name() {
function opencode_extra_search_paths_includes_install_and_fallback_dirs (line 1270) | fn opencode_extra_search_paths_includes_install_and_fallback_dirs() {
function opencode_extra_search_paths_deduplicates_repeated_entries (line 1289) | fn opencode_extra_search_paths_deduplicates_repeated_entries() {
function tool_executable_candidates_non_windows_uses_plain_binary_name (line 1304) | fn tool_executable_candidates_non_windows_uses_plain_binary_name() {
function tool_executable_candidates_windows_includes_cmd_exe_and_plain_name (line 1313) | fn tool_executable_candidates_windows_includes_cmd_exe_and_plain_name() {
FILE: src-tauri/src/commands/omo.rs
function read_omo_local_file (line 8) | pub async fn read_omo_local_file() -> Result<OmoLocalFileData, String> {
function get_current_omo_provider_id (line 13) | pub async fn get_current_omo_provider_id(state: State<'_, AppState>) -> ...
function disable_current_omo (line 22) | pub async fn disable_current_omo(state: State<'_, AppState>) -> Result<(...
function read_omo_slim_local_file (line 42) | pub async fn read_omo_slim_local_file() -> Result<OmoLocalFileData, Stri...
function get_current_omo_slim_provider_id (line 47) | pub async fn get_current_omo_slim_provider_id(
function disable_current_omo_slim (line 58) | pub async fn disable_current_omo_slim(state: State<'_, AppState>) -> Res...
FILE: src-tauri/src/commands/openclaw.rs
function import_openclaw_providers_from_live (line 16) | pub fn import_openclaw_providers_from_live(state: State<'_, AppState>) -...
function get_openclaw_live_provider_ids (line 23) | pub fn get_openclaw_live_provider_ids() -> Result<Vec<String>, String> {
function get_openclaw_live_provider (line 31) | pub fn get_openclaw_live_provider(
function scan_openclaw_config_health (line 39) | pub fn scan_openclaw_config_health() -> Result<Vec<openclaw_config::Open...
function get_openclaw_default_model (line 50) | pub fn get_openclaw_default_model() -> Result<Option<openclaw_config::Op...
function set_openclaw_default_model (line 57) | pub fn set_openclaw_default_model(
function get_openclaw_model_catalog (line 65) | pub fn get_openclaw_model_catalog(
function set_openclaw_model_catalog (line 72) | pub fn set_openclaw_model_catalog(
function get_openclaw_agents_defaults (line 80) | pub fn get_openclaw_agents_defaults(
function set_openclaw_agents_defaults (line 87) | pub fn set_openclaw_agents_defaults(
function get_openclaw_env (line 99) | pub fn get_openclaw_env() -> Result<openclaw_config::OpenClawEnvConfig, ...
function set_openclaw_env (line 105) | pub fn set_openclaw_env(
function get_openclaw_tools (line 117) | pub fn get_openclaw_tools() -> Result<openclaw_config::OpenClawToolsConf...
function set_openclaw_tools (line 123) | pub fn set_openclaw_tools(
FILE: src-tauri/src/commands/plugin.rs
function get_claude_plugin_status (line 7) | pub async fn get_claude_plugin_status() -> Result<ConfigStatus, String> {
function read_claude_plugin_config (line 18) | pub async fn read_claude_plugin_config() -> Result<Option<String>, Strin...
function apply_claude_plugin_config (line 24) | pub async fn apply_claude_plugin_config(official: bool) -> Result<bool, ...
function is_claude_plugin_applied (line 34) | pub async fn is_claude_plugin_applied() -> Result<bool, String> {
function apply_claude_onboarding_skip (line 40) | pub async fn apply_claude_onboarding_skip() -> Result<bool, String> {
function clear_claude_onboarding_skip (line 46) | pub async fn clear_claude_onboarding_skip() -> Result<bool, String> {
FILE: src-tauri/src/commands/prompt.rs
function get_prompts (line 12) | pub async fn get_prompts(
function upsert_prompt (line 21) | pub async fn upsert_prompt(
function delete_prompt (line 32) | pub async fn delete_prompt(
function enable_prompt (line 42) | pub async fn enable_prompt(
function import_prompt_from_file (line 52) | pub async fn import_prompt_from_file(
function get_current_prompt_file_content (line 61) | pub async fn get_current_prompt_file_content(app: String) -> Result<Opti...
FILE: src-tauri/src/commands/provider.rs
constant TEMPLATE_TYPE_GITHUB_COPILOT (line 15) | const TEMPLATE_TYPE_GITHUB_COPILOT: &str = "github_copilot";
constant COPILOT_UNIT_PREMIUM (line 16) | const COPILOT_UNIT_PREMIUM: &str = "requests";
function get_providers (line 20) | pub fn get_providers(
function get_current_provider (line 29) | pub fn get_current_provider(state: State<'_, AppState>, app: String) -> ...
function add_provider (line 35) | pub fn add_provider(
function update_provider (line 45) | pub fn update_provider(
function delete_provider (line 55) | pub fn delete_provider(
function remove_provider_from_live_config (line 67) | pub fn remove_provider_from_live_config(
function switch_provider_internal (line 78) | fn switch_provider_internal(
function switch_provider_test_hook (line 87) | pub fn switch_provider_test_hook(
function switch_provider (line 96) | pub fn switch_provider(
function import_default_config_internal (line 105) | fn import_default_config_internal(state: &AppState, app_type: AppType) -...
function import_default_config_test_hook (line 134) | pub fn import_default_config_test_hook(
function import_default_config (line 142) | pub fn import_default_config(state: State<'_, AppState>, app: String) ->...
function queryProviderUsage (line 149) | pub async fn queryProviderUsage(
function testUsageScript (line 218) | pub async fn testUsageScript(
function read_live_provider_settings (line 248) | pub fn read_live_provider_settings(app: String) -> Result<serde_json::Va...
function test_api_endpoints (line 254) | pub async fn test_api_endpoints(
function get_custom_endpoints (line 264) | pub fn get_custom_endpoints(
function add_custom_endpoint (line 275) | pub fn add_custom_endpoint(
function remove_custom_endpoint (line 287) | pub fn remove_custom_endpoint(
function update_endpoint_last_used (line 299) | pub fn update_endpoint_last_used(
function update_providers_sort_order (line 311) | pub fn update_providers_sort_order(
type UniversalProviderSyncedEvent (line 325) | pub struct UniversalProviderSyncedEvent {
function emit_universal_provider_synced (line 330) | fn emit_universal_provider_synced(app: &AppHandle, action: &str, id: &st...
function get_universal_providers (line 341) | pub fn get_universal_providers(
function get_universal_provider (line 348) | pub fn get_universal_provider(
function upsert_universal_provider (line 356) | pub fn upsert_universal_provider(
function delete_universal_provider (line 371) | pub fn delete_universal_provider(
function sync_universal_provider (line 385) | pub fn sync_universal_provider(
function import_opencode_providers_from_live (line 399) | pub fn import_opencode_providers_from_live(state: State<'_, AppState>) -...
function get_opencode_live_provider_ids (line 405) | pub fn get_opencode_live_provider_ids() -> Result<Vec<String>, String> {
FILE: src-tauri/src/commands/proxy.rs
function start_proxy_server (line 12) | pub async fn start_proxy_server(
function stop_proxy_with_restore (line 20) | pub async fn stop_proxy_with_restore(state: tauri::State<'_, AppState>) ...
function get_proxy_takeover_status (line 26) | pub async fn get_proxy_takeover_status(
function set_proxy_takeover_for_app (line 34) | pub async fn set_proxy_takeover_for_app(
function get_proxy_status (line 47) | pub async fn get_proxy_status(state: tauri::State<'_, AppState>) -> Resu...
function get_proxy_config (line 53) | pub async fn get_proxy_config(state: tauri::State<'_, AppState>) -> Resu...
function update_proxy_config (line 59) | pub async fn update_proxy_config(
function get_global_proxy_config (line 72) | pub async fn get_global_proxy_config(
function update_global_proxy_config (line 85) | pub async fn update_global_proxy_config(
function get_proxy_config_for_app (line 99) | pub async fn get_proxy_config_for_app(
function update_proxy_config_for_app (line 113) | pub async fn update_proxy_config_for_app(
function get_default_cost_multiplier_internal (line 123) | async fn get_default_cost_multiplier_internal(
function get_default_cost_multiplier_test_hook (line 132) | pub async fn get_default_cost_multiplier_test_hook(
function get_default_cost_multiplier (line 141) | pub async fn get_default_cost_multiplier(
function set_default_cost_multiplier_internal (line 150) | async fn set_default_cost_multiplier_internal(
function set_default_cost_multiplier_test_hook (line 160) | pub async fn set_default_cost_multiplier_test_hook(
function set_default_cost_multiplier (line 170) | pub async fn set_default_cost_multiplier(
function get_pricing_model_source_internal (line 180) | async fn get_pricing_model_source_internal(
function get_pricing_model_source_test_hook (line 189) | pub async fn get_pricing_model_source_test_hook(
function get_pricing_model_source (line 198) | pub async fn get_pricing_model_source(
function set_pricing_model_source_internal (line 207) | async fn set_pricing_model_source_internal(
function set_pricing_model_source_test_hook (line 217) | pub async fn set_pricing_model_source_test_hook(
function set_pricing_model_source (line 227) | pub async fn set_pricing_model_source(
function is_proxy_running (line 239) | pub async fn is_proxy_running(state: tauri::State<'_, AppState>) -> Resu...
function is_live_takeover_active (line 245) | pub async fn is_live_takeover_active(state: tauri::State<'_, AppState>) ...
function switch_proxy_provider (line 251) | pub async fn switch_proxy_provider(
function get_provider_health (line 266) | pub async fn get_provider_health(
function reset_circuit_breaker (line 283) | pub async fn reset_circuit_breaker(
function get_circuit_breaker_config (line 367) | pub async fn get_circuit_breaker_config(
function update_circuit_breaker_config (line 378) | pub async fn update_circuit_breaker_config(
function get_circuit_breaker_stats (line 400) | pub async fn get_circuit_breaker_stats(
FILE: src-tauri/src/commands/session_manager.rs
function list_sessions (line 6) | pub async fn list_sessions() -> Result<Vec<session_manager::SessionMeta>...
function get_session_messages (line 14) | pub async fn get_session_messages(
function launch_session_terminal (line 28) | pub async fn launch_session_terminal(
function delete_session (line 62) | pub async fn delete_session(
FILE: src-tauri/src/commands/settings.rs
function merge_settings_for_save (line 5) | fn merge_settings_for_save(
function get_settings (line 17) | pub async fn get_settings() -> Result<crate::settings::AppSettings, Stri...
function save_settings (line 23) | pub async fn save_settings(settings: crate::settings::AppSettings) -> Re...
function restart_app (line 32) | pub async fn restart_app(app: AppHandle) -> Result<bool, String> {
function get_app_config_dir_override (line 43) | pub async fn get_app_config_dir_override(app: AppHandle) -> Result<Optio...
function set_app_config_dir_override (line 50) | pub async fn set_app_config_dir_override(
function set_auto_launch (line 60) | pub async fn set_auto_launch(enabled: bool) -> Result<bool, String> {
function save_settings_should_preserve_existing_webdav_when_payload_omits_it (line 75) | fn save_settings_should_preserve_existing_webdav_when_payload_omits_it() {
function save_settings_should_keep_incoming_webdav_when_present (line 95) | fn save_settings_should_keep_incoming_webdav_when_present() {
function get_auto_launch_status (line 123) | pub async fn get_auto_launch_status() -> Result<bool, String> {
function get_rectifier_config (line 129) | pub async fn get_rectifier_config(
function set_rectifier_config (line 137) | pub async fn set_rectifier_config(
function get_optimizer_config (line 150) | pub async fn get_optimizer_config(
function set_optimizer_config (line 158) | pub async fn set_optimizer_config(
function get_log_config (line 180) | pub async fn get_log_config(
function set_log_config (line 188) | pub async fn set_log_config(
FILE: src-tauri/src/commands/skill.rs
type SkillServiceState (line 18) | pub struct SkillServiceState(pub Arc<SkillService>);
function parse_app_type (line 21) | fn parse_app_type(app: &str) -> Result<AppType, String> {
function get_installed_skills (line 35) | pub fn get_installed_skills(app_state: State<'_, AppState>) -> Result<Ve...
function get_skill_backups (line 40) | pub fn get_skill_backups() -> Result<Vec<SkillBackupEntry>, String> {
function delete_skill_backup (line 45) | pub fn delete_skill_backup(backup_id: String) -> Result<bool, String> {
function install_skill_unified (line 56) | pub async fn install_skill_unified(
function uninstall_skill_unified (line 73) | pub fn uninstall_skill_unified(
function restore_skill_backup (line 81) | pub fn restore_skill_backup(
function toggle_skill_app (line 93) | pub fn toggle_skill_app(
function scan_unmanaged_skills (line 106) | pub fn scan_unmanaged_skills(
function import_skills_from_apps (line 114) | pub fn import_skills_from_apps(
function discover_available_skills (line 125) | pub async fn discover_available_skills(
function get_skills (line 141) | pub async fn get_skills(
function get_skills_for_app (line 155) | pub async fn get_skills_for_app(
function install_skill (line 167) | pub async fn install_skill(
function install_skill_for_app (line 177) | pub async fn install_skill_for_app(
function uninstall_skill (line 222) | pub fn uninstall_skill(
function uninstall_skill_for_app (line 231) | pub fn uninstall_skill_for_app(
function get_skill_repos (line 253) | pub fn get_skill_repos(app_state: State<'_, AppState>) -> Result<Vec<Ski...
function add_skill_repo (line 259) | pub fn add_skill_repo(repo: SkillRepo, app_state: State<'_, AppState>) -...
function remove_skill_repo (line 269) | pub fn remove_skill_repo(
function install_skills_from_zip (line 283) | pub fn install_skills_from_zip(
FILE: src-tauri/src/commands/stream_check.rs
function stream_check_provider (line 15) | pub async fn stream_check_provider(
function stream_check_all_providers (line 43) | pub async fn stream_check_all_providers(
function get_stream_check_config (line 102) | pub fn get_stream_check_config(state: State<'_, AppState>) -> Result<Str...
function save_stream_check_config (line 108) | pub fn save_stream_check_config(
function resolve_copilot_auth_override (line 115) | async fn resolve_copilot_auth_override(
FILE: src-tauri/src/commands/sync_support.rs
function run_post_import_sync (line 10) | pub(crate) fn run_post_import_sync(db: Arc<Database>) -> Result<(), AppE...
function post_sync_warning (line 17) | fn post_sync_warning<E: std::fmt::Display>(err: E) -> String {
function post_sync_warning_from_result (line 26) | pub(crate) fn post_sync_warning_from_result(
function attach_warning (line 36) | pub(crate) fn attach_warning(mut value: Value, warning: Option<String>) ...
function success_payload_with_warning (line 45) | pub(crate) fn success_payload_with_warning(backup_id: String, warning: O...
function post_sync_warning_from_result_returns_none_on_success (line 62) | fn post_sync_warning_from_result_returns_none_on_success() {
function post_sync_warning_from_result_returns_some_on_sync_error (line 68) | fn post_sync_warning_from_result_returns_some_on_sync_error() {
function post_sync_warning_from_result_returns_some_on_join_error (line 75) | async fn post_sync_warning_from_result_returns_some_on_join_error() {
function attach_warning_adds_warning_without_dropping_existing_fields (line 85) | fn attach_warning_adds_warning_without_dropping_existing_fields() {
FILE: src-tauri/src/commands/usage.rs
function get_usage_summary (line 10) | pub fn get_usage_summary(
function get_usage_trends (line 20) | pub fn get_usage_trends(
function get_provider_stats (line 30) | pub fn get_provider_stats(state: State<'_, AppState>) -> Result<Vec<Prov...
function get_model_stats (line 36) | pub fn get_model_stats(state: State<'_, AppState>) -> Result<Vec<ModelSt...
function get_request_logs (line 42) | pub fn get_request_logs(
function get_request_detail (line 53) | pub fn get_request_detail(
function get_model_pricing (line 62) | pub fn get_model_pricing(state: State<'_, AppState>) -> Result<Vec<Model...
function update_model_pricing (line 112) | pub fn update_model_pricing(
function check_provider_limits (line 145) | pub fn check_provider_limits(
function delete_model_pricing (line 155) | pub fn delete_model_pricing(state: State<'_, AppState>, model_id: String...
type ModelPricingInfo (line 172) | pub struct ModelPricingInfo {
FILE: src-tauri/src/commands/webdav_sync.rs
function persist_sync_error (line 14) | fn persist_sync_error(settings: &mut WebDavSyncSettings, error: &AppErro...
function webdav_not_configured_error (line 20) | fn webdav_not_configured_error() -> String {
function webdav_sync_disabled_error (line 29) | fn webdav_sync_disabled_error() -> String {
function require_enabled_webdav_settings (line 38) | fn require_enabled_webdav_settings() -> Result<WebDavSyncSettings, Strin...
function resolve_password_for_request (line 46) | fn resolve_password_for_request(
function webdav_sync_mutex (line 60) | fn webdav_sync_mutex() -> &'static tokio::sync::Mutex<()> {
function run_with_webdav_lock (line 64) | async fn run_with_webdav_lock<T, Fut>(operation: Fut) -> Result<T, AppEr...
function map_sync_result (line 71) | fn map_sync_result<T, F>(result: Result<T, AppError>, on_error: F) -> Re...
function webdav_test_connection (line 85) | pub async fn webdav_test_connection(
function webdav_sync_upload (line 105) | pub async fn webdav_sync_upload(state: State<'_, AppState>) -> Result<Va...
function webdav_sync_download (line 116) | pub async fn webdav_sync_download(state: State<'_, AppState>) -> Result<...
function webdav_sync_save_settings (line 142) | pub async fn webdav_sync_save_settings(
function webdav_sync_fetch_remote_info (line 163) | pub async fn webdav_sync_fetch_remote_info() -> Result<Value, String> {
function webdav_sync_mutex_is_singleton (line 185) | async fn webdav_sync_mutex_is_singleton() {
function webdav_sync_mutex_serializes_concurrent_access (line 193) | async fn webdav_sync_mutex_serializes_concurrent_access() {
function map_sync_result_runs_error_handler_after_lock_release (line 217) | async fn map_sync_result_runs_error_handler_after_lock_release() {
function resolve_password_for_request_preserves_existing_when_requested (line 233) | fn resolve_password_for_request_preserves_existing_when_requested() {
function resolve_password_for_request_allows_explicit_empty_password (line 249) | fn resolve_password_for_request_allows_explicit_empty_password() {
function persist_sync_error_updates_status_without_overwriting_credentials (line 266) | fn persist_sync_error_updates_status_without_overwriting_credentials() {
function require_enabled_webdav_settings_rejects_disabled_config (line 311) | fn require_enabled_webdav_settings_rejects_disabled_config() {
function require_enabled_webdav_settings_returns_settings_when_enabled (line 336) | fn require_enabled_webdav_settings_returns_settings_when_enabled() {
FILE: src-tauri/src/commands/workspace.rs
constant ALLOWED_FILES (line 10) | const ALLOWED_FILES: &[&str] = &[
function validate_filename (line 22) | fn validate_filename(filename: &str) -> Result<(), String> {
function validate_daily_memory_filename (line 37) | fn validate_daily_memory_filename(filename: &str) -> Result<(), String> {
type DailyMemoryFileInfo (line 48) | pub struct DailyMemoryFileInfo {
function list_daily_memory_files (line 60) | pub async fn list_daily_memory_files() -> Result<Vec<DailyMemoryFileInfo...
function read_daily_memory_file (line 119) | pub async fn read_daily_memory_file(filename: String) -> Result<Option<S...
function write_daily_memory_file (line 138) | pub async fn write_daily_memory_file(filename: String, content: String) ...
function floor_char_boundary (line 154) | fn floor_char_boundary(s: &str, mut i: usize) -> usize {
function ceil_char_boundary (line 166) | fn ceil_char_boundary(s: &str, mut i: usize) -> usize {
type DailyMemorySearchResult (line 179) | pub struct DailyMemorySearchResult {
function search_daily_memory_files (line 194) | pub async fn search_daily_memory_files(
function delete_daily_memory_file (line 289) | pub async fn delete_daily_memory_file(filename: String) -> Result<(), St...
function read_workspace_file (line 310) | pub async fn read_workspace_file(filename: String) -> Result<Option<Stri...
function write_workspace_file (line 327) | pub async fn write_workspace_file(filename: String, content: String) -> ...
function open_workspace_directory (line 346) | pub async fn open_workspace_directory(handle: AppHandle, subdir: String)...
FILE: src-tauri/src/config.rs
function get_home_dir (line 21) | pub fn get_home_dir() -> PathBuf {
function get_claude_config_dir (line 36) | pub fn get_claude_config_dir() -> PathBuf {
function get_default_claude_mcp_path (line 45) | pub fn get_default_claude_mcp_path() -> PathBuf {
function derive_mcp_path_from_override (line 49) | fn derive_mcp_path_from_override(dir: &Path) -> Option<PathBuf> {
function get_claude_mcp_path (line 63) | pub fn get_claude_mcp_path() -> PathBuf {
function get_claude_settings_path (line 73) | pub fn get_claude_settings_path() -> PathBuf {
function get_app_config_dir (line 89) | pub fn get_app_config_dir() -> PathBuf {
function get_app_config_path (line 125) | pub fn get_app_config_path() -> PathBuf {
function sanitize_provider_name (line 131) | pub fn sanitize_provider_name(name: &str) -> String {
function get_provider_config_path (line 143) | pub fn get_provider_config_path(provider_id: &str, provider_name: Option...
function read_json_file (line 152) | pub fn read_json_file<T: for<'a> Deserialize<'a>>(path: &Path) -> Result...
function write_json_file (line 163) | pub fn write_json_file<T: Serialize>(path: &Path, data: &T) -> Result<()...
function write_text_file (line 176) | pub fn write_text_file(path: &Path, data: &str) -> Result<(), AppError> {
function atomic_write (line 184) | pub fn atomic_write(path: &Path, data: &[u8]) -> Result<(), AppError> {
function derive_mcp_path_from_override_preserves_folder_name (line 246) | fn derive_mcp_path_from_override_preserves_folder_name() {
function derive_mcp_path_from_override_handles_non_hidden_folder (line 254) | fn derive_mcp_path_from_override_handles_non_hidden_folder() {
function derive_mcp_path_from_override_supports_relative_rootless_dir (line 262) | fn derive_mcp_path_from_override_supports_relative_rootless_dir() {
function derive_mcp_path_from_root_like_dir_returns_none (line 270) | fn derive_mcp_path_from_root_like_dir_returns_none() {
function copy_file (line 277) | pub fn copy_file(from: &Path, to: &Path) -> Result<(), AppError> {
function delete_file (line 286) | pub fn delete_file(path: &Path) -> Result<(), AppError> {
type ConfigStatus (line 295) | pub struct ConfigStatus {
function get_claude_config_status (line 301) | pub fn get_claude_config_status() -> ConfigStatus {
FILE: src-tauri/src/database/backup.rs
constant CC_SWITCH_SQL_EXPORT_HEADER (line 16) | const CC_SWITCH_SQL_EXPORT_HEADER: &str = "-- CC Switch SQLite 导出";
constant SYNC_SKIP_TABLES (line 19) | const SYNC_SKIP_TABLES: &[&str] = &[
constant SYNC_PRESERVE_TABLES (line 29) | const SYNC_PRESERVE_TABLES: &[&str] = &[
type BackupEntry (line 39) | pub struct BackupEntry {
method export_sql_string (line 47) | pub fn export_sql_string(&self) -> Result<String, AppError> {
method export_sql_string_for_sync (line 53) | pub fn export_sql_string_for_sync(&self) -> Result<String, AppError> {
method export_sql (line 59) | pub fn export_sql(&self, target_path: &Path) -> Result<(), AppError> {
method import_sql (line 70) | pub fn import_sql(&self, source_path: &Path) -> Result<String, AppError> {
method import_sql_string (line 84) | pub fn import_sql_string(&self, sql_raw: &str) -> Result<String, AppErro...
method import_sql_string_for_sync (line 90) | pub(crate) fn import_sql_string_for_sync(&self, sql_raw: &str) -> Result...
method import_sql_string_inner (line 94) | fn import_sql_string_inner(
method snapshot_to_memory (line 150) | pub(crate) fn snapshot_to_memory(&self) -> Result<Connection, AppError> {
method validate_cc_switch_sql_export (line 166) | fn validate_cc_switch_sql_export(sql: &str) -> Result<(), AppError> {
method restore_tables (line 179) | fn restore_tables(
method periodic_backup_if_needed (line 236) | pub(crate) fn periodic_backup_if_needed(&self) -> Result<(), AppError> {
method backup_database_file (line 298) | pub(crate) fn backup_database_file(&self) -> Result<Option<PathBuf>, App...
method cleanup_db_backups (line 337) | fn cleanup_db_backups(dir: &Path) -> Result<(), AppError> {
method validate_basic_state (line 370) | fn validate_basic_state(conn: &Connection) -> Result<(), AppError> {
method dump_sql (line 387) | fn dump_sql(conn: &Connection, skip_tables: &[&str]) -> Result<String, A...
method get_table_columns (line 476) | fn get_table_columns(conn: &Connection, table: &str) -> Result<Vec<Strin...
method format_sql_value (line 492) | fn format_sql_value(value: ValueRef<'_>) -> Result<String, AppError> {
method list_backups (line 516) | pub fn list_backups() -> Result<Vec<BackupEntry>, AppError> {
method restore_from_backup (line 552) | pub fn restore_from_backup(&self, filename: &str) -> Result<String, AppE...
method rename_backup (line 602) | pub fn rename_backup(old_filename: &str, new_name: &str) -> Result<Strin...
method delete_backup (line 665) | pub fn delete_backup(filename: &str) -> Result<(), AppError> {
function sync_import_preserves_local_only_tables (line 698) | fn sync_import_preserves_local_only_tables() -> Result<(), AppError> {
function periodic_maintenance_runs_even_when_auto_backup_disabled (line 786) | fn periodic_maintenance_runs_even_when_auto_backup_disabled() -> Result<...
FILE: src-tauri/src/database/dao/failover.rs
type FailoverQueueItem (line 13) | pub struct FailoverQueueItem {
method get_failover_queue (line 21) | pub fn get_failover_queue(&self, app_type: &str) -> Result<Vec<FailoverQ...
method get_failover_providers (line 49) | pub fn get_failover_providers(&self, app_type: &str) -> Result<Vec<Provi...
method add_to_failover_queue (line 61) | pub fn add_to_failover_queue(&self, app_type: &str, provider_id: &str) -...
method remove_from_failover_queue (line 74) | pub fn remove_from_failover_queue(
method clear_failover_queue (line 101) | pub fn clear_failover_queue(&self, app_type: &str) -> Result<(), AppErro...
method is_in_failover_queue (line 114) | pub fn is_in_failover_queue(
method get_available_providers_for_failover (line 133) | pub fn get_available_providers_for_failover(
FILE: src-tauri/src/database/dao/mcp.rs
method get_all_mcp_servers (line 13) | pub fn get_all_mcp_servers(&self) -> Result<IndexMap<String, McpServer>,...
method save_mcp_server (line 68) | pub fn save_mcp_server(&self, server: &McpServer) -> Result<(), AppError> {
method delete_mcp_server (line 97) | pub fn delete_mcp_server(&self, id: &str) -> Result<(), AppError> {
FILE: src-tauri/src/database/dao/prompts.rs
method get_prompts (line 13) | pub fn get_prompts(&self, app_type: &str) -> Result<IndexMap<String, Pro...
method save_prompt (line 57) | pub fn save_prompt(&self, app_type: &str, prompt: &Prompt) -> Result<(),...
method delete_prompt (line 79) | pub fn delete_prompt(&self, app_type: &str, id: &str) -> Result<(), AppE...
FILE: src-tauri/src/database/dao/providers.rs
type OmoProviderRow (line 8) | type OmoProviderRow = (
method get_all_providers (line 20) | pub fn get_all_providers(
method get_current_provider (line 111) | pub fn get_current_provider(&self, app_type: &str) -> Result<Option<Stri...
method get_provider_by_id (line 130) | pub fn get_provider_by_id(
method save_provider (line 180) | pub fn save_provider(&self, app_type: &str, provider: &Provider) -> Resu...
method delete_provider (line 280) | pub fn delete_provider(&self, app_type: &str, id: &str) -> Result<(), Ap...
method set_current_provider (line 290) | pub fn set_current_provider(&self, app_type: &str, id: &str) -> Result<(...
method update_provider_settings_config (line 312) | pub fn update_provider_settings_config(
method add_custom_endpoint (line 333) | pub fn add_custom_endpoint(
method remove_custom_endpoint (line 348) | pub fn remove_custom_endpoint(
method set_omo_provider_current (line 363) | pub fn set_omo_provider_current(
method is_omo_provider_current (line 406) | pub fn is_omo_provider_current(
method clear_omo_provider_current (line 425) | pub fn clear_omo_provider_current(
method get_current_omo_provider (line 441) | pub fn get_current_omo_provider(
FILE: src-tauri/src/database/dao/proxy.rs
method get_global_proxy_config (line 17) | pub async fn get_global_proxy_config(&self) -> Result<GlobalProxyConfig,...
method update_global_proxy_config (line 54) | pub async fn update_global_proxy_config(
method get_default_cost_multiplier (line 80) | pub async fn get_default_cost_multiplier(&self, app_type: &str) -> Resul...
method set_default_cost_multiplier (line 101) | pub async fn set_default_cost_multiplier(
method get_pricing_model_source (line 139) | pub async fn get_pricing_model_source(&self, app_type: &str) -> Result<S...
method set_pricing_model_source (line 160) | pub async fn set_pricing_model_source(
method get_proxy_config_for_app (line 191) | pub async fn get_proxy_config_for_app(
method update_proxy_config_for_app (line 251) | pub async fn update_proxy_config_for_app(
method ensure_proxy_config_row_exists (line 295) | fn ensure_proxy_config_row_exists(&self, app_type: &str) -> Result<(), A...
method init_proxy_config_rows (line 337) | async fn init_proxy_config_rows(&self) -> Result<(), AppError> {
method get_proxy_config (line 383) | pub async fn get_proxy_config(&self) -> Result<ProxyConfig, AppError> {
method update_proxy_config (line 422) | pub async fn update_proxy_config(&self, config: ProxyConfig) -> Result<(...
method set_live_takeover_active (line 452) | pub async fn set_live_takeover_active(&self, _active: bool) -> Result<()...
method is_live_takeover_active (line 461) | pub async fn is_live_takeover_active(&self) -> Result<bool, AppError> {
method get_provider_health (line 476) | pub async fn get_provider_health(
method update_provider_health (line 525) | pub async fn update_provider_health(
method update_provider_health_with_threshold (line 541) | pub async fn update_provider_health_with_threshold(
method reset_provider_health (line 606) | pub async fn reset_provider_health(
method clear_provider_health_for_app (line 625) | pub async fn clear_provider_health_for_app(&self, app_type: &str) -> Res...
method clear_all_provider_health (line 639) | pub async fn clear_all_provider_health(&self) -> Result<(), AppError> {
method get_circuit_breaker_config (line 655) | pub async fn get_circuit_breaker_config(
method update_circuit_breaker_config (line 694) | pub async fn update_circuit_breaker_config(
method save_live_backup (line 725) | pub async fn save_live_backup(
method has_any_live_backup (line 745) | pub async fn has_any_live_backup(&self) -> Result<bool, AppError> {
method get_live_backup (line 756) | pub async fn get_live_backup(&self, app_type: &str) -> Result<Option<Liv...
method delete_live_backup (line 779) | pub async fn delete_live_backup(&self, app_type: &str) -> Result<(), App...
method delete_all_live_backups (line 793) | pub async fn delete_all_live_backups(&self) -> Result<(), AppError> {
method get_proxy_flags_sync (line 809) | pub fn get_proxy_flags_sync(&self, app_type: &str) -> (bool, bool) {
method set_proxy_flags_sync (line 826) | pub fn set_proxy_flags_sync(
function test_default_cost_multiplier_round_trip (line 857) | async fn test_default_cost_multiplier_round_trip() -> Result<(), AppErro...
function test_default_cost_multiplier_validation (line 871) | async fn test_default_cost_multiplier_validation() -> Result<(), AppErro...
function test_pricing_model_source_round_trip_and_validation (line 891) | async fn test_pricing_model_source_round_trip_and_validation() -> Result...
FILE: src-tauri/src/database/dao/settings.rs
constant LEGACY_COMMON_CONFIG_MIGRATED_KEY (line 10) | const LEGACY_COMMON_CONFIG_MIGRATED_KEY: &'static str = "common_config_l...
method config_snippet_cleared_key (line 12) | fn config_snippet_cleared_key(app_type: &str) -> String {
method get_setting (line 17) | pub fn get_setting(&self, key: &str) -> Result<Option<String>, AppError> {
method set_setting (line 37) | pub fn set_setting(&self, key: &str, value: &str) -> Result<(), AppError> {
method get_config_snippet (line 50) | pub fn get_config_snippet(&self, app_type: &str) -> Result<Option<String...
method is_config_snippet_cleared (line 55) | pub fn is_config_snippet_cleared(&self, app_type: &str) -> Result<bool, ...
method set_config_snippet_cleared (line 63) | pub fn set_config_snippet_cleared(
method should_auto_extract_config_snippet (line 80) | pub fn should_auto_extract_config_snippet(&self, app_type: &str) -> Resu...
method is_legacy_common_config_migrated (line 86) | pub fn is_legacy_common_config_migrated(&self) -> Result<bool, AppError> {
method set_legacy_common_config_migrated (line 94) | pub fn set_legacy_common_config_migrated(&self, migrated: bool) -> Resul...
method set_config_snippet (line 109) | pub fn set_config_snippet(
constant GLOBAL_PROXY_URL_KEY (line 129) | const GLOBAL_PROXY_URL_KEY: &'static str = "global_proxy_url";
method get_global_proxy_url (line 135) | pub fn get_global_proxy_url(&self) -> Result<Option<String>, AppError> {
method set_global_proxy_url (line 143) | pub fn set_global_proxy_url(&self, url: Option<&str>) -> Result<(), AppE...
method get_proxy_takeover_enabled (line 168) | pub fn get_proxy_takeover_enabled(&self, app_type: &str) -> Result<bool,...
method set_proxy_takeover_enabled (line 183) | pub fn set_proxy_takeover_enabled(
method has_any_proxy_takeover (line 197) | pub fn has_any_proxy_takeover(&self) -> Result<bool, AppError> {
method clear_all_proxy_takeover (line 216) | pub fn clear_all_proxy_takeover(&self) -> Result<(), AppError> {
method get_rectifier_config (line 232) | pub fn get_rectifier_config(&self) -> Result<crate::proxy::types::Rectif...
method set_rectifier_config (line 241) | pub fn set_rectifier_config(
method get_optimizer_config (line 255) | pub fn get_optimizer_config(&self) -> Result<crate::proxy::types::Optimi...
method set_optimizer_config (line 264) | pub fn set_optimizer_config(
method get_log_config (line 276) | pub fn get_log_config(&self) -> Result<crate::proxy::types::LogConfig, A...
method set_log_config (line 285) | pub fn set_log_config(&self, config: &crate::proxy::types::LogConfig) ->...
FILE: src-tauri/src/database/dao/skills.rs
method get_all_installed_skills (line 20) | pub fn get_all_installed_skills(&self) -> Result<IndexMap<String, Instal...
method get_installed_skill (line 61) | pub fn get_installed_skill(&self, id: &str) -> Result<Option<InstalledSk...
method save_skill (line 99) | pub fn save_skill(&self, skill: &InstalledSkill) -> Result<(), AppError> {
method delete_skill (line 127) | pub fn delete_skill(&self, id: &str) -> Result<bool, AppError> {
method clear_skills (line 136) | pub fn clear_skills(&self) -> Result<(), AppError> {
method update_skill_apps (line 144) | pub fn update_skill_apps(&self, id: &str, apps: &SkillApps) -> Result<bo...
method get_skill_repos (line 158) | pub fn get_skill_repos(&self) -> Result<Vec<SkillRepo>, AppError> {
method save_skill_repo (line 185) | pub fn save_skill_repo(&self, repo: &SkillRepo) -> Result<(), AppError> {
method delete_skill_repo (line 196) | pub fn delete_skill_repo(&self, owner: &str, name: &str) -> Result<(), A...
method init_default_skill_repos (line 207) | pub fn init_default_skill_repos(&self) -> Result<usize, AppError> {
FILE: src-tauri/src/database/dao/stream_check.rs
method save_stream_check_log (line 9) | pub fn save_stream_check_log(
method get_stream_check_config (line 43) | pub fn get_stream_check_config(&self) -> Result<StreamCheckConfig, AppEr...
method cleanup_old_stream_check_logs (line 53) | pub fn cleanup_old_stream_check_logs(&self, retain_days: i64) -> Result<...
method save_stream_check_config (line 69) | pub fn save_stream_check_config(&self, config: &StreamCheckConfig) -> Re...
FILE: src-tauri/src/database/dao/universal_providers.rs
constant UNIVERSAL_PROVIDERS_KEY (line 11) | const UNIVERSAL_PROVIDERS_KEY: &str = "universal_providers";
method get_all_universal_providers (line 15) | pub fn get_all_universal_providers(
method get_universal_provider (line 36) | pub fn get_universal_provider(&self, id: &str) -> Result<Option<Universa...
method save_universal_provider (line 42) | pub fn save_universal_provider(&self, provider: &UniversalProvider) -> R...
method delete_universal_provider (line 49) | pub fn delete_universal_provider(&self, id: &str) -> Result<bool, AppErr...
method save_all_universal_providers (line 59) | fn save_all_universal_providers(
FILE: src-tauri/src/database/dao/usage_rollup.rs
method rollup_and_prune (line 12) | pub fn rollup_and_prune(&self, retain_days: i64) -> Result<u64, AppError> {
method do_rollup_and_prune (line 54) | fn do_rollup_and_prune(conn: &rusqlite::Connection, cutoff: i64) -> Resu...
function test_rollup_and_prune (line 117) | fn test_rollup_and_prune() -> Result<(), AppError> {
function test_rollup_noop_when_no_old_data (line 169) | fn test_rollup_noop_when_no_old_data() -> Result<(), AppError> {
function test_rollup_merges_with_existing (line 176) | fn test_rollup_merges_with_existing() -> Result<(), AppError> {
FILE: src-tauri/src/database/migration.rs
method migrate_from_json (line 12) | pub fn migrate_from_json(&self, config: &MultiAppConfig) -> Result<(), A...
method migrate_from_json_dry_run (line 28) | pub fn migrate_from_json_dry_run(config: &MultiAppConfig) -> Result<(), ...
method migrate_from_json_tx (line 45) | fn migrate_from_json_tx(
method migrate_providers (line 68) | fn migrate_providers(
method migrate_mcp_servers (line 121) | fn migrate_mcp_servers(
method migrate_prompts (line 152) | fn migrate_prompts(
method migrate_skills (line 191) | fn migrate_skills(
method migrate_common_config (line 217) | fn migrate_common_config(
FILE: src-tauri/src/database/mod.rs
constant SCHEMA_VERSION (line 47) | pub(crate) const SCHEMA_VERSION: i32 = 6;
function to_json_string (line 50) | pub(crate) fn to_json_string<T: Serialize>(value: &T) -> Result<String, ...
type Database (line 71) | pub struct Database {
method init (line 90) | pub fn init() -> Result<Self, AppError> {
method memory (line 157) | pub fn memory() -> Result<Self, AppError> {
method get_auto_vacuum_mode (line 176) | pub(crate) fn get_auto_vacuum_mode(conn: &Connection) -> Result<i32, A...
method has_user_tables (line 181) | fn has_user_tables(conn: &Connection) -> Result<bool, AppError> {
method ensure_incremental_auto_vacuum_on_conn (line 192) | pub(crate) fn ensure_incremental_auto_vacuum_on_conn(
method ensure_incremental_auto_vacuum (line 215) | pub(crate) fn ensure_incremental_auto_vacuum(&self) -> Result<bool, Ap...
method is_mcp_table_empty (line 250) | pub fn is_mcp_table_empty(&self) -> Result<bool, AppError> {
method is_prompts_table_empty (line 259) | pub fn is_prompts_table_empty(&self) -> Result<bool, AppError> {
function register_db_change_hook (line 75) | fn register_db_change_hook(conn: &Connection) {
FILE: src-tauri/src/database/schema.rs
type LegacySkillMigrationRow (line 11) | struct LegacySkillMigrationRow {
method create_tables (line 18) | pub(crate) fn create_tables(&self) -> Result<(), AppError> {
method create_tables_on_conn (line 24) | pub(crate) fn create_tables_on_conn(conn: &Connection) -> Result<(), App...
method apply_schema_migrations (line 341) | pub(crate) fn apply_schema_migrations(&self) -> Result<(), AppError> {
method apply_schema_migrations_on_conn (line 347) | pub(crate) fn apply_schema_migrations_on_conn(conn: &Connection) -> Resu...
method migrate_v0_to_v1 (line 422) | fn migrate_v0_to_v1(conn: &Connection) -> Result<(), AppError> {
method migrate_v1_to_v2 (line 482) | fn migrate_v1_to_v2(conn: &Connection) -> Result<(), AppError> {
method migrate_proxy_config_to_per_app (line 619) | fn migrate_proxy_config_to_per_app(conn: &Connection) -> Result<(), AppE...
method migrate_skills_table (line 756) | fn migrate_skills_table(conn: &Connection) -> Result<(), AppError> {
method migrate_v2_to_v3 (line 847) | fn migrate_v2_to_v3(conn: &Connection) -> Result<(), AppError> {
method migrate_v3_to_v4 (line 928) | fn migrate_v3_to_v4(conn: &Connection) -> Result<(), AppError> {
method migrate_v4_to_v5 (line 950) | fn migrate_v4_to_v5(conn: &Connection) -> Result<(), AppError> {
method migrate_v5_to_v6 (line 974) | fn migrate_v5_to_v6(conn: &Connection) -> Result<(), AppError> {
method seed_model_pricing (line 1051) | fn seed_model_pricing(conn: &Connection) -> Result<(), AppError> {
method ensure_model_pricing_seeded (line 1419) | pub fn ensure_model_pricing_seeded(&self) -> Result<(), AppError> {
method ensure_model_pricing_seeded_on_conn (line 1424) | fn ensure_model_pricing_seeded_on_conn(conn: &Connection) -> Result<(), ...
method get_user_version (line 1431) | pub(crate) fn get_user_version(conn: &Connection) -> Result<i32, AppErro...
method set_user_version (line 1436) | pub(crate) fn set_user_version(conn: &Connection, version: i32) -> Resul...
method validate_identifier (line 1446) | fn validate_identifier(s: &str, kind: &str) -> Result<(), AppError> {
method table_exists (line 1458) | pub(crate) fn table_exists(conn: &Connection, table: &str) -> Result<boo...
method has_column (line 1478) | pub(crate) fn has_column(
method add_column_if_missing (line 1504) | fn add_column_if_missing(
FILE: src-tauri/src/database/tests.rs
constant LEGACY_SCHEMA_SQL (line 14) | const LEGACY_SCHEMA_SQL: &str = r#"
constant V3_8_SCHEMA_V1_SQL (line 57) | const V3_8_SCHEMA_V1_SQL: &str = r#"
type ColumnInfo (line 124) | struct ColumnInfo {
function get_column_info (line 130) | fn get_column_info(conn: &Connection, table: &str, column: &str) -> Colu...
function normalize_default (line 148) | fn normalize_default(default: &Option<String>) -> Option<String> {
function schema_migration_sets_user_version_when_missing (line 155) | fn schema_migration_sets_user_version_when_missing() {
function schema_migration_rejects_future_version (line 173) | fn schema_migration_rejects_future_version() {
function schema_migration_adds_missing_columns_for_providers (line 187) | fn schema_migration_adds_missing_columns_for_providers() {
function schema_migration_aligns_column_defaults_and_types (line 228) | fn schema_migration_aligns_column_defaults_and_types() {
function schema_create_tables_include_pricing_model_columns (line 272) | fn schema_create_tables_include_pricing_model_columns() {
function schema_migration_v4_adds_pricing_model_columns (line 295) | fn schema_migration_v4_adds_pricing_model_columns() {
function schema_create_tables_repairs_legacy_proxy_config_singleton_to_per_app (line 341) | fn schema_create_tables_repairs_legacy_proxy_config_singleton_to_per_app...
function migration_from_v3_8_schema_v1_to_current_schema_v3 (line 388) | fn migration_from_v3_8_schema_v1_to_current_schema_v3() {
function schema_dry_run_does_not_write_to_disk (line 525) | fn schema_dry_run_does_not_write_to_disk() {
function dry_run_validates_schema_compatibility (line 549) | fn dry_run_validates_schema_compatibility() {
function schema_model_pricing_is_seeded_on_init (line 599) | fn schema_model_pricing_is_seeded_on_init() {
function ensure_incremental_auto_vacuum_rebuilds_existing_file_db (line 658) | fn ensure_incremental_auto_vacuum_rebuilds_existing_file_db() {
FILE: src-tauri/src/deeplink/mcp.rs
type McpImportResult (line 17) | pub struct McpImportResult {
type McpImportError (line 29) | pub struct McpImportError {
function import_mcp_from_deeplink (line 40) | pub fn import_mcp_from_deeplink(
function parse_mcp_apps (line 164) | pub(crate) fn parse_mcp_apps(apps_str: &str) -> Result<McpApps, AppError> {
FILE: src-tauri/src/deeplink/mod.rs
type DeepLinkImportRequest (line 36) | pub struct DeepLinkImportRequest {
FILE: src-tauri/src/deeplink/parser.rs
function parse_deeplink_url (line 15) | pub fn parse_deeplink_url(url_str: &str) -> Result<DeepLinkImportRequest...
function parse_provider_deeplink (line 71) | fn parse_provider_deeplink(
function parse_prompt_deeplink (line 180) | fn parse_prompt_deeplink(
function parse_mcp_deeplink (line 250) | fn parse_mcp_deeplink(
function parse_skill_deeplink (line 315) | fn parse_skill_deeplink(
FILE: src-tauri/src/deeplink/prompt.rs
function import_prompt_from_deeplink (line 15) | pub fn import_prompt_from_deeplink(
FILE: src-tauri/src/deeplink/provider.rs
function import_provider_from_deeplink (line 23) | pub fn import_provider_from_deeplink(
function build_provider_from_request (line 140) | pub(crate) fn build_provider_from_request(
function get_primary_endpoint (line 174) | fn get_primary_endpoint(request: &DeepLinkImportRequest) -> String {
function build_provider_meta (line 184) | fn build_provider_meta(request: &DeepLinkImportRequest) -> Result<Option...
function build_claude_settings (line 241) | fn build_claude_settings(request: &DeepLinkImportRequest) -> serde_json:...
function build_codex_settings (line 281) | fn build_codex_settings(request: &DeepLinkImportRequest) -> serde_json::...
function build_gemini_settings (line 352) | fn build_gemini_settings(request: &DeepLinkImportRequest) -> serde_json:...
function build_opencode_settings (line 369) | fn build_opencode_settings(request: &DeepLinkImportRequest) -> serde_jso...
function build_openclaw_settings (line 395) | fn build_openclaw_settings(request: &DeepLinkImportRequest) -> serde_jso...
function parse_and_merge_config (line 431) | pub fn parse_and_merge_config(
function merge_claude_config (line 505) | fn merge_claude_config(
function merge_codex_config (line 570) | fn merge_codex_config(
function merge_gemini_config (line 619) | fn merge_gemini_config(
function merge_additive_config (line 664) | fn merge_additive_config(
function extract_codex_base_url (line 702) | fn extract_codex_base_url(toml_value: &toml::Value) -> Option<String> {
FILE: src-tauri/src/deeplink/skill.rs
function import_skill_from_deeplink (line 11) | pub fn import_skill_from_deeplink(
FILE: src-tauri/src/deeplink/tests.rs
function test_parse_valid_claude_deeplink (line 19) | fn test_parse_valid_claude_deeplink() {
function test_parse_deeplink_with_notes (line 38) | fn test_parse_deeplink_with_notes() {
function test_parse_invalid_scheme (line 47) | fn test_parse_invalid_scheme() {
function test_parse_unsupported_version (line 56) | fn test_parse_unsupported_version() {
function test_parse_missing_required_field (line 68) | fn test_parse_missing_required_field() {
function test_validate_invalid_url (line 85) | fn test_validate_invalid_url() {
function test_validate_invalid_scheme (line 91) | fn test_validate_invalid_scheme() {
function test_infer_homepage (line 101) | fn test_infer_homepage() {
function test_build_gemini_provider_with_model (line 121) | fn test_build_gemini_provider_with_model() {
function test_build_gemini_provider_without_model (line 174) | fn test_build_gemini_provider_without_model() {
function test_parse_and_merge_config_claude (line 220) | fn test_parse_and_merge_config_claude() {
function test_parse_and_merge_config_url_override (line 271) | fn test_parse_and_merge_config_url_override() {
function test_import_prompt_allows_space_in_base64_content (line 324) | fn test_import_prompt_allows_space_in_base64_content() {
function test_parse_mcp_apps (line 348) | fn test_parse_mcp_apps() {
function test_parse_prompt_deeplink (line 364) | fn test_parse_prompt_deeplink() {
function test_parse_mcp_deeplink (line 382) | fn test_parse_mcp_deeplink() {
function test_parse_skill_deeplink (line 398) | fn test_parse_skill_deeplink() {
function test_parse_multiple_endpoints_comma_separated (line 413) | fn test_parse_multiple_endpoints_comma_separated() {
function test_parse_single_endpoint_backward_compatible (line 427) | fn test_parse_single_endpoint_backward_compatible() {
function test_parse_endpoints_with_spaces_trimmed (line 440) | fn test_parse_endpoints_with_spaces_trimmed() {
function test_infer_homepage_from_endpoint_without_homepage (line 450) | fn test_infer_homepage_from_endpoint_without_homepage() {
FILE: src-tauri/src/deeplink/utils.rs
function validate_url (line 10) | pub fn validate_url(url_str: &str, field_name: &str) -> Result<(), AppEr...
function decode_base64_param (line 30) | pub fn decode_base64_param(field: &str, raw: &str) -> Result<Vec<u8>, Ap...
function infer_homepage_from_endpoint (line 88) | pub fn infer_homepage_from_endpoint(endpoint: &str) -> Option<String> {
FILE: src-tauri/src/error.rs
type AppError (line 7) | pub enum AppError {
method io (line 64) | pub fn io(path: impl AsRef<Path>, source: std::io::Error) -> Self {
method json (line 71) | pub fn json(path: impl AsRef<Path>, source: serde_json::Error) -> Self {
method toml (line 78) | pub fn toml(path: impl AsRef<Path>, source: toml::de::Error) -> Self {
method localized (line 85) | pub fn localized(key: &'static str, zh: impl Into<String>, en: impl In...
method from (line 95) | fn from(err: PoisonError<T>) -> Self {
method from (line 101) | fn from(err: rusqlite::Error) -> Self {
method serialize (line 113) | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
method from (line 107) | fn from(err: AppError) -> Self {
function format_skill_error (line 122) | pub fn format_skill_error(
FILE: src-tauri/src/gemini_config.rs
function get_gemini_dir (line 9) | pub fn get_gemini_dir() -> PathBuf {
function get_gemini_env_path (line 18) | pub fn get_gemini_env_path() -> PathBuf {
function parse_env_file (line 26) | pub fn parse_env_file(content: &str) -> HashMap<String, String> {
function parse_env_file_strict (line 74) | pub fn parse_env_file_strict(content: &str) -> Result<HashMap<String, St...
function serialize_env_file (line 126) | pub fn serialize_env_file(map: &HashMap<String, String>) -> String {
function read_gemini_env (line 143) | pub fn read_gemini_env() -> Result<HashMap<String, String>, AppError> {
function write_gemini_env_atomic (line 156) | pub fn write_gemini_env_atomic(map: &HashMap<String, String>) -> Result<...
function env_to_json (line 193) | pub fn env_to_json(env_map: &HashMap<String, String>) -> Value {
function json_to_env (line 204) | pub fn json_to_env(settings: &Value) -> Result<HashMap<String, String>, ...
function validate_gemini_settings (line 224) | pub fn validate_gemini_settings(settings: &Value) -> Result<(), AppError> {
function validate_gemini_settings_strict (line 255) | pub fn validate_gemini_settings_strict(settings: &Value) -> Result<(), A...
function get_gemini_settings_path (line 281) | pub fn get_gemini_settings_path() -> PathBuf {
function update_selected_type (line 294) | fn update_selected_type(selected_type: &str) -> Result<(), AppError> {
function write_packycode_settings (line 351) | pub fn write_packycode_settings() -> Result<(), AppError> {
function write_google_oauth_settings (line 369) | pub fn write_google_oauth_settings() -> Result<(), AppError> {
function test_parse_env_file (line 378) | fn test_parse_env_file() {
function test_serialize_env_file (line 403) | fn test_serialize_env_file() {
function test_env_json_conversion (line 418) | fn test_env_json_conversion() {
function test_parse_env_file_strict_success (line 432) | fn test_parse_env_file_strict_success() {
function test_parse_env_file_strict_missing_equals (line 460) | fn test_parse_env_file_strict_missing_equals() {
function test_parse_env_file_strict_empty_key (line 476) | fn test_parse_env_file_strict_empty_key() {
function test_parse_env_file_strict_invalid_key_characters (line 492) | fn test_parse_env_file_strict_invalid_key_characters() {
function test_parse_env_file_lax_vs_strict (line 508) | fn test_parse_env_file_lax_vs_strict() {
function test_packycode_settings_structure (line 525) | fn test_packycode_settings_structure() {
function test_packycode_settings_merge (line 542) | fn test_packycode_settings_merge() {
function test_google_oauth_settings_structure (line 588) | fn test_google_oauth_settings_structure() {
function test_validate_empty_env_for_oauth (line 605) | fn test_validate_empty_env_for_oauth() {
function test_validate_env_with_api_key (line 617) | fn test_validate_env_with_api_key() {
function test_validate_env_without_api_key_relaxed (line 631) | fn test_validate_env_without_api_key_relaxed() {
function test_validate_invalid_env_type (line 646) | fn test_validate_invalid_env_type() {
FILE: src-tauri/src/gemini_mcp.rs
function user_config_path (line 10) | fn user_config_path() -> PathBuf {
function read_json_value (line 14) | fn read_json_value(path: &Path) -> Result<Value, AppError> {
function write_json_value (line 23) | fn write_json_value(path: &Path, value: &Value) -> Result<(), AppError> {
function read_mcp_servers_map (line 38) | pub fn read_mcp_servers_map() -> Result<std::collections::HashMap<String...
function set_mcp_servers_map (line 76) | pub fn set_mcp_servers_map(
FILE: src-tauri/src/init_status.rs
type InitErrorPayload (line 5) | pub struct InitErrorPayload {
function cell (line 12) | fn cell() -> &'static RwLock<Option<InitErrorPayload>> {
function set_init_error (line 17) | pub fn set_init_error(payload: InitErrorPayload) {
function get_init_error (line 24) | pub fn get_init_error() -> Option<InitErrorPayload> {
function migration_cell (line 34) | fn migration_cell() -> &'static RwLock<bool> {
function set_migration_success (line 38) | pub fn set_migration_success() {
function take_migration_success (line 45) | pub fn take_migration_success() -> bool {
type SkillsMigrationPayload (line 60) | pub struct SkillsMigrationPayload {
function skills_migration_cell (line 68) | fn skills_migration_cell() -> &'static RwLock<Option<SkillsMigrationPayl...
function set_skills_migration_result (line 72) | pub fn set_skills_migration_result(count: usize) {
function set_skills_migration_error (line 78) | pub fn set_skills_migration_error(error: String) {
function take_skills_migration_result (line 88) | pub fn take_skills_migration_result() -> Option<SkillsMigrationPayload> {
function init_error_roundtrip (line 101) | fn init_error_roundtrip() {
FILE: src-tauri/src/lib.rs
function redact_url_for_log (line 64) | fn redact_url_for_log(url_str: &str) -> String {
function handle_deeplink_url (line 100) | fn handle_deeplink_url(
function update_tray_menu (line 158) | async fn update_tray_menu(
function macos_tray_icon (line 179) | fn macos_tray_icon() -> Option<Image<'static>> {
function run (line 192) | pub fn run() {
function cleanup_before_exit (line 1212) | pub async fn cleanup_before_exit(app_handle: &tauri::AppHandle) {
function restore_proxy_state_on_startup (line 1257) | async fn restore_proxy_state_on_startup(state: &store::AppState) {
function initialize_common_config_snippets (line 1300) | fn initialize_common_config_snippets(state: &store::AppState) {
function is_chinese_locale (line 1384) | fn is_chinese_locale() -> bool {
function show_migration_error_dialog (line 1394) | fn show_migration_error_dialog(app: &tauri::AppHandle, error: &str) -> b...
function show_database_init_error_dialog (line 1445) | fn show_database_init_error_dialog(
FILE: src-tauri/src/main.rs
function main (line 4) | fn main() {
FILE: src-tauri/src/mcp/claude.rs
function should_sync_claude_mcp (line 11) | fn should_sync_claude_mcp() -> bool {
function collect_enabled_servers (line 18) | fn collect_enabled_servers(cfg: &McpConfig) -> HashMap<String, Value> {
function sync_enabled_to_claude (line 41) | pub fn sync_enabled_to_claude(config: &MultiAppConfig) -> Result<(), App...
function import_from_claude (line 51) | pub fn import_from_claude(config: &mut MultiAppConfig) -> Result<usize, ...
function sync_single_server_to_claude (line 115) | pub fn sync_single_server_to_claude(
function remove_server_from_claude (line 135) | pub fn remove_server_from_claude(id: &str) -> Result<(), AppError> {
FILE: src-tauri/src/mcp/codex.rs
function should_sync_codex_mcp (line 16) | fn should_sync_codex_mcp() -> bool {
function collect_enabled_servers (line 23) | fn collect_enabled_servers(cfg: &McpConfig) -> HashMap<String, Value> {
function import_from_codex (line 52) | pub fn import_from_codex(config: &mut MultiAppConfig) -> Result<usize, A...
function sync_enabled_to_codex (line 282) | pub fn sync_enabled_to_codex(config: &MultiAppConfig) -> Result<(), AppE...
function sync_single_server_to_codex (line 347) | pub fn sync_single_server_to_codex(
function remove_server_from_codex (line 405) | pub fn remove_server_from_codex(id: &str) -> Result<(), AppError> {
function json_value_to_toml_item (line 466) | fn json_value_to_toml_item(value: &Value, field_name: &str) -> Option<to...
function json_server_to_toml_table (line 561) | fn json_server_to_toml_table(spec: &Value) -> Result<toml_edit::Table, A...
FILE: src-tauri/src/mcp/gemini.rs
function should_sync_gemini_mcp (line 11) | fn should_sync_gemini_mcp() -> bool {
function collect_enabled_servers (line 18) | fn collect_enabled_servers(cfg: &McpConfig) -> HashMap<String, Value> {
function sync_enabled_to_gemini (line 41) | pub fn sync_enabled_to_gemini(config: &MultiAppConfig) -> Result<(), App...
function import_from_gemini (line 51) | pub fn import_from_gemini(config: &mut MultiAppConfig) -> Result<usize, ...
function sync_single_server_to_gemini (line 111) | pub fn sync_single_server_to_gemini(
function remove_server_from_gemini (line 130) | pub fn remove_server_from_gemini(id: &str) -> Result<(), AppError> {
FILE: src-tauri/src/mcp/opencode.rs
function should_sync_opencode_mcp (line 29) | fn should_sync_opencode_mcp() -> bool {
function convert_to_opencode_format (line 43) | pub fn convert_to_opencode_format(spec: &Value) -> Result<Value, AppErro...
function convert_from_opencode_format (line 115) | pub fn convert_from_opencode_format(spec: &Value) -> Result<Value, AppEr...
function sync_single_server_to_opencode (line 184) | pub fn sync_single_server_to_opencode(
function remove_server_from_opencode (line 201) | pub fn remove_server_from_opencode(id: &str) -> Result<(), AppError> {
function import_from_opencode (line 212) | pub fn import_from_opencode(config: &mut MultiAppConfig) -> Result<usize...
function test_convert_stdio_to_local (line 290) | fn test_convert_stdio_to_local() {
function test_convert_sse_to_remote (line 311) | fn test_convert_sse_to_remote() {
function test_convert_local_to_stdio (line 326) | fn test_convert_local_to_stdio() {
function test_convert_remote_to_sse (line 342) | fn test_convert_remote_to_sse() {
FILE: src-tauri/src/mcp/validation.rs
function validate_server_spec (line 8) | pub fn validate_server_spec(spec: &Value) -> Result<(), AppError> {
function extract_server_spec (line 54) | pub fn extract_server_spec(entry: &Value) -> Result<Value, AppError> {
FILE: src-tauri/src/openclaw_config.rs
constant OPENCLAW_DEFAULT_SOURCE (line 24) | const OPENCLAW_DEFAULT_SOURCE: &str =
constant OPENCLAW_TOOLS_PROFILES (line 26) | const OPENCLAW_TOOLS_PROFILES: &[&str] = &["minimal", "coding", "messagi...
function get_openclaw_dir (line 36) | pub fn get_openclaw_dir() -> PathBuf {
function get_openclaw_config_path (line 49) | pub fn get_openclaw_config_path() -> PathBuf {
function default_openclaw_config_value (line 53) | fn default_openclaw_config_value() -> Value {
function openclaw_write_lock (line 62) | fn openclaw_write_lock() -> &'static Mutex<()> {
type OpenClawHealthWarning (line 74) | pub struct OpenClawHealthWarning {
type OpenClawWriteOutcome (line 84) | pub struct OpenClawWriteOutcome {
type OpenClawProviderConfig (line 94) | pub struct OpenClawProviderConfig {
type OpenClawModelEntry (line 112) | pub struct OpenClawModelEntry {
type OpenClawModelCost (line 128) | pub struct OpenClawModelCost {
type OpenClawDefaultModel (line 137) | pub struct OpenClawDefaultModel {
type OpenClawModelCatalogEntry (line 147) | pub struct OpenClawModelCatalogEntry {
type OpenClawAgentsDefaults (line 156) | pub struct OpenClawAgentsDefaults {
type OpenClawAgents (line 168) | pub struct OpenClawAgents {
type OpenClawEnvConfig (line 177) | pub struct OpenClawEnvConfig {
type OpenClawToolsConfig (line 184) | pub struct OpenClawToolsConfig {
function read_openclaw_config (line 202) | pub fn read_openclaw_config() -> Result<Value, AppError> {
function scan_openclaw_config_health (line 216) | pub fn scan_openclaw_config_health() -> Result<Vec<OpenClawHealthWarning...
type OpenClawConfigDocument (line 233) | struct OpenClawConfigDocument {
method load (line 240) | fn load() -> Result<Self, AppError> {
method set_root_section (line 265) | fn set_root_section(&mut self, key: &str, value: &Value) -> Result<(),...
method save (line 327) | fn save(self) -> Result<OpenClawWriteOutcome, AppError> {
function write_root_section (line 382) | fn write_root_section(section: &str, value: &Value) -> Result<OpenClawWr...
function create_openclaw_backup (line 388) | fn create_openclaw_backup(source: &str) -> Result<PathBuf, AppError> {
function cleanup_openclaw_backups (line 408) | fn cleanup_openclaw_backups(dir: &Path) -> Result<(), AppError> {
function ensure_object (line 440) | fn ensure_object(value: &mut Value) -> &mut Map<String, Value> {
function ensure_kvp_context (line 449) | fn ensure_kvp_context(pair: &mut RtJSONKeyValuePair) -> &mut RtKeyValueP...
function extract_trailing_indent (line 455) | fn extract_trailing_indent(separator_ws: &str) -> String {
function derive_closing_ws_from_separator (line 462) | fn derive_closing_ws_from_separator(separator_ws: &str) -> String {
function derive_entry_separator (line 480) | fn derive_entry_separator(leading_ws: &str) -> String {
function value_to_rt_value (line 492) | fn value_to_rt_value(value: &Value, parent_indent: &str) -> Result<RtJSO...
function reindent_json5_block (line 509) | fn reindent_json5_block(source: &str, parent_indent: &str) -> String {
function normalize_json_five_output (line 529) | fn normalize_json_five_output(source: &str) -> String {
function make_root_pair (line 533) | fn make_root_pair(key: &str, value: RtJSONValue, closing_ws: String) -> ...
function make_json5_key (line 543) | fn make_json5_key(key: &str) -> RtJSONValue {
function is_identifier_key (line 551) | fn is_identifier_key(key: &str) -> bool {
function json5_key_name (line 561) | fn json5_key_name(key: &RtJSONValue) -> Option<&str> {
function warning (line 570) | fn warning(code: &str, message: impl Into<String>, path: Option<&str>) -...
function scan_openclaw_health_from_value (line 578) | fn scan_openclaw_health_from_value(config: &Value) -> Vec<OpenClawHealth...
function remove_legacy_timeout (line 631) | fn remove_legacy_timeout(defaults_value: &mut Value) {
function get_providers (line 644) | pub fn get_providers() -> Result<Map<String, Value>, AppError> {
function get_provider (line 655) | pub fn get_provider(id: &str) -> Result<Option<Value>, AppError> {
function set_provider (line 662) | pub fn set_provider(id: &str, provider_config: Value) -> Result<OpenClaw...
function remove_provider (line 686) | pub fn remove_provider(id: &str) -> Result<OpenClawWriteOutcome, AppErro...
function get_typed_providers (line 716) | pub fn get_typed_providers() -> Result<IndexMap<String, OpenClawProvider...
function set_typed_provider (line 735) | pub fn set_typed_provider(
function get_default_model (line 748) | pub fn get_default_model() -> Result<Option<OpenClawDefaultModel>, AppEr...
function set_default_model (line 765) | pub fn set_default_model(model: &OpenClawDefaultModel) -> Result<OpenCla...
function get_model_catalog (line 787) | pub fn get_model_catalog() -> Result<Option<HashMap<String, OpenClawMode...
function set_model_catalog (line 804) | pub fn set_model_catalog(
function get_agents_defaults (line 832) | pub fn get_agents_defaults() -> Result<Option<OpenClawAgentsDefaults>, A...
function set_agents_defaults (line 845) | pub fn set_agents_defaults(
function get_env_config (line 871) | pub fn get_env_config() -> Result<OpenClawEnvConfig, AppError> {
function set_env_config (line 885) | pub fn set_env_config(env: &OpenClawEnvConfig) -> Result<OpenClawWriteOu...
function get_tools_config (line 895) | pub fn get_tools_config() -> Result<OpenClawToolsConfig, AppError> {
function set_tools_config (line 912) | pub fn set_tools_config(tools: &OpenClawToolsConfig) -> Result<OpenClawW...
function test_guard (line 922) | fn test_guard() -> std::sync::MutexGuard<'static, ()> {
function with_test_paths (line 929) | fn with_test_paths<T>(source: &str, test: impl FnOnce(&Path) -> T) -> T {
function scan_health_detects_known_openclaw_issues (line 953) | fn scan_health_detects_known_openclaw_issues() {
function default_model_write_preserves_top_level_comments (line 972) | fn default_model_write_preserves_top_level_comments() {
function default_model_noop_write_skips_backup (line 1000) | fn default_model_noop_write_skips_backup() {
function save_detects_external_conflict (line 1034) | fn save_detects_external_conflict() {
FILE: src-tauri/src/opencode_config.rs
function get_opencode_dir (line 9) | pub fn get_opencode_dir() -> PathBuf {
function get_opencode_config_path (line 19) | pub fn get_opencode_config_path() -> PathBuf {
function get_opencode_env_path (line 24) | pub fn get_opencode_env_path() -> PathBuf {
function read_opencode_config (line 28) | pub fn read_opencode_config() -> Result<Value, AppError> {
function write_opencode_config (line 41) | pub fn write_opencode_config(config: &Value) -> Result<(), AppError> {
function get_providers (line 49) | pub fn get_providers() -> Result<Map<String, Value>, AppError> {
function set_provider (line 58) | pub fn set_provider(id: &str, config: Value) -> Result<(), AppError> {
function remove_provider (line 75) | pub fn remove_provider(id: &str) -> Result<(), AppError> {
function get_typed_providers (line 85) | pub fn get_typed_providers() -> Result<IndexMap<String, OpenCodeProvider...
function set_typed_provider (line 103) | pub fn set_typed_provider(id: &str, config: &OpenCodeProviderConfig) -> ...
function get_mcp_servers (line 108) | pub fn get_mcp_servers() -> Result<Map<String, Value>, AppError> {
function set_mcp_server (line 117) | pub fn set_mcp_server(id: &str, config: Value) -> Result<(), AppError> {
function remove_mcp_server (line 131) | pub fn remove_mcp_server(id: &str) -> Result<(), AppError> {
function add_plugin (line 141) | pub fn add_plugin(plugin_name: &str) -> Result<(), AppError> {
function remove_plugin_by_prefix (line 182) | pub fn remove_plugin_by_prefix(prefix: &str) -> Result<(), AppError> {
FILE: src-tauri/src/panic_hook.rs
constant APP_VERSION (line 13) | const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
function init_app_config_dir (line 17) | pub fn init_app_config_dir(dir: PathBuf) {
function default_app_config_dir (line 22) | fn default_app_config_dir() -> PathBuf {
function get_app_config_dir (line 29) | fn get_app_config_dir() -> PathBuf {
function get_crash_log_path (line 37) | fn get_crash_log_path() -> PathBuf {
function get_log_dir (line 42) | pub fn get_log_dir() -> PathBuf {
function get_system_info (line 47) | fn get_system_info() -> String {
function setup_panic_hook (line 80) | pub fn setup_panic_hook() {
function test_crash_log_path (line 192) | fn test_crash_log_path() {
function test_system_info (line 199) | fn test_system_info() {
FILE: src-tauri/src/prompt.rs
type Prompt (line 4) | pub struct Prompt {
FILE: src-tauri/src/prompt_files.rs
function prompt_file_path (line 12) | pub fn prompt_file_path(app: &AppType) -> Result<PathBuf, AppError> {
function get_base_dir_with_fallback (line 32) | fn get_base_dir_with_fallback(
FILE: src-tauri/src/provider.rs
type Provider (line 10) | pub struct Provider {
method with_id (line 47) | pub fn with_id(
type ProviderManager (line 72) | pub struct ProviderManager {
method get_all_providers (line 317) | pub fn get_all_providers(&self) -> &IndexMap<String, Provider> {
type UsageScript (line 79) | pub struct UsageScript {
type UsageData (line 113) | pub struct UsageData {
type UsageResult (line 137) | pub struct UsageResult {
type ProviderTestConfig (line 147) | pub struct ProviderTestConfig {
type ProviderProxyConfig (line 173) | pub struct ProviderProxyConfig {
type AuthBindingSource (line 197) | pub enum AuthBindingSource {
type AuthBinding (line 207) | pub struct AuthBinding {
type ProviderMeta (line 221) | pub struct ProviderMeta {
method managed_account_id_for (line 298) | pub fn managed_account_id_for(&self, auth_provider: &str) -> Option<St...
type UniversalProviderApps (line 328) | pub struct UniversalProviderApps {
type ClaudeModelConfig (line 339) | pub struct ClaudeModelConfig {
type CodexModelConfig (line 359) | pub struct CodexModelConfig {
type GeminiModelConfig (line 371) | pub struct GeminiModelConfig {
type UniversalProviderModels (line 379) | pub struct UniversalProviderModels {
type UniversalProvider (line 390) | pub struct UniversalProvider {
method new (line 438) | pub fn new(
method to_claude_provider (line 464) | pub fn to_claude_provider(&self) -> Option<Provider> {
method to_codex_provider (line 511) | pub fn to_codex_provider(&self) -> Option<Provider> {
method to_gemini_provider (line 576) | pub fn to_gemini_provider(&self) -> Option<Provider> {
type OpenCodeProviderConfig (line 627) | pub struct OpenCodeProviderConfig {
method default (line 645) | fn default() -> Self {
type OpenCodeProviderOptions (line 657) | pub struct OpenCodeProviderOptions {
type OpenCodeModel (line 678) | pub struct OpenCodeModel {
type OpenCodeModelLimit (line 698) | pub struct OpenCodeModelLimit {
function provider_meta_serializes_pricing_model_source (line 717) | fn provider_meta_serializes_pricing_model_source() {
function provider_meta_omits_pricing_model_source_when_none (line 733) | fn provider_meta_omits_pricing_model_source_when_none() {
function provider_with_id_populates_defaults (line 741) | fn provider_with_id_populates_defaults() {
function provider_manager_get_all_providers_returns_map (line 767) | fn provider_manager_get_all_providers_returns_map() {
function universal_provider_to_claude_provider_uses_models (line 782) | fn universal_provider_to_claude_provider_uses_models() {
function universal_provider_to_claude_provider_disabled_returns_none (line 834) | fn universal_provider_to_claude_provider_disabled_returns_none() {
function universal_provider_to_codex_provider_appends_v1 (line 847) | fn universal_provider_to_codex_provider_appends_v1() {
function universal_provider_to_codex_provider_keeps_v1_suffix (line 879) | fn universal_provider_to_codex_provider_keeps_v1_suffix() {
function universal_provider_to_codex_provider_disabled_returns_none (line 900) | fn universal_provider_to_codex_provider_disabled_returns_none() {
function universal_provider_to_gemini_provider_defaults_model (line 913) | fn universal_provider_to_gemini_provider_defaults_model() {
function universal_provider_to_gemini_provider_uses_model (line 935) | fn universal_provider_to_gemini_provider_uses_model() {
function opencode_provider_config_defaults (line 960) | fn opencode_provider_config_defaults() {
function universal_codex_provider_origin_base_url_adds_v1 (line 972) | fn universal_codex_provider_origin_base_url_adds_v1() {
function universal_codex_provider_custom_prefix_does_not_force_v1 (line 993) | fn universal_codex_provider_custom_prefix_does_not_force_v1() {
FILE: src-tauri/src/provider_defaults.rs
type ProviderIcon (line 7) | pub struct ProviderIcon {
function infer_provider_icon (line 187) | pub fn infer_provider_icon(provider_name: &str) -> Option<ProviderIcon> {
function test_exact_match (line 210) | fn test_exact_match() {
function test_fuzzy_match (line 219) | fn test_fuzzy_match() {
function test_case_insensitive (line 227) | fn test_case_insensitive() {
function test_no_match (line 234) | fn test_no_match() {
FILE: src-tauri/src/proxy/body_filter.rs
function filter_private_params (line 40) | pub fn filter_private_params(body: Value) -> Value {
function filter_private_params_with_whitelist (line 66) | pub fn filter_private_params_with_whitelist(body: Value, whitelist: &[St...
function filter_recursive_with_whitelist (line 72) | fn filter_recursive_with_whitelist(
function test_filter_top_level_private_params (line 118) | fn test_filter_top_level_private_params() {
function test_filter_nested_private_params (line 135) | fn test_filter_nested_private_params() {
function test_filter_deeply_nested (line 171) | fn test_filter_deeply_nested() {
function test_filter_array_of_objects (line 198) | fn test_filter_array_of_objects() {
function test_no_private_params (line 217) | fn test_no_private_params() {
function test_empty_object (line 230) | fn test_empty_object() {
function test_primitive_values (line 237) | fn test_primitive_values() {
function test_whitelist_preserves_private_params (line 246) | fn test_whitelist_preserves_private_params() {
function test_whitelist_nested (line 267) | fn test_whitelist_nested() {
function test_empty_whitelist_same_as_default (line 286) | fn test_empty_whitelist_same_as_default() {
FILE: src-tauri/src/proxy/cache_injector.rs
function inject (line 9) | pub fn inject(body: &mut Value, config: &OptimizerConfig) {
function make_cache_control (line 116) | fn make_cache_control(ttl: &str) -> Value {
function count_existing (line 124) | fn count_existing(body: &Value) -> usize {
function upgrade_existing_ttl (line 155) | fn upgrade_existing_ttl(body: &mut Value, ttl: &str) {
function default_config (line 194) | fn default_config() -> OptimizerConfig {
function test_empty_body_no_injection (line 204) | fn test_empty_body_no_injection() {
function test_inject_three_breakpoints (line 213) | fn test_inject_three_breakpoints() {
function test_existing_four_breakpoints_only_upgrades_ttl (line 240) | fn test_existing_four_breakpoints_only_upgrades_ttl() {
function test_existing_two_injects_two_more (line 270) | fn test_existing_two_injects_two_more() {
function test_system_string_converted_to_array (line 293) | fn test_system_string_converted_to_array() {
function test_ttl_5m_no_ttl_field (line 311) | fn test_ttl_5m_no_ttl_field() {
function test_disabled_no_change (line 330) | fn test_disabled_no_change() {
function test_skip_thinking_blocks_in_assistant (line 349) | fn test_skip_thinking_blocks_in_assistant() {
FILE: src-tauri/src/proxy/circuit_breaker.rs
type CircuitState (line 15) | pub enum CircuitState {
method fmt (line 25) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type CircuitBreakerConfig (line 37) | pub struct CircuitBreakerConfig {
method default (line 51) | fn default() -> Self {
type CircuitBreaker (line 63) | pub struct CircuitBreaker {
method new (line 94) | pub fn new(config: CircuitBreakerConfig) -> Self {
method update_config (line 108) | pub async fn update_config(&self, new_config: CircuitBreakerConfig) {
method is_available (line 120) | pub async fn is_available(&self) -> bool {
method allow_request (line 144) | pub async fn allow_request(&self) -> AllowResult {
method record_success (line 190) | pub async fn record_success(&self, used_half_open_permit: bool) {
method record_failure (line 217) | pub async fn record_failure(&self, used_half_open_permit: bool) {
method get_state (line 279) | pub async fn get_state(&self) -> CircuitState {
method get_stats (line 285) | pub async fn get_stats(&self) -> CircuitBreakerStats {
method reset (line 297) | pub async fn reset(&self) {
method allow_half_open_probe (line 302) | fn allow_half_open_probe(&self) -> AllowResult {
method release_half_open_permit (line 326) | pub fn release_half_open_permit(&self) {
method transition_to_open (line 346) | async fn transition_to_open(&self) {
method transition_to_half_open (line 354) | async fn transition_to_half_open(&self) {
method transition_to_closed (line 367) | async fn transition_to_closed(&self) {
type AllowResult (line 87) | pub struct AllowResult {
type CircuitBreakerStats (line 380) | pub struct CircuitBreakerStats {
function test_circuit_breaker_closed_to_open (line 393) | async fn test_circuit_breaker_closed_to_open() {
function test_circuit_breaker_half_open_to_closed (line 415) | async fn test_circuit_breaker_half_open_to_closed() {
function test_half_open_transition_does_not_reset_inflight_permit (line 441) | async fn test_half_open_transition_does_not_reset_inflight_permit() {
function test_circuit_breaker_reset (line 465) | async fn test_circuit_breaker_reset() {
FILE: src-tauri/src/proxy/error.rs
type ProxyError (line 10) | pub enum ProxyError {
method into_response (line 81) | fn into_response(self) -> Response {
type ErrorCategory (line 180) | pub enum ErrorCategory {
function categorize_error (line 191) | pub fn categorize_error(error: &reqwest::Error) -> ErrorCategory {
FILE: src-tauri/src/proxy/error_mapper.rs
function map_proxy_error_to_status (line 16) | pub fn map_proxy_error_to_status(error: &ProxyError) -> u16 {
function get_error_message (line 54) | pub fn get_error_message(error: &ProxyError) -> String {
function test_map_upstream_error (line 81) | fn test_map_upstream_error() {
function test_map_timeout_error (line 90) | fn test_map_timeout_error() {
function test_map_connection_error (line 96) | fn test_map_connection_error() {
function test_map_no_provider_error (line 102) | fn test_map_no_provider_error() {
function test_get_error_message (line 108) | fn test_get_error_message() {
FILE: src-tauri/src/proxy/failover_switch.rs
type FailoverSwitchManager (line 22) | pub struct FailoverSwitchManager {
method new (line 29) | pub fn new(db: Arc<Database>) -> Self {
method try_switch (line 44) | pub async fn try_switch(
method do_switch (line 77) | async fn do_switch(
FILE: src-tauri/src/proxy/forwarder.rs
constant HEADER_BLACKLIST (line 34) | const HEADER_BLACKLIST: &[&str] = &[
type ForwardResult (line 81) | pub struct ForwardResult {
type ForwardError (line 86) | pub struct ForwardError {
type RequestForwarder (line 91) | pub struct RequestForwarder {
method new (line 112) | pub fn new(
method forward_with_retry (line 146) | pub async fn forward_with_retry(
method forward (line 784) | async fn forward(
method categorize_proxy_error (line 1017) | fn categorize_proxy_error(&self, error: &ProxyError) -> ErrorCategory {
function extract_error_message (line 1041) | fn extract_error_message(error: &ProxyError) -> Option<String> {
function is_bedrock_provider (line 1049) | fn is_bedrock_provider(provider: &Provider) -> bool {
function build_retryable_failure_log (line 1059) | fn build_retryable_failure_log(
function build_terminal_failure_log (line 1082) | fn build_terminal_failure_log(
function summarize_proxy_error (line 1103) | fn summarize_proxy_error(error: &ProxyError) -> String {
function summarize_upstream_body (line 1135) | fn summarize_upstream_body(body: &str) -> String {
function extract_json_error_message (line 1149) | fn extract_json_error_message(body: &Value) -> Option<String> {
function should_force_identity_encoding (line 1163) | fn should_force_identity_encoding(
function summarize_text_for_log (line 1187) | fn summarize_text_for_log(text: &str, max_chars: usize) -> String {
function single_provider_retryable_log_uses_single_provider_code (line 1207) | fn single_provider_retryable_log_uses_single_provider_code() {
function multi_provider_retryable_log_keeps_failover_wording (line 1223) | fn multi_provider_retryable_log_keeps_failover_wording() {
function single_provider_has_no_terminal_all_failed_log (line 1234) | fn single_provider_has_no_terminal_all_failed_log() {
function multi_provider_terminal_log_contains_last_error_summary (line 1239) | fn multi_provider_terminal_log_contains_last_error_summary() {
function summarize_upstream_body_prefers_json_message (line 1251) | fn summarize_upstream_body_prefers_json_message() {
function summarize_text_for_log_collapses_whitespace_and_truncates (line 1265) | fn summarize_text_for_log_collapses_whitespace_and_truncates() {
function force_identity_for_stream_flag_requests (line 1272) | fn force_identity_for_stream_flag_requests() {
function force_identity_for_gemini_stream_endpoints (line 1283) | fn force_identity_for_gemini_stream_endpoints() {
function force_identity_for_sse_accept_header (line 1294) | fn force_identity_for_sse_accept_header() {
function non_streaming_requests_allow_automatic_compression (line 1306) | fn non_streaming_requests_allow_automatic_compression() {
FILE: src-tauri/src/proxy/handler_config.rs
type StreamUsageParser (line 10) | pub type StreamUsageParser = fn(&[Value]) -> Option<TokenUsage>;
type ResponseUsageParser (line 11) | pub type ResponseUsageParser = fn(&Value) -> Option<TokenUsage>;
type StreamModelExtractor (line 15) | pub type StreamModelExtractor = fn(&[Value], &str) -> String;
type UsageParserConfig (line 19) | pub struct UsageParserConfig {
function claude_model_extractor (line 35) | fn claude_model_extractor(events: &[Value], request_model: &str) -> Stri...
function openai_model_extractor (line 46) | fn openai_model_extractor(events: &[Value], request_model: &str) -> Stri...
function codex_auto_model_extractor (line 62) | fn codex_auto_model_extractor(events: &[Value], request_model: &str) -> ...
function gemini_model_extractor (line 88) | fn gemini_model_extractor(events: &[Value], request_model: &str) -> Stri...
constant CLAUDE_PARSER_CONFIG (line 103) | pub const CLAUDE_PARSER_CONFIG: UsageParserConfig = UsageParserConfig {
constant OPENAI_PARSER_CONFIG (line 111) | pub const OPENAI_PARSER_CONFIG: UsageParserConfig = UsageParserConfig {
constant CODEX_PARSER_CONFIG (line 119) | pub const CODEX_PARSER_CONFIG: UsageParserConfig = UsageParserConfig {
constant GEMINI_PARSER_CONFIG (line 127) | pub const GEMINI_PARSER_CONFIG: UsageParserConfig = UsageParserConfig {
type HandlerConfig (line 143) | pub struct HandlerConfig {
constant CLAUDE_HANDLER_CONFIG (line 156) | pub const CLAUDE_HANDLER_CONFIG: HandlerConfig = HandlerConfig {
constant CODEX_CHAT_HANDLER_CONFIG (line 165) | pub const CODEX_CHAT_HANDLER_CONFIG: HandlerConfig = HandlerConfig {
constant CODEX_RESPONSES_HANDLER_CONFIG (line 174) | pub const CODEX_RESPONSES_HANDLER_CONFIG: HandlerConfig = HandlerConfig {
constant GEMINI_HANDLER_CONFIG (line 183) | pub const GEMINI_HANDLER_CONFIG: HandlerConfig = HandlerConfig {
FILE: src-tauri/src/proxy/handler_context.rs
type StreamingTimeoutConfig (line 19) | pub struct StreamingTimeoutConfig {
type RequestContext (line 35) | pub struct RequestContext {
method new (line 79) | pub async fn new(
method with_model_from_uri (line 170) | pub fn with_model_from_uri(mut self, uri: &axum::http::Uri) -> Self {
method create_forwarder (line 194) | pub fn create_forwarder(&self, state: &ProxyState) -> RequestForwarder {
method get_providers (line 230) | pub fn get_providers(&self) -> Vec<Provider> {
method latency_ms (line 236) | pub fn latency_ms(&self) -> u64 {
method streaming_timeout_config (line 246) | pub fn streaming_timeout_config(&self) -> StreamingTimeoutConfig {
FILE: src-tauri/src/proxy/handlers.rs
function health_check (line 37) | pub async fn health_check() -> (StatusCode, Json<Value>) {
function get_status (line 48) | pub async fn get_status(State(state): State<ProxyState>) -> Result<Json<...
function handle_messages (line 62) | pub async fn handle_messages(
function handle_claude_transform (line 116) | async fn handle_claude_transform(
function handle_chat_completions (line 288) | pub async fn handle_chat_completions(
function handle_responses (line 329) | pub async fn handle_responses(
function handle_responses_compact (line 370) | pub async fn handle_responses_compact(
function handle_gemini (line 415) | pub async fn handle_gemini(
function log_forward_error (line 468) | fn log_forward_error(
function log_usage (line 499) | async fn log_usage(
FILE: src-tauri/src/proxy/health.rs
type HealthChecker (line 7) | pub struct HealthChecker;
FILE: src-tauri/src/proxy/http_client.rs
function set_proxy_port (line 26) | pub fn set_proxy_port(port: u16) {
function get_proxy_port (line 39) | fn get_proxy_port() -> u16 {
function init (line 54) | pub fn init(proxy_url: Option<&str>) -> Result<(), String> {
function validate_proxy (line 93) | pub fn validate_proxy(proxy_url: Option<&str>) -> Result<(), String> {
function apply_proxy (line 107) | pub fn apply_proxy(proxy_url: Option<&str>) -> Result<(), String> {
function update_proxy (line 151) | pub fn update_proxy(proxy_url: Option<&str>) -> Result<(), String> {
function get (line 189) | pub fn get() -> Client {
function get_current_proxy_url (line 203) | pub fn get_current_proxy_url() -> Option<String> {
function is_proxy_enabled (line 212) | pub fn is_proxy_enabled() -> bool {
function build_client (line 217) | fn build_client(proxy_url: Option<&str>) -> Result<Client, String> {
function system_proxy_points_to_loopback (line 261) | fn system_proxy_points_to_loopback() -> bool {
function proxy_points_to_loopback (line 278) | fn proxy_points_to_loopback(value: &str) -> bool {
function mask_url (line 314) | pub fn mask_url(url: &str) -> String {
function build_proxy_url_from_config (line 335) | fn build_proxy_url_from_config(config: &ProviderProxyConfig) -> Option<S...
function build_client_for_provider (line 362) | pub fn build_client_for_provider(proxy_config: Option<&ProviderProxyConf...
function get_for_provider (line 416) | pub fn get_for_provider(proxy_config: Option<&ProviderProxyConfig>) -> C...
function env_lock (line 431) | fn env_lock() -> &'static Mutex<()> {
function test_mask_url (line 437) | fn test_mask_url() {
function test_build_client_direct (line 459) | fn test_build_client_direct() {
function test_build_client_with_http_proxy (line 465) | fn test_build_client_with_http_proxy() {
function test_build_client_with_socks5_proxy (line 471) | fn test_build_client_with_socks5_proxy() {
function test_build_client_invalid_url (line 477) | fn test_build_client_invalid_url() {
function test_proxy_points_to_loopback (line 485) | fn test_proxy_points_to_loopback() {
function test_system_proxy_points_to_loopback (line 504) | fn test_system_proxy_points_to_loopback() {
FILE: src-tauri/src/proxy/log_codes.rs
constant OPEN_TO_HALF_OPEN (line 15) | pub const OPEN_TO_HALF_OPEN: &str = "CB-001";
constant HALF_OPEN_TO_CLOSED (line 16) | pub const HALF_OPEN_TO_CLOSED: &str = "CB-002";
constant HALF_OPEN_PROBE_FAILED (line 17) | pub const HALF_OPEN_PROBE_FAILED: &str = "CB-003";
constant TRIGGERED_FAILURES (line 18) | pub const TRIGGERED_FAILURES: &str = "CB-004";
constant TRIGGERED_ERROR_RATE (line 19) | pub const TRIGGERED_ERROR_RATE: &str = "CB-005";
constant MANUAL_RESET (line 20) | pub const MANUAL_RESET: &str = "CB-006";
constant STARTED (line 25) | pub const STARTED: &str = "SRV-001";
constant STOPPED (line 26) | pub const STOPPED: &str = "SRV-002";
constant STOP_TIMEOUT (line 27) | pub const STOP_TIMEOUT: &str = "SRV-003";
constant TASK_ERROR (line 28) | pub const TASK_ERROR: &str = "SRV-004";
constant PROVIDER_FAILED_RETRY (line 33) | pub const PROVIDER_FAILED_RETRY: &str = "FWD-001";
constant ALL_PROVIDERS_FAILED (line 34) | pub const ALL_PROVIDERS_FAILED: &str = "FWD-002";
constant SINGLE_PROVIDER_FAILED (line 35) | pub const SINGLE_PROVIDER_FAILED: &str = "FWD-003";
constant SWITCH_SUCCESS (line 40) | pub const SWITCH_SUCCESS: &str = "FO-001";
constant CONFIG_READ_ERROR (line 41) | pub const CONFIG_READ_ERROR: &str = "FO-002";
constant LIVE_BACKUP_ERROR (line 42) | pub const LIVE_BACKUP_ERROR: &str = "FO-003";
constant ALL_CIRCUIT_OPEN (line 43) | pub const ALL_CIRCUIT_OPEN: &str = "FO-004";
constant NO_PROVIDERS (line 44) | pub const NO_PROVIDERS: &str = "FO-005";
constant BUILD_STREAM_ERROR (line 49) | pub const BUILD_STREAM_ERROR: &str = "RSP-001";
constant READ_BODY_ERROR (line 50) | pub const READ_BODY_ERROR: &str = "RSP-002";
constant BUILD_RESPONSE_ERROR (line 51) | pub const BUILD_RESPONSE_ERROR: &str = "RSP-003";
constant STREAM_TIMEOUT (line 52) | pub const STREAM_TIMEOUT: &str = "RSP-004";
constant STREAM_ERROR (line 53) | pub const STREAM_ERROR: &str = "RSP-005";
constant LOG_FAILED (line 58) | pub const LOG_FAILED: &str = "USG-001";
constant PRICING_NOT_FOUND (line 59) | pub const PRICING_NOT_FOUND: &str = "USG-002";
FILE: src-tauri/src/proxy/model_mapper.rs
type ModelMapping (line 9) | pub struct ModelMapping {
method from_provider (line 19) | pub fn from_provider(provider: &Provider) -> Self {
method has_mapping (line 52) | pub fn has_mapping(&self) -> bool {
method map_model (line 61) | pub fn map_model(&self, original_model: &str, has_thinking: bool) -> S...
function has_thinking_enabled (line 99) | pub fn has_thinking_enabled(body: &Value) -> bool {
function apply_model_mapping (line 120) | pub fn apply_model_mapping(
function create_provider_with_mapping (line 154) | fn create_provider_with_mapping() -> Provider {
function create_provider_without_mapping (line 179) | fn create_provider_without_mapping() -> Provider {
function create_provider_with_reasoning_only (line 196) | fn create_provider_with_reasoning_only() -> Provider {
function test_sonnet_mapping (line 218) | fn test_sonnet_mapping() {
function test_haiku_mapping (line 228) | fn test_haiku_mapping() {
function test_opus_mapping (line 237) | fn test_opus_mapping() {
function test_thinking_mode (line 246) | fn test_thinking_mode() {
function test_reasoning_only_mapping_in_thinking_mode (line 258) | fn test_reasoning_only_mapping_in_thinking_mode() {
function test_reasoning_only_mapping_does_not_affect_non_thinking (line 270) | fn test_reasoning_only_mapping_does_not_affect_non_thinking() {
function test_thinking_disabled (line 283) | fn test_thinking_disabled() {
function test_unknown_model_uses_default (line 295) | fn test_unknown_model_uses_default() {
function test_no_mapping_configured (line 304) | fn test_no_mapping_configured() {
function test_thinking_adaptive (line 314) | fn test_thinking_adaptive() {
function test_thinking_unknown_type (line 326) | fn test_thinking_unknown_type() {
function test_case_insensitive (line 338) | fn test_case_insensitive() {
FILE: src-tauri/src/proxy/provider_router.rs
type ProviderRouter (line 16) | pub struct ProviderRouter {
method new (line 25) | pub fn new(db: Arc<Database>) -> Self {
method select_providers (line 37) | pub async fn select_providers(&self, app_type: &str) -> Result<Vec<Pro...
method allow_provider_request (line 119) | pub async fn allow_provider_request(&self, provider_id: &str, app_type...
method record_result (line 126) | pub async fn record_result(
method reset_circuit_breaker (line 165) | pub async fn reset_circuit_breaker(&self, circuit_key: &str) {
method reset_provider_breaker (line 173) | pub async fn reset_provider_breaker(&self, provider_id: &str, app_type...
method release_permit_neutral (line 182) | pub async fn release_permit_neutral(
method update_all_configs (line 197) | pub async fn update_all_configs(&self, config: CircuitBreakerConfig) {
method get_circuit_breaker_stats (line 206) | pub async fn get_circuit_breaker_stats(
method get_or_create_circuit_breaker (line 222) | async fn get_or_create_circuit_breaker(&self, key: &str) -> Arc<Circui...
type TempHome (line 270) | struct TempHome {
method new (line 278) | fn new() -> Self {
method drop (line 296) | fn drop(&mut self) {
function test_provider_router_creation (line 311) | async fn test_provider_router_creation() {
function test_failover_disabled_uses_current_provider (line 322) | async fn test_failover_disabled_uses_current_provider() {
function test_failover_enabled_uses_queue_order_ignoring_current (line 345) | async fn test_failover_enabled_uses_queue_order_ignoring_current() {
function test_failover_enabled_uses_queue_only_even_if_current_not_in_queue (line 380) | async fn test_failover_enabled_uses_queue_only_even_if_current_not_in_qu...
function test_select_providers_does_not_consume_half_open_permit (line 410) | async fn test_select_providers_does_not_consume_half_open_permit() {
function test_release_permit_neutral_frees_half_open_slot (line 453) | async fn test_release_permit_neutral_frees_half_open_slot() {
FILE: src-tauri/src/proxy/providers/adapter.rs
type ProviderAdapter (line 43) | pub trait ProviderAdapter: Send + Sync {
method name (line 45) | fn name(&self) -> &'static str;
method extract_base_url (line 55) | fn extract_base_url(&self, provider: &Provider) -> Result<String, Prox...
method extract_auth (line 65) | fn extract_auth(&self, provider: &Provider) -> Option<AuthInfo>;
method build_url (line 75) | fn build_url(&self, base_url: &str, endpoint: &str) -> String;
method add_auth_headers (line 85) | fn add_auth_headers(&self, request: RequestBuilder, auth: &AuthInfo) -...
method needs_transform (line 94) | fn needs_transform(&self, _provider: &Provider) -> bool {
method transform_request (line 110) | fn transform_request(&self, body: Value, _provider: &Provider) -> Resu...
method transform_response (line 128) | fn transform_response(&self, body: Value) -> Result<Value, ProxyError> {
FILE: src-tauri/src/proxy/providers/auth.rs
type AuthInfo (line 9) | pub struct AuthInfo {
method new (line 20) | pub fn new(api_key: String, strategy: AuthStrategy) -> Self {
method with_access_token (line 29) | pub fn with_access_token(api_key: String, access_token: String) -> Self {
method masked_key (line 42) | pub fn masked_key(&self) -> String {
method masked_access_token (line 62) | pub fn masked_access_token(&self) -> Option<String> {
type AuthStrategy (line 86) | pub enum AuthStrategy {
function test_masked_key_long (line 129) | fn test_masked_key_long() {
function test_masked_key_short (line 135) | fn test_masked_key_short() {
function test_masked_key_exactly_8 (line 141) | fn test_masked_key_exactly_8() {
function test_masked_key_9_chars (line 147) | fn test_masked_key_9_chars() {
function test_masked_key_utf8_safe (line 153) | fn test_masked_key_utf8_safe() {
function test_auth_strategy_equality (line 160) | fn test_auth_strategy_equality() {
function test_auth_info_new_has_no_access_token (line 167) | fn test_auth_info_new_has_no_access_token() {
function test_auth_info_with_access_token (line 173) | fn test_auth_info_with_access_token() {
function test_masked_access_token_long (line 187) | fn test_masked_access_token_long() {
function test_masked_access_token_utf8_safe (line 194) | fn test_masked_access_token_utf8_safe() {
function test_masked_access_token_short (line 202) | fn test_masked_access_token_short() {
function test_masked_access_token_none (line 208) | fn test_masked_access_token_none() {
function test_claude_auth_strategy (line 214) | fn test_claude_auth_strategy() {
function test_google_oauth_strategy (line 222) | fn test_google_oauth_strategy() {
function test_all_strategies_are_distinct (line 229) | fn test_all_strategies_are_distinct() {
FILE: src-tauri/src/proxy/providers/claude.rs
function get_claude_api_format (line 25) | pub fn get_claude_api_format(provider: &Provider) -> &'static str {
type ClaudeAdapter (line 70) | pub struct ClaudeAdapter;
method new (line 73) | pub fn new() -> Self {
method provider_type (line 84) | pub fn provider_type(&self, provider: &Provider) -> ProviderType {
method is_github_copilot (line 104) | fn is_github_copilot(&self, provider: &Provider) -> bool {
method is_openrouter (line 123) | fn is_openrouter(&self, provider: &Provider) -> bool {
method get_api_format (line 136) | fn get_api_format(&self, provider: &Provider) -> &'static str {
method is_bearer_only_mode (line 141) | fn is_bearer_only_mode(&self, provider: &Provider) -> bool {
method extract_key (line 166) | fn extract_key(&self, provider: &Provider) -> Option<String> {
method default (line 223) | fn default() -> Self {
method name (line 229) | fn name(&self) -> &'static str {
method extract_base_url (line 233) | fn extract_base_url(&self, provider: &Provider) -> Result<String, ProxyE...
method extract_auth (line 271) | fn extract_auth(&self, provider: &Provider) -> Option<AuthInfo> {
method build_url (line 294) | fn build_url(&self, base_url: &str, endpoint: &str) -> String {
method add_auth_headers (line 333) | fn add_auth_headers(&self, request: RequestBuilder, auth: &AuthInfo) -> ...
method needs_transform (line 359) | fn needs_transform(&self, provider: &Provider) -> bool {
method transform_request (line 375) | fn transform_request(
method transform_response (line 395) | fn transform_response(&self, body: serde_json::Value) -> Result<serde_js...
function create_provider (line 415) | fn create_provider(config: serde_json::Value) -> Provider {
function create_provider_with_meta (line 432) | fn create_provider_with_meta(config: serde_json::Value, meta: ProviderMe...
function test_extract_base_url_from_env (line 450) | fn test_extract_base_url_from_env() {
function test_extract_auth_anthropic (line 463) | fn test_extract_auth_anthropic() {
function test_extract_auth_anthropic_api_key (line 478) | fn test_extract_auth_anthropic_api_key() {
function test_extract_auth_openrouter (line 493) | fn test_extract_auth_openrouter() {
function test_extract_auth_claude_auth_mode (line 508) | fn test_extract_auth_claude_auth_mode() {
function test_extract_auth_claude_auth_env_mode (line 524) | fn test_extract_auth_claude_auth_env_mode() {
function test_provider_type_detection (line 540) | fn test_provider_type_detection() {
function test_build_url_anthropic (line 576) | fn test_build_url_anthropic() {
function test_build_url_openrouter (line 584) | fn test_build_url_openrouter() {
function test_build_url_no_beta_for_other_endpoints (line 592) | fn test_build_url_no_beta_for_other_endpoints() {
function test_build_url_preserve_existing_query (line 600) | fn test_build_url_preserve_existing_query() {
function test_build_url_no_beta_for_openai_chat_completions (line 608) | fn test_build_url_no_beta_for_openai_chat_completions() {
function test_needs_transform (line 617) | fn test_needs_transform() {
function test_github_copilot_detection_by_url (line 736) | fn test_github_copilot_detection_by_url() {
function test_github_copilot_detection_by_meta (line 749) | fn test_github_copilot_detection_by_meta() {
function test_github_copilot_auth (line 771) | fn test_github_copilot_auth() {
function test_github_copilot_needs_transform (line 785) | fn test_github_copilot_needs_transform() {
FILE: src-tauri/src/proxy/providers/codex.rs
type CodexAdapter (line 21) | pub struct CodexAdapter;
method new (line 24) | pub fn new() -> Self {
method is_official_client (line 32) | pub fn is_official_client(user_agent: &str) -> bool {
method extract_key (line 37) | fn extract_key(&self, provider: &Provider) -> Option<String> {
method default (line 78) | fn default() -> Self {
method name (line 84) | fn name(&self) -> &'static str {
method extract_base_url (line 88) | fn extract_base_url(&self, provider: &Provider) -> Result<String, ProxyE...
method extract_auth (line 135) | fn extract_auth(&self, provider: &Provider) -> Option<AuthInfo> {
method build_url (line 140) | fn build_url(&self, base_url: &str, endpoint: &str) -> String {
method add_auth_headers (line 177) | fn add_auth_headers(&self, request: RequestBuilder, auth: &AuthInfo) -> ...
function create_provider (line 187) | fn create_provider(config: serde_json::Value) -> Provider {
function test_extract_base_url_direct (line 205) | fn test_extract_base_url_direct() {
function test_extract_auth_from_auth_field (line 216) | fn test_extract_auth_from_auth_field() {
function test_extract_auth_from_env (line 230) | fn test_extract_auth_from_env() {
function test_build_url (line 243) | fn test_build_url() {
function test_build_url_origin_adds_v1 (line 250) | fn test_build_url_origin_adds_v1() {
function test_build_url_custom_prefix_no_v1 (line 257) | fn test_build_url_custom_prefix_no_v1() {
function test_build_url_dedup_v1 (line 264) | fn test_build_url_dedup_v1() {
function test_is_official_client_vscode (line 273) | fn test_is_official_client_vscode() {
function test_is_official_client_cli (line 280) | fn test_is_official_client_cli() {
function test_is_not_official_client (line 286) | fn test_is_not_official_client() {
function test_is_official_client_partial_match (line 295) | fn test_is_official_client_partial_match() {
FILE: src-tauri/src/proxy/providers/copilot_auth.rs
constant GITHUB_CLIENT_ID (line 28) | const GITHUB_CLIENT_ID: &str = "Iv1.b507a08c87ecfe98";
constant GITHUB_DEVICE_CODE_URL (line 31) | const GITHUB_DEVICE_CODE_URL: &str = "https://github.com/login/device/co...
constant GITHUB_OAUTH_TOKEN_URL (line 34) | const GITHUB_OAUTH_TOKEN_URL: &str = "https://github.com/login/oauth/acc...
constant COPILOT_TOKEN_URL (line 37) | const COPILOT_TOKEN_URL: &str = "https://api.github.com/copilot_internal...
constant GITHUB_USER_URL (line 40) | const GITHUB_USER_URL: &str = "https://api.github.com/user";
constant TOKEN_REFRESH_BUFFER_SECONDS (line 43) | const TOKEN_REFRESH_BUFFER_SECONDS: i64 = 60;
constant COPILOT_MODELS_URL (line 46) | const COPILOT_MODELS_URL: &str = "https://api.githubcopilot.com/models";
constant COPILOT_EDITOR_VERSION (line 49) | const COPILOT_EDITOR_VERSION: &str = "vscode/1.96.0";
constant COPILOT_PLUGIN_VERSION (line 50) | const COPILOT_PLUGIN_VERSION: &str = "copilot-chat/0.26.7";
constant COPILOT_USER_AGENT (line 51) | const COPILOT_USER_AGENT: &str = "GitHubCopilotChat/0.26.7";
constant COPILOT_API_VERSION (line 52) | const COPILOT_API_VERSION: &str = "2025-04-01";
constant COPILOT_USAGE_URL (line 55) | const COPILOT_USAGE_URL: &str = "https://api.github.com/copilot_internal...
type CopilotUsageResponse (line 59) | pub struct CopilotUsageResponse {
type QuotaSnapshots (line 70) | pub struct QuotaSnapshots {
type QuotaDetail (line 81) | pub struct QuotaDetail {
type CopilotModel (line 94) | pub struct CopilotModel {
type CopilotModelsResponse (line 107) | struct CopilotModelsResponse {
type CopilotModelsResponseItem (line 113) | struct CopilotModelsResponseItem {
type CopilotAuthError (line 122) | pub enum CopilotAuthError {
method from (line 158) | fn from(err: reqwest::Error) -> Self {
method from (line 164) | fn from(err: std::io::Error) -> Self {
type GitHubDeviceCodeResponse (line 171) | pub struct GitHubDeviceCodeResponse {
type GitHubOAuthResponse (line 186) | struct GitHubOAuthResponse {
type CopilotToken (line 196) | pub struct CopilotToken {
method is_expiring_soon (line 205) | pub fn is_expiring_soon(&self) -> bool {
type CopilotTokenResponse (line 213) | struct CopilotTokenResponse {
type GitHubUser (line 222) | pub struct GitHubUser {
type GitHubAccount (line 230) | pub struct GitHubAccount {
method from (line 242) | fn from(data: &GitHubAccountData) -> Self {
type CopilotAuthStatus (line 254) | pub struct CopilotAuthStatus {
type GitHubAccountData (line 271) | struct GitHubAccountData {
type CopilotAuthStore (line 285) | struct CopilotAuthStore {
type CopilotAuthManager (line 303) | pub struct CopilotAuthManager {
method new (line 324) | pub fn new(data_dir: PathBuf) -> Self {
method list_accounts (line 349) | pub async fn list_accounts(&self) -> Vec<GitHubAccount> {
method get_account (line 356) | pub async fn get_account(&self, account_id: &str) -> Option<GitHubAcco...
method remove_account (line 362) | pub async fn remove_account(&self, account_id: &str) -> Result<(), Cop...
method add_account_internal (line 397) | async fn add_account_internal(
method set_default_account (line 441) | pub async fn set_default_account(&self, account_id: &str) -> Result<()...
method start_device_flow (line 461) | pub async fn start_device_flow(&self) -> Result<GitHubDeviceCodeRespon...
method poll_for_token (line 495) | pub async fn poll_for_token(
method get_valid_token_for_account (line 556) | pub async fn get_valid_token_for_account(
method get_valid_token (line 613) | pub async fn get_valid_token(&self) -> Result<String, CopilotAuthError> {
method fetch_models_for_account (line 626) | pub async fn fetch_models_for_account(
method fetch_models (line 679) | pub async fn fetch_models(&self) -> Result<Vec<CopilotModel>, CopilotA...
method fetch_usage_for_account (line 687) | pub async fn fetch_usage_for_account(
method fetch_usage (line 741) | pub async fn fetch_usage(&self) -> Result<CopilotUsageResponse, Copilo...
method get_status (line 751) | pub async fn get_status(&self) -> CopilotAuthStatus {
method is_authenticated (line 785) | pub async fn is_authenticated(&self) -> bool {
method clear_auth (line 791) | pub async fn clear_auth(&self) -> Result<(), CopilotAuthError> {
method fallback_default_account_id (line 822) | fn fallback_default_account_id(
method sorted_accounts (line 835) | fn sorted_accounts(
method resolve_default_account_id (line 853) | async fn resolve_default_account_id(&self) -> Option<String> {
method get_refresh_lock (line 866) | async fn get_refresh_lock(&self, account_id: &str) -> Arc<Mutex<()>> {
method set_migration_error (line 882) | async fn set_migration_error(&self, message: Option<String>) {
method write_store_atomic (line 887) | fn write_store_atomic(&self, content: &str) -> Result<(), CopilotAuthE...
method fetch_user_info_with_token (line 943) | async fn fetch_user_info_with_token(
method fetch_copilot_token_with_github_token (line 970) | async fn fetch_copilot_token_with_github_token(
method load_from_disk_sync (line 1029) | fn load_from_disk_sync(&self) -> Result<(), CopilotAuthError> {
method ensure_migration_complete (line 1064) | async fn ensure_migration_complete(&self) -> Result<(), CopilotAuthErr...
method save_to_disk (line 1112) | async fn save_to_disk(&self) -> Result<(), CopilotAuthError> {
function test_copilot_token_expiry (line 1143) | fn test_copilot_token_expiry() {
function test_auth_status_serialization (line 1169) | fn test_auth_status_serialization() {
function test_multi_account_store_serialization (line 1197) | fn test_multi_account_store_serialization() {
function test_legacy_format_detection (line 1245) | fn test_legacy_format_detection() {
function test_github_account_from_data (line 1259) | fn test_github_account_from_data() {
function test_fallback_default_account_prefers_latest_authenticated (line 1281) | fn test_fallback_default_account_prefers_latest_authenticated() {
FILE: src-tauri/src/proxy/providers/gemini.rs
type GeminiAdapter (line 15) | pub struct GeminiAdapter;
method new (line 41) | pub fn new() -> Self {
method provider_type (line 50) | pub fn provider_type(&self, provider: &Provider) -> ProviderType {
method detect_auth_type (line 65) | pub fn detect_auth_type(&self, provider: &Provider) -> AuthStrategy {
method parse_oauth_credentials (line 73) | pub fn parse_oauth_credentials(&self, key: &str) -> Option<OAuthCreden...
method extract_key_raw (line 121) | fn extract_key_raw(&self, provider: &Provider) -> Option<String> {
type OAuthCredentials (line 20) | pub struct OAuthCredentials {
method needs_refresh (line 30) | pub fn needs_refresh(&self) -> bool {
method can_refresh (line 35) | pub fn can_refresh(&self) -> bool {
method default (line 144) | fn default() -> Self {
method name (line 150) | fn name(&self) -> &'static str {
method extract_base_url (line 154) | fn extract_base_url(&self, provider: &Provider) -> Result<String, ProxyE...
method extract_auth (line 184) | fn extract_auth(&self, provider: &Provider) -> Option<AuthInfo> {
method build_url (line 202) | fn build_url(&self, base_url: &str, endpoint: &str) -> String {
method add_auth_headers (line 220) | fn add_auth_headers(&self, request: RequestBuilder, auth: &AuthInfo) -> ...
function create_provider (line 240) | fn create_provider(config: serde_json::Value) -> Provider {
function test_extract_base_url_from_env (line 258) | fn test_extract_base_url_from_env() {
function test_extract_auth_api_key (line 271) | fn test_extract_auth_api_key() {
function test_extract_auth_oauth_access_token (line 286) | fn test_extract_auth_oauth_access_token() {
function test_extract_auth_oauth_json (line 303) | fn test_extract_auth_oauth_json() {
function test_provider_type_detection (line 317) | fn test_provider_type_detection() {
function test_extract_auth_fallback (line 355) | fn test_extract_auth_fallback() {
function test_build_url_dedup (line 368) | fn test_build_url_dedup() {
function test_build_url_normal (line 382) | fn test_build_url_normal() {
function test_parse_oauth_credentials_direct_token (line 395) | fn test_parse_oauth_credentials_direct_token() {
function test_parse_oauth_credentials_json (line 405) | fn test_parse_oauth_credentials_json() {
function test_parse_oauth_credentials_invalid (line 417) | fn test_parse_oauth_credentials_invalid() {
FILE: src-tauri/src/proxy/providers/mod.rs
type ProviderType (line 43) | pub enum ProviderType {
method needs_transform (line 67) | pub fn needs_transform(&self) -> bool {
method default_endpoint (line 77) | pub fn default_endpoint(&self) -> &'static str {
method from_app_type_and_config (line 93) | pub fn from_app_type_and_config(app_type: &AppType, provider: &Provide...
method as_str (line 166) | pub fn as_str(&self) -> &'static str {
method fmt (line 180) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Err (line 186) | type Err = String;
method from_str (line 188) | fn from_str(s: &str) -> Result<Self, Self::Err> {
function get_adapter (line 205) | pub fn get_adapter(app_type: &AppType) -> Box<dyn ProviderAdapter> {
function get_adapter_for_provider_type (line 223) | pub fn get_adapter_for_provider_type(provider_type: &ProviderType) -> Bo...
function create_provider (line 239) | fn create_provider(config: serde_json::Value) -> Provider {
function test_provider_type_needs_transform (line 257) | fn test_provider_type_needs_transform() {
function test_provider_type_default_endpoint (line 268) | fn test_provider_type_default_endpoint() {
function test_provider_type_from_str (line 300) | fn test_provider_type_from_str() {
function test_provider_type_as_str (line 349) | fn test_provider_type_as_str() {
function test_provider_type_serde (line 360) | fn test_provider_type_serde() {
function test_from_app_type_claude_direct (line 379) | fn test_from_app_type_claude_direct() {
function test_from_app_type_claude_openrouter (line 392) | fn test_from_app_type_claude_openrouter() {
function test_from_app_type_claude_auth (line 405) | fn test_from_app_type_claude_auth() {
function test_from_app_type_codex (line 419) | fn test_from_app_type_codex() {
function test_from_app_type_gemini_api_key (line 431) | fn test_from_app_type_gemini_api_key() {
function test_from_app_type_gemini_cli_oauth (line 443) | fn test_from_app_type_gemini_cli_oauth() {
function test_from_app_type_gemini_cli_json (line 455) | fn test_from_app_type_gemini_cli_json() {
function test_get_adapter_for_provider_type (line 467) | fn test_get_adapter_for_provider_type() {
FILE: src-tauri/src/proxy/providers/models/anthropic.rs
type AnthropicRequest (line 12) | pub struct AnthropicRequest {
type AnthropicMessage (line 30) | pub struct AnthropicMessage {
type AnthropicContentBlock (line 38) | pub enum AnthropicContentBlock {
type ImageSource (line 55) | pub struct ImageSource {
type AnthropicTool (line 64) | pub struct AnthropicTool {
type AnthropicResponse (line 73) | pub struct AnthropicResponse {
type AnthropicResponseContent (line 90) | pub enum AnthropicResponseContent {
type AnthropicUsage (line 103) | pub struct AnthropicUsage {
FILE: src-tauri/src/proxy/providers/models/openai.rs
type OpenAIRequest (line 12) | pub struct OpenAIRequest {
type OpenAIMessage (line 29) | pub struct OpenAIMessage {
type OpenAIContentPart (line 42) | pub enum OpenAIContentPart {
type ImageUrl (line 51) | pub struct ImageUrl {
type OpenAIToolCall (line 57) | pub struct OpenAIToolCall {
type OpenAIFunction (line 66) | pub struct OpenAIFunction {
type OpenAITool (line 73) | pub struct OpenAITool {
type OpenAIFunctionDef (line 81) | pub struct OpenAIFunctionDef {
type OpenAIResponse (line 90) | pub struct OpenAIResponse {
type OpenAIChoice (line 102) | pub struct OpenAIChoice {
type OpenAIUsage (line 111) | pub struct OpenAIUsage {
FILE: src-tauri/src/proxy/providers/streaming.rs
type OpenAIStreamChunk (line 13) | struct OpenAIStreamChunk {
type StreamChoice (line 22) | struct StreamChoice {
type Delta (line 29) | struct Delta {
type DeltaToolCall (line 39) | struct DeltaToolCall {
type DeltaFunction (line 50) | struct DeltaFunction {
type Usage (line 59) | struct Usage {
type PromptTokensDetails (line 75) | struct PromptTokensDetails {
type ToolBlockState (line 81) | struct ToolBlockState {
function create_anthropic_sse_stream (line 90) | pub fn create_anthropic_sse_stream(
function extract_cache_read_tokens (line 539) | fn extract_cache_read_tokens(usage: &Usage) -> Option<u32> {
function map_stop_reason (line 553) | fn map_stop_reason(finish_reason: Option<&str>) -> Option<String> {
function test_map_stop_reason_legacy_and_filtered_values (line 578) | fn test_map_stop_reason_legacy_and_filtered_values() {
function test_streaming_tool_calls_routed_by_index (line 590) | async fn test_streaming_tool_calls_routed_by_index() {
function test_streaming_delays_tool_start_until_id_and_name_ready (line 677) | async fn test_streaming_delays_tool_start_until_id_and_name_ready() {
FILE: src-tauri/src/proxy/providers/streaming_responses.rs
function response_object_from_event (line 18) | fn response_object_from_event(data: &Value) -> &Value {
function content_part_key (line 23) | fn content_part_key(data: &Value) -> Option<String> {
function tool_item_key_from_added (line 40) | fn tool_item_key_from_added(data: &Value, item: &Value) -> Option<String> {
function tool_item_key_from_event (line 54) | fn tool_item_key_from_event(data: &Value) -> Option<String> {
function resolve_content_index (line 69) | fn resolve_content_index(
function create_anthropic_sse_stream_from_responses (line 98) | pub fn create_anthropic_sse_stream_from_responses(
function test_map_responses_stop_reason_tool_use (line 712) | fn test_map_responses_stop_reason_tool_use() {
function test_response_object_from_event_with_wrapper (line 732) | fn test_response_object_from_event_with_wrapper() {
function test_streaming_conversion_with_wrapped_response_events (line 746) | async fn test_streaming_conversion_with_wrapped_response_events() {
function test_streaming_conversion_interleaved_tool_deltas_by_item_id (line 782) | async fn test_streaming_conversion_interleaved_tool_deltas_by_item_id() {
function test_streaming_reasoning_delta_emits_thinking_blocks (line 853) | async fn test_streaming_reasoning_delta_emits_thinking_blocks() {
FILE: src-tauri/src/proxy/providers/transform.rs
function is_openai_o_series (line 11) | pub fn is_openai_o_series(model: &str) -> bool {
function anthropic_to_openai (line 20) | pub fn anthropic_to_openai(body: Value, cache_key: Option<&str>) -> Resu...
function convert_message_to_openai (line 122) | fn convert_message_to_openai(
function clean_schema (line 251) | pub fn clean_schema(mut schema: Value) -> Value {
function openai_to_anthropic (line 273) | pub fn openai_to_anthropic(body: Value) -> Result<Value, ProxyError> {
function test_anthropic_to_openai_simple (line 449) | fn test_anthropic_to_openai_simple() {
function test_anthropic_to_openai_with_system (line 464) | fn test_anthropic_to_openai_with_system() {
function test_anthropic_to_openai_with_tools (line 482) | fn test_anthropic_to_openai_with_tools() {
function test_anthropic_to_openai_tool_use (line 500) | fn test_anthropic_to_openai_tool_use() {
function test_anthropic_to_openai_tool_result (line 521) | fn test_anthropic_to_openai_tool_result() {
function test_openai_to_anthropic_simple (line 541) | fn test_openai_to_anthropic_simple() {
function test_openai_to_anthropic_with_tool_calls (line 566) | fn test_openai_to_anthropic_with_tool_calls() {
function test_model_passthrough (line 597) | fn test_model_passthrough() {
function test_anthropic_to_openai_with_cache_key (line 610) | fn test_anthropic_to_openai_with_cache_key() {
function test_anthropic_to_openai_no_cache_key (line 622) | fn test_anthropic_to_openai_no_cache_key() {
function test_anthropic_to_openai_cache_control_preserved (line 634) | fn test_anthropic_to_openai_cache_control_preserved() {
function test_openai_to_anthropic_with_cache_tokens (line 672) | fn test_openai_to_anthropic_with_cache_tokens() {
function test_openai_to_anthropic_with_direct_cache_fields (line 697) | fn test_openai_to_anthropic_with_direct_cache_fields() {
function test_openai_to_anthropic_finish_reason_content_filter_maps_end_turn (line 720) | fn test_openai_to_anthropic_finish_reason_content_filter_maps_end_turn() {
function test_openai_to_anthropic_with_legacy_function_call (line 737) | fn test_openai_to_anthropic_with_legacy_function_call() {
function test_openai_to_anthropic_with_content_parts_and_refusal (line 764) | fn test_openai_to_anthropic_with_content_parts_and_refusal() {
function test_is_openai_o_series (line 790) | fn test_is_openai_o_series() {
function test_anthropic_to_openai_o_series_max_completion_tokens (line 804) | fn test_anthropic_to_openai_o_series_max_completion_tokens() {
function test_anthropic_to_openai_non_o_series_keeps_max_tokens (line 825) | fn test_anthropic_to_openai_non_o_series_keeps_max_tokens() {
FILE: src-tauri/src/proxy/providers/transform_responses.rs
function anthropic_to_responses (line 17) | pub fn anthropic_to_responses(body: Value, cache_key: Option<&str>) -> R...
function map_tool_choice_to_responses (line 100) | fn map_tool_choice_to_responses(tool_choice: &Value) -> Value {
function map_responses_stop_reason (line 122) | pub(crate) fn map_responses_stop_reason(
function build_anthropic_usage_from_responses (line 155) | pub(crate) fn build_anthropic_usage_from_responses(usage: Option<&Value>...
function convert_messages_to_input (line 210) | fn convert_messages_to_input(messages: &[Value]) -> Result<Vec<Value>, P...
function responses_to_anthropic (line 346) | pub fn responses_to_anthropic(body: Value) -> Result<Value, ProxyError> {
function test_anthropic_to_responses_simple (line 455) | fn test_anthropic_to_responses_simple() {
function test_anthropic_to_responses_with_system_string (line 473) | fn test_anthropic_to_responses_with_system_string() {
function test_anthropic_to_responses_with_system_array (line 488) | fn test_anthropic_to_responses_with_system_array() {
function test_anthropic_to_responses_with_tools (line 504) | fn test_anthropic_to_responses_with_tools() {
function test_anthropic_to_responses_tool_choice_any_to_required (line 525) | fn test_anthropic_to_responses_tool_choice_any_to_required() {
function test_anthropic_to_responses_tool_choice_tool_to_function (line 538) | fn test_anthropic_to_responses_tool_choice_tool_to_function() {
function test_anthropic_to_responses_tool_use_lifting (line 552) | fn test_anthropic_to_responses_tool_use_lifting() {
function test_anthropic_to_responses_tool_result_lifting (line 583) | fn test_anthropic_to_responses_tool_result_lifting() {
function test_anthropic_to_responses_thinking_discarded (line 606) | fn test_anthropic_to_responses_thinking_discarded() {
function test_anthropic_to_responses_image (line 629) | fn test_anthropic_to_responses_image() {
function test_responses_to_anthropic_simple (line 651) | fn test_responses_to_anthropic_simple() {
function test_responses_to_anthropic_with_function_call (line 677) | fn test_responses_to_anthropic_with_function_call() {
function test_responses_to_anthropic_with_refusal_block (line 703) | fn test_responses_to_anthropic_with_refusal_block() {
function test_responses_to_anthropic_with_reasoning (line 722) | fn test_responses_to_anthropic_with_reasoning() {
function test_responses_to_anthropic_incomplete_status (line 758) | fn test_responses_to_anthropic_incomplete_status() {
function test_responses_to_anthropic_incomplete_non_token_reason (line 775) | fn test_responses_to_anthropic_incomplete_non_token_reason() {
function test_model_passthrough (line 793) | fn test_model_passthrough() {
function test_anthropic_to_responses_with_cache_key (line 805) | fn test_anthropic_to_responses_with_cache_key() {
function test_anthropic_to_responses_strip_cache_control_on_tools (line 817) | fn test_anthropic_to_responses_strip_cache_control_on_tools() {
function test_anthropic_to_responses_strip_cache_control_on_text (line 835) | fn test_anthropic_to_responses_strip_cache_control_on_text() {
function test_responses_to_anthropic_with_cache_tokens (line 854) | fn test_responses_to_anthropic_with_cache_tokens() {
function test_responses_to_anthropic_with_direct_cache_fields (line 879) | fn test_responses_to_anthropic_with_direct_cache_fields() {
function test_anthropic_to_responses_o_series_uses_max_output_tokens (line 902) | fn test_anthropic_to_responses_o_series_uses_max_output_tokens() {
FILE: src-tauri/src/proxy/response_handler.rs
type ResponseType (line 19) | pub enum ResponseType {
method from_content_type (line 29) | pub fn from_content_type(content_type: &str) -> Self {
type StreamHandler (line 40) | pub struct StreamHandler {
method new (line 50) | pub fn new(idle_timeout_secs: u64) -> Self {
method handle_stream (line 60) | pub fn handle_stream<S>(
method get_events (line 127) | pub async fn get_events(&self) -> Vec<Value> {
method extract_usage (line 133) | pub async fn extract_usage(&self, session: &ProxySession) -> Option<To...
type NonStreamHandler (line 149) | pub struct NonStreamHandler;
method handle_response (line 156) | pub async fn handle_response(
type ResponseDispatcher (line 179) | pub struct ResponseDispatcher;
method detect_type (line 184) | pub fn detect_type(content_type: &str) -> ResponseType {
function test_response_type_detection (line 194) | fn test_response_type_detection() {
function test_stream_handler_creation (line 210) | fn test_stream_handler_creation() {
FILE: src-tauri/src/proxy/response_processor.rs
function is_sse_response (line 32) | pub fn is_sse_response(response: &reqwest::Response) -> bool {
function handle_streaming (line 42) | pub async fn handle_streaming(
function handle_non_streaming (line 88) | pub async fn handle_non_streaming(
function process_response (line 191) | pub async fn process_response(
type UsageCallbackWithTiming (line 208) | type UsageCallbackWithTiming = Arc<dyn Fn(Vec<Value>, Option<u64>) + Sen...
type SseUsageCollector (line 212) | pub struct SseUsageCollector {
method new (line 226) | pub fn new(
method push (line 243) | pub async fn push(&self, event: Value) {
method finish (line 256) | pub async fn finish(&self) {
type SseUsageCollectorInner (line 216) | struct SseUsageCollectorInner {
function create_usage_collector (line 280) | fn create_usage_collector(
function spawn_log_usage (line 360) | fn spawn_log_usage(
function log_usage_internal (line 404) | async fn log_usage_internal(
function create_logged_passthrough_stream (line 460) | pub fn create_logged_passthrough_stream(
function format_headers (line 568) | fn format_headers(headers: &HeaderMap) -> String {
function build_state (line 594) | fn build_state(db: Arc<Database>) -> ProxyState {
function seed_pricing (line 607) | fn seed_pricing(db: &Database) -> Result<(), AppError> {
function insert_provider (line 624) | fn insert_provider(
function test_log_usage_uses_provider_override_config (line 643) | async fn test_log_usage_uses_provider_override_config() -> Result<(), Ap...
function test_log_usage_falls_back_to_global_defaults (line 704) | async fn test_log_usage_falls_back_to_global_defaults() -> Result<(), Ap...
FILE: src-tauri/src/proxy/server.rs
type ProxyState (line 23) | pub struct ProxyState {
type ProxyServer (line 39) | pub struct ProxyServer {
method new (line 48) | pub fn new(
method start (line 77) | pub async fn start(&self) -> Result<ProxyServerInfo, ProxyError> {
method stop (line 142) | pub async fn stop(&self) -> Result<(), ProxyError> {
method get_status (line 174) | pub async fn get_status(&self) -> ProxyStatus {
method set_active_target (line 200) | pub async fn set_active_target(&self, app_type: &str, provider_id: &st...
method build_router (line 208) | fn build_router(&self) -> Router {
method apply_runtime_config (line 267) | pub async fn apply_runtime_config(&self, config: &ProxyConfig) {
method update_circuit_breaker_configs (line 274) | pub async fn update_circuit_breaker_configs(
method reset_provider_circuit_breaker (line 282) | pub async fn reset_provider_circuit_breaker(&self, provider_id: &str, ...
FILE: src-tauri/src/proxy/session.rs
type ClientFormat (line 19) | pub enum ClientFormat {
method from_path (line 37) | pub fn from_path(path: &str) -> Self {
method from_body (line 61) | pub fn from_body(body: &serde_json::Value) -> Self {
method as_str (line 89) | pub fn as_str(&self) -> &'static str {
method fmt (line 102) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type ProxySession (line 112) | pub struct ProxySession {
method from_request (line 136) | pub fn from_request(
method with_provider (line 176) | pub fn with_provider(mut self, provider_id: &str) -> Self {
method latency_ms (line 182) | pub fn latency_ms(&self) -> u64 {
type SessionIdSource (line 193) | pub enum SessionIdSource {
type SessionIdResult (line 208) | pub struct SessionIdResult {
function extract_session_id (line 240) | pub fn extract_session_id(
function extract_codex_session (line 262) | fn extract_codex_session(headers: &HeaderMap, body: &serde_json::Value) ...
function extract_from_metadata (line 309) | fn extract_from_metadata(body: &serde_json::Value) -> Option<SessionIdRe...
function parse_session_from_user_id (line 340) | fn parse_session_from_user_id(user_id: &str) -> Option<String> {
function generate_new_session_id (line 352) | fn generate_new_session_id() -> SessionIdResult {
function test_client_format_from_path_claude (line 366) | fn test_client_format_from_path_claude() {
function test_client_format_from_path_codex (line 378) | fn test_client_format_from_path_codex() {
function test_client_format_from_path_openai (line 386) | fn test_client_format_from_path_openai() {
function test_client_format_from_path_gemini (line 394) | fn test_client_format_from_path_gemini() {
function test_client_format_from_path_gemini_cli (line 402) | fn test_client_format_from_path_gemini_cli() {
function test_client_format_from_body_claude (line 410) | fn test_client_format_from_body_claude() {
function test_client_format_from_body_codex (line 420) | fn test_client_format_from_body_codex() {
function test_client_format_from_body_gemini (line 428) | fn test_client_format_from_body_gemini() {
function test_session_id_uniqueness (line 436) | fn test_session_id_uniqueness() {
function test_session_from_request (line 443) | fn test_session_from_request() {
function test_session_with_provider (line 463) | fn test_session_with_provider() {
function test_client_format_as_str (line 471) | fn test_client_format_as_str() {
function test_extract_session_from_claude_metadata_user_id (line 483) | fn test_extract_session_from_claude_metadata_user_id() {
function test_extract_session_from_claude_metadata_session_id (line 501) | fn test_extract_session_from_claude_metadata_session_id() {
function test_extract_session_from_codex_previous_response_id (line 519) | fn test_extract_session_from_codex_previous_response_id() {
function test_extract_session_generates_new_when_not_found (line 534) | fn test_extract_session_generates_new_when_not_found() {
function test_parse_session_from_user_id (line 549) | fn test_parse_session_from_user_id() {
FILE: src-tauri/src/proxy/thinking_budget_rectifier.rs
constant MAX_THINKING_BUDGET (line 10) | const MAX_THINKING_BUDGET: u64 = 32000;
constant MAX_TOKENS_VALUE (line 13) | const MAX_TOKENS_VALUE: u64 = 64000;
constant MIN_MAX_TOKENS_FOR_BUDGET (line 16) | const MIN_MAX_TOKENS_FOR_BUDGET: u64 = MAX_THINKING_BUDGET + 1;
type BudgetRectifySnapshot (line 20) | pub struct BudgetRectifySnapshot {
type BudgetRectifyResult (line 31) | pub struct BudgetRectifyResult {
function should_rectify_thinking_budget (line 43) | pub fn should_rectify_thinking_budget(
function rectify_thinking_budget (line 81) | pub fn rectify_thinking_budget(body: &mut Value) -> BudgetRectifyResult {
function snapshot_budget (line 124) | fn snapshot_budget(body: &Value) -> BudgetRectifySnapshot {
function enabled_config (line 146) | fn enabled_config() -> RectifierConfig {
function budget_disabled_config (line 154) | fn budget_disabled_config() -> RectifierConfig {
function master_disabled_config (line 162) | fn master_disabled_config() -> RectifierConfig {
function test_detect_budget_tokens_thinking_error (line 173) | fn test_detect_budget_tokens_thinking_error() {
function test_detect_budget_tokens_max_tokens_error (line 181) | fn test_detect_budget_tokens_max_tokens_error() {
function test_detect_budget_tokens_1024_error (line 189) | fn test_detect_budget_tokens_1024_error() {
function test_detect_budget_tokens_with_thinking_and_1024_error (line 197) | fn test_detect_budget_tokens_with_thinking_and_1024_error() {
function test_no_trigger_for_unrelated_error (line 205) | fn test_no_trigger_for_unrelated_error() {
function test_disabled_budget_config (line 214) | fn test_disabled_budget_config() {
function test_master_disabled (line 222) | fn test_master_disabled() {
function test_rectify_budget_basic (line 232) | fn test_rectify_budget_basic() {
function test_rectify_budget_skips_adaptive (line 257) | fn test_rectify_budget_skips_adaptive() {
function test_rectify_budget_preserves_large_max_tokens (line 274) | fn test_rectify_budget_preserves_large_max_tokens() {
function test_rectify_budget_creates_thinking_object_when_missing (line 290) | fn test_rectify_budget_creates_thinking_object_when_missing() {
function test_rectify_budget_no_max_tokens (line 312) | fn test_rectify_budget_no_max_tokens() {
function test_rectify_budget_normalizes_non_enabled_type (line 327) | fn test_rectify_budget_normalizes_non_enabled_type() {
function test_rectify_budget_no_change_when_already_valid (line 345) | fn test_rectify_budget_no_change_when_already_valid() {
FILE: src-tauri/src/proxy/thinking_optimizer.rs
function optimize (line 12) | pub fn optimize(body: &mut Value, config: &OptimizerConfig) {
function append_beta (line 77) | fn append_beta(body: &mut Value, beta: &str) {
function enabled_config (line 102) | fn enabled_config() -> OptimizerConfig {
function disabled_config (line 111) | fn disabled_config() -> OptimizerConfig {
function test_adaptive_opus_4_6 (line 121) | fn test_adaptive_opus_4_6() {
function test_adaptive_sonnet_4_6 (line 139) | fn test_adaptive_sonnet_4_6() {
function test_legacy_sonnet_4_5_thinking_null (line 156) | fn test_legacy_sonnet_4_5_thinking_null() {
function test_legacy_budget_too_small_upgraded (line 172) | fn test_legacy_budget_too_small_upgraded() {
function test_skip_haiku (line 187) | fn test_skip_haiku() {
function test_thinking_optimizer_disabled (line 201) | fn test_thinking_optimizer_disabled() {
function test_adaptive_dedup_beta (line 215) | fn test_adaptive_dedup_beta() {
function test_legacy_disabled_thinking_injected (line 234) | fn test_legacy_disabled_thinking_injected() {
function test_legacy_default_max_tokens (line 249) | fn test_legacy_default_max_tokens() {
function test_append_beta_null_field (line 262) | fn test_append_beta_null_field() {
FILE: src-tauri/src/proxy/thinking_rectifier.rs
type RectifyResult (line 11) | pub struct RectifyResult {
function should_rectify_thinking_signature (line 26) | pub fn should_rectify_thinking_signature(
function rectify_anthropic_request (line 110) | pub fn rectify_anthropic_request(body: &mut Value) -> RectifyResult {
function should_remove_top_level_thinking (line 184) | fn should_remove_top_level_thinking(body: &Value, messages: &[Value]) ->...
function normalize_thinking_type (line 232) | pub fn normalize_thinking_type(body: Value) -> Value {
function enabled_config (line 241) | fn enabled_config() -> RectifierConfig {
function disabled_config (line 249) | fn disabled_config() -> RectifierConfig {
function master_disabled_config (line 257) | fn master_disabled_config() -> RectifierConfig {
function test_detect_invalid_signature (line 268) | fn test_detect_invalid_signature() {
function test_detect_invalid_signature_no_backticks (line 276) | fn test_detect_invalid_signature_no_backticks() {
function test_detect_invalid_signature_nested_json (line 284) | fn test_detect_invalid_signature_nested_json() {
function test_detect_thinking_expected (line 294) | fn test_detect_thinking_expected() {
function test_no_detect_thinking_expected_without_tool_use (line 302) | fn test_no_detect_thinking_expected_without_tool_use() {
function test_detect_must_start_with_thinking (line 310) | fn test_detect_must_start_with_thinking() {
function test_no_trigger_for_unrelated_error (line 318) | fn test_no_trigger_for_unrelated_error() {
function test_detect_signature_field_required (line 331) | fn test_detect_signature_field_required() {
function test_disabled_config (line 346) | fn test_disabled_config() {
function test_master_disabled (line 355) | fn test_master_disabled() {
function test_rectify_removes_thinking_blocks (line 366) | fn test_rectify_removes_thinking_blocks() {
function test_rectify_removes_top_level_thinking (line 396) | fn test_rectify_removes_top_level_thinking() {
function test_rectify_no_change_when_no_issues (line 418) | fn test_rectify_no_change_when_no_issues() {
function test_rectify_no_messages (line 434) | fn test_rectify_no_messages() {
function test_rectify_preserves_thinking_when_prefix_exists (line 441) | fn test_rectify_preserves_thinking_when_prefix_exists() {
function test_detect_signature_extra_inputs (line 467) | fn test_detect_signature_extra_inputs() {
function test_detect_thinking_cannot_be_modified (line 476) | fn test_detect_thinking_cannot_be_modified() {
function test_detect_invalid_request (line 485) | fn test_detect_invalid_request() {
function test_do_not_detect_thinking_type_tag_mismatch (line 502) | fn test_do_not_detect_thinking_type_tag_mismatch() {
function test_rectify_keeps_adaptive_when_no_legacy_blocks (line 513) | fn test_rectify_keeps_adaptive_when_no_legacy_blocks() {
function test_rectify_adaptive_preserves_existing_budget_tokens (line 531) | fn test_rectify_adaptive_preserves_existing_budget_tokens() {
function test_rectify_does_not_change_enabled_type (line 549) | fn test_rectify_does_not_change_enabled_type() {
function test_rectify_removes_top_level_thinking_adaptive (line 566) | fn test_rectify_removes_top_level_thinking_adaptive() {
function test_rectify_adaptive_still_cleans_legacy_signature_blocks (line 589) | fn test_rectify_adaptive_still_cleans_legacy_signature_blocks() {
function test_normalize_thinking_type_adaptive_unchanged (line 616) | fn test_normalize_thinking_type_adaptive_unchanged() {
function test_normalize_thinking_type_enabled_unchanged (line 629) | fn test_normalize_thinking_type_enabled_unchanged() {
function test_normalize_thinking_type_disabled_unchanged (line 642) | fn test_normalize_thinking_type_disabled_unchanged() {
function test_normalize_thinking_type_preserves_budget (line 654) | fn test_normalize_thinking_type_preserves_budget() {
function test_normalize_thinking_type_no_thinking (line 667) | fn test_normalize_thinking_type_no_thinking() {
function test_normalize_thinking_type_unknown_unchanged (line 678) | fn test_normalize_thinking_type_unknown_unchanged() {
FILE: src-tauri/src/proxy/types.rs
type ProxyConfig (line 5) | pub struct ProxyConfig {
function default_streaming_first_byte_timeout (line 30) | fn default_streaming_first_byte_timeout() -> u64 {
function default_streaming_idle_timeout (line 34) | fn default_streaming_idle_timeout() -> u64 {
function default_non_streaming_timeout (line 38) | fn default_non_streaming_timeout() -> u64 {
method default (line 43) | fn default() -> Self {
type ProxyStatus (line 60) | pub struct ProxyStatus {
type ActiveTarget (line 96) | pub struct ActiveTarget {
type ProxyServerInfo (line 104) | pub struct ProxyServerInfo {
type ProxyTakeoverStatus (line 112) | pub struct ProxyTakeoverStatus {
type ApiFormat (line 123) | pub enum ApiFormat {
type ProviderHealth (line 131) | pub struct ProviderHealth {
type LiveBackup (line 144) | pub struct LiveBackup {
type GlobalProxyConfig (line 156) | pub struct GlobalProxyConfig {
type AppProxyConfig (line 170) | pub struct AppProxyConfig {
type RectifierConfig (line 202) | pub struct RectifierConfig {
function default_true (line 218) | fn default_true() -> bool {
function default_log_level (line 222) | fn default_log_level() -> String {
method default (line 227) | fn default() -> Self {
type OptimizerConfig (line 242) | pub struct OptimizerConfig {
function default_cache_ttl (line 257) | fn default_cache_ttl() -> String {
method default (line 262) | fn default() -> Self {
type LogConfig (line 277) | pub struct LogConfig {
method to_level_filter (line 297) | pub fn to_level_filter(&self) -> log::LevelFilter {
method default (line 287) | fn default() -> Self {
function test_rectifier_config_default_enabled (line 317) | fn test_rectifier_config_default_enabled() {
function test_rectifier_config_serde_default (line 332) | fn test_rectifier_config_serde_default() {
function test_rectifier_config_serde_explicit_true (line 342) | fn test_rectifier_config_serde_explicit_true() {
function test_rectifier_config_serde_partial_fields (line 353) | fn test_rectifier_config_serde_partial_fields() {
function test_log_config_default (line 363) | fn test_log_config_default() {
function test_log_config_serde_default (line 370) | fn test_log_config_serde_default() {
function test_log_config_to_level_filter (line 378) | fn test_log_config_to_level_filter() {
function test_log_config_serde_roundtrip (line 425) | fn test_log_config_serde_roundtrip() {
FILE: src-tauri/src/proxy/usage/calculator.rs
type CostBreakdown (line 11) | pub struct CostBreakdown {
type ModelPricing (line 21) | pub struct ModelPricing {
method from_strings (line 90) | pub fn from_strings(
type CostCalculator (line 29) | pub struct CostCalculator;
method calculate (line 44) | pub fn calculate(
method try_calculate (line 79) | pub fn try_calculate(
function test_cost_calculation (line 110) | fn test_cost_calculation() {
function test_cost_multiplier (line 140) | fn test_cost_multiplier() {
function test_unknown_model_handling (line 161) | fn test_unknown_model_handling() {
function test_decimal_precision (line 177) | fn test_decimal_precision() {
FILE: src-tauri/src/proxy/usage/logger.rs
type RequestLog (line 13) | pub struct RequestLog {
type UsageLogger (line 35) | pub struct UsageLogger<'a> {
function new (line 40) | pub fn new(db: &'a Database) -> Self {
function log_request (line 45) | pub fn log_request(&self, log: &RequestLog) -> Result<(), AppError> {
function log_error (line 118) | pub fn log_error(
function log_error_with_context (line 154) | pub fn log_error_with_context(
function get_model_pricing (line 190) | pub fn get_model_pricing(&self, model_id: &str) -> Result<Option<ModelPr...
function resolve_pricing_config (line 204) | pub async fn resolve_pricing_config(
function log_with_calculation (line 287) | pub fn log_with_calculation(
function test_log_request (line 339) | fn test_log_request() -> Result<(), AppError> {
function test_log_error (line 395) | fn test_log_error() -> Result<(), AppError> {
FILE: src-tauri/src/proxy/usage/parser.rs
type TokenUsage (line 14) | pub struct TokenUsage {
method from_claude_response (line 35) | pub fn from_claude_response(body: &Value) -> Option<Self> {
method from_claude_stream_events (line 60) | pub fn from_claude_stream_events(events: &[Value]) -> Option<Self> {
method from_openrouter_response (line 148) | pub fn from_openrouter_response(body: &Value) -> Option<Self> {
method from_codex_response (line 160) | pub fn from_codex_response(body: &Value) -> Option<Self> {
method from_codex_response_adjusted (line 213) | pub fn from_codex_response_adjusted(body: &Value) -> Option<Self> {
method from_codex_stream_events (line 253) | pub fn from_codex_stream_events(events: &[Value]) -> Option<Self> {
method from_codex_response_auto (line 277) | pub fn from_codex_response_auto(body: &Value) -> Option<Self> {
method from_codex_stream_events_auto (line 295) | pub fn from_codex_stream_events_auto(events: &[Value]) -> Option<Self> {
method from_openai_response (line 316) | pub fn from_openai_response(body: &Value) -> Option<Self> {
method from_openai_stream_events (line 346) | pub fn from_openai_stream_events(events: &[Value]) -> Option<Self> {
method from_gemini_response (line 362) | pub fn from_gemini_response(body: &Value) -> Option<Self> {
method from_gemini_stream_chunks (line 391) | pub fn from_gemini_stream_chunks(chunks: &[Value]) -> Option<Self> {
type ApiType (line 26) | pub enum ApiType {
function test_claude_response_parsing (line 449) | fn test_claude_response_parsing() {
function test_claude_response_parsing_no_model (line 469) | fn test_claude_response_parsing_no_model() {
function test_claude_stream_parsing (line 488) | fn test_claude_stream_parsing() {
function test_claude_stream_parsing_no_model (line 518) | fn test_claude_stream_parsing_no_model() {
function test_openrouter_response_parsing (line 547) | fn test_openrouter_response_parsing() {
function test_gemini_response_parsing (line 563) | fn test_gemini_response_parsing() {
function test_gemini_response_parsing_no_model (line 585) | fn test_gemini_response_parsing_no_model() {
function test_gemini_response_with_thoughts (line 605) | fn test_gemini_response_with_thoughts() {
function test_codex_response_parsing_cached_tokens_in_details (line 644) | fn test_codex_response_parsing_cached_tokens_in_details() {
function test_codex_response_adjusted (line 663) | fn test_codex_response_adjusted() {
function test_codex_response_adjusted_no_cache (line 682) | fn test_codex_response_adjusted_no_cache() {
function test_codex_response_adjusted_cache_read_input_tokens (line 698) | fn test_codex_response_adjusted_cache_read_input_tokens() {
function test_codex_response_adjusted_saturating_sub (line 714) | fn test_codex_response_adjusted_saturating_sub() {
function test_openrouter_stream_parsing (line 733) | fn test_openrouter_stream_parsing() {
function test_native_claude_stream_parsing (line 766) | fn test_native_claude_stream_parsing() {
function test_codex_response_auto_openai_format (line 800) | fn test_codex_response_auto_openai_format() {
function test_codex_response_auto_codex_format (line 821) | fn test_codex_response_auto_codex_format() {
function test_codex_stream_events_auto_codex_format (line 843) | fn test_codex_stream_events_auto_codex_format() {
function test_codex_stream_events_auto_openai_format (line 876) | fn test_codex_stream_events_auto_openai_format() {
FILE: src-tauri/src/services/config.rs
constant MAX_BACKUPS (line 10) | const MAX_BACKUPS: usize = 10;
type ConfigService (line 13) | pub struct ConfigService;
method create_backup (line 17) | pub fn create_backup(config_path: &Path) -> Result<String, AppError> {
method cleanup_old_backups (line 41) | fn cleanup_old_backups(backup_dir: &Path, retain: usize) -> Result<(),...
method sync_current_providers_to_live (line 87) | pub fn sync_current_providers_to_live(config: &mut MultiAppConfig) -> ...
method sync_current_provider_for_app (line 94) | fn sync_current_provider_for_app(
method sync_codex_live (line 138) | fn sync_codex_live(
method sync_claude_live (line 176) | fn sync_claude_live(
method sync_gemini_live (line 201) | fn sync_gemini_live(
FILE: src-tauri/src/services/env_checker.rs
type EnvConflict (line 7) | pub struct EnvConflict {
function check_env_conflicts (line 20) | pub fn check_env_conflicts(app: &str) -> Result<Vec<EnvConflict>, String> {
function get_keywords_for_app (line 35) | fn get_keywords_for_app(app: &str) -> Vec<&str> {
function check_system_env (line 46) | fn check_system_env(keywords: &[&str]) -> Result<Vec<EnvConflict>, Strin...
function check_system_env (line 83) | fn check_system_env(keywords: &[&str]) -> Result<Vec<EnvConflict>, Strin...
function check_shell_configs (line 103) | fn check_shell_configs(keywords: &[&str]) -> Result<Vec<EnvConflict>, St...
function test_get_keywords (line 159) | fn test_get_keywords() {
FILE: src-tauri/src/services/env_manager.rs
type BackupInfo (line 14) | pub struct BackupInfo {
function delete_env_vars (line 21) | pub fn delete_env_vars(conflicts: Vec<EnvConflict>) -> Result<BackupInfo...
function create_backup (line 43) | fn create_backup(conflicts: &[EnvConflict]) -> Result<BackupInfo, String> {
function get_backup_dir (line 69) | fn get_backup_dir() -> Result<PathBuf, String> {
function delete_single_env (line 76) | fn delete_single_env(conflict: &EnvConflict) -> Result<(), String> {
function delete_single_env (line 105) | fn delete_single_env(conflict: &EnvConflict) -> Result<(), String> {
function restore_from_backup (line 153) | pub fn restore_from_backup(backup_path: String) -> Result<(), String> {
function restore_single_env (line 170) | fn restore_single_env(conflict: &EnvConflict) -> Result<(), String> {
function restore_single_env (line 200) | fn restore_single_env(conflict: &EnvConflict) -> Result<(), String> {
function test_backup_dir_creation (line 236) | fn test_backup_dir_creation() {
FILE: src-tauri/src/services/mcp.rs
type McpService (line 10) | pub struct McpService;
method get_all_servers (line 14) | pub fn get_all_servers(state: &AppState) -> Result<IndexMap<String, Mc...
method upsert_server (line 19) | pub fn upsert_server(state: &AppState, server: McpServer) -> Result<()...
method delete_server (line 51) | pub fn delete_server(state: &AppState, id: &str) -> Result<bool, AppEr...
method toggle_app (line 66) | pub fn toggle_app(
method sync_server_to_apps (line 90) | fn sync_server_to_apps(_state: &AppState, server: &McpServer) -> Resul...
method sync_server_to_app (line 99) | fn sync_server_to_app(
method sync_server_to_app_no_config (line 107) | fn sync_server_to_app_no_config(server: &McpServer, app: &AppType) -> ...
method remove_server_from_all_apps (line 136) | fn remove_server_from_all_apps(
method remove_server_from_app (line 148) | fn remove_server_from_app(_state: &AppState, id: &str, app: &AppType) ...
method sync_all_enabled (line 165) | pub fn sync_all_enabled(state: &AppState) -> Result<(), AppError> {
method get_servers (line 191) | pub fn get_servers(
method set_enabled (line 209) | pub fn set_enabled(
method sync_enabled (line 221) | pub fn sync_enabled(state: &AppState, app: AppType) -> Result<(), AppE...
method import_from_claude (line 234) | pub fn import_from_claude(state: &AppState) -> Result<usize, AppError> {
method import_from_codex (line 272) | pub fn import_from_codex(state: &AppState) -> Result<usize, AppError> {
method import_from_gemini (line 310) | pub fn import_from_gemini(state: &AppState) -> Result<usize, AppError> {
method import_from_opencode (line 348) | pub fn import_from_opencode(state: &AppState) -> Result<usize, AppErro...
FILE: src-tauri/src/services/omo.rs
type OmoLocalFileData (line 11) | pub struct OmoLocalFileData {
type OmoProfileData (line 19) | type OmoProfileData = (Option<Value>, Option<Value>, Option<Value>);
type OmoVariant (line 23) | pub struct OmoVariant {
constant STANDARD (line 34) | pub const STANDARD: OmoVariant = OmoVariant {
constant SLIM (line 45) | pub const SLIM: OmoVariant = OmoVariant {
type OmoService (line 58) | pub struct OmoService;
method config_path (line 63) | fn config_path(v: &OmoVariant) -> PathBuf {
method resolve_local_config_path (line 67) | fn resolve_local_config_path(v: &OmoVariant) -> Result<PathBuf, AppErr...
method read_jsonc_object (line 81) | fn read_jsonc_object(path: &Path) -> Result<Map<String, Value>, AppErr...
method extract_other_fields_with_keys (line 94) | fn extract_other_fields_with_keys(
method insert_opt_value (line 109) | fn insert_opt_value(result: &mut Map<String, Value>, key: &str, value:...
method insert_object_entries (line 115) | fn insert_object_entries(result: &mut Map<String, Value>, value: Optio...
method delete_config_file (line 125) | pub fn delete_config_file(v: &OmoVariant) -> Result<(), AppError> {
method write_config_to_file (line 135) | pub fn write_config_to_file(state: &AppState, v: &OmoVariant) -> Resul...
method build_config (line 161) | fn build_config(v: &OmoVariant, profile_data: Option<&OmoProfileData>)...
method import_from_local (line 173) | pub fn import_from_local(
method read_local_file (line 227) | pub fn read_local_file(v: &OmoVariant) -> Result<OmoLocalFileData, App...
method build_local_file_data (line 244) | fn build_local_file_data(
method strip_jsonc_comments (line 273) | fn strip_jsonc_comments(input: &str) -> String {
function test_strip_jsonc_comments (line 335) | fn test_strip_jsonc_comments() {
function test_build_config_empty (line 350) | fn test_build_config_empty() {
function test_build_config_with_profile (line 357) | fn test_build_config_with_profile() {
function test_build_local_file_data_keeps_all_non_agent_category_fields_in_other (line 377) | fn test_build_local_file_data_keeps_all_non_agent_category_fields_in_oth...
function test_build_config_ignores_non_object_other_fields (line 421) | fn test_build_config_ignores_non_object_other_fields() {
function test_build_config_slim_excludes_categories (line 434) | fn test_build_config_slim_excludes_categories() {
FILE: src-tauri/src/services/prompt.rs
function get_unix_timestamp (line 11) | fn get_unix_timestamp() -> Result<i64, AppError> {
type PromptService (line 18) | pub struct PromptService;
method get_prompts (line 21) | pub fn get_prompts(
method upsert_prompt (line 28) | pub fn upsert_prompt(
method delete_prompt (line 60) | pub fn delete_prompt(state: &AppState, app: AppType, id: &str) -> Resu...
method enable_prompt (line 73) | pub fn enable_prompt(state: &AppState, app: AppType, id: &str) -> Resu...
method import_from_file (line 146) | pub fn import_from_file(state: &AppState, app: AppType) -> Result<Stri...
method get_current_file_content (line 175) | pub fn get_current_file_content(app: AppType) -> Result<Option<String>...
method import_from_file_on_first_launch (line 187) | pub fn import_from_file_on_first_launch(
FILE: src-tauri/src/services/provider/endpoints.rs
function get_custom_endpoints (line 13) | pub fn get_custom_endpoints(
function add_custom_endpoint (line 35) | pub fn add_custom_endpoint(
function remove_custom_endpoint (line 57) | pub fn remove_custom_endpoint(
function update_endpoint_last_used (line 71) | pub fn update_endpoint_last_used(
function now_millis (line 93) | fn now_millis() -> i64 {
FILE: src-tauri/src/services/provider/gemini_auth.rs
type GeminiAuthType (line 12) | pub(crate) enum GeminiAuthType {
constant PACKYCODE_PARTNER_KEY (line 22) | const PACKYCODE_PARTNER_KEY: &str = "packycode";
constant GOOGLE_OFFICIAL_PARTNER_KEY (line 23) | const GOOGLE_OFFICIAL_PARTNER_KEY: &str = "google-official";
constant PACKYCODE_KEYWORDS (line 26) | const PACKYCODE_KEYWORDS: [&str; 3] = ["packycode", "packyapi", "packy"];
function detect_gemini_auth_type (line 37) | pub(crate) fn detect_gemini_auth_type(provider: &Provider) -> GeminiAuth...
function contains_packycode_keyword (line 85) | fn contains_packycode_keyword(value: &str) -> bool {
function is_google_official_gemini (line 97) | pub(crate) fn is_google_official_gemini(provider: &Provider) -> bool {
function ensure_google_oauth_security_flag (line 132) | pub(crate) fn ensure_google_oauth_security_flag(provider: &Provider) -> ...
FILE: src-tauri/src/services/provider/live.rs
function sanitize_claude_settings_for_live (line 24) | pub(crate) fn sanitize_claude_settings_for_live(settings: &Value) -> Val...
function json_is_subset (line 36) | fn json_is_subset(target: &Value, source: &Value) -> bool {
function json_array_contains_subset (line 58) | fn json_array_contains_subset(target_arr: &[Value], source_arr: &[Value]...
function json_remove_array_items (line 73) | fn json_remove_array_items(target_arr: &mut Vec<Value>, source_arr: &[Va...
function json_deep_merge (line 84) | fn json_deep_merge(target: &mut Value, source: &Value) {
function json_deep_remove (line 102) | fn json_deep_remove(target: &mut Value, source: &Value) {
function toml_value_is_subset (line 130) | fn toml_value_is_subset(target: &toml_edit::Value, source: &toml_edit::V...
function toml_array_contains_subset (line 161) | fn toml_array_contains_subset(target: &toml_edit::Array, source: &toml_e...
function toml_remove_array_items (line 181) | fn toml_remove_array_items(target: &mut toml_edit::Array, source: &toml_...
function toml_item_is_subset (line 198) | fn toml_item_is_subset(target: &Item, source: &Item) -> bool {
function merge_toml_item (line 218) | fn merge_toml_item(target: &mut Item, source: &Item) {
function merge_toml_table_like (line 229) | fn merge_toml_table_like(target: &mut dyn TableLike, source: &dyn TableL...
function remove_toml_item (line 240) | fn remove_toml_item(target: &mut Item, source: &Item) {
function remove_toml_table_like (line 275) | fn remove_toml_table_like(target: &mut dyn TableLike, source: &dyn Table...
function settings_contain_common_config (line 294) | fn settings_contain_common_config(app_type: &AppType, settings: &Value, ...
function provider_uses_common_config (line 339) | pub(crate) fn provider_uses_common_config(
function remove_common_config_from_settings (line 356) | pub(crate) fn remove_common_config_from_settings(
function apply_common_config_to_settings (line 409) | fn apply_common_config_to_settings(
function build_effective_settings_with_common_config (line 464) | pub(crate) fn build_effective_settings_with_common_config(
function write_live_with_common_config (line 490) | pub(crate) fn write_live_with_common_config(
function strip_common_config_from_live_settings (line 502) | pub(crate) fn strip_common_config_from_live_settings(
function normalize_provider_common_config_for_storage (line 541) | pub(crate) fn normalize_provider_common_config_for_storage(
type LiveSnapshot (line 581) | pub(crate) enum LiveSnapshot {
method restore (line 597) | pub(crate) fn restore(&self) -> Result<(), AppError> {
function write_live_snapshot (line 652) | pub(crate) fn write_live_snapshot(app_type: &AppType, provider: &Provide...
function sync_all_providers_to_live (line 788) | fn sync_all_providers_to_live(state: &AppState, app_type: &AppType) -> R...
function sync_current_provider_for_app_to_live (line 810) | pub(crate) fn sync_current_provider_for_app_to_live(
function sync_current_to_live (line 841) | pub fn sync_current_to_live(state: &AppState) -> Result<(), AppError> {
function read_live_settings (line 879) | pub fn read_live_settings(app_type: AppType) -> Result<Value, AppError> {
function import_default_config (line 975) | pub fn import_default_config(state: &AppState, app_type: AppType) -> Res...
function write_gemini_live (line 1072) | pub(crate) fn write_gemini_live(provider: &Provider) -> Result<(), AppEr...
function remove_opencode_provider_from_live (line 1162) | pub(crate) fn remove_opencode_provider_from_live(provider_id: &str) -> R...
function import_opencode_providers_from_live (line 1182) | pub fn import_opencode_providers_from_live(state: &AppState) -> Result<u...
function import_openclaw_providers_from_live (line 1235) | pub fn import_openclaw_providers_from_live(state: &AppState) -> Result<u...
function remove_openclaw_provider_from_live (line 1299) | pub fn remove_openclaw_provider_from_live(provider_id: &str) -> Result<(...
function claude_common_config_apply_and_remove_roundtrip_for_non_overlapping_fields (line 1320) | fn claude_common_config_apply_and_remove_roundtrip_for_non_overlapping_f...
function codex_common_config_apply_and_remove_roundtrip_for_non_overlapping_fields (line 1344) | fn codex_common_config_apply_and_remove_roundtrip_for_non_overlapping_fi...
function explicit_common_config_flag_overrides_legacy_subset_detection (line 1364) | fn explicit_common_config_flag_overrides_legacy_subset_detection() {
function claude_common_config_array_subset_detection_and_strip_preserve_extra_items (line 1389) | fn claude_common_config_array_subset_detection_and_strip_preserve_extra_...
function codex_common_config_array_subset_detection_and_strip_preserve_extra_items (line 1413) | fn codex_common_config_array_subset_detection_and_strip_preserve_extra_i...
FILE: src-tauri/src/services/provider/mod.rs
type ProviderService (line 43) | pub struct ProviderService;
method normalize_provider_if_claude (line 135) | fn normalize_provider_if_claude(app_type: &AppType, provider: &mut Pro...
method list (line 145) | pub fn list(
method current (line 159) | pub fn current(state: &AppState, app_type: AppType) -> Result<String, ...
method add (line 169) | pub fn add(state: &AppState, app_type: AppType, provider: Provider) ->...
method update (line 207) | pub fn update(
method delete (line 295) | pub fn delete(state: &AppState, app_type: AppType, id: &str) -> Result...
method remove_from_live_config (line 363) | pub fn remove_from_live_config(
method switch (line 440) | pub fn switch(state: &AppState, app_type: AppType, id: &str) -> Result...
method switch_normal (line 520) | fn switch_normal(
method sync_current_to_live (line 609) | pub fn sync_current_to_live(state: &AppState) -> Result<(), AppError> {
method sync_current_provider_for_app (line 613) | pub fn sync_current_provider_for_app(
method migrate_legacy_common_config_usage (line 660) | pub fn migrate_legacy_common_config_usage(
method migrate_legacy_common_config_usage_if_needed (line 714) | pub fn migrate_legacy_common_config_usage_if_needed(
method extract_common_config_snippet (line 737) | pub fn extract_common_config_snippet(
method extract_common_config_snippet_from_settings (line 762) | pub fn extract_common_config_snippet_from_settings(
method extract_claude_common_config (line 776) | fn extract_claude_common_config(settings: &Value) -> Result<String, Ap...
method extract_codex_common_config (line 829) | fn extract_codex_common_config(settings: &Value) -> Result<String, App...
method extract_gemini_common_config (line 878) | fn extract_gemini_common_config(settings: &Value) -> Result<String, Ap...
method extract_opencode_common_config (line 906) | fn extract_opencode_common_config(settings: &Value) -> Result<String, ...
method extract_openclaw_common_config (line 929) | fn extract_openclaw_common_config(settings: &Value) -> Result<String, ...
method import_default_config (line 952) | pub fn import_default_config(state: &AppState, app_type: AppType) -> R...
method read_live_settings (line 957) | pub fn read_live_settings(app_type: AppType) -> Result<Value, AppError> {
method get_custom_endpoints (line 962) | pub fn get_custom_endpoints(
method add_custom_endpoint (line 971) | pub fn add_custom_endpoint(
method remove_custom_endpoint (line 981) | pub fn remove_custom_endpoint(
method update_endpoint_last_used (line 991) | pub fn update_endpoint_last_used(
method update_sort_order (line 1001) | pub fn update_sort_order(
method query_usage (line 1019) | pub async fn query_usage(
method test_usage_script (line 1029) | pub async fn test_usage_script(
method write_gemini_live (line 1056) | pub(crate) fn write_gemini_live(provider: &Provider) -> Result<(), App...
method validate_provider_settings (line 1060) | fn validate_provider_settings(app_type: &AppType, provider: &Provider)...
method extract_credentials (line 1150) | fn extract_credentials(
method list_universal (line 1426) | pub fn list_universal(
method get_universal (line 1433) | pub fn get_universal(
method upsert_universal (line 1441) | pub fn upsert_universal(
method delete_universal (line 1452) | pub fn delete_universal(state: &AppState, id: &str) -> Result<bool, Ap...
method sync_universal_to_apps (line 1479) | pub fn sync_universal_to_apps(state: &AppState, id: &str) -> Result<bo...
method merge_json (line 1532) | fn merge_json(base: &mut serde_json::Value, patch: &serde_json::Value) {
type SwitchResult (line 48) | pub struct SwitchResult {
function validate_provider_settings_rejects_missing_auth (line 58) | fn validate_provider_settings_rejects_missing_auth() {
function extract_credentials_returns_expected_values (line 74) | fn extract_credentials_returns_expected_values() {
function extract_codex_common_config_preserves_mcp_servers_base_url (line 93) | fn extract_codex_common_config_preserves_mcp_servers_base_url() {
function normalize_claude_models_in_value (line 1339) | pub(crate) fn normalize_claude_models_in_value(settings: &mut Value) -> ...
type ProviderSortUpdate (line 1411) | pub struct ProviderSortUpdate {
FILE: src-tauri/src/services/provider/usage.rs
function execute_and_format_usage_result (line 13) | pub(crate) async fn execute_and_format_usage_result(
function extract_api_key_from_provider (line 85) | fn extract_api_key_from_provider(provider: &crate::provider::Provider) -...
function extract_base_url_from_provider (line 100) | fn extract_base_url_from_provider(provider: &crate::provider::Provider) ...
function query_usage (line 113) | pub async fn query_usage(
function test_usage_script (line 187) | pub async fn test_usage_script(
function validate_usage_script (line 213) | pub(crate) fn validate_usage_script(script: &UsageScript) -> Result<(), ...
FILE: src-tauri/src/services/proxy.rs
constant PROXY_TOKEN_PLACEHOLDER (line 20) | const PROXY_TOKEN_PLACEHOLDER: &str = "PROXY_MANAGED";
constant CLAUDE_MODEL_OVERRIDE_ENV_KEYS (line 26) | const CLAUDE_MODEL_OVERRIDE_ENV_KEYS: [&str; 6] = [
type ProxyService (line 37) | pub struct ProxyService {
method new (line 45) | pub fn new(db: Arc<Database>) -> Self {
method cleanup_claude_model_overrides_in_live (line 57) | pub fn cleanup_claude_model_overrides_in_live(&self) -> Result<(), Str...
method set_app_handle (line 79) | pub fn set_app_handle(&self, handle: tauri::AppHandle) {
method start (line 86) | pub async fn start(&self) -> Result<ProxyServerInfo, String> {
method start_with_takeover (line 136) | pub async fn start_with_takeover(&self) -> Result<ProxyServerInfo, Str...
method get_takeover_status (line 195) | pub async fn get_takeover_status(&self) -> Result<ProxyTakeoverStatus,...
method set_takeover_for_app (line 232) | pub async fn set_takeover_for_app(&self, app_type: &str, enabled: bool...
method sync_live_to_provider (line 373) | async fn sync_live_to_provider(&self, app_type: &AppType) -> Result<()...
method sync_live_config_to_provider (line 392) | async fn sync_live_config_to_provider(
method sync_live_to_providers (line 610) | async fn sync_live_to_providers(&self) -> Result<(), String> {
method stop (line 631) | pub async fn stop(&self) -> Result<(), String> {
method stop_with_restore (line 662) | pub async fn stop_with_restore(&self) -> Result<(), String> {
method stop_with_restore_keep_state (line 709) | pub async fn stop_with_restore_keep_state(&self) -> Result<(), String> {
method backup_live_configs (line 742) | async fn backup_live_configs(&self) -> Result<(), String> {
method backup_live_config_strict (line 778) | async fn backup_live_config_strict(&self, app_type: &AppType) -> Resul...
method build_proxy_urls (line 804) | async fn build_proxy_urls(&self) -> Result<(String, String), String> {
method takeover_live_configs (line 839) | async fn takeover_live_configs(&self) -> Result<(), String> {
method takeover_live_config_strict (line 922) | async fn takeover_live_config_strict(&self, app_type: &AppType) -> Res...
method takeover_live_config_best_effort (line 1013) | async fn takeover_live_config_best_effort(&self, app_type: &AppType) -...
method restore_live_config_for_app (line 1102) | async fn restore_live_config_for_app(&self, app_type: &AppType) -> Res...
method restore_live_configs (line 1140) | async fn restore_live_configs(&self) -> Result<(), String> {
method restore_live_config_for_app_with_fallback (line 1159) | async fn restore_live_config_for_app_with_fallback(
method write_live_config_for_app (line 1208) | fn write_live_config_for_app(&self, app_type: &AppType, config: &Value...
method detect_takeover_in_live_config_for_app (line 1224) | pub fn detect_takeover_in_live_config_for_app(&self, app_type: &AppTyp...
method restore_live_from_ssot_for_app (line 1254) | fn restore_live_from_ssot_for_app(&self, app_type: &AppType) -> Result...
method cleanup_takeover_placeholders_in_live_for_app (line 1277) | fn cleanup_takeover_placeholders_in_live_for_app(
method is_local_proxy_url (line 1296) | fn is_local_proxy_url(url: &str) -> bool {
method cleanup_claude_takeover_placeholders_in_live (line 1311) | fn cleanup_claude_takeover_placeholders_in_live(&self) -> Result<(), S...
method cleanup_codex_takeover_placeholders_in_live (line 1342) | fn cleanup_codex_takeover_placeholders_in_live(&self) -> Result<(), St...
method remove_local_toml_base_url (line 1362) | fn remove_local_toml_base_url(toml_str: &str) -> String {
method cleanup_gemini_takeover_placeholders_in_live (line 1366) | fn cleanup_gemini_takeover_placeholders_in_live(&self) -> Result<(), S...
method is_takeover_active (line 1391) | pub async fn is_takeover_active(&self) -> Result<bool, String> {
method recover_from_crash (line 1400) | pub async fn recover_from_crash(&self) -> Result<(), String> {
method detect_takeover_in_live_configs (line 1424) | pub fn detect_takeover_in_live_configs(&self) -> bool {
method is_claude_live_taken_over (line 1446) | fn is_claude_live_taken_over(config: &Value) -> bool {
method is_codex_live_taken_over (line 1466) | fn is_codex_live_taken_over(config: &Value) -> bool {
method is_gemini_live_taken_over (line 1474) | fn is_gemini_live_taken_over(config: &Value) -> bool {
method update_live_backup_from_provider (line 1486) | pub async fn update_live_backup_from_provider(
method preserve_codex_mcp_servers_in_backup (line 1543) | fn preserve_codex_mcp_servers_in_backup(
method switch_proxy_target (line 1605) | pub async fn switch_proxy_target(
method update_toml_base_url (line 1657) | fn update_toml_base_url(toml_str: &str, new_url: &str) -> String {
method read_claude_live (line 1662) | fn read_claude_live(&self) -> Result<Value, String> {
method write_claude_live (line 1693) | fn write_claude_live(&self, config: &Value) -> Result<(), String> {
method read_codex_live (line 1699) | fn read_codex_live(&self) -> Result<Value, String> {
method write_codex_live (line 1724) | fn write_codex_live(&self, config: &Value) -> Result<(), String> {
method read_gemini_live (line 1751) | fn read_gemini_live(&self) ->
Condensed preview — 606 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (5,547K chars).
[
{
"path": ".gitattributes",
"chars": 723,
"preview": "# Auto detect text files and perform LF normalization\n* text=auto\n\n# Explicitly declare text files you want to always be"
},
{
"path": ".gitignore",
"chars": 339,
"preview": "node_modules/\ndist/\nrelease/\n.DS_Store\n*.log\n.env\n.env.local\n*.tsbuildinfo\n.npmrc\nCLAUDE.md\n# AGENTS.md\nGEMINI.md\n/.clau"
},
{
"path": ".node-version",
"chars": 8,
"preview": "22.12.0\n"
},
{
"path": "CHANGELOG.md",
"chars": 80164,
"preview": "# Changelog\n\nAll notable changes to CC Switch will be documented in this file.\n\nThe format is based on [Keep a Changelog"
},
{
"path": "LICENSE",
"chars": 1067,
"preview": "MIT License\n\nCopyright (c) 2025 Jason Young\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
},
{
"path": "README.md",
"chars": 27367,
"preview": "<div align=\"center\">\n\n# CC Switch\n\n### The All-in-One Manager for Claude Code, Codex, Gemini CLI, OpenCode & OpenClaw\n\n["
},
{
"path": "README_JA.md",
"chars": 19953,
"preview": "<div align=\"center\">\n\n# CC Switch\n\n### Claude Code、Codex、Gemini CLI、OpenCode、OpenClaw のオールインワン管理ツール\n\n[预设配置\n *\n * 统一供应商是跨应用共享的配置,修改后会自动同步到 Claude、Codex、Gemini 三个应用。\n * 适用于 NewAPI 等支持多种协议的 API"
},
{
"path": "components.json",
"chars": 445,
"preview": "{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"default\",\n \"rsc\": false,\n \"tsx\": true,\n \"tailwind\": {"
},
{
"path": "deplink.html",
"chars": 98644,
"preview": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "docs/proxy-guide-zh.md",
"chars": 2607,
"preview": "# CC Switch 代理功能使用指南\n\n## 功能介绍\n\nCC Switch 的代理功能是一个本地 HTTP 代理服务器,可以统一管理 Claude Code、Codex 和 Gemini CLI 的 API 请求。主要特性包括:\n\n-"
},
{
"path": "docs/release-notes/v3.10.0-en.md",
"chars": 8576,
"preview": "# CC Switch v3.10.0\n\n> OpenCode Support, Global Proxy, Claude Rectifier & Multi-App Experience Enhancements\n\n**[中文版 →](v"
},
{
"path": "docs/release-notes/v3.10.0-ja.md",
"chars": 5877,
"preview": "# CC Switch v3.10.0\n\n> OpenCode サポート、グローバルプロキシ、Claude Rectifier とマルチアプリ体験の強化\n\n**[中文版 →](v3.10.0-zh.md) | [English →](v3."
},
{
"path": "docs/release-notes/v3.10.0-zh.md",
"chars": 5016,
"preview": "# CC Switch v3.10.0\n\n> OpenCode 支持、全局代理、Claude Rectifier 与多应用体验增强\n\n**[English →](v3.10.0-en.md) | [日本語版 →](v3.10.0-ja.md"
},
{
"path": "docs/release-notes/v3.11.0-en.md",
"chars": 15804,
"preview": "# CC Switch v3.11.0\n\n> OpenClaw Support, Session Manager, Backup Management & 50+ Improvements\n\n**[中文版 →](v3.11.0-zh.md)"
},
{
"path": "docs/release-notes/v3.11.0-ja.md",
"chars": 10242,
"preview": "# CC Switch v3.11.0\n\n> OpenClaw サポート、セッションマネージャー、バックアップ管理と 50 以上の改善\n\n**[中文版 →](v3.11.0-zh.md) | [English →](v3.11.0-en.m"
},
{
"path": "docs/release-notes/v3.11.0-zh.md",
"chars": 8551,
"preview": "# CC Switch v3.11.0\n\n> OpenClaw 支持、会话管理器、备份管理与 50+ 项改进\n\n**[English →](v3.11.0-en.md) | [日本語版 →](v3.11.0-ja.md)**\n\n---\n\n#"
},
{
"path": "docs/release-notes/v3.11.1-en.md",
"chars": 6600,
"preview": "# CC Switch v3.11.1\n\n> Revert Partial Key-Field Merging, Restore Common Config Snippet & Bug Fixes\n\n**[中文版 →](v3.11.1-zh"
},
{
"path": "docs/release-notes/v3.11.1-ja.md",
"chars": 4695,
"preview": "# CC Switch v3.11.1\n\n> 部分キーフィールドマージの撤回、共通設定スニペットの復元とバグ修正\n\n**[中文版 →](v3.11.1-zh.md) | [English →](v3.11.1-en.md)**\n\n---\n\n"
},
{
"path": "docs/release-notes/v3.11.1-zh.md",
"chars": 3940,
"preview": "# CC Switch v3.11.1\n\n> 回退部分键值合并、恢复通用配置片段与多项修复\n\n**[English →](v3.11.1-en.md) | [日本語版 →](v3.11.1-ja.md)**\n\n---\n\n## 概览\n\nCC "
},
{
"path": "docs/release-notes/v3.12.0-en.md",
"chars": 11634,
"preview": "# CC Switch v3.12.0\n\n> Stream Check Returns, OpenAI Responses API Arrives, and OpenClaw / WebDAV Get a Major Upgrade\n\n**"
},
{
"path": "docs/release-notes/v3.12.0-ja.md",
"chars": 8026,
"preview": "# CC Switch v3.12.0\n\n> Stream Check が復活し、OpenAI Responses API に対応、OpenClaw と WebDAV も大幅強化\n\n**[中文版 →](v3.12.0-zh.md) | [E"
},
{
"path": "docs/release-notes/v3.12.0-zh.md",
"chars": 6919,
"preview": "# CC Switch v3.12.0\n\n> Stream Check 回归,OpenAI Responses API 上线,OpenClaw 与 WebDAV 迎来一次大升级\n\n**[English →](v3.12.0-en.md) |"
},
{
"path": "docs/release-notes/v3.12.1-en.md",
"chars": 6728,
"preview": "# CC Switch v3.12.1\n\n> Stability Fixes, StepFun Presets, OpenClaw authHeader, and New Sponsor Partners\n\n**[中文版 →](v3.12."
},
{
"path": "docs/release-notes/v3.12.1-ja.md",
"chars": 5082,
"preview": "# CC Switch v3.12.1\n\n> 安定性修正、StepFun プリセット、OpenClaw authHeader 対応、新スポンサーパートナー\n\n**[中文版 →](v3.12.1-zh.md) | [English →](v3"
},
{
"path": "docs/release-notes/v3.12.1-zh.md",
"chars": 4506,
"preview": "# CC Switch v3.12.1\n\n> 稳定性修复、StepFun 预设、OpenClaw authHeader 支持,以及新赞助商伙伴\n\n**[English →](v3.12.1-en.md) | [日本語版 →](v3.12.1"
},
{
"path": "docs/release-notes/v3.12.2-en.md",
"chars": 7338,
"preview": "# CC Switch v3.12.2\n\n> Common Config Protection During Proxy Takeover, Snippet Lifecycle Stability, Section-Aware Codex "
},
{
"path": "docs/release-notes/v3.12.2-ja.md",
"chars": 5177,
"preview": "# CC Switch v3.12.2\n\n> プロキシテイクオーバー中の共通設定保護、Snippet ライフサイクルの安定化、Codex TOML セクション対応編集\n\n**[中文版 →](v3.12.2-zh.md) | [English"
},
{
"path": "docs/release-notes/v3.12.2-zh.md",
"chars": 4489,
"preview": "# CC Switch v3.12.2\n\n> 代理接管期间通用配置保护、Snippet 生命周期稳定性、Codex TOML Section 感知编辑\n\n**[English →](v3.12.2-en.md) | [日本語版 →](v3."
},
{
"path": "docs/release-notes/v3.12.3-en.md",
"chars": 8249,
"preview": "# CC Switch v3.12.3\n\n> Tool Search Domain Bypass, Skill Backup/Restore Lifecycle, Proxy Gzip & o-Series Compatibility\n\n*"
},
{
"path": "docs/release-notes/v3.12.3-ja.md",
"chars": 6050,
"preview": "# CC Switch v3.12.3\n\n> Tool Search ドメイン制限バイパス、Skill バックアップ/リストアライフサイクル、プロキシ Gzip 圧縮と o シリーズモデル互換性\n\n**[中文版 →](v3.12.3-zh."
},
{
"path": "docs/release-notes/v3.12.3-zh.md",
"chars": 5066,
"preview": "# CC Switch v3.12.3\n\n> Tool Search 域名限制绕过、Skill 备份/恢复生命周期、代理 Gzip 压缩与 o 系列模型兼容性\n\n**[English →](v3.12.3-en.md) | [日本語版 →]"
},
{
"path": "docs/release-notes/v3.6.0-en.md",
"chars": 10077,
"preview": "## Major architecture refactoring with enhanced config sync and data protection\n\n**[中文更新说明 Chinese Documentation →](http"
},
{
"path": "docs/release-notes/v3.6.0-zh.md",
"chars": 5087,
"preview": "# CC Switch v3.6.0\n\n> 全栈架构重构,增强配置同步与数据保护\n\n**[English Version →](v3.6.0-en.md)**\n\n---\n\n## 新增功能\n\n### 编辑模式与供应商管理\n\n- **供应商复制"
},
{
"path": "docs/release-notes/v3.6.1-en.md",
"chars": 15604,
"preview": "# CC Switch v3.6.1\n\n> Stability improvements and user experience optimization (based on v3.6.0)\n\n**[中文更新说明 Chinese Docum"
},
{
"path": "docs/release-notes/v3.6.1-zh.md",
"chars": 7866,
"preview": "# CC Switch v3.6.1\n\n> 稳定性提升与用户体验优化(基于 v3.6.0)\n\n**[English Version →](v3.6.1-en.md)**\n\n---\n\n## 📦 v3.6.1 新增内容 (2025-11-10)"
},
{
"path": "docs/release-notes/v3.7.0-en.md",
"chars": 10838,
"preview": "# CC Switch v3.7.0\n\n> From Provider Switcher to All-in-One AI CLI Management Platform\n\n**[中文更新说明 Chinese Documentation →"
},
{
"path": "docs/release-notes/v3.7.0-zh.md",
"chars": 6256,
"preview": "# CC Switch v3.7.0\n\n> 从供应商切换器到 AI CLI 一体化管理平台\n\n**[English Version →](v3.7.0-en.md)**\n\n---\n\n## 概览\n\nCC Switch v3.7.0 新增六大核"
},
{
"path": "docs/release-notes/v3.7.1-en.md",
"chars": 12297,
"preview": "# CC Switch v3.7.1\n\n> Stability Enhancements and User Experience Improvements\n\n**[中文更新说明 Chinese Documentation →](v3.7.1"
},
{
"path": "docs/release-notes/v3.7.1-zh.md",
"chars": 7071,
"preview": "# CC Switch v3.7.1\n\n> 稳定性增强与用户体验改进\n\n**[English Version →](v3.7.1-en.md)**\n\n---\n\n## v3.7.1 更新内容\n\n**发布日期**:2025-11-22\n**代码"
},
{
"path": "docs/release-notes/v3.8.0-en.md",
"chars": 9701,
"preview": "# CC Switch v3.8.0\n\n> Persistence Architecture Upgrade, Laying the Foundation for Cloud Sync\n\n**[中文版 →](v3.8.0-zh.md) | "
},
{
"path": "docs/release-notes/v3.8.0-ja.md",
"chars": 6100,
"preview": "# CC Switch v3.8.0\n\n> 永続化アーキテクチャを刷新し、クラウド同期の土台を構築\n\n**[English →](v3.8.0-en.md) | [中文版 →](v3.8.0-zh.md)**\n\n---\n\n## 概要\n\nCC"
},
{
"path": "docs/release-notes/v3.8.0-zh.md",
"chars": 5933,
"preview": "# CC Switch v3.8.0\n\n> 持久化架构升级,为云同步奠定基础\n\n**[English Version →](v3.8.0-en.md)**\n\n---\n\n## 概览\n\nCC Switch v3.8.0 是一次重大的架构升级版本"
},
{
"path": "docs/release-notes/v3.9.0-en.md",
"chars": 9164,
"preview": "# CC Switch v3.9.0\n\n> Local API Proxy, Auto Failover, Universal Provider, and a more complete multi-app workflow\n\n**[中文版"
},
{
"path": "docs/release-notes/v3.9.0-ja.md",
"chars": 6233,
"preview": "# CC Switch v3.9.0\n\n> ローカル API プロキシ、自動フェイルオーバー、Universal Provider、多アプリ対応の強化\n\n**[English →](v3.9.0-en.md) | [中文版 →](v3.9."
},
{
"path": "docs/release-notes/v3.9.0-zh.md",
"chars": 5489,
"preview": "# CC Switch v3.9.0\n\n> 本地 API 代理、自动故障切换、统一供应商与多应用工作流增强\n\n**[English →](v3.9.0-en.md) | [日本語版 →](v3.9.0-ja.md)**\n\n---\n\n## 概"
},
{
"path": "docs/user-manual/README.md",
"chars": 557,
"preview": "# CC Switch User Manual / 用户手册 / ユーザーマニュアル\n\n> Claude Code / Codex / Gemini CLI / OpenCode / OpenClaw\n\n## Language / 语言 /"
},
{
"path": "docs/user-manual/en/1-getting-started/1.1-introduction.md",
"chars": 2651,
"preview": "# 1.1 Introduction\n\n## What is CC Switch\n\nCC Switch is a cross-platform desktop application designed for developers who "
},
{
"path": "docs/user-manual/en/1-getting-started/1.2-installation.md",
"chars": 4068,
"preview": "# 1.2 Installation Guide\n\n## Prerequisites\n\n### Install Node.js\n\nThe CLI tools managed by CC Switch (Claude Code, Codex,"
},
{
"path": "docs/user-manual/en/1-getting-started/1.3-interface.md",
"chars": 5580,
"preview": "# 1.3 Interface Overview\n\n## Main Interface Layout\n\n"
},
{
"path": "docs/user-manual/en/1-getting-started/1.4-quickstart.md",
"chars": 3239,
"preview": "# 1.4 Quick Start\n\nThis section helps you complete the initial setup in 5 minutes.\n\n## Step 1: Add a Provider\n\n1. Click "
},
{
"path": "docs/user-manual/en/1-getting-started/1.5-settings.md",
"chars": 7801,
"preview": "# 1.5 Personalization\n\nThis section describes how to configure CC Switch according to your preferences.\n\n## Open Setting"
},
{
"path": "docs/user-manual/en/2-providers/2.1-add.md",
"chars": 10507,
"preview": "# 2.1 Add Provider\n\n## Open the Add Panel\n\nClick the **+** button in the top-right corner of the main interface to open "
},
{
"path": "docs/user-manual/en/2-providers/2.2-switch.md",
"chars": 2510,
"preview": "# 2.2 Switch Provider\n\n## Switch from Main Interface\n\nIn the provider list, click the \"Enable\" button on the target prov"
},
{
"path": "docs/user-manual/en/2-providers/2.3-edit.md",
"chars": 3501,
"preview": "# 2.3 Edit Provider\n\n## Open the Edit Panel\n\n1. Find the provider card you want to edit\n2. Hover over the card to reveal"
},
{
"path": "docs/user-manual/en/2-providers/2.4-sort-duplicate.md",
"chars": 1994,
"preview": "# 2.4 Sort & Duplicate\n\n## Drag to Reorder\n\nAdjust the display order of providers by dragging.\n\n### Steps\n\n1. Move the m"
},
{
"path": "docs/user-manual/en/2-providers/2.5-usage-query.md",
"chars": 4811,
"preview": "# 2.5 Usage Query\n\n## Overview\n\nThe usage query feature allows you to configure custom scripts to query a provider's rem"
},
{
"path": "docs/user-manual/en/3-extensions/3.1-mcp.md",
"chars": 5767,
"preview": "# 3.1 MCP Server Management\n\n## What is MCP\n\nMCP (Model Context Protocol) is a protocol that allows AI tools to access e"
},
{
"path": "docs/user-manual/en/3-extensions/3.2-prompts.md",
"chars": 4100,
"preview": "# 3.2 Prompts Management\n\n## Overview\n\nThe Prompts feature manages system prompt presets. System prompts influence the A"
},
{
"path": "docs/user-manual/en/3-extensions/3.3-skills.md",
"chars": 4228,
"preview": "# 3.3 Skills Management\n\n## Overview\n\nSkills are reusable capability extensions that give AI tools specialized abilities"
},
{
"path": "docs/user-manual/en/4-proxy/4.1-service.md",
"chars": 4771,
"preview": "# 4.1 Proxy Service\n\n## Overview\n\nThe proxy service starts a local HTTP proxy through which all API requests are forward"
},
{
"path": "docs/user-manual/en/4-proxy/4.2-takeover.md",
"chars": 4627,
"preview": "# 4.2 App Takeover\n\n## Overview\n\nApp takeover means letting CC Switch's proxy intercept and forward a specific applicati"
},
{
"path": "docs/user-manual/en/4-proxy/4.3-failover.md",
"chars": 6538,
"preview": "# 4.3 Failover\n\n## Overview\n\nThe failover feature automatically switches to a backup provider when the primary provider'"
},
{
"path": "docs/user-manual/en/4-proxy/4.4-usage.md",
"chars": 8391,
"preview": "# 4.4 Usage Statistics\n\n## Overview\n\nThe usage statistics feature records and analyzes API request data, helping you:\n\n-"
},
{
"path": "docs/user-manual/en/4-proxy/4.5-model-test.md",
"chars": 4228,
"preview": "# 4.5 Model Test\n\n## Overview\n\nThe model test feature verifies whether a provider's configured model is available by sen"
},
{
"path": "docs/user-manual/en/5-faq/5.1-config-files.md",
"chars": 6696,
"preview": "# 5.1 Configuration Files\n\n## CC Switch Data Storage\n\n### Storage Directory\n\nDefault location: `~/.cc-switch/`\n\nCustomiz"
},
{
"path": "docs/user-manual/en/5-faq/5.2-questions.md",
"chars": 5269,
"preview": "# 5.2 Frequently Asked Questions\n\n## Installation Issues\n\n### macOS Shows \"Unidentified Developer\"\n\n**Problem**: First l"
},
{
"path": "docs/user-manual/en/5-faq/5.3-deeplink.md",
"chars": 6733,
"preview": "# 5.3 Deep Link Protocol\n\n## Overview\n\nCC Switch supports the `ccswitch://` deep link protocol, enabling one-click confi"
},
{
"path": "docs/user-manual/en/5-faq/5.4-env-conflict.md",
"chars": 3310,
"preview": "# 5.4 Environment Variable Conflicts\n\n## Overview\n\nCC Switch automatically detects conflicts between system environment "
},
{
"path": "docs/user-manual/en/README.md",
"chars": 4253,
"preview": "# CC Switch User Manual\n\n> All-in-One Assistant for Claude Code / Codex / Gemini CLI / OpenCode / OpenClaw\n\n## Table of "
},
{
"path": "docs/user-manual/ja/1-getting-started/1.1-introduction.md",
"chars": 1658,
"preview": "# 1.1 ソフトウェア紹介\n\n## CC Switch とは\n\nCC Switch はクロスプラットフォームのデスクトップアプリケーションで、AI プログラミングツールを使用する開発者向けに設計されています。**Claude Code**"
},
{
"path": "docs/user-manual/ja/1-getting-started/1.2-installation.md",
"chars": 3161,
"preview": "# 1.2 インストールガイド\n\n## 前提条件\n\n### Node.js のインストール\n\nCC Switch が管理する CLI ツール(Claude Code、Codex、Gemini CLI)には Node.js 環境が必要です。\n"
},
{
"path": "docs/user-manual/ja/1-getting-started/1.3-interface.md",
"chars": 3483,
"preview": "# 1.3 インターフェース概要\n\n## メイン画面のレイアウト\n\n\n\n## 上部ナビゲーションバー\n\n"
},
{
"path": "docs/user-manual/ja/1-getting-started/1.4-quickstart.md",
"chars": 1987,
"preview": "# 1.4 クイックスタート\n\nこのセクションでは、5 分で初回設定を完了する方法を説明します。\n\n## ステップ 1:プロバイダーの追加\n\n1. メイン画面右上の **+** ボタンをクリック\n2. 「プリセット」ドロップダウンからプロバ"
},
{
"path": "docs/user-manual/ja/1-getting-started/1.5-settings.md",
"chars": 5793,
"preview": "# 1.5 個人設定\n\nこのセクションでは、個人の好みに合わせて CC Switch を設定する方法を説明します。\n\n## 設定を開く\n\n- 左上の **⚙️** ボタンをクリック\n- またはショートカット `Cmd/Ctrl + ,`\n\n"
},
{
"path": "docs/user-manual/ja/2-providers/2.1-add.md",
"chars": 7577,
"preview": "# 2.1 プロバイダーの追加\n\n## 追加パネルを開く\n\nメイン画面右上の **+** ボタンをクリックして、プロバイダー追加パネルを開きます。\n\nパネルは 2 つのタブに分かれています:\n- **アプリ専用プロバイダー**:現在選択中の"
},
{
"path": "docs/user-manual/ja/2-providers/2.2-switch.md",
"chars": 1534,
"preview": "# 2.2 プロバイダーの切り替え\n\n## メイン画面での切り替え\n\nプロバイダーリストで、対象のプロバイダーカードの「有効化」ボタンをクリックします。\n\n### 切り替えフロー\n\n1. 「有効化」ボタンをクリック\n2. CC Switch"
},
{
"path": "docs/user-manual/ja/2-providers/2.3-edit.md",
"chars": 2180,
"preview": "# 2.3 プロバイダーの編集\n\n## 編集パネルを開く\n\n1. 編集したいプロバイダーカードを見つける\n2. カードにマウスをホバーして操作ボタンを表示\n3. 「編集」ボタンをクリック\n\n## 編集可能な内容\n\n### 基本情報\n\n| フ"
},
{
"path": "docs/user-manual/ja/2-providers/2.4-sort-duplicate.md",
"chars": 1123,
"preview": "# 2.4 並べ替えと複製\n\n## ドラッグで並べ替え\n\nドラッグでプロバイダーの表示順序を調整します。\n\n### 操作手順\n\n1. プロバイダーカード左側の **≡** ドラッグハンドルにマウスを合わせる\n2. マウスの左ボタンを押し続け"
},
{
"path": "docs/user-manual/ja/2-providers/2.5-usage-query.md",
"chars": 3294,
"preview": "# 2.5 使用量クエリ\n\n## 機能説明\n\n使用量クエリ機能により、カスタムスクリプトを設定して、プロバイダーの残額や使用量などの情報をリアルタイムでクエリできます。\n\n**使用シーン**:\n- API アカウントの残額確認\n- プランの"
},
{
"path": "docs/user-manual/ja/3-extensions/3.1-mcp.md",
"chars": 3921,
"preview": "# 3.1 MCP サーバー管理\n\n## MCP とは\n\nMCP (Model Context Protocol) は、AI ツールが外部データソースやツールにアクセスできるようにするプロトコルです。MCP サーバーにより、AI は以下のこ"
},
{
"path": "docs/user-manual/ja/3-extensions/3.2-prompts.md",
"chars": 2418,
"preview": "# 3.2 Prompts プロンプト管理\n\n## 機能説明\n\nPrompts 機能は、システムプロンプトのプリセットを管理します。システムプロンプトは AI の動作や回答スタイルに影響します。\n\nCC Switch を使用すると:\n\n- "
},
{
"path": "docs/user-manual/ja/3-extensions/3.3-skills.md",
"chars": 2964,
"preview": "# 3.3 Skills スキル管理\n\n## 機能説明\n\nSkills は再利用可能な機能拡張で、AI ツールに特定分野の専門的な能力を与えます。\n\nスキルはフォルダ形式で存在し、以下を含みます:\n\n- プロンプトテンプレート\n- ツール定"
},
{
"path": "docs/user-manual/ja/4-proxy/4.1-service.md",
"chars": 3124,
"preview": "# 4.1 プロキシサービス\n\n## 機能説明\n\nプロキシサービスは、ローカルで HTTP プロキシを起動し、すべての API リクエストをプロキシ経由で転送します。\n\n**主な用途**:\n- リクエストログの記録\n- API 使用量の統計"
},
{
"path": "docs/user-manual/ja/4-proxy/4.2-takeover.md",
"chars": 2658,
"preview": "# 4.2 アプリケーション接管\n\n## 機能説明\n\nアプリケーション接管とは、CC Switch のプロキシが特定アプリの API リクエストを接管することです。\n\n接管を有効にすると:\n- アプリの API リクエストがローカルプロキシ"
},
{
"path": "docs/user-manual/ja/4-proxy/4.3-failover.md",
"chars": 4003,
"preview": "# 4.3 フェイルオーバー\n\n## 機能説明\n\nフェイルオーバー機能は、メインプロバイダーのリクエストが失敗した場合に、自動的にバックアッププロバイダーに切り替えてサービスの中断を防ぎます。\n\n**適用シーン**:\n- プロバイダーのサー"
},
{
"path": "docs/user-manual/ja/4-proxy/4.4-usage.md",
"chars": 5756,
"preview": "# 4.4 使用量統計\n\n## 機能説明\n\n使用量統計機能は、API リクエストデータを記録・分析して、以下をサポートします:\n\n- API の使用状況の把握\n- 費用支出の見積もり\n- 使用パターンの分析\n- 問題のトラブルシューティング"
},
{
"path": "docs/user-manual/ja/4-proxy/4.5-model-test.md",
"chars": 2413,
"preview": "# 4.5 モデルテスト\n\n## 機能説明\n\nモデルテスト機能は、プロバイダーに設定されたモデルが使用可能かどうかを確認するために、実際の API リクエストを送信してテストします:\n\n- モデルが存在するか\n- API Key が有効か\n"
},
{
"path": "docs/user-manual/ja/5-faq/5.1-config-files.md",
"chars": 4975,
"preview": "# 5.1 設定ファイルの説明\n\n## CC Switch のデータストレージ\n\n### ストレージディレクトリ\n\nデフォルトの場所:`~/.cc-switch/`\n\n設定で場所をカスタマイズ可能です(クラウド同期用)。\n\n### ディレク"
},
{
"path": "docs/user-manual/ja/5-faq/5.2-questions.md",
"chars": 3380,
"preview": "# 5.2 よくある質問 FAQ\n\n## インストールに関する問題\n\n### macOS で「不明な開発者」と表示される\n\n**問題**:初回起動時に「開けません。身元不明の開発者のものです」と表示される\n\n**解決方法 1**:システム設"
},
{
"path": "docs/user-manual/ja/5-faq/5.3-deeplink.md",
"chars": 5067,
"preview": "# 5.3 ディープリンクプロトコル\n\n## 機能説明\n\nCC Switch は `ccswitch://` ディープリンクプロトコルをサポートしており、リンクからワンクリックで設定をインポートできます。\n\n**使用シーン**:\n- チーム"
},
{
"path": "docs/user-manual/ja/5-faq/5.4-env-conflict.md",
"chars": 1725,
"preview": "# 5.4 環境変数の競合\n\n## 機能説明\n\nCC Switch は、システム環境変数とアプリ設定の競合を自動的に検出し、設定が意図せず上書きされるのを防ぎます。\n\n**検出される環境変数**:\n- `ANTHROPIC_API_KEY`"
},
{
"path": "docs/user-manual/ja/README.md",
"chars": 3293,
"preview": "# CC Switch ユーザーマニュアル\n\n> Claude Code / Codex / Gemini CLI / OpenCode / OpenClaw オールインワンアシスタント\n\n## 目次構成\n\n```\nCC Switch ユー"
},
{
"path": "docs/user-manual/zh/1-getting-started/1.1-introduction.md",
"chars": 1251,
"preview": "# 1.1 软件介绍\n\n## 什么是 CC Switch\n\nCC Switch 是一款跨平台桌面应用,专为使用 AI 编程工具的开发者设计。它帮助你统一管理 **Claude Code**、**Codex**、**Gemini CLI**、"
},
{
"path": "docs/user-manual/zh/1-getting-started/1.2-installation.md",
"chars": 3162,
"preview": "# 1.2 安装指南\n\n## 前置要求\n\n### 安装 Node.js\n\nCC Switch 管理的 CLI 工具(Claude Code、Codex、Gemini CLI)需要 Node.js 环境。\n\n**推荐版本**:Node.js "
},
{
"path": "docs/user-manual/zh/1-getting-started/1.3-interface.md",
"chars": 2828,
"preview": "# 1.3 界面概览\n\n## 主界面布局\n\n\n\n## 顶部导航栏\n\n| 序号 | 元素 | 功能说明 |"
},
{
"path": "docs/user-manual/zh/1-getting-started/1.4-quickstart.md",
"chars": 1586,
"preview": "# 1.4 快速上手\n\n本节帮助你在 5 分钟内完成首次配置。\n\n## 第一步:添加供应商\n\n1. 点击主界面右上角的 **+** 按钮\n2. 在「预设」下拉框中选择你的供应商\n - 常用预设:智谱 GLM、MiniMax、DeepSe"
},
{
"path": "docs/user-manual/zh/1-getting-started/1.5-settings.md",
"chars": 4733,
"preview": "# 1.5 个性化配置\n\n本节介绍如何根据个人偏好配置 CC Switch。\n\n## 打开设置\n\n- 点击左上角 **⚙️** 按钮\n- 或使用快捷键 `Cmd/Ctrl + ,`\n\n## 语言设置\n\nCC Switch 支持三种语言:\n\n"
},
{
"path": "docs/user-manual/zh/2-providers/2.1-add.md",
"chars": 6572,
"preview": "# 2.1 添加供应商\n\n## 打开添加面板\n\n点击主界面右上角的 **+** 按钮,打开添加供应商面板。\n\n面板分为两个 Tab:\n- **应用专属供应商**:仅用于当前选中的应用(Claude/Codex/Gemini/OpenCode"
},
{
"path": "docs/user-manual/zh/2-providers/2.2-switch.md",
"chars": 1220,
"preview": "# 2.2 切换供应商\n\n## 主界面切换\n\n在供应商列表中,点击目标供应商卡片的「启用」按钮。\n\n### 切换流程\n\n1. 点击「启用」按钮\n2. CC Switch 更新配置文件\n3. 卡片状态变为「当前启用」\n4. Claude/Ge"
},
{
"path": "docs/user-manual/zh/2-providers/2.3-edit.md",
"chars": 1678,
"preview": "# 2.3 编辑供应商\n\n## 打开编辑面板\n\n1. 找到要编辑的供应商卡片\n2. 鼠标悬停在卡片上,显示操作按钮\n3. 点击「编辑」按钮\n\n## 可编辑内容\n\n### 基本信息\n\n| 字段 | 说明 |\n|------|------|\n|"
},
{
"path": "docs/user-manual/zh/2-providers/2.4-sort-duplicate.md",
"chars": 847,
"preview": "# 2.4 排序与复制\n\n## 拖拽排序\n\n通过拖拽调整供应商的显示顺序。\n\n### 操作步骤\n\n1. 将鼠标移到供应商卡片左侧的 **≡** 拖拽手柄\n2. 按住鼠标左键\n3. 上下拖动到目标位置\n4. 松开鼠标完成排序\n\n### 排序用"
},
{
"path": "docs/user-manual/zh/2-providers/2.5-usage-query.md",
"chars": 2820,
"preview": "# 2.5 用量查询\n\n## 功能说明\n\n用量查询功能允许你配置自定义脚本,实时查询供应商的剩余额度、已用量等信息。\n\n**使用场景**:\n- 查看 API 账户剩余余额\n- 监控套餐使用情况\n- 多套餐额度汇总显示\n\n## 打开配置\n\n1"
},
{
"path": "docs/user-manual/zh/3-extensions/3.1-mcp.md",
"chars": 3314,
"preview": "# 3.1 MCP 服务器管理\n\n## 什么是 MCP\n\nMCP (Model Context Protocol) 是一种协议,允许 AI 工具访问外部数据源和工具。通过 MCP 服务器,你可以让 AI:\n\n- 访问文件系统\n- 执行网络请"
},
{
"path": "docs/user-manual/zh/3-extensions/3.2-prompts.md",
"chars": 1822,
"preview": "# 3.2 Prompts 提示词管理\n\n## 功能说明\n\nPrompts 功能用于管理系统提示词预设。系统提示词会影响 AI 的行为和回复风格。\n\n通过 CC Switch,你可以:\n\n- 创建多个提示词预设\n- 快速切换不同场景的提示词"
},
{
"path": "docs/user-manual/zh/3-extensions/3.3-skills.md",
"chars": 2380,
"preview": "# 3.3 Skills 技能管理\n\n## 功能说明\n\nSkills 是可复用的能力扩展,让 AI 工具获得特定领域的专业能力。\n\n技能以文件夹形式存在,包含:\n\n- 提示词模板\n- 工具定义\n- 示例代码\n\n## 支持的应用\n\nSkill"
},
{
"path": "docs/user-manual/zh/4-proxy/4.1-service.md",
"chars": 2586,
"preview": "# 4.1 代理服务\n\n## 功能说明\n\n代理服务在本地启动一个 HTTP 代理,所有 API 请求都通过代理转发。\n\n**主要用途**:\n- 记录请求日志\n- 统计 API 用量\n- 支持故障转移\n- 集中管理多个应用的请求\n\n## 启动"
},
{
"path": "docs/user-manual/zh/4-proxy/4.2-takeover.md",
"chars": 2028,
"preview": "# 4.2 应用接管\n\n## 功能说明\n\n应用接管是指让 CC Switch 代理接管特定应用的 API 请求。\n\n开启接管后:\n- 应用的 API 请求会通过本地代理转发\n- 可以记录请求日志和统计用量\n- 可以使用故障转移功能\n\n## "
},
{
"path": "docs/user-manual/zh/4-proxy/4.3-failover.md",
"chars": 3066,
"preview": "# 4.3 故障转移\n\n## 功能说明\n\n故障转移功能在主供应商请求失败时,自动切换到备用供应商,确保服务不中断。\n\n**适用场景**:\n- 供应商服务不稳定\n- 需要高可用性\n- 长时间运行的任务\n\n## 前提条件\n\n使用故障转移功能需要"
},
{
"path": "docs/user-manual/zh/4-proxy/4.4-usage.md",
"chars": 5076,
"preview": "# 4.4 用量统计\n\n## 功能说明\n\n用量统计功能记录和分析 API 请求数据,帮助你:\n\n- 了解 API 使用情况\n- 估算费用支出\n- 分析使用模式\n- 排查问题\n\n## 前提条件\n\n使用用量统计功能需要:\n\n1. ✅ 启动代理服"
},
{
"path": "docs/user-manual/zh/4-proxy/4.5-model-test.md",
"chars": 1793,
"preview": "# 4.5 模型检查\n\n## 功能说明\n\n模型检查功能用于验证供应商配置的模型是否可用,通过发送实际的 API 请求来测试:\n\n- 模型是否存在\n- API Key 是否有效\n- 端点是否正常响应\n- 响应延迟是否正常\n\n## 打开配置\n\n"
},
{
"path": "docs/user-manual/zh/5-faq/5.1-config-files.md",
"chars": 4398,
"preview": "# 5.1 配置文件说明\n\n## CC Switch 数据存储\n\n### 存储目录\n\n默认位置:`~/.cc-switch/`\n\n可在设置中自定义位置(用于云同步)。\n\n### 目录结构\n\n```\n~/.cc-switch/\n├── cc-"
},
{
"path": "docs/user-manual/zh/5-faq/5.2-questions.md",
"chars": 2644,
"preview": "# 5.2 常见问题 FAQ\n\n## 安装问题\n\n### macOS 提示「未知开发者」\n\n**问题**:首次打开时提示「无法打开,因为它来自身份不明的开发者」\n\n**解决方法一**:通过系统设置\n1. 关闭警告弹窗\n2. 打开「系统设置」"
},
{
"path": "docs/user-manual/zh/5-faq/5.3-deeplink.md",
"chars": 4407,
"preview": "# 5.3 深度链接协议\n\n## 功能说明\n\nCC Switch 支持 `ccswitch://` 深度链接协议,可以通过链接一键导入配置。\n\n**使用场景**:\n- 团队共享配置\n- 教程中的一键配置\n- 跨设备快速同步\n\n## 在线生成"
},
{
"path": "docs/user-manual/zh/5-faq/5.4-env-conflict.md",
"chars": 1384,
"preview": "# 5.4 环境变量冲突\n\n## 功能说明\n\nCC Switch 会自动检测系统环境变量与应用配置的冲突,避免配置被意外覆盖。\n\n**检测的环境变量**:\n- `ANTHROPIC_API_KEY` - Claude API 密钥\n- `A"
},
{
"path": "docs/user-manual/zh/README.md",
"chars": 2906,
"preview": "# CC Switch 用户手册\n\n> Claude Code / Codex / Gemini CLI / OpenCode / OpenClaw 全方位辅助工具\n\n## 目录结构\n\n```\n📚 CC Switch 用户手册\n│\n├── "
},
{
"path": "flatpak/README.md",
"chars": 2432,
"preview": "# Flatpak Build Guide\n\nThis directory contains the Flatpak manifest (`com.ccswitch.desktop`) for CC Switch, used to conv"
},
{
"path": "flatpak/com.ccswitch.desktop.desktop",
"chars": 220,
"preview": "[Desktop Entry]\nType=Application\nName=CC Switch\nComment=All-in-One Assistant for Claude Code, Codex & Gemini CLI\nExec=cc"
},
{
"path": "flatpak/com.ccswitch.desktop.metainfo.xml",
"chars": 992,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<component type=\"desktop-application\">\n <id>com.ccswitch.desktop</id>\n <name>CC"
},
{
"path": "flatpak/com.ccswitch.desktop.yml",
"chars": 3255,
"preview": "id: com.ccswitch.desktop\n\nruntime: org.gnome.Platform\nruntime-version: '46'\nsdk: org.gnome.Sdk\n\ncommand: cc-switch\n\nfini"
},
{
"path": "package.json",
"chars": 3096,
"preview": "{\n \"name\": \"cc-switch\",\n \"version\": \"3.12.3\",\n \"description\": \"All-in-One Assistant for Claude Code, Codex & Gemini C"
},
{
"path": "pnpm-workspace.yaml",
"chars": 62,
"preview": "packages: []\n\nonlyBuiltDependencies:\n - '@tailwindcss/oxide'\n"
},
{
"path": "postcss.config.cjs",
"chars": 84,
"preview": "module.exports = {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n};\n\n"
},
{
"path": "scripts/extract-icons.js",
"chars": 9055,
"preview": "const fs = require('fs');\nconst path = require('path');\n\n// 要提取的图标列表(按分类组织)\nconst ICONS_TO_EXTRACT = {\n // AI 服务商(必需)\n "
},
{
"path": "scripts/filter-icons.js",
"chars": 3086,
"preview": "const fs = require('fs');\nconst path = require('path');\n\nconst ICONS_DIR = path.join(__dirname, '../src/icons/extracted'"
},
{
"path": "scripts/generate-icon-index.js",
"chars": 6896,
"preview": "const fs = require('fs');\nconst path = require('path');\n\nconst ICONS_DIR = path.join(__dirname, '../src/icons/extracted'"
},
{
"path": "session-manager.md",
"chars": 5205,
"preview": "# 会话管理(Session Manager)需求文档(PRD / Markdown)\n\n> 目标:对 **Codex / Claude Code** 的本地会话记录进行可视化管理,并提供“一键复制 / 一键终端恢复”能力。\n> 范围:**"
},
{
"path": "src/App.tsx",
"chars": 45621,
"preview": "import { useEffect, useMemo, useState, useRef } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { m"
},
{
"path": "src/components/AppSwitcher.tsx",
"chars": 2287,
"preview": "import type { AppId } from \"@/lib/api\";\nimport type { VisibleApps } from \"@/types\";\nimport { ProviderIcon } from \"@/comp"
},
{
"path": "src/components/BrandIcons.tsx",
"chars": 2442,
"preview": "interface IconProps {\n size?: number;\n className?: string;\n}\n\n// 导入本地 SVG 图标\nimport ClaudeSvg from \"@/icons/extracted/"
},
{
"path": "src/components/ColorPicker.tsx",
"chars": 1944,
"preview": "import React from \"react\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";"
},
{
"path": "src/components/ConfirmDialog.tsx",
"chars": 2049,
"preview": "import {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@/compon"
},
{
"path": "src/components/DeepLinkImportDialog.tsx",
"chars": 29868,
"preview": "import { useState, useEffect, useMemo } from \"react\";\nimport { listen } from \"@tauri-apps/api/event\";\nimport { DeepLinkI"
},
{
"path": "src/components/IconPicker.tsx",
"chars": 2767,
"preview": "import React, { useState, useMemo } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Input } from "
},
{
"path": "src/components/JsonEditor.tsx",
"chars": 7886,
"preview": "import React, { useRef, useEffect, useMemo } from \"react\";\nimport { EditorView, basicSetup } from \"codemirror\";\nimport {"
},
{
"path": "src/components/MarkdownEditor.tsx",
"chars": 4056,
"preview": "import React, { useRef, useEffect } from \"react\";\nimport { EditorView, basicSetup } from \"codemirror\";\nimport { markdown"
},
{
"path": "src/components/ProviderIcon.tsx",
"chars": 2566,
"preview": "import React, { useMemo } from \"react\";\nimport { getIcon, hasIcon, getIconMetadata } from \"@/icons/extracted\";\nimport { "
},
{
"path": "src/components/UpdateBadge.tsx",
"chars": 1142,
"preview": "import { useUpdate } from \"@/contexts/UpdateContext\";\nimport { useTranslation } from \"react-i18next\";\nimport { Button } "
},
{
"path": "src/components/UsageFooter.tsx",
"chars": 11330,
"preview": "import React from \"react\";\nimport { RefreshCw, AlertCircle, Clock } from \"lucide-react\";\nimport { useTranslation } from "
},
{
"path": "src/components/UsageScriptModal.tsx",
"chars": 33509,
"preview": "import React, { useState } from \"react\";\nimport { Play, Wand2, Eye, EyeOff, Save } from \"lucide-react\";\nimport { toast }"
},
{
"path": "src/components/agents/AgentsPanel.tsx",
"chars": 837,
"preview": "import { Bot } from \"lucide-react\";\n\ninterface AgentsPanelProps {\n onOpenChange: (open: boolean) => void;\n}\n\nexport fun"
},
{
"path": "src/components/common/AppCountBar.tsx",
"chars": 1094,
"preview": "import React from \"react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport type { AppId } from \"@/lib/api/types\";\n"
},
{
"path": "src/components/common/AppToggleGroup.tsx",
"chars": 1384,
"preview": "import React from \"react\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimp"
},
{
"path": "src/components/common/FullScreenPanel.tsx",
"chars": 4330,
"preview": "import React from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { motion, AnimatePresence } from \"framer-mot"
},
{
"path": "src/components/common/ListItemRow.tsx",
"chars": 424,
"preview": "import React from \"react\";\n\ninterface ListItemRowProps {\n isLast?: boolean;\n children: React.ReactNode;\n}\n\nexport cons"
},
{
"path": "src/components/deeplink/McpConfirmation.tsx",
"chars": 2435,
"preview": "import { useMemo } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { DeepLinkImportRequest } from \""
},
{
"path": "src/components/deeplink/PromptConfirmation.tsx",
"chars": 1969,
"preview": "import { useMemo } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { DeepLinkImportRequest } from \""
},
{
"path": "src/components/deeplink/SkillConfirmation.tsx",
"chars": 1444,
"preview": "import { useTranslation } from \"react-i18next\";\nimport { DeepLinkImportRequest } from \"../../lib/api/deeplink\";\n\nexport "
},
{
"path": "src/components/env/EnvWarningBanner.tsx",
"chars": 9954,
"preview": "import { useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { AlertTriangle, ChevronDown, C"
},
{
"path": "src/components/icons/TerminalIcons.tsx",
"chars": 10233,
"preview": "import { SVGProps } from \"react\";\n\nexport function ITermIcon(props: SVGProps<SVGSVGElement>) {\n return (\n <svg\n "
},
{
"path": "src/components/mcp/McpFormModal.tsx",
"chars": 23160,
"preview": "import React, { useMemo, useState, useEffect } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { to"
},
{
"path": "src/components/mcp/McpWizardModal.tsx",
"chars": 14032,
"preview": "import React, { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { toast } fro"
},
{
"path": "src/components/mcp/UnifiedMcpPanel.tsx",
"chars": 9207,
"preview": "import React, { useMemo, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Server } from"
},
{
"path": "src/components/mcp/useMcpValidation.ts",
"chars": 2709,
"preview": "import { useTranslation } from \"react-i18next\";\nimport { validateToml, tomlToMcpServer } from \"@/utils/tomlUtils\";\n\nexpo"
},
{
"path": "src/components/mode-toggle.tsx",
"chars": 901,
"preview": "import { Moon, Sun } from \"lucide-react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Button } from \"@/comp"
},
{
"path": "src/components/openclaw/AgentsDefaultsPanel.tsx",
"chars": 13789,
"preview": "import React, { useState, useEffect, useMemo } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Sa"
},
{
"path": "src/components/openclaw/EnvPanel.tsx",
"chars": 3556,
"preview": "import React, { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Save } from"
},
{
"path": "src/components/openclaw/OpenClawHealthBanner.tsx",
"chars": 2752,
"preview": "import React, { useMemo } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { TriangleAlert } from \"l"
},
{
"path": "src/components/openclaw/ToolsPanel.tsx",
"chars": 9000,
"preview": "import React, { useEffect, useMemo, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Pl"
},
{
"path": "src/components/openclaw/hooks/useOpenClawModelOptions.ts",
"chars": 1934,
"preview": "import { useMemo } from \"react\";\nimport { useProvidersQuery } from \"@/lib/query/queries\";\nimport type { OpenClawProvider"
},
{
"path": "src/components/openclaw/utils.ts",
"chars": 1851,
"preview": "import type {\n OpenClawAgentsDefaults,\n OpenClawEnvConfig,\n OpenClawToolsProfile,\n} from \"@/types\";\n\nexport const OPE"
},
{
"path": "src/components/prompts/PromptFormModal.tsx",
"chars": 4573,
"preview": "import React, { useState, useEffect } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Button } fr"
},
{
"path": "src/components/prompts/PromptFormPanel.tsx",
"chars": 4390,
"preview": "import React, { useState, useEffect } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Button } fr"
},
{
"path": "src/components/prompts/PromptListItem.tsx",
"chars": 2132,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Edit3, Trash2 } from \"lucide-react\";"
},
{
"path": "src/components/prompts/PromptPanel.tsx",
"chars": 5150,
"preview": "import React, { useEffect, useMemo, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Fi"
},
{
"path": "src/components/prompts/PromptToggle.tsx",
"chars": 1037,
"preview": "import React from \"react\";\n\ninterface PromptToggleProps {\n enabled: boolean;\n onChange: (enabled: boolean) => void;\n "
},
{
"path": "src/components/providers/AddProviderDialog.tsx",
"chars": 11749,
"preview": "import { useCallback, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Plus } from \"luc"
},
{
"path": "src/components/providers/EditProviderDialog.tsx",
"chars": 6451,
"preview": "import { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimpor"
},
{
"path": "src/components/providers/FailoverPriorityBadge.tsx",
"chars": 738,
"preview": "import { cn } from \"@/lib/utils\";\nimport { useTranslation } from \"react-i18next\";\n\ninterface FailoverPriorityBadgeProps "
},
{
"path": "src/components/providers/HealthStatusIndicator.tsx",
"chars": 1389,
"preview": "import React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport type { HealthStatus } from \"@/lib/api/model-test\";\ni"
},
{
"path": "src/components/providers/ProviderActions.tsx",
"chars": 8503,
"preview": "import {\n BarChart3,\n Check,\n Copy,\n Edit,\n Loader2,\n Minus,\n Play,\n Plus,\n Terminal,\n TestTube2,\n Trash2,\n "
},
{
"path": "src/components/providers/ProviderCard.tsx",
"chars": 14981,
"preview": "import { useMemo, useState, useEffect, useRef } from \"react\";\nimport { GripVertical, ChevronDown, ChevronUp } from \"luci"
},
{
"path": "src/components/providers/ProviderEmptyState.tsx",
"chars": 1644,
"preview": "import { Download, Users } from \"lucide-react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Button } from \""
},
{
"path": "src/components/providers/ProviderHealthBadge.tsx",
"chars": 2001,
"preview": "import { cn } from \"@/lib/utils\";\nimport { ProviderHealthStatus } from \"@/types/proxy\";\nimport { useTranslation } from \""
},
{
"path": "src/components/providers/ProviderList.tsx",
"chars": 18825,
"preview": "import { CSS } from \"@dnd-kit/utilities\";\nimport { DndContext, closestCenter } from \"@dnd-kit/core\";\nimport {\n Sortable"
},
{
"path": "src/components/providers/forms/ApiKeyInput.tsx",
"chars": 2044,
"preview": "import React, { useState } from \"react\";\nimport { Eye, EyeOff } from \"lucide-react\";\nimport { useTranslation } from \"rea"
},
{
"path": "src/components/providers/forms/BasicFormFields.tsx",
"chars": 5765,
"preview": "import { useTranslation } from \"react-i18next\";\nimport { useState } from \"react\";\nimport type { ReactNode } from \"react\""
},
{
"path": "src/components/providers/forms/ClaudeFormFields.tsx",
"chars": 18394,
"preview": "import { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Button } from \"@/c"
},
{
"path": "src/components/providers/forms/CodexCommonConfigModal.tsx",
"chars": 3279,
"preview": "import React, { useEffect, useState } from \"react\";\nimport { Save, Download, Loader2 } from \"lucide-react\";\nimport { use"
},
{
"path": "src/components/providers/forms/CodexConfigEditor.tsx",
"chars": 2296,
"preview": "import React, { useState } from \"react\";\nimport { CodexAuthSection, CodexConfigSection } from \"./CodexConfigSections\";\ni"
},
{
"path": "src/components/providers/forms/CodexConfigSections.tsx",
"chars": 8450,
"preview": "import React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useTranslation } from \"react-"
},
{
"path": "src/components/providers/forms/CodexFormFields.tsx",
"chars": 4429,
"preview": "import { useTranslation } from \"react-i18next\";\nimport EndpointSpeedTest from \"./EndpointSpeedTest\";\nimport { ApiKeySect"
},
{
"path": "src/components/providers/forms/CommonConfigEditor.tsx",
"chars": 10405,
"preview": "import { useTranslation } from \"react-i18next\";\nimport { useEffect, useState, useCallback, useMemo } from \"react\";\nimpor"
},
{
"path": "src/components/providers/forms/CopilotAuthSection.tsx",
"chars": 10927,
"preview": "import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Button } from \"@/components/ui/butto"
},
{
"path": "src/components/providers/forms/EndpointSpeedTest.tsx",
"chars": 19165,
"preview": "import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\""
},
{
"path": "src/components/providers/forms/GeminiCommonConfigModal.tsx",
"chars": 3426,
"preview": "import React, { useEffect, useState } from \"react\";\nimport { Save, Download, Loader2 } from \"lucide-react\";\nimport { use"
},
{
"path": "src/components/providers/forms/GeminiConfigEditor.tsx",
"chars": 2254,
"preview": "import React, { useState } from \"react\";\nimport { GeminiEnvSection, GeminiConfigSection } from \"./GeminiConfigSections\";"
},
{
"path": "src/components/providers/forms/GeminiConfigSections.tsx",
"chars": 5029,
"preview": "import React, { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport JsonEditor fr"
},
{
"path": "src/components/providers/forms/GeminiFormFields.tsx",
"chars": 4545,
"preview": "import { useTranslation } from \"react-i18next\";\nimport { FormLabel } from \"@/components/ui/form\";\nimport { Input } from "
},
{
"path": "src/components/providers/forms/OmoFormFields.tsx",
"chars": 38703,
"preview": "import { useState, useCallback, useEffect } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Input"
},
{
"path": "src/components/providers/forms/OpenClawFormFields.tsx",
"chars": 20254,
"preview": "import { useTranslation } from \"react-i18next\";\nimport { useState, useRef, useCallback } from \"react\";\nimport { FormLabe"
},
{
"path": "src/components/providers/forms/OpenCodeFormFields.tsx",
"chars": 26682,
"preview": "import { useState, useEffect } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { FormLabel } from \""
},
{
"path": "src/components/providers/forms/ProviderAdvancedConfig.tsx",
"chars": 21408,
"preview": "import { useTranslation } from \"react-i18next\";\nimport { useState, useEffect } from \"react\";\nimport {\n ChevronDown,\n C"
},
{
"path": "src/components/providers/forms/ProviderForm.tsx",
"chars": 55824,
"preview": "import { useEffect, useMemo, useState, useCallback } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { z"
},
{
"path": "src/components/providers/forms/ProviderPresetSelector.tsx",
"chars": 7838,
"preview": "import { useTranslation } from \"react-i18next\";\nimport { FormLabel } from \"@/components/ui/form\";\nimport { ClaudeIcon, C"
},
{
"path": "src/components/providers/forms/helpers/opencodeFormUtils.ts",
"chars": 3955,
"preview": "import type { OpenCodeModel, OpenCodeProviderConfig } from \"@/types\";\nimport type { PricingModelSourceOption } from \"../"
},
{
"path": "src/components/providers/forms/hooks/index.ts",
"chars": 1170,
"preview": "export { useProviderCategory } from \"./useProviderCategory\";\nexport { useApiKeyState } from \"./useApiKeyState\";\nexport {"
},
{
"path": "src/components/providers/forms/hooks/useApiKeyLink.ts",
"chars": 2542,
"preview": "import { useMemo } from \"react\";\nimport type { AppId } from \"@/lib/api\";\nimport type { ProviderCategory } from \"@/types\""
},
{
"path": "src/components/providers/forms/hooks/useApiKeyState.ts",
"chars": 2390,
"preview": "import { useEffect, useState, useCallback } from \"react\";\nimport type { ProviderCategory } from \"@/types\";\nimport {\n ge"
}
]
// ... and 406 more files (download for full content)
About this extraction
This page contains the full source code of the farion1231/cc-switch GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 606 files (5.0 MB), approximately 1.3M tokens, and a symbol index with 3512 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.