feat: add OpenClaw/Hermes/OpenCode proxy_config support and UI integration#1
feat: add OpenClaw/Hermes/OpenCode proxy_config support and UI integration#1dashitongzhi wants to merge 4 commits intomainfrom
Conversation
…r support - Expand proxy_config CHECK constraint to include opencode/openclaw/hermes - Add seed data for new app types with appropriate defaults - Add v10->v11 migration for existing databases - Update proxy.rs to read OpenClaw/OpenCode takeover status from DB - Bump SCHEMA_VERSION to 11
…out section - Add OpenClaw to ProxyPanel app takeover grid - Add OpenClaw to PricingConfigPanel apps list - Add opencode/openclaw to AppTypeFilter type - Add openclaw/hermes to AboutSection tool names
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 898641604d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "INSERT OR IGNORE INTO proxy_config (app_type, max_retries, | ||
| streaming_first_byte_timeout, streaming_idle_timeout, non_streaming_timeout, | ||
| circuit_failure_threshold, circuit_success_threshold, circuit_timeout_seconds, | ||
| circuit_error_rate_threshold, circuit_min_requests) | ||
| VALUES ('opencode', 3, 60, 120, 600, 4, 2, 60, 0.6, 10)", |
There was a problem hiding this comment.
Avoid seeding new app rows before migration runs
Database::init() calls create_tables() before apply_schema_migrations(), so existing v10 databases still enforce proxy_config's old CHECK(app_type IN ('claude','codex','gemini')) when this insert executes. In that upgrade path, inserting 'opencode' fails immediately with a CHECK-constraint error and aborts startup, which means users cannot reach the v10→v11 migration step at all.
Useful? React with 👍 / 👎.
| import { proxyApi } from "@/lib/api/proxy"; | ||
|
|
||
| const PRICING_APPS = ["claude", "codex", "gemini"] as const; | ||
| const PRICING_APPS = ["claude", "codex", "gemini", "openclaw"] as const; |
There was a problem hiding this comment.
Keep PRICING_APPS aligned with initialized state shape
After adding openclaw here, appConfigs/newState still initialize only claude|codex|gemini; if loading config fails for any app, isConfigLoading is cleared and render paths read appConfigs[openclaw].multiplier, which dereferences undefined and crashes the pricing panel. This regression is triggered specifically on partial/failed API loads.
Useful? React with 👍 / 👎.
- Add hermes field to ProxyTakeoverStatus struct - Read hermes takeover status from DB - Add hermes to ProxyPanel and PricingConfigPanel
There was a problem hiding this comment.
Pull request overview
Extends the proxy_config database table and related backend/frontend surfaces to recognize additional app types (OpenCode/OpenClaw/Hermes), aiming to enable health monitoring / circuit-breaker configuration and surface new apps in the UI.
Changes:
- Expanded
proxy_configschema CHECK constraint and added seed rows + a v10→v11 migration; bumped DBSCHEMA_VERSIONto 11. - Updated proxy takeover status reading in the backend to consult DB flags for OpenCode/OpenClaw.
- Updated UI/type surfaces to include OpenClaw (proxy takeover UI + pricing config) and OpenClaw/Hermes (About tool list), plus expanded usage filter types.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/types/usage.ts | Expands AppTypeFilter union to include opencode/openclaw. |
| src/components/usage/PricingConfigPanel.tsx | Adds openclaw to the pricing-app list for default multiplier/source configuration. |
| src/components/settings/AboutSection.tsx | Adds openclaw/hermes to the list of tool names requested from backend version detection. |
| src/components/proxy/ProxyPanel.tsx | Adds openclaw to the “app takeover” switch grid in the proxy UI. |
| src-tauri/src/services/proxy.rs | Reads OpenCode/OpenClaw takeover enablement from DB instead of hardcoding false. |
| src-tauri/src/database/schema.rs | Expands proxy_config app_type constraint, seeds new rows, and adds v10→v11 migration. |
| src-tauri/src/database/mod.rs | Bumps SCHEMA_VERSION to 11. |
| .gitignore | Ignores .codex-plans/. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // 初始化三行数据(每应用不同默认值) | ||
| // | ||
| // 兼容旧数据库: | ||
| // - 老版本 proxy_config 是单例表(没有 app_type 列),此时不能执行三行 seed insert; | ||
| // - 旧表会在 apply_schema_migrations() 中迁移为三行结构后再插入。 | ||
| if Self::has_column(conn, "proxy_config", "app_type")? { | ||
| conn.execute( | ||
| "INSERT OR IGNORE INTO proxy_config (app_type, max_retries, | ||
| streaming_first_byte_timeout, streaming_idle_timeout, non_streaming_timeout, | ||
| circuit_failure_threshold, circuit_success_threshold, circuit_timeout_seconds, | ||
| circuit_error_rate_threshold, circuit_min_requests) | ||
| VALUES ('claude', 6, 90, 180, 600, 8, 3, 90, 0.7, 15)", | ||
| [], | ||
| ) | ||
| .map_err(|e| AppError::Database(e.to_string()))?; | ||
| conn.execute( | ||
| "INSERT OR IGNORE INTO proxy_config (app_type, max_retries, | ||
| streaming_first_byte_timeout, streaming_idle_timeout, non_streaming_timeout, | ||
| circuit_failure_threshold, circuit_success_threshold, circuit_timeout_seconds, | ||
| circuit_error_rate_threshold, circuit_min_requests) | ||
| VALUES ('codex', 3, 60, 120, 600, 4, 2, 60, 0.6, 10)", | ||
| [], | ||
| ) | ||
| .map_err(|e| AppError::Database(e.to_string()))?; | ||
| conn.execute( | ||
| "INSERT OR IGNORE INTO proxy_config (app_type, max_retries, | ||
| streaming_first_byte_timeout, streaming_idle_timeout, non_streaming_timeout, | ||
| circuit_failure_threshold, circuit_success_threshold, circuit_timeout_seconds, | ||
| circuit_error_rate_threshold, circuit_min_requests) | ||
| VALUES ('gemini', 5, 60, 120, 600, 4, 2, 60, 0.6, 10)", | ||
| [], | ||
| ) | ||
| .map_err(|e| AppError::Database(e.to_string()))?; | ||
| conn.execute( | ||
| "INSERT OR IGNORE INTO proxy_config (app_type, max_retries, | ||
| streaming_first_byte_timeout, streaming_idle_timeout, non_streaming_timeout, | ||
| circuit_failure_threshold, circuit_success_threshold, circuit_timeout_seconds, | ||
| circuit_error_rate_threshold, circuit_min_requests) | ||
| VALUES ('opencode', 3, 60, 120, 600, 4, 2, 60, 0.6, 10)", | ||
| [], |
There was a problem hiding this comment.
proxy_config seed logic now inserts additional app rows (opencode/openclaw/hermes). There are existing migration/unit tests that assert proxy_config has exactly 3 rows after migrations; those tests will now fail. Update the expected row count and/or assertions to reflect the expanded app set.
| import { proxyApi } from "@/lib/api/proxy"; | ||
|
|
||
| const PRICING_APPS = ["claude", "codex", "gemini"] as const; | ||
| const PRICING_APPS = ["claude", "codex", "gemini", "openclaw"] as const; |
There was a problem hiding this comment.
PRICING_APPS now includes "openclaw", but later in this component the appConfigs/newState initialization only defines claude/codex/gemini. That makes appConfigs[app] undefined for openclaw (and should also fail the AppConfigState type). Add openclaw defaults everywhere the config state is initialized (initial useState and newState in loadAllConfigs).
| } | ||
|
|
||
| const TOOL_NAMES = ["claude", "codex", "gemini", "opencode"] as const; | ||
| const TOOL_NAMES = ["claude", "codex", "gemini", "opencode", "openclaw", "hermes"] as const; |
There was a problem hiding this comment.
TOOL_NAMES now includes "openclaw" and "hermes", but the backend get_tool_versions command filters requests against VALID_TOOLS (currently only claude/codex/gemini/opencode). As-is, these new tools will never show up (they'll be silently dropped). Either extend VALID_TOOLS + version detection to include them, or keep the frontend list aligned with what the backend supports.
| const TOOL_NAMES = ["claude", "codex", "gemini", "opencode", "openclaw", "hermes"] as const; | |
| const TOOL_NAMES = ["claude", "codex", "gemini", "opencode"] as const; |
| })} | ||
| </p> | ||
| <div className="grid gap-2 sm:grid-cols-3"> | ||
| {(["claude", "codex", "gemini"] as const).map((appType) => { | ||
| {(["claude", "codex", "gemini", "openclaw"] as const).map((appType) => { | ||
| const isEnabled = | ||
| takeoverStatus?.[ |
There was a problem hiding this comment.
This section is explicitly labeled as “应用接管” (live config takeover). Adding "openclaw" here allows users to toggle takeover for an additive-mode app, but backend takeover logic operates by rewriting Live config files and does not appear to guard against additive-mode apps. If takeover is intentionally unsupported for additive-mode apps (per PR notes), keep this list to switch-mode apps only and/or render additive apps as disabled with an explanation.
| /// 获取各应用的接管状态(是否改写该应用的 Live 配置指向本地代理) | ||
| pub async fn get_takeover_status(&self) -> Result<ProxyTakeoverStatus, String> { | ||
| // 从 proxy_config.enabled 读取(优先),兼容旧的 live_backup 备份检测 | ||
| let claude_enabled = self | ||
| .db | ||
| .get_proxy_config_for_app("claude") | ||
| .await | ||
| .map(|c| c.enabled) | ||
| .unwrap_or(false); | ||
| let codex_enabled = self | ||
| .db | ||
| .get_proxy_config_for_app("codex") | ||
| .await | ||
| .map(|c| c.enabled) | ||
| .unwrap_or(false); | ||
| let gemini_enabled = self | ||
| .db | ||
| .get_proxy_config_for_app("gemini") | ||
| .await | ||
| .map(|c| c.enabled) | ||
| .unwrap_or(false); | ||
| // OpenCode and OpenClaw don't support proxy features, always return false | ||
| let opencode_enabled = false; | ||
| let openclaw_enabled = false; | ||
| let opencode_enabled = self | ||
| .db | ||
| .get_proxy_config_for_app("opencode") | ||
| .await | ||
| .map(|c| c.enabled) | ||
| .unwrap_or(false); | ||
| let openclaw_enabled = self | ||
| .db | ||
| .get_proxy_config_for_app("openclaw") | ||
| .await | ||
| .map(|c| c.enabled) | ||
| .unwrap_or(false); |
There was a problem hiding this comment.
get_takeover_status now reports takeover enabled for opencode/openclaw by reading proxy_config.enabled, but set_takeover_for_app rewrites Live configs and currently has no is_additive_mode() guard. This makes it possible to attempt a takeover on additive-mode apps, which is likely unsupported and can fail or corrupt configs. Consider hard-disabling takeover for additive-mode apps in the service/API layer (return false + reject enabling), and keep proxy_config.enabled for these apps reserved for non-takeover features if needed.
|
|
||
| // 复制已有数据 | ||
| conn.execute( | ||
| "INSERT INTO proxy_config_new SELECT * FROM proxy_config", |
There was a problem hiding this comment.
The v10→v11 migration copies rows with INSERT INTO proxy_config_new SELECT * FROM proxy_config, which is brittle and will fail if the existing table has extra columns (e.g., columns added via ALTER TABLE in create_tables_on_conn). Use an explicit column list for both sides (copy only the common columns), or ensure proxy_config_new matches the existing schema before copying.
| "INSERT INTO proxy_config_new SELECT * FROM proxy_config", | |
| "INSERT INTO proxy_config_new ( | |
| app_type, proxy_enabled, listen_address, listen_port, enable_logging, | |
| enabled, auto_failover_enabled, max_retries, streaming_first_byte_timeout, | |
| streaming_idle_timeout, non_streaming_timeout, circuit_failure_threshold, | |
| circuit_success_threshold, circuit_timeout_seconds, circuit_error_rate_threshold, | |
| circuit_min_requests, default_cost_multiplier, pricing_model_source, | |
| created_at, updated_at | |
| ) | |
| SELECT | |
| app_type, proxy_enabled, listen_address, listen_port, enable_logging, | |
| enabled, auto_failover_enabled, max_retries, streaming_first_byte_timeout, | |
| streaming_idle_timeout, non_streaming_timeout, circuit_failure_threshold, | |
| circuit_success_threshold, circuit_timeout_seconds, circuit_error_rate_threshold, | |
| circuit_min_requests, default_cost_multiplier, pricing_model_source, | |
| created_at, updated_at | |
| FROM proxy_config", |
- PricingConfigPanel: add openclaw/hermes/opencode to appConfigs init (#1) - proxy/types: add hermes field to ProxyTakeoverStatus - proxy.rs: return consistent false for unsupported app takeover (farion1231#2) - schema.rs: use explicit column list + preserve live_takeover_active in migration (farion1231#3) - AboutSection + misc.rs: add openclaw/hermes to VALID_TOOLS (farion1231#4) - ProxyPanel: filter unsupported apps from takeover switches (farion1231#5)
- PricingConfigPanel: add openclaw/hermes/opencode to appConfigs init (#1) - proxy/types: add hermes field to ProxyTakeoverStatus - proxy.rs: return consistent false for unsupported app takeover (farion1231#2) - schema.rs: use explicit column list + preserve live_takeover_active in migration (farion1231#3) - AboutSection + misc.rs: add openclaw/hermes to VALID_TOOLS (farion1231#4) - ProxyPanel: filter unsupported apps from takeover switches (farion1231#5)
Summary
Extends proxy_config table support to all 6 app types (Claude, Codex, Gemini, OpenCode, OpenClaw, Hermes), enabling health monitoring, circuit breaker config, and failover settings for additive-mode apps.
Changes
Database (schema.rs)
Backend (proxy.rs, types.rs)
Frontend
Notes
Future Work