@@ -4,7 +4,7 @@ import os from 'node:os';
44import path from 'node:path' ;
55import { AppError } from './utils/errors.ts' ;
66import type { DaemonRequest as SharedDaemonRequest , DaemonResponse as SharedDaemonResponse } from './daemon/types.ts' ;
7- import { runCmdDetached } from './utils/exec.ts' ;
7+ import { runCmdDetached , runCmdSync } from './utils/exec.ts' ;
88import { findProjectRoot , readVersion } from './utils/version.ts' ;
99import { createRequestId , emitDiagnostic , withDiagnosticTimer } from './utils/diagnostics.ts' ;
1010import {
@@ -41,6 +41,11 @@ const REQUEST_TIMEOUT_MS = resolveDaemonRequestTimeoutMs();
4141const DAEMON_STARTUP_TIMEOUT_MS = 5000 ;
4242const DAEMON_TAKEOVER_TERM_TIMEOUT_MS = 3000 ;
4343const DAEMON_TAKEOVER_KILL_TIMEOUT_MS = 1000 ;
44+ const IOS_RUNNER_XCODEBUILD_KILL_PATTERNS = [
45+ 'xcodebuild .*AgentDeviceRunnerUITests/RunnerTests/testCommand' ,
46+ 'xcodebuild .*AgentDeviceRunner\\.env\\.session-' ,
47+ 'xcodebuild build-for-testing .*ios-runner/AgentDeviceRunner/AgentDeviceRunner\\.xcodeproj' ,
48+ ] ;
4449
4550export async function sendToDaemon ( req : Omit < DaemonRequest , 'token' > ) : Promise < DaemonResponse > {
4651 const requestId = req . meta ?. requestId ?? createRequestId ( ) ;
@@ -242,20 +247,23 @@ async function sendRequest(info: DaemonInfo, req: DaemonRequest): Promise<Daemon
242247 } ) ;
243248 const timeout = setTimeout ( ( ) => {
244249 socket . destroy ( ) ;
250+ const cleanup = cleanupTimedOutIosRunnerBuilds ( ) ;
245251 emitDiagnostic ( {
246252 level : 'error' ,
247253 phase : 'daemon_request_timeout' ,
248254 data : {
249255 timeoutMs : REQUEST_TIMEOUT_MS ,
250256 requestId : req . meta ?. requestId ,
251257 command : req . command ,
258+ timedOutRunnerPidsTerminated : cleanup . terminated ,
259+ timedOutRunnerCleanupError : cleanup . error ,
252260 } ,
253261 } ) ;
254262 reject (
255263 new AppError ( 'COMMAND_FAILED' , 'Daemon request timed out' , {
256264 timeoutMs : REQUEST_TIMEOUT_MS ,
257265 requestId : req . meta ?. requestId ,
258- hint : 'Retry with --debug and check daemon diagnostics logs.' ,
266+ hint : 'Retry with --debug and check daemon diagnostics logs. Timed-out iOS runner xcodebuild processes were terminated when detected. ' ,
259267 } ) ,
260268 ) ;
261269 } , REQUEST_TIMEOUT_MS ) ;
@@ -307,6 +315,22 @@ async function sendRequest(info: DaemonInfo, req: DaemonRequest): Promise<Daemon
307315 } ) ;
308316}
309317
318+ function cleanupTimedOutIosRunnerBuilds ( ) : { terminated : number ; error ?: string } {
319+ let terminated = 0 ;
320+ try {
321+ for ( const pattern of IOS_RUNNER_XCODEBUILD_KILL_PATTERNS ) {
322+ const result = runCmdSync ( 'pkill' , [ '-f' , pattern ] , { allowFailure : true } ) ;
323+ if ( result . exitCode === 0 ) terminated += 1 ;
324+ }
325+ return { terminated } ;
326+ } catch ( error ) {
327+ return {
328+ terminated,
329+ error : error instanceof Error ? error . message : String ( error ) ,
330+ } ;
331+ }
332+ }
333+
310334export function resolveDaemonRequestTimeoutMs ( raw : string | undefined = process . env . AGENT_DEVICE_DAEMON_TIMEOUT_MS ) : number {
311335 if ( ! raw ) return 90000 ;
312336 const parsed = Number ( raw ) ;
0 commit comments