From 278ffbdb53579be1ca0e01a938bf590d04028d1a Mon Sep 17 00:00:00 2001 From: statxc <181730535+statxc@users.noreply.github.com> Date: Wed, 29 Apr 2026 14:14:24 +0200 Subject: [PATCH] fix(qqbot): type active config provider --- extensions/codex/index.test.ts | 4 +- .../src/engine/gateway/active-cfg.test.ts | 77 +++++++++---------- .../qqbot/src/engine/gateway/active-cfg.ts | 30 ++++---- extensions/qqbot/src/engine/gateway/types.ts | 3 +- 4 files changed, 54 insertions(+), 60 deletions(-) diff --git a/extensions/codex/index.test.ts b/extensions/codex/index.test.ts index 4e21514ccd5..fe6d91495a3 100644 --- a/extensions/codex/index.test.ts +++ b/extensions/codex/index.test.ts @@ -93,9 +93,7 @@ describe("codex plugin", () => { registerMediaUnderstandingProvider: vi.fn(), registerProvider, on: vi.fn(), - }) as ReturnType & { - onConversationBindingResolved?: ReturnType; - }; + }); delete (api as { onConversationBindingResolved?: unknown }).onConversationBindingResolved; plugin.register(api); diff --git a/extensions/qqbot/src/engine/gateway/active-cfg.test.ts b/extensions/qqbot/src/engine/gateway/active-cfg.test.ts index 388215ec212..0c8bd312821 100644 --- a/extensions/qqbot/src/engine/gateway/active-cfg.test.ts +++ b/extensions/qqbot/src/engine/gateway/active-cfg.test.ts @@ -1,71 +1,64 @@ +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types"; import { describe, expect, it, vi } from "vitest"; -import { - createActiveCfgProvider, - resolveActiveCfg, - type GatewayCfg, - type GatewayCfgFetcher, -} from "./active-cfg.js"; +import { createActiveCfgProvider, resolveActiveCfg, type GatewayCfgLoader } from "./active-cfg.js"; -const getRuntimeConfigMock = vi.hoisted(() => vi.fn<() => GatewayCfg | undefined>()); +const getRuntimeConfigMock = vi.hoisted(() => vi.fn<() => OpenClawConfig>()); vi.mock("openclaw/plugin-sdk/runtime-config-snapshot", () => ({ getRuntimeConfig: getRuntimeConfigMock, })); +function asCfg(shape: { bindings: Array<{ id: string }> }): OpenClawConfig { + return shape as unknown as OpenClawConfig; +} + describe("resolveActiveCfg", () => { - it("returns the freshly fetched value when present", () => { - const fresh = { bindings: [{ id: "fresh" }] }; - const fallback = { bindings: [{ id: "stale" }] }; - const fetch: GatewayCfgFetcher = () => fresh; + it("returns the freshly loaded value when the loader succeeds", () => { + const fresh = asCfg({ bindings: [{ id: "fresh" }] }); + const fallback = asCfg({ bindings: [{ id: "stale" }] }); + const loader: GatewayCfgLoader = () => fresh; - expect(resolveActiveCfg(fetch, fallback)).toBe(fresh); + expect(resolveActiveCfg(loader, fallback)).toBe(fresh); }); - it("falls back when the fetcher returns undefined", () => { - const fallback = { bindings: [{ id: "stale" }] }; - const fetch: GatewayCfgFetcher = () => undefined; - - expect(resolveActiveCfg(fetch, fallback)).toBe(fallback); - }); - - it("falls back when the fetcher throws", () => { - const fallback = { bindings: [{ id: "stale" }] }; - const fetch: GatewayCfgFetcher = () => { + it("falls back when the loader throws", () => { + const fallback = asCfg({ bindings: [{ id: "stale" }] }); + const loader: GatewayCfgLoader = () => { throw new Error("snapshot not initialised"); }; - expect(resolveActiveCfg(fetch, fallback)).toBe(fallback); + expect(resolveActiveCfg(loader, fallback)).toBe(fallback); }); }); describe("createActiveCfgProvider", () => { - it("invokes the injected fetcher on every getActiveCfg call", () => { - const fallback = { bindings: [] }; - const first = { bindings: [{ id: "first" }] }; - const second = { bindings: [{ id: "second" }] }; - const fetch = vi - .fn<() => GatewayCfg | undefined>() + it("invokes the injected loader on every getActiveCfg call", () => { + const fallback = asCfg({ bindings: [] }); + const first = asCfg({ bindings: [{ id: "first" }] }); + const second = asCfg({ bindings: [{ id: "second" }] }); + const load = vi + .fn<() => OpenClawConfig>() .mockReturnValueOnce(first) .mockReturnValueOnce(second); - const provider = createActiveCfgProvider({ fallback, fetch }); + const provider = createActiveCfgProvider({ fallback, load }); expect(provider.getActiveCfg()).toBe(first); expect(provider.getActiveCfg()).toBe(second); - expect(fetch).toHaveBeenCalledTimes(2); + expect(load).toHaveBeenCalledTimes(2); }); - it("never caches a previously fetched value", () => { - const fallback = { bindings: [] }; - const calls: GatewayCfg[] = [ - { bindings: [{ id: "a" }] }, - { bindings: [{ id: "b" }] }, - { bindings: [{ id: "c" }] }, + it("never caches a previously loaded value", () => { + const fallback = asCfg({ bindings: [] }); + const calls: OpenClawConfig[] = [ + asCfg({ bindings: [{ id: "a" }] }), + asCfg({ bindings: [{ id: "b" }] }), + asCfg({ bindings: [{ id: "c" }] }), ]; let index = 0; const provider = createActiveCfgProvider({ fallback, - fetch: () => calls[index++], + load: () => calls[index++], }); expect(provider.getActiveCfg()).toBe(calls[0]); @@ -73,19 +66,19 @@ describe("createActiveCfgProvider", () => { expect(provider.getActiveCfg()).toBe(calls[2]); }); - it("delegates to getRuntimeConfig when no fetcher is provided", () => { - const live = { bindings: [{ id: "live" }] }; + it("delegates to getRuntimeConfig when no loader is provided", () => { + const live = asCfg({ bindings: [{ id: "live" }] }); getRuntimeConfigMock.mockReset(); getRuntimeConfigMock.mockReturnValue(live); - const provider = createActiveCfgProvider({ fallback: { bindings: [] } }); + const provider = createActiveCfgProvider({ fallback: asCfg({ bindings: [] }) }); expect(provider.getActiveCfg()).toBe(live); expect(getRuntimeConfigMock).toHaveBeenCalledTimes(1); }); it("falls back to the supplied snapshot when the SDK getter throws", () => { - const fallback = { bindings: [{ id: "snapshot" }] }; + const fallback = asCfg({ bindings: [{ id: "snapshot" }] }); getRuntimeConfigMock.mockReset(); getRuntimeConfigMock.mockImplementation(() => { throw new Error("not ready"); diff --git a/extensions/qqbot/src/engine/gateway/active-cfg.ts b/extensions/qqbot/src/engine/gateway/active-cfg.ts index 7cec678ba34..97d980db815 100644 --- a/extensions/qqbot/src/engine/gateway/active-cfg.ts +++ b/extensions/qqbot/src/engine/gateway/active-cfg.ts @@ -5,46 +5,48 @@ * peer/account binding edits made via the CLI take effect without * restarting the gateway. The provider hides the per-event lookup * behind a typed seam and falls back to the startup snapshot when the - * runtime registry is not yet (or no longer) populated. + * runtime registry getter throws (e.g. snapshot not yet initialised). * * Issue #69546. */ +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types"; import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot"; -export type GatewayCfg = unknown; +export type GatewayCfg = OpenClawConfig; -export type GatewayCfgFetcher = () => GatewayCfg | undefined; +export type GatewayCfgLoader = () => OpenClawConfig; export interface ActiveCfgProvider { - getActiveCfg(): GatewayCfg; + getActiveCfg(): OpenClawConfig; } export interface ActiveCfgProviderOptions { - fallback: GatewayCfg; - fetch?: GatewayCfgFetcher; + fallback: OpenClawConfig; + load?: GatewayCfgLoader; } export function createActiveCfgProvider(options: ActiveCfgProviderOptions): ActiveCfgProvider { - const fetch = options.fetch ?? defaultGatewayCfgFetcher; + const loader = options.load ?? defaultGatewayCfgLoader; const fallback = options.fallback; return { - getActiveCfg(): GatewayCfg { - return resolveActiveCfg(fetch, fallback); + getActiveCfg(): OpenClawConfig { + return resolveActiveCfg(loader, fallback); }, }; } -export function resolveActiveCfg(fetch: GatewayCfgFetcher, fallback: GatewayCfg): GatewayCfg { - let fresh: GatewayCfg | undefined; +export function resolveActiveCfg( + loader: GatewayCfgLoader, + fallback: OpenClawConfig, +): OpenClawConfig { try { - fresh = fetch(); + return loader(); } catch { return fallback; } - return fresh ?? fallback; } -function defaultGatewayCfgFetcher(): GatewayCfg | undefined { +function defaultGatewayCfgLoader(): OpenClawConfig { return getRuntimeConfig(); } diff --git a/extensions/qqbot/src/engine/gateway/types.ts b/extensions/qqbot/src/engine/gateway/types.ts index ef26ede3761..69109c96d27 100644 --- a/extensions/qqbot/src/engine/gateway/types.ts +++ b/extensions/qqbot/src/engine/gateway/types.ts @@ -1,3 +1,4 @@ +import type { OpenClawConfig } from "openclaw/plugin-sdk/core"; import type { EngineLogger } from "../types.js"; export type { EngineLogger }; @@ -210,7 +211,7 @@ interface GatewayGroupOptions { export interface CoreGatewayContext { account: GatewayAccount; abortSignal: AbortSignal; - cfg: unknown; + cfg: OpenClawConfig; onReady?: (data: unknown) => void; /** * Invoked when a RESUMED event is received after reconnect.