fix(desktop): server spawn resilience (#13028)

Co-authored-by: Brendan Allan <git@brendonovich.dev>
This commit is contained in:
Adam
2026-02-11 03:44:26 -06:00
committed by GitHub
parent 783888131e
commit 7e1247c420
3 changed files with 107 additions and 19 deletions

View File

@@ -1,9 +1,10 @@
use tauri::{AppHandle, Manager, path::BaseDirectory};
use tauri_plugin_shell::{
ShellExt,
process::{Command, CommandChild, CommandEvent},
process::{Command, CommandChild, CommandEvent, TerminatedPayload},
};
use tauri_plugin_store::StoreExt;
use tokio::sync::oneshot;
use crate::{
LogState,
@@ -273,12 +274,45 @@ pub fn create_command(app: &tauri::AppHandle, args: &str, extra_env: &[(&str, St
}
}
pub fn serve(app: &AppHandle, hostname: &str, port: u32, password: &str) -> CommandChild {
pub fn serve(
app: &AppHandle,
hostname: &str,
port: u32,
password: &str,
) -> (CommandChild, oneshot::Receiver<TerminatedPayload>) {
let log_state = app.state::<LogState>();
let log_state_clone = log_state.inner().clone();
let (exit_tx, exit_rx) = oneshot::channel::<TerminatedPayload>();
println!("spawning sidecar on port {port}");
if let Ok(mut logs) = log_state_clone.0.lock() {
let args =
format!("--print-logs --log-level WARN serve --hostname {hostname} --port {port}");
#[cfg(target_os = "windows")]
{
logs.push_back(format!("[SPAWN] sidecar=opencode-cli args=\"{args}\"\n"));
}
#[cfg(not(target_os = "windows"))]
{
let sidecar = get_sidecar_path(app);
let shell = get_user_shell();
let cmd = if shell.ends_with("/nu") {
format!("^\"{}\" {}", sidecar.display(), args)
} else {
format!("\"{}\" {}", sidecar.display(), args)
};
logs.push_back(format!("[SPAWN] shell=\"{shell}\" argv=\"-il -c {cmd}\"\n"));
}
while logs.len() > MAX_LOG_ENTRIES {
logs.pop_front();
}
}
let envs = [
("OPENCODE_SERVER_USERNAME", "opencode".to_string()),
("OPENCODE_SERVER_PASSWORD", password.to_string()),
@@ -286,13 +320,14 @@ pub fn serve(app: &AppHandle, hostname: &str, port: u32, password: &str) -> Comm
let (mut rx, child) = create_command(
app,
format!("serve --hostname {hostname} --port {port}").as_str(),
format!("--print-logs --log-level WARN serve --hostname {hostname} --port {port}").as_str(),
&envs,
)
.spawn()
.expect("Failed to spawn opencode");
tokio::spawn(async move {
let mut exit_tx = Some(exit_tx);
while let Some(event) = rx.recv().await {
match event {
CommandEvent::Stdout(line_bytes) => {
@@ -321,10 +356,35 @@ pub fn serve(app: &AppHandle, hostname: &str, port: u32, password: &str) -> Comm
}
}
}
CommandEvent::Error(err) => {
eprintln!("{err}");
if let Ok(mut logs) = log_state_clone.0.lock() {
logs.push_back(format!("[ERROR] {err}\n"));
while logs.len() > MAX_LOG_ENTRIES {
logs.pop_front();
}
}
}
CommandEvent::Terminated(payload) => {
if let Ok(mut logs) = log_state_clone.0.lock() {
logs.push_back(format!(
"[EXIT] code={:?} signal={:?}\n",
payload.code, payload.signal
));
while logs.len() > MAX_LOG_ENTRIES {
logs.pop_front();
}
}
if let Some(tx) = exit_tx.take() {
let _ = tx.send(payload);
}
}
_ => {}
}
}
});
child
(child, exit_rx)
}