Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 51 additions & 4 deletions packages/desktop-electron/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { existsSync } from "node:fs"
import { createServer } from "node:net"
import { homedir } from "node:os"
import { join } from "node:path"
import { parseArgs } from "node:util"
import type { Event } from "electron"
import { app, BrowserWindow, dialog } from "electron"
import pkg from "electron-updater"
Expand Down Expand Up @@ -63,6 +64,7 @@ const pendingDeepLinks: string[] = []

const serverReady = defer<ServerReadyData>()
const logger = initLogging()
const remoteServerUrl = getRemoteServerUrl()

logger.log("app starting", {
version: app.getVersion(),
Expand Down Expand Up @@ -138,6 +140,25 @@ function setInitStep(step: InitStep) {
}

async function initialize() {
const overlay = remoteServerUrl ? initializeRemoteServer(remoteServerUrl) : await initializeSidecarServer()
mainWindow = createMainWindow()
wireMenu()
overlay?.close()
}

function initializeRemoteServer(url: string) {
logger.log("using remote server", { url })
serverReady.resolve({
url,
username: null,
password: null,
source: "remote",
})
setInitStep({ phase: "done" })
return null
}

async function initializeSidecarServer() {
const needsMigration = !sqliteFileExists()
const sqliteDone = needsMigration ? defer<void>() : undefined
let overlay: BrowserWindow | null = null
Expand Down Expand Up @@ -181,6 +202,7 @@ async function initialize() {
url,
username: "opencode",
password,
source: "sidecar",
})

await Promise.race([
Expand Down Expand Up @@ -210,10 +232,7 @@ async function initialize() {
await loadingComplete.promise
}

mainWindow = createMainWindow()
wireMenu()

overlay?.close()
return overlay
}

function wireMenu() {
Expand Down Expand Up @@ -292,6 +311,34 @@ function ensureLoopbackNoProxy() {
upsert("no_proxy")
}

function getRemoteServerUrl() {
const parsed = parseLaunchArgs().values["server-url"]
const value = typeof parsed === "string" ? parsed : process.env.OPENCODE_DESKTOP_SERVER_URL
if (!value) return

try {
const url = new URL(value)
if (url.protocol !== "http:" && url.protocol !== "https:") return
url.pathname = url.pathname.replace(/\/+$/, "")
return url.toString().replace(/\/$/, "")
} catch {
logger.warn("ignoring invalid --server-url", { value })
}
}

function parseLaunchArgs() {
return parseArgs({
args: process.argv.slice(1),
options: {
"server-url": {
type: "string",
},
},
strict: false,
allowPositionals: true,
})
}

async function getSidecarPort() {
const fromEnv = process.env.OPENCODE_PORT
if (fromEnv) {
Expand Down
1 change: 1 addition & 0 deletions packages/desktop-electron/src/main/windows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export function createMainWindow() {
const { responseHeaders = {} } = details
upsertKeyValue(responseHeaders, "Access-Control-Allow-Origin", ["*"])
upsertKeyValue(responseHeaders, "Access-Control-Allow-Headers", ["*"])
upsertKeyValue(responseHeaders, "Access-Control-Allow-Methods", ["GET, POST, PUT, PATCH, DELETE, OPTIONS"])
callback({ responseHeaders })
})

Expand Down
1 change: 1 addition & 0 deletions packages/desktop-electron/src/preload/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export type ServerReadyData = {
url: string
username: string | null
password: string | null
source: "sidecar" | "remote"
}

export type SqliteMigrationProgress = { type: "InProgress"; value: number } | { type: "Done" }
Expand Down
23 changes: 18 additions & 5 deletions packages/desktop-electron/src/renderer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,7 @@ render(() => {

const [windowCount] = createResource(() => window.api.getWindowCount())

// Fetch sidecar credentials (available immediately, before health check)
const [sidecar] = createResource(() => window.api.awaitInitialization(() => undefined))
const [launchServer] = createResource(() => window.api.awaitInitialization(() => undefined))

const [defaultServer] = createResource(() =>
platform.getDefaultServer?.().then((url) => {
Expand All @@ -279,8 +278,18 @@ render(() => {
const [locale] = createResource(loadLocale)

const servers = () => {
const data = sidecar()
const data = launchServer()
if (!data) return []
if (data.source === "remote") {
return [
{
type: "http",
http: {
url: data.url,
},
},
] as ServerConnection.Any[]
}
const server: ServerConnection.Sidecar = {
displayName: "Local Server",
type: "sidecar",
Expand Down Expand Up @@ -333,7 +342,7 @@ render(() => {
<Show
when={
!defaultServer.loading &&
!sidecar.loading &&
!launchServer.loading &&
!windowConfig.loading &&
!windowCount.loading &&
!locale.loading
Expand All @@ -342,7 +351,11 @@ render(() => {
{(_) => {
return (
<AppInterface
defaultServer={defaultServer.latest ?? ServerConnection.Key.make("sidecar")}
defaultServer={
launchServer.latest?.source === "remote"
? ServerConnection.Key.make(launchServer.latest.url)
: (defaultServer.latest ?? ServerConnection.Key.make("sidecar"))
}
servers={servers()}
router={MemoryRouter}
>
Expand Down
Loading