Merge 37828cd4bb3972e12ae48ecb002c61e7cb29628c into 598f1826d8b2bc969aace2c6459824737667218c

This commit is contained in:
Alix-007 2026-03-20 21:06:28 -07:00 committed by GitHub
commit 8332a0e7c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 55 additions and 4 deletions

View File

@ -3,7 +3,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite
type RestartHealthSnapshot = {
healthy: boolean;
staleGatewayPids: number[];
runtime: { status?: string };
runtime: { status?: string; pid?: number };
portUsage: { port: number; status: string; listeners: []; hints: []; errors?: string[] };
};
@ -206,6 +206,50 @@ describe("runDaemonRestart health checks", () => {
expect(waitForGatewayHealthyRestart).toHaveBeenCalledTimes(2);
});
it("does not kill the current running gateway pid when stale detection includes self", async () => {
const unhealthy: RestartHealthSnapshot = {
healthy: false,
staleGatewayPids: [1993, 2111],
runtime: { status: "running", pid: 1993 },
portUsage: { port: 18789, status: "busy", listeners: [], hints: [] },
};
const healthy: RestartHealthSnapshot = {
healthy: true,
staleGatewayPids: [],
runtime: { status: "running", pid: 1993 },
portUsage: { port: 18789, status: "busy", listeners: [], hints: [] },
};
waitForGatewayHealthyRestart.mockResolvedValueOnce(unhealthy).mockResolvedValueOnce(healthy);
terminateStaleGatewayPids.mockResolvedValue([2111]);
const result = await runDaemonRestart({ json: true });
expect(result).toBe(true);
expect(terminateStaleGatewayPids).toHaveBeenCalledWith([2111]);
expect(service.restart).toHaveBeenCalledTimes(1);
expect(waitForGatewayHealthyRestart).toHaveBeenCalledTimes(2);
});
it("fails without killing the current runtime pid when stale detection only includes self", async () => {
const unhealthy: RestartHealthSnapshot = {
healthy: false,
staleGatewayPids: [1993],
runtime: { status: "running", pid: 1993 },
portUsage: { port: 18789, status: "busy", listeners: [], hints: [] },
};
waitForGatewayHealthyRestart.mockResolvedValue(unhealthy);
await expect(runDaemonRestart({ json: true })).rejects.toMatchObject({
message: "Gateway restart timed out after 60s waiting for health checks.",
hints: ["openclaw gateway status --deep", "openclaw doctor"],
});
expect(terminateStaleGatewayPids).not.toHaveBeenCalled();
expect(service.restart).not.toHaveBeenCalled();
expect(waitForGatewayHealthyRestart).toHaveBeenCalledTimes(1);
expect(renderRestartDiagnostics).toHaveBeenCalledTimes(1);
});
it("skips stale-pid retry health checks when the retry restart is only scheduled", async () => {
const unhealthy: RestartHealthSnapshot = {
healthy: false,

View File

@ -207,15 +207,22 @@ export async function runDaemonRestart(opts: DaemonLifecycleOptions = {}): Promi
includeUnknownListenersAsStale: process.platform === "win32",
});
if (!health.healthy && health.staleGatewayPids.length > 0) {
const staleMsg = `Found stale gateway process(es): ${health.staleGatewayPids.join(", ")}.`;
const staleGatewayPids = health.staleGatewayPids.filter((pid) => {
if (health.runtime.status !== "running") {
return true;
}
return health.runtime.pid == null || pid !== health.runtime.pid;
});
if (!health.healthy && staleGatewayPids.length > 0) {
const staleMsg = `Found stale gateway process(es): ${staleGatewayPids.join(", ")}.`;
warnings.push(staleMsg);
if (!json) {
defaultRuntime.log(theme.warn(staleMsg));
defaultRuntime.log(theme.muted("Stopping stale process(es) and retrying restart..."));
}
await terminateStaleGatewayPids(health.staleGatewayPids);
await terminateStaleGatewayPids(staleGatewayPids);
const retryRestart = await service.restart({ env: process.env, stdout });
if (retryRestart.outcome === "scheduled") {
return retryRestart;