11#!/usr/bin/env bun
22
3+ import { existsSync } from "node:fs" ;
4+ import { readFile } from "node:fs/promises" ;
5+ import { homedir } from "node:os" ;
6+ import { join } from "node:path" ;
37import { parseArgs } from "node:util" ;
4- import { findAvailablePort } from "@r_masseater /ops-harbor-core" ;
8+ import { findAvailablePort } from "@repo /ops-harbor-core" ;
59import { createServedApp } from "./server" ;
610
11+ type ChildProcessHandle = {
12+ kill ( signal ?: number ) : void ;
13+ exited : Promise < number > ;
14+ } ;
15+
16+ const CONTROL_PLANE_CONFIG_PATH = join ( homedir ( ) , ".config" , "ops-harbor" , "control-plane.json" ) ;
17+
18+ async function readConfiguredControlPlanePort ( ) : Promise < number > {
19+ if ( ! existsSync ( CONTROL_PLANE_CONFIG_PATH ) ) return 4130 ;
20+ try {
21+ const raw = await readFile ( CONTROL_PLANE_CONFIG_PATH , "utf-8" ) ;
22+ const parsed = JSON . parse ( raw ) as { port ?: unknown } ;
23+ if ( typeof parsed . port === "number" && Number . isInteger ( parsed . port ) ) return parsed . port ;
24+ } catch {
25+ // Fall through to the default.
26+ }
27+ return 4130 ;
28+ }
29+
30+ async function isControlPlaneReachable ( port : number ) : Promise < boolean > {
31+ const controller = new AbortController ( ) ;
32+ const timeout = setTimeout ( ( ) => controller . abort ( ) , 700 ) ;
33+ try {
34+ const response = await fetch ( `http://127.0.0.1:${ port } /api/health` , {
35+ signal : controller . signal ,
36+ } ) ;
37+ return response . ok ;
38+ } catch {
39+ return false ;
40+ } finally {
41+ clearTimeout ( timeout ) ;
42+ }
43+ }
44+
745const subcommand = Bun . argv [ 2 ] ;
846
947if ( subcommand === "mcp" ) {
@@ -42,6 +80,24 @@ Options:
4280 process . exit ( 0 ) ;
4381 }
4482
83+ let managedControlPlane : ChildProcessHandle | null = null ;
84+ const controlPlanePort = await readConfiguredControlPlanePort ( ) ;
85+ if ( ! ( await isControlPlaneReachable ( controlPlanePort ) ) ) {
86+ const controlPlanePath = join ( import . meta. dir , "control-plane.js" ) ;
87+ if ( existsSync ( controlPlanePath ) ) {
88+ const child = Bun . spawn ( {
89+ cmd : [ "bun" , controlPlanePath ] ,
90+ stdout : "inherit" ,
91+ stderr : "inherit" ,
92+ } ) ;
93+ managedControlPlane = child ;
94+ child . exited . then ( ( exitCode ) => {
95+ if ( exitCode !== 0 ) console . error ( `control-plane exited with code ${ exitCode } ` ) ;
96+ if ( managedControlPlane === child ) managedControlPlane = null ;
97+ } ) ;
98+ }
99+ }
100+
45101 const port = values . port ? Number ( values . port ) : await findAvailablePort ( 4131 ) ;
46102 if ( ! Number . isInteger ( port ) || port < 1 || port > 65_535 ) {
47103 console . error ( `Invalid port: ${ values . port ?? String ( port ) } (expected 1-65535)` ) ;
@@ -62,4 +118,14 @@ Options:
62118 hostname : "127.0.0.1" ,
63119 fetch : app . fetch ,
64120 } ) ;
121+
122+ const shutdown = ( ) => {
123+ if ( managedControlPlane ) {
124+ managedControlPlane . kill ( 15 ) ;
125+ managedControlPlane = null ;
126+ }
127+ process . exit ( 0 ) ;
128+ } ;
129+ process . on ( "SIGINT" , shutdown ) ;
130+ process . on ( "SIGTERM" , shutdown ) ;
65131}
0 commit comments