diff --git a/electron/main/index.ts b/electron/main/index.ts index fa2b45d5d..e070e1abd 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -921,7 +921,7 @@ async function createWindow() { update(win); // ==================== check tool installed ==================== - let res:PromiseReturnType = await checkAndInstallDepsOnUpdate(win); + let res:PromiseReturnType = await checkAndInstallDepsOnUpdate({ win }); if (!res.success) { log.info("[DEPS INSTALL] Dependency Error: ", res.message); win.webContents.send('install-dependencies-complete', { success: false, code: 2, error: res.message }); @@ -986,26 +986,26 @@ const setupExternalLinkHandling = () => { const checkAndStartBackend = async () => { log.info('Checking and starting backend service...'); try { - const isToolInstalled = await checkToolInstalled(); - if (isToolInstalled) { - log.info('Tool installed, starting backend service...'); - - // Notify frontend installation success - if (win && !win.isDestroyed()) { - win.webContents.send('install-dependencies-complete', { success: true, code: 0 }); - } - - python_process = await startBackend((port) => { - backendPort = port; - log.info('Backend service started successfully', { port }); - }); - - python_process?.on('exit', (code, signal) => { - - log.info('Python process exited', { code, signal }); - }); - } else { - log.warn('Tool not installed, cannot start backend service'); + const isToolInstalled = await checkToolInstalled(); + if (isToolInstalled) { + log.info('Tool installed, starting backend service...'); + + // Notify frontend installation success + if (win && !win.isDestroyed()) { + win.webContents.send('install-dependencies-complete', { success: true, code: 0 }); + } + + python_process = await startBackend((port) => { + backendPort = port; + log.info('Backend service started successfully', { port }); + }); + + python_process?.on('exit', (code, signal) => { + + log.info('Python process exited', { code, signal }); + }); + } else { + log.warn('Tool not installed, cannot start backend service'); } } catch (error) { log.debug("Cannot Start Backend due to ", error) diff --git a/electron/main/init.ts b/electron/main/init.ts index d1db6b8a7..f993b60d2 100644 --- a/electron/main/init.ts +++ b/electron/main/init.ts @@ -6,7 +6,7 @@ import path from 'path' import * as net from "net"; import { ipcMain, BrowserWindow, app } from 'electron' import { promisify } from 'util' -import { PromiseReturnType } from "./install-deps"; +import { detectInstallationLogs, PromiseReturnType } from "./install-deps"; const execAsync = promisify(exec); @@ -158,6 +158,9 @@ export async function startBackend(setPort?: (port: number) => void): Promise { if (!data) return; const msg = data.toString().trimEnd(); + //Detect if uv sync is run + detectInstallationLogs(msg); + if (msg.toLowerCase().includes("error") || msg.toLowerCase().includes("traceback")) { log.error(`BACKEND: ${msg}`); } else if (msg.toLowerCase().includes("warn")) { @@ -171,6 +174,7 @@ export async function startBackend(setPort?: (port: number) => void): Promise { + //Implicitly runs uv sync const node_process = spawn( uv_path, ["run", "uvicorn", "main:api", "--port", port.toString(), "--loop", "asyncio"], diff --git a/electron/main/install-deps.ts b/electron/main/install-deps.ts index 52fb7a75b..d4205c18e 100644 --- a/electron/main/install-deps.ts +++ b/electron/main/install-deps.ts @@ -15,8 +15,13 @@ export type PromiseReturnType = { success: boolean; } +interface checkInstallProps { + win:BrowserWindow|null; + forceInstall?:boolean +} // Read last run version and install dependencies on update -export async function checkAndInstallDepsOnUpdate(win:BrowserWindow): Promise { +export const checkAndInstallDepsOnUpdate = async ({win, forceInstall=false}:checkInstallProps): +Promise => { const currentVersion = app.getVersion(); let savedVersion = ''; const checkInstallOperations = { @@ -55,7 +60,7 @@ export async function checkAndInstallDepsOnUpdate(win:BrowserWindow): Promise { } }) } + +let dependencyInstallationDetected = false; +let installationNotificationSent = false; +export function detectInstallationLogs(msg:string) { + // Check for UV dependency installation patterns + const installPatterns = [ + "Resolved", // UV resolving dependencies + "Downloaded", // UV downloading packages + "Installing", // UV installing packages + "Built", // UV building packages + "Prepared", // UV preparing virtual environment + "Syncing", // UV sync process + "Creating virtualenv", // Virtual environment creation + "Updating", // UV updating packages + "× No solution found when resolving dependencies", // Dependency resolution issues + "Audited" // UV auditing dependencies + ]; + + // Detect if UV is installing dependencies + if (!dependencyInstallationDetected && installPatterns.some(pattern => + msg.includes(pattern) && !msg.includes("Uvicorn running on") + )) { + dependencyInstallationDetected = true; + log.info('[BACKEND STARTUP] UV dependency installation detected during uvicorn startup'); + + // Create installing lock file to maintain consistency with install-deps.ts + InstallLogs.setLockPath(); + log.info('[BACKEND STARTUP] Created uv_installing.lock file'); + + // Notify frontend that installation has started (only once) + if (!installationNotificationSent) { + installationNotificationSent = true; + const notificationSent = safeMainWindowSend('install-dependencies-start'); + if (notificationSent) { + log.info('[BACKEND STARTUP] Notified frontend of dependency installation start'); + } else { + log.warn('[BACKEND STARTUP] Failed to notify frontend of dependency installation start'); + } + } + } + + // Send installation logs to frontend if installation was detected + if (dependencyInstallationDetected && !msg.includes("Uvicorn running on")) { + safeMainWindowSend('install-dependencies-log', { + type: msg.toLowerCase().includes("error") || msg.toLowerCase().includes("traceback") ? 'stderr' : 'stdout', + data: msg + }); + } + + // Check if installation is complete (uvicorn starts successfully) + if (dependencyInstallationDetected && msg.includes("Uvicorn running on")) { + log.info('[BACKEND STARTUP] UV dependency installation completed, uvicorn started successfully'); + + // Clean up installing lock and create installed lock + InstallLogs.cleanLockPath(); + fs.writeFileSync(installedLockPath, ''); + log.info('[BACKEND STARTUP] Created uv_installed.lock file'); + + safeMainWindowSend('install-dependencies-complete', { + success: true, + message: 'Dependencies installed successfully during backend startup' + }); + } + + // Handle installation failures + if (dependencyInstallationDetected && ( + msg.toLowerCase().includes("failed to resolve dependencies") || + msg.toLowerCase().includes("installation failed") || + msg.includes("× No solution found when resolving dependencies") + )) { + log.error('[BACKEND STARTUP] UV dependency installation failed'); + + // Clean up installing lock file + InstallLogs.cleanLockPath(); + log.info('[BACKEND STARTUP] Cleaned up uv_installing.lock file after failure'); + + safeMainWindowSend('install-dependencies-complete', { + success: false, + error: 'Dependency installation failed during backend startup' + }); + } +} \ No newline at end of file