@@ -3,9 +3,9 @@ import { execFileSync, spawn } from "node:child_process";
33import { rmSync } from "node:fs" ;
44import { restoreNativeCodex } from "./codex-inject" ;
55import { restoreLegacyOpenaiHistory } from "./codex-history-provider" ;
6- import { codexAutoStartEnabled , getConfigDir , loadConfig , readPid , removePid , saveConfig , writePid } from "./config" ;
6+ import { codexAutoStartEnabled , getConfigDir , getConfigPath , loadConfig , readPid , removePid , saveConfig , writePid } from "./config" ;
77import { findAvailablePort } from "./ports" ;
8- import { serviceCommand , stopServiceIfInstalled , uninstallServiceIfInstalled } from "./service" ;
8+ import { serviceCommand , serviceStatusSummary , stopServiceIfInstalled , uninstallServiceIfInstalled } from "./service" ;
99import { drainAndShutdown , startServer } from "./server" ;
1010import { maybeShowStarPrompt } from "./star-prompt" ;
1111
@@ -365,13 +365,56 @@ async function handleUninstall() {
365365 console . log ( "\n✅ opencodex local state removed. Remove the package with: npm uninstall -g @bitkyc08/opencodex" ) ;
366366}
367367
368- function handleStatus ( ) {
368+ type HealthCheck = {
369+ ok : boolean ;
370+ label : string ;
371+ } ;
372+
373+ async function checkProxyHealth ( port : number , hostname ?: string ) : Promise < HealthCheck > {
374+ const url = `http://${ healthHost ( hostname ) } :${ port } /healthz` ;
375+ const controller = new AbortController ( ) ;
376+ const timer = setTimeout ( ( ) => controller . abort ( ) , 800 ) ;
377+ try {
378+ const response = await fetch ( url , { signal : controller . signal } ) ;
379+ if ( ! response . ok ) return { ok : false , label : `${ url } returned HTTP ${ response . status } ` } ;
380+ const body = await response . json ( ) . catch ( ( ) => null ) as { version ?: unknown ; uptime ?: unknown } | null ;
381+ const version = typeof body ?. version === "string" ? ` v${ body . version } ` : "" ;
382+ const uptime = typeof body ?. uptime === "number" ? `, uptime ${ Math . round ( body . uptime ) } s` : "" ;
383+ return { ok : true , label : `${ url } ok${ version } ${ uptime } ` } ;
384+ } catch ( error ) {
385+ const reason = error instanceof Error && error . name === "AbortError" ? "timed out" : "unreachable" ;
386+ return { ok : false , label : `${ url } ${ reason } ` } ;
387+ } finally {
388+ clearTimeout ( timer ) ;
389+ }
390+ }
391+
392+ async function handleStatus ( ) {
393+ const config = loadConfig ( ) ;
394+ const port = config . port ?? 10100 ;
369395 const pid = readPid ( ) ;
370- if ( pid ) {
371- console . log ( `✅ Proxy running (PID ${ pid } )` ) ;
396+ const health = await checkProxyHealth ( port , config . hostname ) ;
397+ const proxyLabel = pid && health . ok
398+ ? `running (PID ${ pid } )`
399+ : pid
400+ ? `PID file points to PID ${ pid } , but health check failed`
401+ : health . ok
402+ ? "reachable, but PID file is missing or stale"
403+ : "not running" ;
404+
405+ if ( pid || health . ok ) {
406+ console . log ( `✅ Proxy: ${ proxyLabel } ` ) ;
372407 } else {
373- console . log ( " ❌ Proxy not running" ) ;
408+ console . log ( ` ❌ Proxy: ${ proxyLabel } ` ) ;
374409 }
410+ console . log ( ` Health: ${ health . label } ` ) ;
411+ console . log ( ` Dashboard: http://localhost:${ port } /` ) ;
412+ console . log ( ` Config: ${ getConfigPath ( ) } ` ) ;
413+ console . log ( ` Default provider: ${ config . defaultProvider } ` ) ;
414+ console . log ( ` Codex autostart: ${ codexAutoStartEnabled ( config ) ? "enabled" : "disabled" } ` ) ;
415+ console . log ( ` Service: ${ serviceStatusSummary ( ) } ` ) ;
416+ const { codexShimStatus } = await import ( "./codex-shim" ) ;
417+ console . log ( ` ${ codexShimStatus ( ) } ` ) ;
375418}
376419
377420function handleRecoverHistory ( ) {
@@ -411,7 +454,7 @@ switch (command) {
411454 await handleUninstall ( ) ;
412455 break ;
413456 case "status" :
414- handleStatus ( ) ;
457+ await handleStatus ( ) ;
415458 break ;
416459 case "ensure" :
417460 await handleEnsure ( ) ;
0 commit comments