1717 */
1818
1919import * as http from "node:http" ;
20- import * as net from "node:net" ;
2120import { WebSocketServer , type WebSocket } from "ws" ;
2221import { ChatController } from "../controller.js" ;
2322import { FRONTEND_HTML } from "./index.html.js" ;
2423
25- const DEFAULT_WEB_PORT = 3000 ;
2624const WEB_HOST = "127.0.0.1" ;
2725
26+ // ---------------------------------------------------------------------------
27+ // Types
28+ // ---------------------------------------------------------------------------
29+
30+ export interface WebServerHandle {
31+ server : http . Server ;
32+ controller : ChatController ;
33+ wss : WebSocketServer ;
34+ }
35+
2836// ---------------------------------------------------------------------------
2937// Auto-start — called by every bridge after MeshStore.init()
3038// ---------------------------------------------------------------------------
3139
3240/**
33- * Try to start the web UI server on the well-known port.
34- * Returns the server if this bridge won the race, or undefined if another
35- * bridge is already serving. The server runs independently with its own
36- * MeshStore that syncs state from the mesh.
41+ * Start the web UI server on an OS-assigned port.
42+ * Returns the server handle, or undefined if port discovery fails.
3743 */
38- export async function tryStartWebServer (
39- port = DEFAULT_WEB_PORT ,
40- ) : Promise < http . Server | undefined > {
41- const alive = await isPortAlive ( port , WEB_HOST ) ;
42- if ( alive ) return undefined ;
43-
44- const server = createWebServer ( port ) ;
45- return server ;
44+ export function tryStartWebServer ( ) : WebServerHandle | undefined {
45+ return createWebServer ( ) ;
4646}
4747
4848/**
49- * Create and start the web server. Used by tryStartWebServer (auto-start)
50- * and runWeb (standalone `npx agent-comms chat` mode).
49+ * Create and start the web server on a dynamic port.
50+ * Used by tryStartWebServer (auto-start) and runWeb (standalone mode).
5151 */
52- export function createWebServer ( port = DEFAULT_WEB_PORT ) : http . Server {
52+ export function createWebServer ( port = 0 ) : WebServerHandle {
5353 const controller = new ChatController ( "Dashboard" ) ;
5454 void controller . init ( ) ;
5555
@@ -63,57 +63,35 @@ export function createWebServer(port = DEFAULT_WEB_PORT): http.Server {
6363 } ) ;
6464
6565 server . listen ( port , WEB_HOST , ( ) => {
66- console . log ( `Agent Comms web UI: http://${ WEB_HOST } :${ String ( port ) } ` ) ;
66+ const addr = server . address ( ) ;
67+ const actualPort = typeof addr === "object" && addr ? addr . port : port ;
68+ console . log ( `Agent Comms web UI: http://${ WEB_HOST } :${ String ( actualPort ) } ` ) ;
6769 } ) ;
6870
69- return server ;
70- }
71-
72- function isPortAlive ( port : number , host : string ) : Promise < boolean > {
73- return new Promise ( ( resolve ) => {
74- const socket = new net . Socket ( ) ;
75- socket . connect ( port , host , ( ) => {
76- socket . destroy ( ) ;
77- resolve ( true ) ;
78- } ) ;
79- socket . on ( "error" , ( ) => {
80- socket . destroy ( ) ;
81- resolve ( false ) ;
82- } ) ;
83- } ) ;
71+ return { server, controller, wss } ;
8472}
8573
8674// ---------------------------------------------------------------------------
8775// Standalone mode — `npx agent-comms chat`
8876// ---------------------------------------------------------------------------
8977
90- export async function runWeb (
91- userName : string ,
92- port = DEFAULT_WEB_PORT ,
93- ) : Promise < void > {
94- const controller = new ChatController ( userName ) ;
95- await controller . init ( ) ;
96-
97- const server = http . createServer ( ( req , res ) => {
98- handleRequest ( req , res , controller ) ;
99- } ) ;
100-
101- const wss = new WebSocketServer ( { server } ) ;
102-
103- wss . on ( "connection" , ( ws ) => {
104- handleWebSocket ( ws , controller ) ;
105- } ) ;
78+ export function runWeb ( userName : string , port = 0 ) : void {
79+ const handle = createWebServer ( port ) ;
10680
107- server . listen ( port , ( ) => {
108- console . log ( `Agent Comms web UI: http://localhost:${ String ( port ) } ` ) ;
109- console . log ( `Connected as ${ userName } (user) [${ controller . agentId } ]` ) ;
81+ handle . server . on ( "listening" , ( ) => {
82+ const addr = handle . server . address ( ) ;
83+ const actualPort = typeof addr === "object" && addr ? addr . port : port ;
84+ console . log ( `Agent Comms web UI: http://localhost:${ String ( actualPort ) } ` ) ;
85+ console . log (
86+ `Connected as ${ userName } (user) [${ handle . controller . agentId } ]` ,
87+ ) ;
11088 } ) ;
11189
11290 // Graceful shutdown
11391 const cleanup = async ( ) : Promise < void > => {
114- wss . close ( ) ;
115- server . close ( ) ;
116- await controller . shutdown ( ) ;
92+ handle . wss . close ( ) ;
93+ handle . server . close ( ) ;
94+ await handle . controller . shutdown ( ) ;
11795 process . exit ( 0 ) ;
11896 } ;
11997
0 commit comments