mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 13:26:04 -06:00
fix(ports): prevent duplicate macOS listener probes during status checks (#115813)
* fix(ports): batch macOS lsof listener inspection to one spawn per cycle (cherry picked from commitedd01c72ae) * fix(ports): keep single-port lsof checks targeted (cherry picked from commit18ece86a57) * test(daemon): mock batched port usage in coverage (cherry picked from commit917ef0d416) * test(daemon): model batched listener status * refactor(ports): isolate lsof listener parsing --------- Co-authored-by: 狼哥 <hanwanlonga@gmail.com>
This commit is contained in:
@@ -55,6 +55,22 @@ const inspectPortUsage = vi.fn<(port: number) => Promise<PortUsageTestSummary>>(
|
||||
hints: [],
|
||||
}),
|
||||
);
|
||||
const inspectPortUsages = vi.fn<
|
||||
(ports: readonly number[]) => Promise<Map<number, PortUsageTestSummary>>
|
||||
>(
|
||||
async (ports) =>
|
||||
new Map(
|
||||
ports.map((port) => [
|
||||
port,
|
||||
{
|
||||
port,
|
||||
status: "free",
|
||||
listeners: [],
|
||||
hints: [],
|
||||
},
|
||||
]),
|
||||
),
|
||||
);
|
||||
const inspectPortConnections = vi.fn<(port: number) => Promise<PortConnections>>(
|
||||
async (port: number) => ({
|
||||
port,
|
||||
@@ -232,6 +248,7 @@ vi.mock("../../gateway/probe-auth.js", async (importOriginal) => {
|
||||
vi.mock("../../infra/ports.js", () => ({
|
||||
inspectPortConnections: (port: number) => inspectPortConnections(port),
|
||||
inspectPortUsage: (port: number) => inspectPortUsage(port),
|
||||
inspectPortUsages: (ports: readonly number[]) => inspectPortUsages(ports),
|
||||
formatPortDiagnostics: () => [],
|
||||
}));
|
||||
|
||||
@@ -314,6 +331,20 @@ describe("gatherDaemonStatus", () => {
|
||||
listeners: [],
|
||||
hints: [],
|
||||
}));
|
||||
inspectPortUsages.mockReset();
|
||||
inspectPortUsages.mockImplementation(async (ports: readonly number[]) => {
|
||||
return new Map(
|
||||
ports.map((port) => [
|
||||
port,
|
||||
{
|
||||
port,
|
||||
status: "free" as const,
|
||||
listeners: [],
|
||||
hints: [],
|
||||
},
|
||||
]),
|
||||
);
|
||||
});
|
||||
inspectPortConnections.mockClear();
|
||||
inspectWindowsGatewayFirewall.mockClear();
|
||||
inspectWindowsGatewayFirewall.mockResolvedValue({
|
||||
@@ -382,6 +413,17 @@ describe("gatherDaemonStatus", () => {
|
||||
expect(inspectWindowsGatewayFirewall).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("batches daemon and CLI port status inspection when ports differ", async () => {
|
||||
await gatherDaemonStatus({
|
||||
rpc: {},
|
||||
probe: true,
|
||||
deep: false,
|
||||
});
|
||||
|
||||
expect(inspectPortUsages).toHaveBeenCalledWith([19001, 18789]);
|
||||
expect(inspectPortUsage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("reports the heap limit from the installed Gateway service", async () => {
|
||||
serviceReadCommand.mockResolvedValueOnce({
|
||||
programArguments: ["/bin/node", "cli", "gateway", "--port", "19001"],
|
||||
@@ -1149,12 +1191,19 @@ describe("gatherDaemonStatus", () => {
|
||||
});
|
||||
|
||||
it("includes the last gateway error when the service is listening but the RPC probe fails", async () => {
|
||||
inspectPortUsage.mockResolvedValueOnce({
|
||||
port: 19001,
|
||||
status: "busy",
|
||||
listeners: [{ pid: 8000, ppid: 1, commandLine: "openclaw gateway" }],
|
||||
hints: [],
|
||||
});
|
||||
inspectPortUsages.mockResolvedValueOnce(
|
||||
new Map([
|
||||
[
|
||||
19001,
|
||||
{
|
||||
port: 19001,
|
||||
status: "busy",
|
||||
listeners: [{ pid: 8000, ppid: 1, commandLine: "openclaw gateway" }],
|
||||
hints: [],
|
||||
},
|
||||
],
|
||||
]),
|
||||
);
|
||||
callGatewayStatusProbe.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
url: "wss://127.0.0.1:19001",
|
||||
@@ -1185,12 +1234,6 @@ describe("gatherDaemonStatus", () => {
|
||||
});
|
||||
|
||||
it("does not read local gateway errors for an explicit probe URL", async () => {
|
||||
inspectPortUsage.mockResolvedValueOnce({
|
||||
port: 19001,
|
||||
status: "busy",
|
||||
listeners: [{ pid: 8000, ppid: 1, commandLine: "openclaw gateway" }],
|
||||
hints: [],
|
||||
});
|
||||
callGatewayStatusProbe.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
url: "wss://remote.example:18790",
|
||||
@@ -1215,12 +1258,6 @@ describe("gatherDaemonStatus", () => {
|
||||
auth: { token: "daemon-token" },
|
||||
},
|
||||
};
|
||||
inspectPortUsage.mockResolvedValueOnce({
|
||||
port: 19001,
|
||||
status: "busy",
|
||||
listeners: [{ pid: 8000, ppid: 1, commandLine: "openclaw gateway" }],
|
||||
hints: [],
|
||||
});
|
||||
callGatewayStatusProbe.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
url: "wss://remote.example:18790",
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
formatPortDiagnostics,
|
||||
inspectPortConnections,
|
||||
inspectPortUsage,
|
||||
inspectPortUsages,
|
||||
type PortConnection,
|
||||
type PortListener,
|
||||
type PortUsageStatus,
|
||||
@@ -495,15 +496,19 @@ async function inspectDaemonPortStatuses(params: {
|
||||
daemonPort: number;
|
||||
cliPort: number;
|
||||
}): Promise<{ portStatus?: PortStatusSummary; portCliStatus?: PortStatusSummary }> {
|
||||
const [portDiagnostics, portCliDiagnostics] = await Promise.all([
|
||||
inspectPortUsage(params.daemonPort).catch(() => null),
|
||||
params.cliPort !== params.daemonPort
|
||||
? inspectPortUsage(params.cliPort).catch(() => null)
|
||||
: null,
|
||||
]);
|
||||
if (params.cliPort === params.daemonPort) {
|
||||
const portDiagnostics = await inspectPortUsage(params.daemonPort).catch(() => null);
|
||||
return {
|
||||
portStatus: toPortStatusSummary(portDiagnostics),
|
||||
portCliStatus: undefined,
|
||||
};
|
||||
}
|
||||
const portDiagnosticsByPort = await inspectPortUsages([params.daemonPort, params.cliPort]).catch(
|
||||
() => new Map(),
|
||||
);
|
||||
return {
|
||||
portStatus: toPortStatusSummary(portDiagnostics),
|
||||
portCliStatus: toPortStatusSummary(portCliDiagnostics),
|
||||
portStatus: toPortStatusSummary(portDiagnosticsByPort.get(params.daemonPort) ?? null),
|
||||
portCliStatus: toPortStatusSummary(portDiagnosticsByPort.get(params.cliPort) ?? null),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user