@@ -11,6 +11,7 @@ import { isTransientBrowserError } from './errors.js';
1111
1212const DAEMON_PORT = parseInt ( process . env . OPENCLI_DAEMON_PORT ?? String ( DEFAULT_DAEMON_PORT ) , 10 ) ;
1313const DAEMON_URL = `http://127.0.0.1:${ DAEMON_PORT } ` ;
14+ const OPENCLI_HEADERS = { 'X-OpenCLI' : '1' } ;
1415
1516let _idCounter = 0 ;
1617
@@ -44,42 +45,65 @@ export interface DaemonResult {
4445 error ?: string ;
4546}
4647
47- /**
48- * Check if daemon is running.
49- */
50- export async function isDaemonRunning ( ) : Promise < boolean > {
48+ export interface DaemonStatus {
49+ ok : boolean ;
50+ pid : number ;
51+ uptime : number ;
52+ extensionConnected : boolean ;
53+ extensionVersion ?: string ;
54+ pending : number ;
55+ lastCliRequestTime : number ;
56+ memoryMB : number ;
57+ port : number ;
58+ }
59+
60+ async function requestDaemon ( pathname : string , init ?: RequestInit & { timeout ?: number } ) : Promise < Response > {
61+ const { timeout = 2000 , headers, ...rest } = init ?? { } ;
62+ const controller = new AbortController ( ) ;
63+ const timer = setTimeout ( ( ) => controller . abort ( ) , timeout ) ;
5164 try {
52- const controller = new AbortController ( ) ;
53- const timer = setTimeout ( ( ) => controller . abort ( ) , 2000 ) ;
54- const res = await fetch ( `${ DAEMON_URL } /status` , {
55- headers : { 'X-OpenCLI' : '1' } ,
65+ return await fetch ( `${ DAEMON_URL } ${ pathname } ` , {
66+ ...rest ,
67+ headers : { ...OPENCLI_HEADERS , ...headers } ,
5668 signal : controller . signal ,
5769 } ) ;
70+ } finally {
5871 clearTimeout ( timer ) ;
72+ }
73+ }
74+
75+ export async function fetchDaemonStatus ( opts ?: { timeout ?: number } ) : Promise < DaemonStatus | null > {
76+ try {
77+ const res = await requestDaemon ( '/status' , { timeout : opts ?. timeout ?? 2000 } ) ;
78+ if ( ! res . ok ) return null ;
79+ return await res . json ( ) as DaemonStatus ;
80+ } catch {
81+ return null ;
82+ }
83+ }
84+
85+ export async function requestDaemonShutdown ( opts ?: { timeout ?: number } ) : Promise < boolean > {
86+ try {
87+ const res = await requestDaemon ( '/shutdown' , { method : 'POST' , timeout : opts ?. timeout ?? 5000 } ) ;
5988 return res . ok ;
6089 } catch {
6190 return false ;
6291 }
6392}
6493
94+ /**
95+ * Check if daemon is running.
96+ */
97+ export async function isDaemonRunning ( ) : Promise < boolean > {
98+ return ( await fetchDaemonStatus ( ) ) !== null ;
99+ }
100+
65101/**
66102 * Check if daemon is running AND the extension is connected.
67103 */
68104export async function isExtensionConnected ( ) : Promise < boolean > {
69- try {
70- const controller = new AbortController ( ) ;
71- const timer = setTimeout ( ( ) => controller . abort ( ) , 2000 ) ;
72- const res = await fetch ( `${ DAEMON_URL } /status` , {
73- headers : { 'X-OpenCLI' : '1' } ,
74- signal : controller . signal ,
75- } ) ;
76- clearTimeout ( timer ) ;
77- if ( ! res . ok ) return false ;
78- const data = await res . json ( ) as { extensionConnected ?: boolean } ;
79- return ! ! data . extensionConnected ;
80- } catch {
81- return false ;
82- }
105+ const status = await fetchDaemonStatus ( ) ;
106+ return ! ! status ?. extensionConnected ;
83107}
84108
85109/**
@@ -98,16 +122,12 @@ export async function sendCommand(
98122 const id = generateId ( ) ;
99123 const command : DaemonCommand = { id, action, ...params } ;
100124 try {
101- const controller = new AbortController ( ) ;
102- const timer = setTimeout ( ( ) => controller . abort ( ) , 30000 ) ;
103-
104- const res = await fetch ( `${ DAEMON_URL } /command` , {
125+ const res = await requestDaemon ( '/command' , {
105126 method : 'POST' ,
106- headers : { 'Content-Type' : 'application/json' , 'X-OpenCLI' : '1' } ,
127+ headers : { 'Content-Type' : 'application/json' } ,
107128 body : JSON . stringify ( command ) ,
108- signal : controller . signal ,
129+ timeout : 30000 ,
109130 } ) ;
110- clearTimeout ( timer ) ;
111131
112132 const result = ( await res . json ( ) ) as DaemonResult ;
113133
0 commit comments