@@ -8,6 +8,14 @@ import { createDaemonHttpServer } from './daemon/http-server.ts';
88import { trackDownloadableArtifact } from './daemon/artifact-tracking.ts' ;
99import { LeaseRegistry } from './daemon/lease-registry.ts' ;
1010import { createRequestHandler } from './daemon/request-router.ts' ;
11+ import { teardownSessionResources } from './daemon/handlers/session-close.ts' ;
12+ import { closeDaemonServers } from './daemon/server-shutdown.ts' ;
13+ import type { SessionState } from './daemon/types.ts' ;
14+ import {
15+ emitDiagnostic ,
16+ flushDiagnosticsToSessionFile ,
17+ withDiagnosticsScope ,
18+ } from './utils/diagnostics.ts' ;
1119import {
1220 acquireDaemonLock ,
1321 parseIntegerEnv ,
@@ -18,7 +26,15 @@ import {
1826 resolveDaemonCodeSignature ,
1927 writeInfo ,
2028} from './daemon/server-lifecycle.ts' ;
21- import { createSocketServer , listenHttpServer , listenNetServer } from './daemon/transport.ts' ;
29+ import {
30+ createSocketServer ,
31+ listenHttpServer ,
32+ listenNetServer ,
33+ type DaemonServer ,
34+ } from './daemon/transport.ts' ;
35+ import { sleep } from './utils/timeouts.ts' ;
36+
37+ const DAEMON_SESSION_TEARDOWN_TIMEOUT_MS = 5_000 ;
2238
2339const daemonPaths = resolveDaemonPaths ( process . env . AGENT_DEVICE_STATE_DIR ) ;
2440const { baseDir, infoPath, lockPath, logPath, sessionsDir } = daemonPaths ;
@@ -45,6 +61,90 @@ const handleRequest = createRequestHandler({
4561 trackDownloadableArtifact,
4662} ) ;
4763
64+ async function emitFatalDiagnostic ( error : unknown ) : Promise < void > {
65+ await withDiagnosticsScope (
66+ { command : 'daemon' , session : 'daemon' , logPath, debug : true } ,
67+ async ( ) => {
68+ emitDiagnostic ( {
69+ level : 'error' ,
70+ phase : 'daemon_fatal' ,
71+ data : {
72+ error : error instanceof Error ? error . message : String ( error ) ,
73+ } ,
74+ } ) ;
75+ flushDiagnosticsToSessionFile ( { force : true } ) ;
76+ } ,
77+ ) ;
78+ }
79+
80+ async function teardownDaemonSessions ( ) : Promise < void > {
81+ const sessionsToStop = sessionStore . toArray ( ) ;
82+ await Promise . all ( sessionsToStop . map ( teardownDaemonSession ) ) ;
83+ }
84+
85+ async function teardownDaemonSession ( session : SessionState ) {
86+ const teardown = teardownSessionResources ( session , session . name ) . catch ( ( error ) => {
87+ process . stderr . write (
88+ `Daemon session teardown error (${ session . name } ): ${
89+ error instanceof Error ? error . message : String ( error )
90+ } \n`,
91+ ) ;
92+ } ) ;
93+ await Promise . race ( [
94+ teardown ,
95+ sleep ( DAEMON_SESSION_TEARDOWN_TIMEOUT_MS ) . then ( ( ) => {
96+ process . stderr . write ( `Daemon session teardown timed out (${ session . name } ).\n` ) ;
97+ } ) ,
98+ ] ) ;
99+ sessionStore . writeSessionLog ( session ) ;
100+ sessionStore . delete ( session . name ) ;
101+ }
102+
103+ async function openDaemonServers ( ) : Promise < {
104+ servers : DaemonServer [ ] ;
105+ socketPort ?: number ;
106+ httpPort ?: number ;
107+ } > {
108+ const servers : DaemonServer [ ] = [ ] ;
109+ let socketPort : number | undefined ;
110+ let httpPort : number | undefined ;
111+ const startSocketServer = daemonServerMode !== 'http' ;
112+ const startHttpServer = daemonServerMode !== 'socket' ;
113+ if ( startSocketServer ) {
114+ const socketServer = createSocketServer ( handleRequest ) ;
115+ servers . push ( socketServer ) ;
116+ socketPort = await listenNetServer ( socketServer ) ;
117+ }
118+
119+ if ( startHttpServer ) {
120+ const httpServer = await createDaemonHttpServer ( { handleRequest, token } ) ;
121+ servers . push ( httpServer ) ;
122+ httpPort = await listenHttpServer ( httpServer ) ;
123+ }
124+ return { servers, socketPort, httpPort } ;
125+ }
126+
127+ function publishDaemonInfo ( socketPort : number | undefined , httpPort : number | undefined ) : void {
128+ writeInfo ( baseDir , infoPath , logPath , {
129+ socketPort,
130+ httpPort,
131+ token,
132+ version,
133+ codeSignature : daemonCodeSignature ,
134+ processStartTime : daemonProcessStartTime ,
135+ } ) ;
136+ if ( socketPort ) process . stdout . write ( `AGENT_DEVICE_DAEMON_PORT=${ socketPort } \n` ) ;
137+ if ( httpPort ) process . stdout . write ( `AGENT_DEVICE_DAEMON_HTTP_PORT=${ httpPort } \n` ) ;
138+ }
139+
140+ function closeServersBestEffort ( servers : DaemonServer [ ] ) : void {
141+ for ( const server of servers ) {
142+ try {
143+ server . close ( ( ) => { } ) ;
144+ } catch { }
145+ }
146+ }
147+
48148async function start ( ) : Promise < void > {
49149 const lockData = {
50150 pid : process . pid ,
@@ -58,73 +158,34 @@ async function start(): Promise<void> {
58158 return ;
59159 }
60160
61- const servers : Array < { close : ( cb : ( err ?: Error ) => void ) => void } > = [ ] ;
62- let socketPort : number | undefined ;
63- let httpPort : number | undefined ;
64-
161+ let servers : DaemonServer [ ] = [ ] ;
65162 try {
66- if ( daemonServerMode === 'socket' || daemonServerMode === 'dual' ) {
67- const socketServer = createSocketServer ( handleRequest ) ;
68- servers . push ( socketServer ) ;
69- socketPort = await listenNetServer ( socketServer ) ;
70- }
71-
72- if ( daemonServerMode === 'http' || daemonServerMode === 'dual' ) {
73- const httpServer = await createDaemonHttpServer ( { handleRequest, token } ) ;
74- servers . push ( httpServer ) ;
75- httpPort = await listenHttpServer ( httpServer ) ;
76- }
77-
78- writeInfo ( baseDir , infoPath , logPath , {
79- socketPort,
80- httpPort,
81- token,
82- version,
83- codeSignature : daemonCodeSignature ,
84- processStartTime : daemonProcessStartTime ,
85- } ) ;
86- if ( socketPort ) process . stdout . write ( `AGENT_DEVICE_DAEMON_PORT=${ socketPort } \n` ) ;
87- if ( httpPort ) process . stdout . write ( `AGENT_DEVICE_DAEMON_HTTP_PORT=${ httpPort } \n` ) ;
163+ const opened = await openDaemonServers ( ) ;
164+ servers = opened . servers ;
165+ publishDaemonInfo ( opened . socketPort , opened . httpPort ) ;
88166 } catch ( error ) {
89167 const appErr = asAppError ( error ) ;
90168 process . stderr . write ( `Daemon error: ${ appErr . message } \n` ) ;
91- for ( const server of servers ) {
92- try {
93- server . close ( ( ) => { } ) ;
94- } catch { }
95- }
169+ closeServersBestEffort ( servers ) ;
96170 removeInfo ( infoPath ) ;
97171 releaseDaemonLock ( lockPath ) ;
98172 process . exit ( 1 ) ;
99173 return ;
100174 }
101175
102176 let shuttingDown = false ;
103- const closeServers = async ( ) : Promise < void > => {
104- await Promise . all (
105- servers . map ( async ( server ) => {
106- await new Promise < void > ( ( resolve ) => {
107- try {
108- server . close ( ( ) => resolve ( ) ) ;
109- } catch {
110- resolve ( ) ;
111- }
112- } ) ;
113- } ) ,
114- ) ;
115- } ;
116- const shutdown = async ( ) => {
177+ const shutdown = async ( options : { exitCode ?: number ; cause ?: unknown } = { } ) => {
117178 if ( shuttingDown ) return ;
118179 shuttingDown = true ;
119- await closeServers ( ) ;
120- const sessionsToStop = sessionStore . toArray ( ) ;
121- for ( const session of sessionsToStop ) {
122- sessionStore . writeSessionLog ( session ) ;
180+ if ( options . cause ) {
181+ await emitFatalDiagnostic ( options . cause ) ;
123182 }
183+ await closeDaemonServers ( servers ) ;
184+ await teardownDaemonSessions ( ) ;
124185 await stopAllIosRunnerSessions ( ) ;
125186 removeInfo ( infoPath ) ;
126187 releaseDaemonLock ( lockPath ) ;
127- process . exit ( 0 ) ;
188+ process . exit ( options . exitCode ?? 0 ) ;
128189 } ;
129190
130191 process . on ( 'SIGINT' , ( ) => {
@@ -139,13 +200,13 @@ async function start(): Promise<void> {
139200 process . on ( 'uncaughtException' , ( err ) => {
140201 const appErr = err instanceof AppError ? err : asAppError ( err ) ;
141202 process . stderr . write ( `Daemon error: ${ appErr . message } \n` ) ;
142- void shutdown ( ) ;
203+ void shutdown ( { exitCode : 1 , cause : err } ) ;
143204 } ) ;
144205 process . on ( 'unhandledRejection' , ( reason ) => {
145206 const err = reason instanceof Error ? reason : new Error ( String ( reason ) ) ;
146207 const appErr = err instanceof AppError ? err : asAppError ( err ) ;
147208 process . stderr . write ( `Daemon error: ${ appErr . message } \n` ) ;
148- void shutdown ( ) ;
209+ void shutdown ( { exitCode : 1 , cause : err } ) ;
149210 } ) ;
150211}
151212
0 commit comments