├── src
├── vite-env.d.ts
├── components
│ ├── mcp
│ │ └── index.ts
│ ├── extensions
│ │ ├── index.ts
│ │ └── ExtensionsPage.tsx
│ ├── websocket
│ │ └── index.ts
│ ├── plugins
│ │ └── index.ts
│ ├── prompts
│ │ └── index.ts
│ ├── skills
│ │ └── index.ts
│ ├── resilience
│ │ ├── index.ts
│ │ └── ResiliencePage.tsx
│ ├── monitoring
│ │ └── index.ts
│ ├── switch
│ │ ├── index.ts
│ │ ├── SwitchPage.tsx
│ │ └── AppTabs.tsx
│ ├── clients
│ │ ├── index.ts
│ │ ├── ClientsPage.tsx
│ │ └── AppTabs.tsx
│ ├── config
│ │ ├── index.ts
│ │ └── ConfigManagementPage.tsx
│ ├── routing
│ │ ├── index.ts
│ │ └── RoutingManagementPage.tsx
│ ├── provider-pool
│ │ ├── credential-forms
│ │ │ ├── index.ts
│ │ │ ├── ModeSelector.tsx
│ │ │ ├── types.ts
│ │ │ ├── FileImportForm.tsx
│ │ │ └── OAuthUrlDisplay.tsx
│ │ └── index.ts
│ ├── flow-monitor
│ │ └── index.ts
│ ├── settings
│ │ ├── index.ts
│ │ ├── SettingsPage.tsx
│ │ └── GeneralSettings.test.ts
│ ├── ui
│ │ └── sonner.tsx
│ ├── HelpTip.tsx
│ ├── Sidebar.tsx
│ └── ConfirmDialog.tsx
├── hooks
│ ├── index.ts
│ ├── useErrorHandler.ts
│ ├── useFileMonitoring.ts
│ ├── useSkills.ts
│ ├── useSwitch.ts
│ └── useProviderState.ts
├── pages
│ └── index.ts
├── main.tsx
├── icons
│ ├── providers
│ │ ├── anthropic.svg
│ │ ├── openai.svg
│ │ ├── index.tsx
│ │ └── gemini.svg
│ ├── app-icon-compact.svg
│ └── app-icon.svg
├── lib
│ ├── utils.ts
│ └── api
│ │ ├── usage.ts
│ │ ├── routes.ts
│ │ ├── mcp.ts
│ │ ├── resilience.ts
│ │ ├── skills.ts
│ │ ├── switch.ts
│ │ ├── injection.ts
│ │ ├── prompts.ts
│ │ └── credentials.ts
├── index.css
└── App.tsx
├── src-tauri
├── build.rs
├── icons
│ ├── icon.ico
│ ├── icon.png
│ ├── 128x128.png
│ ├── 32x32.png
│ ├── 64x64.png
│ ├── icon.icns
│ ├── StoreLogo.png
│ ├── 128x128@2x.png
│ ├── Square107x107Logo.png
│ ├── Square142x142Logo.png
│ ├── Square150x150Logo.png
│ ├── Square284x284Logo.png
│ ├── Square30x30Logo.png
│ ├── Square310x310Logo.png
│ ├── Square44x44Logo.png
│ ├── Square71x71Logo.png
│ ├── Square89x89Logo.png
│ ├── tray
│ │ ├── tray-error.png
│ │ ├── tray-running.png
│ │ ├── tray-stopped.png
│ │ ├── tray-warning.png
│ │ ├── trayTemplate.png
│ │ └── trayTemplate@2x.png
│ ├── ios
│ │ ├── AppIcon-512@2x.png
│ │ ├── AppIcon-20x20@1x.png
│ │ ├── AppIcon-20x20@2x-1.png
│ │ ├── AppIcon-20x20@2x.png
│ │ ├── AppIcon-20x20@3x.png
│ │ ├── AppIcon-29x29@1x.png
│ │ ├── AppIcon-29x29@2x-1.png
│ │ ├── AppIcon-29x29@2x.png
│ │ ├── AppIcon-29x29@3x.png
│ │ ├── AppIcon-40x40@1x.png
│ │ ├── AppIcon-40x40@2x-1.png
│ │ ├── AppIcon-40x40@2x.png
│ │ ├── AppIcon-40x40@3x.png
│ │ ├── AppIcon-60x60@2x.png
│ │ ├── AppIcon-60x60@3x.png
│ │ ├── AppIcon-76x76@1x.png
│ │ ├── AppIcon-76x76@2x.png
│ │ └── AppIcon-83.5x83.5@2x.png
│ └── android
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ └── ic_launcher_foreground.png
│ │ ├── values
│ │ └── ic_launcher_background.xml
│ │ └── mipmap-anydpi-v26
│ │ └── ic_launcher.xml
├── src
│ ├── database
│ │ ├── dao
│ │ │ └── mod.rs
│ │ ├── mod.rs
│ │ └── migration.rs
│ ├── main.rs
│ ├── proxy
│ │ └── mod.rs
│ ├── middleware
│ │ └── mod.rs
│ ├── services
│ │ ├── mod.rs
│ │ └── prompt_sync.rs
│ ├── server
│ │ └── handlers
│ │ │ └── mod.rs
│ ├── injection
│ │ └── mod.rs
│ ├── commands
│ │ ├── mod.rs
│ │ ├── mcp_cmd.rs
│ │ ├── switch_cmd.rs
│ │ └── prompt_cmd.rs
│ ├── tray
│ │ └── mod.rs
│ ├── processor
│ │ └── steps
│ │ │ ├── mod.rs
│ │ │ └── traits.rs
│ ├── plugin
│ │ └── mod.rs
│ ├── converter
│ │ └── mod.rs
│ ├── telemetry
│ │ └── mod.rs
│ ├── resilience
│ │ └── mod.rs
│ ├── models
│ │ ├── mod.rs
│ │ ├── prompt_model.rs
│ │ ├── mcp_model.rs
│ │ ├── app_type.rs
│ │ ├── provider_model.rs
│ │ └── skill_model.rs
│ ├── credential
│ │ └── mod.rs
│ ├── router
│ │ └── mod.rs
│ ├── config
│ │ └── mod.rs
│ ├── providers
│ │ └── mod.rs
│ ├── streaming
│ │ └── mod.rs
│ └── flow_monitor
│ │ └── mod.rs
├── gen
│ └── schemas
│ │ └── capabilities.json
├── capabilities
│ └── default.json
├── proptest-regressions
│ ├── proxy
│ │ └── tests.txt
│ ├── flow_monitor
│ │ ├── memory_store.txt
│ │ ├── file_store.txt
│ │ └── stream_rebuilder.txt
│ ├── router
│ │ └── tests.txt
│ ├── providers
│ │ └── tests.txt
│ ├── websocket
│ │ └── tests.txt
│ └── config
│ │ └── tests.txt
├── tauri.conf.json
└── Cargo.toml
├── postcss.config.js
├── docs
├── images
│ ├── 067c7d64-e116-4a30-b533-748873166f37.png
│ ├── 151b4355-821c-4bda-a731-c4367b6b8716.png
│ ├── 25eb018a-5be2-4f82-ba22-e68f39160cac.png
│ ├── 943663ed-b17c-4b32-a74c-c0243ffb3dea.png
│ ├── aee62eb5-3aeb-4454-b14d-24b1d5f9a0fe.png
│ ├── c7d8236b-ea6c-4496-ada5-288cd0a01738.png
│ └── ffc70018-aa5f-4738-883d-045614488608.png
├── nuxt.config.ts
├── package.json
├── app.config.ts
└── content
│ ├── 07.legal
│ └── 1.disclaimer.md
│ ├── 02.user-guide
│ ├── 1.dashboard.md
│ ├── 2.monitoring.md
│ ├── 3.credential-pool.md
│ ├── 10.prompts.md
│ ├── 7.config-switch.md
│ ├── 5.resilience.md
│ ├── 12.settings.md
│ ├── 6.config-management.md
│ ├── 11.skills.md
│ └── 4.smart-routing.md
│ ├── 01.introduction
│ ├── 2.installation.md
│ ├── 1.overview.md
│ └── 3.quickstart.md
│ ├── 03.providers
│ ├── 2.kiro-claude.md
│ ├── 7.codex.md
│ ├── 3.gemini-cli.md
│ ├── 4.qwen.md
│ └── 5.openai-custom.md
│ ├── 05.troubleshooting
│ ├── 1.common-issues.md
│ ├── 2.credential-errors.md
│ └── 3.connection-issues.md
│ └── 04.api-reference
│ └── 1.overview.md
├── .gitignore
├── tsconfig.node.json
├── index.html
├── vite.config.ts
├── .claude
└── settings.local.json
├── tsconfig.json
├── scripts
└── update-version.sh
├── .husky
└── pre-commit
├── eslint.config.js
├── .github
└── workflows
│ ├── ci.yml
│ └── deploy-docs.yml
├── public
└── vite.svg
├── tailwind.config.js
├── AGENTS.md
└── package.json
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/components/mcp/index.ts:
--------------------------------------------------------------------------------
1 | export { McpPage } from "./McpPage";
2 |
--------------------------------------------------------------------------------
/src-tauri/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | tauri_build::build()
3 | }
4 |
--------------------------------------------------------------------------------
/src/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export { useFlowEvents } from "./useFlowEvents";
2 |
--------------------------------------------------------------------------------
/src/pages/index.ts:
--------------------------------------------------------------------------------
1 | export { FlowMonitorPage } from "./FlowMonitorPage";
2 |
--------------------------------------------------------------------------------
/src/components/extensions/index.ts:
--------------------------------------------------------------------------------
1 | export { ExtensionsPage } from "./ExtensionsPage";
2 |
--------------------------------------------------------------------------------
/src/components/websocket/index.ts:
--------------------------------------------------------------------------------
1 | export { WebSocketStatus } from "./WebSocketStatus";
2 |
--------------------------------------------------------------------------------
/src-tauri/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/icon.ico
--------------------------------------------------------------------------------
/src-tauri/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/icon.png
--------------------------------------------------------------------------------
/src-tauri/icons/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/128x128.png
--------------------------------------------------------------------------------
/src-tauri/icons/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/32x32.png
--------------------------------------------------------------------------------
/src-tauri/icons/64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/64x64.png
--------------------------------------------------------------------------------
/src-tauri/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/icon.icns
--------------------------------------------------------------------------------
/src-tauri/icons/StoreLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/StoreLogo.png
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/src-tauri/icons/128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/128x128@2x.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square107x107Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/Square107x107Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square142x142Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/Square142x142Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square150x150Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/Square150x150Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square284x284Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/Square284x284Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square30x30Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/Square30x30Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square310x310Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/Square310x310Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square44x44Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/Square44x44Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square71x71Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/Square71x71Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square89x89Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/Square89x89Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/tray/tray-error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/tray/tray-error.png
--------------------------------------------------------------------------------
/src-tauri/icons/tray/tray-running.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/tray/tray-running.png
--------------------------------------------------------------------------------
/src-tauri/icons/tray/tray-stopped.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/tray/tray-stopped.png
--------------------------------------------------------------------------------
/src-tauri/icons/tray/tray-warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/tray/tray-warning.png
--------------------------------------------------------------------------------
/src-tauri/icons/tray/trayTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/tray/trayTemplate.png
--------------------------------------------------------------------------------
/src/components/plugins/index.ts:
--------------------------------------------------------------------------------
1 | export { PluginManager } from "./PluginManager";
2 | export { default } from "./PluginManager";
3 |
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-512@2x.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-20x20@1x.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-20x20@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-20x20@2x-1.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-20x20@2x.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-20x20@3x.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-29x29@1x.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-29x29@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-29x29@2x-1.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-29x29@2x.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-29x29@3x.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-40x40@1x.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-40x40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-40x40@2x-1.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-40x40@2x.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-40x40@3x.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-60x60@2x.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-60x60@3x.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-76x76@1x.png
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-76x76@2x.png
--------------------------------------------------------------------------------
/src-tauri/icons/tray/trayTemplate@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/tray/trayTemplate@2x.png
--------------------------------------------------------------------------------
/src-tauri/src/database/dao/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod mcp;
2 | pub mod prompts;
3 | pub mod provider_pool;
4 | pub mod providers;
5 | pub mod skills;
6 |
--------------------------------------------------------------------------------
/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/src-tauri/src/main.rs:
--------------------------------------------------------------------------------
1 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
2 |
3 | fn main() {
4 | proxycast_lib::run()
5 | }
6 |
--------------------------------------------------------------------------------
/docs/images/067c7d64-e116-4a30-b533-748873166f37.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/docs/images/067c7d64-e116-4a30-b533-748873166f37.png
--------------------------------------------------------------------------------
/docs/images/151b4355-821c-4bda-a731-c4367b6b8716.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/docs/images/151b4355-821c-4bda-a731-c4367b6b8716.png
--------------------------------------------------------------------------------
/docs/images/25eb018a-5be2-4f82-ba22-e68f39160cac.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/docs/images/25eb018a-5be2-4f82-ba22-e68f39160cac.png
--------------------------------------------------------------------------------
/docs/images/943663ed-b17c-4b32-a74c-c0243ffb3dea.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/docs/images/943663ed-b17c-4b32-a74c-c0243ffb3dea.png
--------------------------------------------------------------------------------
/docs/images/aee62eb5-3aeb-4454-b14d-24b1d5f9a0fe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/docs/images/aee62eb5-3aeb-4454-b14d-24b1d5f9a0fe.png
--------------------------------------------------------------------------------
/docs/images/c7d8236b-ea6c-4496-ada5-288cd0a01738.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/docs/images/c7d8236b-ea6c-4496-ada5-288cd0a01738.png
--------------------------------------------------------------------------------
/docs/images/ffc70018-aa5f-4738-883d-045614488608.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/docs/images/ffc70018-aa5f-4738-883d-045614488608.png
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/src/components/prompts/index.ts:
--------------------------------------------------------------------------------
1 | export { PromptsPage } from "./PromptsPage";
2 | export { PromptCard } from "./PromptCard";
3 | export { PromptForm } from "./PromptForm";
4 |
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/src/components/skills/index.ts:
--------------------------------------------------------------------------------
1 | export { SkillsPage } from "./SkillsPage";
2 | export { SkillCard } from "./SkillCard";
3 | export { RepoManagerPanel } from "./RepoManagerPanel";
4 |
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/src-tauri/icons/android/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #fff
4 |
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiclientproxy/proxycast/HEAD/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/src/components/resilience/index.ts:
--------------------------------------------------------------------------------
1 | export { RetrySettings } from "./RetrySettings";
2 | export { FailoverSettings } from "./FailoverSettings";
3 | export { ResiliencePage } from "./ResiliencePage";
4 |
--------------------------------------------------------------------------------
/src-tauri/gen/schemas/capabilities.json:
--------------------------------------------------------------------------------
1 | {"default":{"identifier":"default","description":"Default capabilities for ProxyCast","local":true,"windows":["main"],"permissions":["core:default","shell:allow-open","dialog:default"]}}
--------------------------------------------------------------------------------
/src/components/monitoring/index.ts:
--------------------------------------------------------------------------------
1 | export { MonitoringPage } from "./MonitoringPage";
2 | export { StatsOverview } from "./StatsOverview";
3 | export { LogViewer } from "./LogViewer";
4 | export { TokenStats } from "./TokenStats";
5 |
--------------------------------------------------------------------------------
/src-tauri/src/proxy/mod.rs:
--------------------------------------------------------------------------------
1 | //! 代理模块
2 | //!
3 | //! 提供 Per-Key 代理支持,允许为每个凭证配置独立的代理设置
4 |
5 | mod client_factory;
6 | #[cfg(test)]
7 | mod tests;
8 |
9 | pub use client_factory::{ProxyClientFactory, ProxyError, ProxyProtocol};
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | node_modules/
3 |
4 | # Build
5 | dist/
6 | src-tauri/target/
7 |
8 | # IDE
9 | .vscode/
10 | .idea/
11 |
12 | # OS
13 | .DS_Store
14 | Thumbs.db
15 |
16 | # Logs
17 | *.log
18 |
19 | # Kiro
20 | .kiro/
21 |
--------------------------------------------------------------------------------
/src-tauri/src/middleware/mod.rs:
--------------------------------------------------------------------------------
1 | //! Middleware 模块
2 | //!
3 | //! 提供 HTTP 请求处理的中间件组件
4 |
5 | pub mod management_auth;
6 |
7 | #[cfg(test)]
8 | mod tests;
9 |
10 | pub use management_auth::{ManagementAuthLayer, ManagementAuthService};
11 |
--------------------------------------------------------------------------------
/src/components/switch/index.ts:
--------------------------------------------------------------------------------
1 | export { SwitchPage } from "./SwitchPage";
2 | export { AppTabs } from "./AppTabs";
3 | export { ProviderList } from "./ProviderList";
4 | export { ProviderCard } from "./ProviderCard";
5 | export { ProviderForm } from "./ProviderForm";
6 |
--------------------------------------------------------------------------------
/src/components/clients/index.ts:
--------------------------------------------------------------------------------
1 | export { ClientsPage } from "./ClientsPage";
2 | export { AppTabs } from "./AppTabs";
3 | export { ProviderList } from "./ProviderList";
4 | export { ProviderCard } from "./ProviderCard";
5 | export { ProviderForm } from "./ProviderForm";
6 |
--------------------------------------------------------------------------------
/src/components/config/index.ts:
--------------------------------------------------------------------------------
1 | export { ConfigPage } from "./ConfigPage";
2 | export { ConfigEditor } from "./ConfigEditor";
3 | export { ImportExport } from "./ImportExport";
4 | export { AuthDirSettings } from "./AuthDirSettings";
5 | export { ConfigManagementPage } from "./ConfigManagementPage";
6 |
--------------------------------------------------------------------------------
/src-tauri/src/services/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod live_sync;
2 | pub mod mcp_service;
3 | pub mod mcp_sync;
4 | pub mod prompt_service;
5 | pub mod prompt_sync;
6 | pub mod provider_pool_service;
7 | pub mod skill_service;
8 | pub mod switch;
9 | pub mod token_cache_service;
10 | pub mod usage_service;
11 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true,
8 | "strict": true
9 | },
10 | "include": ["vite.config.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/docs/nuxt.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | extends: ["docus"],
3 | app: {
4 | baseURL: "/proxycast/",
5 | },
6 | image: {
7 | provider: "none",
8 | },
9 | robots: {
10 | robotsTxt: false,
11 | },
12 | llms: {
13 | domain: "https://proxycast.local",
14 | },
15 | };
16 |
--------------------------------------------------------------------------------
/src-tauri/src/server/handlers/mod.rs:
--------------------------------------------------------------------------------
1 | //! HTTP 请求处理器模块
2 | //!
3 | //! 将 server 中的各类处理器拆分到独立文件
4 |
5 | pub mod api;
6 | pub mod management;
7 | pub mod provider_calls;
8 | pub mod websocket;
9 |
10 | pub use api::*;
11 | pub use management::*;
12 | pub use provider_calls::*;
13 | pub use websocket::*;
14 |
--------------------------------------------------------------------------------
/src-tauri/src/injection/mod.rs:
--------------------------------------------------------------------------------
1 | //! 参数注入模块
2 | //!
3 | //! 提供请求参数注入功能,支持:
4 | //! - 模型通配符匹配规则
5 | //! - merge 和 override 两种注入模式
6 | //! - 规则优先级排序
7 |
8 | mod types;
9 |
10 | pub use types::{InjectionConfig, InjectionMode, InjectionResult, InjectionRule, Injector};
11 |
12 | #[cfg(test)]
13 | mod tests;
14 |
--------------------------------------------------------------------------------
/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src-tauri/capabilities/default.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schemas.tauri.app/config/2/capability",
3 | "identifier": "default",
4 | "description": "Default capabilities for ProxyCast",
5 | "windows": ["main"],
6 | "permissions": [
7 | "core:default",
8 | "shell:allow-open",
9 | "dialog:default"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App";
4 | import { Toaster } from "./components/ui/sonner";
5 | import "./index.css";
6 |
7 | ReactDOM.createRoot(document.getElementById("root")!).render(
8 |
9 |
10 |
11 | ,
12 | );
13 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proxycast-docs",
3 | "scripts": {
4 | "dev": "nuxt dev --extends docus",
5 | "build": "nuxt build --extends docus",
6 | "generate": "nuxt generate --extends docus"
7 | },
8 | "dependencies": {
9 | "docus": "latest",
10 | "better-sqlite3": "^12.2.0",
11 | "nuxt": "^4.2.1",
12 | "mermaid": "^11.4.0"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/icons/providers/anthropic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/routing/index.ts:
--------------------------------------------------------------------------------
1 | export { ModelMapping } from "./ModelMapping";
2 | export { RoutingRules } from "./RoutingRules";
3 | export { ExclusionList } from "./ExclusionList";
4 | export { InjectionRules } from "./InjectionRules";
5 | export { RoutingPage } from "./RoutingPage";
6 | export type { RoutingPageRef } from "./RoutingPage";
7 | export { RoutingManagementPage } from "./RoutingManagementPage";
8 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ProxyCast
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/provider-pool/credential-forms/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 凭证表单组件导出
3 | */
4 |
5 | export * from "./types";
6 | export * from "./ModeSelector";
7 | export * from "./OAuthUrlDisplay";
8 | export * from "./FileImportForm";
9 | export * from "./AntigravityForm";
10 | export * from "./CodexForm";
11 | export * from "./ClaudeOAuthForm";
12 | export * from "./QwenForm";
13 | export * from "./IFlowForm";
14 | export * from "./GeminiForm";
15 |
--------------------------------------------------------------------------------
/src/components/flow-monitor/index.ts:
--------------------------------------------------------------------------------
1 | export { FlowList } from "./FlowList";
2 | export { FlowFilters } from "./FlowFilters";
3 | export { FlowDetail } from "./FlowDetail";
4 | export { FlowTimeline, FlowTimelineCompact } from "./FlowTimeline";
5 | export { FlowStats } from "./FlowStats";
6 | export { ExportDialog } from "./ExportDialog";
7 | export { useFlowEvents } from "@/hooks/useFlowEvents";
8 | export { useFlowActions } from "@/hooks/useFlowActions";
9 |
--------------------------------------------------------------------------------
/src-tauri/proptest-regressions/proxy/tests.txt:
--------------------------------------------------------------------------------
1 | # Seeds for failure cases proptest has generated in the past. It is
2 | # automatically read and these particular cases re-run before any
3 | # novel cases are generated.
4 | #
5 | # It is recommended to check this file in to source control so that
6 | # everyone who runs the test benefits from these saved cases.
7 | cc 99af5a6dad66f0b5a2650223417a2e83a7a3bfa8b6eab8ad57a88023367e739c # shrinks to url = "http://08:1024"
8 |
--------------------------------------------------------------------------------
/src-tauri/src/commands/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod config_cmd;
2 | pub mod flow_monitor_cmd;
3 | pub mod injection_cmd;
4 | pub mod mcp_cmd;
5 | pub mod oauth_cmd;
6 | pub mod plugin_cmd;
7 | pub mod prompt_cmd;
8 | pub mod provider_pool_cmd;
9 | pub mod resilience_cmd;
10 | pub mod route_cmd;
11 | pub mod router_cmd;
12 | pub mod skill_cmd;
13 | pub mod switch_cmd;
14 | pub mod telemetry_cmd;
15 | pub mod tray_cmd;
16 | pub mod usage_cmd;
17 | pub mod websocket_cmd;
18 |
--------------------------------------------------------------------------------
/src-tauri/src/tray/mod.rs:
--------------------------------------------------------------------------------
1 | //! 系统托盘模块
2 | //!
3 | //! 提供系统托盘功能,包括:
4 | //! - 托盘图标状态管理
5 | //! - 托盘菜单构建
6 | //! - 菜单事件处理
7 | //! - 托盘图标点击事件处理
8 | //! - 状态同步
9 |
10 | mod events;
11 | mod format;
12 | mod manager;
13 | mod menu;
14 | mod menu_handler;
15 | mod state;
16 | mod sync;
17 |
18 | pub use events::*;
19 | pub use format::*;
20 | pub use manager::*;
21 | pub use menu::*;
22 | pub use menu_handler::*;
23 | pub use state::*;
24 | pub use sync::*;
25 |
--------------------------------------------------------------------------------
/src-tauri/proptest-regressions/flow_monitor/memory_store.txt:
--------------------------------------------------------------------------------
1 | # Seeds for failure cases proptest has generated in the past. It is
2 | # automatically read and these particular cases re-run before any
3 | # novel cases are generated.
4 | #
5 | # It is recommended to check this file in to source control so that
6 | # everyone who runs the test benefits from these saved cases.
7 | cc 4ef289c82d7068ccd05f549e93999e0d88c53e9d4ad4ebbcac1b33d00993d259 # shrinks to prefix = "ot"
8 |
--------------------------------------------------------------------------------
/src/components/settings/index.ts:
--------------------------------------------------------------------------------
1 | export { SettingsPage } from "./SettingsPage";
2 | export { GeneralSettings } from "./GeneralSettings";
3 | export { ProxySettings } from "./ProxySettings";
4 | export { DirectorySettings } from "./DirectorySettings";
5 | export { AboutSection } from "./AboutSection";
6 | export { TlsSettings } from "./TlsSettings";
7 | export { QuotaSettings } from "./QuotaSettings";
8 | export { RemoteManagementSettings } from "./RemoteManagementSettings";
9 |
--------------------------------------------------------------------------------
/src-tauri/proptest-regressions/router/tests.txt:
--------------------------------------------------------------------------------
1 | # Seeds for failure cases proptest has generated in the past. It is
2 | # automatically read and these particular cases re-run before any
3 | # novel cases are generated.
4 | #
5 | # It is recommended to check this file in to source control so that
6 | # everyone who runs the test benefits from these saved cases.
7 | cc 98a27aecaee1a9145362c2fa8a876f39010daa37133f7dd9f9713f6c2f8118f7 # shrinks to provider = "google", version = "v1"
8 |
--------------------------------------------------------------------------------
/src-tauri/src/processor/steps/mod.rs:
--------------------------------------------------------------------------------
1 | //! 管道步骤模块
2 | //!
3 | //! 定义请求处理管道中的各个步骤
4 |
5 | mod auth;
6 | mod injection;
7 | mod plugin;
8 | mod provider;
9 | mod routing;
10 | mod telemetry;
11 | mod traits;
12 |
13 | pub use auth::AuthStep;
14 | pub use injection::InjectionStep;
15 | pub use plugin::{PluginPostStep, PluginPreStep};
16 | pub use provider::ProviderStep;
17 | pub use routing::RoutingStep;
18 | pub use telemetry::TelemetryStep;
19 | pub use traits::PipelineStep;
20 |
--------------------------------------------------------------------------------
/src-tauri/proptest-regressions/providers/tests.txt:
--------------------------------------------------------------------------------
1 | # Seeds for failure cases proptest has generated in the past. It is
2 | # automatically read and these particular cases re-run before any
3 | # novel cases are generated.
4 | #
5 | # It is recommended to check this file in to source control so that
6 | # everyone who runs the test benefits from these saved cases.
7 | cc 10c52f014c7e9b4cd049a9802452d417b5774e6f42173c2c9329544d8ac4340c # shrinks to lead_time_mins = 21, time_offset_secs = 1260
8 |
--------------------------------------------------------------------------------
/src-tauri/src/plugin/mod.rs:
--------------------------------------------------------------------------------
1 | //! 插件系统模块
2 | //!
3 | //! 提供插件扩展功能,支持:
4 | //! - 插件加载和初始化
5 | //! - 请求前/响应后钩子
6 | //! - 插件隔离和错误处理
7 | //! - 插件配置管理
8 |
9 | mod loader;
10 | mod manager;
11 | mod types;
12 |
13 | pub use loader::PluginLoader;
14 | pub use manager::PluginManager;
15 | pub use types::{
16 | HookResult, Plugin, PluginConfig, PluginContext, PluginError, PluginInfo, PluginManifest,
17 | PluginState, PluginStatus, PluginType,
18 | };
19 |
20 | #[cfg(test)]
21 | mod tests;
22 |
--------------------------------------------------------------------------------
/src-tauri/src/converter/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod anthropic_to_openai;
2 | pub mod cw_to_openai;
3 | pub mod openai_to_antigravity;
4 | pub mod openai_to_cw;
5 | pub mod protocol_selector;
6 |
7 | #[allow(unused_imports)]
8 | pub use anthropic_to_openai::*;
9 | #[allow(unused_imports)]
10 | pub use cw_to_openai::*;
11 | #[allow(unused_imports)]
12 | pub use openai_to_antigravity::*;
13 | #[allow(unused_imports)]
14 | pub use openai_to_cw::*;
15 | #[allow(unused_imports)]
16 | pub use protocol_selector::*;
17 |
--------------------------------------------------------------------------------
/src-tauri/proptest-regressions/flow_monitor/file_store.txt:
--------------------------------------------------------------------------------
1 | # Seeds for failure cases proptest has generated in the past. It is
2 | # automatically read and these particular cases re-run before any
3 | # novel cases are generated.
4 | #
5 | # It is recommended to check this file in to source control so that
6 | # everyone who runs the test benefits from these saved cases.
7 | cc 93bc59c922237bf2c4ffc251c66c90f6bdcf7433a6a188b325fe65f38a8a6fd4 # shrinks to flow_count = 1
8 | cc 5f7ef8a17a79bad4599803df567d02ed7c7a29bae560e3e90dfb8b2f920cadf2 # shrinks to flow_count = 5
9 |
--------------------------------------------------------------------------------
/src-tauri/src/telemetry/mod.rs:
--------------------------------------------------------------------------------
1 | //! 监控与日志模块
2 | //!
3 | //! 提供请求日志记录、统计聚合和 Token 追踪功能
4 |
5 | mod logger;
6 | mod stats;
7 | mod tokens;
8 | mod types;
9 |
10 | pub use logger::{LogRotationConfig, LoggerError, RequestLogger};
11 | pub use stats::StatsAggregator;
12 | pub use tokens::{
13 | ModelTokenStats, PeriodTokenStats, ProviderTokenStats, TokenSource, TokenStatsSummary,
14 | TokenTracker, TokenUsageRecord,
15 | };
16 | pub use types::{ModelStats, ProviderStats, RequestLog, RequestStatus, StatsSummary, TimeRange};
17 |
18 | #[cfg(test)]
19 | mod tests;
20 |
--------------------------------------------------------------------------------
/src-tauri/src/resilience/mod.rs:
--------------------------------------------------------------------------------
1 | //! 容错机制模块
2 | //!
3 | //! 提供重试、故障转移和超时控制功能
4 |
5 | mod failover;
6 | mod retry;
7 | mod timeout;
8 |
9 | pub use failover::{
10 | Failover, FailoverConfig, FailoverManager, FailoverResult, FailureType, SwitchEvent,
11 | QUOTA_EXCEEDED_KEYWORDS, QUOTA_EXCEEDED_STATUS_CODES,
12 | };
13 | pub use retry::{Retrier, RetryConfig, RetryError};
14 | pub use timeout::{
15 | CancellationToken, StreamIdleDetector, StreamWithIdleTimeout, TimeoutConfig, TimeoutController,
16 | TimeoutError,
17 | };
18 |
19 | #[cfg(test)]
20 | mod tests;
21 |
--------------------------------------------------------------------------------
/src/components/ui/sonner.tsx:
--------------------------------------------------------------------------------
1 | import { Toaster as SonnerToaster } from "sonner";
2 |
3 | export function Toaster() {
4 | return (
5 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { defineConfig } from "vite";
3 | import react from "@vitejs/plugin-react";
4 | import path from "path";
5 |
6 | export default defineConfig({
7 | plugins: [react()],
8 | resolve: {
9 | alias: {
10 | "@": path.resolve(__dirname, "./src"),
11 | },
12 | },
13 | clearScreen: false,
14 | server: {
15 | port: 1420,
16 | strictPort: true,
17 | watch: {
18 | ignored: ["**/src-tauri/**"],
19 | },
20 | },
21 | test: {
22 | globals: true,
23 | environment: "jsdom",
24 | },
25 | });
26 |
--------------------------------------------------------------------------------
/src/components/provider-pool/index.ts:
--------------------------------------------------------------------------------
1 | export { ProviderPoolPage } from "./ProviderPoolPage";
2 | export { CredentialCard } from "./CredentialCard";
3 | export { AddCredentialModal } from "./AddCredentialModal";
4 | export { EditCredentialModal } from "./EditCredentialModal";
5 | export { GeminiApiKeySection } from "./GeminiApiKeySection";
6 | export { VertexAISection } from "./VertexAISection";
7 | export { CodexSection } from "./CodexSection";
8 | export { IFlowSection } from "./IFlowSection";
9 | export { AmpConfigSection } from "./AmpConfigSection";
10 | export { UsageDisplay } from "./UsageDisplay";
11 |
--------------------------------------------------------------------------------
/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from "clsx";
2 | import { twMerge } from "tailwind-merge";
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
8 | /**
9 | * 验证代理 URL 格式
10 | * 支持的格式:
11 | * - http://host:port
12 | * - https://host:port
13 | * - socks5://host:port
14 | * - http://user:pass@host:port(带认证)
15 | */
16 | export function validateProxyUrl(url: string): boolean {
17 | if (!url || url.trim() === "") return true; // 空值允许
18 | const pattern = /^(https?|socks5?):\/\/[^\s]+$/i;
19 | return pattern.test(url.trim());
20 | }
21 |
--------------------------------------------------------------------------------
/.claude/settings.local.json:
--------------------------------------------------------------------------------
1 | {
2 | "permissions": {
3 | "allow": [
4 | "Bash(npm run format:*)",
5 | "Bash(npm run lint)",
6 | "Bash(npx tsc:*)",
7 | "Bash(cargo test:*)",
8 | "Bash(cargo build:*)",
9 | "Bash(npm run check:*)",
10 | "Bash(npm run:*)",
11 | "Bash(tree:*)",
12 | "Bash(find:*)",
13 | "Bash(cargo check:*)",
14 | "Bash(rm:*)",
15 | "Bash(cargo clippy:*)",
16 | "Bash(cargo fmt:*)",
17 | "Bash(lsof:*)",
18 | "Bash(xargs kill:*)",
19 | "Bash(cargo run:*)"
20 | ],
21 | "deny": [],
22 | "ask": []
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src-tauri/proptest-regressions/websocket/tests.txt:
--------------------------------------------------------------------------------
1 | # Seeds for failure cases proptest has generated in the past. It is
2 | # automatically read and these particular cases re-run before any
3 | # novel cases are generated.
4 | #
5 | # It is recommended to check this file in to source control so that
6 | # everyone who runs the test benefits from these saved cases.
7 | cc bba7155a7ed5c22d19990ebdd9935e761dbdc41bc1e11076fbbf4218b82c8002 # shrinks to request_id = "0-AAaA-a", (original_data, sse_body) = ([" "], "data: \n\n")
8 | cc 4f0d0f49ef961c396cdf1aa7ccbd2a01b525e96ba5a2048d18860dbe763abe37 # shrinks to request_id = "a00000aa", data = " ", index = 0
9 |
--------------------------------------------------------------------------------
/src-tauri/proptest-regressions/flow_monitor/stream_rebuilder.txt:
--------------------------------------------------------------------------------
1 | # Seeds for failure cases proptest has generated in the past. It is
2 | # automatically read and these particular cases re-run before any
3 | # novel cases are generated.
4 | #
5 | # It is recommended to check this file in to source control so that
6 | # everyone who runs the test benefits from these saved cases.
7 | cc be956d5aa14123ea1af5d3e310121b3dec52581c16da860aa2622f1689edd520 # shrinks to tool_call = ("call_00aa00aa", "aa_", "{\"value\":\"aAaA_a\"}")
8 | cc f35fbd7673ecfb016ab0ee416718b5561162ae8ac1b04fbffe0fcebd8467a341 # shrinks to tool_call = ("call_a000a0a0", "__a", "{\"value\":\"aaAaaA\"}")
9 |
--------------------------------------------------------------------------------
/src/hooks/useErrorHandler.ts:
--------------------------------------------------------------------------------
1 | import { useState, useCallback } from "react";
2 |
3 | interface AppError {
4 | message: string;
5 | context?: string;
6 | timestamp: Date;
7 | }
8 |
9 | export function useErrorHandler() {
10 | const [error, setError] = useState(null);
11 |
12 | const handleError = useCallback((e: unknown, context?: string) => {
13 | console.error(`[${context || "Error"}]`, e);
14 | setError({
15 | message: e instanceof Error ? e.message : String(e),
16 | context,
17 | timestamp: new Date(),
18 | });
19 | }, []);
20 |
21 | const clearError = useCallback(() => setError(null), []);
22 |
23 | return { error, handleError, clearError };
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/switch/SwitchPage.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { AppType } from "@/lib/api/switch";
3 | import { AppTabs } from "./AppTabs";
4 | import { ProviderList } from "./ProviderList";
5 |
6 | export function SwitchPage() {
7 | const [activeApp, setActiveApp] = useState("claude");
8 |
9 | return (
10 |
11 |
12 |
Switch
13 |
14 | 管理和切换不同应用的 Provider 配置
15 |
16 |
17 |
18 |
19 |
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src-tauri/src/models/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod anthropic;
2 | pub mod app_type;
3 | pub mod codewhisperer;
4 | pub mod mcp_model;
5 | pub mod openai;
6 | pub mod prompt_model;
7 | pub mod provider_model;
8 | pub mod provider_pool_model;
9 | pub mod route_model;
10 | pub mod skill_model;
11 |
12 | #[allow(unused_imports)]
13 | pub use anthropic::*;
14 | pub use app_type::AppType;
15 | #[allow(unused_imports)]
16 | pub use codewhisperer::*;
17 | pub use mcp_model::McpServer;
18 | #[allow(unused_imports)]
19 | pub use openai::*;
20 | pub use prompt_model::Prompt;
21 | pub use provider_model::Provider;
22 | #[allow(unused_imports)]
23 | pub use provider_pool_model::*;
24 | pub use skill_model::{Skill, SkillMetadata, SkillRepo, SkillState, SkillStates};
25 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 | "moduleResolution": "bundler",
9 | "allowImportingTsExtensions": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "noEmit": true,
13 | "jsx": "react-jsx",
14 | "strict": true,
15 | "noUnusedLocals": true,
16 | "noUnusedParameters": true,
17 | "noFallthroughCasesInSwitch": true,
18 | "baseUrl": ".",
19 | "paths": {
20 | "@/*": ["./src/*"]
21 | }
22 | },
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/src-tauri/src/credential/mod.rs:
--------------------------------------------------------------------------------
1 | //! 凭证池管理模块
2 | //!
3 | //! 提供多凭证管理、负载均衡和健康检查功能
4 |
5 | mod balancer;
6 | mod health;
7 | mod pool;
8 | mod quota;
9 | mod sync;
10 | mod types;
11 |
12 | pub use balancer::{BalanceStrategy, CooldownInfo, CredentialSelection, LoadBalancer};
13 | pub use health::{HealthCheckConfig, HealthCheckResult, HealthChecker, HealthStatus};
14 | pub use pool::{CredentialPool, PoolError, PoolStatus};
15 | pub use quota::{
16 | create_shared_quota_manager, start_quota_cleanup_task, AllCredentialsExhaustedError,
17 | QuotaAutoSwitchResult, QuotaExceededRecord, QuotaManager,
18 | };
19 | pub use sync::{CredentialSyncService, SyncError};
20 | pub use types::{Credential, CredentialData, CredentialStats, CredentialStatus};
21 |
22 | #[cfg(test)]
23 | mod tests;
24 |
--------------------------------------------------------------------------------
/src/lib/api/usage.ts:
--------------------------------------------------------------------------------
1 | import { invoke } from "@tauri-apps/api/core";
2 |
3 | /**
4 | * 用量信息接口
5 | *
6 | * 与后端 UsageInfo 结构对应
7 | * _Requirements: 3.3_
8 | */
9 | export interface UsageInfo {
10 | /** 订阅类型名称 */
11 | subscriptionTitle: string;
12 | /** 总额度 */
13 | usageLimit: number;
14 | /** 已使用 */
15 | currentUsage: number;
16 | /** 余额 = usageLimit - currentUsage */
17 | balance: number;
18 | /** 余额低于 20% */
19 | isLowBalance: boolean;
20 | }
21 |
22 | /**
23 | * Usage API
24 | */
25 | export const usageApi = {
26 | /**
27 | * 获取 Kiro 凭证的用量信息
28 | *
29 | * @param credentialUuid - 凭证的 UUID
30 | * @returns 用量信息
31 | */
32 | getKiroUsage: (credentialUuid: string): Promise =>
33 | invoke("get_kiro_usage", { credentialUuid }),
34 | };
35 |
--------------------------------------------------------------------------------
/src-tauri/src/database/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod dao;
2 | pub mod migration;
3 | pub mod schema;
4 |
5 | use rusqlite::Connection;
6 | use std::path::PathBuf;
7 | use std::sync::{Arc, Mutex};
8 |
9 | pub type DbConnection = Arc>;
10 |
11 | /// 获取数据库文件路径
12 | pub fn get_db_path() -> PathBuf {
13 | let home = dirs::home_dir().expect("Cannot find home directory");
14 | let db_dir = home.join(".proxycast");
15 | std::fs::create_dir_all(&db_dir).expect("Cannot create .proxycast directory");
16 | db_dir.join("proxycast.db")
17 | }
18 |
19 | /// 初始化数据库连接
20 | pub fn init_database() -> Result {
21 | let db_path = get_db_path();
22 | let conn = Connection::open(&db_path)?;
23 |
24 | // 创建表结构
25 | schema::create_tables(&conn)?;
26 |
27 | Ok(Arc::new(Mutex::new(conn)))
28 | }
29 |
--------------------------------------------------------------------------------
/src-tauri/src/router/mod.rs:
--------------------------------------------------------------------------------
1 | //! 路由系统模块
2 | //!
3 | //! 支持动态路由注册、命名空间路由解析、模型映射和路由规则。
4 | //!
5 | //! 路由格式:
6 | //! - `/{provider-name}/v1/messages` - Provider 命名空间路由
7 | //! - `/{selector}/v1/messages` - 凭证选择器路由(向后兼容)
8 | //! - `/v1/messages` - 默认路由
9 | //! - `/api/provider/{provider}/v1/*` - Amp CLI 路由
10 | //!
11 | //! 模型映射:
12 | //! - 支持模型别名映射(如 `gpt-4` -> `claude-sonnet-4-5-20250514`)
13 | //!
14 | //! 路由规则:
15 | //! - 支持通配符模式匹配(前缀、后缀、包含)
16 | //! - 支持规则优先级排序
17 |
18 | mod amp_router;
19 | mod mapper;
20 | mod provider_router;
21 | mod route_registry;
22 | mod rules;
23 |
24 | pub use amp_router::{AmpRouteMatch, AmpRouter};
25 | pub use mapper::{ModelInfo, ModelMapper};
26 | pub use provider_router::ProviderRouter;
27 | pub use route_registry::{RegisteredRoute, RouteRegistry, RouteType};
28 | pub use rules::{RouteResult, Router, RoutingRule};
29 |
30 | #[cfg(test)]
31 | mod tests;
32 |
--------------------------------------------------------------------------------
/src/lib/api/routes.ts:
--------------------------------------------------------------------------------
1 | import { invoke } from "@tauri-apps/api/core";
2 |
3 | export interface RouteEndpoint {
4 | path: string;
5 | protocol: string;
6 | url: string;
7 | }
8 |
9 | export interface RouteInfo {
10 | selector: string;
11 | provider_type: string;
12 | credential_count: number;
13 | endpoints: RouteEndpoint[];
14 | tags: string[];
15 | enabled: boolean;
16 | }
17 |
18 | export interface RouteListResponse {
19 | base_url: string;
20 | default_provider: string;
21 | routes: RouteInfo[];
22 | }
23 |
24 | export interface CurlExample {
25 | description: string;
26 | command: string;
27 | }
28 |
29 | export const routesApi = {
30 | async getAvailableRoutes(): Promise {
31 | return invoke("get_available_routes");
32 | },
33 |
34 | async getCurlExamples(selector: string): Promise {
35 | return invoke("get_route_curl_examples", { selector });
36 | },
37 | };
38 |
--------------------------------------------------------------------------------
/docs/app.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | docus: {
3 | title: "ProxyCast",
4 | description: "把你的 AI 客户端额度用到任何地方",
5 | url: "https://aiclientproxy.github.io/proxycast",
6 |
7 | image: "/images/logo-banner.svg",
8 |
9 | socials: {
10 | github: "aiclientproxy/proxycast",
11 | },
12 |
13 | header: {
14 | logo: true,
15 | title: "ProxyCast",
16 | showLinkIcon: true,
17 | },
18 |
19 | aside: {
20 | level: 0,
21 | collapsed: false,
22 | exclude: [],
23 | },
24 |
25 | footer: {
26 | credits: {
27 | icon: "",
28 | text: "Made with 💜 by ProxyCast Team",
29 | href: "https://github.com/aiclientproxy",
30 | },
31 | textLinks: [
32 | {
33 | text: "GitHub",
34 | href: "https://github.com/aiclientproxy/proxycast",
35 | target: "_blank",
36 | },
37 | ],
38 | iconLinks: [],
39 | },
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/src/icons/app-icon-compact.svg:
--------------------------------------------------------------------------------
1 |
25 |
--------------------------------------------------------------------------------
/src-tauri/tauri.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.tauri.app/config/2",
3 | "productName": "ProxyCast",
4 | "version": "0.16.0",
5 | "identifier": "com.proxycast.app",
6 | "build": {
7 | "beforeDevCommand": "npm run dev",
8 | "devUrl": "http://localhost:1420",
9 | "beforeBuildCommand": "npm run build",
10 | "frontendDist": "../dist"
11 | },
12 | "app": {
13 | "withGlobalTauri": true,
14 | "windows": [
15 | {
16 | "title": "ProxyCast",
17 | "width": 1000,
18 | "height": 700,
19 | "resizable": true,
20 | "fullscreen": false
21 | }
22 | ],
23 | "security": {
24 | "csp": null
25 | }
26 | },
27 | "bundle": {
28 | "active": true,
29 | "targets": "all",
30 | "icon": [
31 | "icons/32x32.png",
32 | "icons/128x128.png",
33 | "icons/128x128@2x.png",
34 | "icons/icon.icns",
35 | "icons/icon.ico"
36 | ],
37 | "resources": [
38 | "icons/tray/*"
39 | ]
40 | },
41 | "plugins": {
42 | "shell": {
43 | "open": true
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src-tauri/src/config/mod.rs:
--------------------------------------------------------------------------------
1 | //! 配置管理模块
2 | //!
3 | //! 提供 YAML 配置文件支持、热重载和配置导入导出功能
4 | //! 同时保持与旧版 JSON 配置的向后兼容性
5 |
6 | mod export;
7 | mod hot_reload;
8 | mod import;
9 | mod path_utils;
10 | mod types;
11 | mod yaml;
12 |
13 | pub use export::{ExportBundle, ExportOptions, ExportService, REDACTED_PLACEHOLDER};
14 | pub use hot_reload::{
15 | ConfigChangeEvent, ConfigChangeKind, FileWatcher, HotReloadManager, ReloadResult,
16 | };
17 | pub use import::{ImportOptions, ImportService, ValidationResult};
18 | pub use path_utils::{collapse_tilde, contains_tilde, expand_tilde};
19 | pub use types::{
20 | AmpConfig, AmpModelMapping, ApiKeyEntry, Config, CredentialEntry, CredentialPoolConfig,
21 | CustomProviderConfig, GeminiApiKeyEntry, IFlowCredentialEntry, InjectionRuleConfig,
22 | InjectionSettings, LoggingConfig, ProviderConfig, ProvidersConfig, QuotaExceededConfig,
23 | RemoteManagementConfig, RetrySettings, RoutingConfig, RoutingRuleConfig, ServerConfig,
24 | TlsConfig, VertexApiKeyEntry, VertexModelAlias,
25 | };
26 | pub use yaml::{load_config, save_config, ConfigError, ConfigManager, YamlService};
27 |
28 | #[cfg(test)]
29 | mod tests;
30 |
--------------------------------------------------------------------------------
/scripts/update-version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # 统一版本管理脚本
3 | # 用法: ./scripts/update-version.sh
4 | # 示例: ./scripts/update-version.sh 0.5.0
5 |
6 | set -e
7 |
8 | if [ -z "$1" ]; then
9 | echo "用法: $0 "
10 | echo "示例: $0 0.5.0"
11 | exit 1
12 | fi
13 |
14 | NEW_VERSION="$1"
15 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
16 | PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
17 |
18 | echo "更新版本到 $NEW_VERSION..."
19 |
20 | # 更新 package.json
21 | sed -i '' "s/\"version\": \"[^\"]*\"/\"version\": \"$NEW_VERSION\"/" "$PROJECT_ROOT/package.json"
22 | echo "✓ package.json"
23 |
24 | # 更新 Cargo.toml
25 | sed -i '' "s/^version = \"[^\"]*\"/version = \"$NEW_VERSION\"/" "$PROJECT_ROOT/src-tauri/Cargo.toml"
26 | echo "✓ src-tauri/Cargo.toml"
27 |
28 | # 更新 tauri.conf.json
29 | sed -i '' "s/\"version\": \"[^\"]*\"/\"version\": \"$NEW_VERSION\"/" "$PROJECT_ROOT/src-tauri/tauri.conf.json"
30 | echo "✓ src-tauri/tauri.conf.json"
31 |
32 | echo ""
33 | echo "版本已更新到 $NEW_VERSION"
34 | echo ""
35 | echo "下一步:"
36 | echo " git add -A"
37 | echo " git commit -m 'chore: bump version to $NEW_VERSION'"
38 | echo " git tag v$NEW_VERSION"
39 | echo " git push origin main --tags"
40 |
--------------------------------------------------------------------------------
/docs/content/07.legal/1.disclaimer.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 免责声明
3 | description: 使用条款和法律声明
4 | navigation:
5 | icon: i-heroicons-scale
6 | ---
7 |
8 | # 免责声明
9 |
10 | ## 使用目的
11 |
12 | ProxyCast 是一款开源工具,其设计初衷是帮助用户**充分利用已订阅的 AI 服务 Token**,在更多场景中发挥其价值。
13 |
14 | ::alert{type="warning"}
15 | **重要提示**: 本工具仅限于个人合法使用,严禁用于任何非法盈利目的。
16 | ::
17 |
18 | ## 合法使用范围
19 |
20 | 本工具的合法使用场景包括但不限于:
21 |
22 | - ✅ 个人学习和研究
23 | - ✅ 个人开发项目中使用已订阅的 AI 服务
24 | - ✅ 在不同开发工具间复用个人订阅额度
25 | - ✅ 提高个人工作效率
26 |
27 | ## 禁止行为
28 |
29 | 以下行为严格禁止:
30 |
31 | - ❌ 将本工具用于商业盈利目的
32 | - ❌ 转售或分享他人的 AI 服务凭证
33 | - ❌ 违反 AI 服务提供商的服务条款
34 | - ❌ 任何形式的非法活动
35 |
36 | ## 责任声明
37 |
38 | 1. **用户责任**: 用户应确保其使用行为符合所在地区的法律法规,以及相关 AI 服务提供商的服务条款。
39 |
40 | 2. **风险自担**: 使用本工具所产生的任何后果由用户自行承担,包括但不限于账户封禁、服务终止等。
41 |
42 | 3. **无担保**: 本工具按"现状"提供,不提供任何明示或暗示的担保。
43 |
44 | 4. **免责**: 开发者不对因使用本工具而导致的任何直接或间接损失承担责任。
45 |
46 | ## 服务条款遵守
47 |
48 | 使用本工具时,请务必遵守以下服务提供商的使用条款:
49 |
50 | - [Anthropic 使用政策](https://www.anthropic.com/policies)
51 | - [Google AI 服务条款](https://policies.google.com/terms)
52 | - [阿里云服务条款](https://terms.alibabacloud.com/)
53 | - [OpenAI 使用政策](https://openai.com/policies)
54 |
55 | ## 联系我们
56 |
57 | 如对本声明有任何疑问,请通过 GitHub Issues 联系我们。
58 |
--------------------------------------------------------------------------------
/src/hooks/useFileMonitoring.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef, useCallback } from "react";
2 |
3 | interface MonitorConfig {
4 | checkFn: () => Promise;
5 | interval?: number;
6 | enabled?: boolean;
7 | }
8 |
9 | export function useFileMonitoring(configs: Record) {
10 | const intervalRef = useRef | null>(null);
11 |
12 | const checkAll = useCallback(async () => {
13 | for (const [key, config] of Object.entries(configs)) {
14 | if (config.enabled !== false) {
15 | try {
16 | await config.checkFn();
17 | } catch (e) {
18 | console.error(`[FileMonitoring:${key}]`, e);
19 | }
20 | }
21 | }
22 | }, [configs]);
23 |
24 | useEffect(() => {
25 | // 获取最小间隔
26 | const intervals = Object.values(configs).map((c) => c.interval || 10000);
27 | const minInterval = Math.min(...intervals);
28 |
29 | intervalRef.current = setInterval(checkAll, minInterval);
30 |
31 | return () => {
32 | if (intervalRef.current) {
33 | clearInterval(intervalRef.current);
34 | }
35 | };
36 | }, [checkAll, configs]);
37 |
38 | return { checkAll };
39 | }
40 |
--------------------------------------------------------------------------------
/src-tauri/src/models/prompt_model.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize, Serialize};
2 |
3 | #[derive(Debug, Clone, Serialize, Deserialize)]
4 | pub struct Prompt {
5 | pub id: String,
6 | pub app_type: String,
7 | pub name: String,
8 | pub content: String,
9 | #[serde(skip_serializing_if = "Option::is_none")]
10 | pub description: Option,
11 | /// Whether this prompt is currently enabled (synced to live file)
12 | #[serde(default)]
13 | pub enabled: bool,
14 | #[serde(rename = "createdAt", skip_serializing_if = "Option::is_none")]
15 | pub created_at: Option,
16 | #[serde(rename = "updatedAt", skip_serializing_if = "Option::is_none")]
17 | pub updated_at: Option,
18 | }
19 |
20 | impl Prompt {
21 | #[allow(dead_code)]
22 | pub fn new(id: String, app_type: String, name: String, content: String) -> Self {
23 | let now = chrono::Utc::now().timestamp();
24 | Self {
25 | id,
26 | app_type,
27 | name,
28 | content,
29 | description: None,
30 | enabled: false,
31 | created_at: Some(now),
32 | updated_at: Some(now),
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src-tauri/src/models/mcp_model.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize, Serialize};
2 | use serde_json::Value;
3 |
4 | #[derive(Debug, Clone, Serialize, Deserialize)]
5 | pub struct McpServer {
6 | pub id: String,
7 | pub name: String,
8 | pub server_config: Value,
9 | #[serde(skip_serializing_if = "Option::is_none")]
10 | pub description: Option,
11 | #[serde(default)]
12 | pub enabled_proxycast: bool,
13 | #[serde(default)]
14 | pub enabled_claude: bool,
15 | #[serde(default)]
16 | pub enabled_codex: bool,
17 | #[serde(default)]
18 | pub enabled_gemini: bool,
19 | #[serde(skip_serializing_if = "Option::is_none")]
20 | pub created_at: Option,
21 | }
22 |
23 | impl McpServer {
24 | #[allow(dead_code)]
25 | pub fn new(id: String, name: String, server_config: Value) -> Self {
26 | Self {
27 | id,
28 | name,
29 | server_config,
30 | description: None,
31 | enabled_proxycast: false,
32 | enabled_claude: false,
33 | enabled_codex: false,
34 | enabled_gemini: false,
35 | created_at: Some(chrono::Utc::now().timestamp()),
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | echo "🔍 Running pre-commit checks..."
4 |
5 | # 前端检查
6 | echo "📦 Checking frontend..."
7 |
8 | # TypeScript 检查
9 | echo " → TypeScript check..."
10 | npx tsc --noEmit
11 | if [ $? -ne 0 ]; then
12 | echo "❌ TypeScript check failed!"
13 | exit 1
14 | fi
15 |
16 | # ESLint 检查
17 | echo " → ESLint check..."
18 | npm run lint
19 | if [ $? -ne 0 ]; then
20 | echo "❌ ESLint check failed!"
21 | exit 1
22 | fi
23 |
24 | # Prettier 检查
25 | echo " → Prettier check..."
26 | npx prettier --check "src/**/*.{ts,tsx,css}"
27 | if [ $? -ne 0 ]; then
28 | echo "❌ Prettier check failed! Run 'npm run format' to fix."
29 | exit 1
30 | fi
31 |
32 | # Rust 检查
33 | echo "🦀 Checking Rust..."
34 |
35 | # Rust 格式检查
36 | echo " → Rust format check..."
37 | cd src-tauri
38 | cargo fmt --all -- --check
39 | if [ $? -ne 0 ]; then
40 | echo "❌ Rust format check failed! Run 'cargo fmt' in src-tauri to fix."
41 | exit 1
42 | fi
43 |
44 | # Rust 编译检查 (不使用严格的 clippy)
45 | echo " → Rust build check..."
46 | cargo check --all-targets
47 | if [ $? -ne 0 ]; then
48 | echo "❌ Rust build check failed!"
49 | exit 1
50 | fi
51 |
52 | cd ..
53 |
54 | echo "✅ All pre-commit checks passed!"
55 |
--------------------------------------------------------------------------------
/src-tauri/src/models/app_type.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize, Serialize};
2 |
3 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4 | #[serde(rename_all = "lowercase")]
5 | pub enum AppType {
6 | ProxyCast,
7 | Claude,
8 | Codex,
9 | Gemini,
10 | }
11 |
12 | impl AppType {
13 | pub fn as_str(&self) -> &'static str {
14 | match self {
15 | AppType::ProxyCast => "proxycast",
16 | AppType::Claude => "claude",
17 | AppType::Codex => "codex",
18 | AppType::Gemini => "gemini",
19 | }
20 | }
21 | }
22 |
23 | impl std::str::FromStr for AppType {
24 | type Err = String;
25 |
26 | fn from_str(s: &str) -> Result {
27 | match s.to_lowercase().as_str() {
28 | "proxycast" => Ok(AppType::ProxyCast),
29 | "claude" => Ok(AppType::Claude),
30 | "codex" => Ok(AppType::Codex),
31 | "gemini" => Ok(AppType::Gemini),
32 | _ => Err(format!("Invalid app type: {s}")),
33 | }
34 | }
35 | }
36 |
37 | impl std::fmt::Display for AppType {
38 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 | write!(f, "{}", self.as_str())
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src-tauri/src/providers/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod antigravity;
2 | pub mod claude_custom;
3 | pub mod claude_oauth;
4 | pub mod codex;
5 | pub mod error;
6 | pub mod gemini;
7 | pub mod iflow;
8 | pub mod kiro;
9 | pub mod openai_custom;
10 | pub mod qwen;
11 | pub mod traits;
12 | pub mod vertex;
13 |
14 | #[cfg(test)]
15 | mod tests;
16 |
17 | // Trait exports
18 | #[allow(unused_imports)]
19 | pub use traits::{CredentialProvider, ProviderResult, TokenManager};
20 |
21 | #[allow(unused_imports)]
22 | pub use antigravity::AntigravityProvider;
23 | #[allow(unused_imports)]
24 | pub use claude_custom::ClaudeCustomProvider;
25 | #[allow(unused_imports)]
26 | pub use claude_oauth::ClaudeOAuthProvider;
27 | #[allow(unused_imports)]
28 | pub use codex::CodexProvider;
29 | #[allow(unused_imports)]
30 | pub use error::ProviderError;
31 | #[allow(unused_imports)]
32 | pub use gemini::{GeminiApiKeyCredential, GeminiApiKeyProvider, GeminiProvider};
33 | #[allow(unused_imports)]
34 | pub use iflow::IFlowProvider;
35 | #[allow(unused_imports)]
36 | pub use kiro::KiroProvider;
37 | #[allow(unused_imports)]
38 | pub use openai_custom::OpenAICustomProvider;
39 | #[allow(unused_imports)]
40 | pub use qwen::QwenProvider;
41 | #[allow(unused_imports)]
42 | pub use vertex::VertexProvider;
43 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from "@eslint/js";
2 | import tseslint from "@typescript-eslint/eslint-plugin";
3 | import tsparser from "@typescript-eslint/parser";
4 | import reactHooks from "eslint-plugin-react-hooks";
5 | import reactRefresh from "eslint-plugin-react-refresh";
6 | import globals from "globals";
7 |
8 | export default [
9 | { ignores: ["dist", "src-tauri", "node_modules"] },
10 | {
11 | files: ["**/*.{ts,tsx}"],
12 | languageOptions: {
13 | ecmaVersion: 2020,
14 | globals: globals.browser,
15 | parser: tsparser,
16 | parserOptions: {
17 | ecmaFeatures: { jsx: true },
18 | },
19 | },
20 | plugins: {
21 | "@typescript-eslint": tseslint,
22 | "react-hooks": reactHooks,
23 | "react-refresh": reactRefresh,
24 | },
25 | rules: {
26 | ...js.configs.recommended.rules,
27 | ...tseslint.configs.recommended.rules,
28 | ...reactHooks.configs.recommended.rules,
29 | "react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
30 | "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_" }],
31 | "@typescript-eslint/no-explicit-any": "off",
32 | },
33 | },
34 | ];
35 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 |
9 | env:
10 | CARGO_TERM_COLOR: always
11 |
12 | jobs:
13 | build-check:
14 | name: Build Check
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - name: Checkout
19 | uses: actions/checkout@v4
20 |
21 | - name: Setup Node.js
22 | uses: actions/setup-node@v4
23 | with:
24 | node-version: '20'
25 | cache: 'npm'
26 |
27 | - name: Setup Rust
28 | uses: dtolnay/rust-toolchain@stable
29 |
30 | - name: Setup Rust cache
31 | uses: Swatinem/rust-cache@v2
32 | with:
33 | workspaces: src-tauri
34 | shared-key: "rust-cache-build"
35 |
36 | - name: Install system dependencies (Linux)
37 | run: |
38 | sudo apt-get update
39 | sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev
40 |
41 | - name: Install frontend dependencies
42 | run: npm ci
43 |
44 | - name: Build frontend
45 | run: npm run build
46 |
47 | - name: Check Rust build
48 | working-directory: src-tauri
49 | run: cargo check --all-targets
50 |
--------------------------------------------------------------------------------
/src/lib/api/mcp.ts:
--------------------------------------------------------------------------------
1 | import { invoke } from "@tauri-apps/api/core";
2 |
3 | export interface McpServer {
4 | id: string;
5 | name: string;
6 | server_config: {
7 | command: string;
8 | args?: string[];
9 | env?: Record;
10 | };
11 | description?: string;
12 | enabled_proxycast: boolean;
13 | enabled_claude: boolean;
14 | enabled_codex: boolean;
15 | enabled_gemini: boolean;
16 | created_at?: number;
17 | }
18 |
19 | export const mcpApi = {
20 | getServers: (): Promise => invoke("get_mcp_servers"),
21 |
22 | addServer: (server: McpServer): Promise =>
23 | invoke("add_mcp_server", { server }),
24 |
25 | updateServer: (server: McpServer): Promise =>
26 | invoke("update_mcp_server", { server }),
27 |
28 | deleteServer: (id: string): Promise =>
29 | invoke("delete_mcp_server", { id }),
30 |
31 | toggleServer: (
32 | id: string,
33 | appType: string,
34 | enabled: boolean,
35 | ): Promise => invoke("toggle_mcp_server", { id, appType, enabled }),
36 |
37 | /** 从外部应用导入 MCP 配置 */
38 | importFromApp: (appType: string): Promise =>
39 | invoke("import_mcp_from_app", { appType }),
40 |
41 | /** 同步所有 MCP 配置到实际配置文件 */
42 | syncAllToLive: (): Promise => invoke("sync_all_mcp_to_live"),
43 | };
44 |
--------------------------------------------------------------------------------
/src-tauri/src/streaming/mod.rs:
--------------------------------------------------------------------------------
1 | //! 流式传输核心模块
2 | //!
3 | //! 该模块提供真正的端到端流式传输支持,将当前的"伪流式"架构改造为
4 | //! 逐 chunk 处理和实时 Flow 监控的真正流式传输。
5 | //!
6 | //! # 主要组件
7 | //!
8 | //! - `error`: 流式错误类型定义
9 | //! - `metrics`: 流式指标类型定义
10 | //! - `aws_parser`: AWS Event Stream 解析器(用于 Kiro/CodeWhisperer)
11 | //! - `converter`: 流式格式转换器
12 | //! - `traits`: StreamingProvider trait 定义
13 | //! - `manager`: 流式管理器
14 |
15 | pub mod aws_parser;
16 | pub mod converter;
17 | pub mod error;
18 | pub mod manager;
19 | pub mod metrics;
20 | pub mod traits;
21 |
22 | // 重新导出核心类型
23 | pub use aws_parser::{
24 | extract_content, extract_tool_calls, serialize_event, AwsEvent, AwsEventStreamParser,
25 | ParserState,
26 | };
27 | pub use converter::{
28 | extract_content_from_sse, extract_tool_calls_from_sse, ConverterState, PartialJsonAccumulator,
29 | StreamConverter, StreamFormat,
30 | };
31 | pub use error::StreamError;
32 | pub use manager::{
33 | collect_stream_content, create_flow_monitor_callback, with_timeout, FlowMonitorCallback,
34 | ManagedStream, ManagedStreamWithCallback, StreamConfig, StreamContext, StreamEvent,
35 | StreamManager, TimeoutStream,
36 | };
37 | pub use metrics::StreamMetrics;
38 | pub use traits::{
39 | reqwest_stream_to_stream_response, StreamFormat as TraitsStreamFormat, StreamResponse,
40 | StreamingProvider,
41 | };
42 |
--------------------------------------------------------------------------------
/docs/content/02.user-guide/1.dashboard.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 仪表盘
3 | description: 监控和控制代理服务
4 | navigation:
5 | icon: i-heroicons-chart-bar
6 | ---
7 |
8 | # 仪表盘
9 |
10 | 仪表盘是 ProxyCast 的主界面,提供服务状态监控和快速操作入口。
11 |
12 | ## 服务状态
13 |
14 | 仪表盘顶部显示当前服务状态:
15 |
16 | | 状态 | 说明 |
17 | |------|------|
18 | | 🟢 运行中 | API Server 正在运行,可以接收请求 |
19 | | 🔴 已停止 | API Server 未启动 |
20 | | 🟡 启动中 | 服务正在初始化 |
21 |
22 | ## 控制按钮
23 |
24 | - **启动服务**: 启动 API Server
25 | - **停止服务**: 停止 API Server
26 | - **重启服务**: 重新启动服务
27 |
28 | ## API 信息
29 |
30 | 服务运行时显示:
31 |
32 | - **API 地址**: 本地 API 端点(如 `http://127.0.0.1:9090`)
33 | - **API Key**: 当前配置的访问密钥
34 | - **复制按钮**: 一键复制 API 地址或 Key
35 |
36 | ## API 测试面板
37 |
38 | 内置的 API 测试工具:
39 |
40 | 1. **消息输入**: 输入测试消息
41 | 2. **模型选择**: 选择要使用的模型
42 | 3. **发送请求**: 点击发送测试请求
43 | 4. **响应显示**: 查看 AI 响应结果
44 |
45 | ### 测试示例
46 |
47 | ```
48 | 用户: Hello, how are you?
49 | 助手: I'm doing well, thank you for asking! How can I help you today?
50 | ```
51 |
52 | ## 请求统计
53 |
54 | 实时统计信息:
55 |
56 | - **总请求数**: 累计处理的请求数量
57 | - **成功率**: 请求成功百分比
58 | - **平均延迟**: 请求平均响应时间
59 | - **Token 使用**: 累计 Token 消耗
60 |
61 | ## 凭证状态
62 |
63 | 显示当前可用的凭证:
64 |
65 | | 字段 | 说明 |
66 | |------|------|
67 | | Provider | 凭证类型 |
68 | | 状态 | 有效/过期/错误 |
69 | | 剩余额度 | 可用额度(如支持) |
70 |
71 | ## 快捷操作
72 |
73 | - **打开设置**: 进入设置页面
74 | - **查看日志**: 打开请求日志
75 | - **刷新凭证**: 重新加载凭证文件
76 |
--------------------------------------------------------------------------------
/src/components/provider-pool/credential-forms/ModeSelector.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * 登录/文件导入模式选择器
3 | */
4 |
5 | import { LogIn, FolderOpen } from "lucide-react";
6 |
7 | interface ModeSelectorProps {
8 | mode: "login" | "file";
9 | setMode: (mode: "login" | "file") => void;
10 | loginLabel?: string;
11 | fileLabel?: string;
12 | }
13 |
14 | export function ModeSelector({
15 | mode,
16 | setMode,
17 | loginLabel = "登录",
18 | fileLabel = "导入文件",
19 | }: ModeSelectorProps) {
20 | return (
21 |
22 |
34 |
46 |
47 | );
48 | }
49 |
--------------------------------------------------------------------------------
/docs/content/01.introduction/2.installation.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 安装指南
3 | description: 下载并安装 ProxyCast
4 | navigation:
5 | icon: i-heroicons-arrow-down-tray
6 | ---
7 |
8 | # 安装指南
9 |
10 | ## 系统要求
11 |
12 | | 平台 | 最低版本 | 架构 |
13 | |------|----------|------|
14 | | macOS | 11.0 (Big Sur) | Apple Silicon (arm64) |
15 | | Windows | 10 | x64 |
16 |
17 | ## 下载
18 |
19 | 从 GitHub Tags 下载最新版本:
20 |
21 | [下载 ProxyCast](https://github.com/aiclientproxy/proxycast/tags)
22 |
23 | ### 安装包
24 |
25 | | 平台 | 文件名 | 说明 |
26 | |------|--------|------|
27 | | macOS | `ProxyCast_x.x.x_aarch64.dmg` | Apple Silicon Mac |
28 | | Windows | `ProxyCast_x.x.x_x64-setup.exe` | Windows 64位 |
29 |
30 | ## macOS 安装
31 |
32 | 1. 下载 `.dmg` 文件
33 | 2. 双击打开 DMG 镜像
34 | 3. 将 ProxyCast 拖入 Applications 文件夹
35 | 4. 首次运行时,右键点击应用选择"打开"(绕过 Gatekeeper)
36 |
37 | ::alert{type="info"}
38 | 如果提示"无法验证开发者",请在系统偏好设置 > 安全性与隐私中点击"仍要打开"。
39 | ::
40 |
41 | ## Windows 安装
42 |
43 | 1. 下载 `.exe` 安装程序
44 | 2. 双击运行安装程序
45 | 3. 按照安装向导完成安装
46 | 4. 从开始菜单启动 ProxyCast
47 |
48 | ## 验证安装
49 |
50 | 启动 ProxyCast 后,你应该看到:
51 |
52 | 1. 系统托盘图标出现
53 | 2. 主窗口显示仪表盘
54 | 3. 服务状态显示"已停止"(首次启动)
55 |
56 | ## 常见安装问题
57 |
58 | ### macOS: "应用已损坏"
59 |
60 | ```bash
61 | # 移除隔离属性
62 | xattr -cr /Applications/ProxyCast.app
63 | ```
64 |
65 | ### Windows: SmartScreen 警告
66 |
67 | 点击"更多信息" > "仍要运行"即可继续安装。
68 |
69 | ## 下一步
70 |
71 | 安装完成后,继续阅读 [快速开始](/introduction/quickstart) 配置你的第一个 Provider。
72 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src-tauri/src/database/migration.rs:
--------------------------------------------------------------------------------
1 | use rusqlite::Connection;
2 | use serde_json::Value;
3 |
4 | /// 从旧的 JSON 配置迁移数据到 SQLite
5 | #[allow(dead_code)]
6 | pub fn migrate_from_json(
7 | conn: &Connection,
8 | ) -> Result<(), Box> {
9 | // 检查是否已经迁移过
10 | let migrated: bool = conn
11 | .query_row(
12 | "SELECT value FROM settings WHERE key = 'migrated_from_json'",
13 | [],
14 | |row| row.get::<_, String>(0),
15 | )
16 | .map(|v| v == "true")
17 | .unwrap_or(false);
18 |
19 | if migrated {
20 | return Ok(());
21 | }
22 |
23 | // 读取旧配置文件
24 | let home = dirs::home_dir().ok_or("Cannot find home directory")?;
25 | let config_path = home.join(".proxycast").join("config.json");
26 |
27 | if config_path.exists() {
28 | let content = std::fs::read_to_string(&config_path)?;
29 | let _config: Value = serde_json::from_str(&content)?;
30 |
31 | // TODO: 解析旧配置并插入到数据库
32 | // 这里需要根据实际的旧配置格式来实现
33 |
34 | // 备份旧配置
35 | let backup_path = home.join(".proxycast").join("config.json.backup");
36 | std::fs::copy(&config_path, &backup_path)?;
37 | }
38 |
39 | // 标记迁移完成
40 | conn.execute(
41 | "INSERT OR REPLACE INTO settings (key, value) VALUES ('migrated_from_json', 'true')",
42 | [],
43 | )?;
44 |
45 | Ok(())
46 | }
47 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
4 | theme: {
5 | extend: {
6 | colors: {
7 | border: "hsl(var(--border))",
8 | background: "hsl(var(--background))",
9 | foreground: "hsl(var(--foreground))",
10 | primary: {
11 | DEFAULT: "hsl(var(--primary))",
12 | foreground: "hsl(var(--primary-foreground))",
13 | },
14 | secondary: {
15 | DEFAULT: "hsl(var(--secondary))",
16 | foreground: "hsl(var(--secondary-foreground))",
17 | },
18 | muted: {
19 | DEFAULT: "hsl(var(--muted))",
20 | foreground: "hsl(var(--muted-foreground))",
21 | },
22 | accent: {
23 | DEFAULT: "hsl(var(--accent))",
24 | foreground: "hsl(var(--accent-foreground))",
25 | },
26 | destructive: {
27 | DEFAULT: "hsl(var(--destructive))",
28 | foreground: "hsl(var(--destructive-foreground))",
29 | },
30 | card: {
31 | DEFAULT: "hsl(var(--card))",
32 | foreground: "hsl(var(--card-foreground))",
33 | },
34 | },
35 | borderRadius: {
36 | lg: "var(--radius)",
37 | md: "calc(var(--radius) - 2px)",
38 | sm: "calc(var(--radius) - 4px)",
39 | },
40 | },
41 | },
42 | plugins: [],
43 | };
44 |
--------------------------------------------------------------------------------
/src/lib/api/resilience.ts:
--------------------------------------------------------------------------------
1 | import { invoke } from "@tauri-apps/api/core";
2 |
3 | // Retry configuration
4 | export interface RetryConfig {
5 | max_retries: number;
6 | base_delay_ms: number;
7 | max_delay_ms: number;
8 | retryable_codes: number[];
9 | }
10 |
11 | // Failover configuration
12 | export interface FailoverConfig {
13 | auto_switch: boolean;
14 | switch_on_quota: boolean;
15 | }
16 |
17 | // Switch log entry
18 | export interface SwitchLogEntry {
19 | from_provider: string;
20 | to_provider: string;
21 | failure_type: string;
22 | timestamp: string;
23 | }
24 |
25 | export const resilienceApi = {
26 | // Retry config
27 | async getRetryConfig(): Promise {
28 | return invoke("get_retry_config");
29 | },
30 |
31 | async updateRetryConfig(config: RetryConfig): Promise {
32 | return invoke("update_retry_config", { config });
33 | },
34 |
35 | // Failover config
36 | async getFailoverConfig(): Promise {
37 | return invoke("get_failover_config");
38 | },
39 |
40 | async updateFailoverConfig(config: FailoverConfig): Promise {
41 | return invoke("update_failover_config", { config });
42 | },
43 |
44 | // Switch log
45 | async getSwitchLog(): Promise {
46 | return invoke("get_switch_log");
47 | },
48 |
49 | async clearSwitchLog(): Promise {
50 | return invoke("clear_switch_log");
51 | },
52 | };
53 |
--------------------------------------------------------------------------------
/src/lib/api/skills.ts:
--------------------------------------------------------------------------------
1 | import { invoke } from "@tauri-apps/api/core";
2 |
3 | export interface Skill {
4 | key: string;
5 | name: string;
6 | description: string;
7 | directory: string;
8 | readmeUrl?: string;
9 | installed: boolean;
10 | repoOwner?: string;
11 | repoName?: string;
12 | repoBranch?: string;
13 | }
14 |
15 | export interface SkillRepo {
16 | owner: string;
17 | name: string;
18 | branch: string;
19 | enabled: boolean;
20 | }
21 |
22 | export type AppType = "claude" | "codex" | "gemini";
23 |
24 | export const skillsApi = {
25 | async getAll(app: AppType = "claude"): Promise {
26 | return invoke("get_skills_for_app", { app });
27 | },
28 |
29 | async install(directory: string, app: AppType = "claude"): Promise {
30 | return invoke("install_skill_for_app", { app, directory });
31 | },
32 |
33 | async uninstall(
34 | directory: string,
35 | app: AppType = "claude",
36 | ): Promise {
37 | return invoke("uninstall_skill_for_app", { app, directory });
38 | },
39 |
40 | async getRepos(): Promise {
41 | return invoke("get_skill_repos");
42 | },
43 |
44 | async addRepo(repo: SkillRepo): Promise {
45 | return invoke("add_skill_repo", { repo });
46 | },
47 |
48 | async removeRepo(owner: string, name: string): Promise {
49 | return invoke("remove_skill_repo", { owner, name });
50 | },
51 | };
52 |
--------------------------------------------------------------------------------
/src-tauri/src/commands/mcp_cmd.rs:
--------------------------------------------------------------------------------
1 | use crate::database::DbConnection;
2 | use crate::models::McpServer;
3 | use crate::services::mcp_service::McpService;
4 | use tauri::State;
5 |
6 | #[tauri::command]
7 | pub fn get_mcp_servers(db: State<'_, DbConnection>) -> Result, String> {
8 | McpService::get_all(&db)
9 | }
10 |
11 | #[tauri::command]
12 | pub fn add_mcp_server(db: State<'_, DbConnection>, server: McpServer) -> Result<(), String> {
13 | McpService::add(&db, server)
14 | }
15 |
16 | #[tauri::command]
17 | pub fn update_mcp_server(db: State<'_, DbConnection>, server: McpServer) -> Result<(), String> {
18 | McpService::update(&db, server)
19 | }
20 |
21 | #[tauri::command]
22 | pub fn delete_mcp_server(db: State<'_, DbConnection>, id: String) -> Result<(), String> {
23 | McpService::delete(&db, &id)
24 | }
25 |
26 | #[tauri::command]
27 | pub fn toggle_mcp_server(
28 | db: State<'_, DbConnection>,
29 | id: String,
30 | app_type: String,
31 | enabled: bool,
32 | ) -> Result<(), String> {
33 | McpService::toggle_enabled(&db, &id, &app_type, enabled)
34 | }
35 |
36 | #[tauri::command]
37 | pub fn import_mcp_from_app(db: State<'_, DbConnection>, app_type: String) -> Result {
38 | McpService::import_from_app(&db, &app_type)
39 | }
40 |
41 | #[tauri::command]
42 | pub fn sync_all_mcp_to_live(db: State<'_, DbConnection>) -> Result<(), String> {
43 | McpService::sync_all_to_live(&db)
44 | }
45 |
--------------------------------------------------------------------------------
/src-tauri/src/models/provider_model.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize, Serialize};
2 | use serde_json::Value;
3 |
4 | #[derive(Debug, Clone, Serialize, Deserialize)]
5 | pub struct Provider {
6 | pub id: String,
7 | pub app_type: String,
8 | pub name: String,
9 | pub settings_config: Value,
10 | #[serde(skip_serializing_if = "Option::is_none")]
11 | pub category: Option,
12 | #[serde(skip_serializing_if = "Option::is_none")]
13 | pub icon: Option,
14 | #[serde(skip_serializing_if = "Option::is_none")]
15 | pub icon_color: Option,
16 | #[serde(skip_serializing_if = "Option::is_none")]
17 | pub notes: Option,
18 | #[serde(skip_serializing_if = "Option::is_none")]
19 | pub created_at: Option,
20 | #[serde(skip_serializing_if = "Option::is_none")]
21 | pub sort_index: Option,
22 | #[serde(default)]
23 | pub is_current: bool,
24 | }
25 |
26 | impl Provider {
27 | #[allow(dead_code)]
28 | pub fn new(id: String, app_type: String, name: String, settings_config: Value) -> Self {
29 | Self {
30 | id,
31 | app_type,
32 | name,
33 | settings_config,
34 | category: None,
35 | icon: None,
36 | icon_color: None,
37 | notes: None,
38 | created_at: Some(chrono::Utc::now().timestamp()),
39 | sort_index: None,
40 | is_current: false,
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/lib/api/switch.ts:
--------------------------------------------------------------------------------
1 | import { invoke } from "@tauri-apps/api/core";
2 |
3 | export interface Provider {
4 | id: string;
5 | app_type: string;
6 | name: string;
7 | settings_config: Record;
8 | category?: string;
9 | icon?: string;
10 | icon_color?: string;
11 | notes?: string;
12 | created_at?: number;
13 | sort_index?: number;
14 | is_current: boolean;
15 | }
16 |
17 | // proxycast 保留用于内部配置存储,但不在 UI 的 Tab 中显示
18 | export type AppType = "claude" | "codex" | "gemini" | "proxycast";
19 |
20 | export const switchApi = {
21 | getProviders: (appType: AppType): Promise =>
22 | invoke("get_switch_providers", { appType }),
23 |
24 | getCurrentProvider: (appType: AppType): Promise =>
25 | invoke("get_current_switch_provider", { appType }),
26 |
27 | addProvider: (provider: Provider): Promise =>
28 | invoke("add_switch_provider", { provider }),
29 |
30 | updateProvider: (provider: Provider): Promise =>
31 | invoke("update_switch_provider", { provider }),
32 |
33 | deleteProvider: (appType: AppType, id: string): Promise =>
34 | invoke("delete_switch_provider", { appType, id }),
35 |
36 | switchProvider: (appType: AppType, id: string): Promise =>
37 | invoke("switch_provider", { appType, id }),
38 |
39 | /** 读取当前生效的配置(从实际配置文件读取) */
40 | readLiveSettings: (appType: AppType): Promise> =>
41 | invoke("read_live_provider_settings", { appType }),
42 | };
43 |
--------------------------------------------------------------------------------
/src/components/clients/ClientsPage.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { AppType } from "@/lib/api/switch";
3 | import { AppTabs } from "./AppTabs";
4 | import { ProviderList } from "./ProviderList";
5 | import { HelpTip } from "@/components/HelpTip";
6 |
7 | interface ClientsPageProps {
8 | hideHeader?: boolean;
9 | }
10 |
11 | export function ClientsPage({ hideHeader = false }: ClientsPageProps) {
12 | const [activeApp, setActiveApp] = useState("claude");
13 |
14 | return (
15 |
16 | {!hideHeader && (
17 |
18 |
配置切换
19 |
20 | 一键切换 Claude Code / Codex / Gemini CLI 的 API 配置,快速在不同
21 | Provider 间切换
22 |
23 |
24 | )}
25 |
26 |
27 |
28 | 添加名为 "ProxyCast" 的 Provider
29 | 后,可将凭证池中的凭证(Kiro/Gemini/Claude 等)转换为标准
30 | OpenAI/Anthropic API, 供 Claude Code、Codex、Cherry Studio
31 | 等工具使用。配置 API 地址为{" "}
32 |
33 | http://localhost:8999
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/src/lib/api/injection.ts:
--------------------------------------------------------------------------------
1 | import { invoke } from "@tauri-apps/api/core";
2 |
3 | // Injection mode
4 | export type InjectionMode = "merge" | "override";
5 |
6 | // Injection rule
7 | export interface InjectionRule {
8 | id: string;
9 | pattern: string;
10 | parameters: Record;
11 | mode: InjectionMode;
12 | priority: number;
13 | enabled: boolean;
14 | }
15 |
16 | // Injection configuration
17 | export interface InjectionConfig {
18 | enabled: boolean;
19 | rules: InjectionRule[];
20 | }
21 |
22 | export const injectionApi = {
23 | // Get injection configuration
24 | async getInjectionConfig(): Promise {
25 | return invoke("get_injection_config");
26 | },
27 |
28 | // Set injection enabled
29 | async setInjectionEnabled(enabled: boolean): Promise {
30 | return invoke("set_injection_enabled", { enabled });
31 | },
32 |
33 | // Add injection rule
34 | async addInjectionRule(rule: InjectionRule): Promise {
35 | return invoke("add_injection_rule", { rule });
36 | },
37 |
38 | // Remove injection rule
39 | async removeInjectionRule(id: string): Promise {
40 | return invoke("remove_injection_rule", { id });
41 | },
42 |
43 | // Update injection rule
44 | async updateInjectionRule(id: string, rule: InjectionRule): Promise {
45 | return invoke("update_injection_rule", { id, rule });
46 | },
47 |
48 | // Get all injection rules
49 | async getInjectionRules(): Promise {
50 | return invoke("get_injection_rules");
51 | },
52 | };
53 |
--------------------------------------------------------------------------------
/src/components/clients/AppTabs.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils";
2 | import { AppType } from "@/lib/api/switch";
3 | import { ProviderIcon } from "@/icons/providers";
4 |
5 | interface AppTabsProps {
6 | activeApp: AppType;
7 | onAppChange: (app: AppType) => void;
8 | }
9 |
10 | const apps: {
11 | id: AppType;
12 | label: string;
13 | description: string;
14 | iconType: string;
15 | }[] = [
16 | {
17 | id: "claude",
18 | label: "Claude Code",
19 | description: "Claude CLI 配置",
20 | iconType: "claude",
21 | },
22 | {
23 | id: "codex",
24 | label: "Codex",
25 | description: "OpenAI Codex CLI",
26 | iconType: "openai",
27 | },
28 | {
29 | id: "gemini",
30 | label: "Gemini",
31 | description: "Google Gemini CLI",
32 | iconType: "gemini",
33 | },
34 | ];
35 |
36 | export function AppTabs({ activeApp, onAppChange }: AppTabsProps) {
37 | return (
38 |
39 | {apps.map((app) => (
40 |
54 | ))}
55 |
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/switch/AppTabs.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils";
2 | import { AppType } from "@/lib/api/switch";
3 | import { ProviderIcon } from "@/icons/providers";
4 |
5 | interface AppTabsProps {
6 | activeApp: AppType;
7 | onAppChange: (app: AppType) => void;
8 | }
9 |
10 | const apps: {
11 | id: AppType;
12 | label: string;
13 | description: string;
14 | iconType: string;
15 | }[] = [
16 | {
17 | id: "claude",
18 | label: "Claude Code",
19 | description: "Claude CLI 配置",
20 | iconType: "claude",
21 | },
22 | {
23 | id: "codex",
24 | label: "Codex",
25 | description: "OpenAI Codex CLI",
26 | iconType: "openai",
27 | },
28 | {
29 | id: "gemini",
30 | label: "Gemini",
31 | description: "Google Gemini CLI",
32 | iconType: "gemini",
33 | },
34 | ];
35 |
36 | export function AppTabs({ activeApp, onAppChange }: AppTabsProps) {
37 | return (
38 |
39 | {apps.map((app) => (
40 |
54 | ))}
55 |
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/src/icons/providers/openai.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 222.2 84% 4.9%;
9 | --card: 0 0% 100%;
10 | --card-foreground: 222.2 84% 4.9%;
11 | --primary: 222.2 47.4% 11.2%;
12 | --primary-foreground: 210 40% 98%;
13 | --secondary: 210 40% 96.1%;
14 | --secondary-foreground: 222.2 47.4% 11.2%;
15 | --muted: 210 40% 96.1%;
16 | --muted-foreground: 215.4 16.3% 46.9%;
17 | --accent: 210 40% 96.1%;
18 | --accent-foreground: 222.2 47.4% 11.2%;
19 | --destructive: 0 84.2% 60.2%;
20 | --destructive-foreground: 210 40% 98%;
21 | --border: 214.3 31.8% 91.4%;
22 | --radius: 0.5rem;
23 | }
24 |
25 | .dark {
26 | --background: 222.2 84% 4.9%;
27 | --foreground: 210 40% 98%;
28 | --card: 222.2 84% 4.9%;
29 | --card-foreground: 210 40% 98%;
30 | --primary: 210 40% 98%;
31 | --primary-foreground: 222.2 47.4% 11.2%;
32 | --secondary: 217.2 32.6% 17.5%;
33 | --secondary-foreground: 210 40% 98%;
34 | --muted: 217.2 32.6% 17.5%;
35 | --muted-foreground: 215 20.2% 65.1%;
36 | --accent: 217.2 32.6% 17.5%;
37 | --accent-foreground: 210 40% 98%;
38 | --destructive: 0 62.8% 30.6%;
39 | --destructive-foreground: 210 40% 98%;
40 | --border: 217.2 32.6% 17.5%;
41 | }
42 | }
43 |
44 | @layer base {
45 | * {
46 | @apply border-border;
47 | }
48 | body {
49 | @apply bg-background text-foreground;
50 | font-family:
51 | -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
52 | Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/docs/content/02.user-guide/2.monitoring.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 监控中心
3 | description: 请求统计和性能监控
4 | navigation:
5 | icon: i-heroicons-eye
6 | ---
7 |
8 | # 监控中心
9 |
10 | 监控中心提供详细的请求统计、性能指标和日志查看功能。
11 |
12 | ## 监控界面
13 |
14 | ### 概览面板
15 |
16 | 显示关键指标的实时数据:
17 |
18 | - **请求总数**: 今日/本周/本月请求量
19 | - **成功率**: 请求成功百分比
20 | - **平均延迟**: 响应时间统计
21 | - **活跃凭证**: 当前可用凭证数量
22 |
23 | ### 图表展示
24 |
25 | - **请求趋势图**: 按时间显示请求量变化
26 | - **延迟分布图**: 响应时间分布
27 | - **Provider 使用占比**: 各 Provider 请求比例
28 |
29 | ## 请求统计
30 |
31 | ### 按 Provider 统计
32 |
33 | | Provider | 请求数 | 成功率 | 平均延迟 |
34 | |----------|--------|--------|----------|
35 | | Kiro Claude | 1,234 | 99.2% | 1.2s |
36 | | Gemini CLI | 567 | 98.5% | 0.8s |
37 | | Qwen | 890 | 99.0% | 1.0s |
38 |
39 | ### 按模型统计
40 |
41 | 查看每个模型的使用情况:
42 |
43 | - 请求次数
44 | - Token 消耗
45 | - 平均响应时间
46 |
47 | ## Token 使用追踪
48 |
49 | ### 使用量统计
50 |
51 | | 指标 | 说明 |
52 | |------|------|
53 | | 输入 Token | 请求消息的 Token 数 |
54 | | 输出 Token | 响应消息的 Token 数 |
55 | | 总计 Token | 输入 + 输出 |
56 |
57 | ### 按时间段查看
58 |
59 | - 今日使用量
60 | - 本周使用量
61 | - 本月使用量
62 | - 自定义时间范围
63 |
64 | ## 请求日志
65 |
66 | ### 日志列表
67 |
68 | 每条日志包含:
69 |
70 | | 字段 | 说明 |
71 | |------|------|
72 | | 时间 | 请求时间戳 |
73 | | 模型 | 请求的模型名称 |
74 | | Provider | 实际使用的 Provider |
75 | | 状态 | 成功/失败 |
76 | | 延迟 | 响应时间 |
77 | | Token | Token 使用量 |
78 |
79 | ### 日志过滤
80 |
81 | 支持按以下条件过滤:
82 |
83 | - 时间范围
84 | - Provider
85 | - 模型
86 | - 状态(成功/失败)
87 |
88 | ### 日志详情
89 |
90 | 点击日志条目查看详细信息:
91 |
92 | - 完整请求内容
93 | - 完整响应内容
94 | - 错误信息(如有)
95 | - 请求头信息
96 |
97 | ## 导出数据
98 |
99 | 支持导出统计数据:
100 |
101 | - CSV 格式
102 | - JSON 格式
103 | - 自定义时间范围
104 |
--------------------------------------------------------------------------------
/src/components/config/ConfigManagementPage.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { Monitor, FileCode } from "lucide-react";
3 | import { cn } from "@/lib/utils";
4 | import { ClientsPage } from "../clients/ClientsPage";
5 | import { ConfigPage } from "./ConfigPage";
6 |
7 | type Tab = "switch" | "config";
8 |
9 | const tabs = [
10 | { id: "switch" as Tab, label: "配置切换", icon: Monitor },
11 | { id: "config" as Tab, label: "配置文件", icon: FileCode },
12 | ];
13 |
14 | export function ConfigManagementPage() {
15 | const [activeTab, setActiveTab] = useState("switch");
16 |
17 | return (
18 |
19 |
20 |
配置管理
21 |
管理客户端配置和配置文件
22 |
23 |
24 | {/* Tab 切换 */}
25 |
26 | {tabs.map((tab) => (
27 |
40 | ))}
41 |
42 |
43 | {/* Tab 内容 */}
44 |
45 | {activeTab === "switch" && }
46 | {activeTab === "config" && }
47 |
48 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/provider-pool/credential-forms/types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 凭证表单共享类型定义
3 | */
4 |
5 | import { PoolProviderType } from "@/lib/api/providerPool";
6 |
7 | /** OAuth 登录表单的通用 Props */
8 | export interface OAuthLoginFormProps {
9 | name: string;
10 | loading: boolean;
11 | error: string | null;
12 | onSuccess: () => void;
13 | setLoading: (loading: boolean) => void;
14 | setError: (error: string | null) => void;
15 | }
16 |
17 | /** 文件导入表单的通用 Props */
18 | export interface FileImportFormProps {
19 | credsFilePath: string;
20 | setCredsFilePath: (path: string) => void;
21 | projectId?: string;
22 | setProjectId?: (id: string) => void;
23 | onSelectFile: () => void;
24 | defaultPathHint?: string;
25 | fileHint?: string;
26 | }
27 |
28 | /** API Key 表单的通用 Props */
29 | export interface ApiKeyFormProps {
30 | apiKey: string;
31 | setApiKey: (key: string) => void;
32 | baseUrl: string;
33 | setBaseUrl: (url: string) => void;
34 | providerType: PoolProviderType;
35 | }
36 |
37 | /** 默认凭证文件路径 */
38 | export const defaultCredsPath: Record = {
39 | kiro: "~/.aws/sso/cache/kiro-auth-token.json",
40 | gemini: "~/.gemini/oauth_creds.json",
41 | qwen: "~/.qwen/oauth_creds.json",
42 | antigravity: "",
43 | codex: "~/.codex/auth.json",
44 | claude_oauth: "~/.claude/oauth.json",
45 | iflow: "~/.iflow/oauth_creds.json",
46 | };
47 |
48 | /** Provider 显示名称 */
49 | export const providerLabels: Record = {
50 | kiro: "Kiro (AWS)",
51 | gemini: "Gemini (Google)",
52 | qwen: "Qwen (阿里)",
53 | openai: "OpenAI",
54 | claude: "Claude (Anthropic)",
55 | antigravity: "Antigravity (Gemini 3 Pro)",
56 | codex: "Codex (OpenAI OAuth)",
57 | claude_oauth: "Claude OAuth",
58 | iflow: "iFlow",
59 | };
60 |
--------------------------------------------------------------------------------
/docs/content/01.introduction/1.overview.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 概述
3 | description: ProxyCast 项目介绍和核心价值
4 | navigation:
5 | icon: i-heroicons-home
6 | ---
7 |
8 | # ProxyCast 概述
9 |
10 | ProxyCast 是一款基于 Tauri 2.0 的跨平台桌面应用,让你可以**把 AI 客户端的订阅额度用到任何地方**。
11 |
12 | ::alert{type="warning"}
13 | **免责声明**: 本工具仅限于个人合法使用,严禁用于非法盈利目的。初衷是帮助用户充分利用已订阅的 AI 服务 Token。[查看完整声明](/legal/disclaimer)
14 | ::
15 |
16 | ## 核心价值
17 |
18 | 你是否有以下困扰?
19 |
20 | - 订阅了 Kiro、Claude Code 等 AI 编程助手,但只能在特定 IDE 中使用
21 | - 想在其他工具(如 Cursor、Continue、自定义脚本)中使用已有的 AI 额度
22 | - 需要管理多个 AI 服务的凭证,频繁切换很麻烦
23 |
24 | ProxyCast 解决这些问题:将你的 AI 客户端凭证转换为标准的 OpenAI/Claude 兼容 API,让任何支持 OpenAI 接口的工具都能使用你的订阅额度。
25 |
26 | ## 支持的 Provider
27 |
28 | | Provider | 类型 | 认证方式 | 说明 |
29 | |----------|------|----------|------|
30 | | Kiro Claude | OAuth | 自动刷新 | AWS Kiro IDE 的 Claude 凭证 |
31 | | Gemini CLI | OAuth | 自动刷新 | Google Gemini CLI 凭证 |
32 | | Qwen (通义千问) | OAuth | 自动刷新 | 阿里云通义千问凭证 |
33 | | OpenAI Custom | API Key | 手动配置 | 自定义 OpenAI 兼容服务 |
34 | | Claude Custom | API Key | 手动配置 | 自定义 Claude 兼容服务 |
35 |
36 | ## 核心特性
37 |
38 | ### 🔑 凭证池管理
39 | - 支持多个 Provider 凭证的统一管理
40 | - 自动检测和加载本地凭证文件
41 | - OAuth Token 自动刷新机制
42 |
43 | ### ⚖️ 智能路由
44 | - 基于模型名称的请求路由
45 | - 负载均衡和优先级配置
46 | - 健康检查和自动故障转移
47 |
48 | ### 🛡️ 容错机制
49 | - 可配置的重试策略
50 | - 超时控制和熔断器
51 | - 多 Provider 故障转移
52 |
53 | ### 🔄 协议转换
54 | - OpenAI Chat Completions API 兼容
55 | - Claude Messages API 兼容
56 | - 自动格式转换
57 |
58 | ### 📊 监控统计
59 | - 实时请求统计
60 | - Token 使用追踪
61 | - 详细的请求日志
62 |
63 | ## 使用场景
64 |
65 | 1. **IDE 集成**: 在 Cursor、Continue 等编辑器中使用 Kiro/Claude Code 额度
66 | 2. **脚本调用**: 在 Python/Node.js 脚本中调用 AI API
67 | 3. **多账户管理**: 统一管理多个 AI 服务账户
68 | 4. **团队共享**: 通过配置导出分享 Provider 设置
69 |
70 | ## 下一步
71 |
72 | - [安装指南](/introduction/installation) - 下载并安装 ProxyCast
73 | - [快速开始](/introduction/quickstart) - 5 分钟内完成首次 API 调用
74 |
--------------------------------------------------------------------------------
/src-tauri/src/commands/switch_cmd.rs:
--------------------------------------------------------------------------------
1 | use crate::database::DbConnection;
2 | use crate::models::Provider;
3 | use crate::services::switch::SwitchService;
4 | use serde_json::Value;
5 | use tauri::State;
6 |
7 | #[tauri::command]
8 | pub fn get_switch_providers(
9 | db: State<'_, DbConnection>,
10 | app_type: String,
11 | ) -> Result, String> {
12 | SwitchService::get_providers(&db, &app_type)
13 | }
14 |
15 | #[tauri::command]
16 | pub fn get_current_switch_provider(
17 | db: State<'_, DbConnection>,
18 | app_type: String,
19 | ) -> Result