()
| 99 | } |
| 100 | |
| 101 | function spawnCaffeinate(): void { |
| 102 | // Only run on macOS |
| 103 | if (process.platform !== 'darwin') { |
| 104 | return |
| 105 | } |
| 106 | |
| 107 | // Already running |
| 108 | if (caffeinateProcess !== null) { |
| 109 | return |
| 110 | } |
| 111 | |
| 112 | // Register cleanup on first use to ensure caffeinate is killed on exit |
| 113 | if (!cleanupRegistered) { |
| 114 | cleanupRegistered = true |
| 115 | registerCleanup(async () => { |
| 116 | forceStopPreventSleep() |
| 117 | }) |
| 118 | } |
| 119 | |
| 120 | try { |
| 121 | // -i: Create an assertion to prevent idle sleep |
| 122 | // This is the least aggressive option - display can still sleep |
| 123 | // -t: Timeout in seconds - caffeinate exits automatically after this |
| 124 | // This provides self-healing if Node is killed with SIGKILL |
| 125 | caffeinateProcess = spawn( |
| 126 | 'caffeinate', |
| 127 | ['-i', '-t', String(CAFFEINATE_TIMEOUT_SECONDS)], |
| 128 | { |
| 129 | stdio: 'ignore', |
| 130 | }, |
| 131 | ) |
| 132 | |
| 133 | // Don't let caffeinate keep the Node process alive |
| 134 | caffeinateProcess.unref() |
| 135 | |
| 136 | const thisProc = caffeinateProcess |
| 137 | caffeinateProcess.on('error', err => { |
| 138 | logForDebugging(`caffeinate spawn error: ${err.message}`) |
| 139 | if (caffeinateProcess === thisProc) caffeinateProcess = null |
| 140 | }) |
| 141 | |
| 142 | caffeinateProcess.on('exit', () => { |
| 143 | if (caffeinateProcess === thisProc) caffeinateProcess = null |
| 144 | }) |
| 145 | |
| 146 | logForDebugging('Started caffeinate to prevent sleep') |
| 147 | } catch { |
| 148 | // Silently fail - caffeinate not available or spawn failed |
| 149 | caffeinateProcess = null |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | function killCaffeinate(): void { |
| 154 | if (caffeinateProcess !== null) { |
no test coverage detected