1- import { mkdir , readFile , rm , writeFile } from "node:fs/promises" ;
21import { homedir } from "node:os" ;
3- import { join } from "node:path" ;
2+ import { FileSystem , Path } from "@effect/platform" ;
3+ import type { PlatformError } from "@effect/platform/Error" ;
4+ import * as Effect from "effect/Effect" ;
45
56// ---------------------------------------------------------------------------
67// Types
@@ -30,40 +31,47 @@ export const canonicalDaemonHost = (hostname: string): string => {
3031// Paths
3132// ---------------------------------------------------------------------------
3233
33- const resolveDaemonDataDir = ( ) : string => process . env . EXECUTOR_DATA_DIR ?? join ( homedir ( ) , ".executor" ) ;
34+ const resolveDaemonDataDir = ( path : Path . Path ) : string =>
35+ process . env . EXECUTOR_DATA_DIR ?? path . join ( homedir ( ) , ".executor" ) ;
3436
3537const sanitizeHostForPath = ( hostname : string ) : string => hostname . replaceAll ( / [ ^ a - z 0 - 9 . - ] + / gi, "_" ) ;
3638
37- const daemonRecordPath = ( input : { hostname : string ; port : number } ) : string => {
39+ const daemonRecordPath = ( path : Path . Path , input : { hostname : string ; port : number } ) : string => {
3840 const host = sanitizeHostForPath ( canonicalDaemonHost ( input . hostname ) ) ;
39- return join ( resolveDaemonDataDir ( ) , `daemon-${ host } -${ input . port } .json` ) ;
41+ return path . join ( resolveDaemonDataDir ( path ) , `daemon-${ host } -${ input . port } .json` ) ;
4042} ;
4143
4244// ---------------------------------------------------------------------------
4345// Persistence
4446// ---------------------------------------------------------------------------
4547
46- export const writeDaemonRecord = async ( input : {
48+ export const writeDaemonRecord = ( input : {
4749 hostname : string ;
4850 port : number ;
4951 pid : number ;
5052 scopeDir : string | null ;
51- } ) : Promise < void > => {
52- const path = daemonRecordPath ( { hostname : input . hostname , port : input . port } ) ;
53- const dir = resolveDaemonDataDir ( ) ;
54- await mkdir ( dir , { recursive : true } ) ;
55-
56- const payload : DaemonRecord = {
57- version : 1 ,
58- hostname : canonicalDaemonHost ( input . hostname ) ,
59- port : input . port ,
60- pid : input . pid ,
61- startedAt : new Date ( ) . toISOString ( ) ,
62- scopeDir : input . scopeDir ,
63- } ;
64-
65- await writeFile ( path , `${ JSON . stringify ( payload , null , 2 ) } \n` , "utf8" ) ;
66- } ;
53+ } ) : Effect . Effect < void , PlatformError , FileSystem . FileSystem | Path . Path > =>
54+ Effect . gen ( function * ( ) {
55+ const fs = yield * FileSystem . FileSystem ;
56+ const path = yield * Path . Path ;
57+ const dataDir = resolveDaemonDataDir ( path ) ;
58+
59+ yield * fs . makeDirectory ( dataDir , { recursive : true } ) ;
60+
61+ const payload : DaemonRecord = {
62+ version : 1 ,
63+ hostname : canonicalDaemonHost ( input . hostname ) ,
64+ port : input . port ,
65+ pid : input . pid ,
66+ startedAt : new Date ( ) . toISOString ( ) ,
67+ scopeDir : input . scopeDir ,
68+ } ;
69+
70+ yield * fs . writeFileString (
71+ daemonRecordPath ( path , { hostname : input . hostname , port : input . port } ) ,
72+ `${ JSON . stringify ( payload , null , 2 ) } \n` ,
73+ ) ;
74+ } ) ;
6775
6876const parseRecord = ( raw : string ) : DaemonRecord | null => {
6977 let parsed : unknown ;
@@ -103,23 +111,29 @@ const parseRecord = (raw: string): DaemonRecord | null => {
103111 } ;
104112} ;
105113
106- export const readDaemonRecord = async ( input : {
114+ export const readDaemonRecord = ( input : {
107115 hostname : string ;
108116 port : number ;
109- } ) : Promise < DaemonRecord | null > => {
110- const path = daemonRecordPath ( { hostname : input . hostname , port : input . port } ) ;
111- try {
112- const raw = await readFile ( path , "utf8" ) ;
117+ } ) : Effect . Effect < DaemonRecord | null , never , FileSystem . FileSystem | Path . Path > =>
118+ Effect . gen ( function * ( ) {
119+ const fs = yield * FileSystem . FileSystem ;
120+ const path = yield * Path . Path ;
121+ const raw = yield * fs . readFileString ( daemonRecordPath ( path , input ) ) . pipe (
122+ Effect . catchAll ( ( ) => Effect . succeed ( null ) ) ,
123+ ) ;
124+ if ( raw === null ) return null ;
113125 return parseRecord ( raw ) ;
114- } catch {
115- return null ;
116- }
117- } ;
126+ } ) ;
118127
119- export const removeDaemonRecord = async ( input : { hostname : string ; port : number } ) : Promise < void > => {
120- const path = daemonRecordPath ( { hostname : input . hostname , port : input . port } ) ;
121- await rm ( path , { force : true } ) ;
122- } ;
128+ export const removeDaemonRecord = ( input : {
129+ hostname : string ;
130+ port : number ;
131+ } ) : Effect . Effect < void , PlatformError , FileSystem . FileSystem | Path . Path > =>
132+ Effect . gen ( function * ( ) {
133+ const fs = yield * FileSystem . FileSystem ;
134+ const path = yield * Path . Path ;
135+ yield * fs . remove ( daemonRecordPath ( path , input ) , { force : true } ) ;
136+ } ) ;
123137
124138// ---------------------------------------------------------------------------
125139// Process helpers
@@ -135,6 +149,11 @@ export const isPidAlive = (pid: number): boolean => {
135149 }
136150} ;
137151
138- export const terminatePid = ( pid : number ) : void => {
139- process . kill ( pid , "SIGTERM" ) ;
140- } ;
152+ export const terminatePid = ( pid : number ) : Effect . Effect < void , Error > =>
153+ Effect . try ( {
154+ try : ( ) => {
155+ process . kill ( pid , "SIGTERM" ) ;
156+ } ,
157+ catch : ( cause ) =>
158+ cause instanceof Error ? cause : new Error ( `Failed to terminate pid ${ pid } : ${ String ( cause ) } ` ) ,
159+ } ) ;
0 commit comments