@@ -4,17 +4,50 @@ import { app, BrowserWindow } from "electron";
44import process from "node:process" ;
55
66const HARNESS_URL = process . env . HARNESS_URL ?? "http://127.0.0.1:4173" ;
7- const SHOW_WINDOW = process . env . CORTEX_ELECTRON_SHOW === "1" ;
8- const OZONE_PLATFORM = process . env . CORTEX_OZONE_PLATFORM ?? "x11" ;
9- const DISABLE_VULKAN = process . env . CORTEX_DISABLE_VULKAN !== "0" ;
7+ const HEADLESS_MODE = process . env . CORTEX_ELECTRON_HEADLESS === "1" ;
8+ const SHOW_WINDOW = ! HEADLESS_MODE && process . env . CORTEX_ELECTRON_SHOW === "1" ;
9+ const EXIT_ON_READY = process . env . CORTEX_EXIT_ON_READY === "1" ;
10+ const READY_TIMEOUT_MS = Number . parseInt (
11+ process . env . CORTEX_READY_TIMEOUT_MS ?? "15000" ,
12+ 10 ,
13+ ) ;
14+ const OZONE_PLATFORM =
15+ process . env . CORTEX_OZONE_PLATFORM ??
16+ ( process . env . WAYLAND_DISPLAY ? "wayland" : "x11" ) ;
17+ const DISABLE_VULKAN = process . env . CORTEX_DISABLE_VULKAN === "1" ;
18+ const ENABLE_UNSAFE_WEBGPU = process . env . CORTEX_ENABLE_UNSAFE_WEBGPU === "1" ;
19+ const IGNORE_GPU_BLOCKLIST = process . env . CORTEX_IGNORE_GPU_BLOCKLIST === "1" ;
1020
11- app . commandLine . appendSwitch ( "enable-unsafe-webgpu" ) ;
12- app . commandLine . appendSwitch ( "ignore-gpu-blocklist" ) ;
13- app . commandLine . appendSwitch ( "ozone-platform" , OZONE_PLATFORM ) ;
21+ if ( HEADLESS_MODE ) {
22+ // Headless mode targets CI/editor sandboxes where hardware DRI may be absent.
23+ app . disableHardwareAcceleration ( ) ;
24+ app . commandLine . appendSwitch ( "headless" ) ;
25+ app . commandLine . appendSwitch ( "disable-gpu" ) ;
26+ app . commandLine . appendSwitch ( "use-gl" , "swiftshader" ) ;
27+ app . commandLine . appendSwitch ( "enable-unsafe-swiftshader" ) ;
28+ }
29+
30+ if ( ENABLE_UNSAFE_WEBGPU ) {
31+ app . commandLine . appendSwitch ( "enable-unsafe-webgpu" ) ;
32+ }
33+ if ( IGNORE_GPU_BLOCKLIST ) {
34+ app . commandLine . appendSwitch ( "ignore-gpu-blocklist" ) ;
35+ }
36+ if ( OZONE_PLATFORM . length > 0 ) {
37+ app . commandLine . appendSwitch ( "ozone-platform" , OZONE_PLATFORM ) ;
38+ }
1439if ( DISABLE_VULKAN ) {
1540 app . commandLine . appendSwitch ( "disable-vulkan" ) ;
1641}
1742
43+ process . on ( "unhandledRejection" , ( error ) => {
44+ globalThis . console . error ( "[electron-harness] unhandledRejection" , error ) ;
45+ } ) ;
46+
47+ process . on ( "uncaughtException" , ( error ) => {
48+ globalThis . console . error ( "[electron-harness] uncaughtException" , error ) ;
49+ } ) ;
50+
1851app . on ( "gpu-info-update" , async ( ) => {
1952 try {
2053 const featureStatus = app . getGPUFeatureStatus ( ) ;
@@ -26,25 +59,98 @@ app.on("gpu-info-update", async () => {
2659 }
2760} ) ;
2861
62+ function sleep ( ms ) {
63+ return new Promise ( ( resolve ) => {
64+ globalThis . setTimeout ( resolve , ms ) ;
65+ } ) ;
66+ }
67+
68+ async function waitForHarnessReady ( mainWindow , timeoutMs ) {
69+ const started = Date . now ( ) ;
70+ while ( Date . now ( ) - started < timeoutMs ) {
71+ try {
72+ const ready = await mainWindow . webContents . executeJavaScript (
73+ "globalThis.__cortexHarnessReady === true" ,
74+ ) ;
75+ if ( ready === true ) {
76+ const report = await mainWindow . webContents . executeJavaScript (
77+ "globalThis.__cortexHarnessReport ?? null" ,
78+ ) ;
79+ return { ready : true , report } ;
80+ }
81+ } catch {
82+ // Renderer can briefly reject while booting; keep polling.
83+ }
84+
85+ await sleep ( 100 ) ;
86+ }
87+
88+ return { ready : false , report : null } ;
89+ }
90+
2991async function createWindow ( ) {
3092 const mainWindow = new BrowserWindow ( {
3193 width : 1280 ,
3294 height : 860 ,
3395 show : SHOW_WINDOW ,
3496 webPreferences : {
97+ offscreen : HEADLESS_MODE ,
3598 contextIsolation : true ,
3699 sandbox : true ,
37100 nodeIntegration : false ,
38101 } ,
39102 } ) ;
40103
104+ mainWindow . on ( "closed" , ( ) => {
105+ globalThis . console . log ( "[electron-harness] window closed" ) ;
106+ } ) ;
107+
108+ mainWindow . webContents . on ( "render-process-gone" , ( _event , details ) => {
109+ globalThis . console . error ( "[electron-harness] render-process-gone" , JSON . stringify ( details ) ) ;
110+ } ) ;
111+
112+ mainWindow . webContents . on ( "did-fail-load" , ( _event , code , description , url ) => {
113+ globalThis . console . error (
114+ `[electron-harness] did-fail-load code=${ code } description=${ description } url=${ url } ` ,
115+ ) ;
116+ } ) ;
117+
41118 await mainWindow . loadURL ( HARNESS_URL ) ;
42119}
43120
44121app . whenReady ( ) . then ( async ( ) => {
45- globalThis . console . log ( `[electron-harness] loading ${ HARNESS_URL } ` ) ;
122+ globalThis . console . log (
123+ `[electron-harness] loading ${ HARNESS_URL } headless=${ HEADLESS_MODE } ozone=${ OZONE_PLATFORM } disableVulkan=${ DISABLE_VULKAN } unsafeWebgpu=${ ENABLE_UNSAFE_WEBGPU } ignoreGpuBlocklist=${ IGNORE_GPU_BLOCKLIST } ` ,
124+ ) ;
46125 await createWindow ( ) ;
47126
127+ if ( EXIT_ON_READY ) {
128+ const window = BrowserWindow . getAllWindows ( ) [ 0 ] ;
129+ if ( ! window ) {
130+ globalThis . console . error ( "[electron-harness] no window created in exit-on-ready mode" ) ;
131+ process . exitCode = 2 ;
132+ app . quit ( ) ;
133+ return ;
134+ }
135+
136+ const { ready, report } = await waitForHarnessReady ( window , READY_TIMEOUT_MS ) ;
137+ if ( ! ready ) {
138+ globalThis . console . error (
139+ `[electron-harness] probe did not become ready within ${ READY_TIMEOUT_MS } ms` ,
140+ ) ;
141+ process . exitCode = 3 ;
142+ app . quit ( ) ;
143+ return ;
144+ }
145+
146+ const selectedProvider = report ?. selectedProvider ?? "unknown" ;
147+ globalThis . console . log (
148+ `[electron-harness] ready selectedProvider=${ selectedProvider } ` ,
149+ ) ;
150+ app . quit ( ) ;
151+ return ;
152+ }
153+
48154 app . on ( "activate" , async ( ) => {
49155 if ( BrowserWindow . getAllWindows ( ) . length === 0 ) {
50156 await createWindow ( ) ;
@@ -55,3 +161,7 @@ app.whenReady().then(async () => {
55161app . on ( "window-all-closed" , ( ) => {
56162 app . quit ( ) ;
57163} ) ;
164+
165+ app . on ( "child-process-gone" , ( _event , details ) => {
166+ globalThis . console . error ( "[electron-harness] child-process-gone" , JSON . stringify ( details ) ) ;
167+ } ) ;
0 commit comments