// SPDX-License-Identifier: AGPL-3.0-or-later import fs from 'node:fs'; import path from 'node:path'; import {getDesktopWindowBehaviorSettings, loadDesktopConfig} from '@electron/common/DesktopConfig'; import { DESKTOP_APP_NAME, LINUX_DESKTOP_ENTRY_ID, WINDOWS_APP_USER_MODEL_ID, WINDOWS_TOAST_ACTIVATOR_CLSID, } from '@electron/common/DesktopIdentity'; import {writeLogFilesUnder} from '@electron/common/Logger'; import {configureUserDataPath} from '@electron/common/UserDataPath'; import { APP_STORE_ADDON_PACKAGE, createAppStoreBoundary, getAppStoreLoadFailure, loadAppStoreBinding, } from '@electron/main/AppStoreNativeBoundary'; import {registerAutostartHandlers} from '@electron/main/Autostart'; import {isStartMinimizedLaunch} from '@electron/main/AutostartLaunch'; import { appendWindowsGpuDriverWorkaroundSwitches, purgeChromiumRuntimeCachesIfNeeded, } from '@electron/main/ChromiumRuntime'; import { consumeInitialJumpListTask, handleOpenUrl, handleSecondInstance, initializeDeepLinks, } from '@electron/main/DeepLinks'; import { DesktopAccountStoreEvent, DesktopStorePermissionState, probeDesktopStorePermissions, recordDesktopAccountStoreEvent, } from '@electron/main/DesktopAccountStoreTelemetry'; import { closeDesktopAppStorage, getDesktopAppStorage, openDesktopAppStorage, recoverDesktopAppStorage, } from '@electron/main/DesktopAppStorage'; import { DesktopAppStoreHealth, desktopAppStoreStateFile, evaluateDesktopAppStoreHealth, readDesktopAppStoreState, recordDesktopAppStoreSuccess, } from '@electron/main/DesktopAppStoreHealth'; import { formatDesktopDebugInfo, getDesktopDebugInfo, getLaunchNetLogPath, hasDesktopDebugInfoArg, logDesktopDebugInfo, shouldResetWindowStateOnLaunch, } from '@electron/main/DesktopDebugInfo'; import {recordDesktopLastRoute} from '@electron/main/DesktopLastRoute'; import {cleanupDesktopOutboundHTTP} from '@electron/main/DesktopOutboundHTTP'; import {registerDesktopRuntimeConfigHandlers} from '@electron/main/DesktopRuntimeConfigIpc'; import {installDesktopSystemTrustVerifier} from '@electron/main/DesktopSystemTrustVerifier'; import {destroyDesktopTray, hasActiveDesktopTray, initializeDesktopTray} from '@electron/main/DesktopTray'; import {registerDisplayMediaHandlers} from '@electron/main/DisplayMedia'; import {initializeDockMenu} from '@electron/main/DockMenu'; import {GATEWAY_SOCKET_ADDON_PACKAGE, loadGatewaySocketBinding} from '@electron/main/GatewaySocketNativeBoundary'; import {cleanupGlobalShortcuts, initializeGlobalShortcuts} from '@electron/main/GlobalShortcutsIpc'; import { cleanupGatewayTransportHandlers, cleanupIpcHandlers, registerDesktopAppStorageHandlers, registerGatewayTransportHandlers, registerIpcHandlers, } from '@electron/main/IpcHandlers'; import {initializeJumpList} from '@electron/main/JumpList'; import {describeLaunchDiagnosticOptions} from '@electron/main/LaunchOptions'; import {cleanupLegacyHarvestHandlers, registerLegacyHarvestHandlers} from '@electron/main/LegacyHarvestIpc'; import {initializeLegacyOriginHarvest} from '@electron/main/LegacyOriginHarvest'; import {cleanupVirtmic, registerVirtmicHandlers} from '@electron/main/LinuxAudioCapture'; import {ensureLinuxDesktopEntry} from '@electron/main/LinuxDesktopEntry'; import {cleanupDesktopLocalAppProtocol, getDesktopLocalAppProtocol} from '@electron/main/LocalAppProtocol'; import {initializeMainI18n, t} from '@electron/main/MainI18n'; import {createApplicationMenu} from '@electron/main/Menu'; import { armOpenUrlForwarding, armSecondInstanceForwarding, setMainWindowFactory, setOpenUrlSink, setSecondInstanceSink, } from '@electron/main/ModuleBootHandoff'; import {cleanupNativeAudio, registerNativeAudioHandlers} from '@electron/main/NativeAudio'; import { cleanupNativeHardwareEncoderHandlers, registerNativeHardwareEncoderHandlers, } from '@electron/main/NativeHardwareEncoder'; import {type NativeModulePreflightResult, runNativeModulePreflight} from '@electron/main/NativeModulePreflight'; import {armNativeProbeCache} from '@electron/main/NativeProbeCache'; import {cleanupNativeScreenCapture, registerNativeScreenCaptureHandlers} from '@electron/main/NativeScreenCapture'; import {applyPreReadyChromiumConfiguration} from '@electron/main/PreReadyChromium'; import {cleanupLinuxChromiumSpellcheckDictionaries} from '@electron/main/Spellcheck'; import { clearSavedWindowBounds, createWindow, getMainWindow, hideWindow, isMainWindowTakenOver, setQuitting, showWindow, } from '@electron/main/Window'; import {removeLegacySquirrelUninstallEntry} from '@electron/main/WindowsLegacyUninstallEntry'; import {removeFluxerVulkanLayerRegistrations} from '@electron/main/WindowsVulkanLayerCleanup'; import { DESKTOP_CAPABILITY_MANIFEST_CHANNEL, type DesktopCapabilityManifest, } from '@fluxer/desktop_ipc/src/CapabilityManifest'; import {DESKTOP_LAST_ROUTE_CHANNEL} from '@fluxer/desktop_ipc/src/LastRouteContract'; import { DESKTOP_LEGACY_IMPORT_FAILURE_MARKER_KEY, DESKTOP_LEGACY_IMPORT_MARKER_KEY, DESKTOP_LEGACY_SESSION_MARKER_KEY, DesktopLegacyImportPhase, readDesktopLegacyImportPhase, } from '@fluxer/desktop_ipc/src/StorageContract'; import {app, dialog, ipcMain, netLog, session, shell} from 'electron'; import log from 'electron-log'; log.transports.file.level = 'info'; log.transports.file.sync = false; log.transports.console.level = 'debug'; if (process.platform === 'linux' && process.env.PULSE_LATENCY_MSEC === undefined) { process.env.PULSE_LATENCY_MSEC = '30'; } if (process.platform === 'linux') { process.env['PULSE_PROP_OVERRIDE_application.name'] = DESKTOP_APP_NAME; } process.on('uncaughtException', (error) => { try { log.error('Uncaught exception (early):', error); } catch {} }); process.on('unhandledRejection', (reason, promise) => { try { log.error('Unhandled rejection (early) at:', promise, 'reason:', reason); } catch {} }); const userDataConfig = configureUserDataPath(); if (userDataConfig.portable) { writeLogFilesUnder(app.getPath('logs')); } armNativeProbeCache(userDataConfig.base); const processConfiguredAt = Date.now(); log.info('Configured user data storage', { channel: userDataConfig.channel, directory: userDataConfig.directoryName, path: userDataConfig.base, portable: userDataConfig.portable, }); cleanupLinuxChromiumSpellcheckDictionaries(userDataConfig.base); loadDesktopConfig(userDataConfig.base); function exitCli(code: number): void { process.exitCode = code; setImmediate(() => { process.exit(code); }); } function reportUnreadableDesktopAppStore(storeFile: string, reason: string | null): void { log.error('[AppStore] The desktop app store worked here before and cannot be opened now', {storeFile, reason}); let choice = 0; try { choice = dialog.showMessageBoxSync({ type: 'error', title: t('desktop.appStore.unreadableTitle'), message: t('desktop.appStore.unreadableMessage'), detail: [ t('desktop.appStore.unreadableUnchanged'), t('desktop.appStore.unreadableAdvice'), reason == null ? '' : t('desktop.appStore.unreadableDetails', {reason}), ] .filter(Boolean) .join('\n\n'), buttons: [t('desktop.appMenu.quit'), t('desktop.appStore.showStoreFolder')], defaultId: 0, cancelId: 0, noLink: true, }); } catch (error) { log.error('[AppStore] Failed to present the unreadable store notice:', error); } if (choice === 1) { try { shell.showItemInFolder(storeFile); } catch (error) { log.error('[AppStore] Failed to reveal the store folder:', error); } } app.exit(1); } async function recordDesktopAppStoreImportState(schemaVersion: number): Promise { const storage = getDesktopAppStorage(); if (storage == null) { return; } try { const marker = await storage.getMarker(DESKTOP_LEGACY_IMPORT_MARKER_KEY); const phase = readDesktopLegacyImportPhase(marker); log.info('[AppStore] Legacy import state', { phase, failures: await storage.getMarker(DESKTOP_LEGACY_IMPORT_FAILURE_MARKER_KEY), reconciledAt: await storage.getMarker(DESKTOP_LEGACY_SESSION_MARKER_KEY), stateFile: desktopAppStoreStateFile(userDataConfig.base), }); if (phase === DesktopLegacyImportPhase.DONE) { recordDesktopAppStoreSuccess({userDataPath: userDataConfig.base, now: Date.now(), schemaVersion}); recordDesktopAccountStoreEvent(userDataConfig.base, DesktopAccountStoreEvent.MIGRATED); } } catch (error) { log.warn('[AppStore] Failed to read the legacy import state:', error); } } function writeCliAndExit(stream: NodeJS.WriteStream, message: string, code: number): void { let done = false; const finish = (): void => { if (done) return; done = true; stream.off('error', finish); exitCli(code); }; stream.once('error', finish); stream.write(`${message}\n`, finish); } let launchConfigurationError: Error | null = null; let launchDiagnosticOptions: Record = {}; try { launchDiagnosticOptions = describeLaunchDiagnosticOptions(process.argv); } catch (error) { launchConfigurationError = error instanceof Error ? error : new Error(String(error)); } if (launchConfigurationError) { console.error(`Fluxer desktop launch configuration error: ${launchConfigurationError.message}`); log.error('Launch configuration error:', launchConfigurationError); app.exit(1); } else if (hasDesktopDebugInfoArg(process.argv)) { void getDesktopDebugInfo(userDataConfig.base, {nativeProbes: false}) .then((info) => { try { logDesktopDebugInfo(info); } catch {} writeCliAndExit(process.stdout, formatDesktopDebugInfo(info), 0); }) .catch((error: unknown) => { const message = error instanceof Error ? error.message : String(error); log.error('Failed to collect desktop debug info:', error); writeCliAndExit(process.stderr, `Failed to collect Fluxer desktop debug info: ${message}`, 1); }); } else { if (shouldResetWindowStateOnLaunch(process.argv)) { clearSavedWindowBounds(); } applyPreReadyChromiumConfiguration(userDataConfig.channel, process.argv); log.info('Launch diagnostic modes', launchDiagnosticOptions); const CHANNEL_APP_NAME = DESKTOP_APP_NAME; app.setName(CHANNEL_APP_NAME); function recordStartupPhase(phase: string, phaseStartedAt: number): void { log.info('[Startup] Phase completed', { phase, durationMs: Date.now() - phaseStartedAt, sinceConfiguredMs: Date.now() - processConfiguredAt, }); } function runStartupPhase(phase: string, fn: () => T): T { const phaseStartedAt = Date.now(); try { return fn(); } finally { recordStartupPhase(phase, phaseStartedAt); } } async function runStartupPhaseAsync(phase: string, fn: () => Promise): Promise { const phaseStartedAt = Date.now(); try { return await fn(); } finally { recordStartupPhase(phase, phaseStartedAt); } } async function startLaunchNetLog(): Promise { const netLogPath = getLaunchNetLogPath(userDataConfig.base, process.argv); if (!netLogPath) { return; } try { fs.mkdirSync(path.dirname(netLogPath), {recursive: true}); await netLog.startLogging(netLogPath, {captureMode: 'default'}); log.info('[DebugInfo] Chromium net log started', {path: netLogPath}); } catch (error) { log.error('[DebugInfo] Failed to start Chromium net log:', error); } } const isLegacySquirrelStartupArg = (arg: string): boolean => arg === '--squirrel-install' || arg === '--squirrel-updated' || arg === '--squirrel-uninstall' || arg === '--squirrel-obsolete' || arg === '--squirrel-firstrun'; const hasLegacySquirrelArg = process.platform === 'win32' && process.argv.some(isLegacySquirrelStartupArg); if (hasLegacySquirrelArg) { app.exit(0); } try { runStartupPhase('main-i18n', initializeMainI18n); } catch (error) { log.error('[Init] Failed to initialize native i18n:', error); } let nativeModulePreflight: NativeModulePreflightResult = {degraded: []}; try { nativeModulePreflight = runStartupPhase('native-module-preflight', runNativeModulePreflight); } catch (error) { const message = error instanceof Error ? error.message : String(error); log.error('[NativeModulePreflight] Fatal native module preflight failure:', error); console.error(message); try { dialog.showErrorBox(t('desktop.startup.failedTitle'), message); } catch {} app.exit(1); process.exit(1); } const degradedNativeModules = new Set(nativeModulePreflight.degraded); const desktopCapabilityManifest: DesktopCapabilityManifest = { appStore: !degradedNativeModules.has(APP_STORE_ADDON_PACKAGE) && loadAppStoreBinding() !== null, gatewaySocket: !degradedNativeModules.has(GATEWAY_SOCKET_ADDON_PACKAGE) && loadGatewaySocketBinding() !== null, }; log.info('[Capabilities] Desktop capability manifest', desktopCapabilityManifest); ipcMain.on(DESKTOP_CAPABILITY_MANIFEST_CHANNEL, (event) => { event.returnValue = desktopCapabilityManifest; }); ipcMain.on(DESKTOP_LAST_ROUTE_CHANNEL, (_event, routePath: unknown) => { recordDesktopLastRoute(app.getPath('userData'), routePath); }); if (process.platform === 'win32') { app.setToastActivatorCLSID(WINDOWS_TOAST_ACTIVATOR_CLSID); app.setAppUserModelId(WINDOWS_APP_USER_MODEL_ID); } try { runStartupPhase('local-app-scheme', () => { getDesktopLocalAppProtocol().registerSchemes(); }); } catch (error) { log.error('[Init] Failed to register the local app scheme privileges:', error); app.exit(1); process.exit(1); } const startupController = new AbortController(); let startupWindowsPending = true; const gotTheLock = app.hasSingleInstanceLock() || app.requestSingleInstanceLock(); if (!gotTheLock) { app.quit(); } else { runStartupPhase('runtime-cache-guard', () => { purgeChromiumRuntimeCachesIfNeeded(userDataConfig.base); }); armSecondInstanceForwarding(); setSecondInstanceSink(handleSecondInstance); setMainWindowFactory(() => createWindow()); app.on('child-process-gone', (_event, details) => { log.error('Child process gone', details); }); armOpenUrlForwarding(); setOpenUrlSink(handleOpenUrl); (app.isReady() ? Promise.resolve() : runStartupPhaseAsync('gpu-driver-workarounds', appendWindowsGpuDriverWorkaroundSwitches) ) .then(() => app.whenReady()) .then(async () => { log.info('App ready, initializing...'); try { runStartupPhase('host-resolver', () => app.configureHostResolver({enableAdditionalDnsQueryTypes: false})); } catch (error) { log.error('[Init] Failed to configure the host resolver:', error); } await runStartupPhaseAsync('launch-net-log', startLaunchNetLog); try { runStartupPhase('system-trust', () => installDesktopSystemTrustVerifier(session.defaultSession)); } catch (error) { log.error('[Init] Failed to install the system trust verifier:', error); } try { await runStartupPhaseAsync('desktop-debug-info', async () => { logDesktopDebugInfo(await getDesktopDebugInfo(userDataConfig.base, {nativeProbes: false})); }); } catch (error) { log.error('[DebugInfo] Failed to collect desktop debug info:', error); } try { runStartupPhase('linux-desktop-entry', () => { if (ensureLinuxDesktopEntry()) { process.env.FLUXER_LINUX_PORTAL_APP_ID = LINUX_DESKTOP_ENTRY_ID; } }); } catch (error) { log.error('[Init] Failed to ensure the Linux desktop entry:', error); } try { runStartupPhase('deep-links', initializeDeepLinks); } catch (error) { log.error('[Init] Failed to initialize deep links:', error); } try { runStartupPhase('jump-list', initializeJumpList); } catch (error) { log.error('[Init] Failed to initialize JumpList:', error); } try { runStartupPhase('dock-menu', initializeDockMenu); } catch (error) { log.error('[Init] Failed to initialize macOS dock menu:', error); } let appStoreSchemaVersion = 0; try { const appStore = runStartupPhase('app-store-open', () => openDesktopAppStorage({ userDataPath: userDataConfig.base, createBoundary: createAppStoreBoundary, describeLoadFailure: getAppStoreLoadFailure, }), ); const health = evaluateDesktopAppStoreHealth({ available: appStore.status.available, state: readDesktopAppStoreState(userDataConfig.base), }); log.info('[AppStore] Desktop app store opened', { available: appStore.status.available, schemaVersion: appStore.status.schemaVersion, storeFile: appStore.storeFile, quarantineReason: appStore.status.quarantineReason, quarantinedFile: appStore.quarantinedFile, unavailableReason: appStore.status.unavailableReason, health, }); if (!appStore.status.available) { recordDesktopAccountStoreEvent(userDataConfig.base, DesktopAccountStoreEvent.FALLBACK_TO_WEB); if (health === DesktopAppStoreHealth.REGRESSED) { reportUnreadableDesktopAppStore(appStore.storeFile, appStore.status.unavailableReason); } } if (appStore.status.quarantined) { recordDesktopAccountStoreEvent(userDataConfig.base, DesktopAccountStoreEvent.QUARANTINED); } const permissions = probeDesktopStorePermissions(appStore.storeFile); if (permissions === DesktopStorePermissionState.WIDENED) { recordDesktopAccountStoreEvent(userDataConfig.base, DesktopAccountStoreEvent.PERMISSION_HARDENING_FAILED); log.warn( '[AppStore] The account store is readable beyond this user and permission hardening did not hold', { storeFile: appStore.storeFile, }, ); } appStoreSchemaVersion = appStore.status.schemaVersion; } catch (error) { log.error('[AppStore] Failed to open the desktop app store:', error); } try { const recovery = await runStartupPhaseAsync('app-store-recovery', recoverDesktopAppStorage); if (recovery.reseedRequired) { recordDesktopAccountStoreEvent(userDataConfig.base, DesktopAccountStoreEvent.QUARANTINED); log.warn('[AppStore] The previous store was quarantined and the renderer re-seeds the fresh one', { quarantineReason: recovery.status.quarantineReason, }); } await recordDesktopAppStoreImportState(appStoreSchemaVersion); } catch (error) { log.error('[AppStore] Desktop app store recovery failed:', error); } try { runStartupPhase('app-store-ipc', registerDesktopAppStorageHandlers); } catch (error) { log.error('[AppStore] Failed to register desktop app store IPC handlers:', error); } try { runStartupPhase('gateway-transport-ipc', registerGatewayTransportHandlers); } catch (error) { log.error('[NativeGateway] Failed to register native gateway transport IPC handlers:', error); } try { runStartupPhase('ipc-handlers', registerIpcHandlers); } catch (error) { log.error('[Init] Failed to register IPC handlers:', error); } try { runStartupPhase('autostart-handlers', registerAutostartHandlers); } catch (error) { log.error('[Init] Failed to register autostart handlers:', error); } try { runStartupPhase('global-shortcuts', initializeGlobalShortcuts); } catch (error) { log.error('[Init] Failed to initialize global shortcuts:', error); } try { runStartupPhase('display-media-handlers', registerDisplayMediaHandlers); } catch (error: unknown) { log.error('[Init] Failed to register display media handlers:', error); } try { runStartupPhase('virtmic-handlers', registerVirtmicHandlers); } catch (error: unknown) { log.error('[Init] Failed to register virtmic handlers:', error); } try { runStartupPhase('native-audio-handlers', registerNativeAudioHandlers); } catch (error: unknown) { log.error('[Init] Failed to register native audio handlers:', error); } try { runStartupPhase('vulkan-layer-cleanup', removeFluxerVulkanLayerRegistrations); } catch (error: unknown) { log.error('[Init] Failed to remove stale Vulkan layer registrations:', error); } try { runStartupPhase('legacy-uninstall-entry-cleanup', removeLegacySquirrelUninstallEntry); } catch (error: unknown) { log.error('[Init] Failed to remove the legacy Squirrel uninstall entry:', error); } try { runStartupPhase('native-screen-capture-handlers', registerNativeScreenCaptureHandlers); } catch (error: unknown) { log.error('[Init] Failed to register native screen capture handlers:', error); } try { runStartupPhase('native-hardware-encoder-handlers', registerNativeHardwareEncoderHandlers); } catch (error: unknown) { log.error('[Init] Failed to register native hardware encoder handlers:', error); } try { runStartupPhase('legacy-harvest-ipc', registerLegacyHarvestHandlers); } catch (error: unknown) { log.error('[LegacyHarvest] Failed to register legacy harvest IPC handlers:', error); } try { runStartupPhase('local-app-protocol', () => { getDesktopLocalAppProtocol().register(); }); } catch (error: unknown) { log.error('[LocalApp] Failed to register the local app protocol handler:', error); } try { await runStartupPhaseAsync('legacy-origin-harvest', () => initializeLegacyOriginHarvest(startupController.signal), ); } catch (error: unknown) { log.error('[LegacyHarvest] Legacy origin harvest failed:', error); } try { runStartupPhase('runtime-config-ipc', registerDesktopRuntimeConfigHandlers); } catch (error: unknown) { log.error('[LocalApp] Failed to register runtime config IPC handlers:', error); } try { runStartupPhase('application-menu', createApplicationMenu); } catch (error: unknown) { log.error('[Init] Failed to create application menu:', error); } runStartupPhase('create-window', () => { try { createWindow({startHidden: isStartMinimizedLaunch()}); } finally { startupWindowsPending = false; } }); const initialTask = consumeInitialJumpListTask(); if (initialTask) { const mainWindow = getMainWindow(); mainWindow?.webContents.once('did-finish-load', () => { if (initialTask === 'open-settings') { mainWindow.webContents.send('open-settings'); } else { mainWindow.webContents.send('jump-list-new-dm'); } }); } runStartupPhase('desktop-tray', () => { initializeDesktopTray({ createWindow, getMainWindow, isMainWindowTakenOver, hideWindow, setQuitting, showWindow, }); }); if (process.env.FLUXER_OFFLINE !== '1') { const {registerUpdater} = await import('@electron/main/Updater'); registerUpdater(getMainWindow); } app.on('activate', () => { const mainWindow = getMainWindow(); if ((mainWindow === null || mainWindow.isDestroyed()) && !isMainWindowTakenOver()) { createWindow(); } else { showWindow(); } }); log.info('App initialized successfully'); }) .catch((error: unknown) => { startupWindowsPending = false; log.error('[Startup] whenReady chain rejected:', error); }); app.on('window-all-closed', () => { if (startupWindowsPending) { log.info('[Shutdown] All windows closed before startup created the main window, keeping app alive'); return; } if (isMainWindowTakenOver()) { log.info('[Shutdown] The update splash closed mid update, keeping app alive to reopen the main window'); return; } const settings = getDesktopWindowBehaviorSettings(); if (process.platform !== 'darwin' && !(hasActiveDesktopTray() && settings.showTrayIcon && settings.closeToTray)) { app.quit(); } else if (process.platform !== 'darwin') { log.info('[Shutdown] All windows closed; keeping app alive because close-to-tray is enabled'); } }); const QUIT_WATCHDOG_MS = 15000; let quitWatchdog: NodeJS.Timeout | null = null; function armQuitWatchdog(reason: string): void { if (quitWatchdog) return; quitWatchdog = setTimeout(() => { log.warn('[Shutdown] Process did not exit before watchdog timeout; forcing exit', { reason, timeoutMs: QUIT_WATCHDOG_MS, }); process.exit(0); }, QUIT_WATCHDOG_MS); quitWatchdog.unref?.(); } app.on('before-quit', () => { log.info('[Shutdown] before-quit received'); setQuitting(true); startupController.abort(); armQuitWatchdog('before-quit'); }); let quitCleanupStarted = false; app.on('will-quit', (event) => { if (quitCleanupStarted) return; quitCleanupStarted = true; log.info('[Shutdown] will-quit cleanup started'); armQuitWatchdog('will-quit'); event.preventDefault(); startupController.abort(); try { cleanupLegacyHarvestHandlers(); } catch (error) { log.error('[Shutdown] Failed to remove the legacy harvest IPC handlers:', error); } try { closeDesktopAppStorage(); log.info('[Shutdown] Desktop app store closed and checkpointed'); } catch (error) { log.error('[Shutdown] Failed to close the desktop app store:', error); } try { cleanupGatewayTransportHandlers(); } catch (error) { log.error('[Shutdown] Failed to close the native gateway transport:', error); } try { cleanupDesktopOutboundHTTP(); } catch (error) { log.error('[Shutdown] Failed to close the desktop outbound HTTP client:', error); } cleanupIpcHandlers({quitting: true}); cleanupGlobalShortcuts(); cleanupNativeAudio(); cleanupNativeScreenCapture(); cleanupNativeHardwareEncoderHandlers(); cleanupVirtmic(); destroyDesktopTray(); const asyncCleanups: Array> = [ cleanupDesktopLocalAppProtocol().catch((error: unknown) => { log.error('[Shutdown] Failed to shut down the local app protocol:', error); }), ]; if (netLog.currentlyLogging) { asyncCleanups.push( netLog.stopLogging().catch((error) => { log.warn('[DebugInfo] Failed to stop Chromium net log:', error); }), ); } void Promise.allSettled(asyncCleanups).finally(() => { log.info('[Shutdown] will-quit cleanup complete; exiting'); app.exit(0); }); }); app.on('quit', (_event, exitCode) => { log.info('[Shutdown] quit received', {exitCode}); }); } }