Files
clawdbot/src/infra/ports-format.ts
2026-01-20 07:43:00 +00:00

64 lines
2.3 KiB
TypeScript

import { formatCliCommand } from "../cli/command-format.js";
import type { PortListener, PortListenerKind, PortUsage } from "./ports-types.js";
export function classifyPortListener(listener: PortListener, port: number): PortListenerKind {
const raw = `${listener.commandLine ?? ""} ${listener.command ?? ""}`.trim().toLowerCase();
if (raw.includes("clawdbot")) return "gateway";
if (raw.includes("ssh")) {
const portToken = String(port);
const tunnelPattern = new RegExp(
`-(l|r)\\s*${portToken}\\b|-(l|r)${portToken}\\b|:${portToken}\\b`,
);
if (!raw || tunnelPattern.test(raw)) return "ssh";
return "ssh";
}
return "unknown";
}
export function buildPortHints(listeners: PortListener[], port: number): string[] {
if (listeners.length === 0) return [];
const kinds = new Set(listeners.map((listener) => classifyPortListener(listener, port)));
const hints: string[] = [];
if (kinds.has("gateway")) {
hints.push(
`Gateway already running locally. Stop it (${formatCliCommand("clawdbot daemon stop")}) or use a different port.`,
);
}
if (kinds.has("ssh")) {
hints.push(
"SSH tunnel already bound to this port. Close the tunnel or use a different local port in -L.",
);
}
if (kinds.has("unknown")) {
hints.push("Another process is listening on this port.");
}
if (listeners.length > 1) {
hints.push(
"Multiple listeners detected; ensure only one gateway/tunnel per port unless intentionally running isolated profiles.",
);
}
return hints;
}
export function formatPortListener(listener: PortListener): string {
const pid = listener.pid ? `pid ${listener.pid}` : "pid ?";
const user = listener.user ? ` ${listener.user}` : "";
const command = listener.commandLine || listener.command || "unknown";
const address = listener.address ? ` (${listener.address})` : "";
return `${pid}${user}: ${command}${address}`;
}
export function formatPortDiagnostics(diagnostics: PortUsage): string[] {
if (diagnostics.status !== "busy") {
return [`Port ${diagnostics.port} is free.`];
}
const lines = [`Port ${diagnostics.port} is already in use.`];
for (const listener of diagnostics.listeners) {
lines.push(`- ${formatPortListener(listener)}`);
}
for (const hint of diagnostics.hints) {
lines.push(`- ${hint}`);
}
return lines;
}