From 4aeabf95ccebb0b93ae058a2beb37f653fbcd6b9 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 6 Apr 2026 04:39:22 +0100 Subject: [PATCH] fix: stabilize contract loader seams --- extensions/mattermost/api.ts | 1 - extensions/mattermost/index.ts | 2 +- extensions/mattermost/policy-api.ts | 1 + extensions/mattermost/setup-entry.ts | 2 +- src/channels/plugins/contracts/registry.ts | 7 +- src/plugin-sdk/mattermost-policy.ts | 12 ++- ...ession-binding-registry-backed-contract.ts | 100 +++++------------- 7 files changed, 40 insertions(+), 85 deletions(-) create mode 100644 extensions/mattermost/policy-api.ts diff --git a/extensions/mattermost/api.ts b/extensions/mattermost/api.ts index 70ffeb395d1..629bba8580e 100644 --- a/extensions/mattermost/api.ts +++ b/extensions/mattermost/api.ts @@ -1,4 +1,3 @@ // Keep this barrel helper-only so plugin-sdk facades do not pull the full // channel plugin (and its runtime state) into tests or other shared surfaces. -export { mattermostPlugin } from "./src/channel.js"; export { isMattermostSenderAllowed } from "./src/mattermost/monitor-auth.js"; diff --git a/extensions/mattermost/index.ts b/extensions/mattermost/index.ts index c5c6718cf61..247e5082e30 100644 --- a/extensions/mattermost/index.ts +++ b/extensions/mattermost/index.ts @@ -18,7 +18,7 @@ export default defineBundledChannelEntry({ description: "Mattermost channel plugin", importMetaUrl: import.meta.url, plugin: { - specifier: "./api.js", + specifier: "./src/channel.js", exportName: "mattermostPlugin", }, runtime: { diff --git a/extensions/mattermost/policy-api.ts b/extensions/mattermost/policy-api.ts new file mode 100644 index 00000000000..5d53d67fc5f --- /dev/null +++ b/extensions/mattermost/policy-api.ts @@ -0,0 +1 @@ +export { isMattermostSenderAllowed } from "./src/mattermost/monitor-auth.js"; diff --git a/extensions/mattermost/setup-entry.ts b/extensions/mattermost/setup-entry.ts index 29f6266d874..c68ed581fbd 100644 --- a/extensions/mattermost/setup-entry.ts +++ b/extensions/mattermost/setup-entry.ts @@ -3,7 +3,7 @@ import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entr export default defineBundledChannelSetupEntry({ importMetaUrl: import.meta.url, plugin: { - specifier: "./api.js", + specifier: "./src/channel.js", exportName: "mattermostPlugin", }, }); diff --git a/src/channels/plugins/contracts/registry.ts b/src/channels/plugins/contracts/registry.ts index a066ab4599c..25f8a437f83 100644 --- a/src/channels/plugins/contracts/registry.ts +++ b/src/channels/plugins/contracts/registry.ts @@ -41,10 +41,9 @@ const sendMessageMatrixMock = vi.hoisted(() => roomId: to.replace(/^room:/, ""), })), ); -const matrixRuntimeApiModuleId = new URL( - "../../../../extensions/matrix/runtime-api.js", - import.meta.url, -).href; +const matrixRuntimeApiModuleId = vi.hoisted( + () => new URL("../../../../extensions/matrix/runtime-api.js", import.meta.url).href, +); const lineContractApi = await importBundledChannelContractArtifact<{ listLineAccountIds: () => string[]; diff --git a/src/plugin-sdk/mattermost-policy.ts b/src/plugin-sdk/mattermost-policy.ts index 17e2ba1c4d8..eb26b918226 100644 --- a/src/plugin-sdk/mattermost-policy.ts +++ b/src/plugin-sdk/mattermost-policy.ts @@ -1,11 +1,19 @@ // Manual facade. Keep loader boundary explicit. -type FacadeModule = typeof import("@openclaw/mattermost/api.js"); +type MattermostSenderAllowed = (params: { + senderId: string; + senderName?: string; + allowFrom: string[]; + allowNameMatching?: boolean; +}) => boolean; +type FacadeModule = { + isMattermostSenderAllowed: MattermostSenderAllowed; +}; import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; function loadFacadeModule(): FacadeModule { return loadBundledPluginPublicSurfaceModuleSync({ dirName: "mattermost", - artifactBasename: "api.js", + artifactBasename: "policy-api.js", }); } export const isMattermostSenderAllowed: FacadeModule["isMattermostSenderAllowed"] = ((...args) => diff --git a/test/helpers/channels/session-binding-registry-backed-contract.ts b/test/helpers/channels/session-binding-registry-backed-contract.ts index 4ac426a9f28..2628bc20466 100644 --- a/test/helpers/channels/session-binding-registry-backed-contract.ts +++ b/test/helpers/channels/session-binding-registry-backed-contract.ts @@ -1,4 +1,16 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { bluebubblesPlugin } from "../../../extensions/bluebubbles/api.js"; +import { + discordPlugin, + discordThreadBindingTesting, +} from "../../../extensions/discord/test-api.js"; +import { feishuPlugin, feishuThreadBindingTesting } from "../../../extensions/feishu/api.js"; +import { imessagePlugin } from "../../../extensions/imessage/api.js"; +import { matrixPlugin, setMatrixRuntime } from "../../../extensions/matrix/test-api.js"; +import { + telegramPlugin, + resetTelegramThreadBindingsForTests, +} from "../../../extensions/telegram/test-api.js"; import { getSessionBindingContractRegistry } from "../../../src/channels/plugins/contracts/registry-session-binding.js"; import type { ChannelPlugin } from "../../../src/channels/plugins/types.js"; import { @@ -13,10 +25,6 @@ import { import { resetPluginRuntimeStateForTest } from "../../../src/plugins/runtime.js"; import { setActivePluginRegistry } from "../../../src/plugins/runtime.js"; import type { PluginRuntime } from "../../../src/plugins/runtime/index.js"; -import { - loadBundledPluginPublicSurfaceSync, - loadBundledPluginTestApiSync, -} from "../../../src/test-utils/bundled-plugin-public-surface.js"; import { createTestRegistry } from "../../../src/test-utils/channel-plugins.js"; type DiscordThreadBindingTesting = { @@ -25,109 +33,49 @@ type DiscordThreadBindingTesting = { type ResetTelegramThreadBindingsForTests = () => Promise; -let discordThreadBindingTestingCache: DiscordThreadBindingTesting | undefined; -let resetTelegramThreadBindingsForTestsCache: ResetTelegramThreadBindingsForTests | undefined; -let feishuApiPromise: Promise | undefined; -let matrixApiPromise: Promise | undefined; -let bluebubblesPluginCache: ChannelPlugin | undefined; -let discordPluginCache: ChannelPlugin | undefined; -let feishuPluginCache: ChannelPlugin | undefined; -let imessagePluginCache: ChannelPlugin | undefined; -let matrixPluginCache: ChannelPlugin | undefined; -let setMatrixRuntimeCache: ((runtime: PluginRuntime) => void) | undefined; -let telegramPluginCache: ChannelPlugin | undefined; - function getBluebubblesPlugin(): ChannelPlugin { - if (!bluebubblesPluginCache) { - ({ bluebubblesPlugin: bluebubblesPluginCache } = loadBundledPluginPublicSurfaceSync<{ - bluebubblesPlugin: ChannelPlugin; - }>({ pluginId: "bluebubbles", artifactBasename: "index.js" })); - } - return bluebubblesPluginCache; + return bluebubblesPlugin as unknown as ChannelPlugin; } function getDiscordPlugin(): ChannelPlugin { - if (!discordPluginCache) { - ({ discordPlugin: discordPluginCache } = loadBundledPluginTestApiSync<{ - discordPlugin: ChannelPlugin; - }>("discord")); - } - return discordPluginCache; + return discordPlugin as unknown as ChannelPlugin; } function getFeishuPlugin(): ChannelPlugin { - if (!feishuPluginCache) { - ({ feishuPlugin: feishuPluginCache } = loadBundledPluginPublicSurfaceSync<{ - feishuPlugin: ChannelPlugin; - }>({ pluginId: "feishu", artifactBasename: "api.js" })); - } - return feishuPluginCache; + return feishuPlugin as unknown as ChannelPlugin; } function getIMessagePlugin(): ChannelPlugin { - if (!imessagePluginCache) { - ({ imessagePlugin: imessagePluginCache } = loadBundledPluginPublicSurfaceSync<{ - imessagePlugin: ChannelPlugin; - }>({ pluginId: "imessage", artifactBasename: "api.js" })); - } - return imessagePluginCache; + return imessagePlugin as unknown as ChannelPlugin; } function getMatrixPlugin(): ChannelPlugin { - if (!matrixPluginCache) { - ({ matrixPlugin: matrixPluginCache, setMatrixRuntime: setMatrixRuntimeCache } = - loadBundledPluginTestApiSync<{ - matrixPlugin: ChannelPlugin; - setMatrixRuntime: (runtime: PluginRuntime) => void; - }>("matrix")); - } - return matrixPluginCache; + return matrixPlugin as unknown as ChannelPlugin; } function getSetMatrixRuntime(): (runtime: PluginRuntime) => void { - if (!setMatrixRuntimeCache) { - void getMatrixPlugin(); - } - return setMatrixRuntimeCache!; + return setMatrixRuntime; } function getTelegramPlugin(): ChannelPlugin { - if (!telegramPluginCache) { - ({ telegramPlugin: telegramPluginCache } = loadBundledPluginTestApiSync<{ - telegramPlugin: ChannelPlugin; - }>("telegram")); - } - return telegramPluginCache; + return telegramPlugin as unknown as ChannelPlugin; } function getDiscordThreadBindingTesting(): DiscordThreadBindingTesting { - if (!discordThreadBindingTestingCache) { - ({ discordThreadBindingTesting: discordThreadBindingTestingCache } = - loadBundledPluginTestApiSync<{ - discordThreadBindingTesting: DiscordThreadBindingTesting; - }>("discord")); - } - return discordThreadBindingTestingCache; + return discordThreadBindingTesting; } function getResetTelegramThreadBindingsForTests(): ResetTelegramThreadBindingsForTests { - if (!resetTelegramThreadBindingsForTestsCache) { - ({ resetTelegramThreadBindingsForTests: resetTelegramThreadBindingsForTestsCache } = - loadBundledPluginTestApiSync<{ - resetTelegramThreadBindingsForTests: ResetTelegramThreadBindingsForTests; - }>("telegram")); - } - return resetTelegramThreadBindingsForTestsCache; + return resetTelegramThreadBindingsForTests; } async function getFeishuThreadBindingTesting() { - feishuApiPromise ??= import("../../../extensions/feishu/api.js"); - return (await feishuApiPromise).feishuThreadBindingTesting; + return feishuThreadBindingTesting; } async function getResetMatrixThreadBindingsForTests() { - matrixApiPromise ??= import("../../../extensions/matrix/api.js"); - return (await matrixApiPromise).resetMatrixThreadBindingsForTests; + const matrixApi = await import("../../../extensions/matrix/api.js"); + return matrixApi.resetMatrixThreadBindingsForTests; } function resolveSessionBindingContractRuntimeConfig(id: string) {