(
oldPid: number,
options: { timeoutMs?: number; pollIntervalMs?: number } = {}
)
| 243 | * `oldPid`, false on timeout. |
| 244 | */ |
| 245 | export async function waitForRunnerHandoff( |
| 246 | oldPid: number, |
| 247 | options: { timeoutMs?: number; pollIntervalMs?: number } = {} |
| 248 | ): Promise<boolean> { |
| 249 | const timeoutMs = options.timeoutMs ?? 30_000; |
| 250 | const pollIntervalMs = options.pollIntervalMs ?? 500; |
| 251 | const deadline = Date.now() + timeoutMs; |
| 252 | |
| 253 | while (Date.now() < deadline) { |
| 254 | try { |
| 255 | const state = await readRunnerState(); |
| 256 | if (state && state.pid !== oldPid && isProcessAlive(state.pid)) { |
| 257 | logger.debug(`[RUNNER CONTROL] Handoff confirmed: new runner PID ${state.pid} replaced ${oldPid}`); |
| 258 | return true; |
| 259 | } |
| 260 | } catch (error) { |
| 261 | logger.debug('[RUNNER CONTROL] Error polling runner state during handoff wait', error); |
| 262 | } |
| 263 | await new Promise(resolve => setTimeout(resolve, pollIntervalMs)); |
| 264 | } |
| 265 | logger.debug(`[RUNNER CONTROL] Handoff timeout: no replacement runner registered within ${timeoutMs}ms`); |
| 266 | return false; |
| 267 | } |
| 268 | |
| 269 | export async function cleanupRunnerState(): Promise<void> { |
| 270 | try { |
no test coverage detected