@@ -202,6 +202,17 @@ const parseDaemonUrl = (baseUrl: string) =>
202202const daemonBaseUrl = ( hostname : string , port : number ) : string =>
203203 `http://${ canonicalDaemonHost ( hostname ) } :${ port } ` ;
204204
205+ const installDefaultExecutorWebBaseUrl = ( baseUrl : string ) : ( ( ) => void ) => {
206+ if ( process . env . EXECUTOR_WEB_BASE_URL !== undefined ) {
207+ return ( ) => { } ;
208+ }
209+
210+ process . env . EXECUTOR_WEB_BASE_URL = baseUrl ;
211+ return ( ) => {
212+ delete process . env . EXECUTOR_WEB_BASE_URL ;
213+ } ;
214+ } ;
215+
205216const cleanupPointer = ( input : { hostname : string ; scopeId : string ; port : number } ) =>
206217 Effect . gen ( function * ( ) {
207218 yield * removeDaemonPointer ( { hostname : input . hostname , scopeId : input . scopeId } ) . pipe (
@@ -543,41 +554,49 @@ const runForegroundSession = (input: {
543554 authPassword : string | undefined ;
544555} ) =>
545556 Effect . gen ( function * ( ) {
546- const server = yield * Effect . promise ( ( ) =>
547- startServer ( {
548- port : input . port ,
549- hostname : input . hostname ,
550- allowedHosts : input . allowedHosts ,
551- authToken : input . authToken ,
552- authPassword : input . authPassword ,
553- embeddedWebUI,
554- } ) ,
555- ) ;
556-
557557 const displayHost =
558558 input . hostname === "0.0.0.0" || input . hostname === "::" ? "localhost" : input . hostname ;
559- const baseUrl = `http://${ displayHost } :${ server . port } ` ;
560- console . log ( `Executor is ready.` ) ;
561- console . log ( `Web: ${ baseUrl } ` ) ;
562- console . log ( `MCP: ${ baseUrl } /mcp` ) ;
563- console . log ( `OpenAPI: ${ baseUrl } /api/docs` ) ;
564- if ( input . hostname !== "127.0.0.1" && input . hostname !== "localhost" ) {
565- console . log (
566- `\n⚠ Listening on ${ input . hostname } . Executor runs arbitrary commands — only expose on trusted networks.` ,
559+ const restoreWebBaseUrl = installDefaultExecutorWebBaseUrl (
560+ `http://${ displayHost } :${ input . port } ` ,
561+ ) ;
562+
563+ try {
564+ const server = yield * Effect . promise ( ( ) =>
565+ startServer ( {
566+ port : input . port ,
567+ hostname : input . hostname ,
568+ allowedHosts : input . allowedHosts ,
569+ authToken : input . authToken ,
570+ authPassword : input . authPassword ,
571+ embeddedWebUI,
572+ } ) ,
567573 ) ;
568- if ( input . allowedHosts . length > 0 ) {
569- console . log ( ` Extra allowed Host headers: ${ input . allowedHosts . join ( ", " ) } ` ) ;
570- }
571- if ( input . authPassword ) {
572- console . log ( " Basic authentication is enabled." ) ;
573- } else if ( input . authToken ) {
574- console . log ( " Token authentication is enabled." ) ;
574+
575+ const baseUrl = `http://${ displayHost } :${ server . port } ` ;
576+ console . log ( `Executor is ready.` ) ;
577+ console . log ( `Web: ${ baseUrl } ` ) ;
578+ console . log ( `MCP: ${ baseUrl } /mcp` ) ;
579+ console . log ( `OpenAPI: ${ baseUrl } /api/docs` ) ;
580+ if ( input . hostname !== "127.0.0.1" && input . hostname !== "localhost" ) {
581+ console . log (
582+ `\n⚠ Listening on ${ input . hostname } . Executor runs arbitrary commands — only expose on trusted networks.` ,
583+ ) ;
584+ if ( input . allowedHosts . length > 0 ) {
585+ console . log ( ` Extra allowed Host headers: ${ input . allowedHosts . join ( ", " ) } ` ) ;
586+ }
587+ if ( input . authPassword ) {
588+ console . log ( " Basic authentication is enabled." ) ;
589+ } else if ( input . authToken ) {
590+ console . log ( " Token authentication is enabled." ) ;
591+ }
575592 }
576- }
577- console . log ( `\nPress Ctrl+C to stop.` ) ;
593+ console . log ( `\nPress Ctrl+C to stop.` ) ;
578594
579- yield * waitForShutdownSignal ( ) ;
580- yield * Effect . promise ( ( ) => server . stop ( ) ) ;
595+ yield * waitForShutdownSignal ( ) ;
596+ yield * Effect . promise ( ( ) => server . stop ( ) ) ;
597+ } finally {
598+ restoreWebBaseUrl ( ) ;
599+ }
581600 } ) ;
582601
583602const runDaemonSession = ( input : {
@@ -589,67 +608,75 @@ const runDaemonSession = (input: {
589608} ) =>
590609 Effect . gen ( function * ( ) {
591610 const daemonHost = canonicalDaemonHost ( input . hostname ) ;
611+ const restoreWebBaseUrl = installDefaultExecutorWebBaseUrl (
612+ daemonBaseUrl ( daemonHost , input . port ) ,
613+ ) ;
592614 const scopeId = currentDaemonScopeId ( ) ;
593- const existing = yield * readDaemonPointer ( { hostname : daemonHost , scopeId } ) ;
594615
595- if ( existing ) {
596- const existingUrl = daemonBaseUrl ( existing . hostname , existing . port ) ;
597- if ( isPidAlive ( existing . pid ) && ( yield * isServerReachable ( existingUrl ) ) ) {
598- return yield * Effect . fail (
599- new Error (
600- [
601- `A daemon is already running for scope ${ scopeId } on ${ daemonHost } .` ,
602- `Existing daemon: ${ existingUrl } (pid ${ existing . pid } ).` ,
603- `Stop it first: ${ cliPrefix } daemon stop` ,
604- ] . join ( "\n" ) ,
605- ) ,
606- ) ;
616+ try {
617+ const existing = yield * readDaemonPointer ( { hostname : daemonHost , scopeId } ) ;
618+
619+ if ( existing ) {
620+ const existingUrl = daemonBaseUrl ( existing . hostname , existing . port ) ;
621+ if ( isPidAlive ( existing . pid ) && ( yield * isServerReachable ( existingUrl ) ) ) {
622+ return yield * Effect . fail (
623+ new Error (
624+ [
625+ `A daemon is already running for scope ${ scopeId } on ${ daemonHost } .` ,
626+ `Existing daemon: ${ existingUrl } (pid ${ existing . pid } ).` ,
627+ `Stop it first: ${ cliPrefix } daemon stop` ,
628+ ] . join ( "\n" ) ,
629+ ) ,
630+ ) ;
631+ }
632+ yield * cleanupPointer ( { hostname : existing . hostname , scopeId, port : existing . port } ) ;
607633 }
608- yield * cleanupPointer ( { hostname : existing . hostname , scopeId, port : existing . port } ) ;
609- }
610634
611- const server = yield * Effect . promise ( ( ) =>
612- startServer ( {
613- port : input . port ,
614- hostname : input . hostname ,
615- allowedHosts : input . allowedHosts ,
616- authToken : input . authToken ,
617- authPassword : input . authPassword ,
618- embeddedWebUI,
619- } ) ,
620- ) ;
635+ const server = yield * Effect . promise ( ( ) =>
636+ startServer ( {
637+ port : input . port ,
638+ hostname : input . hostname ,
639+ allowedHosts : input . allowedHosts ,
640+ authToken : input . authToken ,
641+ authPassword : input . authPassword ,
642+ embeddedWebUI,
643+ } ) ,
644+ ) ;
621645
622- const daemonPort = server . port ;
623- const token = randomUUID ( ) ;
646+ const daemonPort = server . port ;
647+ const token = randomUUID ( ) ;
624648
625- yield * writeDaemonRecord ( {
626- hostname : daemonHost ,
627- port : daemonPort ,
628- pid : process . pid ,
629- scopeDir : process . env . EXECUTOR_SCOPE_DIR ?? null ,
630- } ) ;
631- yield * writeDaemonPointer ( {
632- hostname : daemonHost ,
633- port : daemonPort ,
634- pid : process . pid ,
635- scopeId,
636- scopeDir : process . env . EXECUTOR_SCOPE_DIR ?? null ,
637- token,
638- } ) ;
649+ try {
650+ yield * writeDaemonRecord ( {
651+ hostname : daemonHost ,
652+ port : daemonPort ,
653+ pid : process . pid ,
654+ scopeDir : process . env . EXECUTOR_SCOPE_DIR ?? null ,
655+ } ) ;
656+ yield * writeDaemonPointer ( {
657+ hostname : daemonHost ,
658+ port : daemonPort ,
659+ pid : process . pid ,
660+ scopeId,
661+ scopeDir : process . env . EXECUTOR_SCOPE_DIR ?? null ,
662+ token,
663+ } ) ;
639664
640- console . log ( `Daemon ready on http://${ daemonHost } :${ daemonPort } ` ) ;
641- if ( input . authPassword ) {
642- console . log ( "Basic authentication is enabled." ) ;
643- } else if ( input . authToken ) {
644- console . log ( "Token authentication is enabled." ) ;
645- }
665+ console . log ( `Daemon ready on http://${ daemonHost } :${ daemonPort } ` ) ;
666+ if ( input . authPassword ) {
667+ console . log ( "Basic authentication is enabled." ) ;
668+ } else if ( input . authToken ) {
669+ console . log ( "Token authentication is enabled." ) ;
670+ }
646671
647- try {
648- yield * waitForShutdownSignal ( ) ;
672+ yield * waitForShutdownSignal ( ) ;
673+ } finally {
674+ yield * Effect . promise ( ( ) => server . stop ( ) ) ;
675+ yield * removeDaemonRecord ( { hostname : daemonHost , port : daemonPort } ) ;
676+ yield * removeDaemonPointer ( { hostname : daemonHost , scopeId } ) . pipe ( Effect . ignore ) ;
677+ }
649678 } finally {
650- yield * Effect . promise ( ( ) => server . stop ( ) ) ;
651- yield * removeDaemonRecord ( { hostname : daemonHost , port : daemonPort } ) ;
652- yield * removeDaemonPointer ( { hostname : daemonHost , scopeId } ) . pipe ( Effect . ignore ) ;
679+ restoreWebBaseUrl ( ) ;
653680 }
654681 } ) ;
655682
0 commit comments