From c668ab05bcffbc4768648bd62c2858e067220f37 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Tue, 14 Jul 2026 23:26:46 -0700 Subject: [PATCH] fix(codex): resolve the trust-grant entry without requiring electron The grant bridge is reachable from plain-Node CLI entries, where the plain-node entry guard rejects any chunk containing require("electron"). Resolve the bundled session entry from __dirname (root chunk and chunks/ layouts) with an app.asar -> app.asar.unpacked rewrite for packaged runs, instead of electron's app path APIs. --- .../codex/codex-app-server-grant-bridge.ts | 45 +++++++------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/src/main/codex/codex-app-server-grant-bridge.ts b/src/main/codex/codex-app-server-grant-bridge.ts index ef03183b162..db0aef74235 100644 --- a/src/main/codex/codex-app-server-grant-bridge.ts +++ b/src/main/codex/codex-app-server-grant-bridge.ts @@ -39,40 +39,25 @@ const GRANT_ENTRY_FILE_NAME = 'codex-app-server-grant-entry.js' const GRANT_ENTRY_TIMEOUT_MARGIN_MS = 5_000 const GRANT_ENTRY_MAX_BUFFER_BYTES = 16 * 1024 * 1024 -function loadElectronApp(): { getAppPath(): string; isPackaged: boolean } | null { - try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - return ( - (require('electron') as { app?: { getAppPath(): string; isPackaged: boolean } }).app ?? null - ) - } catch { - return null - } -} - export function resolveCodexGrantEntryPath( pathExists: (candidate: string) => boolean = existsSync ): string | null { - const app = loadElectronApp() - let appPath: string | undefined - try { - appPath = app?.getAppPath() - } catch { - appPath = undefined - } - // Why: ELECTRON_RUN_AS_NODE bypasses Electron's asar integration, so the + // Why: this module is reachable from plain-Node CLI entries, so it must not + // require electron (plain-node-entry-guard). __dirname of the built chunk is + // out/main (root chunk) or out/main/chunks; the entry sits at + // out/main/codex/. ELECTRON_RUN_AS_NODE bypasses asar integration, so the // packaged entry must run from app.asar.unpacked (out/main/codex/** is in - // the asarUnpack list). - const unpackedAppPath = - app?.isPackaged && appPath ? appPath.replace('app.asar', 'app.asar.unpacked') : appPath - const candidates = [ - // Dev/E2E: electron-vite's appPath is already out/main. - unpackedAppPath ? join(unpackedAppPath, 'codex', GRANT_ENTRY_FILE_NAME) : null, - unpackedAppPath ? join(unpackedAppPath, 'out', 'main', 'codex', GRANT_ENTRY_FILE_NAME) : null, - // Plain-node CLI context (no electron): resolve relative to this chunk. - join(__dirname, 'codex', GRANT_ENTRY_FILE_NAME), - join(__dirname, '..', 'codex', GRANT_ENTRY_FILE_NAME) - ].filter((candidate): candidate is string => candidate !== null) + // the asarUnpack list) — cover that with a path rewrite instead of app APIs. + const chunkDirs = [__dirname, join(__dirname, '..')] + const candidates = chunkDirs.flatMap((dir) => { + const unpackedDir = dir.includes('app.asar') + ? dir.replace('app.asar', 'app.asar.unpacked') + : null + return [ + ...(unpackedDir ? [join(unpackedDir, 'codex', GRANT_ENTRY_FILE_NAME)] : []), + join(dir, 'codex', GRANT_ENTRY_FILE_NAME) + ] + }) for (const candidate of candidates) { if (pathExists(candidate)) { return candidate