Skip to content

Commit fbdff7b

Browse files
authored
Merge pull request #53 from tidewave-ai/sd-wsl
Detect WSL
2 parents 42681ce + 329f0e2 commit fbdff7b

File tree

4 files changed

+209
-49
lines changed

4 files changed

+209
-49
lines changed

tidewave-core/src/acp_proxy.rs

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ Because of this, we don't use the ACP SDK in the browser, but instead handle raw
137137
JSON-RPC messages and use the ACP-SDK for types. The proxy will continue to forward
138138
any requests to the new connection.
139139
*/
140+
use crate::command::create_shell_command;
140141
use anyhow::{anyhow, Result};
141142
use axum::{
142143
extract::{
@@ -152,7 +153,6 @@ use serde::{Deserialize, Serialize};
152153
use serde_json::{Map, Value};
153154
use std::{
154155
collections::HashMap,
155-
path::Path,
156156
process::Stdio,
157157
sync::{
158158
atomic::{AtomicBool, AtomicU64, Ordering},
@@ -161,7 +161,7 @@ use std::{
161161
};
162162
use tokio::{
163163
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
164-
process::{Child, Command},
164+
process::Child,
165165
sync::{mpsc, Mutex, RwLock},
166166
};
167167
use tracing::{debug, error, info, trace, warn};
@@ -239,6 +239,8 @@ pub struct TidewaveSpawnOptions {
239239
pub command: String,
240240
pub env: HashMap<String, String>,
241241
pub cwd: String,
242+
#[serde(default)]
243+
is_wsl: bool,
242244
}
243245

244246
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -1201,18 +1203,6 @@ async fn forward_response_to_process(
12011203
Ok(())
12021204
}
12031205

1204-
fn get_shell_command(cmd: &str) -> (&'static str, Vec<&str>) {
1205-
#[cfg(target_os = "windows")]
1206-
{
1207-
("cmd.exe", vec!["/s", "/c", cmd])
1208-
}
1209-
1210-
#[cfg(not(target_os = "windows"))]
1211-
{
1212-
("sh", vec!["-c", cmd])
1213-
}
1214-
}
1215-
12161206
// ============================================================================
12171207
// Process Management
12181208
// ============================================================================
@@ -1221,21 +1211,19 @@ fn get_shell_command(cmd: &str) -> (&'static str, Vec<&str>) {
12211211
pub fn real_process_starter() -> ProcessStarterFn {
12221212
Arc::new(|spawn_opts: TidewaveSpawnOptions| {
12231213
Box::pin(async move {
1224-
let (cmd, args) = get_shell_command(&spawn_opts.command);
1214+
info!("Starting ACP process: {}", spawn_opts.command);
12251215

1226-
info!("Starting ACP process: {} with args: {:?}", cmd, args);
1216+
let mut cmd = create_shell_command(
1217+
&spawn_opts.command,
1218+
spawn_opts.env,
1219+
&spawn_opts.cwd,
1220+
spawn_opts.is_wsl,
1221+
);
12271222

1228-
let mut cmd = Command::new(cmd);
1229-
cmd.args(args)
1230-
.envs(spawn_opts.env)
1231-
.current_dir(Path::new(&spawn_opts.cwd))
1232-
.stdin(Stdio::piped())
1223+
cmd.stdin(Stdio::piped())
12331224
.stdout(Stdio::piped())
12341225
.stderr(Stdio::piped());
12351226

1236-
#[cfg(windows)]
1237-
cmd.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
1238-
12391227
let mut child = cmd
12401228
.spawn()
12411229
.map_err(|e| anyhow!("Failed to spawn process: {}", e))?;
@@ -1903,6 +1891,7 @@ mod tests {
19031891
command: "test_cmd".to_string(),
19041892
env: HashMap::new(),
19051893
cwd: ".".to_string(),
1894+
is_wsl: false,
19061895
}
19071896
}
19081897

tidewave-core/src/command.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use std::collections::HashMap;
2+
use std::path::Path;
3+
use tokio::process::Command;
4+
5+
pub fn create_shell_command(
6+
cmd: &str,
7+
env: HashMap<String, String>,
8+
cwd: &str,
9+
#[cfg_attr(not(target_os = "windows"), allow(unused_variables))] is_wsl: bool,
10+
) -> Command {
11+
#[cfg(target_os = "windows")]
12+
{
13+
if is_wsl {
14+
// WSL case: use --cd flag and construct env string
15+
// Build env assignments string: VAR1=value1 VAR2=value2 ... command
16+
let env_string: Vec<String> = env
17+
.iter()
18+
.map(|(k, v)| {
19+
// Escape single quotes in the value by replacing ' with '\''
20+
let escaped_value = v.replace("'", "'\\''");
21+
format!("{}='{}'", k, escaped_value)
22+
})
23+
.collect();
24+
25+
let full_command = if env_string.is_empty() {
26+
cmd.to_string()
27+
} else {
28+
format!("{} {}", env_string.join(" "), cmd)
29+
};
30+
31+
let mut command = Command::new("wsl.exe");
32+
command
33+
.arg("--cd")
34+
.arg(cwd)
35+
.arg("sh")
36+
.arg("-c")
37+
.arg(full_command)
38+
.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
39+
command
40+
} else {
41+
// Windows cmd case: use .current_dir()
42+
let mut command = Command::new("cmd.exe");
43+
command
44+
.arg("/s")
45+
.arg("/c")
46+
.arg(cmd)
47+
.envs(env)
48+
.current_dir(Path::new(cwd))
49+
.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
50+
command
51+
}
52+
}
53+
54+
#[cfg(not(target_os = "windows"))]
55+
{
56+
// Unix case: use .current_dir()
57+
let mut command = Command::new("sh");
58+
command
59+
.arg("-c")
60+
.arg(cmd)
61+
.envs(env)
62+
.current_dir(Path::new(cwd));
63+
command
64+
}
65+
}

tidewave-core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod acp_proxy;
2+
mod command;
23
pub mod config;
34
mod mcp_remote;
45
pub mod server;

0 commit comments

Comments
 (0)