Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions crates/goose-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ reqwest = { version = "0.12.9", features = ["json", "rustls-tls", "blocking", "m
tokio-util = "0.7.15"
uuid = { version = "1.11", features = ["v4"] }
serde_path_to_error = "0.1.20"
winreg = { version = "0.55.0", optional = true }

[target.'cfg(windows)'.dependencies]
winreg = { version = "0.55.0" }

[[bin]]
name = "goosed"
Expand Down
40 changes: 36 additions & 4 deletions crates/goose-server/src/routes/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,44 @@ async fn add_extension(

// If this is a Stdio extension that uses npx, check for Node.js installation
#[cfg(target_os = "windows")]
use winreg::enums::{HKEY_LOCAL_MACHINE, KEY_READ};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks great. does this mean we can delete the code in the API handler that tries to do the same thing? but only in the API so that would not work for the CLI I think?

Copy link
Contributor Author

@vlascik vlascik Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what code do you mean? All I can see are some shell scripts trying to install node.js again in a default directory, which is probably not going to work for people that already have node installed elsewhere anyway (the chances are their install will just be updated).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean now, there is a new PR that moved this code to crates/goose-server/src/routes/agent.rs now, is this what you meant?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah sorry I moved this - do you want to do that or should I just copy your code over if that is faster

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DOsinga just copy it, it will be faster.

use winreg::RegKey;
if let ExtensionConfig::Stdio { cmd, .. } = &request.config {
if cmd.ends_with("npx.cmd") || cmd.ends_with("npx") {
// Check if Node.js is installed in standard locations
let node_exists = std::path::Path::new(r"C:\Program Files\nodejs\node.exe").exists()
let mut node_exists = std::path::Path::new(r"C:\Program Files\nodejs\node.exe").exists()
|| std::path::Path::new(r"C:\Program Files (x86)\nodejs\node.exe").exists();

// Also check Windows registry: HKEY_LOCAL_MACHINE\\SOFTWARE\\Node.js InstallPath
if !node_exists {
// Try 64-bit view first, then 32-bit. Use open_subkey_with_flags to avoid WOW64 redirection issues.
let install_path_from_reg: Option<String> = (|| {
let hk_local_machine = RegKey::predef(HKEY_LOCAL_MACHINE);
// Common keys to try
let keys = vec![
"SOFTWARE\\Node.js",
"SOFTWARE\\WOW6432Node\\Node.js",
];
for k in keys.iter() {
if let Ok(subkey) = hk_local_machine.open_subkey_with_flags(k, KEY_READ) {
if let Ok(val) = subkey.get_value::<String, _>("InstallPath") {
if !val.trim().is_empty() {
return Some(val);
}
}
}
}
None
})();

if let Some(path_str) = install_path_from_reg {
let node_path = std::path::Path::new(&path_str).join("node.exe");
if node_path.exists() {
node_exists = true;
}
}
}

if !node_exists {
// Get the directory containing npx.cmd
let cmd_path = std::path::Path::new(&cmd);
Expand All @@ -47,7 +79,7 @@ async fn add_extension(
let install_script = script_dir.join("install-node.cmd");

if install_script.exists() {
eprintln!("Installing Node.js...");
eprintln!("Node.js not found on the system, installing Node.js...");
let output = std::process::Command::new(&install_script)
.arg("https://nodejs.org/dist/v23.10.0/node-v23.10.0-x64.msi")
.output()
Expand All @@ -72,12 +104,12 @@ async fn add_extension(
eprintln!("Node.js installation completed");
} else {
eprintln!(
"Node.js installer script not found at: {}",
"Node.js not found on the system, and Node.js installer script not found at: {}",
install_script.display()
);
return Ok(Json(ExtensionResponse {
error: true,
message: Some("Node.js installer script not found".to_string()),
message: Some("Node.js not found on the system, and Node.js installer script not found".to_string()),
}));
}
}
Expand Down
Loading