-
+
通用版
.AppImage
-
+
Debian / Ubuntu
.deb
diff --git a/package-lock.json b/package-lock.json
index ef04cb2..908e6e9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "clawpanel",
- "version": "0.11.1",
+ "version": "0.11.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "clawpanel",
- "version": "0.11.1",
+ "version": "0.11.2",
"license": "AGPL-3.0",
"dependencies": {
"@tauri-apps/api": "^2.5.0",
diff --git a/package.json b/package.json
index 43ca54e..8cc7dd8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "clawpanel",
- "version": "0.11.1",
+ "version": "0.11.2",
"private": true,
"description": "ClawPanel - OpenClaw 可视化管理面板,基于 Tauri v2 的跨平台桌面应用",
"type": "module",
diff --git a/scripts/dev-api.js b/scripts/dev-api.js
index 583b81e..0396f7d 100644
--- a/scripts/dev-api.js
+++ b/scripts/dev-api.js
@@ -2125,7 +2125,7 @@ function currentGatewayOwnerSignature() {
}
}
-function isCurrentGatewayOwner(owner, pid = null) {
+function matchesCurrentGatewayOwnerSignature(owner) {
if (!owner || owner.startedBy !== 'clawpanel') return false
const current = currentGatewayOwnerSignature()
if (Number(owner.port || 0) !== current.port) return false
@@ -2133,10 +2133,19 @@ function isCurrentGatewayOwner(owner, pid = null) {
const ownerCliPath = canonicalCliPath(owner.cliPath)
if (!ownerCliPath || ownerCliPath !== current.cliPath) return false
if (!owner.openclawDir || path.resolve(owner.openclawDir) !== current.openclawDir) return false
- if (pid != null && owner.pid != null && Number(owner.pid) !== Number(pid)) return false
return true
}
+function gatewayOwnerPidNeedsRefresh(owner, pid = null) {
+ if (!matchesCurrentGatewayOwnerSignature(owner)) return false
+ if (!Number.isInteger(pid) || pid <= 0) return false
+ return !Number.isInteger(owner?.pid) || Number(owner.pid) !== Number(pid)
+}
+
+function isCurrentGatewayOwner(owner, pid = null) {
+ return matchesCurrentGatewayOwnerSignature(owner)
+}
+
function writeGatewayOwner(pid = null) {
const ownerPath = gatewayOwnerFilePath()
const ownerDir = path.dirname(ownerPath)
@@ -2164,7 +2173,11 @@ function foreignGatewayError(pid = null) {
}
function ensureOwnedGatewayOrThrow(pid = null) {
- if (isCurrentGatewayOwner(readGatewayOwner(), pid)) return true
+ const owner = readGatewayOwner()
+ if (isCurrentGatewayOwner(owner, pid)) {
+ if (gatewayOwnerPidNeedsRefresh(owner, pid)) writeGatewayOwner(pid)
+ return true
+ }
throw foreignGatewayError(pid)
}
@@ -2753,7 +2766,11 @@ const handlers = {
}
const cliInstalled = !!resolveOpenclawCliPath()
- const ownedByCurrentInstance = !!running && isCurrentGatewayOwner(readGatewayOwner(), pid || null)
+ const owner = readGatewayOwner()
+ const ownedByCurrentInstance = !!running && isCurrentGatewayOwner(owner, pid || null)
+ if (ownedByCurrentInstance && gatewayOwnerPidNeedsRefresh(owner, pid || null)) {
+ writeGatewayOwner(pid || null)
+ }
const ownership = !running ? 'stopped' : ownedByCurrentInstance ? 'owned' : 'foreign'
return [{ label, running, pid, description: 'OpenClaw Gateway', cli_installed: cliInstalled, ownership, owned_by_current_instance: ownedByCurrentInstance }]
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 4a9dd20..a7b0f2b 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -351,7 +351,7 @@ dependencies = [
[[package]]
name = "clawpanel"
-version = "0.11.1"
+version = "0.11.2"
dependencies = [
"base64 0.22.1",
"chrono",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index efc388e..ffb4a65 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clawpanel"
-version = "0.11.1"
+version = "0.11.2"
edition = "2021"
description = "ClawPanel - OpenClaw 可视化管理面板"
authors = ["qingchencloud"]
diff --git a/src-tauri/src/commands/service.rs b/src-tauri/src/commands/service.rs
index f1b95c2..7bff1cc 100644
--- a/src-tauri/src/commands/service.rs
+++ b/src-tauri/src/commands/service.rs
@@ -91,6 +91,29 @@ fn current_gateway_owner_signature() -> (u16, String, Option
) {
)
}
+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();
+ 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
+ )
+}
+
+fn gateway_owner_pid_needs_refresh(owner: &GatewayOwnerRecord, pid: Option) -> bool {
+ matches_current_gateway_owner_signature(owner)
+ && matches!(pid, Some(current_pid) if owner.pid != Some(current_pid))
+}
+
fn read_gateway_owner() -> Option {
let content = std::fs::read_to_string(gateway_owner_path()).ok()?;
serde_json::from_str(&content).ok()
@@ -119,35 +142,8 @@ fn clear_gateway_owner() {
let _ = std::fs::remove_file(gateway_owner_path());
}
-fn is_current_gateway_owner(owner: &GatewayOwnerRecord, pid: Option) -> bool {
- if owner.started_by != "clawpanel" {
- return false;
- }
- 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);
- match (owner_cli_path.as_deref(), cli_path.as_deref()) {
- (Some(owner_cli), Some(current_cli)) if owner_cli == current_cli => {}
- _ => return false,
- }
- if let (Some(owner_pid), Some(current_pid)) = (owner.pid, pid) {
- if owner_pid != current_pid {
- return false;
- }
- }
- true
-}
-
-fn is_gateway_owned_by_current_instance(pid: Option) -> bool {
- read_gateway_owner()
- .as_ref()
- .map(|owner| is_current_gateway_owner(owner, pid))
- .unwrap_or(false)
+fn is_current_gateway_owner(owner: &GatewayOwnerRecord, _pid: Option) -> bool {
+ matches_current_gateway_owner_signature(owner)
}
fn foreign_gateway_error(pid: Option) -> String {
@@ -162,11 +158,15 @@ fn foreign_gateway_error(pid: Option) -> String {
}
fn ensure_owned_gateway_or_err(pid: Option) -> Result<(), String> {
- if is_gateway_owned_by_current_instance(pid) {
- Ok(())
- } else {
- Err(foreign_gateway_error(pid))
+ if let Some(owner) = read_gateway_owner() {
+ if is_current_gateway_owner(&owner, pid) {
+ if gateway_owner_pid_needs_refresh(&owner, pid) {
+ write_gateway_owner(pid)?;
+ }
+ return Ok(());
+ }
}
+ Err(foreign_gateway_error(pid))
}
async fn current_gateway_runtime(label: &str) -> (bool, Option) {
@@ -845,6 +845,9 @@ mod platform {
kill_process_tree(pid);
}
}
+ } else {
+ // 读不到命令行时,不做假设,避免误杀其他进程
+ continue;
}
}
}
@@ -1617,7 +1620,19 @@ pub async fn get_services_status() -> Result, String> {
let mut results = Vec::new();
for label in labels.iter().map(String::as_str) {
let (running, pid) = current_gateway_runtime(label).await;
- let owned_by_current_instance = running && is_gateway_owned_by_current_instance(pid);
+ let owner = read_gateway_owner();
+ let owned_by_current_instance = running
+ && owner
+ .as_ref()
+ .map(|record| is_current_gateway_owner(record, pid))
+ .unwrap_or(false);
+ if owned_by_current_instance {
+ if let Some(record) = owner.as_ref() {
+ if gateway_owner_pid_needs_refresh(record, pid) {
+ let _ = write_gateway_owner(pid);
+ }
+ }
+ }
let ownership = if !running {
Some("stopped".to_string())
} else if owned_by_current_instance {
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 8eba9f1..f743790 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -1,7 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-config-schema/schema.json",
"productName": "ClawPanel",
- "version": "0.11.1",
+ "version": "0.11.2",
"identifier": "ai.openclaw.clawpanel",
"build": {
"frontendDist": "../dist",