@@ -9,7 +9,6 @@ import fs from 'node:fs';
99import net from 'node:net' ;
1010
1111import { logger } from '../logger.js' ;
12- import { START_INDICATOR } from '../server.js' ;
1312import type { CallToolResult } from '../third_party/index.js' ;
1413import { PipeTransport } from '../third_party/index.js' ;
1514
@@ -21,27 +20,46 @@ import {
2120 isDaemonRunning ,
2221} from './utils.js' ;
2322
23+ const FILE_TIMEOUT = 10_000 ;
24+
2425/**
25- * Waits for a file to be created and populated.
26+ * Waits for a file to be created and populated (removed = false) or removed (removed = true) .
2627 */
27- function waitForFile ( filePath : string , timeout = 5000 ) {
28+ function waitForFile ( filePath : string , removed = false ) {
2829 return new Promise < void > ( ( resolve , reject ) => {
29- if ( fs . existsSync ( filePath ) && fs . statSync ( filePath ) . size > 0 ) {
30+ const check = ( ) => {
31+ const exists = fs . existsSync ( filePath ) ;
32+ if ( removed ) {
33+ return ! exists ;
34+ }
35+ if ( ! exists ) {
36+ return false ;
37+ }
38+ try {
39+ return fs . statSync ( filePath ) . size > 0 ;
40+ } catch {
41+ return false ;
42+ }
43+ } ;
44+
45+ if ( check ( ) ) {
3046 resolve ( ) ;
3147 return ;
3248 }
3349
3450 const timer = setTimeout ( ( ) => {
3551 fs . unwatchFile ( filePath ) ;
3652 reject (
37- new Error ( `Timeout: file ${ filePath } not found within ${ timeout } ms` ) ,
53+ new Error (
54+ `Timeout: file ${ filePath } ${ removed ? 'not removed' : 'not found' } within ${ FILE_TIMEOUT } ms` ,
55+ ) ,
3856 ) ;
39- } , timeout ) ;
57+ } , FILE_TIMEOUT ) ;
4058
41- fs . watchFile ( filePath , { interval : 500 } , curr => {
42- if ( curr . size > 0 ) {
59+ fs . watchFile ( filePath , { interval : 500 } , ( ) => {
60+ if ( check ( ) ) {
4361 clearTimeout ( timer ) ;
44- fs . unwatchFile ( filePath ) ; // Always clean up your listeners!
62+ fs . unwatchFile ( filePath ) ;
4563 resolve ( ) ;
4664 }
4765 } ) ;
@@ -54,38 +72,22 @@ export async function startDaemon(mcpArgs: string[] = []) {
5472 return ;
5573 }
5674
75+ const pidFilePath = getPidFilePath ( ) ;
76+
77+ if ( fs . existsSync ( pidFilePath ) ) {
78+ fs . unlinkSync ( pidFilePath ) ;
79+ }
80+
5781 logger ( 'Starting daemon...' ) ;
5882 const child = spawn ( process . execPath , [ DAEMON_SCRIPT_PATH , ...mcpArgs ] , {
5983 detached : true ,
60- stdio : [ 'ignore' , 'ignore' , 'pipe' ] ,
84+ stdio : 'ignore' ,
85+ env : process . env ,
6186 cwd : process . cwd ( ) ,
6287 } ) ;
88+ child . unref ( ) ;
6389
64- await new Promise < void > ( ( resolve , reject ) => {
65- child . on ( 'error' , err => {
66- reject ( err ) ;
67- } ) ;
68- child . on ( 'exit' , code => {
69- logger ( `Child exited with code ${ code } ` ) ;
70- reject ( new Error ( `Daemon process exited prematurely with code ${ code } ` ) ) ;
71- } ) ;
72-
73- waitForFile ( getPidFilePath ( ) ) . then ( resolve ) . catch ( reject ) ;
74- } ) ;
75-
76- logger ( `Pid file found ${ getPidFilePath ( ) } ` ) ;
77-
78- child . stderr . pipe ( process . stderr ) ;
79- await new Promise < void > ( resolve => {
80- child . stderr . on ( 'data' , data => {
81- if ( data . toString ( ) . includes ( START_INDICATOR ) ) {
82- child . stderr . unpipe ( process . stderr ) ;
83- child . stderr . destroy ( ) ;
84- child . unref ( ) ;
85- resolve ( ) ;
86- }
87- } ) ;
88- } ) ;
90+ await waitForFile ( pidFilePath ) ;
8991}
9092
9193const SEND_COMMAND_TIMEOUT = 60_000 ; // ms
@@ -135,7 +137,11 @@ export async function stopDaemon() {
135137 return ;
136138 }
137139
140+ const pidFilePath = getPidFilePath ( ) ;
141+
138142 await sendCommand ( { method : 'stop' } ) ;
143+
144+ await waitForFile ( pidFilePath , /*removed=*/ true ) ;
139145}
140146
141147export function handleResponse (
0 commit comments