From 04d74ac48fa46999158cfd248bf2a6e1c062c075 Mon Sep 17 00:00:00 2001 From: wonfen Date: Thu, 24 Sep 2026 20:24:40 +0800 Subject: [PATCH] fix(service): offer repair when a residual helper blocks Sidecar A macOS helper left running without IPC made Sidecar refuse to start while the service counted as not installed, so no repair was offered (#8031, #8043). Treat such a helper as install evidence and record the Sidecar refusal as unavailable so the repair dialog appears. --- Changelog.md | 1 + src-tauri/src/core/manager/state.rs | 4 +++- src-tauri/src/core/runstate/env.rs | 12 ++++++++++-- src-tauri/src/core/service.rs | 7 +++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 862edfda9..a0fce3b8b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,7 @@ **🍎 macOS** - macOS 修复编辑规则时「规则内容」输入的首字母被自动转为大写的问题 +- macOS 修复残留的服务进程导致内核无法启动、「继续使用 Sidecar」失败且无法修复的问题 diff --git a/src-tauri/src/core/manager/state.rs b/src-tauri/src/core/manager/state.rs index a10f6bac8..346485ac7 100644 --- a/src-tauri/src/core/manager/state.rs +++ b/src-tauri/src/core/manager/state.rs @@ -153,7 +153,9 @@ impl CoreManager { #[tracing::instrument(skip_all, level = "info", fields(pid = tracing::field::Empty))] pub(super) async fn start_core_by_sidecar(&self) -> Result<()> { self.wait_for_sidecar_exit().await?; - let execution = clash_verge_service_ipc::execution::reserve_sidecar().await?; + let execution = clash_verge_service_ipc::execution::reserve_sidecar() + .await + .inspect_err(service::record_residual_service)?; self.core_stopped(); let sidecar_ipc = dirs::sidecar_ipc_path()?; diff --git a/src-tauri/src/core/runstate/env.rs b/src-tauri/src/core/runstate/env.rs index 0fd60202d..fdc1436a6 100644 --- a/src-tauri/src/core/runstate/env.rs +++ b/src-tauri/src/core/runstate/env.rs @@ -62,9 +62,17 @@ impl RunStateEnv for RealEnv { } async fn trusted_install_evidence(&self) -> Result { - tokio::task::spawn_blocking(crate::core::service::trusted_service_evidence) + let registered = tokio::task::spawn_blocking(crate::core::service::trusted_service_evidence) .await - .context("service registration probe did not finish")? + .context("service registration probe did not finish")??; + // A helper that outlived its registration is a broken Service, not an absent one. + #[cfg(unix)] + if !registered && let Err(error) = clash_verge_service_ipc::execution::check_sidecar_available().await { + return Ok(error + .downcast_ref::() + .is_some()); + } + Ok(registered) } fn is_elevated(&self) -> bool { diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs index ae057082e..269ce5191 100644 --- a/src-tauri/src/core/service.rs +++ b/src-tauri/src/core/service.rs @@ -805,6 +805,13 @@ fn record_service_start_refusal( refusal.into() } +/// Sidecar stays blocked until a reinstall replaces the residual helper, so ask for repair. +pub(super) fn record_residual_service(error: &anyhow::Error) { + if let Some(residual) = error.downcast_ref::() { + RUN_STATE.observe(ServiceHealth::Unavailable(residual.to_string())); + } +} + /// 尝试使用服务启动core #[tracing::instrument(skip_all, level = "info", fields(generation = tracing::field::Empty, staging = tracing::field::Empty, code = tracing::field::Empty, outcome = tracing::field::Empty))] pub(super) async fn start_with_existing_service(config_file: &Path) -> Result<()> {