diff --git a/scripts/dev-api.js b/scripts/dev-api.js index 669d0e7f..b49f97c0 100644 --- a/scripts/dev-api.js +++ b/scripts/dev-api.js @@ -173,6 +173,14 @@ function classifyCliSource(cliPath) { if (isWindows) { const shimSource = detectWindowsShimSource(normalized) if (shimSource) return shimSource + } else { + try { + if (fs.lstatSync(normalized).isSymbolicLink()) { + const targetStr = fs.readlinkSync(normalized).toLowerCase() + if (targetStr.includes('openclaw-zh') || targetStr.includes('@qingchencloud')) return 'npm-zh' + if (targetStr.includes('/node_modules/openclaw/') || targetStr.includes('node_modules/openclaw')) return 'npm-official' + } + } catch {} } if (lower.includes('/npm/') || lower.includes('/node_modules/')) return 'npm-official' if (lower.includes('/homebrew/') || lower.includes('/usr/local/bin/') || lower.includes('/usr/bin/')) return 'npm-global' @@ -2238,7 +2246,9 @@ async function waitForGatewayStopped(label = 'ai.openclaw.gateway', timeoutMs = while (Date.now() < deadline) { const status = await getLocalGatewayRuntime(label) if (!status?.running) { - clearGatewayOwner() + if (isCurrentGatewayOwner(readGatewayOwner())) { + clearGatewayOwner() + } return true } await new Promise(resolve => setTimeout(resolve, 300)) @@ -2850,15 +2860,17 @@ const handlers = { if (isMac) { macStopService(label) if (!(await waitForGatewayStopped(label))) throw new Error('Gateway 停止超时') + if (isCurrentGatewayOwner(readGatewayOwner())) clearGatewayOwner() return true } if (isLinux) { linuxStopGateway() if (!(await waitForGatewayStopped(label))) throw new Error('Gateway 停止超时') + if (isCurrentGatewayOwner(readGatewayOwner())) clearGatewayOwner() return true } await winStopGateway() - clearGatewayOwner() + if (isCurrentGatewayOwner(readGatewayOwner())) clearGatewayOwner() return true }, diff --git a/src-tauri/src/commands/service.rs b/src-tauri/src/commands/service.rs index 7bff1cc7..39b5b80d 100644 --- a/src-tauri/src/commands/service.rs +++ b/src-tauri/src/commands/service.rs @@ -208,7 +208,12 @@ async fn wait_for_gateway_stopped(label: &str, timeout: Duration) -> Result<(), while Instant::now() < deadline { let (running, _) = current_gateway_runtime(label).await; if !running { - clear_gateway_owner(); + // 正常停止后,只有当该进程确实是当前面板管理的才清理 owner 文件 + if let Some(owner) = read_gateway_owner() { + if is_current_gateway_owner(&owner, None) { + clear_gateway_owner(); + } + } return Ok(()); } tokio::time::sleep(Duration::from_millis(300)).await; diff --git a/src-tauri/src/utils.rs b/src-tauri/src/utils.rs index 94939939..ff61799c 100644 --- a/src-tauri/src/utils.rs +++ b/src-tauri/src/utils.rs @@ -141,6 +141,33 @@ pub fn classify_cli_source(cli_path: &str) -> String { if lower.contains("openclaw-zh") || lower.contains("@qingchencloud") { return "npm-zh".into(); } + + #[cfg(target_os = "windows")] + { + if let Ok(content) = std::fs::read_to_string(cli_path) { + let content_lower = content.to_lowercase(); + if content_lower.contains("openclaw-zh") || content_lower.contains("@qingchencloud") { + return "npm-zh".into(); + } + if content_lower.contains("/node_modules/openclaw/") || content_lower.contains("\\node_modules\\openclaw\\") { + return "npm-official".into(); + } + } + } + + #[cfg(not(target_os = "windows"))] + { + if let Ok(target) = std::fs::read_link(cli_path) { + let target_str = target.to_string_lossy().to_lowercase(); + if target_str.contains("openclaw-zh") || target_str.contains("@qingchencloud") { + return "npm-zh".into(); + } + if target_str.contains("/node_modules/openclaw/") || target_str.contains("node_modules/openclaw") { + return "npm-official".into(); + } + } + } + // npm 全局(大概率官方版) if lower.contains("/npm/") || lower.contains("/node_modules/") { return "npm-official".into();