refactor: split port diagnostics helpers
This commit is contained in:
74
src/infra/ports-format.ts
Normal file
74
src/infra/ports-format.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
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") || raw.includes("clawdis")) 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 (clawdbot gateway 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.");
|
||||
}
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user