diff --git a/CHANGELOG.md b/CHANGELOG.md index cb4d2cde405..bec1b32283a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Docs: https://docs.openclaw.ai - Agents/memory bootstrap: load only one root memory file, preferring `MEMORY.md` and using `memory.md` as a fallback, so case-insensitive Docker mounts no longer inject duplicate memory context. (#26054) Thanks @Lanfei. - Agents/OpenAI-compatible compat overrides: respect explicit user `models[].compat` opt-ins for non-native `openai-completions` endpoints so usage-in-streaming capability overrides no longer get forced off when the endpoint actually supports them. (#44432) Thanks @cheapestinference. - Agents/Azure OpenAI startup prompts: rephrase the built-in `/new`, `/reset`, and post-compaction startup instruction so Azure OpenAI deployments no longer hit HTTP 400 false positives from the content filter. (#43403) Thanks @xingsy97. +- Windows/gateway stop: resolve Startup-folder fallback listeners from the installed `gateway.cmd` port, so `openclaw gateway stop` now actually kills fallback-launched gateway processes before restart. - Config/validation: accept documented `agents.list[].params` per-agent overrides in strict config validation so `openclaw config validate` no longer rejects runtime-supported `cacheRetention`, `temperature`, and `maxTokens` settings. (#41171) Thanks @atian8179. - Android/onboarding QR scan: switch setup QR scanning to Google Code Scanner so onboarding uses a more reliable scanner instead of the legacy embedded ZXing flow. (#45021) Thanks @obviyus. - Config/web fetch: restore runtime validation for documented `tools.web.fetch.readability` and `tools.web.fetch.firecrawl` settings so valid web fetch configs no longer fail with unrecognized-key errors. (#42583) Thanks @stim64045-spec. diff --git a/src/daemon/schtasks.startup-fallback.test.ts b/src/daemon/schtasks.startup-fallback.test.ts index 1a949856a09..2dbdf388e45 100644 --- a/src/daemon/schtasks.startup-fallback.test.ts +++ b/src/daemon/schtasks.startup-fallback.test.ts @@ -43,6 +43,7 @@ const { readScheduledTaskRuntime, restartScheduledTask, resolveTaskScriptPath, + stopScheduledTask, } = await import("./schtasks.js"); function resolveStartupEntryPath(env: Record) { @@ -74,6 +75,21 @@ async function withWindowsEnv( } } +async function writeGatewayScript(env: Record, port = 18789) { + const scriptPath = resolveTaskScriptPath(env); + await fs.mkdir(path.dirname(scriptPath), { recursive: true }); + await fs.writeFile( + scriptPath, + [ + "@echo off", + `set "OPENCLAW_GATEWAY_PORT=${port}"`, + `"C:\\Program Files\\nodejs\\node.exe" "C:\\Users\\steipete\\AppData\\Roaming\\npm\\node_modules\\openclaw\\dist\\index.js" gateway --port ${port}`, + "", + ].join("\r\n"), + "utf8", + ); +} + beforeEach(() => { schtasksResponses.length = 0; schtasksCalls.length = 0; @@ -211,4 +227,39 @@ describe("Windows startup fallback", () => { ); }); }); + + it("kills the Startup fallback runtime even when the CLI env omits the gateway port", async () => { + await withWindowsEnv(async ({ env }) => { + schtasksResponses.push({ code: 0, stdout: "", stderr: "" }); + await writeGatewayScript(env); + await fs.mkdir(path.dirname(resolveStartupEntryPath(env)), { recursive: true }); + await fs.writeFile(resolveStartupEntryPath(env), "@echo off\r\n", "utf8"); + inspectPortUsage + .mockResolvedValueOnce({ + port: 18789, + status: "busy", + listeners: [{ pid: 5151, command: "node.exe" }], + hints: [], + }) + .mockResolvedValueOnce({ + port: 18789, + status: "busy", + listeners: [{ pid: 5151, command: "node.exe" }], + hints: [], + }) + .mockResolvedValueOnce({ + port: 18789, + status: "free", + listeners: [], + hints: [], + }); + + const stdout = new PassThrough(); + const envWithoutPort = { ...env }; + delete envWithoutPort.OPENCLAW_GATEWAY_PORT; + await stopScheduledTask({ env: envWithoutPort, stdout }); + + expect(killProcessTree).toHaveBeenCalledWith(5151, { graceMs: 300 }); + }); + }); }); diff --git a/src/daemon/schtasks.ts b/src/daemon/schtasks.ts index 3a92f0944fc..5453e6b26d8 100644 --- a/src/daemon/schtasks.ts +++ b/src/daemon/schtasks.ts @@ -482,7 +482,7 @@ async function terminateBusyPortListeners(port: number): Promise { } async function resolveFallbackRuntime(env: GatewayServiceEnv): Promise { - const port = resolveConfiguredGatewayPort(env); + const port = (await resolveScheduledTaskPort(env)) ?? resolveConfiguredGatewayPort(env); if (!port) { return { status: "unknown",