From 5742e0ba1e0f48aa46e52ffd6df20b4538d7a49e Mon Sep 17 00:00:00 2001 From: Rat <261020116+Rat0323@users.noreply.github.com> Date: Fri, 31 Jul 2026 12:06:26 +0800 Subject: [PATCH 1/2] Fix provider-specific goals feature overrides --- Cargo.lock | 1 + apps/codex-plus-manager/src-tauri/Cargo.toml | 1 + .../src-tauri/src/commands.rs | 53 +++++++- apps/codex-plus-manager/src/App.tsx | 117 ++++++++---------- .../src/goals-config.test.ts | 64 ++++++++++ apps/codex-plus-manager/src/goals-config.ts | 97 +++++++++++++++ apps/codex-plus-manager/src/i18n-en.ts | 1 + crates/codex-plus-core/src/relay_config.rs | 38 ++++++ 8 files changed, 309 insertions(+), 63 deletions(-) create mode 100644 apps/codex-plus-manager/src/goals-config.test.ts create mode 100644 apps/codex-plus-manager/src/goals-config.ts diff --git a/Cargo.lock b/Cargo.lock index c7bc80953..441ca5803 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -492,6 +492,7 @@ dependencies = [ "tauri-build", "tauri-plugin-dialog", "tempfile", + "toml_edit 0.22.27", ] [[package]] diff --git a/apps/codex-plus-manager/src-tauri/Cargo.toml b/apps/codex-plus-manager/src-tauri/Cargo.toml index df31990d5..206648b33 100644 --- a/apps/codex-plus-manager/src-tauri/Cargo.toml +++ b/apps/codex-plus-manager/src-tauri/Cargo.toml @@ -23,6 +23,7 @@ serde.workspace = true serde_json.workspace = true tauri = { version = "2", features = ["protocol-asset", "custom-protocol", "tray-icon"] } tauri-plugin-dialog = "2" +toml_edit.workspace = true [build-dependencies] tauri-build = { version = "2", features = [] } diff --git a/apps/codex-plus-manager/src-tauri/src/commands.rs b/apps/codex-plus-manager/src-tauri/src/commands.rs index fa76094e1..09121c4c1 100644 --- a/apps/codex-plus-manager/src-tauri/src/commands.rs +++ b/apps/codex-plus-manager/src-tauri/src/commands.rs @@ -1627,6 +1627,7 @@ fn normalize_settings_before_save(mut settings: BackendSettings) -> BackendSetti if !profile.use_common_config || profile.config_contents.trim().is_empty() { continue; } + let goals_override = relay_config_goals_value(&profile.config_contents); match codex_plus_core::relay_config::strip_common_config_from_config( &profile.config_contents, &common_config, @@ -1640,6 +1641,10 @@ fn normalize_settings_before_save(mut settings: BackendSettings) -> BackendSetti strip_common_config_text_fallback(&profile.config_contents, &common_config); } } + if let Some(enabled) = goals_override { + profile.config_contents = + relay_config_set_goals_override(&profile.config_contents, enabled); + } } } settings.provider_sync_saved_providers = @@ -1653,6 +1658,30 @@ fn normalize_settings_before_save(mut settings: BackendSettings) -> BackendSetti settings } +fn relay_config_goals_value(config: &str) -> Option { + let doc = config.parse::().ok()?; + doc.get("features")? + .as_table_like()? + .get("goals")? + .as_bool() +} + +fn relay_config_set_goals_override(config: &str, enabled: bool) -> String { + let Ok(mut doc) = config.parse::() else { + return config.to_string(); + }; + if !doc.as_table().contains_key("features") + || doc + .get("features") + .and_then(toml_edit::Item::as_table_like) + .is_none() + { + doc["features"] = toml_edit::table(); + } + doc["features"]["goals"] = toml_edit::value(enabled); + codex_plus_core::relay_config::normalize_config_text(&doc.to_string()) +} + fn normalize_provider_sync_provider_list(values: Vec) -> Vec { let mut seen = std::collections::HashSet::new(); let mut result = Vec::new(); @@ -5144,10 +5173,32 @@ enabled = true assert!(config.contains("model = \"gpt-5\"")); assert!(!config.contains("model_reasoning_effort")); - assert!(!config.contains("[features]")); + // `goals` is an explicit per-profile override and must survive + // normalization even when it matches the common configuration. + assert!(config.contains("[features]")); + assert!(config.contains("goals = true")); assert!(!config.contains("[plugins.\"superpowers@openai-curated\"]")); } + #[test] + fn normalize_settings_before_save_preserves_explicit_false_goals_override() { + let settings = BackendSettings { + relay_common_config_contents: "[features]\ngoals = true\nfast_mode = true\n" + .to_string(), + relay_profiles: vec![RelayProfile { + use_common_config: true, + config_contents: "model = \"gpt-5\"\n[features]\ngoals = false\n".to_string(), + ..RelayProfile::default() + }], + ..BackendSettings::default() + }; + + let normalized = normalize_settings_before_save(settings); + let config = &normalized.relay_profiles[0].config_contents; + assert!(config.contains("goals = false")); + assert!(!config.contains("fast_mode = true")); + } + #[test] fn normalize_settings_before_save_repairs_invalid_profile_common_duplication() { let settings = BackendSettings { diff --git a/apps/codex-plus-manager/src/App.tsx b/apps/codex-plus-manager/src/App.tsx index b41a66b31..3c820d1da 100644 --- a/apps/codex-plus-manager/src/App.tsx +++ b/apps/codex-plus-manager/src/App.tsx @@ -73,6 +73,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; +import { codexGoalsFeatureState, setCodexGoalsFeatureInConfig } from "./goals-config"; import { isGitHubRepositoryHomepage } from "./github-repository"; import { mergeModelWindowRows, @@ -5663,6 +5664,11 @@ function RelayProfileEditor({ } const showApiFields = profile.relayMode !== "official" || profile.officialMixApiKey; + const goalsFeatureState = codexGoalsFeatureState( + profile.configContents, + form.relayCommonConfigContents, + profile.useCommonConfig, + ); const sub2apiBaseUrl = profile.upstreamBaseUrl.trim() || profile.baseUrl.trim(); const canFetchSub2ApiRate = profile.sub2apiEnabled && Boolean(sub2apiBaseUrl && profile.apiKey.trim()); const updateDraft = (patch: Partial) => { @@ -5760,7 +5766,7 @@ function RelayProfileEditor({ + {goalsFeatureState.inherited ? ( +

{t("当前继承公共配置;修改后将为该供应商保存独立设置。")}

+ ) : null}