Skip to content

Commit 70edd18

Browse files
committed
update
1 parent bcd0d16 commit 70edd18

2 files changed

Lines changed: 130 additions & 8 deletions

File tree

src-tauri/src/commands/gateway.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ pub struct GatewayStatus {
1414
pub pid: Option<u32>,
1515
}
1616

17+
/// Pinned OpenClaw version. 2026.3.31 broke bundled channel plugin loading
18+
/// (Telegram etc. fail to register). Remove pin once upstream fix #58782 ships.
19+
pub const REQUIRED_OPENCLAW_VERSION: &str = "2026.3.28";
20+
1721
pub(crate) fn maxauto_dir() -> PathBuf {
1822
dirs::home_dir()
1923
.expect("Could not determine home directory")
@@ -43,6 +47,125 @@ fn node_binary() -> PathBuf {
4347
PathBuf::from(if cfg!(windows) { "node.exe" } else { "node" })
4448
}
4549

50+
/// Read the installed OpenClaw version from its package.json.
51+
fn installed_openclaw_version() -> Option<String> {
52+
let pkg = maxauto_dir()
53+
.join("openclaw")
54+
.join("node_modules")
55+
.join("openclaw")
56+
.join("package.json");
57+
let raw = std::fs::read_to_string(pkg).ok()?;
58+
let parsed: serde_json::Value = serde_json::from_str(&raw).ok()?;
59+
parsed.get("version")?.as_str().map(|s| s.to_string())
60+
}
61+
62+
/// Ensure the installed OpenClaw version matches `REQUIRED_OPENCLAW_VERSION`.
63+
/// If it doesn't (or isn't installed), reinstall the correct version via npm.
64+
async fn ensure_openclaw_version(app: &AppHandle) -> Result<(), String> {
65+
if let Some(v) = installed_openclaw_version() {
66+
if v == REQUIRED_OPENCLAW_VERSION {
67+
return Ok(());
68+
}
69+
let _ = app.emit("gateway-log", &format!(
70+
"OpenClaw version mismatch: installed {} but require {}. Reinstalling...",
71+
v, REQUIRED_OPENCLAW_VERSION
72+
));
73+
} else {
74+
let _ = app.emit("gateway-log", &format!(
75+
"OpenClaw version unknown. Installing {}...",
76+
REQUIRED_OPENCLAW_VERSION
77+
));
78+
}
79+
80+
let base_dir = maxauto_dir();
81+
let openclaw_prefix = base_dir.join("openclaw");
82+
std::fs::create_dir_all(&openclaw_prefix)
83+
.map_err(|e| format!("Failed to create openclaw dir: {}", e))?;
84+
85+
let node = node_binary();
86+
87+
// Build PATH with local node/git bin dirs
88+
let node_bin_dir = if cfg!(windows) {
89+
base_dir.join("node")
90+
} else {
91+
base_dir.join("node").join("bin")
92+
};
93+
let git_bin_dir = if cfg!(windows) {
94+
base_dir.join("git").join("cmd")
95+
} else {
96+
base_dir.join("git").join("bin")
97+
};
98+
let path_sep = if cfg!(windows) { ";" } else { ":" };
99+
let new_path = {
100+
let mut parts = vec![node_bin_dir.to_string_lossy().to_string()];
101+
if git_bin_dir.exists() {
102+
parts.push(git_bin_dir.to_string_lossy().to_string());
103+
}
104+
if let Ok(existing) = std::env::var("PATH") {
105+
parts.push(existing);
106+
}
107+
parts.join(path_sep)
108+
};
109+
110+
let npm_cache = base_dir.join("npm-cache");
111+
std::fs::create_dir_all(&npm_cache).ok();
112+
113+
let pkg_spec = format!("openclaw@{}", REQUIRED_OPENCLAW_VERSION);
114+
115+
// Find npm-cli.js for local node
116+
let local_npm_cli = if cfg!(windows) {
117+
base_dir.join("node").join("node_modules").join("npm").join("bin").join("npm-cli.js")
118+
} else {
119+
base_dir.join("node").join("lib").join("node_modules").join("npm").join("bin").join("npm-cli.js")
120+
};
121+
122+
let output = if local_npm_cli.exists() {
123+
tokio::process::Command::new(&node)
124+
.env("PATH", &new_path)
125+
.env("npm_config_cache", npm_cache.to_str().unwrap())
126+
.env("GIT_CONFIG_COUNT", "1")
127+
.env("GIT_CONFIG_KEY_0", "url.https://github.com/.insteadOf")
128+
.env("GIT_CONFIG_VALUE_0", "ssh://git@github.com/")
129+
.arg(local_npm_cli.to_str().unwrap())
130+
.args(["install", "--prefix", openclaw_prefix.to_str().unwrap(), &pkg_spec])
131+
.output()
132+
.await
133+
.map_err(|e| format!("npm install failed: {}", e))?
134+
} else {
135+
let npm_cmd = if cfg!(windows) { "npm.cmd" } else { "npm" };
136+
tokio::process::Command::new(npm_cmd)
137+
.env("PATH", &new_path)
138+
.env("npm_config_cache", npm_cache.to_str().unwrap())
139+
.env("GIT_CONFIG_COUNT", "1")
140+
.env("GIT_CONFIG_KEY_0", "url.https://github.com/.insteadOf")
141+
.env("GIT_CONFIG_VALUE_0", "ssh://git@github.com/")
142+
.args(["install", "--prefix", openclaw_prefix.to_str().unwrap(), &pkg_spec])
143+
.output()
144+
.await
145+
.map_err(|e| format!("npm install failed: {}", e))?
146+
};
147+
148+
if !output.status.success() {
149+
let stderr = String::from_utf8_lossy(&output.stderr);
150+
let errors: String = stderr
151+
.lines()
152+
.filter(|l| !l.trim_start().starts_with("npm notice"))
153+
.collect::<Vec<_>>()
154+
.join("\n");
155+
return Err(format!(
156+
"Failed to install OpenClaw {}: {}",
157+
REQUIRED_OPENCLAW_VERSION,
158+
errors.trim()
159+
));
160+
}
161+
162+
let _ = app.emit("gateway-log", &format!(
163+
"OpenClaw {} installed successfully",
164+
REQUIRED_OPENCLAW_VERSION
165+
));
166+
Ok(())
167+
}
168+
46169
pub(crate) fn generate_token() -> String {
47170
use std::collections::hash_map::RandomState;
48171
use std::hash::{BuildHasher, Hasher};
@@ -322,6 +445,9 @@ pub async fn start_gateway(
322445
));
323446
}
324447

448+
// Ensure installed version matches the pinned version before launching
449+
ensure_openclaw_version(&app).await?;
450+
325451
let mut cmd = tokio::process::Command::new(&node);
326452
cmd.arg(&entry)
327453
.args(["gateway", "run", "--bind", &bind, "--port", &port.to_string()])

src-tauri/src/commands/setup.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,8 @@ pub async fn install_openclaw(app: tauri::AppHandle) -> Result<String, String> {
541541
let git_config_key = "url.https://github.com/.insteadOf";
542542
let git_config_val = "ssh://git@github.com/";
543543

544+
let pkg_spec = format!("openclaw@{}", super::gateway::REQUIRED_OPENCLAW_VERSION);
545+
544546
let output = if use_system_npm {
545547
// Use system npm directly
546548
let npm_cmd = if cfg!(windows) { "npm.cmd" } else { "npm" };
@@ -554,10 +556,7 @@ pub async fn install_openclaw(app: tauri::AppHandle) -> Result<String, String> {
554556
"install",
555557
"--prefix",
556558
openclaw_prefix.to_str().unwrap(),
557-
// Pin to 2026.3.28: version 2026.3.31 broke bundled channel plugin
558-
// runtime loading (Telegram etc. fail to register). Remove pin once
559-
// upstream fix #58782 ships in a release.
560-
"openclaw@2026.3.28",
559+
&pkg_spec,
561560
])
562561
.output()
563562
.await
@@ -575,10 +574,7 @@ pub async fn install_openclaw(app: tauri::AppHandle) -> Result<String, String> {
575574
"install",
576575
"--prefix",
577576
openclaw_prefix.to_str().unwrap(),
578-
// Pin to 2026.3.28: version 2026.3.31 broke bundled channel plugin
579-
// runtime loading (Telegram etc. fail to register). Remove pin once
580-
// upstream fix #58782 ships in a release.
581-
"openclaw@2026.3.28",
577+
&pkg_spec,
582578
])
583579
.output()
584580
.await

0 commit comments

Comments
 (0)