From 4fa12045bf33db744f4b6df68b44a0ef8f670c56 Mon Sep 17 00:00:00 2001 From: AboMeezO Date: Wed, 18 Mar 2026 06:57:21 +0300 Subject: [PATCH 1/3] Add agent logging for window close events and application shutdown in main.js and ipc handlers.js --- electron/ipc/handlers.js | 26 +++++++++++ electron/main.js | 98 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/electron/ipc/handlers.js b/electron/ipc/handlers.js index 5906ee5..fef2b81 100644 --- a/electron/ipc/handlers.js +++ b/electron/ipc/handlers.js @@ -659,6 +659,32 @@ export const registerHandlers = () => { ipcMain.on("window:close", () => { const window = BrowserWindow.getFocusedWindow() || global.mainWindow; if (window) { + // #region agent log + fetch("http://127.0.0.1:7608/ingest/3e572759-2eef-44d1-ae55-79a863313eca", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Debug-Session-Id": "fa43e1", + }, + body: JSON.stringify({ + sessionId: "fa43e1", + runId: "pre-fix", + hypothesisId: "H1", + location: "electron/ipc/handlers.js:window:close", + message: "window:close IPC received", + data: { + hasFocusedWindow: !!BrowserWindow.getFocusedWindow(), + hasGlobalMainWindow: !!global.mainWindow, + isMainWindowMaximized: + !!global.mainWindow && typeof global.mainWindow.isMaximized === "function" + ? global.mainWindow.isMaximized() + : null, + }, + timestamp: Date.now(), + }), + }).catch(() => {}); + // #endregion + window.close(); } }); diff --git a/electron/main.js b/electron/main.js index c6af8ed..c14b73d 100644 --- a/electron/main.js +++ b/electron/main.js @@ -151,11 +151,60 @@ async function createWindow() { mainWindow.on("close", (event) => { if (!isQuitting) { + // #region agent log + fetch("http://127.0.0.1:7608/ingest/3e572759-2eef-44d1-ae55-79a863313eca", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Debug-Session-Id": "fa43e1", + }, + body: JSON.stringify({ + sessionId: "fa43e1", + runId: "pre-fix", + hypothesisId: "H2", + location: "electron/main.js:mainWindow:close:hidden-to-tray", + message: "mainWindow close intercepted (hide to tray)", + data: { + isQuitting, + isShuttingDown, + isMaximized: mainWindow.isMaximized(), + isVisible: mainWindow.isVisible(), + }, + timestamp: Date.now(), + }), + }).catch(() => {}); + // #endregion + logger.debug("[Window] Intercepted close event, hiding to tray."); event.preventDefault(); mainWindow.hide(); return false; } + + // #region agent log + fetch("http://127.0.0.1:7608/ingest/3e572759-2eef-44d1-ae55-79a863313eca", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Debug-Session-Id": "fa43e1", + }, + body: JSON.stringify({ + sessionId: "fa43e1", + runId: "pre-fix", + hypothesisId: "H2", + location: "electron/main.js:mainWindow:close:isQuitting", + message: "mainWindow close with isQuitting=true", + data: { + isQuitting, + isShuttingDown, + isMaximized: mainWindow.isMaximized(), + isVisible: mainWindow.isVisible(), + }, + timestamp: Date.now(), + }), + }).catch(() => {}); + // #endregion + logger.info("[Window] Closing for real (isQuitting=true)."); }); @@ -172,6 +221,29 @@ async function createWindow() { }); const startMaximized = await settingsService.get("startMaximized"); + + // #region agent log + fetch("http://127.0.0.1:7608/ingest/3e572759-2eef-44d1-ae55-79a863313eca", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Debug-Session-Id": "fa43e1", + }, + body: JSON.stringify({ + sessionId: "fa43e1", + runId: "pre-fix", + hypothesisId: "H3", + location: "electron/main.js:createWindow:startMaximized", + message: "createWindow startMaximized state", + data: { + startMaximized, + isDev, + }, + timestamp: Date.now(), + }), + }).catch(() => {}); + // #endregion + if (startMaximized && mainWindow && !mainWindow.isDestroyed()) { mainWindow.maximize(); } @@ -449,6 +521,32 @@ app.on("before-quit", async (e) => { e.preventDefault(); isShuttingDown = true; + // #region agent log + fetch("http://127.0.0.1:7608/ingest/3e572759-2eef-44d1-ae55-79a863313eca", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Debug-Session-Id": "fa43e1", + }, + body: JSON.stringify({ + sessionId: "fa43e1", + runId: "pre-fix", + hypothesisId: "H1", + location: "electron/main.js:before-quit", + message: "before-quit triggered", + data: { + isShuttingDown, + isQuitting, + hasMainWindow: !!mainWindow, + mainWindowIsDestroyed: !!mainWindow && mainWindow.isDestroyed(), + mainWindowIsVisible: !!mainWindow && mainWindow.isVisible(), + mainWindowIsMaximized: !!mainWindow && mainWindow.isMaximized(), + }, + timestamp: Date.now(), + }), + }).catch(() => {}); + // #endregion + logger.info("Shutting down... performing fast cleanup."); try { From ddea249d3047c3284fe0516d57f6287551a555e5 Mon Sep 17 00:00:00 2001 From: AboMeezO Date: Wed, 18 Mar 2026 07:30:25 +0300 Subject: [PATCH 2/3] Update version to 0.35.1 and remove agent logging for window close events in main.js and ipc handlers.js --- electron/ipc/handlers.js | 26 -- electron/main.js | 560 ++++++++++++++++----------------------- package.json | 2 +- 3 files changed, 235 insertions(+), 353 deletions(-) diff --git a/electron/ipc/handlers.js b/electron/ipc/handlers.js index fef2b81..5906ee5 100644 --- a/electron/ipc/handlers.js +++ b/electron/ipc/handlers.js @@ -659,32 +659,6 @@ export const registerHandlers = () => { ipcMain.on("window:close", () => { const window = BrowserWindow.getFocusedWindow() || global.mainWindow; if (window) { - // #region agent log - fetch("http://127.0.0.1:7608/ingest/3e572759-2eef-44d1-ae55-79a863313eca", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-Debug-Session-Id": "fa43e1", - }, - body: JSON.stringify({ - sessionId: "fa43e1", - runId: "pre-fix", - hypothesisId: "H1", - location: "electron/ipc/handlers.js:window:close", - message: "window:close IPC received", - data: { - hasFocusedWindow: !!BrowserWindow.getFocusedWindow(), - hasGlobalMainWindow: !!global.mainWindow, - isMainWindowMaximized: - !!global.mainWindow && typeof global.mainWindow.isMaximized === "function" - ? global.mainWindow.isMaximized() - : null, - }, - timestamp: Date.now(), - }), - }).catch(() => {}); - // #endregion - window.close(); } }); diff --git a/electron/main.js b/electron/main.js index c14b73d..bff1b04 100644 --- a/electron/main.js +++ b/electron/main.js @@ -24,6 +24,7 @@ protocol.registerSchemesAsPrivileged([ let mainWindow = null; let tray = null; let isQuitting = false; +let shutdownReason = "unknown"; global.mainWindow = null; const EXTERNAL_MEDIA_DIRS_ENV_KEY = "SELFHOST_MEDIA_ALLOWED_DIRS"; @@ -151,60 +152,12 @@ async function createWindow() { mainWindow.on("close", (event) => { if (!isQuitting) { - // #region agent log - fetch("http://127.0.0.1:7608/ingest/3e572759-2eef-44d1-ae55-79a863313eca", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-Debug-Session-Id": "fa43e1", - }, - body: JSON.stringify({ - sessionId: "fa43e1", - runId: "pre-fix", - hypothesisId: "H2", - location: "electron/main.js:mainWindow:close:hidden-to-tray", - message: "mainWindow close intercepted (hide to tray)", - data: { - isQuitting, - isShuttingDown, - isMaximized: mainWindow.isMaximized(), - isVisible: mainWindow.isVisible(), - }, - timestamp: Date.now(), - }), - }).catch(() => {}); - // #endregion - logger.debug("[Window] Intercepted close event, hiding to tray."); event.preventDefault(); mainWindow.hide(); return false; } - // #region agent log - fetch("http://127.0.0.1:7608/ingest/3e572759-2eef-44d1-ae55-79a863313eca", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-Debug-Session-Id": "fa43e1", - }, - body: JSON.stringify({ - sessionId: "fa43e1", - runId: "pre-fix", - hypothesisId: "H2", - location: "electron/main.js:mainWindow:close:isQuitting", - message: "mainWindow close with isQuitting=true", - data: { - isQuitting, - isShuttingDown, - isMaximized: mainWindow.isMaximized(), - isVisible: mainWindow.isVisible(), - }, - timestamp: Date.now(), - }), - }).catch(() => {}); - // #endregion - logger.info("[Window] Closing for real (isQuitting=true)."); }); @@ -221,29 +174,6 @@ async function createWindow() { }); const startMaximized = await settingsService.get("startMaximized"); - - // #region agent log - fetch("http://127.0.0.1:7608/ingest/3e572759-2eef-44d1-ae55-79a863313eca", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-Debug-Session-Id": "fa43e1", - }, - body: JSON.stringify({ - sessionId: "fa43e1", - runId: "pre-fix", - hypothesisId: "H3", - location: "electron/main.js:createWindow:startMaximized", - message: "createWindow startMaximized state", - data: { - startMaximized, - isDev, - }, - timestamp: Date.now(), - }), - }).catch(() => {}); - // #endregion - if (startMaximized && mainWindow && !mainWindow.isDestroyed()) { mainWindow.maximize(); } @@ -263,256 +193,273 @@ await settingsService.init(); // Initialize logger early to catch startup issues logger.init(); -app - .whenReady() - .then(async () => { - try { - logger.info("Application starting up (Ready)..."); - } catch (e) { - try { - const logPath = path.join(app.getPath("userData"), "main.log"); - fs.appendFileSync(logPath, `[FATAL] Logger failed: ${String(e?.message || e)}\n`); - } catch (_) {} +const gotTheLock = app.requestSingleInstanceLock(); + +if (!gotTheLock) { + shutdownReason = "single-instance-lock-failed"; + app.quit(); +} else { + app.on("second-instance", () => { + if (mainWindow) { + if (mainWindow.isMinimized()) mainWindow.restore(); + mainWindow.show(); + mainWindow.focus(); } - protocol.handle("media", async (request) => { + }); + + app + .whenReady() + .then(async () => { try { - const url = new URL(request.url); - let filePath; - let allowedBases = []; - - if (url.hostname === "app") { - const relativePath = decodeURIComponent(url.pathname).replace(/^[/\\]+/, ""); - const appBase = path.resolve(app.getAppPath()); - const cwdBase = path.resolve(process.cwd()); - const appCandidate = path.resolve(appBase, relativePath); - const cwdCandidate = path.resolve(cwdBase, relativePath); - - if (isPathWithinBase(appCandidate, appBase) && fs.existsSync(appCandidate)) { - filePath = appCandidate; - } else if (isPathWithinBase(cwdCandidate, cwdBase) && fs.existsSync(cwdCandidate)) { - filePath = cwdCandidate; + logger.info("Application starting up (Ready)..."); + } catch (e) { + try { + const logPath = path.join(app.getPath("userData"), "main.log"); + fs.appendFileSync(logPath, `[FATAL] Logger failed: ${String(e?.message || e)}\n`); + } catch (_) {} + } + protocol.handle("media", async (request) => { + try { + const url = new URL(request.url); + let filePath; + let allowedBases = []; + + if (url.hostname === "app") { + const relativePath = decodeURIComponent(url.pathname).replace(/^[/\\]+/, ""); + const appBase = path.resolve(app.getAppPath()); + const cwdBase = path.resolve(process.cwd()); + const appCandidate = path.resolve(appBase, relativePath); + const cwdCandidate = path.resolve(cwdBase, relativePath); + + if (isPathWithinBase(appCandidate, appBase) && fs.existsSync(appCandidate)) { + filePath = appCandidate; + } else if (isPathWithinBase(cwdCandidate, cwdBase) && fs.existsSync(cwdCandidate)) { + filePath = cwdCandidate; + } else { + // Keep deterministic lookup and validate against both app-local bases below. + filePath = appCandidate; + } + allowedBases = [appBase, cwdBase]; } else { - // Keep deterministic lookup and validate against both app-local bases below. - filePath = appCandidate; - } - allowedBases = [appBase, cwdBase]; - } else { - // Handle media://G/path, media:///G:/path, media:///G/path - let rawPath = decodeURIComponent(url.pathname); - let hostname = url.hostname; - - if (hostname && hostname.length === 1 && /^[a-zA-Z]$/.test(hostname)) { - // If hostname is "G", and rawPath is "/Minecraft server/image.png" - filePath = path.join(`${hostname}:`, rawPath); - } else if (process.platform === "win32") { - // If rawPath is "/G:/Minecraft server image.png" - if (/^\/[a-zA-Z]:/.test(rawPath)) { - filePath = rawPath.slice(1); - } else if (/^\/[a-zA-Z]\//.test(rawPath)) { - // Case: /G/path - filePath = rawPath.charAt(1) + ":" + rawPath.slice(2); + // Handle media://G/path, media:///G:/path, media:///G/path + let rawPath = decodeURIComponent(url.pathname); + let hostname = url.hostname; + + if (hostname && hostname.length === 1 && /^[a-zA-Z]$/.test(hostname)) { + // If hostname is "G", and rawPath is "/Minecraft server/image.png" + filePath = path.join(`${hostname}:`, rawPath); + } else if (process.platform === "win32") { + // If rawPath is "/G:/Minecraft server image.png" + if (/^\/[a-zA-Z]:/.test(rawPath)) { + filePath = rawPath.slice(1); + } else if (/^\/[a-zA-Z]\//.test(rawPath)) { + // Case: /G/path + filePath = rawPath.charAt(1) + ":" + rawPath.slice(2); + } else { + filePath = rawPath; + } } else { filePath = rawPath; } - } else { - filePath = rawPath; - } - if (!path.isAbsolute(filePath)) { - logger.warn(`[Media Protocol] Rejected non-absolute path: ${filePath}`); - return new Response("Forbidden", { status: 403 }); - } - - if (configuredExternalMediaBases.length > 0) { - allowedBases.push(...configuredExternalMediaBases); - } else { - // Backward-compatible permissive mode: allow readable files on the resolved drive root. - const driveRoot = path.parse(path.resolve(filePath)).root; - if (driveRoot) { - allowedBases.push(driveRoot); - } - if ( - process.platform === "win32" && - hostname && - hostname.length === 1 && - /^[a-zA-Z]$/.test(hostname) - ) { - allowedBases.push(path.resolve(`${hostname}:\\`)); + if (!path.isAbsolute(filePath)) { + logger.warn(`[Media Protocol] Rejected non-absolute path: ${filePath}`); + return new Response("Forbidden", { status: 403 }); } - if (!hasLoggedPermissiveExternalMediaMode) { - logger.warn( - `[Media Protocol] External media requests are using permissive drive-root mode. Set ${EXTERNAL_MEDIA_DIRS_ENV_KEY} to a ${path.delimiter}-separated list of allowed directories to restrict access.` - ); - hasLoggedPermissiveExternalMediaMode = true; + if (configuredExternalMediaBases.length > 0) { + allowedBases.push(...configuredExternalMediaBases); + } else { + // Backward-compatible permissive mode: allow readable files on the resolved drive root. + const driveRoot = path.parse(path.resolve(filePath)).root; + if (driveRoot) { + allowedBases.push(driveRoot); + } + if ( + process.platform === "win32" && + hostname && + hostname.length === 1 && + /^[a-zA-Z]$/.test(hostname) + ) { + allowedBases.push(path.resolve(`${hostname}:\\`)); + } + + if (!hasLoggedPermissiveExternalMediaMode) { + logger.warn( + `[Media Protocol] External media requests are using permissive drive-root mode. Set ${EXTERNAL_MEDIA_DIRS_ENV_KEY} to a ${path.delimiter}-separated list of allowed directories to restrict access.` + ); + hasLoggedPermissiveExternalMediaMode = true; + } } } - } - // Remove any surrounding quotes that might have been pasted - filePath = filePath.replace(/^["']|["']$/g, "").trim(); - filePath = path.normalize(filePath); + // Remove any surrounding quotes that might have been pasted + filePath = filePath.replace(/^["']|["']$/g, "").trim(); + filePath = path.normalize(filePath); - const resolvedFilePath = resolveAndValidatePath(filePath, allowedBases); - if (!resolvedFilePath) { - logger.warn(`[Media Protocol] Blocked path outside allowed bases: ${filePath}`); - return new Response("Forbidden", { status: 403 }); - } - filePath = resolvedFilePath; + const resolvedFilePath = resolveAndValidatePath(filePath, allowedBases); + if (!resolvedFilePath) { + logger.warn(`[Media Protocol] Blocked path outside allowed bases: ${filePath}`); + return new Response("Forbidden", { status: 403 }); + } + filePath = resolvedFilePath; - try { - await fs.promises.access(filePath, fs.constants.R_OK); + try { + await fs.promises.access(filePath, fs.constants.R_OK); + } catch (e) { + return new Response("File not found", { status: 404 }); + } + + const buffer = await fs.promises.readFile(filePath); + const ext = path.extname(filePath).toLowerCase(); + logger.debug(`[Protocol:Media] Serving file: ${filePath} (${ext})`); + const mimeTypes = { + ".png": "image/png", + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".gif": "image/gif", + ".webp": "image/webp", + ".ico": "image/x-icon", + ".svg": "image/svg+xml", + ".bmp": "image/bmp", + }; + + return new Response(buffer, { + headers: { + "Content-Type": mimeTypes[ext] || "application/octet-stream", + "Cache-Control": "public, max-age=3600", + "Access-Control-Allow-Origin": "*", + }, + }); } catch (e) { - return new Response("File not found", { status: 404 }); + logger.error("[Media Protocol] Error:", e); + return new Response("Internal Error", { status: 500 }); } + }); - const buffer = await fs.promises.readFile(filePath); - const ext = path.extname(filePath).toLowerCase(); - logger.debug(`[Protocol:Media] Serving file: ${filePath} (${ext})`); - const mimeTypes = { - ".png": "image/png", - ".jpg": "image/jpeg", - ".jpeg": "image/jpeg", - ".gif": "image/gif", - ".webp": "image/webp", - ".ico": "image/x-icon", - ".svg": "image/svg+xml", - ".bmp": "image/bmp", - }; - - return new Response(buffer, { - headers: { - "Content-Type": mimeTypes[ext] || "application/octet-stream", - "Cache-Control": "public, max-age=3600", - "Access-Control-Allow-Origin": "*", - }, - }); - } catch (e) { - logger.error("[Media Protocol] Error:", e); - return new Response("Internal Error", { status: 500 }); - } - }); + await initializeDatabase(); + registerHandlers(ipcMain); - await initializeDatabase(); - registerHandlers(ipcMain); - - const { initUpdateService } = await import("./services/updateService.js"); - try { - await initUpdateService(); - } catch (err) { - logger.error( - "[Startup] Update service init failed (continuing without updates):", - err?.message, - err?.stack - ); - } + const { initUpdateService } = await import("./services/updateService.js"); + try { + await initUpdateService(); + } catch (err) { + logger.error( + "[Startup] Update service init failed (continuing without updates):", + err?.message, + err?.stack + ); + } - // Create the main window *before* starting or recovering projects so that - // status and stats events can be delivered to the renderer. - const window = await createWindow(); - if (!window) { - logger.error("[Window] createWindow returned null during startup. Aborting tray setup."); - app.quit(); - return; - } + // Create the main window *before* starting or recovering projects so that + // status and stats events can be delivered to the renderer. + const window = await createWindow(); + if (!window) { + logger.error("[Window] createWindow returned null during startup. Aborting tray setup."); + shutdownReason = "createWindow-null"; + app.quit(); + return; + } - const { - startProject, - stopProject, - restartProject, - startAllProjects, - stopAllProjects, - getRunningProjects, - onStatusChange, - onProjectListChange, - } = await import("./services/projectsManager.js"); - const { getProjects, getCategories } = await import("./services/database.js"); - const { updateTrayMenu } = await import("./tray/tray.js"); - - const refreshTray = async () => { - logger.debug("[Tray] Refreshing menu state."); - const [projects, categories] = await Promise.all([getProjects(), getCategories()]); - const runningIds = getRunningProjects(); - updateTrayMenu( - projects, - categories, - runningIds, + const { startProject, stopProject, restartProject, startAllProjects, - stopAllProjects - ); - }; + stopAllProjects, + getRunningProjects, + onStatusChange, + onProjectListChange, + } = await import("./services/projectsManager.js"); + const { getProjects, getCategories } = await import("./services/database.js"); + const { updateTrayMenu } = await import("./tray/tray.js"); + + const refreshTray = async () => { + logger.debug("[Tray] Refreshing menu state."); + const [projects, categories] = await Promise.all([getProjects(), getCategories()]); + const runningIds = getRunningProjects(); + updateTrayMenu( + projects, + categories, + runningIds, + startProject, + stopProject, + restartProject, + startAllProjects, + stopAllProjects + ); + }; - tray = initTray(window, () => { - isQuitting = true; - app.quit(); - }); + tray = initTray(window, () => { + isQuitting = true; + shutdownReason = "tray-quit"; + app.quit(); + }); - // Update tray on status changes - onStatusChange(() => { - refreshTray(); - }); + // Update tray on status changes + onStatusChange(() => { + refreshTray(); + }); - // Update tray on project list changes (add/delete) - onProjectListChange(() => { + // Update tray on project list changes (add/delete) + onProjectListChange(() => { + refreshTray(); + }); + + // Initial tray setup refreshTray(); - }); - // Initial tray setup - refreshTray(); - - // Auto-start projects and check for zombies *after* window + tray exist so - // that sendStatus / stats events have an attached BrowserWindow. - const { - startAutoStartProjects, - checkZombieProcesses, - relaunchProjectsAfterUpdate, - getProjectStartTime, - } = await import("./services/projectsManager.js"); - await checkZombieProcesses(); - await startAutoStartProjects(); - await relaunchProjectsAfterUpdate(); - - // Sync running status to renderer so it doesn't show stale "stopped" for auto-started projects - if (mainWindow && !mainWindow.isDestroyed()) { - try { - const runningIds = getRunningProjects(); - const running = runningIds.map((id) => { - const startTime = getProjectStartTime(id); - return { - id, - startTime: startTime instanceof Date ? startTime.getTime() : (startTime ?? null), - }; - }); - if (running.length > 0) { - mainWindow.webContents.send("project:status-sync", { running }); + // Auto-start projects and check for zombies *after* window + tray exist so + // that sendStatus / stats events have an attached BrowserWindow. + const { + startAutoStartProjects, + checkZombieProcesses, + relaunchProjectsAfterUpdate, + getProjectStartTime, + } = await import("./services/projectsManager.js"); + await checkZombieProcesses(); + await startAutoStartProjects(); + await relaunchProjectsAfterUpdate(); + + // Sync running status to renderer so it doesn't show stale "stopped" for auto-started projects + if (mainWindow && !mainWindow.isDestroyed()) { + try { + const runningIds = getRunningProjects(); + const running = runningIds.map((id) => { + const startTime = getProjectStartTime(id); + return { + id, + startTime: startTime instanceof Date ? startTime.getTime() : (startTime ?? null), + }; + }); + if (running.length > 0) { + mainWindow.webContents.send("project:status-sync", { running }); + } + } catch (err) { + logger.warn("[Window] Failed to send project:status-sync:", err); } - } catch (err) { - logger.warn("[Window] Failed to send project:status-sync:", err); } - } - app.on("activate", async () => { - if (BrowserWindow.getAllWindows().length === 0) { - try { - const createdWindow = await createWindow(); - if (!createdWindow) { - logger.error("[Window] createWindow returned null on activate."); + app.on("activate", async () => { + if (BrowserWindow.getAllWindows().length === 0) { + try { + const createdWindow = await createWindow(); + if (!createdWindow) { + logger.error("[Window] createWindow returned null on activate."); + } + } catch (error) { + logger.error("[Window] Failed to create window on activate:", error); } - } catch (error) { - logger.error("[Window] Failed to create window on activate:", error); + } else if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.show(); } - } else if (mainWindow && !mainWindow.isDestroyed()) { - mainWindow.show(); - } + }); + }) + .catch((error) => { + logger.error("[Startup] Fatal initialization error:", error); + app.exit(1); }); - }) - .catch((error) => { - logger.error("[Startup] Fatal initialization error:", error); - app.exit(1); - }); +} let isShuttingDown = false; @@ -521,33 +468,7 @@ app.on("before-quit", async (e) => { e.preventDefault(); isShuttingDown = true; - // #region agent log - fetch("http://127.0.0.1:7608/ingest/3e572759-2eef-44d1-ae55-79a863313eca", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-Debug-Session-Id": "fa43e1", - }, - body: JSON.stringify({ - sessionId: "fa43e1", - runId: "pre-fix", - hypothesisId: "H1", - location: "electron/main.js:before-quit", - message: "before-quit triggered", - data: { - isShuttingDown, - isQuitting, - hasMainWindow: !!mainWindow, - mainWindowIsDestroyed: !!mainWindow && mainWindow.isDestroyed(), - mainWindowIsVisible: !!mainWindow && mainWindow.isVisible(), - mainWindowIsMaximized: !!mainWindow && mainWindow.isMaximized(), - }, - timestamp: Date.now(), - }), - }).catch(() => {}); - // #endregion - - logger.info("Shutting down... performing fast cleanup."); + logger.info(`Shutting down... performing fast cleanup. reason=${shutdownReason}`); try { const { stopAllProjects } = await import("./services/projectsManager.js"); @@ -571,16 +492,3 @@ app.on("window-all-closed", () => { // Keep running in tray } }); - -const gotTheLock = app.requestSingleInstanceLock(); -if (!gotTheLock) { - app.quit(); -} else { - app.on("second-instance", () => { - if (mainWindow) { - if (mainWindow.isMinimized()) mainWindow.restore(); - mainWindow.show(); - mainWindow.focus(); - } - }); -} diff --git a/package.json b/package.json index 028990d..6aa597a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "selfhost-helper", - "version": "0.35.0", + "version": "0.35.1", "description": "Node.js Project Manager", "main": "electron/main.js", "type": "module", From 34c4a84e1112eb0ff554775f1ff0d2293d45d0a3 Mon Sep 17 00:00:00 2001 From: AboMeezO Date: Wed, 18 Mar 2026 07:37:48 +0300 Subject: [PATCH 3/3] Remove unused shutdown reason variable and related logging in main.js to streamline shutdown process. --- electron/main.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/electron/main.js b/electron/main.js index bff1b04..ee21d86 100644 --- a/electron/main.js +++ b/electron/main.js @@ -24,7 +24,6 @@ protocol.registerSchemesAsPrivileged([ let mainWindow = null; let tray = null; let isQuitting = false; -let shutdownReason = "unknown"; global.mainWindow = null; const EXTERNAL_MEDIA_DIRS_ENV_KEY = "SELFHOST_MEDIA_ALLOWED_DIRS"; @@ -196,7 +195,6 @@ logger.init(); const gotTheLock = app.requestSingleInstanceLock(); if (!gotTheLock) { - shutdownReason = "single-instance-lock-failed"; app.quit(); } else { app.on("second-instance", () => { @@ -356,7 +354,6 @@ if (!gotTheLock) { const window = await createWindow(); if (!window) { logger.error("[Window] createWindow returned null during startup. Aborting tray setup."); - shutdownReason = "createWindow-null"; app.quit(); return; } @@ -392,7 +389,6 @@ if (!gotTheLock) { tray = initTray(window, () => { isQuitting = true; - shutdownReason = "tray-quit"; app.quit(); }); @@ -468,7 +464,7 @@ app.on("before-quit", async (e) => { e.preventDefault(); isShuttingDown = true; - logger.info(`Shutting down... performing fast cleanup. reason=${shutdownReason}`); + logger.info("Shutting down... performing fast cleanup."); try { const { stopAllProjects } = await import("./services/projectsManager.js");