mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 16:44:45 +00:00
fix(qqbot): type active config provider
This commit is contained in:
committed by
Peter Steinberger
parent
d69b663021
commit
278ffbdb53
@@ -93,9 +93,7 @@ describe("codex plugin", () => {
|
||||
registerMediaUnderstandingProvider: vi.fn(),
|
||||
registerProvider,
|
||||
on: vi.fn(),
|
||||
}) as ReturnType<typeof createTestPluginApi> & {
|
||||
onConversationBindingResolved?: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
});
|
||||
delete (api as { onConversationBindingResolved?: unknown }).onConversationBindingResolved;
|
||||
|
||||
plugin.register(api);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user