mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-06-06 08:09:54 +08:00
紧急修复:mode 字段位置错误导致 Gateway 无法启动 (v0.4.6)
根因:openclaw.json 的 mode 属于 gateway 对象内部,不是顶层字段。 OpenClaw zod-schema 对顶层 mode 报 Unrecognized key 错误。 修复: - config.rs init_openclaw_config: mode 移入 gateway 对象 - dev-api.js init_openclaw_config: 同上 - dashboard.js 自愈: config.mode → config.gateway.mode - dashboard.js 自愈: 自动删除旧版错误的顶层 mode 字段 - setup.js 安装流程: config.mode → config.gateway.mode
This commit is contained in:
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@@ -328,7 +328,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clawpanel"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clawpanel"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
edition = "2021"
|
||||
description = "ClawPanel - OpenClaw 可视化管理面板"
|
||||
authors = ["qingchencloud"]
|
||||
|
||||
@@ -658,9 +658,9 @@ pub fn init_openclaw_config() -> Result<Value, String> {
|
||||
let default_config = serde_json::json!({
|
||||
"$schema": "https://openclaw.ai/schema/config.json",
|
||||
"meta": { "lastTouchedVersion": "2026.1.1" },
|
||||
"mode": "local",
|
||||
"models": { "providers": {} },
|
||||
"gateway": {
|
||||
"mode": "local",
|
||||
"port": 18789,
|
||||
"auth": { "mode": "none" },
|
||||
"controlUi": { "allowedOrigins": ["*"], "allowInsecureAuth": true }
|
||||
|
||||
@@ -35,7 +35,7 @@ pub fn enhanced_path() -> String {
|
||||
})
|
||||
.flatten();
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let mut extra: Vec<String> = vec![
|
||||
"/usr/local/bin".into(),
|
||||
@@ -69,6 +69,58 @@ pub fn enhanced_path() -> String {
|
||||
parts.join(":")
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let mut extra: Vec<String> = vec![
|
||||
"/usr/local/bin".into(),
|
||||
"/usr/bin".into(),
|
||||
"/snap/bin".into(),
|
||||
format!("{}/.local/bin", home.display()),
|
||||
format!("{}/.nvm/current/bin", home.display()),
|
||||
format!("{}/.volta/bin", home.display()),
|
||||
format!("{}/.nodenv/shims", home.display()),
|
||||
format!("{}/.fnm/current/bin", home.display()),
|
||||
format!("{}/n/bin", home.display()),
|
||||
];
|
||||
// NVM_DIR 环境变量(用户可能自定义了 nvm 安装目录)
|
||||
let nvm_dir = std::env::var("NVM_DIR")
|
||||
.ok()
|
||||
.map(std::path::PathBuf::from)
|
||||
.unwrap_or_else(|| home.join(".nvm"));
|
||||
let nvm_versions = nvm_dir.join("versions/node");
|
||||
if nvm_versions.is_dir() {
|
||||
if let Ok(entries) = std::fs::read_dir(&nvm_versions) {
|
||||
for entry in entries.flatten() {
|
||||
let bin = entry.path().join("bin");
|
||||
if bin.is_dir() {
|
||||
extra.push(bin.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// nodesource / 手动安装的 Node.js 可能在 /usr/local/lib/nodejs/ 下
|
||||
let nodejs_lib = std::path::Path::new("/usr/local/lib/nodejs");
|
||||
if nodejs_lib.is_dir() {
|
||||
if let Ok(entries) = std::fs::read_dir(nodejs_lib) {
|
||||
for entry in entries.flatten() {
|
||||
let bin = entry.path().join("bin");
|
||||
if bin.is_dir() {
|
||||
extra.push(bin.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut parts: Vec<&str> = vec![];
|
||||
if let Some(ref cp) = custom_path {
|
||||
parts.push(cp.as_str());
|
||||
}
|
||||
parts.extend(extra.iter().map(|s| s.as_str()));
|
||||
if !current.is_empty() {
|
||||
parts.push(¤t);
|
||||
}
|
||||
parts.join(":")
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let pf = std::env::var("ProgramFiles").unwrap_or_else(|_| r"C:\Program Files".into());
|
||||
|
||||
@@ -381,6 +381,7 @@ mod platform {
|
||||
pub async fn is_cli_installed() -> bool {
|
||||
Command::new("openclaw")
|
||||
.arg("--version")
|
||||
.env("PATH", crate::commands::enhanced_path())
|
||||
.output()
|
||||
.await
|
||||
.map(|o| o.status.success())
|
||||
@@ -391,14 +392,42 @@ mod platform {
|
||||
vec!["ai.openclaw.gateway".to_string()]
|
||||
}
|
||||
|
||||
/// 从 openclaw.json 读取 gateway 端口,fallback 到 18789
|
||||
fn read_gateway_port() -> u16 {
|
||||
let config_path = crate::commands::openclaw_dir().join("openclaw.json");
|
||||
if let Ok(content) = std::fs::read_to_string(&config_path) {
|
||||
if let Ok(val) = serde_json::from_str::<serde_json::Value>(&content) {
|
||||
if let Some(port) = val
|
||||
.get("gateway")
|
||||
.and_then(|g| g.get("port"))
|
||||
.and_then(|p| p.as_u64())
|
||||
{
|
||||
if port > 0 && port < 65536 {
|
||||
return port as u16;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18789
|
||||
}
|
||||
|
||||
pub async fn check_service_status(_uid: u32, _label: &str) -> (bool, Option<u32>) {
|
||||
let port = read_gateway_port();
|
||||
let addr = format!("127.0.0.1:{port}");
|
||||
match std::net::TcpStream::connect_timeout(
|
||||
&"127.0.0.1:18789".parse().unwrap(),
|
||||
&addr
|
||||
.parse()
|
||||
.unwrap_or_else(|_| "127.0.0.1:18789".parse().unwrap()),
|
||||
std::time::Duration::from_secs(2),
|
||||
) {
|
||||
Ok(_) => (true, None),
|
||||
Err(_) => {
|
||||
if let Ok(output) = Command::new("openclaw").arg("health").output().await {
|
||||
if let Ok(output) = Command::new("openclaw")
|
||||
.arg("health")
|
||||
.env("PATH", crate::commands::enhanced_path())
|
||||
.output()
|
||||
.await
|
||||
{
|
||||
let text = String::from_utf8_lossy(&output.stdout);
|
||||
if output.status.success() && !text.contains("not running") {
|
||||
return (true, None);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-config-schema/schema.json",
|
||||
"productName": "ClawPanel",
|
||||
"version": "0.4.5",
|
||||
"version": "0.4.6",
|
||||
"identifier": "ai.openclaw.clawpanel",
|
||||
"build": {
|
||||
"frontendDist": "../dist",
|
||||
|
||||
Reference in New Issue
Block a user