Commit Graph

278 Commits

  • feat(pricing): add GPT-5 Codex model pricing presets
    Add pricing configuration for GPT-5 Codex variants to support cost tracking for Codex-specific models.
    
    Changes:
    - Add gpt-5-codex model with standard GPT-5 pricing
    - Add gpt-5-1-codex model with standard GPT-5.1 pricing
    - Input: $1.25/M tokens, Output: $10/M tokens
    - Cache read: $0.125/M tokens, Cache creation: $0
    
    This ensures accurate cost calculation for Codex API requests using GPT-5 Codex models.
  • style(rust): apply clippy formatting suggestions
    Apply automatic clippy fixes for uninlined_format_args warnings across Rust codebase. Replace format string placeholders with inline variable syntax for improved readability.
    
    Changes:
    - Convert format!("{}", var) to format!("{var}")
    - Apply to model_test.rs, parser.rs, and usage_stats.rs
    - Fix line length issues by breaking long function calls
    - Improve code formatting consistency
    
    All changes are automatic formatting with no functional impact.
  • fix(pricing): standardize model ID format for pricing lookup
    Normalize model IDs by removing vendor prefixes and converting dots to hyphens to ensure consistent pricing lookups across different API response formats.
    
    Changes:
    - Update seed data to use hyphen format (e.g., gpt-5-1, gemini-2-5-pro)
    - Add normalize_model_id() function to strip vendor prefixes (anthropic/, openai/)
    - Convert dots to hyphens in model IDs (claude-haiku-4.5 → claude-haiku-4-5)
    - Try both original and normalized IDs for exact matching
    - Use normalized ID for suffix-based fallback matching
    - Add comprehensive test cases for prefix and dot handling
    - Add warning log when no pricing found
    
    This ensures pricing lookups work correctly for:
    - Models with vendor prefixes: anthropic/claude-haiku-4.5
    - Models with dots in version: claude-sonnet-4.5
    - Models with date suffixes: claude-haiku-4-5-20240229
  • fix(proxy): resolve token parsing for OpenRouter streaming responses
    Problem:
    - OpenRouter and similar third-party services return streaming responses
      where input_tokens appear in message_delta instead of message_start
    - The previous implementation only extracted input_tokens from message_start,
      causing input_tokens to be recorded as 0 for these providers
    
    Changes:
    - streaming.rs: Add prompt_tokens field to Usage struct and include
      input_tokens in the transformed message_delta event when converting
      OpenAI format to Anthropic format
    - parser.rs: Update from_claude_stream_events() to handle input_tokens
      from both message_start (native Claude API) and message_delta (OpenRouter)
      - Use if-let pattern instead of direct unwrap for safer parsing
      - Only update input_tokens from message_delta if not already set
    - logger.rs: Adjust test parameters to match updated function signature
    
    Tests:
    - Add test_openrouter_stream_parsing() for OpenRouter format validation
    - Add test_native_claude_stream_parsing() for native Claude API validation
  • feat(model-test): add provider model availability testing
    Implement standalone model testing feature to verify provider API connectivity:
    - Add ModelTestService for Claude/Codex/Gemini endpoint testing
    - Create model_test_logs table for test result persistence
    - Add test button to ProviderCard with loading state
    - Include ModelTestConfigPanel for customizing test parameters
  • feat(services): add pagination and enhanced filtering for request logs
    Usage stats service:
    - Change get_request_logs() from limit/offset to page/page_size pagination
    - Return PaginatedLogs with total count, page, and page_size
    - Add appType and providerName filters with LIKE search
    - Add is_streaming, first_token_ms, duration_ms to RequestLogDetail
    - Join with providers table for provider name lookup
    
    Commands:
    - Update get_request_logs command signature for pagination params
    
    Module exports:
    - Export PaginatedLogs struct
  • feat(proxy): enhance proxy server with session tracking and OpenAI route
    Error handling:
    - Add StreamIdleTimeout and AuthError variants for better error classification
    
    Module exports:
    - Export ResponseType, StreamHandler, NonStreamHandler from response_handler
    - Export ProxySession, ClientFormat from session module
    
    Server routing:
    - Add /v1/chat/completions route for OpenAI Chat Completions API
    
    Handlers:
    - Add log_usage_with_session() for enhanced usage tracking with session context
    - Add first_token_ms timing measurement for streaming responses
    - Use SseUsageCollector with start_time for accurate latency calculation
    - Track is_streaming flag in usage logs
  • feat(proxy/usage): enhance token parser and logger for multi-format support
    Parser enhancements:
    - Add OpenAI Chat Completions format parsing (prompt_tokens/completion_tokens)
    - Add model field to TokenUsage for actual model name extraction
    - Add from_codex_response_adjusted() for proper cache token handling
    - Add debug logging for better stream event tracing
    
    Logger enhancements:
    - Add first_token_ms, provider_type, is_streaming, cost_multiplier fields
    - Extend RequestLog struct with full metadata tracking
    - Update log_with_calculation() signature for new fields
    
    Calculator: Update tests with model field in TokenUsage.
  • feat(database): extend schema with streaming and timing fields
    Add new columns to proxy_request_logs table for enhanced usage tracking:
    - first_token_ms and duration_ms for performance metrics
    - provider_type and is_streaming for request classification
    - cost_multiplier for flexible pricing
    
    Update model pricing with accurate rates for Claude/GPT/Gemini models.
    Add ensure_model_pricing_seeded() call on database initialization.
    Add test for model pricing auto-seeding verification.
  • feat(proxy): add ProviderType enum for fine-grained provider detection
    Introduce ProviderType enum to distinguish between different provider
    implementations (Claude, ClaudeAuth, Codex, Gemini, GeminiCli, OpenRouter).
    This enables proper authentication handling and request transformation
    based on the actual provider type rather than just AppType.
    
    - Add ProviderType enum with detection logic from config
    - Enhance Claude adapter with OpenRouter detection
    - Enhance Gemini adapter with CLI mode detection
    - Add helper methods for provider type inference
  • deps: add recharts for charts and rust_decimal/uuid for usage tracking
    - recharts: Chart visualization for usage trends
    - rust_decimal: Precise cost calculations
    - uuid: Request ID generation
  • feat(commands): add usage statistics Tauri commands
    Register usage commands for summary, trends, logs, and pricing.
    Expose usage stats service through Tauri command layer.
  • feat(proxy): integrate usage logging into request handlers
    Add usage logging to forwarder and streaming handlers.
    Track token usage and costs for each proxy request.
  • feat(proxy): implement usage tracking subsystem
    Add request logger with automatic cost calculation.
    Implement token parser for Claude/OpenAI/Gemini responses.
    Add cost calculator based on model pricing configuration.
  • feat(db): add usage tracking schema and types
    Add database tables for proxy request logs and model pricing.
    Extend Provider and error types to support usage statistics.
  • feat(proxy): add streaming SSE transform and thinking parameter support
    New features:
    - Add OpenAI → Anthropic SSE streaming response transformation
    - Support thinking parameter detection for reasoning model selection
    - Add ANTHROPIC_REASONING_MODEL config option for extended thinking
    
    Changes:
    - streaming.rs: Implement SSE event parsing and Anthropic format conversion
    - transform.rs: Add thinking detection logic and reasoning model mapping
    - handlers.rs: Integrate streaming transform for OpenRouter compatibility
    - Cargo.toml: Add async-stream and bytes dependencies
  • feat(proxy): implement provider adapter pattern with OpenRouter support
    This major refactoring introduces a modular provider adapter architecture
    to support format transformation between different AI API formats.
    
    New features:
    - Add ProviderAdapter trait for unified provider abstraction
    - Implement Claude, Codex, and Gemini adapters with specific logic
    - Add Anthropic ↔ OpenAI format transformation for OpenRouter compatibility
    - Support model mapping from provider configuration (ANTHROPIC_MODEL, etc.)
    - Add OpenRouter preset to Claude provider presets
    
    Refactoring:
    - Extract authentication logic into auth.rs with AuthInfo and AuthStrategy
    - Move URL building and request transformation to individual adapters
    - Simplify ProviderRouter to only use proxy target providers
    - Refactor RequestForwarder to use adapter-based request/response handling
    - Use whitelist mode for header forwarding (only pass necessary headers)
    
    Architecture:
    - providers/adapter.rs: ProviderAdapter trait definition
    - providers/auth.rs: AuthInfo, AuthStrategy types
    - providers/claude.rs: Claude adapter with OpenRouter detection
    - providers/codex.rs: Codex (OpenAI) adapter
    - providers/gemini.rs: Gemini (Google) adapter
    - providers/models/: Anthropic and OpenAI API data models
    - providers/transform.rs: Bidirectional format transformation
  • fix(proxy): resolve clippy warnings for dead code and uninlined format args
    - Add #[allow(dead_code)] to unused ProviderUnhealthy variant
    - Inline format string arguments in handlers.rs and codex.rs log macros
    - Refactor error response handling to properly pass through upstream errors
    - Add URL deduplication logic for /v1/v1 paths in CodexAdapter
  • refactor(proxy): remove unused request handlers and routes
    - Remove unused GET/DELETE request forwarding methods
    - Remove count_tokens, get/delete response handlers
    - Simplify router by removing unused endpoints
    - Keep only essential routes: /v1/messages, /v1/responses, /v1beta/*
  • fix(proxy): improve URL building and Gemini request handling
    - Refactor URL construction with version path deduplication (/v1, /v1beta)
    - Preserve query parameters for Gemini API requests
    - Support GOOGLE_GEMINI_API_KEY field name (with fallback)
    - Change default proxy port from 5000 to 15721
    - Fix test: use Option type for is_proxy_target field
  • fix(proxy): resolve UI/UX issues and database constraint error
    Simplify proxy control interface and fix database persistence issues:
    
    Backend Fixes:
    - Fix NOT NULL constraint error in proxy_config.created_at field
      * Use COALESCE to preserve created_at on updates
      * Ensure proper INSERT OR REPLACE behavior
    - Remove redundant enabled field validation on startup
      * Auto-enable when user clicks start button
      * Persist enabled state after successful start
    - Preserve enabled state during config updates
      * Prevent accidental service shutdown on config save
    
    Frontend Improvements:
    - Remove duplicate proxy enable switch from settings dialog
      * Keep only runtime toggle in ProxyPanel
      * Simplify user experience with single control point
    - Hide proxy target button when proxy service is stopped
      * Add isProxyRunning prop to ProviderCard
      * Conditionally render proxy controls based on service status
    - Update form schema to omit enabled field
      * Managed automatically by backend
    
    Files: 5 changed, 81 insertions(+), 94 deletions(-)
  • feat(proxy): implement local HTTP proxy server with multi-provider failover
    Add a complete HTTP proxy server implementation built on Axum framework,
    enabling local API request forwarding with automatic provider failover
    and load balancing capabilities.
    
    Backend Implementation (Rust):
    - Add proxy server module with 7 core components:
      * server.rs: Axum HTTP server lifecycle management (start/stop/status)
      * router.rs: API routing configuration for Claude/OpenAI/Gemini endpoints
      * handlers.rs: Request/response handling and transformation
      * forwarder.rs: Upstream forwarding logic with retry mechanism (652 lines)
      * error.rs: Comprehensive error handling and HTTP status mapping
      * types.rs: Shared types (ProxyConfig, ProxyStatus, ProxyServerInfo)
      * health.rs: Provider health check infrastructure
    
    Service Layer:
    - Add ProxyService (services/proxy.rs, 157 lines):
      * Manage proxy server lifecycle
      * Handle configuration updates
      * Track runtime status and metrics
    
    Database Layer:
    - Add proxy configuration DAO (dao/proxy.rs, 242 lines):
      * Persist proxy settings (listen address, port, timeout)
      * Store provider priority and availability flags
    - Update schema with proxy_config table (schema.rs):
      * Support runtime configuration persistence
    
    Tauri Commands:
    - Add 6 command endpoints (commands/proxy.rs):
      * start_proxy_server: Launch proxy server
      * stop_proxy_server: Gracefully shutdown server
      * get_proxy_status: Query runtime status
      * get_proxy_config: Retrieve current configuration
      * update_proxy_config: Modify settings without restart
      * is_proxy_running: Check server state
    
    Frontend Implementation (React + TypeScript):
    - Add ProxyPanel component (222 lines):
      * Real-time server status display
      * Start/stop controls
      * Provider availability monitoring
    - Add ProxySettingsDialog component (420 lines):
      * Configuration editor (address, port, timeout)
      * Provider priority management
      * Settings validation
    - Add React hooks:
      * useProxyConfig: Manage proxy configuration state
      * useProxyStatus: Poll and display server status
    - Add TypeScript types (types/proxy.ts):
      * Define ProxyConfig, ProxyStatus interfaces
    
    Provider Integration:
    - Extend Provider model with availability field (providers.rs):
      * Track provider health for failover logic
    - Update ProviderCard UI to display proxy status
    - Integrate proxy controls in Settings page
    
    Dependencies:
    - Add Axum 0.7 (async web framework)
    - Add Tower 0.4 (middleware and service abstractions)
    - Add Tower-HTTP (CORS layer)
    - Add Tokio sync primitives (oneshot, RwLock)
    
    Technical Details:
    - Graceful shutdown via oneshot channel
    - Shared state with Arc<RwLock<T>> for thread-safe config updates
    - CORS enabled for cross-origin frontend access
    - Request/response streaming support
    - Automatic retry with exponential backoff (forwarder)
    - API key extraction from multiple config formats (Claude/Codex/Gemini)
    
    File Statistics:
    - 41 files changed
    - 3491 insertions(+), 41 deletions(-)
    - Core modules: 1393 lines (server + forwarder + handlers)
    - Frontend UI: 642 lines (ProxyPanel + ProxySettingsDialog)
    - Database/DAO: 326 lines
    
    This implementation provides the foundation for advanced features like:
    - Multi-provider load balancing
    - Automatic failover on provider errors
    - Request logging and analytics
    - Usage tracking and cost monitoring
  • style: apply code formatting fixes
    - Fix Prettier formatting in claudeProviderPresets.ts
    - Fix cargo fmt trailing blank line in provider/mod.rs
  • chore: remove dead code from MCP and provider modules
    - Remove unused `read_mcp_json` from gemini_mcp.rs
    - Remove unused `normalize_server_keys` from mcp/claude.rs
    - Remove unused `validate_mcp_entry` from mcp/validation.rs
    - Remove unused `write_codex_live`, `write_claude_live`, `app_not_found` from services/provider/mod.rs
    - Clean up unused imports
  • refactor(mcp): modularize MCP and tray menu logic
    Split mcp.rs (1135 lines) into focused modules:
    - validation.rs: server config validation
    - claude.rs: Claude MCP sync/import
    - codex.rs: Codex MCP sync (TOML handling)
    - gemini.rs: Gemini MCP sync/import
    
    Extract tray menu logic from lib.rs to tray.rs for better separation of concerns.
  • fix(auto-launch): use AutoLaunchBuilder for cross-platform compatibility
    The auto-launch crate has different API signatures on each platform:
    - Windows/Linux: AutoLaunch::new() takes 3 arguments
    - macOS: AutoLaunch::new() takes 4 arguments (includes hidden param)
    
    The previous code used #[cfg(not(target_os = "windows"))] which incorrectly
    applied macOS's 4-argument signature to Linux, causing build failures.
    
    Switch to AutoLaunchBuilder which handles platform differences internally.
  • refactor(skill): remove skillsPath configuration (#310)
    Remove the skillsPath field from SkillRepo and Skill structs since
    recursive scanning now automatically discovers skills in all directories.
    Simplify the UI by removing the path input field.
  • fix: pre-release fixes for code formatting, i18n, and test suite
    - Run cargo fmt to fix Rust code formatting (lib.rs)
    - Add missing i18n keys: migration.success, agents.title (zh/en/ja)
    - Replace hardcoded strings "Agents" and "MCP" with t() calls in App.tsx
    - Fix test mocks and assertions:
      - Add providersApi.updateTrayMenu to useSettings.test.tsx mock
      - Update SettingsPage mock path in App.test.tsx
      - Fix toast message assertion in integration/SettingsDialog.test.tsx
      - Add autoSaveSettings to SettingsDialog component test mock
      - Fix loading state test to check spinner instead of title
      - Update import button name matching for selected file state
      - Fix save button test to switch to advanced tab first
      - Remove obsolete cancel button test (button no longer exists)
    
    Test results improved from 99 passed / 17 failed to 104 passed / 11 failed
  • feat(i18n): add Japanese language support
    - Add complete Japanese translation file (ja.json)
    - Update frontend types and hooks to support "ja" language option
    - Add Japanese tray menu texts in Rust backend
    - Add Japanese option to language settings component
    - Update Zod schema to include Japanese language validation
    - Add test case for Japanese language preference
    - Update i18n documentation to reflect three-language support
  • refactor(startup): improve first-launch import logic with per-table detection
    - Replace global `is_empty_for_first_import()` with independent checks:
      - `is_mcp_table_empty()` for MCP server imports
      - `is_prompts_table_empty()` for prompt imports
      - Skills and providers already have built-in idempotency checks
    
    - Fix misleading logs in provider import:
      - Change `import_default_config` return type from `Result<()>` to `Result<bool>`
      - Return `true` when actually imported, `false` when skipped
      - Only log success message when import actually occurred
    
    - Add idempotency protection to `import_from_file_on_first_launch`
    
    This allows each data type to be independently recovered if deleted,
    rather than requiring all tables to be empty for any import to trigger.
  • feat(skill): implement recursive scanning for skill repositories (#309)
    Add recursive directory scanning to discover SKILL.md files in nested
    directories. When a SKILL.md is found, treat sibling directories as
    functional folders rather than separate skills.
  • fix(auto-launch): use platform-specific API for AutoLaunch::new
    Windows version of auto-launch crate takes 3 arguments while
    Linux/macOS takes 4 (includes hidden parameter). Use conditional
    compilation to handle the difference.
  • fix(linux): resolve WebKitGTK DMA-BUF rendering issue and preserve user .desktop customizations
    - Set WEBKIT_DISABLE_DMABUF_RENDERER=1 at startup to fix blank screen on Debian 13.2 and Nvidia GPUs
    - Only register deep-link handler on first run to avoid overwriting user customizations
    - Use correct Tauri path API (app.path().data_dir()) instead of dirs::data_dir() to match plugin's actual .desktop file location
  • feat(migration): add error dialog for config.json loading failure
    - Show system dialog when config.json fails to load during migration
    - User can retry loading or exit the program
    - Exit before database creation ensures clean retry on next launch
    - Support Chinese/English localization based on system locale
  • fix(provider): validate current provider ID existence before use
    Add get_effective_current_provider() to validate local settings ID
    against database, with automatic cleanup and fallback to DB is_current.
    
    This fixes edge cases in multi-device cloud sync scenarios where local
    settings may contain stale provider IDs:
    
    - current(): now returns validated effective provider ID
    - update(): correctly syncs live config when local ID differs from DB
    - delete(): checks both local settings and DB to prevent deletion
    - switch(): backfill logic now targets valid provider
    - sync_current_to_live(): uses validated ID with auto-fallback
    - tray menu: displays correct checkmark on startup
    
    Also fixes test issues:
    - Add missing test setup calls (mutex, reset_test_fs, ensure_test_home)
    - Correct Gemini security settings path to ~/.gemini/settings.json
  • refactor(provider): use local settings for current provider selection
    Complete the device-level settings separation for cloud sync support.
    
    Backend changes:
    - Modify switch() to update both local settings and database is_current
    - Modify current() to read from local settings first, fallback to database
    - Rename sync_current_from_db() to sync_current_to_live()
    - Update tray menu to read current provider from local settings
    
    Frontend changes:
    - Update Settings interface: remove legacy fields (customEndpoints*, security)
    - Add currentProviderClaude/Codex/Gemini fields
    - Update settings schema accordingly
    
    Test fixes:
    - Update Gemini security tests to check ~/.gemini/settings.json
      instead of ~/.cc-switch/settings.json (security field was never
      stored in CC Switch settings)
    
    This ensures each device maintains its own current provider selection
    independently when database is synced across devices.
  • refactor(settings): move device-level settings to local file storage
    Separate device-level settings from database to support cloud sync scenarios
    where multiple devices share the same database but need independent settings.
    
    Changes:
    - Remove database storage logic (bind_db, save_to_db, load_from_db)
    - Remove legacy custom_endpoints_claude/codex fields (actual data in provider.meta)
    - Add current_provider_claude/codex/gemini fields for device-level provider selection
    - Add get_current_provider() and set_current_provider() helper functions
    - Settings now stored only in ~/.cc-switch/settings.json
    
    This ensures that when database is synced across devices, each device
    maintains its own current provider selection independently.
  • refactor(gemini): improve config handling and code consistency
    - Fix envObjToString to preserve custom environment variables
    - Add bidirectional MCP format conversion (httpUrl <-> url)
    - Merge provider config with existing settings.json instead of overwriting
    - Remove redundant is_packycode_gemini function
    - Simplify is_google_official_gemini using detect_gemini_auth_type
    - Add handleGeminiModelChange for consistent field handling
  • fix(gemini): correctly write custom provider env to .env file
    write_live_snapshot was incorrectly passing the already-extracted env
    sub-field to json_to_env, which expects the full settings_config object.
    This caused json_to_env to look for env.env (nested), returning an empty
    HashMap and writing an empty .env file.
    
    Fix by delegating to write_gemini_live which correctly handles env file
    writing and security flag configuration in one place.
  • fix(gemini): write security auth config only to Gemini settings file
    - Remove redundant security.auth.selectedType from CC Switch settings
    - Fix Generic provider type not writing security flag on switch
    - All non-Google Official providers now correctly write "gemini-api-key"
    - Delete unused ensure_packycode_security_flag function
    - Clean up SecuritySettings and SecurityAuthSettings types from AppSettings
  • refactor(config): remove dead code from JSON-era import/export
    Remove obsolete methods that were superseded by SQLite-based
    import/export in v3.7.0:
    
    - export_config_to_path: replaced by Database::export_sql
    - load_config_for_import: no longer used
    - import_config_from_path: stub that only returned error
    
    Also remove unused AppState import.
  • fix(provider): preserve custom endpoints when updating provider
    - Replace INSERT OR REPLACE with UPDATE for existing providers to avoid
      triggering ON DELETE CASCADE on provider_endpoints table
    - Fix endpoint merging logic to correctly mark database endpoints as
      isCustom when they overlap with preset endpoints
    
    The root cause was that INSERT OR REPLACE performs DELETE + INSERT under
    the hood, which triggered the foreign key cascade and deleted all
    associated endpoints from provider_endpoints table.
  • feat(migration): enable silent JSON to SQLite migration with toast notification
    - Remove environment variable feature gate (CC_SWITCH_ENABLE_JSON_DB_MIGRATION)
    - Automatically migrate config.json to SQLite when db doesn't exist
    - Archive migrated config.json as config.json.migrated for recovery
    - Add migration success flag in init_status.rs (one-time consumption)
    - Add get_migration_result command for frontend to query
    - Show toast notification on successful migration in App.tsx
  • refactor(services): split monolithic provider.rs into modular structure
    Split the 1446-line services/provider.rs into 5 focused modules:
    
    - gemini_auth.rs (250 lines): Gemini authentication type detection
      - PackyCode, Google OAuth, and generic provider detection
      - Security flag management for different auth types
    
    - live.rs (300 lines): Live configuration operations
      - LiveSnapshot backup/restore
      - Reading and writing live config files
      - Sync current provider to live config
    
    - usage.rs (150 lines): Usage script execution
      - Query and test usage scripts
      - Format usage results
      - Validate usage script configuration
    
    - endpoints.rs (80 lines): Custom endpoints management
      - CRUD operations for provider custom endpoints
      - Last-used timestamp tracking
    
    - mod.rs (650 lines): Core provider CRUD and service facade
      - ProviderService struct with all public methods
      - Provider add/update/delete/switch operations
      - Claude model key normalization
      - Credential extraction and validation
    
    All 107 tests pass. This improves maintainability by:
    - Separating concerns into cohesive modules
    - Making Gemini-specific logic easier to find and modify
    - Reducing cognitive load when working on specific features