fix(desktop): strip Windows verbatim prefix from runtime spawn paths (#8936)

Tauri's resource_dir() returns \\?\ verbatim paths on Windows, so the
bundled Node executable and the cli-entry.js argument were spawned with
that prefix. Node's entry-script resolution cannot handle it and dies
with EISDIR before the daemon reports its listening URL, making the app
unstartable regardless of workspace. Simplify both paths with dunce
before spawning, complementing the workspace-path fix from #8615.

Fixes #8929
This commit is contained in:
易良 2026-08-11 20:58:05 +08:00 committed by GitHub
parent 32812e9dd7
commit 8cf36b862a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -162,18 +162,28 @@ impl RuntimeLayout {
.join("runtime")
.join("qwen-code")
};
let node = if cfg!(windows) {
root.join("node").join("node.exe")
} else {
root.join("node").join("bin").join("node")
};
let entry = root.join("lib").join("cli-entry.js");
let (node, entry) = layout_from_root(root);
require_file(&node, "Node.js runtime")?;
require_file(&entry, "Qwen Code runtime entry")?;
Ok(Self { node, entry })
}
}
fn layout_from_root(root: PathBuf) -> (PathBuf, PathBuf) {
let node = if cfg!(windows) {
root.join("node").join("node.exe")
} else {
root.join("node").join("bin").join("node")
};
let entry = root.join("lib").join("cli-entry.js");
// Tauri's resource_dir() returns `\\?\` verbatim paths on Windows, and
// Node's entry-script resolution cannot handle that prefix (#8929).
(
dunce::simplified(&node).to_path_buf(),
dunce::simplified(&entry).to_path_buf(),
)
}
fn require_file(path: &Path, description: &str) -> Result<(), String> {
if path.is_file() {
return Ok(());
@ -532,6 +542,8 @@ mod tests {
};
#[cfg(unix)]
use super::{stop_runtime_handle, wait_for_listening};
#[cfg(windows)]
use super::layout_from_root;
use std::path::Path;
#[cfg(windows)]
use std::path::PathBuf;
@ -586,6 +598,22 @@ mod tests {
assert!(error.contains("unsupported Windows extended-length form"));
}
#[cfg(windows)]
#[test]
fn runtime_layout_strips_windows_verbatim_prefix() {
let root = PathBuf::from(r"\\?\C:\Users\user\AppData\Local\Qwen Code Desktop")
.join("runtime")
.join("qwen-code");
let (node, entry) = layout_from_root(root);
for path in [node, entry] {
let path = path.to_string_lossy();
assert!(
!path.starts_with("\\\\?\\"),
"runtime path keeps the verbatim prefix: {path}"
);
}
}
#[test]
fn parses_loopback_listening_line() {
let url = parse_listening_url(