Revert "feat(desktop): add WSL backend mode (#12914)"
This reverts commit 213a87234d.
This commit is contained in:
@@ -3,11 +3,8 @@ 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";
|
||||
|
||||
@@ -23,7 +20,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}"))
|
||||
@@ -152,106 +149,25 @@ fn get_user_shell() -> String {
|
||||
std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string())
|
||||
}
|
||||
|
||||
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 {
|
||||
pub fn create_command(app: &tauri::AppHandle, args: &str) -> Command {
|
||||
let state_dir = app
|
||||
.path()
|
||||
.resolve("", BaseDirectory::AppLocalData)
|
||||
.expect("Failed to resolve app local data dir");
|
||||
|
||||
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())),
|
||||
);
|
||||
#[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);
|
||||
|
||||
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 {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
return {
|
||||
let sidecar = get_sidecar_path(app);
|
||||
let shell = get_user_shell();
|
||||
|
||||
@@ -261,14 +177,14 @@ pub fn create_command(app: &tauri::AppHandle, args: &str, extra_env: &[(&str, St
|
||||
format!("\"{}\" {}", sidecar.display(), args)
|
||||
};
|
||||
|
||||
let mut cmd = app.shell().command(&shell).args(["-il", "-c", &cmd]);
|
||||
|
||||
for (key, value) in envs {
|
||||
cmd = cmd.env(key, value);
|
||||
}
|
||||
|
||||
cmd
|
||||
}
|
||||
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])
|
||||
};
|
||||
}
|
||||
|
||||
pub fn serve(
|
||||
@@ -281,16 +197,12 @@ 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");
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ use tauri_plugin_window_state::StateFlags;
|
||||
|
||||
pub const SETTINGS_STORE: &str = "opencode.settings.dat";
|
||||
pub const DEFAULT_SERVER_URL_KEY: &str = "defaultServerUrl";
|
||||
pub const WSL_ENABLED_KEY: &str = "wslEnabled";
|
||||
pub const UPDATER_ENABLED: bool = option_env!("TAURI_SIGNING_PRIVATE_KEY").is_some();
|
||||
|
||||
pub fn window_state_flags() -> StateFlags {
|
||||
|
||||
@@ -52,13 +52,6 @@ enum InitStep {
|
||||
Done,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, specta::Type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum WslPathMode {
|
||||
Windows,
|
||||
Linux,
|
||||
}
|
||||
|
||||
struct InitState {
|
||||
current: watch::Receiver<InitStep>,
|
||||
}
|
||||
@@ -627,50 +620,32 @@ fn check_linux_app(app_name: &str) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
fn wsl_path(path: String, mode: Option<WslPathMode>) -> Result<String, String> {
|
||||
if !cfg!(windows) {
|
||||
return Ok(path);
|
||||
}
|
||||
|
||||
let flag = match mode.unwrap_or(WslPathMode::Linux) {
|
||||
WslPathMode::Windows => "-w",
|
||||
WslPathMode::Linux => "-u",
|
||||
};
|
||||
|
||||
let output = if path.starts_with('~') {
|
||||
let suffix = path.strip_prefix('~').unwrap_or("");
|
||||
let escaped = suffix.replace('"', "\\\"");
|
||||
let cmd = format!("wslpath {flag} \"$HOME{escaped}\"");
|
||||
Command::new("wsl")
|
||||
.args(["-e", "sh", "-lc", &cmd])
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to run wslpath: {e}"))?
|
||||
} else {
|
||||
Command::new("wsl")
|
||||
.args(["-e", "wslpath", flag, &path])
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to run wslpath: {e}"))?
|
||||
};
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
|
||||
if stderr.is_empty() {
|
||||
return Err("wslpath failed".to_string());
|
||||
}
|
||||
return Err(stderr);
|
||||
}
|
||||
|
||||
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
let builder = make_specta_builder();
|
||||
let builder = tauri_specta::Builder::<tauri::Wry>::new()
|
||||
// Then register them (separated by a comma)
|
||||
.commands(tauri_specta::collect_commands![
|
||||
kill_sidecar,
|
||||
cli::install_cli,
|
||||
await_initialization,
|
||||
server::get_default_server_url,
|
||||
server::set_default_server_url,
|
||||
get_display_backend,
|
||||
set_display_backend,
|
||||
markdown::parse_markdown_command,
|
||||
check_app_exists,
|
||||
resolve_app_path
|
||||
])
|
||||
.events(tauri_specta::collect_events![LoadingWindowComplete])
|
||||
.error_handling(tauri_specta::ErrorHandlingMode::Throw);
|
||||
|
||||
#[cfg(debug_assertions)] // <- Only export on non-release builds
|
||||
export_types(&builder);
|
||||
builder
|
||||
.export(
|
||||
specta_typescript::Typescript::default(),
|
||||
"../src/bindings.ts",
|
||||
)
|
||||
.expect("Failed to export typescript bindings");
|
||||
|
||||
#[cfg(all(target_os = "macos", not(debug_assertions)))]
|
||||
let _ = std::process::Command::new("killall")
|
||||
@@ -737,44 +712,6 @@ pub fn run() {
|
||||
});
|
||||
}
|
||||
|
||||
fn make_specta_builder() -> tauri_specta::Builder<tauri::Wry> {
|
||||
tauri_specta::Builder::<tauri::Wry>::new()
|
||||
// Then register them (separated by a comma)
|
||||
.commands(tauri_specta::collect_commands![
|
||||
kill_sidecar,
|
||||
cli::install_cli,
|
||||
await_initialization,
|
||||
server::get_default_server_url,
|
||||
server::set_default_server_url,
|
||||
server::get_wsl_config,
|
||||
server::set_wsl_config,
|
||||
get_display_backend,
|
||||
set_display_backend,
|
||||
markdown::parse_markdown_command,
|
||||
check_app_exists,
|
||||
wsl_path,
|
||||
resolve_app_path
|
||||
])
|
||||
.events(tauri_specta::collect_events![LoadingWindowComplete])
|
||||
.error_handling(tauri_specta::ErrorHandlingMode::Throw)
|
||||
}
|
||||
|
||||
fn export_types(builder: &tauri_specta::Builder<tauri::Wry>) {
|
||||
builder
|
||||
.export(
|
||||
specta_typescript::Typescript::default(),
|
||||
"../src/bindings.ts",
|
||||
)
|
||||
.expect("Failed to export typescript bindings");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_export_types() {
|
||||
let builder = make_specta_builder();
|
||||
export_types(&builder);
|
||||
}
|
||||
|
||||
#[derive(tauri_specta::Event, serde::Deserialize, specta::Type)]
|
||||
struct LoadingWindowComplete;
|
||||
|
||||
|
||||
@@ -8,20 +8,9 @@ use tokio::task::JoinHandle;
|
||||
|
||||
use crate::{
|
||||
cli,
|
||||
constants::{DEFAULT_SERVER_URL_KEY, SETTINGS_STORE, WSL_ENABLED_KEY},
|
||||
constants::{DEFAULT_SERVER_URL_KEY, SETTINGS_STORE},
|
||||
};
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize, specta::Type, Debug)]
|
||||
pub struct WslConfig {
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl Default for WslConfig {
|
||||
fn default() -> Self {
|
||||
Self { enabled: false }
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn get_default_server_url(app: AppHandle) -> Result<Option<String>, String> {
|
||||
@@ -59,38 +48,6 @@ pub async fn set_default_server_url(app: AppHandle, url: Option<String>) -> Resu
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn get_wsl_config(app: AppHandle) -> Result<WslConfig, String> {
|
||||
let store = app
|
||||
.store(SETTINGS_STORE)
|
||||
.map_err(|e| format!("Failed to open settings store: {}", e))?;
|
||||
|
||||
let enabled = store
|
||||
.get(WSL_ENABLED_KEY)
|
||||
.as_ref()
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false);
|
||||
|
||||
Ok(WslConfig { enabled })
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn set_wsl_config(app: AppHandle, config: WslConfig) -> Result<(), String> {
|
||||
let store = app
|
||||
.store(SETTINGS_STORE)
|
||||
.map_err(|e| format!("Failed to open settings store: {}", e))?;
|
||||
|
||||
store.set(WSL_ENABLED_KEY, serde_json::Value::Bool(config.enabled));
|
||||
|
||||
store
|
||||
.save()
|
||||
.map_err(|e| format!("Failed to save settings: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_saved_server_url(app: &tauri::AppHandle) -> Option<String> {
|
||||
if let Some(url) = get_default_server_url(app.clone()).ok().flatten() {
|
||||
tracing::info!(%url, "Using desktop-specific custom URL");
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
constants::{UPDATER_ENABLED, window_state_flags},
|
||||
server::get_wsl_config,
|
||||
};
|
||||
use crate::constants::{UPDATER_ENABLED, window_state_flags};
|
||||
use std::{ops::Deref, time::Duration};
|
||||
use tauri::{AppHandle, Manager, Runtime, WebviewUrl, WebviewWindow, WebviewWindowBuilder};
|
||||
use tauri_plugin_window_state::AppHandleExt;
|
||||
@@ -25,11 +22,6 @@ impl MainWindow {
|
||||
return Ok(Self(window));
|
||||
}
|
||||
|
||||
let wsl_enabled = get_wsl_config(app.clone())
|
||||
.ok()
|
||||
.map(|v| v.enabled)
|
||||
.unwrap_or(false);
|
||||
|
||||
let window_builder = base_window_config(
|
||||
WebviewWindowBuilder::new(app, Self::LABEL, WebviewUrl::App("/".into())),
|
||||
app,
|
||||
@@ -44,7 +36,6 @@ impl MainWindow {
|
||||
r#"
|
||||
window.__OPENCODE__ ??= {{}};
|
||||
window.__OPENCODE__.updaterEnabled = {UPDATER_ENABLED};
|
||||
window.__OPENCODE__.wsl = {wsl_enabled};
|
||||
"#
|
||||
));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user