From 0ed0c2d39f392062019f4cf0d15cc12ed75156ab Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Tue, 30 Jun 2026 13:30:48 -0500 Subject: [PATCH] refactor(desktop): extract desktop-log IPC handlers from main.cjs into logs-ipc.cjs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fifth main.cjs cluster peel. The two hermes:logs:* handlers (reveal, recent) move verbatim into electron/logs-ipc.cjs behind a registerLogsIpc({ ipcMain, DESKTOP_LOG_PATH, hermesLog, fileExists }) registrar. The log path and the in-memory ring buffer live in the main process and are injected. Channel names unchanged → preload + renderer untouched. Adds electron/logs-ipc.test.cjs (surface invariant + recent-tail behavior). --- apps/desktop/electron/logs-ipc.cjs | 27 +++++++++++++++ apps/desktop/electron/logs-ipc.test.cjs | 44 +++++++++++++++++++++++++ apps/desktop/electron/main.cjs | 17 ++-------- 3 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 apps/desktop/electron/logs-ipc.cjs create mode 100644 apps/desktop/electron/logs-ipc.test.cjs diff --git a/apps/desktop/electron/logs-ipc.cjs b/apps/desktop/electron/logs-ipc.cjs new file mode 100644 index 00000000000..c27b94d82bf --- /dev/null +++ b/apps/desktop/electron/logs-ipc.cjs @@ -0,0 +1,27 @@ +'use strict' + +const { shell } = require('electron') +const fs = require('fs') +const path = require('path') + +// Desktop-log IPC: reveal the log file in the OS file manager + return the +// recent in-memory tail. The log path, the in-memory ring buffer, and the +// file-exists probe live in the main process and are injected. +function registerLogsIpc({ DESKTOP_LOG_PATH, fileExists, hermesLog, ipcMain }) { + ipcMain.handle('hermes:logs:reveal', async () => { + try { + await fs.promises.mkdir(path.dirname(DESKTOP_LOG_PATH), { recursive: true }) + if (!fileExists(DESKTOP_LOG_PATH)) { + await fs.promises.appendFile(DESKTOP_LOG_PATH, '') + } + shell.showItemInFolder(DESKTOP_LOG_PATH) + return { ok: true, path: DESKTOP_LOG_PATH } + } catch (error) { + return { ok: false, path: DESKTOP_LOG_PATH, error: error.message } + } + }) + + ipcMain.handle('hermes:logs:recent', async () => ({ path: DESKTOP_LOG_PATH, lines: hermesLog.slice(-200) })) +} + +module.exports = { registerLogsIpc } diff --git a/apps/desktop/electron/logs-ipc.test.cjs b/apps/desktop/electron/logs-ipc.test.cjs new file mode 100644 index 00000000000..07e7ab65a6f --- /dev/null +++ b/apps/desktop/electron/logs-ipc.test.cjs @@ -0,0 +1,44 @@ +'use strict' + +const assert = require('node:assert/strict') +const test = require('node:test') + +const { registerLogsIpc } = require('./logs-ipc.cjs') + +function fakeIpcMain() { + const handlers = new Map() + + return { + handlers, + handle(channel, handler) { + assert.ok(!handlers.has(channel), `duplicate registration for ${channel}`) + handlers.set(channel, handler) + } + } +} + +test('registerLogsIpc wires only hermes:logs:* channels, each to a handler fn', () => { + const ipcMain = fakeIpcMain() + + registerLogsIpc({ ipcMain, DESKTOP_LOG_PATH: '/tmp/desktop.log', fileExists: () => true, hermesLog: [] }) + + assert.deepEqual([...ipcMain.handlers.keys()].sort(), ['hermes:logs:recent', 'hermes:logs:reveal']) + + for (const handler of ipcMain.handlers.values()) { + assert.equal(typeof handler, 'function') + } +}) + +test('logs:recent returns the injected path and the last 200 buffered lines', async () => { + const ipcMain = fakeIpcMain() + const hermesLog = Array.from({ length: 250 }, (_, i) => `line ${i}`) + + registerLogsIpc({ ipcMain, DESKTOP_LOG_PATH: '/tmp/desktop.log', fileExists: () => true, hermesLog }) + + const res = await ipcMain.handlers.get('hermes:logs:recent')({}) + + assert.equal(res.path, '/tmp/desktop.log') + assert.equal(res.lines.length, 200) + assert.equal(res.lines[0], 'line 50') + assert.equal(res.lines.at(-1), 'line 249') +}) diff --git a/apps/desktop/electron/main.cjs b/apps/desktop/electron/main.cjs index 61089f05301..af2db3c4180 100644 --- a/apps/desktop/electron/main.cjs +++ b/apps/desktop/electron/main.cjs @@ -60,6 +60,7 @@ const { registerGitIpc } = require('./git-ipc.cjs') const { registerFsIpc } = require('./fs-ipc.cjs') const { registerTerminalIpc } = require('./terminal-ipc.cjs') const { registerUpdatesIpc } = require('./updates-ipc.cjs') +const { registerLogsIpc } = require('./logs-ipc.cjs') const { OFFICIAL_REPO_HTTPS_URL, isOfficialSshRemote } = require('./update-remote.cjs') const { resolveBehindCount, shouldCountCommits } = require('./update-count.cjs') const { runRebuildWithRetry } = require('./update-rebuild.cjs') @@ -6702,20 +6703,8 @@ ipcMain.handle('hermes:setting:defaultProjectDir:pick', async () => { ipcMain.handle('hermes:fetchLinkTitle', (_event, url) => fetchLinkTitle(url)) -ipcMain.handle('hermes:logs:reveal', async () => { - try { - await fs.promises.mkdir(path.dirname(DESKTOP_LOG_PATH), { recursive: true }) - if (!fileExists(DESKTOP_LOG_PATH)) { - await fs.promises.appendFile(DESKTOP_LOG_PATH, '') - } - shell.showItemInFolder(DESKTOP_LOG_PATH) - return { ok: true, path: DESKTOP_LOG_PATH } - } catch (error) { - return { ok: false, path: DESKTOP_LOG_PATH, error: error.message } - } -}) - -ipcMain.handle('hermes:logs:recent', async () => ({ path: DESKTOP_LOG_PATH, lines: hermesLog.slice(-200) })) +// Desktop-log IPC lives in logs-ipc.cjs; log path + ring buffer are injected. +registerLogsIpc({ DESKTOP_LOG_PATH, fileExists, hermesLog, ipcMain }) function isExecutableFile(filePath) { if (!filePath || !path.isAbsolute(filePath)) {