Commit Graph

1531 Commits

  • chore(release): consolidate v3.12.3 release notes, changelog and test fixes
    Merge previously unreleased v3.12.4 content into v3.12.3:
    - CHANGELOG: combine [Unreleased] into [3.12.3], clear [Unreleased]
    - Release notes (zh/en/ja): add Copilot proxy, macOS signing,
      Reasoning Effort, OpenCode SQLite, Codex 1M toggle, Disable
      Auto-Upgrade toggle, and contributor thanks
    - Fix test mocks for skill backup/restore hooks
    - Fix schema migration test missing providers table
    - Fix TempHome to save/restore CC_SWITCH_TEST_HOME env var
  • feat(claude): add "Disable Auto-Upgrade" checkbox to provider config editor
    Add a toggle for DISABLE_AUTOUPDATER env var in CommonConfigEditor,
    following the same pattern as the existing Teammates mode toggle.
  • feat(dmg): use create-dmg for styled macOS DMG installer
    Tauri's built-in DMG styling relies on AppleScript/Finder access which
    silently fails on CI (tauri-apps/tauri#1731). Switch to create-dmg tool
    which works on GitHub Actions macOS runners.
    
    - Replace Tauri DMG with create-dmg: background image, icon positions,
      app-drop-link, codesign, hide-extension
    - Regenerate background image at 2x Retina resolution (1320x800)
    - Revert tauri.conf.json dmg config (ineffective on CI)
    - Reorder steps: Prepare → Notarize DMG → Verify
    - Notarize and Verify now use release-assets/ path for DMG
  • feat(dmg): customize macOS DMG installer appearance
    - Add DMG background image with drag-to-install arrow guide
    - Configure window size (660x400), app and Applications icon positions
    - Center icons horizontally with visual arrow between them
  • fix(ci): add separate DMG notarization step and build retry for macOS
    Tauri only notarizes the .app bundle, not the DMG container. This caused
    stapler staple to fail with "Record not found" for the DMG.
    
    - Add "Notarize macOS DMG" step using xcrun notarytool with retry logic
    - Add retry logic (3 attempts) to macOS build step for transient network failures
    - Add hdiutil verify before DMG notarization submission
  • feat(ci): add macOS code signing and Apple notarization to release workflow
    - Import Developer ID Application certificate into temporary keychain
    - Inject APPLE_SIGNING_IDENTITY/APPLE_ID/APPLE_PASSWORD/APPLE_TEAM_ID
      into Tauri build step for automatic signing and notarization
    - Staple notarization tickets to both .app and .dmg (hard-fail)
    - Add verification step: codesign --verify + spctl -a + stapler validate
      for both .app and .dmg, gating the release on success
    - Collect .dmg alongside .tar.gz and .zip in release assets
    - Clean up temporary keychain with original default restored
    - Update release notes to recommend .dmg and note Apple notarization
    - Remove all xattr workarounds and "unidentified developer" warnings
      from README, README_ZH, installation guides, and FAQ (EN/ZH/JA)
  • fix: prevent WebDAV password from being silently cleared by unrelated saves
    Two components (ProviderList, UsageScriptModal) directly spread the full
    settings object from the query into settingsApi.save(), which includes
    webdavSync with an empty password (cleared by get_settings_for_frontend
    for security). The backend merge_settings_for_save only preserved
    existing WebDAV config when the incoming field was None, not when it was
    present with an empty password.
    
    Frontend fix: strip webdavSync before saving in both components, matching
    the pattern already used by useSettings hook.
    
    Backend defense-in-depth: merge_settings_for_save now backfills the
    existing password when the incoming one is empty, preventing future
    regressions from similar oversights.
  • feat(skills): 优化技能安装/卸载的缓存更新策略 (#1573)
    - 修改安装、卸载、导入、ZIP安装等操作的缓存更新逻辑,从invalidateQueries改为直接setQueryData
    - 为已安装和可发现技能查询添加keepPreviousData和staleTime: Infinity配置
    - 修复会话管理页面布局滚动问题,添加min-h-0防止内容溢出
  • fix: parse tool_use/tool_result messages and add OpenCode SQLite backend (#1401)
    * fix: parse tool_use/tool_result messages and add OpenCode SQLite backend
    
      - Claude: reclassify user messages containing tool_result as "tool" role
      - Codex: handle function_call and function_call_output payload types
      - Gemini: support array content and toolCalls extraction, filter info/error types
      - OpenCode: add SQLite session scan, load and delete alongside legacy JSON
      - utils: extend parse_timestamp_to_ms for integer timestamps, extract tool_use/tool_result in shared extract_text
    
    * fix: address remaining issues from tool_use/tool_result parsing commit
      - Claude: fix role misclassification for mixed user+tool_result messages (any → all)
      - OpenCode: extract duplicate part text logic into extract_part_text()
      - OpenCode: add path validation for SQLite delete to prevent foreign DB access
      - OpenCode: wrap SQLite deletion in transaction for atomicity
      - openclaw_config: remove redundant as_deref() on Option<&str>
  • feat(proxy): resolve reasoning_effort from explicit effort with budget fallback
    Replace map_thinking_to_reasoning_effort() with resolve_reasoning_effort()
    that uses a two-tier priority system:
    
    1. Explicit output_config.effort: low/medium/high map 1:1, max → xhigh
    2. Fallback: thinking.type + budget_tokens thresholds (<4k → low,
       4k-16k → medium, ≥16k → high, adaptive → high)
    
    Both Chat Completions and Responses API paths share the same helper,
    ensuring consistent mapping across all OpenAI-compatible endpoints.
  • chore: update Claude 4.6 context window to 1M (GA)
    Claude Opus 4.6 and Sonnet 4.6 1M context window is now GA and no
    longer requires a beta header. Update contextWindow from 200k to 1M
    for all OpenClaw/OpenCode presets (27 entries in OpenClaw, 1 in
    OpenCode Bedrock). Also add claude-sonnet-4-6 model pricing seed.
  • refactor(ui): remove duplicate OAuth tab from AddProviderDialog
    - Remove AuthCenterPanel import and OAuth TabsContent
    - Narrow activeTab type from three values to "app-specific" | "universal"
    - Simplify footer by removing oauth branch, reducing to two-way conditional
    - Change TabsList from grid-cols-3 to grid-cols-2
    - OAuth authentication remains available in settings page and CopilotAuthSection
  • fix(copilot): unify request fingerprint across all Copilot API calls
    - Make header constants in copilot_auth.rs public, add COPILOT_INTEGRATION_ID
    - Unify User-Agent across 4 internal methods (eliminate "CC-Switch" leakage) and version string
    - claude.rs add_auth_headers now references shared constants, adds user-agent / api-version / openai-intent
    - forwarder.rs filters all 6 fixed fingerprint headers for Copilot requests to prevent reqwest append duplication
    - stream_check.rs health check pipeline aligned with new fingerprint
  • feat(copilot): add GitHub Copilot reverse proxy support (#930)
    * refactor(toolsearch): replace binary patch with ENABLE_TOOL_SEARCH env var toggle
    
    - Remove toolsearch_patch.rs binary patching mechanism (~590 lines)
      - Delete `toolsearch_patch.rs` and `commands/toolsearch.rs`
      - Remove auto-patch startup logic and command registration from lib.rs
      - Remove `tool_search_bypass` field from settings.rs
      - Remove frontend settings ToggleRow, useSettings hook sync logic, and API methods
      - Clean up zh/en/ja i18n keys (notifications + settings)
    
    - Add ENABLE_TOOL_SEARCH toggle to Claude provider form
      - Add checkbox in CommonConfigEditor.tsx (alongside teammates toggle)
      - When enabled, writes `"env": { "ENABLE_TOOL_SEARCH": "true" }`
      - When disabled, removes the key; takes effect on provider switch
      - Add zh/en/ja i18n key: `claudeConfig.enableToolSearch`
    
    Claude Code 2.1.76+ natively supports this env var, eliminating the need for binary patching.
    
    * feat(claude): add effortLevel high toggle to provider form
    
    - Add "high-effort thinking" checkbox to Claude provider config form
    - When checked, writes `"effortLevel": "high"`; when unchecked, removes the field
    - Add zh/en/ja i18n translations
    
    * refactor(claude): remove deprecated alwaysThinking toggle
    
    - Claude Code now enables extended thinking by default; alwaysThinkingEnabled is a no-op
    - Thinking control is now handled via effortLevel (added in prior commit)
    - Remove state, switch case, and checkbox UI from CommonConfigEditor
    - Clean up alwaysThinking i18n keys across zh/en/ja locales
    
    * feat(opencode): add setCacheKey: true to all provider presets
    
    - Add setCacheKey: true to options in all 33 regular presets
    - Add setCacheKey: true to OPENCODE_DEFAULT_CONFIG for custom providers
    - Exclude 2 OMO presets (Oh My OpenCode / Slim) which have their own config mechanism
    
    Closes #1523
    
    * fix(codex): resolve 1M context window toggle causing MCP editor flicker
    
    - Add localValueRef to short-circuit duplicate CodeMirror updateListener callbacks,
      breaking the React state → CodeMirror → stale onChange → React state feedback loop
    - Use localValueRef.current in handleContextWindowToggle and handleCompactLimitChange
      to avoid stale closure reads
    - Change compact limit input from type="number" to type="text" with inputMode="numeric"
      to remove unnecessary spinner buttons
    
    * feat(codex): add 1M context window toggle utilities and i18n keys
    
    - Add extractCodexTopLevelInt, setCodexTopLevelInt, removeCodexTopLevelField
      TOML helpers in providerConfigUtils.ts
    - Add i18n keys for contextWindow1M, autoCompactLimit in zh/en/ja locales
    
    * feat(claude): collapse model mapping fields by default
    
    - Wrap 5 model mapping inputs in a Collapsible, collapsed by default
    - Auto-expand when any model value is present (including preset-filled)
    - Show hint text when collapsed explaining most users need no config
    - Add zh/en/ja i18n keys for toggle label and collapsed hint
    - Use variant={null} to avoid ghost button hover style clash in dark mode
    
    * feat(claude): merge advanced fields into single collapsible section
    
    - Merge API format, auth field, and model mapping into a unified "Advanced Options" collapsible
    - Extend smart-expand logic to detect non-default values across all advanced fields
    - Preserve model mapping sub-header and hint with a separator line
    - Update zh/en/ja i18n keys (advancedOptionsToggle, advancedOptionsHint, modelMappingLabel, modelMappingHint)
    
    * feat(copilot): add GitHub Copilot reverse proxy support
    
    Add GitHub Copilot as a Claude provider variant with OAuth device code
    authentication and Anthropic ↔ OpenAI format transformation.
    
    Backend:
    - Add CopilotAuthManager for GitHub OAuth device code flow
    - Implement Copilot token auto-refresh (60s before expiry)
    - Persist GitHub token to ~/.cc-switch/copilot_auth.json
    - Add ProviderType::GitHubCopilot and AuthStrategy::GitHubCopilot
    - Modify forwarder to use /chat/completions for Copilot
    - Add Copilot-specific headers (Editor-Version, Editor-Plugin-Version)
    
    Frontend:
    - Add CopilotAuthSection component for OAuth UI
    - Add useCopilotAuth hook for OAuth state management
    - Auto-copy user code to clipboard and open browser
    - Use 8-second polling interval to avoid GitHub rate limits
    - Skip API Key validation for Copilot providers
    - Add GitHub Copilot preset with claude-sonnet-4 model
    
    Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
    
    * fix(copilot): remove is_expired() calls from tests
    
    Remove references to deleted is_expired() method in test code.
    Only is_expiring_soon() is needed for token refresh logic.
    
    Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
    
    * feat(copilot): add real-time model listing from Copilot API
    
    - Add fetch_models() to CopilotAuthManager calling GET /models endpoint
    - Add copilot_get_models Tauri command
    - Add copilotGetModels() frontend API wrapper
    - Modify ClaudeFormFields to show model dropdown for Copilot providers
      - Fetches available models on component mount when isCopilotPreset
      - Groups models by vendor (Anthropic, OpenAI, Google, etc.)
      - Input + dropdown button combo allows both manual entry and selection
      - Non-Copilot providers keep original plain Input behavior
    
    Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
    
    * feat(copilot): add usage query integration
    
    - Add Copilot usage API integration (fetch_usage method)
    - Add copilot_get_usage Tauri command
    - Add GitHub Copilot template in usage query modal
    - Unify naming: copilot → github_copilot
    - Add constants management (TEMPLATE_TYPES, PROVIDER_TYPES)
    - Improve error handling with detailed error messages
    - Add database migration (v5 → v6) for template type update
    - Add i18n translations (zh, en, ja)
    - Improve type safety with TemplateType
    - Apply code formatting (cargo fmt, prettier)
    
    * 修复github 登录和注销问题 ,模型选择问题
    
    * feat(copilot): add multi-account support for GitHub Copilot
    
    - Add multi-account storage structure with v1 to v2 migration
    - Add per-account token caching and auto-refresh
    - Add new Tauri commands for account management
    - Integrate account selection in Proxy forwarder
    - Add account selection UI in CopilotAuthSection
    - Save githubAccountId to ProviderMeta
    - Add i18n translations for multi-account features (zh/en/ja)
    
    * 修复用量查询Reset字段出现多余字符
    
    * refactor(auth-binding): introduce generic provider auth binding primitives
    
    - add shared authBinding types in Rust and TypeScript while keeping githubAccountId as a compatibility field\n- resolve Copilot token, models, and usage through provider-bound account lookup instead of only the implicit default account\n- fix the Unix build regression in settings.rs by restoring std::io::Write for write_all()\n- remove the accidental .github ignore entry and drop leftover Copilot form debug logs\n- keep the first migration step non-breaking by writing both authBinding and the legacy githubAccountId field from the form
    
    * refactor(auth-service): add managed auth command surface and explicit default account state
    
    - introduce generic managed auth commands and frontend auth API wrappers for provider-scoped login, status, account listing, removal, logout, and default-account selection\n- store an explicit Copilot default_account_id instead of relying on HashMap iteration order, and use it consistently for fallback token/model/usage resolution\n- sort managed accounts deterministically and surface default-account state to the UI\n- refactor the Copilot form hook to wrap a generic useManagedAuth implementation while preserving the existing component contract\n- add default-account controls to the Copilot auth section and extend Copilot auth status serialization/tests for the new state
    
    * feat(auth-center): add a dedicated settings entrypoint for managed OAuth accounts
    
    - add an Auth Center tab to Settings so managed OAuth accounts are no longer hidden inside individual provider forms\n- introduce a first AuthCenterPanel that hosts GitHub Copilot account management as the initial managed auth provider\n- keep the provider form experience intact while establishing a global account-management surface for future providers such as OpenAI\n- validate that the new settings tab works cleanly with the generic managed auth hook and existing Copilot account controls
    
    * feat(add-provider): expose managed OAuth sources alongside universal providers
    
    - add an OAuth tab to the Add Provider flow so managed auth sources sit beside app-specific and universal providers\n- reuse the new Auth Center panel inside the dialog, keeping account management discoverable during provider creation\n- make the dialog footer adapt to the OAuth tab so account setup does not pretend to create a provider directly\n- align the add-provider UX with the new architecture where OAuth accounts are global assets and providers bind to them later
    
    * fix(auth-reliability): harden managed auth persistence and refresh behavior
    
    - replace direct Copilot auth store writes with private temp-file writes and atomic rename semantics, and document the local token storage limitation\n- add per-account refresh locks plus a double-check path so concurrent requests do not stampede GitHub token refresh\n- surface legacy migration failures through auth status, expose them in the UI, and add translated copy for the new account-state labels\n- stop writing the legacy githubAccountId field from the provider form while keeping compatibility reads in place\n- add logout error recovery and Copilot model-load toasts so auth failures are no longer silently swallowed
    
    * refactor(copilot-detection): prefer provider type before URL fallbacks
    
    - update forwarder endpoint rewriting to treat providerType as the primary GitHub Copilot signal\n- keep githubcopilot.com string matching only as a compatibility fallback for older provider records without providerType\n- reduce one more path where Copilot behavior depended purely on URL heuristics
    
    * fix(copilot-auth): add cancel button to error state in CopilotAuthSection
    
    - 错误状态下仅有"重试"按钮,用户无法退出(如不可恢复的 403 未订阅错误)
    - 新增"取消"按钮,复用已有的 cancelAuth 逻辑重置为 idle 状态
    
    * 修复打包后github账号头像显示异常
    
    * 修复github copilot 来源的模型测试报错
    
    * feat(copilot-preset): add default model presets for GitHub Copilot
    
    - 补充 Copilot 预设的默认模型配置,用户选完预设即可直接使用
    - ANTHROPIC_MODEL: claude-opus-4.6
    - ANTHROPIC_DEFAULT_HAIKU_MODEL: claude-haiku-4.5
    - ANTHROPIC_DEFAULT_SONNET_MODEL: claude-sonnet-4.6
    - ANTHROPIC_DEFAULT_OPUS_MODEL: claude-opus-4.6
    
    ---------
    
    Co-authored-by: Jason <farion1231@gmail.com>
    Co-authored-by: 周梦泽 <mengze.zhou@dafeng-tech.com>
    Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
  • chore(release): update release notes, changelog and macOS minimum version for v3.12.3
    Update CHANGELOG.md with full v3.12.3 entry, create release notes in
    three languages (en/zh/ja), bump macOS minimumSystemVersion from 10.15
    to 12.0 (Monterey) to match actual runtime requirements, and update
    README version badges and links.
  • fix(ui): replace hardcoded height calc with flex-1 to eliminate bottom blank area
    All content panels used h-[calc(100vh-8rem)] which didn't match the
    actual available space (CONTENT_TOP_OFFSET is 92px on Mac, 64px on
    Win/Linux, not 128px), causing a visible gap at the bottom.
    
    Replace with flex-1 min-h-0 to let panels fill the remaining space
    after <main> and any sibling elements (e.g. OpenClaw health banner).
  • feat(skills): add restore and delete for skill backups
    Introduce list/restore/delete commands for skill backups created during
    uninstall. Restore copies files back to SSOT, saves the DB record, and
    syncs to the current app with rollback on failure. Delete removes the
    backup directory after a confirmation dialog. ConfirmDialog gains a
    configurable zIndex prop to support nested dialog stacking.
  • feat(skills): auto-backup skill files before uninstall
    Create a local backup under ~/.cc-switch/skill-backups/ before removing
    skill directories. The backup includes all skill files and a meta.json
    with original skill metadata. Old backups are pruned to keep at most 20.
    The backup path is returned to the frontend and shown in the success
    toast. Bump version to 3.12.3.
  • fix(skills): add missing TooltipProvider in ImportSkillsDialog to prevent white screen
    The AppToggleGroup component added in 7097a0d7 uses Radix UI Tooltip
    which requires a TooltipProvider context. Without it, opening the
    import dialog crashes with a runtime error and renders a blank page.
  • fix(proxy): enable gzip compression for non-streaming proxy requests
    Non-streaming requests were forced to use `Accept-Encoding: identity`,
    preventing upstream response compression and increasing bandwidth usage.
    
    Now only streaming requests conservatively keep `identity` to avoid
    decompression errors on interrupted SSE streams. Non-streaming requests
    let reqwest auto-negotiate gzip and transparently decompress responses.
  • fix: revert incorrect o-series max_completion_tokens in Responses API path
    Responses API uses max_output_tokens for all models including o-series.
    The o-series max_completion_tokens fix should only apply to Chat Completions API.
  • fix: place OpenCode model variants at top level instead of inside options (#1317)
    Split the expanded model panel into two editing areas:
    - "Model Properties" for top-level fields (variants, cost, etc.)
    - "SDK Options" for model.options fields (provider routing, etc.)
    
    Also guard against renaming extra fields to reserved keys (name, limit,
    options) or duplicate names, and fix ModelOptionKeyInput blur desync when
    a rename is rejected by the parent handler.
  • fix(proxy): use max_completion_tokens for o1/o3 series models (#1451)
    * fix(proxy): use max_completion_tokens for o1/o3 series models
    
    When converting Anthropic requests to OpenAI format for o1/o3 series
    models (like o1-mini, o3-mini), use max_completion_tokens instead of
    max_tokens to avoid unsupported_parameter errors.
    
    Fixes #1448
    
    * fix: revert incorrect o-series max_completion_tokens in Responses API path
    
    Responses API uses max_output_tokens for all models including o-series.
    The o-series max_completion_tokens fix should only apply to Chat Completions API.
    
    ---------
    
    Co-authored-by: Hajen Teowideo <hajen.teowideo@example.com>
    Co-authored-by: Jason Young <44939412+farion1231@users.noreply.github.com>
    Co-authored-by: Jason <farion1231@gmail.com>
  • fix(provider-form): prevent duplicate submissions on rapid button clicks (#1352)
    Make ProviderForm.handleSubmit async and await onSubmit so react-hook-form's
    isSubmitting state is tracked. Disable submit buttons in AddProviderDialog and
    EditProviderDialog while submission is in flight via onSubmittingChange callback.
  • feat: add Tool Search domain restriction bypass with active-installation patching
    Resolve the active `claude` command from PATH and apply an equal-length
    byte patch to remove the domain whitelist check. Backups are stored in
    ~/.cc-switch/toolsearch-backups/ (SHA-256 of path) so they survive
    Claude Code version upgrades. The patch auto-reapplies on app startup
    when the setting is enabled.
    
    Frontend checks PatchResult.success and rolls back the setting on failure.
  • fix: replace implicit app inference with explicit selection for Skills import and sync
    Skills import previously inferred app enablement from filesystem presence,
    causing incorrect multi-app activation when the same skill directory existed
    under multiple app paths. Now the frontend submits explicit app selections
    via ImportSkillSelection, and schema migration preserves a snapshot of
    legacy app mappings to avoid lossy reconstruction.
    
    Also adds reconciliation to sync_to_app (removes disabled/orphaned symlinks)
    and MCP sync_all_enabled (removes disabled servers from live config).
  • feat: improve empty state guidance for first-run experience
    Show detailed import instructions and conditionally display common
    config snippet hint for Claude/Codex/Gemini (not OpenCode/OpenClaw).
  • fix: merge Codex MCP restore backup entries during takeover
    When proxy takeover is active, update_live_backup_from_provider rebuilds the Codex restore snapshot from the current provider and common-config state. We were then replacing the new mcp_servers table with the previous backup's table wholesale, which meant stopping takeover could restore stale MCP entries and silently discard MCP changes made through provider/common-config updates.
    
    Change the backup preservation step to merge MCP entries by server id instead of replacing the entire table. New provider/common-config MCP definitions now win on conflict, while backup-only servers are retained so live-only MCP state still survives a hot-switch.
    
    Add regression coverage for the existing preservation case and for the conflict case where both the old backup and the new provider define the same MCP server.
  • fix: make Codex TOML base_url editing section-aware
    Rewrite setCodexBaseUrl/extractCodexBaseUrl to understand TOML section
    boundaries, ensuring base_url is written into the correct
    [model_providers.<name>] section instead of being appended to file end.
    
    - Add section-aware TOML helpers in providerConfigUtils.ts
    - Extract shared update_codex_toml_field/remove_codex_toml_base_url_if
      in codex_config.rs, deduplicate proxy.rs TOML editing logic
    - Replace scattered inline base_url regexes with extractCodexBaseUrl()
    - Add comprehensive tests for both Rust and TypeScript implementations
  • fix: prevent common config loss during proxy takeover and stabilize snippet lifecycle
    - Make sync_current_provider_for_app takeover-aware: update restore
      backup instead of overwriting live config when proxy is active
    - Introduce explicit "cleared" flag for common config snippets to
      prevent auto-extraction from resurrecting user-cleared snippets
    - Reorder startup: extract snippets from clean live files before
      restoring proxy takeover state
    - Add one-time migration flag to skip legacy commonConfigEnabled
      migration on subsequent startups
    - Add regression tests for takeover backup preservation, explicit
      clear semantics, and migration flag roundtrip
  • Preserve common config during proxy takeover
    Update takeover backup generation to rebuild effective provider settings with common config applied before saving restore snapshots.
    
    Keep Codex mcp_servers entries when hot-switching providers under takeover so restore does not drop live-only MCP config.
    
    Migrate legacy providers with inferred common-config usage to explicit commonConfigEnabled=true markers during startup and default imports, and cover the new behavior with proxy and provider regression tests.
  • feat: add authHeader field to OpenClawProviderConfig and reuse type in form state
    Add optional `authHeader` boolean to support vendor-specific auth headers
    (e.g. Longcat). Refactor `resetOpenclawState` to use the shared
    `OpenClawProviderConfig` type instead of an inline duplicate definition.
  • feat: add SiliconFlow as sponsor and update affiliate links
    Replace SDS logos with SiliconFlow logos, add SiliconFlow sponsor entries
    to all three README files (en/zh/ja), fix incorrect alt attribute, and
    update all SiliconFlow provider preset apiKeyUrl to affiliate link.
  • fix: prevent common config modal infinite reopen loop and add draft editing
    The auto-open useEffect in CodexConfigEditor and GeminiConfigEditor
    created an inescapable loop: commonConfigError triggered modal open,
    closing the modal didn't clear the error, so the effect immediately
    reopened it — locking the entire UI.
    
    - Remove auto-open useEffect from both Codex and Gemini config editors
    - Convert common config modals to draft editing (edit locally, validate
      before save) instead of persisting on every keystroke
    - Add TOML/JSON pre-validation via parseCommonConfigSnippet before any
      merge operation to prevent invalid content from being persisted
    - Expose clearCommonConfigError so editors can clear stale errors on
      modal close