11require ( "dotenv" ) . config ( ) ;
22
3+ const path = require ( "path" ) ;
34const { app, BrowserWindow, globalShortcut, session, ipcMain } = require ( "electron" ) ;
45
56// ── Linux GPU process crash workaround ──
@@ -58,7 +59,10 @@ class ApplicationController {
5859 // a settings-window prompt on first launch so users don't have to
5960 // dig through docs to figure out they need a Gemini API key.
6061 this . firstRunManager = new FirstRunManager ( {
61- logger : logger
62+ logger : logger ,
63+ // Sentinel lives in userData so it survives cwd changes
64+ // (the app may be launched from any directory).
65+ sentinelPath : path . join ( app . getPath ( "userData" ) , ".opencluely-firstrun-completed" ) ,
6266 } ) ;
6367 // Lazily-initialised in getWhisperInstaller() so tests can mock
6468 // the constructor without polluting main-process startup.
@@ -166,10 +170,21 @@ class ApplicationController {
166170 // Small delay to ensure desktop/space detection is accurate
167171 await new Promise ( ( resolve ) => setTimeout ( resolve , 200 ) ) ;
168172
169- // During first-run onboarding, defer showing the main overlay
170- // window until the wizard finishes (it needs API keys to function).
171- const status = this . firstRunManager . getStatus ( ) ;
173+ // First-run onboarding: ensure .env exists and read status once
174+ // so we can decide whether to defer showing the main overlay.
175+ let status ;
176+ try {
177+ this . firstRunManager . ensureEnv ( ) ;
178+ status = this . firstRunManager . getStatus ( ) ;
179+ this . isFirstRun = status . needsOnboarding ;
180+ logger . info ( "First-run status" , status ) ;
181+ } catch ( e ) {
182+ logger . warn ( "First-run check failed" , { error : e . message } ) ;
183+ status = { needsOnboarding : false } ;
184+ this . isFirstRun = false ;
185+ }
172186 const isFirstRun = status . needsOnboarding ;
187+
173188 await windowManager . initializeWindows ( { showMainWindow : ! isFirstRun } ) ;
174189 this . setupGlobalShortcuts ( ) ;
175190
@@ -179,36 +194,26 @@ class ApplicationController {
179194 this . starting = false ;
180195 this . isReady = true ;
181196
182- // First-run onboarding: ensure .env exists and launch the
183- // multi-step onboarding wizard if the user hasn't completed it.
184- // Non-blocking — failure here just logs.
185- try {
186- this . firstRunManager . ensureEnv ( ) ;
187- const status = this . firstRunManager . getStatus ( ) ;
188- this . isFirstRun = status . needsOnboarding ;
189- logger . info ( "First-run status" , status ) ;
190- if ( this . isFirstRun ) {
191- // Defer slightly so all windows finish loading before we pop
192- // the wizard on top of them.
193- setTimeout ( ( ) => {
194- try {
195- windowManager . showOnboarding ( ) ;
196- windowManager . broadcastToAllWindows ( "first-run" , status ) ;
197- logger . info ( "First-run onboarding: wizard opened" ) ;
198- } catch ( e ) {
199- logger . warn ( "Could not open first-run onboarding window" , {
200- error : e . message
201- } ) ;
202- // Fallback to legacy settings prompt
203- try { this . showSettings ( ) ; } catch ( _ ) { /* ignore */ }
204- }
205- } , 800 ) ;
206- } else {
207- // Already configured — mark completed so we never nag again.
208- this . firstRunManager . markCompleted ( ) ;
209- }
210- } catch ( e ) {
211- logger . warn ( "First-run check failed" , { error : e . message } ) ;
197+ // Launch the onboarding wizard if this is the first run.
198+ if ( this . isFirstRun ) {
199+ // Defer slightly so all windows finish loading before we pop
200+ // the wizard on top of them.
201+ setTimeout ( ( ) => {
202+ try {
203+ windowManager . showOnboarding ( ) ;
204+ windowManager . broadcastToAllWindows ( "first-run" , status ) ;
205+ logger . info ( "First-run onboarding: wizard opened" ) ;
206+ } catch ( e ) {
207+ logger . warn ( "Could not open first-run onboarding window" , {
208+ error : e . message
209+ } ) ;
210+ // Fallback to legacy settings prompt
211+ try { this . showSettings ( ) ; } catch ( _ ) { /* ignore */ }
212+ }
213+ } , 800 ) ;
214+ } else {
215+ // Already configured — mark completed so we never nag again.
216+ this . firstRunManager . markCompleted ( ) ;
212217 }
213218
214219 logger . info ( "Application initialized successfully" , {
0 commit comments