import { disableTailscaleFunnel, disableTailscaleServe, enableTailscaleFunnel, enableTailscaleServe, getTailnetHostname, } from "../infra/tailscale.js"; export async function startGatewayTailscaleExposure(params: { tailscaleMode: "off" | "serve" | "funnel"; resetOnExit?: boolean; socket?: string; port: number; controlUiBasePath?: string; logTailscale: { info: (msg: string) => void; warn: (msg: string) => void }; }): Promise<(() => Promise) | null> { if (params.tailscaleMode === "off") { return null; } const socketOpts = params.socket ? { socket: params.socket } : undefined; try { if (params.tailscaleMode === "serve") { await enableTailscaleServe(params.port, undefined, socketOpts); } else { await enableTailscaleFunnel(params.port, undefined, socketOpts); } const host = await getTailnetHostname(undefined, undefined, socketOpts).catch(() => null); if (host) { const uiPath = params.controlUiBasePath ? `${params.controlUiBasePath}/` : "/"; params.logTailscale.info( `${params.tailscaleMode} enabled: https://${host}${uiPath} (WS via wss://${host})`, ); } else { params.logTailscale.info(`${params.tailscaleMode} enabled`); } } catch (err) { params.logTailscale.warn( `${params.tailscaleMode} failed: ${err instanceof Error ? err.message : String(err)}`, ); } if (!params.resetOnExit) { return null; } return async () => { try { if (params.tailscaleMode === "serve") { await disableTailscaleServe(undefined, socketOpts); } else { await disableTailscaleFunnel(undefined, socketOpts); } } catch (err) { params.logTailscale.warn( `${params.tailscaleMode} cleanup failed: ${err instanceof Error ? err.message : String(err)}`, ); } }; }