mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-25 13:45:03 +08:00
8f7423f011
* feat(deeplink): support comma-separated multiple endpoints in URL Allow importing multiple API endpoints via single endpoint parameter. First URL becomes primary endpoint, rest are added as custom endpoints. * feat(deeplink): add usage query fields to deeplink generator Add form fields for usage query configuration in deeplink HTML generator: - usageEnabled, usageBaseUrl, usageApiKey - usageScript, usageAutoInterval - usageAccessToken, usageUserId * fix(deeplink): auto-infer homepage and improve multi-endpoint display - Auto-infer homepage from primary endpoint when not provided - Display multiple endpoints as list in import dialog (primary marked) - Update deeplink parser in deplink.html to show multi-endpoint info - Add test for homepage inference from endpoint - Minor log format fix in live.rs * fix(deeplink): use primary endpoint for usage script base_url - Fix usage_script.base_url getting comma-separated string when multiple endpoints - Add i18n support for primary endpoint label in DeepLinkImportDialog
140 lines
5.3 KiB
Rust
140 lines
5.3 KiB
Rust
//! Deep link import functionality for CC Switch
|
|
//!
|
|
//! This module implements the ccswitch:// protocol for importing configurations
|
|
//! via deep links. Supports importing:
|
|
//! - Provider configurations (Claude/Codex/Gemini)
|
|
//! - MCP server configurations
|
|
//! - Prompts
|
|
//! - Skills
|
|
//!
|
|
//! See docs/ccswitch-deeplink-design.md for detailed design.
|
|
|
|
mod mcp;
|
|
mod parser;
|
|
mod prompt;
|
|
mod provider;
|
|
mod skill;
|
|
mod utils;
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
// Re-export public API
|
|
pub use mcp::import_mcp_from_deeplink;
|
|
pub use parser::parse_deeplink_url;
|
|
pub use prompt::import_prompt_from_deeplink;
|
|
pub use provider::{import_provider_from_deeplink, parse_and_merge_config};
|
|
pub use skill::import_skill_from_deeplink;
|
|
|
|
/// Deep link import request model
|
|
///
|
|
/// Represents a parsed ccswitch:// URL ready for processing.
|
|
/// This struct contains all possible fields for all resource types.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct DeepLinkImportRequest {
|
|
/// Protocol version (e.g., "v1")
|
|
pub version: String,
|
|
/// Resource type to import: "provider" | "prompt" | "mcp" | "skill"
|
|
pub resource: String,
|
|
|
|
// ============ Common fields ============
|
|
/// Target application (claude/codex/gemini) - for provider, prompt, skill
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub app: Option<String>,
|
|
/// Resource name
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub name: Option<String>,
|
|
/// Whether to enable after import (default: false)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub enabled: Option<bool>,
|
|
|
|
// ============ Provider-specific fields ============
|
|
/// Provider homepage URL
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub homepage: Option<String>,
|
|
/// API endpoint/base URL (supports comma-separated multiple URLs)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub endpoint: Option<String>,
|
|
/// API key
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub api_key: Option<String>,
|
|
/// Optional provider icon name (maps to built-in SVG)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub icon: Option<String>,
|
|
/// Optional model name
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub model: Option<String>,
|
|
/// Optional notes/description
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub notes: Option<String>,
|
|
/// Optional Haiku model (Claude only, v3.7.1+)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub haiku_model: Option<String>,
|
|
/// Optional Sonnet model (Claude only, v3.7.1+)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub sonnet_model: Option<String>,
|
|
/// Optional Opus model (Claude only, v3.7.1+)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub opus_model: Option<String>,
|
|
|
|
// ============ Prompt-specific fields ============
|
|
/// Base64 encoded Markdown content
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub content: Option<String>,
|
|
/// Prompt description
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub description: Option<String>,
|
|
|
|
// ============ MCP-specific fields ============
|
|
/// Target applications for MCP (comma-separated: "claude,codex,gemini")
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub apps: Option<String>,
|
|
|
|
// ============ Skill-specific fields ============
|
|
/// GitHub repository (format: "owner/name")
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub repo: Option<String>,
|
|
/// Skill directory name
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub directory: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub branch: Option<String>,
|
|
|
|
// ============ Config file fields (v3.8+) ============
|
|
/// Base64 encoded config content
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub config: Option<String>,
|
|
/// Config format (json/toml)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub config_format: Option<String>,
|
|
/// Remote config URL
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub config_url: Option<String>,
|
|
|
|
// ============ Usage script fields (v3.9+) ============
|
|
/// Whether to enable usage query (default: true if usage_script is provided)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub usage_enabled: Option<bool>,
|
|
/// Base64 encoded usage query script code
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub usage_script: Option<String>,
|
|
/// Usage query API key (if different from provider API key)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub usage_api_key: Option<String>,
|
|
/// Usage query base URL (if different from provider endpoint)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub usage_base_url: Option<String>,
|
|
/// Usage query access token (for NewAPI template)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub usage_access_token: Option<String>,
|
|
/// Usage query user ID (for NewAPI template)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub usage_user_id: Option<String>,
|
|
/// Auto query interval in minutes (0 to disable)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub usage_auto_interval: Option<u64>,
|
|
}
|