diff --git a/.gitignore b/.gitignore
index fc175568df6..75fa054a5e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,4 @@ target
# Local dev files
opencode-dev
logs/
+*.bun-build
diff --git a/packages/app/src/app.tsx b/packages/app/src/app.tsx
index 13d9d147e25..3504e00acb4 100644
--- a/packages/app/src/app.tsx
+++ b/packages/app/src/app.tsx
@@ -33,7 +33,7 @@ const Loading = () =>
,
})
+ const [defaultUrl, defaultUrlActions] = createResource(() => platform.getDefaultServerUrl?.())
+ const isDesktop = platform.platform === "desktop"
const items = createMemo(() => {
const current = server.url
@@ -173,6 +175,53 @@ export function DialogSelectServer() {
+
+
+
+
+
Default server
+
+ Connect to this server on app launch instead of starting a local server. Requires restart.
+
+
+
+
No server selected}
+ >
+
+
+ }
+ >
+
+ {serverDisplayName(defaultUrl()!)}
+
+
+
+
+
+
)
diff --git a/packages/app/src/context/platform.tsx b/packages/app/src/context/platform.tsx
index 7fcbb620ac1..b0822e70787 100644
--- a/packages/app/src/context/platform.tsx
+++ b/packages/app/src/context/platform.tsx
@@ -37,6 +37,12 @@ export type Platform = {
/** Fetch override */
fetch?: typeof fetch
+
+ /** Get the configured default server URL (desktop only) */
+ getDefaultServerUrl?(): Promise
+
+ /** Set the default server URL to use on app startup (desktop only) */
+ setDefaultServerUrl?(url: string | null): Promise
}
export const { use: usePlatform, provider: PlatformProvider } = createSimpleContext({
diff --git a/packages/desktop/src-tauri/Cargo.lock b/packages/desktop/src-tauri/Cargo.lock
index c533bf9e95d..92953ea19ca 100644
--- a/packages/desktop/src-tauri/Cargo.lock
+++ b/packages/desktop/src-tauri/Cargo.lock
@@ -2795,6 +2795,7 @@ dependencies = [
"futures",
"gtk",
"listeners",
+ "reqwest",
"semver",
"serde",
"serde_json",
diff --git a/packages/desktop/src-tauri/Cargo.toml b/packages/desktop/src-tauri/Cargo.toml
index af8adc1197a..c8eb0846c8d 100644
--- a/packages/desktop/src-tauri/Cargo.toml
+++ b/packages/desktop/src-tauri/Cargo.toml
@@ -38,6 +38,7 @@ listeners = "0.3"
tauri-plugin-os = "2"
futures = "0.3.31"
semver = "1.0.27"
+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] }
[target.'cfg(target_os = "linux")'.dependencies]
gtk = "0.18.2"
diff --git a/packages/desktop/src-tauri/src/lib.rs b/packages/desktop/src-tauri/src/lib.rs
index c31c0df3530..40297d6536a 100644
--- a/packages/desktop/src-tauri/src/lib.rs
+++ b/packages/desktop/src-tauri/src/lib.rs
@@ -13,6 +13,7 @@ use tauri::{
path::BaseDirectory, AppHandle, LogicalSize, Manager, RunEvent, State, WebviewUrl,
WebviewWindow,
};
+use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogResult};
use tauri_plugin_shell::process::{CommandChild, CommandEvent};
use tauri_plugin_shell::ShellExt;
use tauri_plugin_store::StoreExt;
@@ -20,6 +21,9 @@ use tokio::net::TcpSocket;
use crate::window_customizer::PinchZoomDisablePlugin;
+const SETTINGS_STORE: &str = "opencode.settings.dat";
+const DEFAULT_SERVER_URL_KEY: &str = "defaultServerUrl";
+
#[derive(Clone)]
struct ServerState {
child: Arc>>,
@@ -148,6 +152,41 @@ async fn ensure_server_started(state: State<'_, ServerState>) -> Result<(), Stri
.map_err(|_| "Failed to get server status".to_string())?
}
+#[tauri::command]
+async fn get_default_server_url(app: AppHandle) -> Result