diff options
| author | Filip <[email protected]> | 2026-02-11 16:18:44 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-11 09:18:44 -0600 |
| commit | eef3ae3e1f73eca103892a758d44e24d3c725d81 (patch) | |
| tree | b076ba087302927daf732bb766ae545790aad4c6 /packages/desktop/src-tauri/src/cli.rs | |
| parent | fc88dde63f1c4a7f547bbe0634c68c5ce9fc787c (diff) | |
| download | opencode-eef3ae3e1f73eca103892a758d44e24d3c725d81.tar.gz opencode-eef3ae3e1f73eca103892a758d44e24d3c725d81.zip | |
Fix/reverception (#13166)
Co-authored-by: Adam <[email protected]>
Diffstat (limited to 'packages/desktop/src-tauri/src/cli.rs')
| -rw-r--r-- | packages/desktop/src-tauri/src/cli.rs | 138 |
1 files changed, 113 insertions, 25 deletions
diff --git a/packages/desktop/src-tauri/src/cli.rs b/packages/desktop/src-tauri/src/cli.rs index 48d9276a1..b9e1ed4bd 100644 --- a/packages/desktop/src-tauri/src/cli.rs +++ b/packages/desktop/src-tauri/src/cli.rs @@ -3,8 +3,11 @@ use tauri_plugin_shell::{ ShellExt, process::{Command, CommandChild, CommandEvent, TerminatedPayload}, }; +use tauri_plugin_store::StoreExt; use tokio::sync::oneshot; +use crate::constants::{SETTINGS_STORE, WSL_ENABLED_KEY}; + const CLI_INSTALL_DIR: &str = ".opencode/bin"; const CLI_BINARY_NAME: &str = "opencode"; @@ -20,7 +23,7 @@ pub struct Config { } pub async fn get_config(app: &AppHandle) -> Option<Config> { - create_command(app, "debug config") + create_command(app, "debug config", &[]) .output() .await .inspect_err(|e| tracing::warn!("Failed to read OC config: {e}")) @@ -149,25 +152,106 @@ fn get_user_shell() -> String { std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string()) } -pub fn create_command(app: &tauri::AppHandle, args: &str) -> Command { +fn is_wsl_enabled(app: &tauri::AppHandle) -> bool { + let Ok(store) = app.store(SETTINGS_STORE) else { + return false; + }; + + store + .get(WSL_ENABLED_KEY) + .as_ref() + .and_then(|value| value.as_bool()) + .unwrap_or(false) +} + +fn shell_escape(input: &str) -> String { + if input.is_empty() { + return "''".to_string(); + } + + let mut escaped = String::from("'"); + escaped.push_str(&input.replace("'", "'\"'\"'")); + escaped.push('\''); + escaped +} + +pub fn create_command(app: &tauri::AppHandle, args: &str, extra_env: &[(&str, String)]) -> Command { let state_dir = app .path() .resolve("", BaseDirectory::AppLocalData) .expect("Failed to resolve app local data dir"); - #[cfg(target_os = "windows")] - return app - .shell() - .sidecar("opencode-cli") - .unwrap() - .args(args.split_whitespace()) - .env("OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", "true") - .env("OPENCODE_EXPERIMENTAL_FILEWATCHER", "true") - .env("OPENCODE_CLIENT", "desktop") - .env("XDG_STATE_HOME", &state_dir); - - #[cfg(not(target_os = "windows"))] - return { + let mut envs = vec![ + ( + "OPENCODE_EXPERIMENTAL_ICON_DISCOVERY".to_string(), + "true".to_string(), + ), + ( + "OPENCODE_EXPERIMENTAL_FILEWATCHER".to_string(), + "true".to_string(), + ), + ("OPENCODE_CLIENT".to_string(), "desktop".to_string()), + ( + "XDG_STATE_HOME".to_string(), + state_dir.to_string_lossy().to_string(), + ), + ]; + envs.extend( + extra_env + .iter() + .map(|(key, value)| (key.to_string(), value.clone())), + ); + + if cfg!(windows) { + if is_wsl_enabled(app) { + tracing::info!("WSL is enabled, spawning CLI server in WSL"); + let version = app.package_info().version.to_string(); + let mut script = vec![ + "set -e".to_string(), + "BIN=\"$HOME/.opencode/bin/opencode\"".to_string(), + "if [ ! -x \"$BIN\" ]; then".to_string(), + format!( + " curl -fsSL https://opencode.ai/install | bash -s -- --version {} --no-modify-path", + shell_escape(&version) + ), + "fi".to_string(), + ]; + + let mut env_prefix = vec![ + "OPENCODE_EXPERIMENTAL_ICON_DISCOVERY=true".to_string(), + "OPENCODE_EXPERIMENTAL_FILEWATCHER=true".to_string(), + "OPENCODE_CLIENT=desktop".to_string(), + "XDG_STATE_HOME=\"$HOME/.local/state\"".to_string(), + ]; + env_prefix.extend( + envs.iter() + .filter(|(key, _)| key != "OPENCODE_EXPERIMENTAL_ICON_DISCOVERY") + .filter(|(key, _)| key != "OPENCODE_EXPERIMENTAL_FILEWATCHER") + .filter(|(key, _)| key != "OPENCODE_CLIENT") + .filter(|(key, _)| key != "XDG_STATE_HOME") + .map(|(key, value)| format!("{}={}", key, shell_escape(value))), + ); + + script.push(format!("{} exec \"$BIN\" {}", env_prefix.join(" "), args)); + + return app + .shell() + .command("wsl") + .args(["-e", "bash", "-lc", &script.join("\n")]); + } else { + let mut cmd = app + .shell() + .sidecar("opencode-cli") + .unwrap() + .args(args.split_whitespace()); + + for (key, value) in envs { + cmd = cmd.env(key, value); + } + + return cmd; + } + } else { let sidecar = get_sidecar_path(app); let shell = get_user_shell(); @@ -177,14 +261,14 @@ pub fn create_command(app: &tauri::AppHandle, args: &str) -> Command { format!("\"{}\" {}", sidecar.display(), args) }; - app.shell() - .command(&shell) - .env("OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", "true") - .env("OPENCODE_EXPERIMENTAL_FILEWATCHER", "true") - .env("OPENCODE_CLIENT", "desktop") - .env("XDG_STATE_HOME", &state_dir) - .args(["-il", "-c", &cmd]) - }; + let mut cmd = app.shell().command(&shell).args(["-il", "-c", &cmd]); + + for (key, value) in envs { + cmd = cmd.env(key, value); + } + + cmd + } } pub fn serve( @@ -197,12 +281,16 @@ pub fn serve( tracing::info!(port, "Spawning sidecar"); + let envs = [ + ("OPENCODE_SERVER_USERNAME", "opencode".to_string()), + ("OPENCODE_SERVER_PASSWORD", password.to_string()), + ]; + let (mut rx, child) = create_command( app, format!("--print-logs --log-level WARN serve --hostname {hostname} --port {port}").as_str(), + &envs, ) - .env("OPENCODE_SERVER_USERNAME", "opencode") - .env("OPENCODE_SERVER_PASSWORD", password) .spawn() .expect("Failed to spawn opencode"); |
