mirror of https://github.com/openclaw/openclaw.git
Control UI: clear queued connect timeout on stop (#57338)
Merged via squash.
Prepared head SHA: a359fe8367
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
parent
fb81e3fc7f
commit
b952e404fa
|
|
@ -227,6 +227,7 @@ Docs: https://docs.openclaw.ai
|
|||
- Gateway/SQLite transient handling: keep unhandled `SQLITE_CANTOPEN`, `SQLITE_BUSY`, `SQLITE_LOCKED`, and `SQLITE_IOERR` failures non-fatal in the global rejection handler so macOS LaunchAgent restarts do not enter a crash-throttle loop. (#57018)
|
||||
- Control UI/gateway: reconnect the browser client when gateway event sequence gaps are detected, so stale non-chat state recovers automatically instead of only telling the user to refresh. (#23912) thanks @Olshansk.
|
||||
- ClawDock/docs: move the helper scripts to `scripts/clawdock`, publish ClawDock as a first-class docs page on the docs site, and document reinstalling local helper copies from the new raw GitHub path. (#23912) thanks @Olshansk.
|
||||
- Control UI/gateway: clear queued browser connect timeouts on client stop so aborted or replaced gateway clients do not send delayed connect requests after shutdown. (#57338) thanks @gumadeiras.
|
||||
|
||||
## 2026.3.24
|
||||
|
||||
|
|
|
|||
|
|
@ -92,6 +92,16 @@ type ConnectFrame = {
|
|||
};
|
||||
};
|
||||
|
||||
function stubWindowGlobals(storage?: ReturnType<typeof createStorageMock>) {
|
||||
vi.stubGlobal("window", {
|
||||
location: { href: "http://127.0.0.1:18789/" },
|
||||
localStorage: storage,
|
||||
setTimeout: (handler: (...args: unknown[]) => void, timeout?: number, ...args: unknown[]) =>
|
||||
globalThis.setTimeout(() => handler(...args), timeout),
|
||||
clearTimeout: (timeoutId: number | undefined) => globalThis.clearTimeout(timeoutId),
|
||||
});
|
||||
}
|
||||
|
||||
function getLatestWebSocket(): MockWebSocket {
|
||||
const ws = wsInstances.at(-1);
|
||||
if (!ws) {
|
||||
|
|
@ -182,10 +192,7 @@ describe("GatewayBrowserClient", () => {
|
|||
});
|
||||
|
||||
vi.stubGlobal("localStorage", storage);
|
||||
Object.defineProperty(window, "localStorage", {
|
||||
configurable: true,
|
||||
value: storage,
|
||||
});
|
||||
stubWindowGlobals(storage);
|
||||
localStorage.clear();
|
||||
vi.stubGlobal("WebSocket", MockWebSocket);
|
||||
|
||||
|
|
@ -377,6 +384,26 @@ describe("GatewayBrowserClient", () => {
|
|||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("cancels a queued connect send when stopped before the timeout fires", async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const client = new GatewayBrowserClient({
|
||||
url: "ws://127.0.0.1:18789",
|
||||
token: "shared-auth-token",
|
||||
});
|
||||
|
||||
client.start();
|
||||
const ws = getLatestWebSocket();
|
||||
ws.emitOpen();
|
||||
|
||||
client.stop();
|
||||
await vi.advanceTimersByTimeAsync(750);
|
||||
|
||||
expect(ws.sent).toHaveLength(0);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("does not auto-reconnect on AUTH_TOKEN_MISSING", async () => {
|
||||
vi.useFakeTimers();
|
||||
localStorage.clear();
|
||||
|
|
@ -408,6 +435,14 @@ describe("GatewayBrowserClient", () => {
|
|||
});
|
||||
|
||||
describe("shouldRetryWithDeviceToken", () => {
|
||||
beforeEach(() => {
|
||||
stubWindowGlobals();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it("allows a bounded retry for trusted loopback endpoints", () => {
|
||||
expect(
|
||||
shouldRetryWithDeviceToken({
|
||||
|
|
|
|||
|
|
@ -294,6 +294,10 @@ export class GatewayBrowserClient {
|
|||
|
||||
stop() {
|
||||
this.closed = true;
|
||||
if (this.connectTimer !== null) {
|
||||
window.clearTimeout(this.connectTimer);
|
||||
this.connectTimer = null;
|
||||
}
|
||||
this.ws?.close();
|
||||
this.ws = null;
|
||||
this.pendingConnectError = undefined;
|
||||
|
|
|
|||
Loading…
Reference in New Issue