@@ -10,7 +10,7 @@ import {
1010 type DaemonStatus ,
1111} from "@plannotator/shared/daemon-protocol" ;
1212import { getServerPort , isRemoteSession } from "../remote" ;
13- import { readDaemonState , removeDaemonState , type DaemonState , type DaemonStateOptions } from "./state" ;
13+ import { readDaemonState , removeDaemonFiles , type DaemonState , type DaemonStateOptions } from "./state" ;
1414
1515export interface DaemonClientOptions extends DaemonStateOptions {
1616 fetch ?: typeof fetch ;
@@ -31,43 +31,42 @@ export class DaemonClient {
3131 }
3232
3333 async capabilities ( ) : Promise < unknown > {
34- return this . getJson ( "/daemon/capabilities" , false ) ;
34+ return this . getJson ( "/daemon/capabilities" ) ;
3535 }
3636
3737 async status ( ) : Promise < DaemonStatus > {
38- return this . getJson ( "/daemon/status" , true ) as Promise < DaemonStatus > ;
38+ return this . getJson ( "/daemon/status" ) as Promise < DaemonStatus > ;
3939 }
4040
4141 async listSessions ( ) : Promise < unknown > {
42- return this . getJson ( "/daemon/sessions" , true ) ;
42+ return this . getJson ( "/daemon/sessions" ) ;
4343 }
4444
4545 async createSession ( request : DaemonCreateSessionRequest ) : Promise < DaemonCreateSessionResponse | DaemonErrorResponse > {
4646 return this . requestJson ( "/daemon/sessions" , {
4747 method : "POST" ,
4848 body : JSON . stringify ( request ) ,
49- } , true ) as Promise < DaemonCreateSessionResponse | DaemonErrorResponse > ;
49+ } ) as Promise < DaemonCreateSessionResponse | DaemonErrorResponse > ;
5050 }
5151
5252 async waitForResult < T = unknown > ( id : string ) : Promise < DaemonSessionResultResponse < T > | DaemonErrorResponse > {
53- return this . getJson ( `/daemon/sessions/${ encodeURIComponent ( id ) } /result` , true ) as Promise < DaemonSessionResultResponse < T > | DaemonErrorResponse > ;
53+ return this . getJson ( `/daemon/sessions/${ encodeURIComponent ( id ) } /result` ) as Promise < DaemonSessionResultResponse < T > | DaemonErrorResponse > ;
5454 }
5555
5656 async cancelSession ( id : string ) : Promise < DaemonCancelSessionResponse | DaemonErrorResponse > {
57- return this . requestJson ( `/daemon/sessions/${ encodeURIComponent ( id ) } /cancel` , { method : "POST" } , true ) as Promise < DaemonCancelSessionResponse | DaemonErrorResponse > ;
57+ return this . requestJson ( `/daemon/sessions/${ encodeURIComponent ( id ) } /cancel` , { method : "POST" } ) as Promise < DaemonCancelSessionResponse | DaemonErrorResponse > ;
5858 }
5959
6060 async shutdown ( ) : Promise < DaemonShutdownResponse | DaemonErrorResponse > {
61- return this . requestJson ( "/daemon/shutdown" , { method : "POST" } , true ) as Promise < DaemonShutdownResponse | DaemonErrorResponse > ;
61+ return this . requestJson ( "/daemon/shutdown" , { method : "POST" } ) as Promise < DaemonShutdownResponse | DaemonErrorResponse > ;
6262 }
6363
64- private async getJson ( path : string , auth : boolean ) : Promise < unknown > {
65- return this . requestJson ( path , { method : "GET" } , auth ) ;
64+ private async getJson ( path : string ) : Promise < unknown > {
65+ return this . requestJson ( path , { method : "GET" } ) ;
6666 }
6767
68- private async requestJson ( path : string , init : RequestInit , auth : boolean ) : Promise < unknown > {
68+ private async requestJson ( path : string , init : RequestInit ) : Promise < unknown > {
6969 const headers = new Headers ( init . headers ) ;
70- if ( auth ) headers . set ( "authorization" , `Bearer ${ this . state . token } ` ) ;
7170 if ( init . body && ! headers . has ( "content-type" ) ) headers . set ( "content-type" , "application/json" ) ;
7271
7372 const res = await this . fetchImpl ( `${ this . state . baseUrl } ${ path } ` , {
@@ -82,17 +81,31 @@ export class DaemonClient {
8281 }
8382}
8483
84+ function stateBaseUrl ( state : unknown ) : string | undefined {
85+ const baseUrl = ( state as { baseUrl ?: unknown } | null ) ?. baseUrl ;
86+ return typeof baseUrl === "string" ? baseUrl : undefined ;
87+ }
88+
89+ export async function cleanupDaemonState ( state : unknown , options : DaemonClientOptions = { } ) : Promise < void > {
90+ const fetchImpl = options . fetch ?? fetch ;
91+ const baseUrl = stateBaseUrl ( state ) ;
92+ if ( baseUrl ) {
93+ await fetchImpl ( `${ baseUrl } /daemon/shutdown` , { method : "POST" } ) . catch ( ( ) => undefined ) ;
94+ }
95+ removeDaemonFiles ( options ) ;
96+ }
97+
8598export async function discoverDaemon ( options : DaemonClientOptions = { } ) : Promise < DaemonDiscoveryResult > {
8699 const stateResult = readDaemonState ( options ) ;
87100 if ( stateResult . kind === "missing" ) {
88101 return { ok : false , code : "missing" , message : "No Plannotator daemon state found." } ;
89102 }
90103 if ( stateResult . kind === "malformed" ) {
91- removeDaemonState ( options ) ;
104+ removeDaemonFiles ( options ) ;
92105 return { ok : false , code : "malformed" , message : stateResult . error } ;
93106 }
94107 if ( stateResult . kind === "stale" ) {
95- removeDaemonState ( options ) ;
108+ removeDaemonFiles ( options ) ;
96109 return { ok : false , code : "stale" , message : `Stale Plannotator daemon state for PID ${ stateResult . state . pid } .` , state : stateResult . state } ;
97110 }
98111 if ( stateResult . kind === "incompatible" ) {
0 commit comments