fix: don't run npx --version to check package (triggers broken code path)

is_npm_package_installed() was running 'npx --yes camoufox-browser@0.1.0 --version'
which actually LOADS the package code. If we've removed the bundled
camoufox-js@0.9.3, this fails with 'Cannot open database' error, making
the check return false and triggering a re-install.

Fix: check for daemon.js on disk via npm root -g instead.

Also: install_npm_package() now removes the bundled camoufox-js after
install so the daemon always resolves to the global v0.11.0.
This commit is contained in:
2026-06-13 21:29:19 +05:30
parent 274020319f
commit 57308e7805

View File

@@ -326,17 +326,46 @@ async fn run_fetch(npx: &str, js_version: &str) -> Result<String, String> {
.to_string()) .to_string())
} }
/// Check if an npm package is already installed globally. /// Check if an npm package is already installed globally by checking for its daemon.js on disk.
async fn is_npm_package_installed(npx: &str, package: &str, version: &str) -> bool { /// We do NOT run `npx --version` because that actually loads the package code,
Command::new(npx) /// which fails if we've removed the bundled camoufox-js dependency.
.args(&["--yes", &format!("{package}@{version}"), "--version"]) async fn is_npm_package_installed(_npx: &str, package: &str, _version: &str) -> bool {
// Try `npm root -g` to find global node_modules
let root_output = Command::new("npm")
.args(&["root", "-g"])
.output() .output()
.await .await;
.map(|o| o.status.success()) let global_root = match root_output {
.unwrap_or(false) Ok(o) if o.status.success() => String::from_utf8_lossy(&o.stdout).trim().to_string(),
_ => return false,
};
let daemon_path = std::path::Path::new(&global_root)
.join(package)
.join("dist")
.join("daemon.js");
if daemon_path.exists() {
return true;
}
// Fallback: check common locations
let home = dirs::home_dir().unwrap_or_else(|| std::path::PathBuf::from("/tmp"));
let fallbacks = vec![
home.join(".nvm").join("versions").join("node").join("v22").join("lib").join("node_modules")
.join(package).join("dist").join("daemon.js"),
home.join(".nvm").join("versions").join("node").join("v24").join("lib").join("node_modules")
.join(package).join("dist").join("daemon.js"),
std::path::PathBuf::from("/usr/local/lib/node_modules").join(package).join("dist").join("daemon.js"),
std::path::PathBuf::from("/opt/homebrew/lib/node_modules").join(package).join("dist").join("daemon.js"),
];
for fb in &fallbacks {
if fb.exists() {
return true;
}
}
false
} }
/// Install an npm package globally. /// Install an npm package globally, then remove the bundled camoufox-js
/// so the daemon uses the globally installed version instead.
async fn install_npm_package(_npx: &str, package: &str, version: &str) -> Result<String, String> { async fn install_npm_package(_npx: &str, package: &str, version: &str) -> Result<String, String> {
let output = Command::new("npm") let output = Command::new("npm")
.args(&["install", "-g", &format!("{package}@{version}")]) .args(&["install", "-g", &format!("{package}@{version}")])
@@ -349,6 +378,25 @@ async fn install_npm_package(_npx: &str, package: &str, version: &str) -> Result
return Err(stderr.lines().last().unwrap_or("(unknown)").to_string()); return Err(stderr.lines().last().unwrap_or("(unknown)").to_string());
} }
// After install, remove the bundled camoufox-js so Node resolves to the global version
let root_output = Command::new("npm")
.args(&["root", "-g"])
.output()
.await;
if let Ok(o) = root_output {
if o.status.success() {
let global_root = String::from_utf8_lossy(&o.stdout).trim().to_string();
let bundled = std::path::Path::new(&global_root)
.join(package)
.join("node_modules")
.join("camoufox-js");
if bundled.exists() {
let _ = std::fs::remove_dir_all(&bundled);
info!("Removed bundled camoufox-js from {} (daemon will use global version)", package);
}
}
}
Ok(format!("{package}@{version} installed")) Ok(format!("{package}@{version} installed"))
} }