From fee84b69c6e1aab756c5c1b6a31395c63f2377b1 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 20 Jan 2026 14:47:22 -0600 Subject: [PATCH 1/3] fix(desktop): no proxy for connecting to sidecar --- packages/desktop/src-tauri/src/lib.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/desktop/src-tauri/src/lib.rs b/packages/desktop/src-tauri/src/lib.rs index 3cfa8a9be5a..869c35f42f8 100644 --- a/packages/desktop/src-tauri/src/lib.rs +++ b/packages/desktop/src-tauri/src/lib.rs @@ -156,6 +156,7 @@ fn spawn_sidecar(app: &AppHandle, port: u32, password: &str) -> CommandChild { println!("spawning sidecar on port {port}"); let (mut rx, child) = cli::create_command(app, format!("serve --port {port}").as_str()) + .env("OPENCODE_SERVER_USERNAME", "opencode") .env("OPENCODE_SERVER_PASSWORD", password) .spawn() .expect("Failed to spawn opencode"); @@ -199,11 +200,22 @@ fn spawn_sidecar(app: &AppHandle, port: u32, password: &str) -> CommandChild { async fn check_server_health(url: &str, password: Option<&str>) -> bool { let health_url = format!("{}/global/health", url.trim_end_matches('/')); - let client = reqwest::Client::builder() - .timeout(Duration::from_secs(3)) - .build(); - let Ok(client) = client else { + // Some environments set proxy variables (HTTP_PROXY/HTTPS_PROXY/ALL_PROXY) without + // excluding loopback. reqwest respects these by default, which can prevent the desktop + // app from reaching its own local sidecar server. + let no_proxy = reqwest::Url::parse(&health_url).ok().is_some_and(|u| { + u.host_str().is_some_and(|host| { + host.eq_ignore_ascii_case("localhost") + || host + .parse::() + .is_ok_and(|ip| ip.is_loopback()) + }) + }); + + let builder = reqwest::Client::builder().timeout(Duration::from_secs(3)); + let builder = if no_proxy { builder.no_proxy() } else { builder }; + let Ok(client) = builder.build() else { return false; }; From e8531c8bd9c44142160d3d2874c528e9c1e3f25d Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Wed, 21 Jan 2026 16:03:57 +0800 Subject: [PATCH 2/3] cleanup --- packages/desktop/src-tauri/src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/desktop/src-tauri/src/lib.rs b/packages/desktop/src-tauri/src/lib.rs index 869c35f42f8..977f6e31d30 100644 --- a/packages/desktop/src-tauri/src/lib.rs +++ b/packages/desktop/src-tauri/src/lib.rs @@ -213,8 +213,12 @@ async fn check_server_health(url: &str, password: Option<&str>) -> bool { }) }); - let builder = reqwest::Client::builder().timeout(Duration::from_secs(3)); - let builder = if no_proxy { builder.no_proxy() } else { builder }; + let mut builder = reqwest::Client::builder().timeout(Duration::from_secs(3)); + + if no_proxy { + builder = builder.no_proxy(); + }; + let Ok(client) = builder.build() else { return false; }; From 3779d0fc47b9ae07c6eb02cc204aaa7eea17ef06 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Wed, 21 Jan 2026 16:17:54 +0800 Subject: [PATCH 3/3] cleanup --- packages/desktop/src-tauri/src/lib.rs | 35 +++++++++++++++------------ 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/packages/desktop/src-tauri/src/lib.rs b/packages/desktop/src-tauri/src/lib.rs index 977f6e31d30..7b11397a396 100644 --- a/packages/desktop/src-tauri/src/lib.rs +++ b/packages/desktop/src-tauri/src/lib.rs @@ -198,32 +198,37 @@ fn spawn_sidecar(app: &AppHandle, port: u32, password: &str) -> CommandChild { child } +fn url_is_localhost(url: &reqwest::Url) -> bool { + url.host_str().is_some_and(|host| { + host.eq_ignore_ascii_case("localhost") + || host + .parse::() + .is_ok_and(|ip| ip.is_loopback()) + }) +} + async fn check_server_health(url: &str, password: Option<&str>) -> bool { - let health_url = format!("{}/global/health", url.trim_end_matches('/')); - - // Some environments set proxy variables (HTTP_PROXY/HTTPS_PROXY/ALL_PROXY) without - // excluding loopback. reqwest respects these by default, which can prevent the desktop - // app from reaching its own local sidecar server. - let no_proxy = reqwest::Url::parse(&health_url).ok().is_some_and(|u| { - u.host_str().is_some_and(|host| { - host.eq_ignore_ascii_case("localhost") - || host - .parse::() - .is_ok_and(|ip| ip.is_loopback()) - }) - }); + let Ok(url) = reqwest::Url::parse(url) else { + return false; + }; let mut builder = reqwest::Client::builder().timeout(Duration::from_secs(3)); - if no_proxy { + if url_is_localhost(&url) { + // Some environments set proxy variables (HTTP_PROXY/HTTPS_PROXY/ALL_PROXY) without + // excluding loopback. reqwest respects these by default, which can prevent the desktop + // app from reaching its own local sidecar server. builder = builder.no_proxy(); }; let Ok(client) = builder.build() else { return false; }; + let Ok(health_url) = url.join("/global/health") else { + return false; + }; - let mut req = client.get(&health_url); + let mut req = client.get(health_url); if let Some(password) = password { req = req.basic_auth("opencode", Some(password));