
























@@ -1,5 +1,5 @@
11import net from "node:net";
2-import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
2+import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
33import { stripAnsi } from "../terminal/ansi.js";
4455const runCommandWithTimeoutMock = vi.hoisted(() => vi.fn());
@@ -14,6 +14,14 @@ let handlePortError: typeof import("./ports.js").handlePortError;
1414let PortInUseError: typeof import("./ports.js").PortInUseError;
15151616const describeUnix = process.platform === "win32" ? describe.skip : describe;
17+const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
18+19+function setPlatform(platform: NodeJS.Platform): void {
20+Object.defineProperty(process, "platform", {
21+value: platform,
22+configurable: true,
23+});
24+}
17251826async function listenServer(
1927server: net.Server,
@@ -53,6 +61,12 @@ beforeEach(() => {
5361runCommandWithTimeoutMock.mockReset();
5462});
556364+afterEach(() => {
65+if (originalPlatformDescriptor) {
66+Object.defineProperty(process, "platform", originalPlatformDescriptor);
67+}
68+});
69+5670describe("ports helpers", () => {
5771it("ensurePortAvailable rejects when port busy", async () => {
5872const server = net.createServer();
@@ -183,3 +197,74 @@ describeUnix("inspectPortUsage", () => {
183197}
184198});
185199});
200+201+describe("inspectPortUsage on Windows", () => {
202+it("uses PowerShell process command lines to classify OpenClaw listeners", async () => {
203+setPlatform("win32");
204+runCommandWithTimeoutMock.mockImplementation(async (argv: string[]) => {
205+const [command] = argv;
206+if (command === "netstat") {
207+return {
208+stdout: " TCP 127.0.0.1:18789 0.0.0.0:0 LISTENING 4242\r\n",
209+stderr: "",
210+code: 0,
211+};
212+}
213+if (command === "tasklist") {
214+return { stdout: "Image Name: node.exe\r\n", stderr: "", code: 0 };
215+}
216+if (command === "powershell") {
217+return {
218+stdout:
219+'"C:\\Program Files\\nodejs\\node.exe" C:\\Users\\me\\AppData\\Roaming\\npm\\node_modules\\openclaw\\dist\\index.js gateway run\r\n',
220+stderr: "",
221+code: 0,
222+};
223+}
224+return { stdout: "", stderr: "", code: 1 };
225+});
226+227+const result = await inspectPortUsage(18789);
228+229+expect(result.status).toBe("busy");
230+expect(result.listeners).toHaveLength(1);
231+expect(result.listeners[0]?.command).toBe("node.exe");
232+expect(result.listeners[0]?.commandLine).toContain("openclaw");
233+expect(result.hints.some((hint) => hint.includes("Gateway already running locally"))).toBe(
234+true,
235+);
236+});
237+238+it("falls back to wmic when PowerShell cannot read the command line", async () => {
239+setPlatform("win32");
240+runCommandWithTimeoutMock.mockImplementation(async (argv: string[]) => {
241+const [command] = argv;
242+if (command === "netstat") {
243+return {
244+stdout: " TCP 127.0.0.1:18789 0.0.0.0:0 LISTENING 4242\r\n",
245+stderr: "",
246+code: 0,
247+};
248+}
249+if (command === "tasklist") {
250+return { stdout: "Image Name: node.exe\r\n", stderr: "", code: 0 };
251+}
252+if (command === "powershell") {
253+return { stdout: "", stderr: "access denied", code: 1 };
254+}
255+if (command === "wmic") {
256+return {
257+stdout: "CommandLine=node.exe C:\\openclaw\\dist\\index.js gateway run\r\n",
258+stderr: "",
259+code: 0,
260+};
261+}
262+return { stdout: "", stderr: "", code: 1 };
263+});
264+265+const result = await inspectPortUsage(18789);
266+267+expect(result.listeners[0]?.commandLine).toContain("openclaw");
268+expect(runCommandWithTimeoutMock.mock.calls.some(([argv]) => argv[0] === "wmic")).toBe(true);
269+});
270+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。