@@ -62,13 +62,43 @@ async function verifyAuth(baseUrl: string, authToken: string | undefined): Promi
6262 }
6363}
6464
65- /** Check if the server is healthy at the given base URL. */
65+ /** Check if the server is healthy at the given base URL.
66+ * #3346: Also checks PID file as a fallback when the health endpoint times out.
67+ * If the PID file exists and the process is alive, assume the server is running. */
6668async function isServerHealthy ( baseUrl : string , authToken ?: string ) : Promise < boolean > {
6769 try {
6870 const headers : Record < string , string > = { } ;
6971 if ( authToken ) headers [ 'Authorization' ] = `Bearer ${ authToken } ` ;
7072 const res = await fetch ( `${ baseUrl } /v1/health` , { headers, signal : AbortSignal . timeout ( 3000 ) } ) ;
71- return res . ok ;
73+ if ( res . ok ) return true ;
74+ // Non-401 error or network issue — fall through to PID file check
75+ } catch {
76+ // Network error — fall through to PID file check
77+ }
78+
79+ // #3346: Fallback — check if an Aegis server PID file exists with a live process
80+ try {
81+ const { existsSync, readFileSync } = await import ( 'node:fs' ) ;
82+ const stateDir = process . env . AEGIS_STATE_DIR || join ( homedir ( ) , '.aegis' ) ;
83+ const pidFile = join ( stateDir , 'aegis.pid' ) ;
84+ if ( existsSync ( pidFile ) ) {
85+ const pid = parseInt ( readFileSync ( pidFile , 'utf-8' ) . trim ( ) , 10 ) ;
86+ if ( pid > 0 && pidExists ( pid ) ) {
87+ return true ;
88+ }
89+ }
90+ } catch {
91+ // Ignore
92+ }
93+
94+ return false ;
95+ }
96+
97+ /** Check if a process with the given PID is alive. */
98+ function pidExists ( pid : number ) : boolean {
99+ try {
100+ process . kill ( pid , 0 ) ;
101+ return true ;
72102 } catch {
73103 return false ;
74104 }
0 commit comments