@@ -8,8 +8,6 @@ import { getCACertificates, setDefaultCACertificates } from "node:tls"
88import type { Event } from "electron"
99import { app , BrowserWindow , dialog } from "electron"
1010import pkg from "electron-updater"
11- import { drizzle } from "drizzle-orm/node-sqlite/driver"
12- import type { Server } from "virtual:opencode-server"
1311
1412import contextMenu from "electron-context-menu"
1513contextMenu ( { showSaveImageAs : true , showLookUpSelection : false , showSearchWithGoogle : false } )
@@ -48,7 +46,15 @@ import { registerIpcHandlers, sendDeepLinks, sendMenuCommand, sendSqliteMigratio
4846import { initLogging } from "./logging"
4947import { parseMarkdown } from "./markdown"
5048import { createMenu } from "./menu"
51- import { allocatePort , getDefaultServerUrl , setDefaultServerUrl , spawnLocalServer , spawnWslSidecar } from "./server"
49+ import {
50+ allocatePort ,
51+ getDefaultServerUrl ,
52+ preferAppEnv ,
53+ setDefaultServerUrl ,
54+ spawnLocalServer ,
55+ spawnWslSidecar ,
56+ type SidecarListener ,
57+ } from "./server"
5258import { createWslServersController } from "./wsl-servers"
5359import {
5460 createLoadingWindow ,
@@ -63,7 +69,7 @@ const initEmitter = new EventEmitter()
6369let initStep : InitStep = { phase : "server_waiting" }
6470
6571let mainWindow : BrowserWindow | null = null
66- let server : Server . Listener | null = null
72+ let server : SidecarListener | null = null
6773const loadingComplete = defer < void > ( )
6874
6975const pendingDeepLinks : string [ ] = [ ]
@@ -120,6 +126,8 @@ function setupApp() {
120126 return
121127 }
122128
129+ preferAppEnv ( app . getPath ( "userData" ) )
130+
123131 app . on ( "second-instance" , ( _event : Event , argv : string [ ] ) => {
124132 const urls = argv . filter ( ( arg : string ) => arg . startsWith ( "opencode://" ) )
125133 if ( urls . length ) {
@@ -136,20 +144,21 @@ function setupApp() {
136144 } )
137145
138146 app . on ( "before-quit" , ( ) => {
139- killSidecar ( )
147+ void killSidecar ( )
140148 wslServers . stopAll ( )
141149 } )
142150
143151 app . on ( "will-quit" , ( ) => {
144- killSidecar ( )
152+ void killSidecar ( )
145153 wslServers . stopAll ( )
146154 } )
147155
148156 for ( const signal of [ "SIGINT" , "SIGTERM" ] as const ) {
149157 process . on ( signal , ( ) => {
150- killSidecar ( )
151- wslServers . stopAll ( )
152- app . exit ( 0 )
158+ void killSidecar ( ) . finally ( ( ) => {
159+ wslServers . stopAll ( )
160+ app . exit ( 0 )
161+ } )
153162 } )
154163 }
155164
@@ -216,22 +225,24 @@ async function initialize() {
216225 if ( mainWindow ) sendSqliteMigrationProgress ( mainWindow , progress )
217226 } )
218227
219- if ( needsMigration ) {
220- const { Database, JsonMigration } = await import ( "virtual:opencode-server" )
221- await JsonMigration . run ( drizzle ( { client : Database . Client ( ) . $client } ) , {
222- progress : ( event : { current : number ; total : number } ) => {
223- const percent = Math . round ( ( event . current / event . total ) * 100 )
224- initEmitter . emit ( "sqlite" , { type : "InProgress" , value : percent } )
225- } ,
226- } )
227- initEmitter . emit ( "sqlite" , { type : "Done" } )
228- }
229-
230228 logger . log ( "spawning sidecar" , { url } )
231- const { listener, health } = await spawnLocalServer ( hostname , port , password , ( ) => {
232- ensureLoopbackNoProxy ( )
233- useEnvProxy ( )
234- } )
229+ const { listener, health } = await spawnLocalServer (
230+ hostname ,
231+ port ,
232+ password ,
233+ ( ) => {
234+ ensureLoopbackNoProxy ( )
235+ useEnvProxy ( )
236+ } ,
237+ {
238+ needsMigration,
239+ userDataPath : app . getPath ( "userData" ) ,
240+ onSqliteProgress : ( progress ) => initEmitter . emit ( "sqlite" , progress ) ,
241+ onStdout : ( message ) => logger . log ( "sidecar stdout" , { message } ) ,
242+ onStderr : ( message ) => logger . warn ( "sidecar stderr" , { message } ) ,
243+ onExit : ( code ) => logger . warn ( "sidecar exited" , { code } ) ,
244+ } ,
245+ )
235246 server = listener
236247 serverReady . resolve ( {
237248 url,
@@ -323,7 +334,7 @@ registerIpcHandlers({
323334 getDisplayBackend : async ( ) => null ,
324335 setDisplayBackend : async ( ) => undefined ,
325336 parseMarkdown : async ( markdown ) => parseMarkdown ( markdown ) ,
326- checkAppExists : async ( appName ) => checkAppExists ( appName ) ,
337+ checkAppExists : ( appName ) => checkAppExists ( appName ) ,
327338 wslPath : async ( path , mode , distro ) => wslPath ( path , mode , distro ) ,
328339 resolveAppPath : async ( appName ) => resolveAppPath ( appName ) ,
329340 loadingWindowComplete : ( ) => loadingComplete . resolve ( ) ,
@@ -333,19 +344,21 @@ registerIpcHandlers({
333344 setBackgroundColor : ( color ) => setBackgroundColor ( color ) ,
334345} )
335346
336- function killSidecar ( ) {
347+ async function killSidecar ( ) {
337348 if ( ! server ) return
338- server . stop ( )
349+ const current = server
339350 server = null
351+ await current . stop ( )
340352}
341353
342354function relaunchApp ( ) {
343355 // app.exit() skips before-quit / will-quit, so relaunch callers must
344356 // explicitly stop sidecars here rather than relying on process hooks.
345- killSidecar ( )
346- wslServers . stopAll ( )
347- app . relaunch ( )
348- app . exit ( 0 )
357+ void killSidecar ( ) . finally ( ( ) => {
358+ wslServers . stopAll ( )
359+ app . relaunch ( )
360+ app . exit ( 0 )
361+ } )
349362}
350363
351364function ensureLoopbackNoProxy ( ) {
@@ -445,7 +458,7 @@ async function installUpdate() {
445458 logger . log ( "installing downloaded update" , {
446459 version : downloadedUpdateVersion ,
447460 } )
448- killSidecar ( )
461+ await killSidecar ( )
449462 wslServers . stopAll ( )
450463 autoUpdater . quitAndInstall ( )
451464}
0 commit comments