UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

115 lines (114 loc) • 3.65 kB
import { u as toErrorObject } from "./error-coercion-CvJK4lmH.mjs"; import { n as isErrno } from "./errno-FR4CRyya.mjs"; import "./errors-O33D01XW.mjs"; import net from "node:net"; //#region src/infra/ports-probe.ts const PORT_PROBE_HOSTS = [ "127.0.0.1", "0.0.0.0", "::1", "::" ]; const LOOPBACK_PORT_PROBE_HOSTS = ["127.0.0.1"]; async function tryListenOnPort(params) { if (params.signal?.aborted) throw params.signal.reason; const listenOptions = { port: params.port }; if (params.host) listenOptions.host = params.host; if (typeof params.exclusive === "boolean") listenOptions.exclusive = params.exclusive; if (params.signal) listenOptions.signal = params.signal; return await new Promise((resolve, reject) => { const clearAbort = () => params.signal?.removeEventListener("abort", onAbort); const onAbort = () => { clearAbort(); reject(toErrorObject(params.signal?.reason, "Port probe aborted")); }; params.signal?.addEventListener("abort", onAbort, { once: true }); const tester = net.createServer().once("error", (error) => { clearAbort(); reject(error); }).once("listening", () => { const address = tester.address(); if (!address || typeof address === "string") { tester.close(() => { clearAbort(); reject(/* @__PURE__ */ new Error("expected TCP listener address")); }); return; } tester.close(() => { clearAbort(); resolve(params.port === 0 ? address.port : void 0); }); }).listen(listenOptions); }); } /** Observe a listener without retaining its socket or cancellation hook. */ async function probeTcpListener(port, host, signal) { signal?.throwIfAborted(); return await new Promise((resolve) => { const socket = net.connect({ host, port }); let result = "unknown"; const destroy = () => socket.destroy(); signal?.addEventListener("abort", destroy, { once: true }); socket.once("connect", () => { result = "busy"; destroy(); }); socket.once("error", (error) => { result = isErrno(error) && error.code === "ECONNREFUSED" ? "free" : "unknown"; destroy(); }); socket.setTimeout(250, destroy); socket.once("close", () => { signal?.removeEventListener("abort", destroy); resolve(result); }); }); } async function isIpv6LoopbackUnavailable(signal) { try { await tryListenOnPort({ port: 0, host: "::1", ...signal ? { signal } : {} }); return false; } catch (err) { return isErrno(err) && err.code === "EADDRNOTAVAIL"; } } async function probePortOnHost(port, host, signal) { try { await tryListenOnPort({ port, host, exclusive: true, ...signal ? { signal } : {} }); const confirmed = await probeTcpListener(port, host, signal); if (confirmed === "unknown" && host === "::" && await isIpv6LoopbackUnavailable(signal)) return "free"; return confirmed; } catch (err) { signal?.throwIfAborted(); if (isErrno(err) && err.code === "EADDRINUSE") return "busy"; if (isErrno(err) && (err.code === "EADDRNOTAVAIL" || err.code === "EAFNOSUPPORT")) return "skip"; return "unknown"; } } /** Checks selected local addresses without resolving listener diagnostics. */ async function probePortUsage(port, probeHosts = PORT_PROBE_HOSTS, signal) { signal?.throwIfAborted(); let sawUnknown = false; for (const host of probeHosts) { const result = await probePortOnHost(port, host, signal); signal?.throwIfAborted(); if (result === "busy") return "busy"; if (result === "unknown") sawUnknown = true; } return sawUnknown ? "unknown" : "free"; } //#endregion export { tryListenOnPort as i, probePortUsage as n, probeTcpListener as r, LOOPBACK_PORT_PROBE_HOSTS as t };