fix powershell executions (#3006)
This commit is contained in:
Generated
+20
-1
@@ -1265,7 +1265,7 @@ dependencies = [
|
|||||||
"rustc-hash 1.1.0",
|
"rustc-hash 1.1.0",
|
||||||
"shlex",
|
"shlex",
|
||||||
"syn 2.0.99",
|
"syn 2.0.99",
|
||||||
"which",
|
"which 4.4.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3635,6 +3635,7 @@ dependencies = [
|
|||||||
"urlencoding",
|
"urlencoding",
|
||||||
"utoipa",
|
"utoipa",
|
||||||
"webbrowser 0.8.15",
|
"webbrowser 0.8.15",
|
||||||
|
"which 6.0.3",
|
||||||
"xcap",
|
"xcap",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -9401,6 +9402,18 @@ dependencies = [
|
|||||||
"rustix 0.38.44",
|
"rustix 0.38.44",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "which"
|
||||||
|
version = "6.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"home",
|
||||||
|
"rustix 0.38.44",
|
||||||
|
"winsafe",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wild"
|
name = "wild"
|
||||||
version = "2.2.1"
|
version = "2.2.1"
|
||||||
@@ -9821,6 +9834,12 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winsafe"
|
||||||
|
version = "0.0.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wiremock"
|
name = "wiremock"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ oauth2 = { version = "5.0.0", features = ["reqwest"] }
|
|||||||
utoipa = { version = "4.1", optional = true }
|
utoipa = { version = "4.1", optional = true }
|
||||||
hyper = "1"
|
hyper = "1"
|
||||||
serde_with = "3"
|
serde_with = "3"
|
||||||
|
which = "6.0"
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -736,10 +736,7 @@ impl ComputerControllerRouter {
|
|||||||
ToolError::ExecutionError(format!("Failed to write script: {}", e))
|
ToolError::ExecutionError(format!("Failed to write script: {}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
format!(
|
script_path.display().to_string()
|
||||||
"powershell -NoProfile -NonInteractive -File {}",
|
|
||||||
script_path.display()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err( ToolError::InvalidParameters(
|
return Err( ToolError::InvalidParameters(
|
||||||
@@ -749,12 +746,27 @@ impl ComputerControllerRouter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Run the script
|
// Run the script
|
||||||
let output = Command::new(shell)
|
let output = match language {
|
||||||
.arg(shell_arg)
|
"powershell" => {
|
||||||
.arg(&command)
|
// For PowerShell, we need to use -File instead of -Command
|
||||||
.output()
|
Command::new("powershell")
|
||||||
.await
|
.arg("-NoProfile")
|
||||||
.map_err(|e| ToolError::ExecutionError(format!("Failed to run script: {}", e)))?;
|
.arg("-NonInteractive")
|
||||||
|
.arg("-File")
|
||||||
|
.arg(&command)
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
ToolError::ExecutionError(format!("Failed to run script: {}", e))
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
_ => Command::new(shell)
|
||||||
|
.arg(shell_arg)
|
||||||
|
.arg(&command)
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.map_err(|e| ToolError::ExecutionError(format!("Failed to run script: {}", e)))?,
|
||||||
|
};
|
||||||
|
|
||||||
let output_str = String::from_utf8_lossy(&output.stdout).into_owned();
|
let output_str = String::from_utf8_lossy(&output.stdout).into_owned();
|
||||||
let error_str = String::from_utf8_lossy(&output.stderr).into_owned();
|
let error_str = String::from_utf8_lossy(&output.stderr).into_owned();
|
||||||
|
|||||||
@@ -39,10 +39,7 @@ use mcp_server::Router;
|
|||||||
use mcp_core::role::Role;
|
use mcp_core::role::Role;
|
||||||
|
|
||||||
use self::editor_models::{create_editor_model, EditorModel};
|
use self::editor_models::{create_editor_model, EditorModel};
|
||||||
use self::shell::{
|
use self::shell::{expand_path, get_shell_config, is_absolute_path, normalize_line_endings};
|
||||||
expand_path, format_command_for_platform, get_shell_config, is_absolute_path,
|
|
||||||
normalize_line_endings,
|
|
||||||
};
|
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
@@ -540,7 +537,6 @@ impl DeveloperRouter {
|
|||||||
|
|
||||||
// Get platform-specific shell configuration
|
// Get platform-specific shell configuration
|
||||||
let shell_config = get_shell_config();
|
let shell_config = get_shell_config();
|
||||||
let cmd_str = format_command_for_platform(command);
|
|
||||||
|
|
||||||
// Execute the command using platform-specific shell
|
// Execute the command using platform-specific shell
|
||||||
let mut child = Command::new(&shell_config.executable)
|
let mut child = Command::new(&shell_config.executable)
|
||||||
@@ -548,8 +544,8 @@ impl DeveloperRouter {
|
|||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.stdin(Stdio::null())
|
.stdin(Stdio::null())
|
||||||
.kill_on_drop(true)
|
.kill_on_drop(true)
|
||||||
.arg(&shell_config.arg)
|
.args(&shell_config.args)
|
||||||
.arg(cmd_str)
|
.arg(command)
|
||||||
.spawn()
|
.spawn()
|
||||||
.map_err(|e| ToolError::ExecutionError(e.to_string()))?;
|
.map_err(|e| ToolError::ExecutionError(e.to_string()))?;
|
||||||
|
|
||||||
|
|||||||
@@ -3,21 +3,65 @@ use std::env;
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ShellConfig {
|
pub struct ShellConfig {
|
||||||
pub executable: String,
|
pub executable: String,
|
||||||
pub arg: String,
|
pub args: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ShellConfig {
|
impl Default for ShellConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
// Execute PowerShell commands directly
|
// Detect the default shell on Windows
|
||||||
Self {
|
#[cfg(windows)]
|
||||||
executable: "powershell.exe".to_string(),
|
{
|
||||||
arg: "-NoProfile -NonInteractive -Command".to_string(),
|
Self::detect_windows_shell()
|
||||||
|
}
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
{
|
||||||
|
// This branch should never be taken on non-Windows
|
||||||
|
// but we need it for compilation
|
||||||
|
Self {
|
||||||
|
executable: "cmd".to_string(),
|
||||||
|
args: vec!["/c".to_string()],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Use bash on Unix/macOS (keep existing behavior)
|
||||||
Self {
|
Self {
|
||||||
executable: "bash".to_string(),
|
executable: "bash".to_string(),
|
||||||
arg: "-c".to_string(),
|
args: vec!["-c".to_string()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShellConfig {
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn detect_windows_shell() -> Self {
|
||||||
|
// Check for PowerShell first (more modern)
|
||||||
|
if let Ok(ps_path) = which::which("pwsh") {
|
||||||
|
// PowerShell 7+ (cross-platform PowerShell)
|
||||||
|
Self {
|
||||||
|
executable: ps_path.to_string_lossy().to_string(),
|
||||||
|
args: vec![
|
||||||
|
"-NoProfile".to_string(),
|
||||||
|
"-NonInteractive".to_string(),
|
||||||
|
"-Command".to_string(),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
} else if let Ok(ps_path) = which::which("powershell") {
|
||||||
|
// Windows PowerShell 5.1
|
||||||
|
Self {
|
||||||
|
executable: ps_path.to_string_lossy().to_string(),
|
||||||
|
args: vec![
|
||||||
|
"-NoProfile".to_string(),
|
||||||
|
"-NonInteractive".to_string(),
|
||||||
|
"-Command".to_string(),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fall back to cmd.exe
|
||||||
|
Self {
|
||||||
|
executable: "cmd".to_string(),
|
||||||
|
args: vec!["/c".to_string()],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,16 +71,6 @@ pub fn get_shell_config() -> ShellConfig {
|
|||||||
ShellConfig::default()
|
ShellConfig::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_command_for_platform(command: &str) -> String {
|
|
||||||
if cfg!(windows) {
|
|
||||||
// For PowerShell, wrap the command in braces to handle special characters
|
|
||||||
format!("{{ {} }}", command)
|
|
||||||
} else {
|
|
||||||
// For other shells, no braces needed
|
|
||||||
command.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expand_path(path_str: &str) -> String {
|
pub fn expand_path(path_str: &str) -> String {
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
// Expand Windows environment variables (%VAR%)
|
// Expand Windows environment variables (%VAR%)
|
||||||
|
|||||||
Reference in New Issue
Block a user