diff --git a/src-tauri/src/agents.rs b/src-tauri/src/agents.rs index 2054508..a6a93b7 100644 --- a/src-tauri/src/agents.rs +++ b/src-tauri/src/agents.rs @@ -1,4 +1,3 @@ -//! Agent client discovery, configuration generation, and reversible state management. use super::*; diff --git a/src-tauri/src/agents/backups.rs b/src-tauri/src/agents/backups.rs index 99f7b16..c192826 100644 --- a/src-tauri/src/agents/backups.rs +++ b/src-tauri/src/agents/backups.rs @@ -1,4 +1,3 @@ -//! Explicit, immutable backups. Legacy history and dated backups are never read or migrated. use super::*; use serde_json::Value; use std::sync::atomic::{AtomicU64, Ordering}; @@ -78,7 +77,6 @@ pub(crate) struct BackupPreview { pub(crate) fn agent_data_directory(paths: &[PathBuf]) -> Result { #[cfg(test)] { - // Tests cannot reach the running application's data or real client configuration. let temp = std::env::temp_dir(); let relative = paths .first() @@ -107,7 +105,6 @@ fn path_identity(paths: &[PathBuf]) -> String { } fn backup_directory(client: &str, paths: &[PathBuf]) -> Result { - // Only client IDs returned by config_paths reach here; validate again for internal callers. if client != PI_AGENT_ID { AgentClient::parse(client)?; } @@ -293,7 +290,6 @@ pub(crate) fn create_backup(client: &str, home: &Path) -> Result Result Result<(), String> { let paths = config_paths(client, home)?; let path = version_path(client, &paths, id)?; - // Check parents, then unlink only this entry. A damaged or symlinked package can be deleted safely. validate_config_path(&path.parent().ok_or("备份目录无效")?.join(".path-check"))?; fs::remove_file(&path).map_err(|_| "删除手动备份失败,请刷新列表后重试".to_string()) } @@ -347,7 +342,6 @@ fn preview( validate_restorable(&version)?; let current = config_images(paths)?; let after = backup_images(&version); - // Entire package hash binds preview to bytes, existence, paths and Desktop mappings. if read_agent_bytes(&version_path(client, paths, id)?)?.as_deref() != Some(&package) { return Err("备份在预览期间发生变化,请重新预览".into()); } diff --git a/src-tauri/src/agents/backups/tests.rs b/src-tauri/src/agents/backups/tests.rs index a87684b..e90d6c0 100644 --- a/src-tauri/src/agents/backups/tests.rs +++ b/src-tauri/src/agents/backups/tests.rs @@ -146,7 +146,6 @@ fn codex_catalog_reset_removes_managed_fields_and_preserves_extensions_on_disk() .unwrap(); let defaults = codex_catalog::prepare_catalog(&runtime).unwrap(); let mut expected: Value = serde_json::from_str(&defaults.json).unwrap(); - // The editor restores 95% by dropping its override on this official model. assert!(expected["models"][0] .get("effective_context_window_percent") .is_none()); @@ -787,7 +786,6 @@ fn nested_model_options_and_third_party_providers_survive_all_updates() { } save(path, render(path, &root).unwrap()); } - // Change credentials without reordering the selected model inventory. let catalog = catalog("gpt-one"); let mappings = ClaudeDesktopModelMappings::all("gpt-one"); apply_agent_configuration_with_oauth( diff --git a/src-tauri/src/agents/configuration.rs b/src-tauri/src/agents/configuration.rs index 12ce41c..09028eb 100644 --- a/src-tauri/src/agents/configuration.rs +++ b/src-tauri/src/agents/configuration.rs @@ -439,7 +439,7 @@ fn validate_deepseek_harness_versioned_credentials( let Some(key) = key.as_str() else { return Err(format!("{label} 顶层字段名必须是字符串")); }; - let _ = key; // Preserve extension fields from the client and other integrations. + let _ = key; } for section in ["refs", "records"] { if yaml_mapping_value(root, section).is_some_and(|value| !value.is_mapping()) { @@ -460,8 +460,6 @@ fn deepseek_harness_credentials_refs_mut<'a>( ); } else { validate_deepseek_harness_versioned_credentials(root, label)?; - // Repair files written by older EasyCLIProxyAPI builds, which placed - // this managed reference beside version/refs/records. root.remove(yaml_key(DEEPSEEK_HARNESS_CREDENTIAL)); } @@ -2982,8 +2980,6 @@ fn build_zcode_config( }) .collect::>(); let managed_provider = ensure_json_object_entry(providers, MANAGED_AGENT_PROVIDER_ID); - // `npm` is an OpenCode provider field. ZCode treats it as an incompatible - // provider definition, so also remove values written by earlier versions. managed_provider.remove("npm"); managed_provider.insert("enabled".to_string(), serde_json::json!(true)); managed_provider.insert("name".to_string(), serde_json::json!("EasyCLIProxyAPI")); diff --git a/src-tauri/src/agents/discovery.rs b/src-tauri/src/agents/discovery.rs index 74d2f90..aa9e52b 100644 --- a/src-tauri/src/agents/discovery.rs +++ b/src-tauri/src/agents/discovery.rs @@ -826,10 +826,6 @@ pub(crate) fn claude_desktop_config_paths_from_directories( pub(crate) fn claude_desktop_config_paths_from_local_app_data( local_app_data: &Path, ) -> Vec { - // Claude Desktop may add a channel or version suffix to these directories. - // Match cc-switch's resolver so status detection and configuration writes use - // the same files as the installed Desktop client rather than only the legacy - // fixed `Claude` / `Claude-3p` locations. let normal = find_windows_claude_data_directory(local_app_data, false) .unwrap_or_else(|| local_app_data.join("Claude")); let threep = find_windows_claude_data_directory(local_app_data, true) @@ -908,9 +904,6 @@ pub(crate) fn inspect_agent_config( Err(_) => (false, None, false, false, Some("配置读取或解析失败,请检查文件权限,或使用手动备份恢复、基础配置模板修复".to_string())), }; let executable = find_agent_executable(client, home); - // Desktop application executables are not CLIs. Invoking them with - // --version can start their GUI and block discovery, so never probe them - // directly. ZCode may still expose a separate command-line entry point. let cli_version = if client == AgentClient::ZCode { find_named_agent_executable(home, &["zcode"]) .filter(|path| executable.as_ref() != Some(path)) @@ -966,7 +959,6 @@ pub(crate) fn inspect_agent_config( } let configuration_synchronized = agent_configuration_is_synchronized(client, home, configured); - // Legacy backup state never participates in current configuration discovery. AgentConfigStatus { id: client.id().to_string(), @@ -1213,8 +1205,6 @@ pub(crate) fn inspect_agent_managed_config( } } -// Configuration health and evidence of CPA integration are separate facts. -// A saved history version alone does not mean the current files still use CPA. pub(crate) fn agent_connection_state(configured: bool, valid: bool, managed: Result) -> &'static str { if !valid || managed.is_err() { "invalid" } else if configured { "configured" } @@ -1225,7 +1215,6 @@ pub(crate) fn agent_connection_state(configured: bool, valid: bool, managed: Res pub(crate) fn agent_has_connection_evidence(client: AgentClient, paths: &[PathBuf]) -> Result { let active_marker = agent_has_managed_marker(client, paths)?; if active_marker { return Ok(true); } - // Recognize partial configurations too, even if the selected provider/model was removed. for path in paths { let Some(bytes) = read_agent_bytes(path)? else { continue; }; let content = std::str::from_utf8(&bytes).map_err(|e| e.to_string())?; @@ -2020,7 +2009,6 @@ pub(crate) fn read_codex_app_installation_version( pub(crate) fn read_zcode_app_version(executable: &Path) -> Option { #[cfg(target_os = "windows")] { - // Missing version metadata must not cause the desktop app to be launched. read_windows_executable_version(executable) } #[cfg(target_os = "macos")] @@ -2093,8 +2081,6 @@ pub(crate) fn find_zcode_desktop_executable(home: &Path) -> Option { #[cfg(target_os = "windows")] pub(crate) fn find_windows_registered_zcode_executable() -> Option { - // Electron/NSIS permits arbitrary installation directories. Its uninstall - // record may omit InstallLocation and point DisplayIcon at an .ico file. const DISCOVERY_SCRIPT: &str = r#" $ErrorActionPreference = 'SilentlyContinue' $ProgressPreference = 'SilentlyContinue' @@ -2172,7 +2158,6 @@ pub(crate) fn parse_windows_zcode_discovery_output(output: &str) -> Option Option Option { pub(crate) fn read_codex_asar_version(path: &Path) -> Option { use std::io::{Read, Seek, SeekFrom}; - // ASAR starts with two Chromium pickles: a header-size pickle followed by - // a JSON header pickle. Read only the header and the root package.json, - // not the hundreds of MB of bundled application code. let mut file = fs::File::open(path).ok()?; let mut prefix = [0_u8; 16]; file.read_exact(&mut prefix).ok()?; diff --git a/src-tauri/src/agents/launch.rs b/src-tauri/src/agents/launch.rs index 932d473..1cbf1a9 100644 --- a/src-tauri/src/agents/launch.rs +++ b/src-tauri/src/agents/launch.rs @@ -421,7 +421,6 @@ fn restart_managed_deepseek_harness_with( stop: impl FnOnce(&mut Child) -> Result<(), String>, spawn: impl FnOnce(&Path, &Path, &[String]) -> Result, ) -> Result { - // Keep the lock across stop and spawn so launch/stop/status cannot interleave. let mut process = process_state .process .lock() @@ -442,13 +441,11 @@ fn restart_managed_deepseek_harness_with( return Err("只有 Web 模式支持重启".to_string()); } let launch = managed.launch.clone(); - // Validate paths before shutting down a working service. if !launch.executable.is_file() || !launch.working_directory.is_dir() { return Err("DeepSeek Harness 启动程序或工作目录已不存在".to_string()); } stop(&mut managed.child)?; *process = None; - // The old process must release its endpoint before the replacement starts. ensure_deepseek_harness_web_endpoint_available(launch.options.as_ref())?; let child = spawn( &launch.executable, @@ -1146,8 +1143,6 @@ fn linux_process_matches_application(process_id: i32, application: &Path) -> boo .map(|argument| Path::new(OsStr::from_bytes(argument))) .enumerate() .any(|(index, argument)| { - // An unrelated CLI may mention the desktop executable as an input file. - // Only argv[0] identifies a launcher; retain Nix wrapper discovery below. (index == 0 && linux_process_path_matches(argument, application)) || installation_root.is_some_and(|root| { fs::canonicalize(argument) @@ -1666,7 +1661,6 @@ mod tests { processes.children[1].try_wait().unwrap().is_none(), "unrelated installation must keep running" ); - // Stopping an installation with no running processes succeeds as well. stop_other_desktop(&installation, "Test Desktop").unwrap(); } diff --git a/src-tauri/src/agents/templates.rs b/src-tauri/src/agents/templates.rs index 98ed605..e14ec0f 100644 --- a/src-tauri/src/agents/templates.rs +++ b/src-tauri/src/agents/templates.rs @@ -1,4 +1,3 @@ -//! Generate and validate the entire template before presenting any destructive confirmation. use super::*; #[cfg(test)] @@ -86,8 +85,6 @@ pub(crate) async fn prepare_desktop_core_update( Ok((before, after)) } -// Management API/parser errors may quote configuration contents. Keep actionable transaction -// outcomes without forwarding response bodies, YAML source lines or credentials to the UI. pub(crate) fn agent_core_error(error: String) -> String { if error.contains("自动恢复失败") || error.contains("回滚失败") { "内核别名或智能体配置写入失败且回滚失败,请检查当前配置".into() diff --git a/src-tauri/src/agents/transactions.rs b/src-tauri/src/agents/transactions.rs index 61b8d9f..cd6e2b8 100644 --- a/src-tauri/src/agents/transactions.rs +++ b/src-tauri/src/agents/transactions.rs @@ -1,4 +1,3 @@ -//! Configuration transactions use only in-memory rollback images. No backup I/O. use super::*; use serde_json::Value; @@ -24,7 +23,6 @@ pub(crate) fn config_paths(client: &str, home: &Path) -> Result, St )) } -// Reject links (including Windows junctions) in every existing path component. pub(crate) fn validate_config_path(path: &Path) -> Result<(), String> { if !path.is_absolute() || path @@ -223,8 +221,6 @@ fn commit_config_transaction( if config_images(&all_paths)? != previous { return Err("配置已被其他程序修改,请刷新后重试".into()); } - // Check each file again immediately before writing. Track only files this operation touched - // so a conflict on a later file cannot roll back someone else's edit to that file. let mut attempted = Vec::new(); let result = (if checked { (|| { @@ -252,12 +248,10 @@ fn commit_config_transaction( } }); if result.is_err() { - // Do not expose arbitrary writer errors: parsers and external tools may include credentials. let rollback = (if checked { let mut failed = false; for (index, observed) in attempted.into_iter().rev() { let (path, _) = &previous[index]; - // A file changed after our write: leave that external edit intact. if observed.is_err() || read_agent_bytes(path) != observed { failed = true; continue; @@ -373,8 +367,6 @@ pub(crate) fn prepare_config_updates( Ok(after) } -// Model inventories are refreshed from CPA, but matching model entries may have client-specific -// nested options. Keep those options while allowing unavailable managed models to disappear. fn preserve_model_extensions(client: &str, path: &Path, before: &Value, after: &mut Value) { fn fill_missing(before: &Value, after: &mut Value) { if let (Some(old), Some(new)) = (before.as_object(), after.as_object_mut()) { @@ -590,7 +582,6 @@ fn validate_unmanaged_preserved( None, ); set(&mut value, &[DEEPSEEK_HARNESS_CREDENTIAL.into()], None); - // The credentials format requires version 1 when initially created. if value.get("version") == Some(&serde_json::json!(1)) { set(&mut value, &["version".into()], None); } diff --git a/src-tauri/src/app_update.rs b/src-tauri/src/app_update.rs index 70ddf99..18c3850 100644 --- a/src-tauri/src/app_update.rs +++ b/src-tauri/src/app_update.rs @@ -894,9 +894,6 @@ pub(crate) async fn download_and_stage_portable_app_update( work_dir: work_dir.clone(), target_version: pending.version.clone(), }; - // Keep the updater inside its signed application bundle. Copying the Mach-O - // executable into a temporary directory strips the bundle context Gatekeeper - // uses to validate it and can cause macOS to terminate it before it starts. let helper_path = macos_update_helper_path(¤t_exe); launch_portable_update_helper(app, &helper_path, &work_dir, &descriptor).await } diff --git a/src-tauri/src/codex_catalog.rs b/src-tauri/src/codex_catalog.rs index 9ab2379..147a9eb 100644 --- a/src-tauri/src/codex_catalog.rs +++ b/src-tauri/src/codex_catalog.rs @@ -77,8 +77,6 @@ pub(crate) fn validate_embedded_catalog() -> Result<(), String> { catalog_state().map(|_| ()) } -// An omitted managed field means "use the Codex default", not "keep the disk value". -// Derive ownership from the bundled schemas and editor so optional fields stay in sync. pub(crate) fn is_managed_model_field(field: &str) -> bool { static FIELDS: OnceLock> = OnceLock::new(); FIELDS @@ -608,8 +606,6 @@ fn normalize_fallback_model(model: &mut Map) { } } -// Both official and fallback templates take context defaults from the CPA API. -// Template values are used only when the API provides no valid context metadata. fn apply_runtime_context_windows(model: &mut Map, runtime: &CodexRuntimeModel) { if let Some(context_window) = runtime.context_window.or(runtime.max_context_window) { let max_context_window = runtime diff --git a/src-tauri/src/codex_catalog/customizations.rs b/src-tauri/src/codex_catalog/customizations.rs index e56d5f0..c02620a 100644 --- a/src-tauri/src/codex_catalog/customizations.rs +++ b/src-tauri/src/codex_catalog/customizations.rs @@ -66,8 +66,6 @@ fn editable_configuration(model: &Map) -> Map { ( field.to_string(), model.get(*field).cloned().unwrap_or_else(|| { - // Codex defaults this optional field to 95 when it is omitted. - // The editor needs the same value to validate and save context changes. if *field == "effective_context_window_percent" { Value::from(95) } else { @@ -517,7 +515,6 @@ mod tests { 256_000 ); - // Restoring defaults drops the saved override and follows the current API response. state.customizations = customizations_from_request( &updated, CatalogEditorRequest { diff --git a/src-tauri/src/core_config/aliases.rs b/src-tauri/src/core_config/aliases.rs index 5f49f2e..15bf06d 100644 --- a/src-tauri/src/core_config/aliases.rs +++ b/src-tauri/src/core_config/aliases.rs @@ -332,8 +332,6 @@ pub(crate) fn ensure_claude_desktop_model_aliases_with_oauth_definitions_in_yaml .as_mapping_mut() .ok_or_else(|| "内核配置顶层必须是 YAML 映射".to_string())?; - // All roles resolve against the same input. Replacing one old role must not erase - // the only source record needed to configure another role. let sources = root.clone(); for (alias, source_model) in [ (CLAUDE_DESKTOP_OPUS_MODEL_ID, mappings.opus.as_str()), @@ -654,8 +652,6 @@ fn resolve_claude_desktop_alias_source( root: &serde_norway::Mapping, source_model: &str, ) -> Result, String> { - // Prefer an exact client-visible model. Legacy configurations may only retain - // an aliased entry, whose upstream name still identifies its configured source. for upstream in [false, true] { let matching_model = |model: &serde_norway::Value| { let (name, client_model, _) = configured_model_identity(model)?; diff --git a/src-tauri/src/core_config/settings.rs b/src-tauri/src/core_config/settings.rs index df5ad45..6c9ff9f 100644 --- a/src-tauri/src/core_config/settings.rs +++ b/src-tauri/src/core_config/settings.rs @@ -666,9 +666,6 @@ pub(crate) fn write_bytes_atomically(path: &Path, content: &[u8]) -> Result<(), let mut file = File::create(&temporary_path)?; file.write_all(content)?; file.sync_all()?; - // ReplaceFileW requires the replacement file handle to be closed. - // Unix rename permits replacing an open file, so this otherwise only - // surfaces on Windows as ERROR_SHARING_VIOLATION (os error 32). drop(file); replace_file_atomically(&temporary_path, path) })(); diff --git a/src-tauri/src/core_runtime.rs b/src-tauri/src/core_runtime.rs index 5c21e60..8285038 100644 --- a/src-tauri/src/core_runtime.rs +++ b/src-tauri/src/core_runtime.rs @@ -12,7 +12,6 @@ pub(crate) fn lock_core_operation( Ok(guard) } -// Own the guard from spawn, including while the management port is starting. pub(crate) struct CoreChild { child: Child, #[cfg(windows)] @@ -323,7 +322,6 @@ pub(crate) fn pause_core_process_for_install( process_state: &CoreProcessState, ) -> Result { process_state.ensure_active()?; - // A process can hold files even when its HTTP port is not responding. let was_running = current_core_status(Some(process_state), None)?.running; if was_running { stop_core_process_inner(process_state)?; @@ -1036,7 +1034,6 @@ pub(crate) fn core_release_asset_name(version: &str, platform: &CorePlatform) -> ) } -// Download progress and cancellation require the complete transfer context here. #[allow(clippy::too_many_arguments)] pub(crate) async fn download_asset( client: &reqwest::Client, @@ -1318,9 +1315,6 @@ pub(crate) fn core_start_stdio(log_path: &Path) -> io::Result<(Stdio, Stdio)> { fs::create_dir_all(parent)?; } - // Keep only the current process run so console output cannot grow without - // bound across restarts. Both child handles use append mode to avoid their - // independent file cursors overwriting each other's output. let mut header_file = File::options() .write(true) .create(true) @@ -2304,9 +2298,6 @@ pub(crate) fn terminate_process_id(process_id: u32) -> Result<(), String> { System::Threading::{OpenProcess, WaitForSingleObject, PROCESS_SYNCHRONIZE}, }; - // Keep a handle to this process until it is signalled. taskkill exiting - // successfully only means termination was requested, not that file - // handles have been released. let handle = unsafe { OpenProcess(PROCESS_SYNCHRONIZE, 0, process_id) }; if handle.is_null() { let error = io::Error::last_os_error(); @@ -2474,11 +2465,6 @@ fn overlay_directory(source_dir: &Path, target_dir: &Path) -> Result<(), String> Ok(()) } -/// Copies through a sibling temporary file and atomically replaces the target. -/// -/// In particular, do not change this back to copying over an existing file: -/// macOS caches code-signature validation by vnode, so in-place updates can -/// leave an otherwise valid executable permanently rejected with SIGKILL. pub(crate) fn copy_core_file_replace(source_path: &Path, target_path: &Path) -> Result<(), String> { use std::sync::atomic::{AtomicU64, Ordering}; @@ -2522,8 +2508,6 @@ pub(crate) fn copy_core_file_replace(source_path: &Path, target_path: &Path) -> #[cfg(any(target_os = "macos", test))] pub(crate) fn rematerialize_core_binary(binary_path: &Path) -> Result<(), String> { - // Replacing the path with an identical copy gives it a fresh vnode and - // clears the macOS signature-cache state left by older in-place updates. copy_core_file_replace(binary_path, binary_path) } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index ae4d487..2cc2dd2 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -87,8 +87,6 @@ const RELEASE_DOWNLOAD_PREFIX: &str = const APP_UPDATE_MANIFEST_URL: &str = "https://github.com/router-for-me/EasyCLIProxyAPI/releases/latest/download/portable-update-windows.json"; #[cfg(target_os = "linux")] const APP_UPDATE_MANIFEST_URL: &str = "https://github.com/router-for-me/EasyCLIProxyAPI/releases/latest/download/portable-update-linux.json"; -// Legacy macOS clients contain an updater that cannot launch outside its signed app bundle. -// A separate channel makes those clients fall back to a one-time manual installation. #[cfg(target_os = "macos")] const APP_UPDATE_MANIFEST_URL: &str = "https://github.com/router-for-me/EasyCLIProxyAPI/releases/latest/download/portable-update-darwin-v2.json"; const APP_RELEASE_DOWNLOAD_PREFIX: &str = @@ -643,8 +641,6 @@ struct GuiConfigFile { download_source: VersionDownloadSource, custom_download_mirrors: Vec, active_custom_download_mirror: String, - // Kept for migration compatibility with configurations written before - // multi-source downloads were introduced. prefer_gitcode_downloads: bool, routing_session_affinity: bool, routing_session_affinity_ttl: String, @@ -920,8 +916,6 @@ impl Default for GuiConfigFile { auth_dir: DEFAULT_AUTH_DIR.to_string(), api_keys: vec![default_api_key_entry()], api_access_remarks: Vec::new(), - // Populated with an OS-generated secret while loading the GUI - // configuration. Core hashes the value written into config.yaml. management_secret_key: String::new(), debug: false, commercial_mode: false, @@ -1505,7 +1499,6 @@ struct CoreConfigSettings { max_retry_credentials: u32, max_retry_interval: u32, streaming_bootstrap_retries: u32, - // Kept for internal config migration/tests; never exposed to the WebView. #[allow(dead_code)] #[serde(skip_serializing)] management_secret_key: Option, @@ -2638,8 +2631,6 @@ fn main() { } app_handle.state::().cancel(); let app_handle = app_handle.clone(); - // Keep the event loop alive while an in-flight operation finishes: - // tray/status updates from that operation may need the UI thread. tauri::async_runtime::spawn_blocking(move || { let _guard = CORE_OPERATION_LOCK .lock() diff --git a/src-tauri/src/management_api.rs b/src-tauri/src/management_api.rs index 7d9385a..181d6c2 100644 --- a/src-tauri/src/management_api.rs +++ b/src-tauri/src/management_api.rs @@ -298,14 +298,9 @@ pub(crate) async fn submit_oauth_callback( pub(crate) fn management_http_client() -> Result { static CLIENT: LazyLock> = LazyLock::new(|| { reqwest::Client::builder() - // GUI-to-Core management traffic must always connect directly. Upstream - // traffic still uses Core's proxy-url, and other GUI HTTP clients keep - // their independently configured proxy behavior. .no_proxy() .connect_timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(30)) - // Management requests target the configured local listener. This keeps - // self-signed/private-CA certificates usable without weakening upstream clients. .danger_accept_invalid_certs(true) .build() .map_err(|err| format_management_request_error("创建管理 API 客户端失败", &err)) diff --git a/src-tauri/src/tests/core_config.rs b/src-tauri/src/tests/core_config.rs index d524365..48ab2b1 100644 --- a/src-tauri/src/tests/core_config.rs +++ b/src-tauri/src/tests/core_config.rs @@ -731,8 +731,6 @@ fn yaml_edit_runtime_patch_removes_empty_keys_and_skips_unsupported_sections() { assert!(rendered.contains("host: 127.0.0.1")); assert!(rendered.contains("port: 8317")); - // Nested plugin/routing sections are still optional for comment-preserving - // runtime patches; missing maps remain unsupported and stay untouched. let unsupported = "host: 127.0.0.1\nport: 8317\n"; let file = unsupported.parse::().unwrap(); let document = file.document().unwrap(); diff --git a/src-tauri/src/tests/core_runtime.rs b/src-tauri/src/tests/core_runtime.rs index 28a2b9d..d03c43a 100644 --- a/src-tauri/src/tests/core_runtime.rs +++ b/src-tauri/src/tests/core_runtime.rs @@ -172,7 +172,6 @@ fn updating_stops_an_adopted_core_without_a_port_before_replacing_its_config() { .stdout(Stdio::piped()) .stderr(Stdio::null()); configure_background_command(&mut command); - // Spawn without the GUI's job guard to simulate a previous orphan. let mut child = command.spawn().unwrap(); let output = io::BufReader::new(child.stdout.take().unwrap()); let ready = output diff --git a/src-tauri/src/tests/desktop_alias_routing.rs b/src-tauri/src/tests/desktop_alias_routing.rs index 1c40596..b01b57c 100644 --- a/src-tauri/src/tests/desktop_alias_routing.rs +++ b/src-tauri/src/tests/desktop_alias_routing.rs @@ -123,7 +123,6 @@ fn legacy_desktop_alias_only_provider_retains_its_source_during_reconfiguration( "codex-api-key:\n - api-key: preserved-key\n base-url: https://example.test\n models:\n - name: model-a\n alias: {opus}\n context-length: 123456\n", opus = CLAUDE_DESKTOP_OPUS_MODEL_ID, ); - // Older configurations can retain only the aliased entry for the upstream model. let result = ensure_claude_desktop_model_aliases_in_yaml( &input, &ClaudeDesktopModelMappings::all("model-a"), @@ -169,7 +168,6 @@ fn legacy_desktop_alias_source_is_resolved_before_removing_its_route() { "codex-api-key:\n - api-key: preserved-key\n models: [{{name: model-a, alias: {opus}}}]\n", opus = CLAUDE_DESKTOP_OPUS_MODEL_ID, ); - // Legacy callers and restore previews may have model IDs without is_alias metadata. let selected = CLAUDE_DESKTOP_OPUS_MODEL_ID; let updated = ensure_claude_desktop_model_aliases_in_yaml( &input, diff --git a/src/App.tsx b/src/App.tsx index fb4bb6a..034d2a6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,3 +1,4 @@ +import { MessageNotice } from './appNotice'; import { useEffect, useRef, useState } from 'react'; import { invoke } from '@tauri-apps/api/core'; import { listen } from '@tauri-apps/api/event'; @@ -486,9 +487,7 @@ function AppContent() { {t('app.close.description')}

{windowsClosePrompt.error ? ( -
- {windowsClosePrompt.error} -
+ setWindowsClosePrompt(current => current ? { ...current, error: null } : current)} /> ) : null}