import type { Server } from 'node:http'; /** * Default upward-probe span on EADDRINUSE when a caller doesn't pass `maxProbe`. * The dashboard binds wildcard at config.dashboard.port and can walk this many * ports up, so config.dashboard.ipcBasePort is kept clear of * [port, port + DEFAULT_PROBE_SPAN] to stop the dashboard from ever landing on a * loopback-shadowed IPC port (see config.ts + test/dashboard-ipc-port-range.test.ts). */ export declare const DEFAULT_PROBE_SPAN = 20; export interface ListenWithProbeOpts { server: Server; /** Preferred port to try first. */ port: number; host: string; /** Max upward probes on EADDRINUSE before rejecting (default DEFAULT_PROBE_SPAN). */ maxProbe?: number; /** Optional caller-specific availability gate before attempting a bind. */ portAvailable?: (port: number) => boolean | Promise; /** * Optional post-bind verification, run AFTER a successful listen with the * actually-bound port. Return false to REJECT the port: the server is closed * and the probe steps to port+1. This exists to catch a wildcard (0.0.0.0) * bind that succeeds at the OS level yet is shadowed on loopback — on macOS * another process holding 127.0.0.1:port coexists with the wildcard bind and * wins loopback routing, so clients dialing 127.0.0.1:port reach the shadow, * not us. A loopback self-check (does 127.0.0.1:port answer as ME?) detects * that and re-probes, independent of which port number collided. */ verifyBound?: (port: number) => boolean | Promise; log?: (msg: string) => void; } /** * Bind `server` to `port`, walking port+1, port+2 … up to `maxProbe` times when * the port is already in use, and resolve with the actually-bound port. * * Why this exists: several daemon/dashboard listeners (dashboard-ipc-server.ts, * dashboard.ts) historically did a single `server.listen(fixedPort)` with no * 'error' listener / no probe, so on a shared machine a second botmux instance * binding the same default port emitted an UNHANDLED 'error' that crashed the * whole process (the IPC bind even took the daemon down at startup). This * mirrors the already-proven probe in core/terminal-proxy.ts so those binds * self-heal to a free port; callers MUST advertise the returned (bound) port to * their consumers (the IPC port via the daemon descriptor, the dashboard port * via ~/.botmux/.dashboard-port) since it may differ from the requested one. */ export declare function listenWithProbe(opts: ListenWithProbeOpts): Promise; //# sourceMappingURL=listen-with-probe.d.ts.map