mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-29 01:31:18 +00:00
fix(control-ui): stop websocket client on lifecycle teardown (#23422)
Co-authored-by: floatinggball-design <262259579+floatinggball-design@users.noreply.github.com>
This commit is contained in:
@@ -41,6 +41,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Security/Elevated: match `tools.elevated.allowFrom` against sender identities only (not recipient `ctx.To`), closing a recipient-token bypass for `/elevated` authorization. (#11022) Thanks @coygeek.
|
- Security/Elevated: match `tools.elevated.allowFrom` against sender identities only (not recipient `ctx.To`), closing a recipient-token bypass for `/elevated` authorization. (#11022) Thanks @coygeek.
|
||||||
- Webchat/Sessions: preserve external session routing metadata when internal `chat.send` turns run under `webchat`, so explicit channel-keyed sessions (for example Telegram) no longer get rewritten to `webchat` and misroute follow-up delivery. (#23258) Thanks @binary64.
|
- Webchat/Sessions: preserve external session routing metadata when internal `chat.send` turns run under `webchat`, so explicit channel-keyed sessions (for example Telegram) no longer get rewritten to `webchat` and misroute follow-up delivery. (#23258) Thanks @binary64.
|
||||||
- Webchat/Sessions: preserve existing session `label` across `/new` and `/reset` rollovers so reset sessions remain discoverable in session history lists. (#23755) Thanks @ThunderStormer.
|
- Webchat/Sessions: preserve existing session `label` across `/new` and `/reset` rollovers so reset sessions remain discoverable in session history lists. (#23755) Thanks @ThunderStormer.
|
||||||
|
- Control UI/WebSocket: stop and clear the browser gateway client on UI teardown so remounts cannot leave orphan websocket clients that create duplicate active connections. (#23422) Thanks @floatinggball-design.
|
||||||
- Config/Memory: allow `"mistral"` in `agents.defaults.memorySearch.provider` and `agents.defaults.memorySearch.fallback` schema validation. (#14934) Thanks @ThomsenDrake.
|
- Config/Memory: allow `"mistral"` in `agents.defaults.memorySearch.provider` and `agents.defaults.memorySearch.fallback` schema validation. (#14934) Thanks @ThomsenDrake.
|
||||||
- Security/Feishu: enforce ID-only allowlist matching for DM/group sender authorization, normalize Feishu ID prefixes during checks, and ignore mutable display names so display-name collisions cannot satisfy allowlist entries. This ships in the next npm release. Thanks @jiseoung for reporting.
|
- Security/Feishu: enforce ID-only allowlist matching for DM/group sender authorization, normalize Feishu ID prefixes during checks, and ignore mutable display names so display-name collisions cannot satisfy allowlist entries. This ships in the next npm release. Thanks @jiseoung for reporting.
|
||||||
- Security/Group policy: harden `channels.*.groups.*.toolsBySender` matching by requiring explicit sender-key types (`id:`, `e164:`, `username:`, `name:`), preventing cross-identifier collisions across mutable/display-name fields while keeping legacy untyped keys on a deprecated ID-only path. This ships in the next npm release. Thanks @jiseoung for reporting.
|
- Security/Group policy: harden `channels.*.groups.*.toolsBySender` matching by requiring explicit sender-key types (`id:`, `e164:`, `username:`, `name:`), preventing cross-identifier collisions across mutable/display-name fields while keeping legacy untyped keys on a deprecated ID-only path. This ships in the next npm release. Thanks @jiseoung for reporting.
|
||||||
|
|||||||
44
ui/src/ui/app-lifecycle.node.test.ts
Normal file
44
ui/src/ui/app-lifecycle.node.test.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { handleDisconnected } from "./app-lifecycle.ts";
|
||||||
|
|
||||||
|
function createHost() {
|
||||||
|
return {
|
||||||
|
basePath: "",
|
||||||
|
client: { stop: vi.fn() },
|
||||||
|
connected: true,
|
||||||
|
tab: "chat",
|
||||||
|
assistantName: "OpenClaw",
|
||||||
|
assistantAvatar: null,
|
||||||
|
assistantAgentId: null,
|
||||||
|
chatHasAutoScrolled: false,
|
||||||
|
chatManualRefreshInFlight: false,
|
||||||
|
chatLoading: false,
|
||||||
|
chatMessages: [],
|
||||||
|
chatToolMessages: [],
|
||||||
|
chatStream: null,
|
||||||
|
logsAutoFollow: false,
|
||||||
|
logsAtBottom: true,
|
||||||
|
logsEntries: [],
|
||||||
|
popStateHandler: vi.fn(),
|
||||||
|
topbarObserver: { disconnect: vi.fn() } as unknown as ResizeObserver,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("handleDisconnected", () => {
|
||||||
|
it("stops and clears gateway client on teardown", () => {
|
||||||
|
const removeSpy = vi.spyOn(window, "removeEventListener").mockImplementation(() => undefined);
|
||||||
|
const host = createHost();
|
||||||
|
const disconnectSpy = (
|
||||||
|
host.topbarObserver as unknown as { disconnect: ReturnType<typeof vi.fn> }
|
||||||
|
).disconnect;
|
||||||
|
|
||||||
|
handleDisconnected(host as unknown as Parameters<typeof handleDisconnected>[0]);
|
||||||
|
|
||||||
|
expect(removeSpy).toHaveBeenCalledWith("popstate", host.popStateHandler);
|
||||||
|
expect(host.client).toBeNull();
|
||||||
|
expect(host.connected).toBe(false);
|
||||||
|
expect(disconnectSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(host.topbarObserver).toBeNull();
|
||||||
|
removeSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -21,6 +21,8 @@ import type { Tab } from "./navigation.ts";
|
|||||||
|
|
||||||
type LifecycleHost = {
|
type LifecycleHost = {
|
||||||
basePath: string;
|
basePath: string;
|
||||||
|
client?: { stop: () => void } | null;
|
||||||
|
connected?: boolean;
|
||||||
tab: Tab;
|
tab: Tab;
|
||||||
assistantName: string;
|
assistantName: string;
|
||||||
assistantAvatar: string | null;
|
assistantAvatar: string | null;
|
||||||
@@ -65,6 +67,9 @@ export function handleDisconnected(host: LifecycleHost) {
|
|||||||
stopNodesPolling(host as unknown as Parameters<typeof stopNodesPolling>[0]);
|
stopNodesPolling(host as unknown as Parameters<typeof stopNodesPolling>[0]);
|
||||||
stopLogsPolling(host as unknown as Parameters<typeof stopLogsPolling>[0]);
|
stopLogsPolling(host as unknown as Parameters<typeof stopLogsPolling>[0]);
|
||||||
stopDebugPolling(host as unknown as Parameters<typeof stopDebugPolling>[0]);
|
stopDebugPolling(host as unknown as Parameters<typeof stopDebugPolling>[0]);
|
||||||
|
host.client?.stop();
|
||||||
|
host.client = null;
|
||||||
|
host.connected = false;
|
||||||
detachThemeListener(host as unknown as Parameters<typeof detachThemeListener>[0]);
|
detachThemeListener(host as unknown as Parameters<typeof detachThemeListener>[0]);
|
||||||
host.topbarObserver?.disconnect();
|
host.topbarObserver?.disconnect();
|
||||||
host.topbarObserver = null;
|
host.topbarObserver = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user