chore(release): v0.13.3

- 修复 #212 AI 消息气泡空白
- 修复 #215 HTTPS 下 WebSocket Mixed Content
- 修复 #219 多实例版本检测错误
- 修复引擎切换后仪表盘无限加载
- 修复热更新假更新循环(macOS/Linux)
- CI release 构建前自动同步版本号
This commit is contained in:
晴天
2026-04-16 13:55:26 +08:00
parent 55e8365cab
commit 36eaa64bf4
25 changed files with 204 additions and 50 deletions

2
src-tauri/Cargo.lock generated
View File

@@ -351,7 +351,7 @@ dependencies = [
[[package]]
name = "clawpanel"
version = "0.13.2"
version = "0.13.3"
dependencies = [
"base64 0.22.1",
"chrono",

View File

@@ -1,6 +1,6 @@
[package]
name = "clawpanel"
version = "0.13.2"
version = "0.13.3"
edition = "2021"
description = "ClawPanel - OpenClaw 可视化管理面板"
authors = ["qingchencloud"]

View File

@@ -1891,6 +1891,22 @@ pub fn write_mcp_config(config: Value) -> Result<(), String> {
/// macOS: 优先从 npm 包的 package.json 读取含完整后缀fallback 到 CLI
/// Windows/Linux: 优先读文件系统fallback 到 CLI
async fn get_local_version() -> Option<String> {
// Fix #219: 优先从运行中的 openclaw 实例获取版本,避免多实例共存时读取到非活跃安装的版本
if let Ok(output) = crate::utils::openclaw_command_async()
.args(["status", "--json"])
.output()
.await
{
if output.status.success() {
let stdout = String::from_utf8_lossy(&output.stdout);
if let Some(ver) = crate::commands::skills::extract_json_pub(&stdout)
.and_then(|v| v.get("runtimeVersion")?.as_str().map(String::from))
{
return Some(ver);
}
}
}
#[cfg(target_os = "macos")]
{
if let Some(cli_path) = crate::utils::resolve_openclaw_cli_path() {
@@ -2638,6 +2654,17 @@ fn read_version_from_installation(cli_path: &std::path::Path) -> Option<String>
}
}
}
// CLI 本体位于包目录中时(如 npm 全局安装nvm、Homebrew 等),
// 直接读取同目录的 package.json即该包自身的版本文件
let own_pkg = dir.join("package.json");
if let Ok(content) = std::fs::read_to_string(&own_pkg) {
if let Some(ver) = serde_json::from_str::<serde_json::Value>(&content)
.ok()
.and_then(|v| v.get("version")?.as_str().map(String::from))
{
return Some(ver);
}
}
// 根据 CLI 路径判断来源,决定 package.json 检查顺序
// 避免残留的另一来源包被优先读取
let cli_source = crate::utils::classify_cli_source(&cli_path.to_string_lossy());

View File

@@ -606,7 +606,7 @@ pub fn check_hermes() -> Result<Value, String> {
// 提取版本号(格式可能是 "Hermes Agent v0.8.0" 或 "0.8.0"
let version = ver_raw
.split_whitespace()
.find(|s| s.starts_with('v') || s.chars().next().map_or(false, |c| c.is_ascii_digit()))
.find(|s| s.starts_with('v') || s.chars().next().is_some_and(|c| c.is_ascii_digit()))
.unwrap_or(&ver_raw)
.trim_start_matches('v')
.to_string();
@@ -1888,7 +1888,7 @@ pub async fn hermes_detect_environments() -> Result<Value, String> {
if let Ok(ip_out) = ip_cmd {
if ip_out.status.success() {
let ip_str = String::from_utf8_lossy(&ip_out.stdout);
let ip = ip_str.trim().split_whitespace().next().unwrap_or("").to_string();
let ip = ip_str.split_whitespace().next().unwrap_or("").to_string();
if !ip.is_empty() {
result["wsl2"]["ip"] = serde_json::json!(ip);
}
@@ -2004,7 +2004,7 @@ pub async fn hermes_set_gateway_url(url: Option<String>) -> Result<String, Strin
};
// 确保 hermes 对象存在
if !config.get("hermes").map_or(false, |v| v.is_object()) {
if !config.get("hermes").is_some_and(|v| v.is_object()) {
config["hermes"] = serde_json::json!({});
}
@@ -2543,8 +2543,7 @@ pub async fn hermes_logs_list() -> Result<Value, String> {
.map(|d| {
let secs = d.as_secs() as i64;
// Simple ISO-ish format
let dt = chrono_simple(secs);
dt
chrono_simple(secs)
})
})
.unwrap_or_default();

View File

@@ -36,7 +36,15 @@ pub async fn check_frontend_update() -> Result<Value, String> {
.unwrap_or("")
.to_string();
let current = env!("CARGO_PKG_VERSION");
// 优先读取已热更新的版本,避免 macOS/Linux 用户安装旧包后永远提示有更新
let current = {
let version_file = update_dir().join(".version");
std::fs::read_to_string(&version_file)
.ok()
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_string())
};
// 检查最低兼容的 app 版本(前端可能依赖较新的 Rust 后端命令)
let min_app = manifest
@@ -44,8 +52,8 @@ pub async fn check_frontend_update() -> Result<Value, String> {
.and_then(|v| v.as_str())
.unwrap_or("0.0.0");
let compatible = version_ge(current, min_app);
let remote_newer = !latest.is_empty() && compatible && version_gt(&latest, current);
let compatible = version_ge(&current, min_app);
let remote_newer = !latest.is_empty() && compatible && version_gt(&latest, &current);
let update_ready = remote_newer && update_dir().join("index.html").exists();
let has_update = remote_newer && !update_ready;
@@ -61,7 +69,7 @@ pub async fn check_frontend_update() -> Result<Value, String> {
/// 下载并解压前端更新包
#[tauri::command]
pub async fn download_frontend_update(url: String, expected_hash: String) -> Result<Value, String> {
pub async fn download_frontend_update(url: String, expected_hash: String, version: String) -> Result<Value, String> {
let client = super::build_http_client(std::time::Duration::from_secs(120), Some("ClawPanel"))
.map_err(|e| format!("HTTP 客户端错误: {e}"))?;
@@ -124,6 +132,11 @@ pub async fn download_frontend_update(url: String, expected_hash: String) -> Res
}
}
// 写入版本号文件,供下次 check_frontend_update 读取
if !version.is_empty() {
let _ = std::fs::write(dir.join(".version"), &version);
}
Ok(serde_json::json!({
"success": true,
"files": archive.len(),

View File

@@ -110,11 +110,8 @@ pub fn resolve_openclaw_cli_path() -> Option<String> {
}
#[cfg(not(target_os = "windows"))]
{
for candidate in common_non_windows_cli_candidates() {
if candidate.exists() {
return Some(candidate.to_string_lossy().to_string());
}
}
// Fix #219: 优先通过 enhanced_path 搜索:其中 nvm/volta 等版本管理器路径排在 Homebrew 前面,
// 与 `which openclaw` 的优先级一致,避免残留的 Homebrew 旧版本被优先检测到
let path = crate::commands::enhanced_path();
let sep = ':';
for dir in path.split(sep) {
@@ -123,6 +120,12 @@ pub fn resolve_openclaw_cli_path() -> Option<String> {
return Some(candidate.to_string_lossy().to_string());
}
}
// 兜底:检查 enhanced_path 可能未覆盖到的固定路径(如 GUI 环境 PATH 受限时)
for candidate in common_non_windows_cli_candidates() {
if candidate.exists() {
return Some(candidate.to_string_lossy().to_string());
}
}
None
}
}

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-config-schema/schema.json",
"productName": "ClawPanel",
"version": "0.13.2",
"version": "0.13.3",
"identifier": "ai.openclaw.clawpanel",
"build": {
"frontendDist": "../dist",