Files
clawpanel/src-tauri/src/lib.rs
晴天 328624cf03 chore: release v0.15.0
发布 0.15.0:
- 新增内核版本兼容层、特性门控、低版本阻断和升级提示
- 新增 PATH 中 OpenClaw CLI 冲突检测、隔离与恢复
- 修复 Hermes Gateway loopback 自动拉起与 /v1/runs 诊断
- 修复 standalone 一键安装包在 About/仪表盘显示未知版本
- 同步 OpenClaw 2026.5.6 推荐版本和热更新 minAppVersion
- 补齐本地 JS/Rust 测试与发布前检查说明

验证:
- npm run build
- node --test tests/*.test.js
- node --check src/scripts JS 文件
- cargo fmt --all -- --check
- cargo check
- cargo clippy --all-targets -- -D warnings
- cargo test
2026-05-08 04:39:36 +08:00

344 lines
13 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
mod commands;
mod models;
mod tray;
mod utils;
use commands::{
agent, assistant, cli_conflict, config, device, diagnose, extensions, hermes, hermes_providers,
logs, memory, messaging, pairing, service, skills, update,
};
pub fn run() {
let hot_update_dir = commands::openclaw_dir()
.join("clawpanel")
.join("web-update");
// issue #261: 装新版 app 时,如果旧的热更新目录里装的是更旧版本的前端
// (或根本没 .version 标记protocol handler 会优先读它,导致
// 用户装了 v0.14.0 看到的还是 v0.9.8 的界面。
// 启动时先比对版本,落后于当前 app 就直接清掉,让 protocol handler 回退到内嵌 bundle。
cleanup_stale_hot_update(&hot_update_dir);
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_autostart::init(
tauri_plugin_autostart::MacosLauncher::LaunchAgent,
None,
))
.register_uri_scheme_protocol("tauri", move |ctx, request| {
let uri_path = request.uri().path();
let path = if uri_path == "/" || uri_path.is_empty() {
"index.html"
} else {
uri_path.strip_prefix('/').unwrap_or(uri_path)
};
// 1. 优先检查热更新目录
let update_file = hot_update_dir.join(path);
if update_file.is_file() {
if let Ok(data) = std::fs::read(&update_file) {
return tauri::http::Response::builder()
.header(
tauri::http::header::CONTENT_TYPE,
update::mime_from_path(path),
)
.body(data)
.unwrap();
}
}
// 2. 回退到内嵌资源
if let Some(asset) = ctx.app_handle().asset_resolver().get(path.to_string()) {
let builder = tauri::http::Response::builder()
.header(tauri::http::header::CONTENT_TYPE, &asset.mime_type);
// Tauri 内嵌资源可能带 CSP header
let builder = if let Some(csp) = asset.csp_header {
builder.header("Content-Security-Policy", csp)
} else {
builder
};
builder.body(asset.bytes).unwrap()
} else {
tauri::http::Response::builder()
.status(tauri::http::StatusCode::NOT_FOUND)
.body(b"Not Found".to_vec())
.unwrap()
}
})
.setup(|app| {
service::start_backend_guardian(app.handle().clone());
tray::setup_tray(app.handle())?;
Ok(())
})
.invoke_handler(tauri::generate_handler![
// 配置
config::read_openclaw_config,
config::write_openclaw_config,
config::validate_openclaw_config,
config::read_mcp_config,
config::write_mcp_config,
config::get_version_info,
config::check_installation,
config::init_openclaw_config,
config::calibrate_openclaw_config,
config::check_node,
config::check_node_at_path,
config::check_openclaw_at_path,
config::scan_node_paths,
config::scan_openclaw_paths,
config::save_custom_node_path,
config::write_env_file,
config::list_backups,
config::create_backup,
config::restore_backup,
config::delete_backup,
config::reload_gateway,
config::restart_gateway,
config::test_model,
config::test_model_verbose,
config::list_remote_models,
config::list_openclaw_versions,
config::upgrade_openclaw,
config::uninstall_openclaw,
config::install_gateway,
config::uninstall_gateway,
config::patch_model_vision,
config::check_panel_update,
config::get_openclaw_dir,
config::read_panel_config,
config::write_panel_config,
config::test_proxy,
config::get_npm_registry,
config::set_npm_registry,
config::check_git,
config::scan_git_paths,
config::auto_install_git,
config::configure_git_https,
config::invalidate_path_cache,
config::get_status_summary,
config::doctor_fix,
config::doctor_check,
config::relaunch_app,
// 设备密钥 + Gateway 握手
device::create_connect_frame,
// 设备配对
pairing::auto_pair_device,
pairing::check_pairing_status,
pairing::pairing_list_channel,
pairing::pairing_approve_channel,
// 服务
service::get_services_status,
service::start_service,
service::stop_service,
service::restart_service,
service::claim_gateway,
service::probe_gateway_port,
service::guardian_status,
// 诊断
diagnose::diagnose_gateway_connection,
diagnose::check_ciao_windowshide_bug,
// CLI 冲突检测与隔离PATH 中残留的非 standalone openclaw
cli_conflict::scan_openclaw_path_conflicts,
cli_conflict::quarantine_openclaw_path,
cli_conflict::quarantine_openclaw_paths_bulk,
cli_conflict::list_quarantined_openclaw,
cli_conflict::restore_quarantined_openclaw,
// 日志
logs::read_log_tail,
logs::search_log,
// 记忆文件
memory::list_memory_files,
memory::read_memory_file,
memory::write_memory_file,
memory::delete_memory_file,
memory::export_memory_zip,
// 扩展工具
extensions::get_cftunnel_status,
extensions::cftunnel_action,
extensions::get_cftunnel_logs,
extensions::get_clawapp_status,
extensions::install_cftunnel,
extensions::install_clawapp,
// Agent 管理
agent::list_agents,
agent::get_agent_detail,
agent::list_agent_files,
agent::read_agent_file,
agent::write_agent_file,
agent::get_agent_workspace_info,
agent::list_agent_workspace_entries,
agent::read_agent_workspace_file,
agent::write_agent_workspace_file,
agent::add_agent,
agent::delete_agent,
agent::update_agent_config,
agent::update_agent_identity,
agent::update_agent_model,
agent::backup_agent,
// AI 助手工具
assistant::assistant_exec,
assistant::assistant_read_file,
assistant::assistant_write_file,
assistant::assistant_list_dir,
assistant::assistant_system_info,
assistant::assistant_list_processes,
assistant::assistant_check_port,
assistant::assistant_web_search,
assistant::assistant_fetch_url,
// 数据目录 & 图片存储
assistant::assistant_ensure_data_dir,
assistant::assistant_save_image,
assistant::assistant_load_image,
assistant::assistant_delete_image,
// 消息渠道管理
messaging::read_platform_config,
messaging::save_messaging_platform,
messaging::remove_messaging_platform,
messaging::toggle_messaging_platform,
messaging::verify_bot_token,
messaging::diagnose_channel,
messaging::repair_qqbot_channel_setup,
messaging::list_configured_platforms,
messaging::get_channel_plugin_status,
messaging::list_all_plugins,
messaging::toggle_plugin,
messaging::install_plugin,
messaging::install_channel_plugin,
messaging::install_qqbot_plugin,
messaging::run_channel_action,
messaging::check_weixin_plugin_status,
// Agent 渠道绑定管理
messaging::get_agent_bindings,
messaging::list_all_bindings,
messaging::save_agent_binding,
messaging::delete_agent_binding,
messaging::delete_agent_all_bindings,
// Skills 管理
skills::skills_list,
skills::skills_info,
skills::skills_check,
skills::skills_install_dep,
skills::skills_uninstall,
skills::skills_validate,
// SkillHub SDK内置 HTTP不依赖 CLI
skills::skillhub_search,
skills::skillhub_index,
skills::skillhub_install,
// 前端热更新
update::check_frontend_update,
update::download_frontend_update,
update::rollback_frontend_update,
update::get_update_status,
// Hermes Agent 管理
hermes::check_python,
hermes::check_hermes,
hermes::install_hermes,
hermes::configure_hermes,
hermes::hermes_gateway_action,
hermes::hermes_health_check,
hermes::hermes_api_proxy,
hermes::hermes_agent_run,
hermes::hermes_read_config,
hermes::hermes_fetch_models,
hermes::hermes_update_model,
hermes::hermes_detect_environments,
hermes_providers::hermes_list_providers,
hermes::hermes_env_read_unmanaged,
hermes::hermes_env_set,
hermes::hermes_env_delete,
hermes::hermes_env_reveal,
hermes::hermes_config_raw_read,
hermes::hermes_config_raw_write,
hermes::hermes_set_gateway_url,
hermes::update_hermes,
hermes::uninstall_hermes,
hermes::hermes_sessions_list,
hermes::hermes_sessions_summary_list,
hermes::hermes_usage_analytics,
hermes::hermes_session_detail,
hermes::hermes_session_delete,
hermes::hermes_session_rename,
hermes::hermes_profiles_list,
hermes::hermes_profile_use,
hermes::hermes_logs_list,
hermes::hermes_logs_read,
hermes::hermes_skills_list,
hermes::hermes_skill_detail,
hermes::hermes_skill_toggle,
hermes::hermes_skill_files,
hermes::hermes_skill_write,
hermes::hermes_memory_read,
hermes::hermes_memory_write,
hermes::hermes_memory_read_all,
hermes::hermes_logs_download,
hermes::hermes_dashboard_themes,
hermes::hermes_dashboard_theme_set,
hermes::hermes_dashboard_plugins,
hermes::hermes_dashboard_plugins_rescan,
hermes::hermes_dashboard_probe,
hermes::hermes_dashboard_start,
hermes::hermes_dashboard_stop,
hermes::hermes_toolsets_list,
hermes::hermes_cron_jobs_list,
])
.on_window_event(|window, event| {
// 关闭窗口时最小化到托盘,不退出应用
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
api.prevent_close();
let _ = window.hide();
}
})
.build(tauri::generate_context!())
.expect("启动 ClawPanel 失败")
.run(|_app, event| {
if let tauri::RunEvent::Exit = event {
#[cfg(target_os = "windows")]
{
// 退出时关闭 Gateway 终端窗口
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x08000000;
let _ = std::process::Command::new("cmd")
.args(["/c", "taskkill", "/fi", "WINDOWTITLE eq OpenClaw Gateway"])
.creation_flags(CREATE_NO_WINDOW)
.output();
}
}
});
}
/// 启动时清理落后版本的热更新目录issue #261
///
/// 规则:
/// - 目录不存在noop
/// - 目录存在,`.version` 文件不存在:视为"版本未知",保守清理
/// (老版 ClawPanel 没写 .version不清理就会永远卡在旧前端
/// - 目录存在,`.version >= app 版本`:保留(正常热更新场景)
/// - 目录存在,`.version < app 版本`:清理(用户装了新 app旧热更新残留
fn cleanup_stale_hot_update(dir: &std::path::Path) {
if !dir.exists() {
return;
}
let app_version = env!("CARGO_PKG_VERSION");
let web_version = std::fs::read_to_string(dir.join(".version"))
.ok()
.map(|s| s.trim().to_string())
.unwrap_or_default();
// 有标记且 >= app 版本:保留
if !web_version.is_empty() && update::version_ge(&web_version, app_version) {
return;
}
// 落后或无标记:清理,让 protocol handler 回退到 asset_resolver
eprintln!(
"[clawpanel] clearing stale web-update dir (app={}, web={})",
app_version,
if web_version.is_empty() {
"<missing>"
} else {
web_version.as_str()
}
);
let _ = std::fs::remove_dir_all(dir);
}