-
+
通用版
.AppImage
-
+
Debian / Ubuntu
.deb
diff --git a/package-lock.json b/package-lock.json
index 0b55a19..cc396e6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "clawpanel",
- "version": "0.11.3",
+ "version": "0.11.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "clawpanel",
- "version": "0.11.3",
+ "version": "0.11.4",
"license": "AGPL-3.0",
"dependencies": {
"@tauri-apps/api": "^2.5.0",
diff --git a/package.json b/package.json
index d5c7e51..7d64d39 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "clawpanel",
- "version": "0.11.3",
+ "version": "0.11.4",
"private": true,
"description": "ClawPanel - OpenClaw 可视化管理面板,基于 Tauri v2 的跨平台桌面应用",
"type": "module",
diff --git a/scripts/dev-api.js b/scripts/dev-api.js
index bfd9816..ac3c6ad 100644
--- a/scripts/dev-api.js
+++ b/scripts/dev-api.js
@@ -2391,6 +2391,14 @@ function clearGatewayOwner() {
} catch {}
}
+function shouldAutoClaimGateway(owner) {
+ const current = currentGatewayOwnerSignature()
+ if (!owner) return true // 无 owner 文件 → 自动认领
+ // owner 文件存在但签名不完全匹配 → 仅按 port + openclaw_dir 判断
+ return Number(owner.port || 0) === current.port
+ && !!owner.openclawDir && path.resolve(owner.openclawDir) === current.openclawDir
+}
+
function foreignGatewayError(pid = null) {
const port = readGatewayPort()
const pidText = pid ? ` (PID: ${pid})` : ''
@@ -2403,6 +2411,11 @@ function ensureOwnedGatewayOrThrow(pid = null) {
if (gatewayOwnerPidNeedsRefresh(owner, pid)) writeGatewayOwner(pid)
return true
}
+ // 无有效 owner 或签名不匹配 → 尝试自动认领(端口 + 数据目录匹配即可)
+ if (shouldAutoClaimGateway(owner)) {
+ writeGatewayOwner(pid)
+ return true
+ }
throw foreignGatewayError(pid)
}
@@ -2992,10 +3005,15 @@ const handlers = {
const cliInstalled = !!resolveOpenclawCliPath()
const owner = readGatewayOwner()
- const ownedByCurrentInstance = !!running && isCurrentGatewayOwner(owner, pid || null)
+ let ownedByCurrentInstance = !!running && isCurrentGatewayOwner(owner, pid || null)
if (ownedByCurrentInstance && gatewayOwnerPidNeedsRefresh(owner, pid || null)) {
writeGatewayOwner(pid || null)
}
+ // 自动认领:Gateway 在运行但无有效 owner,且端口 + 数据目录匹配
+ if (running && !ownedByCurrentInstance && shouldAutoClaimGateway(owner)) {
+ writeGatewayOwner(pid || null)
+ ownedByCurrentInstance = true
+ }
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 a38d91e..e116366 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -351,7 +351,7 @@ dependencies = [
[[package]]
name = "clawpanel"
-version = "0.11.3"
+version = "0.11.4"
dependencies = [
"base64 0.22.1",
"chrono",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index d0eeec6..c07b044 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clawpanel"
-version = "0.11.3"
+version = "0.11.4"
edition = "2021"
description = "ClawPanel - OpenClaw 可视化管理面板"
authors = ["qingchencloud"]
diff --git a/src-tauri/src/commands/service.rs b/src-tauri/src/commands/service.rs
index a45267b..467ba3f 100644
--- a/src-tauri/src/commands/service.rs
+++ b/src-tauri/src/commands/service.rs
@@ -147,6 +147,19 @@ fn is_current_gateway_owner(owner: &GatewayOwnerRecord, _pid: Option
) -> bo
matches_current_gateway_owner_signature(owner)
}
+/// 判断是否可以安全地自动认领 Gateway:端口 + 数据目录匹配即可(忽略 started_by)
+fn should_auto_claim_gateway(owner: &Option) -> bool {
+ let (port, openclaw_dir, _cli_path) = current_gateway_owner_signature();
+ match owner {
+ None => true, // 无 owner 文件 → 自动认领
+ Some(record) => {
+ // owner 文件存在但签名不完全匹配 → 仅按 port + openclaw_dir 判断
+ record.port == port
+ && normalize_owned_path(&record.openclaw_dir) == openclaw_dir
+ }
+ }
+}
+
fn foreign_gateway_error(pid: Option) -> String {
let pid_suffix = pid
.map(|value| format!(" (PID: {value})"))
@@ -159,14 +172,20 @@ fn foreign_gateway_error(pid: Option) -> String {
}
fn ensure_owned_gateway_or_err(pid: Option) -> Result<(), String> {
- if let Some(owner) = read_gateway_owner() {
- if is_current_gateway_owner(&owner, pid) {
- if gateway_owner_pid_needs_refresh(&owner, pid) {
+ let owner = read_gateway_owner();
+ if let Some(ref record) = owner {
+ if is_current_gateway_owner(record, pid) {
+ if gateway_owner_pid_needs_refresh(record, pid) {
write_gateway_owner(pid)?;
}
return Ok(());
}
}
+ // 无有效 owner 或签名不匹配 → 尝试自动认领(端口 + 数据目录匹配即可)
+ if should_auto_claim_gateway(&owner) {
+ write_gateway_owner(pid)?;
+ return Ok(());
+ }
Err(foreign_gateway_error(pid))
}
@@ -1622,7 +1641,7 @@ pub async fn get_services_status() -> Result, String> {
for label in labels.iter().map(String::as_str) {
let (running, pid) = current_gateway_runtime(label).await;
let owner = read_gateway_owner();
- let owned_by_current_instance = running
+ let mut owned_by_current_instance = running
&& owner
.as_ref()
.map(|record| is_current_gateway_owner(record, pid))
@@ -1634,6 +1653,11 @@ pub async fn get_services_status() -> Result, String> {
}
}
}
+ // 自动认领:Gateway 在运行但无有效 owner,且端口 + 数据目录匹配 → 自动写入 owner
+ if running && !owned_by_current_instance && should_auto_claim_gateway(&owner) {
+ let _ = write_gateway_owner(pid);
+ owned_by_current_instance = true;
+ }
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 517198d..322d96a 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.3",
+ "version": "0.11.4",
"identifier": "ai.openclaw.clawpanel",
"build": {
"frontendDist": "../dist",