From a4c3a12a2688601bc1498ae0912099393a93f775 Mon Sep 17 00:00:00 2001 From: SEVENTEEN-TAN Date: Fri, 3 Apr 2026 06:55:10 +0000 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E6=9F=A5=E7=9C=8B=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: traeagent --- scripts/dev-api.js | 8 ++++++++ src-tauri/src/utils.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/scripts/dev-api.js b/scripts/dev-api.js index 669d0e7f..8092e571 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' 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(); From c9ec6b5765f4aaf9a232f53c67e65facee7a26f1 Mon Sep 17 00:00:00 2001 From: SEVENTEEN-TAN Date: Fri, 3 Apr 2026 06:56:44 +0000 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=E6=9F=A5=E7=9C=8B=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: traeagent --- scripts/dev-api.js | 3 --- src-tauri/src/commands/service.rs | 9 +++------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/scripts/dev-api.js b/scripts/dev-api.js index 8092e571..d19edd9e 100644 --- a/scripts/dev-api.js +++ b/scripts/dev-api.js @@ -2170,9 +2170,6 @@ function matchesCurrentGatewayOwnerSignature(owner) { if (!owner || owner.startedBy !== 'clawpanel') return false const current = currentGatewayOwnerSignature() if (Number(owner.port || 0) !== current.port) return false - if (!current.cliPath) return false - const ownerCliPath = canonicalCliPath(owner.cliPath) - if (!ownerCliPath || ownerCliPath !== current.cliPath) return false if (!owner.openclawDir || path.resolve(owner.openclawDir) !== current.openclawDir) return false return true } diff --git a/src-tauri/src/commands/service.rs b/src-tauri/src/commands/service.rs index 7bff1cc7..8348263c 100644 --- a/src-tauri/src/commands/service.rs +++ b/src-tauri/src/commands/service.rs @@ -95,18 +95,15 @@ fn matches_current_gateway_owner_signature(owner: &GatewayOwnerRecord) -> bool { if owner.started_by != "clawpanel" { return false; } - let (port, openclaw_dir, cli_path) = current_gateway_owner_signature(); + let (port, openclaw_dir, _cli_path) = current_gateway_owner_signature(); if owner.port != port { return false; } if normalize_owned_path(&owner.openclaw_dir) != openclaw_dir { return false; } - let owner_cli_path = owner.cli_path.as_ref().map(normalize_owned_path); - matches!( - (owner_cli_path.as_deref(), cli_path.as_deref()), - (Some(owner_cli), Some(current_cli)) if owner_cli == current_cli - ) + // 放宽 CLI 路径校验:只要由当前面板启动,且端口和目录一致,就不因多实例共存时的 active path 切换而阻止操作 + true } fn gateway_owner_pid_needs_refresh(owner: &GatewayOwnerRecord, pid: Option) -> bool { From d58695c6637df49da403b125cee43a2ace9528f9 Mon Sep 17 00:00:00 2001 From: SEVENTEEN-TAN Date: Fri, 3 Apr 2026 07:00:12 +0000 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E6=9F=A5=E7=9C=8B=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: traeagent --- scripts/dev-api.js | 7 ++++++- src-tauri/src/commands/service.rs | 16 ++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/scripts/dev-api.js b/scripts/dev-api.js index d19edd9e..0ff8c7cb 100644 --- a/scripts/dev-api.js +++ b/scripts/dev-api.js @@ -2170,6 +2170,9 @@ function matchesCurrentGatewayOwnerSignature(owner) { if (!owner || owner.startedBy !== 'clawpanel') return false const current = currentGatewayOwnerSignature() if (Number(owner.port || 0) !== current.port) return false + if (!current.cliPath) return false + const ownerCliPath = canonicalCliPath(owner.cliPath) + if (!ownerCliPath || ownerCliPath !== current.cliPath) return false if (!owner.openclawDir || path.resolve(owner.openclawDir) !== current.openclawDir) return false return true } @@ -2855,15 +2858,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 8348263c..39b5b80d 100644 --- a/src-tauri/src/commands/service.rs +++ b/src-tauri/src/commands/service.rs @@ -95,15 +95,18 @@ fn matches_current_gateway_owner_signature(owner: &GatewayOwnerRecord) -> bool { if owner.started_by != "clawpanel" { return false; } - let (port, openclaw_dir, _cli_path) = current_gateway_owner_signature(); + let (port, openclaw_dir, cli_path) = current_gateway_owner_signature(); if owner.port != port { return false; } if normalize_owned_path(&owner.openclaw_dir) != openclaw_dir { return false; } - // 放宽 CLI 路径校验:只要由当前面板启动,且端口和目录一致,就不因多实例共存时的 active path 切换而阻止操作 - true + let owner_cli_path = owner.cli_path.as_ref().map(normalize_owned_path); + matches!( + (owner_cli_path.as_deref(), cli_path.as_deref()), + (Some(owner_cli), Some(current_cli)) if owner_cli == current_cli + ) } fn gateway_owner_pid_needs_refresh(owner: &GatewayOwnerRecord, pid: Option) -> bool { @@ -205,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; From 6f1c19df79214bbe61369e2578f183407c01ae3e Mon Sep 17 00:00:00 2001 From: SEVENTEEN-TAN Date: Fri, 3 Apr 2026 07:04:13 +0000 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E6=9F=A5=E7=9C=8B=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: traeagent --- scripts/dev-api.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/dev-api.js b/scripts/dev-api.js index 0ff8c7cb..b49f97c0 100644 --- a/scripts/dev-api.js +++ b/scripts/dev-api.js @@ -2246,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))