From f6868b7e42db7dc6a8a1d22daf02880bafd5dc42 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 16 Mar 2026 23:51:41 -0700 Subject: [PATCH 1/6] refactor: dedupe channel entrypoints and test bridges --- extensions/amazon-bedrock/index.test.ts | 2 +- extensions/bluebubbles/index.ts | 16 ++-- extensions/bluebubbles/setup-entry.ts | 5 +- extensions/diffs/index.test.ts | 2 +- extensions/diffs/src/http.test.ts | 2 +- extensions/discord/index.ts | 21 ++--- extensions/discord/setup-entry.ts | 3 +- extensions/discord/src/api.test.ts | 2 +- extensions/discord/src/chunk.test.ts | 2 +- extensions/discord/src/monitor.test.ts | 2 +- .../discord/src/resolve-channels.test.ts | 2 +- extensions/discord/src/resolve-users.test.ts | 2 +- extensions/feishu/index.ts | 19 ++-- extensions/feishu/setup-entry.ts | 5 +- extensions/github-copilot/usage.test.ts | 5 +- extensions/googlechat/index.ts | 16 ++-- extensions/googlechat/setup-entry.ts | 5 +- .../src/monitor.webhook-routing.test.ts | 2 +- extensions/imessage/index.ts | 16 ++-- extensions/imessage/setup-entry.ts | 3 +- extensions/irc/index.ts | 17 ++-- extensions/irc/setup-entry.ts | 5 +- extensions/line/index.ts | 21 ++--- extensions/line/setup-entry.ts | 5 +- extensions/matrix/index.ts | 18 ++-- extensions/matrix/setup-entry.ts | 5 +- extensions/mattermost/index.ts | 27 ++---- extensions/mattermost/setup-entry.ts | 5 +- extensions/msteams/index.ts | 16 ++-- extensions/msteams/setup-entry.ts | 5 +- extensions/msteams/src/graph-upload.test.ts | 2 +- extensions/nextcloud-talk/index.ts | 16 ++-- extensions/nextcloud-talk/setup-entry.ts | 5 +- extensions/nostr/index.ts | 42 +++------ extensions/nostr/setup-entry.ts | 5 +- extensions/signal/index.ts | 16 ++-- extensions/signal/setup-entry.ts | 3 +- extensions/slack/index.ts | 16 ++-- extensions/slack/setup-entry.ts | 3 +- extensions/slack/src/monitor/media.test.ts | 2 +- extensions/synology-chat/index.ts | 16 ++-- extensions/synology-chat/setup-entry.ts | 5 +- extensions/talk-voice/index.test.ts | 2 +- extensions/telegram/index.ts | 17 ++-- extensions/telegram/setup-entry.ts | 3 +- .../telegram/src/account-inspect.test.ts | 2 +- extensions/telegram/src/accounts.test.ts | 2 +- .../src/bot.create-telegram-bot.test.ts | 4 +- extensions/telegram/src/bot.test.ts | 2 +- extensions/telegram/src/probe.test.ts | 2 +- extensions/test-utils/chunk-test-helpers.ts | 1 + extensions/test-utils/env.ts | 1 + extensions/test-utils/fetch-mock.ts | 1 + extensions/test-utils/frozen-time.ts | 1 + extensions/test-utils/mock-http-response.ts | 1 + extensions/test-utils/plugin-command.ts | 1 + extensions/test-utils/plugin-registration.ts | 1 + extensions/test-utils/provider-usage-fetch.ts | 4 + extensions/test-utils/temp-dir.ts | 1 + extensions/test-utils/typed-cases.ts | 1 + extensions/tlon/index.ts | 54 +++-------- extensions/tlon/setup-entry.ts | 5 +- extensions/twitch/index.ts | 19 ++-- extensions/whatsapp/index.ts | 16 ++-- extensions/whatsapp/setup-entry.ts | 3 +- .../src/accounts.whatsapp-auth.test.ts | 2 +- ...o-reply.connection-and-logging.e2e.test.ts | 2 +- .../auto-reply/web-auto-reply-utils.test.ts | 2 +- extensions/whatsapp/src/media.test.ts | 2 +- extensions/zalo/index.ts | 18 ++-- extensions/zalo/setup-entry.ts | 5 +- extensions/zalouser/index.ts | 21 ++--- extensions/zalouser/setup-entry.ts | 5 +- package.json | 3 +- .../check-no-extension-test-core-imports.ts | 90 +++++++++++++++++++ src/plugin-sdk/core.ts | 53 +++++++++++ src/plugin-sdk/subpaths.test.ts | 2 + 77 files changed, 360 insertions(+), 376 deletions(-) create mode 100644 extensions/test-utils/chunk-test-helpers.ts create mode 100644 extensions/test-utils/env.ts create mode 100644 extensions/test-utils/fetch-mock.ts create mode 100644 extensions/test-utils/frozen-time.ts create mode 100644 extensions/test-utils/mock-http-response.ts create mode 100644 extensions/test-utils/plugin-command.ts create mode 100644 extensions/test-utils/plugin-registration.ts create mode 100644 extensions/test-utils/provider-usage-fetch.ts create mode 100644 extensions/test-utils/temp-dir.ts create mode 100644 extensions/test-utils/typed-cases.ts create mode 100644 scripts/check-no-extension-test-core-imports.ts diff --git a/extensions/amazon-bedrock/index.test.ts b/extensions/amazon-bedrock/index.test.ts index 641173cd6ce..61b33a0bc68 100644 --- a/extensions/amazon-bedrock/index.test.ts +++ b/extensions/amazon-bedrock/index.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { registerSingleProviderPlugin } from "../../src/test-utils/plugin-registration.js"; +import { registerSingleProviderPlugin } from "../test-utils/plugin-registration.js"; import amazonBedrockPlugin from "./index.js"; describe("amazon-bedrock provider plugin", () => { diff --git a/extensions/bluebubbles/index.ts b/extensions/bluebubbles/index.ts index f04afb40959..778cbd8ae8f 100644 --- a/extensions/bluebubbles/index.ts +++ b/extensions/bluebubbles/index.ts @@ -1,17 +1,11 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/bluebubbles"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/bluebubbles"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { bluebubblesPlugin } from "./src/channel.js"; import { setBlueBubblesRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "bluebubbles", name: "BlueBubbles", description: "BlueBubbles channel plugin (macOS app)", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setBlueBubblesRuntime(api.runtime); - api.registerChannel({ plugin: bluebubblesPlugin }); - }, -}; - -export default plugin; + plugin: bluebubblesPlugin, + setRuntime: setBlueBubblesRuntime, +}); diff --git a/extensions/bluebubbles/setup-entry.ts b/extensions/bluebubbles/setup-entry.ts index 5e05d9c8bb2..940837c87f6 100644 --- a/extensions/bluebubbles/setup-entry.ts +++ b/extensions/bluebubbles/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { bluebubblesPlugin } from "./src/channel.js"; -export default { - plugin: bluebubblesPlugin, -}; +export default defineSetupPluginEntry(bluebubblesPlugin); diff --git a/extensions/diffs/index.test.ts b/extensions/diffs/index.test.ts index c38da12bfcd..b1ade0c6a09 100644 --- a/extensions/diffs/index.test.ts +++ b/extensions/diffs/index.test.ts @@ -1,7 +1,7 @@ import type { IncomingMessage } from "node:http"; import type { OpenClawPluginApi } from "openclaw/plugin-sdk/diffs"; import { describe, expect, it, vi } from "vitest"; -import { createMockServerResponse } from "../../src/test-utils/mock-http-response.js"; +import { createMockServerResponse } from "../test-utils/mock-http-response.js"; import { createTestPluginApi } from "../test-utils/plugin-api.js"; import plugin from "./index.js"; diff --git a/extensions/diffs/src/http.test.ts b/extensions/diffs/src/http.test.ts index a1caef018e4..eed9abd77d8 100644 --- a/extensions/diffs/src/http.test.ts +++ b/extensions/diffs/src/http.test.ts @@ -1,6 +1,6 @@ import type { IncomingMessage } from "node:http"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { createMockServerResponse } from "../../../src/test-utils/mock-http-response.js"; +import { createMockServerResponse } from "../../test-utils/mock-http-response.js"; import { createDiffsHttpHandler } from "./http.js"; import { DiffArtifactStore } from "./store.js"; import { createDiffStoreHarness } from "./test-helpers.js"; diff --git a/extensions/discord/index.ts b/extensions/discord/index.ts index 13b32f08bb1..7c179623e23 100644 --- a/extensions/discord/index.ts +++ b/extensions/discord/index.ts @@ -1,22 +1,13 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { discordPlugin } from "./src/channel.js"; import { setDiscordRuntime } from "./src/runtime.js"; import { registerDiscordSubagentHooks } from "./src/subagent-hooks.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "discord", name: "Discord", description: "Discord channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setDiscordRuntime(api.runtime); - api.registerChannel({ plugin: discordPlugin }); - if (api.registrationMode !== "full") { - return; - } - registerDiscordSubagentHooks(api); - }, -}; - -export default plugin; + plugin: discordPlugin, + setRuntime: setDiscordRuntime, + registerFull: registerDiscordSubagentHooks, +}); diff --git a/extensions/discord/setup-entry.ts b/extensions/discord/setup-entry.ts index 329a9376c9f..e59c812ff4b 100644 --- a/extensions/discord/setup-entry.ts +++ b/extensions/discord/setup-entry.ts @@ -1,3 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { discordSetupPlugin } from "./src/channel.setup.js"; -export default { plugin: discordSetupPlugin }; +export default defineSetupPluginEntry(discordSetupPlugin); diff --git a/extensions/discord/src/api.test.ts b/extensions/discord/src/api.test.ts index 5b0e648aa1d..09e0863e137 100644 --- a/extensions/discord/src/api.test.ts +++ b/extensions/discord/src/api.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { withFetchPreconnect } from "../../../src/test-utils/fetch-mock.js"; +import { withFetchPreconnect } from "../../test-utils/fetch-mock.js"; import { fetchDiscord } from "./api.js"; import { jsonResponse } from "./test-http-helpers.js"; diff --git a/extensions/discord/src/chunk.test.ts b/extensions/discord/src/chunk.test.ts index 3c667c0fc9f..69f5ec856ec 100644 --- a/extensions/discord/src/chunk.test.ts +++ b/extensions/discord/src/chunk.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { countLines, hasBalancedFences } from "../../../src/test-utils/chunk-test-helpers.js"; +import { countLines, hasBalancedFences } from "../../test-utils/chunk-test-helpers.js"; import { chunkDiscordText, chunkDiscordTextWithMode } from "./chunk.js"; describe("chunkDiscordText", () => { diff --git a/extensions/discord/src/monitor.test.ts b/extensions/discord/src/monitor.test.ts index 40f14a00551..b3af666c35f 100644 --- a/extensions/discord/src/monitor.test.ts +++ b/extensions/discord/src/monitor.test.ts @@ -1,6 +1,6 @@ import { ChannelType, type Guild } from "@buape/carbon"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { typedCases } from "../../../src/test-utils/typed-cases.js"; +import { typedCases } from "../../test-utils/typed-cases.js"; import { allowListMatches, buildDiscordMediaPayload, diff --git a/extensions/discord/src/resolve-channels.test.ts b/extensions/discord/src/resolve-channels.test.ts index fb46792aaaa..f053fb97888 100644 --- a/extensions/discord/src/resolve-channels.test.ts +++ b/extensions/discord/src/resolve-channels.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { withFetchPreconnect } from "../../../src/test-utils/fetch-mock.js"; +import { withFetchPreconnect } from "../../test-utils/fetch-mock.js"; import { resolveDiscordChannelAllowlist } from "./resolve-channels.js"; import { jsonResponse, urlToString } from "./test-http-helpers.js"; diff --git a/extensions/discord/src/resolve-users.test.ts b/extensions/discord/src/resolve-users.test.ts index d788b77ebe0..f67b7289a59 100644 --- a/extensions/discord/src/resolve-users.test.ts +++ b/extensions/discord/src/resolve-users.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { withFetchPreconnect } from "../../../src/test-utils/fetch-mock.js"; +import { withFetchPreconnect } from "../../test-utils/fetch-mock.js"; import { resolveDiscordUserAllowlist } from "./resolve-users.js"; import { jsonResponse, urlToString } from "./test-http-helpers.js"; diff --git a/extensions/feishu/index.ts b/extensions/feishu/index.ts index ba7ac26922b..27f90f66479 100644 --- a/extensions/feishu/index.ts +++ b/extensions/feishu/index.ts @@ -1,5 +1,4 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/feishu"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/feishu"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { registerFeishuBitableTools } from "./src/bitable.js"; import { feishuPlugin } from "./src/channel.js"; import { registerFeishuChatTools } from "./src/chat.js"; @@ -46,17 +45,13 @@ export { } from "./src/mention.js"; export { feishuPlugin } from "./src/channel.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "feishu", name: "Feishu", description: "Feishu/Lark channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setFeishuRuntime(api.runtime); - api.registerChannel({ plugin: feishuPlugin }); - if (api.registrationMode !== "full") { - return; - } + plugin: feishuPlugin, + setRuntime: setFeishuRuntime, + registerFull(api) { registerFeishuSubagentHooks(api); registerFeishuDocTools(api); registerFeishuChatTools(api); @@ -65,6 +60,4 @@ const plugin = { registerFeishuPermTools(api); registerFeishuBitableTools(api); }, -}; - -export default plugin; +}); diff --git a/extensions/feishu/setup-entry.ts b/extensions/feishu/setup-entry.ts index 3e4df4faee8..1f16bde8bdd 100644 --- a/extensions/feishu/setup-entry.ts +++ b/extensions/feishu/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { feishuPlugin } from "./src/channel.js"; -export default { - plugin: feishuPlugin, -}; +export default defineSetupPluginEntry(feishuPlugin); diff --git a/extensions/github-copilot/usage.test.ts b/extensions/github-copilot/usage.test.ts index b4044c7f5f9..0bc97974d70 100644 --- a/extensions/github-copilot/usage.test.ts +++ b/extensions/github-copilot/usage.test.ts @@ -1,8 +1,5 @@ import { describe, expect, it } from "vitest"; -import { - createProviderUsageFetch, - makeResponse, -} from "../../src/test-utils/provider-usage-fetch.js"; +import { createProviderUsageFetch, makeResponse } from "../test-utils/provider-usage-fetch.js"; import { fetchCopilotUsage } from "./usage.js"; describe("fetchCopilotUsage", () => { diff --git a/extensions/googlechat/index.ts b/extensions/googlechat/index.ts index 892694f93b4..414bfc9557b 100644 --- a/extensions/googlechat/index.ts +++ b/extensions/googlechat/index.ts @@ -1,17 +1,11 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/googlechat"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/googlechat"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { googlechatPlugin } from "./src/channel.js"; import { setGoogleChatRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "googlechat", name: "Google Chat", description: "OpenClaw Google Chat channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setGoogleChatRuntime(api.runtime); - api.registerChannel(googlechatPlugin); - }, -}; - -export default plugin; + plugin: googlechatPlugin, + setRuntime: setGoogleChatRuntime, +}); diff --git a/extensions/googlechat/setup-entry.ts b/extensions/googlechat/setup-entry.ts index be33127799f..44fd1f11fb3 100644 --- a/extensions/googlechat/setup-entry.ts +++ b/extensions/googlechat/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { googlechatPlugin } from "./src/channel.js"; -export default { - plugin: googlechatPlugin, -}; +export default defineSetupPluginEntry(googlechatPlugin); diff --git a/extensions/googlechat/src/monitor.webhook-routing.test.ts b/extensions/googlechat/src/monitor.webhook-routing.test.ts index 9896efce645..2258d154449 100644 --- a/extensions/googlechat/src/monitor.webhook-routing.test.ts +++ b/extensions/googlechat/src/monitor.webhook-routing.test.ts @@ -4,7 +4,7 @@ import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk/googlech import { afterEach, describe, expect, it, vi } from "vitest"; import { createEmptyPluginRegistry } from "../../../src/plugins/registry.js"; import { setActivePluginRegistry } from "../../../src/plugins/runtime.js"; -import { createMockServerResponse } from "../../../src/test-utils/mock-http-response.js"; +import { createMockServerResponse } from "../../test-utils/mock-http-response.js"; import type { ResolvedGoogleChatAccount } from "./accounts.js"; import { verifyGoogleChatRequest } from "./auth.js"; import { handleGoogleChatWebhookRequest, registerGoogleChatWebhookTarget } from "./monitor.js"; diff --git a/extensions/imessage/index.ts b/extensions/imessage/index.ts index e87d421cf2e..aea014f06d4 100644 --- a/extensions/imessage/index.ts +++ b/extensions/imessage/index.ts @@ -1,17 +1,11 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { imessagePlugin } from "./src/channel.js"; import { setIMessageRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "imessage", name: "iMessage", description: "iMessage channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setIMessageRuntime(api.runtime); - api.registerChannel({ plugin: imessagePlugin }); - }, -}; - -export default plugin; + plugin: imessagePlugin, + setRuntime: setIMessageRuntime, +}); diff --git a/extensions/imessage/setup-entry.ts b/extensions/imessage/setup-entry.ts index 6b4c642d0ae..ed6936ca387 100644 --- a/extensions/imessage/setup-entry.ts +++ b/extensions/imessage/setup-entry.ts @@ -1,3 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { imessageSetupPlugin } from "./src/channel.setup.js"; -export default { plugin: imessageSetupPlugin }; +export default defineSetupPluginEntry(imessageSetupPlugin); diff --git a/extensions/irc/index.ts b/extensions/irc/index.ts index 40182558dcb..5ae8619812d 100644 --- a/extensions/irc/index.ts +++ b/extensions/irc/index.ts @@ -1,17 +1,12 @@ -import type { ChannelPlugin, OpenClawPluginApi } from "openclaw/plugin-sdk/irc"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/irc"; +import type { ChannelPlugin } from "openclaw/plugin-sdk/core"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { ircPlugin } from "./src/channel.js"; import { setIrcRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "irc", name: "IRC", description: "IRC channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setIrcRuntime(api.runtime); - api.registerChannel({ plugin: ircPlugin as ChannelPlugin }); - }, -}; - -export default plugin; + plugin: ircPlugin as ChannelPlugin, + setRuntime: setIrcRuntime, +}); diff --git a/extensions/irc/setup-entry.ts b/extensions/irc/setup-entry.ts index fe8bea1814d..3d3d040990c 100644 --- a/extensions/irc/setup-entry.ts +++ b/extensions/irc/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { ircPlugin } from "./src/channel.js"; -export default { - plugin: ircPlugin, -}; +export default defineSetupPluginEntry(ircPlugin); diff --git a/extensions/line/index.ts b/extensions/line/index.ts index 59b1d97920d..fabf1c9d5b7 100644 --- a/extensions/line/index.ts +++ b/extensions/line/index.ts @@ -1,22 +1,13 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/line"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/line"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { registerLineCardCommand } from "./src/card-command.js"; import { linePlugin } from "./src/channel.js"; import { setLineRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "line", name: "LINE", description: "LINE Messaging API channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setLineRuntime(api.runtime); - api.registerChannel({ plugin: linePlugin }); - if (api.registrationMode !== "full") { - return; - } - registerLineCardCommand(api); - }, -}; - -export default plugin; + plugin: linePlugin, + setRuntime: setLineRuntime, + registerFull: registerLineCardCommand, +}); diff --git a/extensions/line/setup-entry.ts b/extensions/line/setup-entry.ts index ca25d243155..97ed5fa30c6 100644 --- a/extensions/line/setup-entry.ts +++ b/extensions/line/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { lineSetupPlugin } from "./src/channel.setup.js"; -export default { - plugin: lineSetupPlugin, -}; +export default defineSetupPluginEntry(lineSetupPlugin); diff --git a/extensions/matrix/index.ts b/extensions/matrix/index.ts index 46a4ba5864f..5400a9b94c6 100644 --- a/extensions/matrix/index.ts +++ b/extensions/matrix/index.ts @@ -1,17 +1,11 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/matrix"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/matrix"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { matrixPlugin } from "./src/channel.js"; import { setMatrixRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "matrix", name: "Matrix", - description: "Matrix channel plugin (matrix-js-sdk)", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setMatrixRuntime(api.runtime); - api.registerChannel({ plugin: matrixPlugin }); - }, -}; - -export default plugin; + description: "Matrix channel plugin", + plugin: matrixPlugin, + setRuntime: setMatrixRuntime, +}); diff --git a/extensions/matrix/setup-entry.ts b/extensions/matrix/setup-entry.ts index 4cbabfe6333..045b3a58917 100644 --- a/extensions/matrix/setup-entry.ts +++ b/extensions/matrix/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { matrixPlugin } from "./src/channel.js"; -export default { - plugin: matrixPlugin, -}; +export default defineSetupPluginEntry(matrixPlugin); diff --git a/extensions/mattermost/index.ts b/extensions/mattermost/index.ts index de6f4e1d8a0..f5086aba465 100644 --- a/extensions/mattermost/index.ts +++ b/extensions/mattermost/index.ts @@ -1,26 +1,17 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/mattermost"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/mattermost"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { mattermostPlugin } from "./src/channel.js"; -import { getSlashCommandState, registerSlashCommandRoute } from "./src/mattermost/slash-state.js"; +import { registerSlashCommandRoute } from "./src/mattermost/slash-state.js"; import { setMattermostRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "mattermost", name: "Mattermost", description: "Mattermost channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setMattermostRuntime(api.runtime); - api.registerChannel({ plugin: mattermostPlugin }); - if (api.registrationMode !== "full") { - return; - } - - // Register the HTTP route for slash command callbacks. - // The actual command registration with MM happens in the monitor - // after the bot connects and we know the team ID. + plugin: mattermostPlugin, + setRuntime: setMattermostRuntime, + registerFull(api) { + // Actual slash-command registration happens after the monitor connects and + // knows the team id; the route itself can be wired here. registerSlashCommandRoute(api); }, -}; - -export default plugin; +}); diff --git a/extensions/mattermost/setup-entry.ts b/extensions/mattermost/setup-entry.ts index 64c02fcbe9d..34ce40972e4 100644 --- a/extensions/mattermost/setup-entry.ts +++ b/extensions/mattermost/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { mattermostPlugin } from "./src/channel.js"; -export default { - plugin: mattermostPlugin, -}; +export default defineSetupPluginEntry(mattermostPlugin); diff --git a/extensions/msteams/index.ts b/extensions/msteams/index.ts index 725ad40dfdf..c190ea49224 100644 --- a/extensions/msteams/index.ts +++ b/extensions/msteams/index.ts @@ -1,17 +1,11 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/msteams"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/msteams"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { msteamsPlugin } from "./src/channel.js"; import { setMSTeamsRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "msteams", name: "Microsoft Teams", description: "Microsoft Teams channel plugin (Bot Framework)", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setMSTeamsRuntime(api.runtime); - api.registerChannel({ plugin: msteamsPlugin }); - }, -}; - -export default plugin; + plugin: msteamsPlugin, + setRuntime: setMSTeamsRuntime, +}); diff --git a/extensions/msteams/setup-entry.ts b/extensions/msteams/setup-entry.ts index fb850b60e18..6e29414c82e 100644 --- a/extensions/msteams/setup-entry.ts +++ b/extensions/msteams/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { msteamsPlugin } from "./src/channel.js"; -export default { - plugin: msteamsPlugin, -}; +export default defineSetupPluginEntry(msteamsPlugin); diff --git a/extensions/msteams/src/graph-upload.test.ts b/extensions/msteams/src/graph-upload.test.ts index b79086f54ca..90a9da1d352 100644 --- a/extensions/msteams/src/graph-upload.test.ts +++ b/extensions/msteams/src/graph-upload.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from "vitest"; -import { withFetchPreconnect } from "../../../src/test-utils/fetch-mock.js"; +import { withFetchPreconnect } from "../../test-utils/fetch-mock.js"; import { uploadToOneDrive, uploadToSharePoint } from "./graph-upload.js"; describe("graph upload helpers", () => { diff --git a/extensions/nextcloud-talk/index.ts b/extensions/nextcloud-talk/index.ts index 697a810009f..2057bd435e8 100644 --- a/extensions/nextcloud-talk/index.ts +++ b/extensions/nextcloud-talk/index.ts @@ -1,17 +1,11 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/nextcloud-talk"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/nextcloud-talk"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { nextcloudTalkPlugin } from "./src/channel.js"; import { setNextcloudTalkRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "nextcloud-talk", name: "Nextcloud Talk", description: "Nextcloud Talk channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setNextcloudTalkRuntime(api.runtime); - api.registerChannel({ plugin: nextcloudTalkPlugin }); - }, -}; - -export default plugin; + plugin: nextcloudTalkPlugin, + setRuntime: setNextcloudTalkRuntime, +}); diff --git a/extensions/nextcloud-talk/setup-entry.ts b/extensions/nextcloud-talk/setup-entry.ts index f33df37c7dc..88aec7d47e9 100644 --- a/extensions/nextcloud-talk/setup-entry.ts +++ b/extensions/nextcloud-talk/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { nextcloudTalkPlugin } from "./src/channel.js"; -export default { - plugin: nextcloudTalkPlugin, -}; +export default defineSetupPluginEntry(nextcloudTalkPlugin); diff --git a/extensions/nostr/index.ts b/extensions/nostr/index.ts index d8fdb203924..cdabf64c322 100644 --- a/extensions/nostr/index.ts +++ b/extensions/nostr/index.ts @@ -1,24 +1,17 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/nostr"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/nostr"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { nostrPlugin } from "./src/channel.js"; import type { NostrProfile } from "./src/config-schema.js"; import { createNostrProfileHttpHandler } from "./src/nostr-profile-http.js"; -import { setNostrRuntime, getNostrRuntime } from "./src/runtime.js"; +import { getNostrRuntime, setNostrRuntime } from "./src/runtime.js"; import { resolveNostrAccount } from "./src/types.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "nostr", name: "Nostr", description: "Nostr DM channel plugin via NIP-04", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setNostrRuntime(api.runtime); - api.registerChannel({ plugin: nostrPlugin }); - if (api.registrationMode !== "full") { - return; - } - - // Register HTTP handler for profile management + plugin: nostrPlugin, + setRuntime: setNostrRuntime, + registerFull(api) { const httpHandler = createNostrProfileHttpHandler({ getConfigProfile: (accountId: string) => { const runtime = getNostrRuntime(); @@ -30,23 +23,18 @@ const plugin = { const runtime = getNostrRuntime(); const cfg = runtime.config.loadConfig(); - // Build the config patch for channels.nostr.profile const channels = (cfg.channels ?? {}) as Record; const nostrConfig = (channels.nostr ?? {}) as Record; - const updatedNostrConfig = { - ...nostrConfig, - profile, - }; - - const updatedChannels = { - ...channels, - nostr: updatedNostrConfig, - }; - await runtime.config.writeConfigFile({ ...cfg, - channels: updatedChannels, + channels: { + ...channels, + nostr: { + ...nostrConfig, + profile, + }, + }, }); }, getAccountInfo: (accountId: string) => { @@ -71,6 +59,4 @@ const plugin = { handler: httpHandler, }); }, -}; - -export default plugin; +}); diff --git a/extensions/nostr/setup-entry.ts b/extensions/nostr/setup-entry.ts index 8884a71cc80..f2ac263fd0f 100644 --- a/extensions/nostr/setup-entry.ts +++ b/extensions/nostr/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { nostrPlugin } from "./src/channel.js"; -export default { - plugin: nostrPlugin, -}; +export default defineSetupPluginEntry(nostrPlugin); diff --git a/extensions/signal/index.ts b/extensions/signal/index.ts index 0a686851120..6b20777f842 100644 --- a/extensions/signal/index.ts +++ b/extensions/signal/index.ts @@ -1,17 +1,11 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { signalPlugin } from "./src/channel.js"; import { setSignalRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "signal", name: "Signal", description: "Signal channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setSignalRuntime(api.runtime); - api.registerChannel({ plugin: signalPlugin }); - }, -}; - -export default plugin; + plugin: signalPlugin, + setRuntime: setSignalRuntime, +}); diff --git a/extensions/signal/setup-entry.ts b/extensions/signal/setup-entry.ts index 18c27ec5a16..63f6d95e8fc 100644 --- a/extensions/signal/setup-entry.ts +++ b/extensions/signal/setup-entry.ts @@ -1,3 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { signalSetupPlugin } from "./src/channel.setup.js"; -export default { plugin: signalSetupPlugin }; +export default defineSetupPluginEntry(signalSetupPlugin); diff --git a/extensions/slack/index.ts b/extensions/slack/index.ts index f1147cb9c91..44abfa36b0d 100644 --- a/extensions/slack/index.ts +++ b/extensions/slack/index.ts @@ -1,17 +1,11 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { slackPlugin } from "./src/channel.js"; import { setSlackRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "slack", name: "Slack", description: "Slack channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setSlackRuntime(api.runtime); - api.registerChannel({ plugin: slackPlugin }); - }, -}; - -export default plugin; + plugin: slackPlugin, + setRuntime: setSlackRuntime, +}); diff --git a/extensions/slack/setup-entry.ts b/extensions/slack/setup-entry.ts index 1bd6eabde59..5a80ca2128b 100644 --- a/extensions/slack/setup-entry.ts +++ b/extensions/slack/setup-entry.ts @@ -1,3 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { slackSetupPlugin } from "./src/channel.setup.js"; -export default { plugin: slackSetupPlugin }; +export default defineSetupPluginEntry(slackSetupPlugin); diff --git a/extensions/slack/src/monitor/media.test.ts b/extensions/slack/src/monitor/media.test.ts index f745f205950..9d5114e2961 100644 --- a/extensions/slack/src/monitor/media.test.ts +++ b/extensions/slack/src/monitor/media.test.ts @@ -4,7 +4,7 @@ import * as mediaFetch from "../../../../src/media/fetch.js"; import type { SavedMedia } from "../../../../src/media/store.js"; import * as mediaStore from "../../../../src/media/store.js"; import { mockPinnedHostnameResolution } from "../../../../src/test-helpers/ssrf.js"; -import { type FetchMock, withFetchPreconnect } from "../../../../src/test-utils/fetch-mock.js"; +import { type FetchMock, withFetchPreconnect } from "../../../test-utils/fetch-mock.js"; import { fetchWithSlackAuth, resolveSlackAttachmentContent, diff --git a/extensions/synology-chat/index.ts b/extensions/synology-chat/index.ts index 9078b9f86c7..79e3f49d513 100644 --- a/extensions/synology-chat/index.ts +++ b/extensions/synology-chat/index.ts @@ -1,17 +1,11 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/synology-chat"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/synology-chat"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { synologyChatPlugin } from "./src/channel.js"; import { setSynologyRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "synology-chat", name: "Synology Chat", description: "Native Synology Chat channel plugin for OpenClaw", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setSynologyRuntime(api.runtime); - api.registerChannel({ plugin: synologyChatPlugin }); - }, -}; - -export default plugin; + plugin: synologyChatPlugin, + setRuntime: setSynologyRuntime, +}); diff --git a/extensions/synology-chat/setup-entry.ts b/extensions/synology-chat/setup-entry.ts index 45cc966e082..858696710a8 100644 --- a/extensions/synology-chat/setup-entry.ts +++ b/extensions/synology-chat/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { synologyChatPlugin } from "./src/channel.js"; -export default { - plugin: synologyChatPlugin, -}; +export default defineSetupPluginEntry(synologyChatPlugin); diff --git a/extensions/talk-voice/index.test.ts b/extensions/talk-voice/index.test.ts index 2d0a991aa47..15876987554 100644 --- a/extensions/talk-voice/index.test.ts +++ b/extensions/talk-voice/index.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from "vitest"; -import type { OpenClawPluginCommandDefinition } from "../../src/plugins/types.js"; +import type { OpenClawPluginCommandDefinition } from "../test-utils/plugin-command.js"; import { createPluginRuntimeMock } from "../test-utils/plugin-runtime-mock.js"; import register from "./index.js"; diff --git a/extensions/telegram/index.ts b/extensions/telegram/index.ts index d47ae46b6ce..89413373c5a 100644 --- a/extensions/telegram/index.ts +++ b/extensions/telegram/index.ts @@ -1,17 +1,12 @@ -import type { ChannelPlugin, OpenClawPluginApi } from "openclaw/plugin-sdk/core"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core"; +import type { ChannelPlugin } from "openclaw/plugin-sdk/core"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { telegramPlugin } from "./src/channel.js"; import { setTelegramRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "telegram", name: "Telegram", description: "Telegram channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setTelegramRuntime(api.runtime); - api.registerChannel({ plugin: telegramPlugin as ChannelPlugin }); - }, -}; - -export default plugin; + plugin: telegramPlugin as ChannelPlugin, + setRuntime: setTelegramRuntime, +}); diff --git a/extensions/telegram/setup-entry.ts b/extensions/telegram/setup-entry.ts index 030f4bb3295..c44a073e80b 100644 --- a/extensions/telegram/setup-entry.ts +++ b/extensions/telegram/setup-entry.ts @@ -1,3 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { telegramSetupPlugin } from "./src/channel.setup.js"; -export default { plugin: telegramSetupPlugin }; +export default defineSetupPluginEntry(telegramSetupPlugin); diff --git a/extensions/telegram/src/account-inspect.test.ts b/extensions/telegram/src/account-inspect.test.ts index 5e58626ba03..54915edb61c 100644 --- a/extensions/telegram/src/account-inspect.test.ts +++ b/extensions/telegram/src/account-inspect.test.ts @@ -3,7 +3,7 @@ import os from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../../../src/config/config.js"; -import { withEnv } from "../../../src/test-utils/env.js"; +import { withEnv } from "../../test-utils/env.js"; import { inspectTelegramAccount } from "./account-inspect.js"; describe("inspectTelegramAccount SecretRef resolution", () => { diff --git a/extensions/telegram/src/accounts.test.ts b/extensions/telegram/src/accounts.test.ts index fb83b9071a5..6155b89d0af 100644 --- a/extensions/telegram/src/accounts.test.ts +++ b/extensions/telegram/src/accounts.test.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../../src/config/config.js"; import * as subsystemModule from "../../../src/logging/subsystem.js"; -import { withEnv } from "../../../src/test-utils/env.js"; +import { withEnv } from "../../test-utils/env.js"; import { listTelegramAccountIds, resetMissingDefaultWarnFlag, diff --git a/extensions/telegram/src/bot.create-telegram-bot.test.ts b/extensions/telegram/src/bot.create-telegram-bot.test.ts index d3854849b10..3390aa3ff24 100644 --- a/extensions/telegram/src/bot.create-telegram-bot.test.ts +++ b/extensions/telegram/src/bot.create-telegram-bot.test.ts @@ -2,9 +2,9 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; -import { withEnvAsync } from "../../../src/test-utils/env.js"; -import { useFrozenTime, useRealTime } from "../../../src/test-utils/frozen-time.js"; import { escapeRegExp, formatEnvelopeTimestamp } from "../../../test/helpers/envelope-timestamp.js"; +import { withEnvAsync } from "../../test-utils/env.js"; +import { useFrozenTime, useRealTime } from "../../test-utils/frozen-time.js"; import { answerCallbackQuerySpy, botCtorSpy, diff --git a/extensions/telegram/src/bot.test.ts b/extensions/telegram/src/bot.test.ts index 17f6870a964..3266c080254 100644 --- a/extensions/telegram/src/bot.test.ts +++ b/extensions/telegram/src/bot.test.ts @@ -1,11 +1,11 @@ import { rm } from "node:fs/promises"; +import type { PluginInteractiveTelegramHandlerContext } from "openclaw/plugin-sdk/core"; import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { expectChannelInboundContextContract as expectInboundContextContract } from "../../../src/channels/plugins/contracts/suites.js"; import { clearPluginInteractiveHandlers, registerPluginInteractiveHandler, } from "../../../src/plugins/interactive.js"; -import type { PluginInteractiveTelegramHandlerContext } from "../../../src/plugins/types.js"; import { escapeRegExp, formatEnvelopeTimestamp } from "../../../test/helpers/envelope-timestamp.js"; import { answerCallbackQuerySpy, diff --git a/extensions/telegram/src/probe.test.ts b/extensions/telegram/src/probe.test.ts index 23a2051cfa0..970e2559540 100644 --- a/extensions/telegram/src/probe.test.ts +++ b/extensions/telegram/src/probe.test.ts @@ -1,5 +1,5 @@ import { afterEach, type Mock, describe, expect, it, vi } from "vitest"; -import { withFetchPreconnect } from "../../../src/test-utils/fetch-mock.js"; +import { withFetchPreconnect } from "../../test-utils/fetch-mock.js"; import { probeTelegram, resetTelegramProbeFetcherCacheForTests } from "./probe.js"; const resolveTelegramFetch = vi.hoisted(() => vi.fn()); diff --git a/extensions/test-utils/chunk-test-helpers.ts b/extensions/test-utils/chunk-test-helpers.ts new file mode 100644 index 00000000000..643e28e5c24 --- /dev/null +++ b/extensions/test-utils/chunk-test-helpers.ts @@ -0,0 +1 @@ +export { countLines, hasBalancedFences } from "../../src/test-utils/chunk-test-helpers.js"; diff --git a/extensions/test-utils/env.ts b/extensions/test-utils/env.ts new file mode 100644 index 00000000000..b171aa55a6c --- /dev/null +++ b/extensions/test-utils/env.ts @@ -0,0 +1 @@ +export { captureEnv, withEnv, withEnvAsync } from "../../src/test-utils/env.js"; diff --git a/extensions/test-utils/fetch-mock.ts b/extensions/test-utils/fetch-mock.ts new file mode 100644 index 00000000000..2cd6b65e680 --- /dev/null +++ b/extensions/test-utils/fetch-mock.ts @@ -0,0 +1 @@ +export { withFetchPreconnect, type FetchMock } from "../../src/test-utils/fetch-mock.js"; diff --git a/extensions/test-utils/frozen-time.ts b/extensions/test-utils/frozen-time.ts new file mode 100644 index 00000000000..ec31962fb76 --- /dev/null +++ b/extensions/test-utils/frozen-time.ts @@ -0,0 +1 @@ +export { useFrozenTime, useRealTime } from "../../src/test-utils/frozen-time.js"; diff --git a/extensions/test-utils/mock-http-response.ts b/extensions/test-utils/mock-http-response.ts new file mode 100644 index 00000000000..bf0d8bef20c --- /dev/null +++ b/extensions/test-utils/mock-http-response.ts @@ -0,0 +1 @@ +export { createMockServerResponse } from "../../src/test-utils/mock-http-response.js"; diff --git a/extensions/test-utils/plugin-command.ts b/extensions/test-utils/plugin-command.ts new file mode 100644 index 00000000000..3b6f3aad50f --- /dev/null +++ b/extensions/test-utils/plugin-command.ts @@ -0,0 +1 @@ +export type { OpenClawPluginCommandDefinition } from "openclaw/plugin-sdk/core"; diff --git a/extensions/test-utils/plugin-registration.ts b/extensions/test-utils/plugin-registration.ts new file mode 100644 index 00000000000..7a7da8ecdad --- /dev/null +++ b/extensions/test-utils/plugin-registration.ts @@ -0,0 +1 @@ +export { registerSingleProviderPlugin } from "../../src/test-utils/plugin-registration.js"; diff --git a/extensions/test-utils/provider-usage-fetch.ts b/extensions/test-utils/provider-usage-fetch.ts new file mode 100644 index 00000000000..d70a6e1657a --- /dev/null +++ b/extensions/test-utils/provider-usage-fetch.ts @@ -0,0 +1,4 @@ +export { + createProviderUsageFetch, + makeResponse, +} from "../../src/test-utils/provider-usage-fetch.js"; diff --git a/extensions/test-utils/temp-dir.ts b/extensions/test-utils/temp-dir.ts new file mode 100644 index 00000000000..3bd69bcc7b9 --- /dev/null +++ b/extensions/test-utils/temp-dir.ts @@ -0,0 +1 @@ +export { withTempDir } from "../../src/test-utils/temp-dir.js"; diff --git a/extensions/test-utils/typed-cases.ts b/extensions/test-utils/typed-cases.ts new file mode 100644 index 00000000000..4b6bd35b1ec --- /dev/null +++ b/extensions/test-utils/typed-cases.ts @@ -0,0 +1 @@ +export { typedCases } from "../../src/test-utils/typed-cases.js"; diff --git a/extensions/tlon/index.ts b/extensions/tlon/index.ts index 2927a9a4b53..9ae569fea03 100644 --- a/extensions/tlon/index.ts +++ b/extensions/tlon/index.ts @@ -2,14 +2,12 @@ import { spawn } from "node:child_process"; import { existsSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/tlon"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/tlon"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { tlonPlugin } from "./src/channel.js"; import { setTlonRuntime } from "./src/runtime.js"; const __dirname = dirname(fileURLToPath(import.meta.url)); -// Whitelist of allowed tlon subcommands const ALLOWED_TLON_COMMANDS = new Set([ "activity", "channels", @@ -24,40 +22,29 @@ const ALLOWED_TLON_COMMANDS = new Set([ "version", ]); -/** - * Find the tlon binary from the skill package - */ let cachedTlonBinary: string | undefined; function findTlonBinary(): string { if (cachedTlonBinary) { return cachedTlonBinary; } - // Check in node_modules/.bin const skillBin = join(__dirname, "node_modules", ".bin", "tlon"); if (existsSync(skillBin)) { cachedTlonBinary = skillBin; return skillBin; } - // Check for platform-specific binary directly - const platform = process.platform; - const arch = process.arch; - const platformPkg = `@tloncorp/tlon-skill-${platform}-${arch}`; + const platformPkg = `@tloncorp/tlon-skill-${process.platform}-${process.arch}`; const platformBin = join(__dirname, "node_modules", platformPkg, "tlon"); if (existsSync(platformBin)) { cachedTlonBinary = platformBin; return platformBin; } - // Fallback to PATH cachedTlonBinary = "tlon"; return cachedTlonBinary; } -/** - * Shell-like argument splitter that respects quotes - */ function shellSplit(str: string): string[] { const args: string[] = []; let cur = ""; @@ -92,18 +79,15 @@ function shellSplit(str: string): string[] { } cur += ch; } - if (cur) args.push(cur); + if (cur) { + args.push(cur); + } return args; } -/** - * Run the tlon command and return the result - */ function runTlonCommand(binary: string, args: string[]): Promise { return new Promise((resolve, reject) => { - const child = spawn(binary, args, { - env: process.env, - }); + const child = spawn(binary, args, { env: process.env }); let stdout = ""; let stderr = ""; @@ -123,25 +107,20 @@ function runTlonCommand(binary: string, args: string[]): Promise { child.on("close", (code) => { if (code !== 0) { reject(new Error(stderr || `tlon exited with code ${code}`)); - } else { - resolve(stdout); + return; } + resolve(stdout); }); }); } -const plugin = { +export default defineChannelPluginEntry({ id: "tlon", name: "Tlon", description: "Tlon/Urbit channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setTlonRuntime(api.runtime); - api.registerChannel({ plugin: tlonPlugin }); - if (api.registrationMode !== "full") { - return; - } - + plugin: tlonPlugin, + setRuntime: setTlonRuntime, + registerFull(api) { api.logger.debug?.("[tlon] Registering tlon tool"); api.registerTool({ name: "tlon", @@ -164,9 +143,6 @@ const plugin = { async execute(_id: string, params: { command: string }) { try { const args = shellSplit(params.command); - const tlonBinary = findTlonBinary(); - - // Validate first argument is a whitelisted tlon subcommand const subcommand = args[0]; if (!ALLOWED_TLON_COMMANDS.has(subcommand)) { return { @@ -180,7 +156,7 @@ const plugin = { }; } - const output = await runTlonCommand(tlonBinary, args); + const output = await runTlonCommand(findTlonBinary(), args); return { content: [{ type: "text" as const, text: output }], details: undefined, @@ -194,6 +170,4 @@ const plugin = { }, }); }, -}; - -export default plugin; +}); diff --git a/extensions/tlon/setup-entry.ts b/extensions/tlon/setup-entry.ts index 667e917c8da..6a14ba3bade 100644 --- a/extensions/tlon/setup-entry.ts +++ b/extensions/tlon/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { tlonPlugin } from "./src/channel.js"; -export default { - plugin: tlonPlugin, -}; +export default defineSetupPluginEntry(tlonPlugin); diff --git a/extensions/twitch/index.ts b/extensions/twitch/index.ts index cbdb20bff4d..1a4ea89185c 100644 --- a/extensions/twitch/index.ts +++ b/extensions/twitch/index.ts @@ -1,20 +1,13 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/twitch"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/twitch"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { twitchPlugin } from "./src/plugin.js"; import { setTwitchRuntime } from "./src/runtime.js"; export { monitorTwitchProvider } from "./src/monitor.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "twitch", name: "Twitch", - description: "Twitch channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setTwitchRuntime(api.runtime); - // oxlint-disable-next-line typescript/no-explicit-any - api.registerChannel({ plugin: twitchPlugin as any }); - }, -}; - -export default plugin; + description: "Twitch chat channel plugin", + plugin: twitchPlugin, + setRuntime: setTwitchRuntime, +}); diff --git a/extensions/whatsapp/index.ts b/extensions/whatsapp/index.ts index c0f097ddf7d..da16917fa43 100644 --- a/extensions/whatsapp/index.ts +++ b/extensions/whatsapp/index.ts @@ -1,17 +1,11 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { whatsappPlugin } from "./src/channel.js"; import { setWhatsAppRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "whatsapp", name: "WhatsApp", description: "WhatsApp channel plugin", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setWhatsAppRuntime(api.runtime); - api.registerChannel({ plugin: whatsappPlugin }); - }, -}; - -export default plugin; + plugin: whatsappPlugin, + setRuntime: setWhatsAppRuntime, +}); diff --git a/extensions/whatsapp/setup-entry.ts b/extensions/whatsapp/setup-entry.ts index 5b18e10073b..a01efecdc36 100644 --- a/extensions/whatsapp/setup-entry.ts +++ b/extensions/whatsapp/setup-entry.ts @@ -1,3 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { whatsappSetupPlugin } from "./src/channel.setup.js"; -export default { plugin: whatsappSetupPlugin }; +export default defineSetupPluginEntry(whatsappSetupPlugin); diff --git a/extensions/whatsapp/src/accounts.whatsapp-auth.test.ts b/extensions/whatsapp/src/accounts.whatsapp-auth.test.ts index 349bccc65e5..43d1739e13f 100644 --- a/extensions/whatsapp/src/accounts.whatsapp-auth.test.ts +++ b/extensions/whatsapp/src/accounts.whatsapp-auth.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { captureEnv } from "../../../src/test-utils/env.js"; +import { captureEnv } from "../../test-utils/env.js"; import { hasAnyWhatsAppAuth, listWhatsAppAuthDirs } from "./accounts.js"; describe("hasAnyWhatsAppAuth", () => { diff --git a/extensions/whatsapp/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts b/extensions/whatsapp/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts index dd324f47351..6a5184fc059 100644 --- a/extensions/whatsapp/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts +++ b/extensions/whatsapp/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts @@ -4,8 +4,8 @@ import fs from "node:fs/promises"; import { beforeAll, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../../src/config/config.js"; import { setLoggerOverride } from "../../../src/logging.js"; -import { withEnvAsync } from "../../../src/test-utils/env.js"; import { escapeRegExp, formatEnvelopeTimestamp } from "../../../test/helpers/envelope-timestamp.js"; +import { withEnvAsync } from "../../test-utils/env.js"; import { createMockWebListener, createWebListenerFactoryCapture, diff --git a/extensions/whatsapp/src/auto-reply/web-auto-reply-utils.test.ts b/extensions/whatsapp/src/auto-reply/web-auto-reply-utils.test.ts index 0107fa126d7..d1011f5c7f8 100644 --- a/extensions/whatsapp/src/auto-reply/web-auto-reply-utils.test.ts +++ b/extensions/whatsapp/src/auto-reply/web-auto-reply-utils.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import { saveSessionStore } from "../../../../src/config/sessions.js"; -import { withTempDir } from "../../../../src/test-utils/temp-dir.js"; +import { withTempDir } from "../../../test-utils/temp-dir.js"; import { debugMention, isBotMentionedFromTargets, diff --git a/extensions/whatsapp/src/media.test.ts b/extensions/whatsapp/src/media.test.ts index e21d58b4bb7..45f3fbae309 100644 --- a/extensions/whatsapp/src/media.test.ts +++ b/extensions/whatsapp/src/media.test.ts @@ -7,8 +7,8 @@ import { resolveStateDir } from "../../../src/config/paths.js"; import { resolvePreferredOpenClawTmpDir } from "../../../src/infra/tmp-openclaw-dir.js"; import { optimizeImageToPng } from "../../../src/media/image-ops.js"; import { mockPinnedHostnameResolution } from "../../../src/test-helpers/ssrf.js"; -import { captureEnv } from "../../../src/test-utils/env.js"; import { sendVoiceMessageDiscord } from "../../discord/src/send.js"; +import { captureEnv } from "../../test-utils/env.js"; import { LocalMediaAccessError, loadWebMedia, diff --git a/extensions/zalo/index.ts b/extensions/zalo/index.ts index ef62ee6e560..c5091444450 100644 --- a/extensions/zalo/index.ts +++ b/extensions/zalo/index.ts @@ -1,17 +1,11 @@ -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/zalo"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/zalo"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; import { zaloPlugin } from "./src/channel.js"; import { setZaloRuntime } from "./src/runtime.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "zalo", name: "Zalo", - description: "Zalo channel plugin (Bot API)", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setZaloRuntime(api.runtime); - api.registerChannel(zaloPlugin); - }, -}; - -export default plugin; + description: "Zalo channel plugin", + plugin: zaloPlugin, + setRuntime: setZaloRuntime, +}); diff --git a/extensions/zalo/setup-entry.ts b/extensions/zalo/setup-entry.ts index dd8ca1b70f8..d26b0f93fe0 100644 --- a/extensions/zalo/setup-entry.ts +++ b/extensions/zalo/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { zaloPlugin } from "./src/channel.js"; -export default { - plugin: zaloPlugin, -}; +export default defineSetupPluginEntry(zaloPlugin); diff --git a/extensions/zalouser/index.ts b/extensions/zalouser/index.ts index 8d470b043e3..2199567cff8 100644 --- a/extensions/zalouser/index.ts +++ b/extensions/zalouser/index.ts @@ -1,21 +1,16 @@ -import type { AnyAgentTool, OpenClawPluginApi } from "openclaw/plugin-sdk/zalouser"; -import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/zalouser"; +import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core"; +import type { AnyAgentTool } from "openclaw/plugin-sdk/zalouser"; import { zalouserPlugin } from "./src/channel.js"; import { setZalouserRuntime } from "./src/runtime.js"; import { ZalouserToolSchema, executeZalouserTool } from "./src/tool.js"; -const plugin = { +export default defineChannelPluginEntry({ id: "zalouser", name: "Zalo Personal", description: "Zalo personal account messaging via native zca-js integration", - configSchema: emptyPluginConfigSchema(), - register(api: OpenClawPluginApi) { - setZalouserRuntime(api.runtime); - api.registerChannel(zalouserPlugin); - if (api.registrationMode !== "full") { - return; - } - + plugin: zalouserPlugin, + setRuntime: setZalouserRuntime, + registerFull(api) { api.registerTool({ name: "zalouser", label: "Zalo Personal", @@ -27,6 +22,4 @@ const plugin = { execute: executeZalouserTool, } as AnyAgentTool); }, -}; - -export default plugin; +}); diff --git a/extensions/zalouser/setup-entry.ts b/extensions/zalouser/setup-entry.ts index f983cad8f80..0320d3cf945 100644 --- a/extensions/zalouser/setup-entry.ts +++ b/extensions/zalouser/setup-entry.ts @@ -1,5 +1,4 @@ +import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core"; import { zalouserPlugin } from "./src/channel.js"; -export default { - plugin: zalouserPlugin, -}; +export default defineSetupPluginEntry(zalouserPlugin); diff --git a/package.json b/package.json index 4bb825d0d7a..08acac5db40 100644 --- a/package.json +++ b/package.json @@ -443,7 +443,7 @@ "build:plugin-sdk:dts": "tsc -p tsconfig.plugin-sdk.dts.json || true", "build:strict-smoke": "pnpm canvas:a2ui:bundle && node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && pnpm build:plugin-sdk:dts", "canvas:a2ui:bundle": "bash scripts/bundle-a2ui.sh", - "check": "pnpm check:host-env-policy:swift && pnpm format:check && pnpm tsgo && pnpm plugin-sdk:check-exports && pnpm lint && pnpm lint:tmp:no-random-messaging && pnpm lint:tmp:channel-agnostic-boundaries && pnpm lint:tmp:no-raw-channel-fetch && pnpm lint:agent:ingress-owner && pnpm lint:plugins:no-register-http-handler && pnpm lint:plugins:no-monolithic-plugin-sdk-entry-imports && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scope", + "check": "pnpm check:host-env-policy:swift && pnpm format:check && pnpm tsgo && pnpm plugin-sdk:check-exports && pnpm lint && pnpm lint:tmp:no-random-messaging && pnpm lint:tmp:channel-agnostic-boundaries && pnpm lint:tmp:no-raw-channel-fetch && pnpm lint:agent:ingress-owner && pnpm lint:plugins:no-register-http-handler && pnpm lint:plugins:no-monolithic-plugin-sdk-entry-imports && pnpm lint:plugins:no-extension-test-core-imports && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scope", "check:docs": "pnpm format:docs:check && pnpm lint:docs && pnpm docs:check-i18n-glossary && pnpm docs:check-links", "check:host-env-policy:swift": "node scripts/generate-host-env-security-policy-swift.mjs --check", "check:loc": "node --import tsx scripts/check-ts-max-loc.ts --max 500", @@ -495,6 +495,7 @@ "lint:docs": "pnpm dlx markdownlint-cli2", "lint:docs:fix": "pnpm dlx markdownlint-cli2 --fix", "lint:fix": "oxlint --type-aware --fix && pnpm format", + "lint:plugins:no-extension-test-core-imports": "node --import tsx scripts/check-no-extension-test-core-imports.ts", "lint:plugins:no-monolithic-plugin-sdk-entry-imports": "node --import tsx scripts/check-no-monolithic-plugin-sdk-entry-imports.ts", "lint:plugins:no-register-http-handler": "node scripts/check-no-register-http-handler.mjs", "lint:swift": "swiftlint lint --config .swiftlint.yml && (cd apps/ios && swiftlint lint --config .swiftlint.yml)", diff --git a/scripts/check-no-extension-test-core-imports.ts b/scripts/check-no-extension-test-core-imports.ts new file mode 100644 index 00000000000..b8e3b1bc764 --- /dev/null +++ b/scripts/check-no-extension-test-core-imports.ts @@ -0,0 +1,90 @@ +import fs from "node:fs"; +import path from "node:path"; + +const FORBIDDEN_PATTERNS: Array<{ pattern: RegExp; hint: string }> = [ + { + pattern: /["']openclaw\/plugin-sdk["']/, + hint: "Use openclaw/plugin-sdk/ instead of the monolithic root entry.", + }, + { + pattern: /["']openclaw\/plugin-sdk\/compat["']/, + hint: "Use a focused public plugin-sdk subpath instead of compat.", + }, + { + pattern: /["'](?:\.\.\/)+(?:src\/test-utils\/)[^"']+["']/, + hint: "Use extensions/test-utils/* bridges for shared extension test helpers.", + }, + { + pattern: /["'](?:\.\.\/)+(?:src\/plugins\/types\.js)["']/, + hint: "Use public plugin-sdk/core types or extensions/test-utils bridges instead.", + }, +]; + +function isExtensionTestFile(filePath: string): boolean { + return /\.test\.[cm]?[jt]sx?$/u.test(filePath) || /\.e2e\.test\.[cm]?[jt]sx?$/u.test(filePath); +} + +function collectExtensionTestFiles(rootDir: string): string[] { + const files: string[] = []; + const stack = [rootDir]; + while (stack.length > 0) { + const current = stack.pop(); + if (!current) { + continue; + } + let entries: fs.Dirent[] = []; + try { + entries = fs.readdirSync(current, { withFileTypes: true }); + } catch { + continue; + } + for (const entry of entries) { + const fullPath = path.join(current, entry.name); + if (entry.isDirectory()) { + if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "coverage") { + continue; + } + stack.push(fullPath); + continue; + } + if (entry.isFile() && isExtensionTestFile(fullPath)) { + files.push(fullPath); + } + } + } + return files; +} + +function main() { + const extensionsDir = path.join(process.cwd(), "extensions"); + const files = collectExtensionTestFiles(extensionsDir); + const offenders: Array<{ file: string; hint: string }> = []; + + for (const file of files) { + const content = fs.readFileSync(file, "utf8"); + for (const rule of FORBIDDEN_PATTERNS) { + if (!rule.pattern.test(content)) { + continue; + } + offenders.push({ file, hint: rule.hint }); + break; + } + } + + if (offenders.length > 0) { + console.error( + "Extension test files must stay on extension test bridges or public plugin-sdk seams.", + ); + for (const offender of offenders.toSorted((a, b) => a.file.localeCompare(b.file))) { + const relative = path.relative(process.cwd(), offender.file) || offender.file; + console.error(`- ${relative}: ${offender.hint}`); + } + process.exit(1); + } + + console.log( + `OK: extension test files avoid direct core test/internal imports (${files.length} checked).`, + ); +} + +main(); diff --git a/src/plugin-sdk/core.ts b/src/plugin-sdk/core.ts index a6c842e79d5..1cfea088601 100644 --- a/src/plugin-sdk/core.ts +++ b/src/plugin-sdk/core.ts @@ -1,3 +1,13 @@ +import type { ChannelPlugin } from "../channels/plugins/types.plugin.js"; +import { emptyPluginConfigSchema } from "../plugins/config-schema.js"; +import type { PluginRuntime } from "../plugins/runtime/types.js"; +import type { + OpenClawPluginApi, + OpenClawPluginCommandDefinition, + OpenClawPluginConfigSchema, + PluginInteractiveTelegramHandlerContext, +} from "../plugins/types.js"; + export type { AnyAgentTool, MediaUnderstandingProviderPlugin, @@ -31,6 +41,8 @@ export type { ProviderAuthMethodNonInteractiveContext, ProviderAuthMethod, ProviderAuthResult, + OpenClawPluginCommandDefinition, + PluginInteractiveTelegramHandlerContext, } from "../plugins/types.js"; export type { OpenClawConfig } from "../config/config.js"; export type { GatewayRequestHandlerOptions } from "../gateway/server-methods/types.js"; @@ -70,3 +82,44 @@ export { export { buildOutboundBaseSessionKey } from "../infra/outbound/base-session-key.js"; export { normalizeOutboundThreadId } from "../infra/outbound/thread-id.js"; export { resolveThreadSessionKeys } from "../routing/session-key.js"; + +type DefineChannelPluginEntryOptions = { + id: string; + name: string; + description: string; + plugin: TPlugin; + configSchema?: () => OpenClawPluginConfigSchema; + setRuntime?: (runtime: PluginRuntime) => void; + registerFull?: (api: OpenClawPluginApi) => void; +}; + +// Shared channel-plugin entry boilerplate for bundled and third-party channels. +export function defineChannelPluginEntry({ + id, + name, + description, + plugin, + configSchema = emptyPluginConfigSchema, + setRuntime, + registerFull, +}: DefineChannelPluginEntryOptions) { + return { + id, + name, + description, + configSchema: configSchema(), + register(api: OpenClawPluginApi) { + setRuntime?.(api.runtime); + api.registerChannel({ plugin }); + if (api.registrationMode !== "full") { + return; + } + registerFull?.(api); + }, + }; +} + +// Shared setup-entry shape so bundled channels do not duplicate `{ plugin }`. +export function defineSetupPluginEntry(plugin: TPlugin) { + return { plugin }; +} diff --git a/src/plugin-sdk/subpaths.test.ts b/src/plugin-sdk/subpaths.test.ts index 0166fb52081..156f7d9b81f 100644 --- a/src/plugin-sdk/subpaths.test.ts +++ b/src/plugin-sdk/subpaths.test.ts @@ -49,6 +49,8 @@ describe("plugin-sdk subpath exports", () => { it("keeps core focused on generic shared exports", () => { expect(typeof coreSdk.emptyPluginConfigSchema).toBe("function"); + expect(typeof coreSdk.defineChannelPluginEntry).toBe("function"); + expect(typeof coreSdk.defineSetupPluginEntry).toBe("function"); expect("runPassiveAccountLifecycle" in asExports(coreSdk)).toBe(false); expect("createLoggerBackedRuntime" in asExports(coreSdk)).toBe(false); expect("registerSandboxBackend" in asExports(coreSdk)).toBe(false); From c1e56978897e35b46b68d3be69e4fefd09f4b4bb Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 16 Mar 2026 23:52:41 -0700 Subject: [PATCH 2/6] style: fix rebase formatting drift --- extensions/discord/src/channel.ts | 7 ++----- extensions/slack/src/channel.ts | 5 +---- extensions/telegram/src/channel.ts | 5 +---- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/extensions/discord/src/channel.ts b/extensions/discord/src/channel.ts index 46679586665..e2d202cb6ee 100644 --- a/extensions/discord/src/channel.ts +++ b/extensions/discord/src/channel.ts @@ -10,11 +10,7 @@ import { } from "openclaw/plugin-sdk/channel-config-helpers"; import { resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-runtime"; import { normalizeMessageChannel } from "openclaw/plugin-sdk/channel-runtime"; -import { - buildOutboundBaseSessionKey, - normalizeOutboundThreadId, -} from "openclaw/plugin-sdk/core"; -import { resolveThreadSessionKeys, type RoutePeer } from "openclaw/plugin-sdk/routing"; +import { buildOutboundBaseSessionKey, normalizeOutboundThreadId } from "openclaw/plugin-sdk/core"; import { buildComputedAccountStatusSnapshot, buildTokenChannelStatusSummary, @@ -31,6 +27,7 @@ import { type ChannelPlugin, type OpenClawConfig, } from "openclaw/plugin-sdk/discord"; +import { resolveThreadSessionKeys, type RoutePeer } from "openclaw/plugin-sdk/routing"; import { listDiscordAccountIds, resolveDiscordAccount, diff --git a/extensions/slack/src/channel.ts b/extensions/slack/src/channel.ts index 99d0fe3cbdf..d2dffaf8a2b 100644 --- a/extensions/slack/src/channel.ts +++ b/extensions/slack/src/channel.ts @@ -8,10 +8,7 @@ import { collectOpenProviderGroupPolicyWarnings, } from "openclaw/plugin-sdk/channel-config-helpers"; import { resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-runtime"; -import { - buildOutboundBaseSessionKey, - normalizeOutboundThreadId, -} from "openclaw/plugin-sdk/core"; +import { buildOutboundBaseSessionKey, normalizeOutboundThreadId } from "openclaw/plugin-sdk/core"; import { resolveThreadSessionKeys, type RoutePeer } from "openclaw/plugin-sdk/routing"; import { buildComputedAccountStatusSnapshot, diff --git a/extensions/telegram/src/channel.ts b/extensions/telegram/src/channel.ts index 88056309ede..c05c926d52f 100644 --- a/extensions/telegram/src/channel.ts +++ b/extensions/telegram/src/channel.ts @@ -6,12 +6,9 @@ import { } from "openclaw/plugin-sdk/channel-config-helpers"; import { type OutboundSendDeps, resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-runtime"; import { normalizeMessageChannel } from "openclaw/plugin-sdk/channel-runtime"; +import { buildOutboundBaseSessionKey, normalizeOutboundThreadId } from "openclaw/plugin-sdk/core"; import { resolveExecApprovalCommandDisplay } from "openclaw/plugin-sdk/infra-runtime"; import { buildExecApprovalPendingReplyPayload } from "openclaw/plugin-sdk/infra-runtime"; -import { - buildOutboundBaseSessionKey, - normalizeOutboundThreadId, -} from "openclaw/plugin-sdk/core"; import { resolveThreadSessionKeys, type RoutePeer } from "openclaw/plugin-sdk/routing"; import { parseTelegramTopicConversation } from "openclaw/plugin-sdk/telegram"; import { From 42c8c3c983c61d5807bec836de6306c916aae9c4 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 16 Mar 2026 23:54:37 -0700 Subject: [PATCH 3/6] fix: resolve rebase type fallout in channel setup seams --- extensions/discord/src/channel.ts | 2 +- extensions/discord/src/setup-surface.ts | 42 +++++++++------------- extensions/slack/src/channel.ts | 8 ++++- extensions/slack/src/setup-surface.ts | 40 +++++++++------------ src/channels/plugins/setup-helpers.test.ts | 3 ++ src/channels/plugins/slack.actions.ts | 3 +- 6 files changed, 46 insertions(+), 52 deletions(-) diff --git a/extensions/discord/src/channel.ts b/extensions/discord/src/channel.ts index e2d202cb6ee..dff011825b0 100644 --- a/extensions/discord/src/channel.ts +++ b/extensions/discord/src/channel.ts @@ -48,7 +48,7 @@ import { resolveDiscordUserAllowlist } from "./resolve-users.js"; import { getDiscordRuntime } from "./runtime.js"; import { fetchChannelPermissionsDiscord } from "./send.js"; import { discordSetupAdapter } from "./setup-core.js"; -import { createDiscordPluginBase } from "./shared.js"; +import { createDiscordPluginBase, discordConfigAccessors } from "./shared.js"; import { collectDiscordStatusIssues } from "./status-issues.js"; import { parseDiscordTarget } from "./targets.js"; import { DiscordUiContainer } from "./ui.js"; diff --git a/extensions/discord/src/setup-surface.ts b/extensions/discord/src/setup-surface.ts index 9c1ce7f5f1c..5432302ff4b 100644 --- a/extensions/discord/src/setup-surface.ts +++ b/extensions/discord/src/setup-surface.ts @@ -100,28 +100,20 @@ async function resolveDiscordGroupAllowlist(params: { }); } -export const discordSetupWizard: ChannelSetupWizard = createDiscordSetupWizardBase(async () => ({ - discordSetupWizard: { - dmPolicy: { - promptAllowFrom: promptDiscordAllowFrom, - }, - groupAccess: { - resolveAllowlist: async ({ cfg, accountId, credentialValues, entries }) => - await resolveDiscordGroupAllowlist({ - cfg, - accountId, - credentialValues, - entries, - }), - }, - allowFrom: { - resolveEntries: async ({ cfg, accountId, credentialValues, entries }) => - await resolveDiscordAllowFromEntries({ - token: - resolveDiscordAccount({ cfg, accountId }).token || - (typeof credentialValues.token === "string" ? credentialValues.token : ""), - entries, - }), - }, - } as ChannelSetupWizard, -})); +export const discordSetupWizard: ChannelSetupWizard = createDiscordSetupWizardBase({ + promptAllowFrom: promptDiscordAllowFrom, + resolveGroupAllowlist: async ({ cfg, accountId, credentialValues, entries }) => + await resolveDiscordGroupAllowlist({ + cfg, + accountId, + credentialValues, + entries, + }), + resolveAllowFromEntries: async ({ cfg, accountId, credentialValues, entries }) => + await resolveDiscordAllowFromEntries({ + token: + resolveDiscordAccount({ cfg, accountId }).token || + (typeof credentialValues.token === "string" ? credentialValues.token : ""), + entries, + }), +}); diff --git a/extensions/slack/src/channel.ts b/extensions/slack/src/channel.ts index d2dffaf8a2b..2149f22ec60 100644 --- a/extensions/slack/src/channel.ts +++ b/extensions/slack/src/channel.ts @@ -486,7 +486,13 @@ export const slackPlugin: ChannelPlugin = { }, actions: createSlackActions(SLACK_CHANNEL, { invoke: async (action, cfg, toolContext) => - await getSlackRuntime().channel.slack.handleSlackAction(action, cfg, toolContext), + await getSlackRuntime().channel.slack.handleSlackAction( + action, + cfg as OpenClawConfig, + toolContext as Parameters< + ReturnType["channel"]["slack"]["handleSlackAction"] + >[2], + ), }), setup: slackSetupAdapter, outbound: { diff --git a/extensions/slack/src/setup-surface.ts b/extensions/slack/src/setup-surface.ts index 063129267cf..f7a52a72888 100644 --- a/extensions/slack/src/setup-surface.ts +++ b/extensions/slack/src/setup-surface.ts @@ -130,27 +130,19 @@ async function resolveSlackGroupAllowlist(params: { return keys; } -export const slackSetupWizard: ChannelSetupWizard = createSlackSetupWizardBase(async () => ({ - slackSetupWizard: { - dmPolicy: { - promptAllowFrom: promptSlackAllowFrom, - }, - allowFrom: { - resolveEntries: async ({ credentialValues, entries }) => - await resolveSlackAllowFromEntries({ - token: credentialValues.botToken, - entries, - }), - }, - groupAccess: { - resolveAllowlist: async ({ cfg, accountId, credentialValues, entries, prompter }) => - await resolveSlackGroupAllowlist({ - cfg, - accountId, - credentialValues, - entries, - prompter, - }), - }, - } as ChannelSetupWizard, -})); +export const slackSetupWizard: ChannelSetupWizard = createSlackSetupWizardBase({ + promptAllowFrom: promptSlackAllowFrom, + resolveAllowFromEntries: async ({ credentialValues, entries }) => + await resolveSlackAllowFromEntries({ + token: credentialValues.botToken, + entries, + }), + resolveGroupAllowlist: async ({ cfg, accountId, credentialValues, entries, prompter }) => + await resolveSlackGroupAllowlist({ + cfg, + accountId, + credentialValues, + entries, + prompter, + }), +}); diff --git a/src/channels/plugins/setup-helpers.test.ts b/src/channels/plugins/setup-helpers.test.ts index f81de4fe4ed..4111986e175 100644 --- a/src/channels/plugins/setup-helpers.test.ts +++ b/src/channels/plugins/setup-helpers.test.ts @@ -175,6 +175,7 @@ describe("createEnvPatchedAccountSetupAdapter", () => { expect( adapter.validateInput?.({ + cfg: asConfig({}), accountId: "work", input: { useEnv: true }, }), @@ -182,6 +183,7 @@ describe("createEnvPatchedAccountSetupAdapter", () => { expect( adapter.validateInput?.({ + cfg: asConfig({}), accountId: DEFAULT_ACCOUNT_ID, input: {}, }), @@ -189,6 +191,7 @@ describe("createEnvPatchedAccountSetupAdapter", () => { expect( adapter.validateInput?.({ + cfg: asConfig({}), accountId: DEFAULT_ACCOUNT_ID, input: { token: "tok" }, }), diff --git a/src/channels/plugins/slack.actions.ts b/src/channels/plugins/slack.actions.ts index e65a85d98f6..483b4db7df9 100644 --- a/src/channels/plugins/slack.actions.ts +++ b/src/channels/plugins/slack.actions.ts @@ -1,3 +1,4 @@ +import type { AgentToolResult } from "@mariozechner/pi-agent-core"; import { handleSlackAction, type SlackActionContext } from "../../agents/tools/slack-actions.js"; import { extractSlackToolSend, @@ -12,7 +13,7 @@ type SlackActionInvoke = ( action: Record, cfg: unknown, toolContext: unknown, -) => Promise; +) => Promise>; export function createSlackActions( providerId: string, From 0d776c87c389bd1157a57afabd116bf6424ddd2e Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 16 Mar 2026 23:56:32 -0700 Subject: [PATCH 4/6] fix(macos): block canvas symlink escapes --- .../OpenClaw/CanvasSchemeHandler.swift | 22 ++++++++++------ .../LowCoverageHelperTests.swift | 26 +++++++++++++++++++ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/apps/macos/Sources/OpenClaw/CanvasSchemeHandler.swift b/apps/macos/Sources/OpenClaw/CanvasSchemeHandler.swift index 6905af50014..9b4c8e5ebad 100644 --- a/apps/macos/Sources/OpenClaw/CanvasSchemeHandler.swift +++ b/apps/macos/Sources/OpenClaw/CanvasSchemeHandler.swift @@ -81,22 +81,23 @@ final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler { return self.html("Not Found", title: "Canvas: 404") } - // Directory traversal guard: served files must live under the session root. - let standardizedRoot = sessionRoot.standardizedFileURL - let standardizedFile = fileURL.standardizedFileURL - guard standardizedFile.path.hasPrefix(standardizedRoot.path) else { + // Resolve symlinks before enforcing the session-root boundary so links inside + // the canvas tree cannot escape to arbitrary host files. + let resolvedRoot = sessionRoot.resolvingSymlinksInPath().standardizedFileURL + let resolvedFile = fileURL.resolvingSymlinksInPath().standardizedFileURL + guard self.isFileURL(resolvedFile, withinDirectory: resolvedRoot) else { return self.html("Forbidden", title: "Canvas: 403") } do { - let data = try Data(contentsOf: standardizedFile) - let mime = CanvasScheme.mimeType(forExtension: standardizedFile.pathExtension) - let servedPath = standardizedFile.path + let data = try Data(contentsOf: resolvedFile) + let mime = CanvasScheme.mimeType(forExtension: resolvedFile.pathExtension) + let servedPath = resolvedFile.path canvasLogger.debug( "served \(session, privacy: .public)/\(path, privacy: .public) -> \(servedPath, privacy: .public)") return CanvasResponse(mime: mime, data: data) } catch { - let failedPath = standardizedFile.path + let failedPath = resolvedFile.path let errorText = error.localizedDescription canvasLogger .error( @@ -145,6 +146,11 @@ final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler { return nil } + private func isFileURL(_ fileURL: URL, withinDirectory rootURL: URL) -> Bool { + let rootPath = rootURL.path.hasSuffix("/") ? rootURL.path : rootURL.path + "/" + return fileURL.path == rootURL.path || fileURL.path.hasPrefix(rootPath) + } + private func html(_ body: String, title: String = "Canvas") -> CanvasResponse { let html = """ diff --git a/apps/macos/Tests/OpenClawIPCTests/LowCoverageHelperTests.swift b/apps/macos/Tests/OpenClawIPCTests/LowCoverageHelperTests.swift index a37135ff490..b47dd70c3ff 100644 --- a/apps/macos/Tests/OpenClawIPCTests/LowCoverageHelperTests.swift +++ b/apps/macos/Tests/OpenClawIPCTests/LowCoverageHelperTests.swift @@ -216,6 +216,32 @@ struct LowCoverageHelperTests { #expect(handler._testTextEncodingName(for: "application/octet-stream") == nil) } + @Test @MainActor func `canvas scheme handler blocks symlink escapes`() throws { + let root = FileManager().temporaryDirectory + .appendingPathComponent("canvas-\(UUID().uuidString)", isDirectory: true) + defer { try? FileManager().removeItem(at: root) } + try FileManager().createDirectory(at: root, withIntermediateDirectories: true) + + let session = root.appendingPathComponent("main", isDirectory: true) + try FileManager().createDirectory(at: session, withIntermediateDirectories: true) + + let outside = root.deletingLastPathComponent().appendingPathComponent("canvas-secret-\(UUID().uuidString).txt") + defer { try? FileManager().removeItem(at: outside) } + try "top-secret".write(to: outside, atomically: true, encoding: .utf8) + + let symlink = session.appendingPathComponent("index.html") + try FileManager().createSymbolicLink(at: symlink, withDestinationURL: outside) + + let handler = CanvasSchemeHandler(root: root) + let url = try #require(CanvasScheme.makeURL(session: "main", path: "index.html")) + let response = handler._testResponse(for: url) + let body = String(data: response.data, encoding: .utf8) ?? "" + + #expect(response.mime == "text/html") + #expect(body.contains("Forbidden")) + #expect(!body.contains("top-secret")) + } + @Test @MainActor func `menu context card injector inserts and finds index`() { let injector = MenuContextCardInjector() let menu = NSMenu() From 3dec814fda938c44cafbcd73ba8525d54cf5c469 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 17 Mar 2026 00:00:05 -0700 Subject: [PATCH 5/6] refactor: bundle lazy runtime surfaces --- extensions/bluebubbles/src/actions.runtime.ts | 112 ++++++++++-- extensions/bluebubbles/src/channel.runtime.ts | 63 ++++++- extensions/discord/src/channel.runtime.ts | 6 +- extensions/discord/src/setup-surface.ts | 15 +- .../discord/src/voice/manager.runtime.ts | 9 +- extensions/feishu/src/channel.runtime.ts | 136 +++++++++++++- extensions/googlechat/src/channel.runtime.ts | 45 ++++- extensions/matrix/src/channel.runtime.ts | 61 ++++++- extensions/msteams/src/channel.runtime.ts | 53 +++++- extensions/signal/src/channel.runtime.ts | 6 +- extensions/slack/src/channel.runtime.ts | 6 +- extensions/slack/src/channel.ts | 5 +- extensions/whatsapp/src/channel.runtime.ts | 77 ++++++-- extensions/zalo/src/actions.runtime.ts | 8 +- src/cli/deps.test.ts | 12 +- src/cli/deps.ts | 36 ++-- src/cli/send-runtime/discord.ts | 12 +- src/cli/send-runtime/imessage.ts | 12 +- src/cli/send-runtime/signal.ts | 12 +- src/cli/send-runtime/slack.ts | 12 +- src/cli/send-runtime/telegram.ts | 12 +- src/cli/send-runtime/whatsapp.ts | 12 +- src/commands/status.scan.deps.runtime.ts | 36 +++- src/config/schema.shared.test.ts | 2 +- .../contracts/auth-choice.contract.test.ts | 42 +++-- .../contracts/runtime.contract.test.ts | 2 +- .../runtime/runtime-discord-ops.runtime.ts | 170 +++++------------- src/plugins/runtime/runtime-discord.ts | 77 ++++---- .../runtime/runtime-slack-ops.runtime.ts | 80 +++------ src/plugins/runtime/runtime-slack.ts | 40 +++-- .../runtime/runtime-telegram-ops.runtime.ts | 151 ++++------------ src/plugins/runtime/runtime-telegram.ts | 57 +++--- .../runtime/runtime-whatsapp-login.runtime.ts | 9 +- .../runtime-whatsapp-outbound.runtime.ts | 23 +-- src/plugins/runtime/runtime-whatsapp.ts | 30 ++-- 35 files changed, 887 insertions(+), 554 deletions(-) diff --git a/extensions/bluebubbles/src/actions.runtime.ts b/extensions/bluebubbles/src/actions.runtime.ts index 53285c19f17..00d0bc00efd 100644 --- a/extensions/bluebubbles/src/actions.runtime.ts +++ b/extensions/bluebubbles/src/actions.runtime.ts @@ -1,13 +1,101 @@ -export { sendBlueBubblesAttachment } from "./attachments.js"; -export { - addBlueBubblesParticipant, - editBlueBubblesMessage, - leaveBlueBubblesChat, - removeBlueBubblesParticipant, - renameBlueBubblesChat, - setGroupIconBlueBubbles, - unsendBlueBubblesMessage, +import { sendBlueBubblesAttachment as sendBlueBubblesAttachmentImpl } from "./attachments.js"; +import { + addBlueBubblesParticipant as addBlueBubblesParticipantImpl, + editBlueBubblesMessage as editBlueBubblesMessageImpl, + leaveBlueBubblesChat as leaveBlueBubblesChatImpl, + removeBlueBubblesParticipant as removeBlueBubblesParticipantImpl, + renameBlueBubblesChat as renameBlueBubblesChatImpl, + setGroupIconBlueBubbles as setGroupIconBlueBubblesImpl, + unsendBlueBubblesMessage as unsendBlueBubblesMessageImpl, } from "./chat.js"; -export { resolveBlueBubblesMessageId } from "./monitor.js"; -export { sendBlueBubblesReaction } from "./reactions.js"; -export { resolveChatGuidForTarget, sendMessageBlueBubbles } from "./send.js"; +import { resolveBlueBubblesMessageId as resolveBlueBubblesMessageIdImpl } from "./monitor.js"; +import { sendBlueBubblesReaction as sendBlueBubblesReactionImpl } from "./reactions.js"; +import { + resolveChatGuidForTarget as resolveChatGuidForTargetImpl, + sendMessageBlueBubbles as sendMessageBlueBubblesImpl, +} from "./send.js"; + +type SendBlueBubblesAttachment = typeof import("./attachments.js").sendBlueBubblesAttachment; +type AddBlueBubblesParticipant = typeof import("./chat.js").addBlueBubblesParticipant; +type EditBlueBubblesMessage = typeof import("./chat.js").editBlueBubblesMessage; +type LeaveBlueBubblesChat = typeof import("./chat.js").leaveBlueBubblesChat; +type RemoveBlueBubblesParticipant = typeof import("./chat.js").removeBlueBubblesParticipant; +type RenameBlueBubblesChat = typeof import("./chat.js").renameBlueBubblesChat; +type SetGroupIconBlueBubbles = typeof import("./chat.js").setGroupIconBlueBubbles; +type UnsendBlueBubblesMessage = typeof import("./chat.js").unsendBlueBubblesMessage; +type ResolveBlueBubblesMessageId = typeof import("./monitor.js").resolveBlueBubblesMessageId; +type SendBlueBubblesReaction = typeof import("./reactions.js").sendBlueBubblesReaction; +type ResolveChatGuidForTarget = typeof import("./send.js").resolveChatGuidForTarget; +type SendMessageBlueBubbles = typeof import("./send.js").sendMessageBlueBubbles; + +export function sendBlueBubblesAttachment( + ...args: Parameters +): ReturnType { + return sendBlueBubblesAttachmentImpl(...args); +} + +export function addBlueBubblesParticipant( + ...args: Parameters +): ReturnType { + return addBlueBubblesParticipantImpl(...args); +} + +export function editBlueBubblesMessage( + ...args: Parameters +): ReturnType { + return editBlueBubblesMessageImpl(...args); +} + +export function leaveBlueBubblesChat( + ...args: Parameters +): ReturnType { + return leaveBlueBubblesChatImpl(...args); +} + +export function removeBlueBubblesParticipant( + ...args: Parameters +): ReturnType { + return removeBlueBubblesParticipantImpl(...args); +} + +export function renameBlueBubblesChat( + ...args: Parameters +): ReturnType { + return renameBlueBubblesChatImpl(...args); +} + +export function setGroupIconBlueBubbles( + ...args: Parameters +): ReturnType { + return setGroupIconBlueBubblesImpl(...args); +} + +export function unsendBlueBubblesMessage( + ...args: Parameters +): ReturnType { + return unsendBlueBubblesMessageImpl(...args); +} + +export function resolveBlueBubblesMessageId( + ...args: Parameters +): ReturnType { + return resolveBlueBubblesMessageIdImpl(...args); +} + +export function sendBlueBubblesReaction( + ...args: Parameters +): ReturnType { + return sendBlueBubblesReactionImpl(...args); +} + +export function resolveChatGuidForTarget( + ...args: Parameters +): ReturnType { + return resolveChatGuidForTargetImpl(...args); +} + +export function sendMessageBlueBubbles( + ...args: Parameters +): ReturnType { + return sendMessageBlueBubblesImpl(...args); +} diff --git a/extensions/bluebubbles/src/channel.runtime.ts b/extensions/bluebubbles/src/channel.runtime.ts index 32bf567dcf5..d318943d3f2 100644 --- a/extensions/bluebubbles/src/channel.runtime.ts +++ b/extensions/bluebubbles/src/channel.runtime.ts @@ -1,6 +1,57 @@ -export { sendBlueBubblesMedia } from "./media-send.js"; -export { resolveBlueBubblesMessageId } from "./monitor.js"; -export { monitorBlueBubblesProvider, resolveWebhookPathFromConfig } from "./monitor.js"; -export { type BlueBubblesProbe, probeBlueBubbles } from "./probe.js"; -export { sendMessageBlueBubbles } from "./send.js"; -export { blueBubblesSetupWizard } from "./setup-surface.js"; +import { sendBlueBubblesMedia as sendBlueBubblesMediaImpl } from "./media-send.js"; +import { + monitorBlueBubblesProvider as monitorBlueBubblesProviderImpl, + resolveBlueBubblesMessageId as resolveBlueBubblesMessageIdImpl, + resolveWebhookPathFromConfig as resolveWebhookPathFromConfigImpl, +} from "./monitor.js"; +import { probeBlueBubbles as probeBlueBubblesImpl } from "./probe.js"; +import { sendMessageBlueBubbles as sendMessageBlueBubblesImpl } from "./send.js"; +import { blueBubblesSetupWizard as blueBubblesSetupWizardImpl } from "./setup-surface.js"; + +export type { BlueBubblesProbe } from "./probe.js"; + +type SendBlueBubblesMedia = typeof import("./media-send.js").sendBlueBubblesMedia; +type ResolveBlueBubblesMessageId = typeof import("./monitor.js").resolveBlueBubblesMessageId; +type MonitorBlueBubblesProvider = typeof import("./monitor.js").monitorBlueBubblesProvider; +type ResolveWebhookPathFromConfig = typeof import("./monitor.js").resolveWebhookPathFromConfig; +type ProbeBlueBubbles = typeof import("./probe.js").probeBlueBubbles; +type SendMessageBlueBubbles = typeof import("./send.js").sendMessageBlueBubbles; +type BlueBubblesSetupWizard = typeof import("./setup-surface.js").blueBubblesSetupWizard; + +export function sendBlueBubblesMedia( + ...args: Parameters +): ReturnType { + return sendBlueBubblesMediaImpl(...args); +} + +export function resolveBlueBubblesMessageId( + ...args: Parameters +): ReturnType { + return resolveBlueBubblesMessageIdImpl(...args); +} + +export function monitorBlueBubblesProvider( + ...args: Parameters +): ReturnType { + return monitorBlueBubblesProviderImpl(...args); +} + +export function resolveWebhookPathFromConfig( + ...args: Parameters +): ReturnType { + return resolveWebhookPathFromConfigImpl(...args); +} + +export function probeBlueBubbles( + ...args: Parameters +): ReturnType { + return probeBlueBubblesImpl(...args); +} + +export function sendMessageBlueBubbles( + ...args: Parameters +): ReturnType { + return sendMessageBlueBubblesImpl(...args); +} + +export const blueBubblesSetupWizard: BlueBubblesSetupWizard = { ...blueBubblesSetupWizardImpl }; diff --git a/extensions/discord/src/channel.runtime.ts b/extensions/discord/src/channel.runtime.ts index bc22b64706a..d4da518fdc1 100644 --- a/extensions/discord/src/channel.runtime.ts +++ b/extensions/discord/src/channel.runtime.ts @@ -1 +1,5 @@ -export { discordSetupWizard } from "./setup-surface.js"; +import { discordSetupWizard as discordSetupWizardImpl } from "./setup-surface.js"; + +type DiscordSetupWizard = typeof import("./setup-surface.js").discordSetupWizard; + +export const discordSetupWizard: DiscordSetupWizard = { ...discordSetupWizardImpl }; diff --git a/extensions/discord/src/setup-surface.ts b/extensions/discord/src/setup-surface.ts index 5432302ff4b..78e1c1da804 100644 --- a/extensions/discord/src/setup-surface.ts +++ b/extensions/discord/src/setup-surface.ts @@ -100,15 +100,9 @@ async function resolveDiscordGroupAllowlist(params: { }); } +<<<<<<< HEAD export const discordSetupWizard: ChannelSetupWizard = createDiscordSetupWizardBase({ promptAllowFrom: promptDiscordAllowFrom, - resolveGroupAllowlist: async ({ cfg, accountId, credentialValues, entries }) => - await resolveDiscordGroupAllowlist({ - cfg, - accountId, - credentialValues, - entries, - }), resolveAllowFromEntries: async ({ cfg, accountId, credentialValues, entries }) => await resolveDiscordAllowFromEntries({ token: @@ -116,4 +110,11 @@ export const discordSetupWizard: ChannelSetupWizard = createDiscordSetupWizardBa (typeof credentialValues.token === "string" ? credentialValues.token : ""), entries, }), + resolveGroupAllowlist: async ({ cfg, accountId, credentialValues, entries }) => + await resolveDiscordGroupAllowlist({ + cfg, + accountId, + credentialValues, + entries, + }), }); diff --git a/extensions/discord/src/voice/manager.runtime.ts b/extensions/discord/src/voice/manager.runtime.ts index 77574b166e5..1619d63a27c 100644 --- a/extensions/discord/src/voice/manager.runtime.ts +++ b/extensions/discord/src/voice/manager.runtime.ts @@ -1 +1,8 @@ -export { DiscordVoiceManager, DiscordVoiceReadyListener } from "./manager.js"; +import { + DiscordVoiceManager as DiscordVoiceManagerImpl, + DiscordVoiceReadyListener as DiscordVoiceReadyListenerImpl, +} from "./manager.js"; + +export class DiscordVoiceManager extends DiscordVoiceManagerImpl {} + +export class DiscordVoiceReadyListener extends DiscordVoiceReadyListenerImpl {} diff --git a/extensions/feishu/src/channel.runtime.ts b/extensions/feishu/src/channel.runtime.ts index 0e4d9fc7583..c8a742942ea 100644 --- a/extensions/feishu/src/channel.runtime.ts +++ b/extensions/feishu/src/channel.runtime.ts @@ -1,7 +1,129 @@ -export { listFeishuDirectoryGroupsLive, listFeishuDirectoryPeersLive } from "./directory.js"; -export { feishuOutbound } from "./outbound.js"; -export { createPinFeishu, listPinsFeishu, removePinFeishu } from "./pins.js"; -export { probeFeishu } from "./probe.js"; -export { addReactionFeishu, listReactionsFeishu, removeReactionFeishu } from "./reactions.js"; -export { getChatInfo, getChatMembers, getFeishuMemberInfo } from "./chat.js"; -export { editMessageFeishu, getMessageFeishu, sendCardFeishu, sendMessageFeishu } from "./send.js"; +import { + getChatInfo as getChatInfoImpl, + getChatMembers as getChatMembersImpl, + getFeishuMemberInfo as getFeishuMemberInfoImpl, +} from "./chat.js"; +import { + listFeishuDirectoryGroupsLive as listFeishuDirectoryGroupsLiveImpl, + listFeishuDirectoryPeersLive as listFeishuDirectoryPeersLiveImpl, +} from "./directory.js"; +import { feishuOutbound as feishuOutboundImpl } from "./outbound.js"; +import { + createPinFeishu as createPinFeishuImpl, + listPinsFeishu as listPinsFeishuImpl, + removePinFeishu as removePinFeishuImpl, +} from "./pins.js"; +import { probeFeishu as probeFeishuImpl } from "./probe.js"; +import { + addReactionFeishu as addReactionFeishuImpl, + listReactionsFeishu as listReactionsFeishuImpl, + removeReactionFeishu as removeReactionFeishuImpl, +} from "./reactions.js"; +import { + editMessageFeishu as editMessageFeishuImpl, + getMessageFeishu as getMessageFeishuImpl, + sendCardFeishu as sendCardFeishuImpl, + sendMessageFeishu as sendMessageFeishuImpl, +} from "./send.js"; + +type ListFeishuDirectoryGroupsLive = typeof import("./directory.js").listFeishuDirectoryGroupsLive; +type ListFeishuDirectoryPeersLive = typeof import("./directory.js").listFeishuDirectoryPeersLive; +type FeishuOutbound = typeof import("./outbound.js").feishuOutbound; +type CreatePinFeishu = typeof import("./pins.js").createPinFeishu; +type ListPinsFeishu = typeof import("./pins.js").listPinsFeishu; +type RemovePinFeishu = typeof import("./pins.js").removePinFeishu; +type ProbeFeishu = typeof import("./probe.js").probeFeishu; +type AddReactionFeishu = typeof import("./reactions.js").addReactionFeishu; +type ListReactionsFeishu = typeof import("./reactions.js").listReactionsFeishu; +type RemoveReactionFeishu = typeof import("./reactions.js").removeReactionFeishu; +type GetChatInfo = typeof import("./chat.js").getChatInfo; +type GetChatMembers = typeof import("./chat.js").getChatMembers; +type GetFeishuMemberInfo = typeof import("./chat.js").getFeishuMemberInfo; +type EditMessageFeishu = typeof import("./send.js").editMessageFeishu; +type GetMessageFeishu = typeof import("./send.js").getMessageFeishu; +type SendCardFeishu = typeof import("./send.js").sendCardFeishu; +type SendMessageFeishu = typeof import("./send.js").sendMessageFeishu; + +export function listFeishuDirectoryGroupsLive( + ...args: Parameters +): ReturnType { + return listFeishuDirectoryGroupsLiveImpl(...args); +} + +export function listFeishuDirectoryPeersLive( + ...args: Parameters +): ReturnType { + return listFeishuDirectoryPeersLiveImpl(...args); +} + +export const feishuOutbound: FeishuOutbound = { ...feishuOutboundImpl }; + +export function createPinFeishu(...args: Parameters): ReturnType { + return createPinFeishuImpl(...args); +} + +export function listPinsFeishu(...args: Parameters): ReturnType { + return listPinsFeishuImpl(...args); +} + +export function removePinFeishu(...args: Parameters): ReturnType { + return removePinFeishuImpl(...args); +} + +export function probeFeishu(...args: Parameters): ReturnType { + return probeFeishuImpl(...args); +} + +export function addReactionFeishu( + ...args: Parameters +): ReturnType { + return addReactionFeishuImpl(...args); +} + +export function listReactionsFeishu( + ...args: Parameters +): ReturnType { + return listReactionsFeishuImpl(...args); +} + +export function removeReactionFeishu( + ...args: Parameters +): ReturnType { + return removeReactionFeishuImpl(...args); +} + +export function getChatInfo(...args: Parameters): ReturnType { + return getChatInfoImpl(...args); +} + +export function getChatMembers(...args: Parameters): ReturnType { + return getChatMembersImpl(...args); +} + +export function getFeishuMemberInfo( + ...args: Parameters +): ReturnType { + return getFeishuMemberInfoImpl(...args); +} + +export function editMessageFeishu( + ...args: Parameters +): ReturnType { + return editMessageFeishuImpl(...args); +} + +export function getMessageFeishu( + ...args: Parameters +): ReturnType { + return getMessageFeishuImpl(...args); +} + +export function sendCardFeishu(...args: Parameters): ReturnType { + return sendCardFeishuImpl(...args); +} + +export function sendMessageFeishu( + ...args: Parameters +): ReturnType { + return sendMessageFeishuImpl(...args); +} diff --git a/extensions/googlechat/src/channel.runtime.ts b/extensions/googlechat/src/channel.runtime.ts index fdf060f9fd4..1e41376c8f5 100644 --- a/extensions/googlechat/src/channel.runtime.ts +++ b/extensions/googlechat/src/channel.runtime.ts @@ -1,2 +1,43 @@ -export { probeGoogleChat, sendGoogleChatMessage, uploadGoogleChatAttachment } from "./api.js"; -export { resolveGoogleChatWebhookPath, startGoogleChatMonitor } from "./monitor.js"; +import { + probeGoogleChat as probeGoogleChatImpl, + sendGoogleChatMessage as sendGoogleChatMessageImpl, + uploadGoogleChatAttachment as uploadGoogleChatAttachmentImpl, +} from "./api.js"; +import { + resolveGoogleChatWebhookPath as resolveGoogleChatWebhookPathImpl, + startGoogleChatMonitor as startGoogleChatMonitorImpl, +} from "./monitor.js"; + +type ProbeGoogleChat = typeof import("./api.js").probeGoogleChat; +type SendGoogleChatMessage = typeof import("./api.js").sendGoogleChatMessage; +type UploadGoogleChatAttachment = typeof import("./api.js").uploadGoogleChatAttachment; +type ResolveGoogleChatWebhookPath = typeof import("./monitor.js").resolveGoogleChatWebhookPath; +type StartGoogleChatMonitor = typeof import("./monitor.js").startGoogleChatMonitor; + +export function probeGoogleChat(...args: Parameters): ReturnType { + return probeGoogleChatImpl(...args); +} + +export function sendGoogleChatMessage( + ...args: Parameters +): ReturnType { + return sendGoogleChatMessageImpl(...args); +} + +export function uploadGoogleChatAttachment( + ...args: Parameters +): ReturnType { + return uploadGoogleChatAttachmentImpl(...args); +} + +export function resolveGoogleChatWebhookPath( + ...args: Parameters +): ReturnType { + return resolveGoogleChatWebhookPathImpl(...args); +} + +export function startGoogleChatMonitor( + ...args: Parameters +): ReturnType { + return startGoogleChatMonitorImpl(...args); +} diff --git a/extensions/matrix/src/channel.runtime.ts b/extensions/matrix/src/channel.runtime.ts index bcce71da2d1..df56d07ff2c 100644 --- a/extensions/matrix/src/channel.runtime.ts +++ b/extensions/matrix/src/channel.runtime.ts @@ -1,6 +1,55 @@ -export { listMatrixDirectoryGroupsLive, listMatrixDirectoryPeersLive } from "./directory-live.js"; -export { resolveMatrixAuth } from "./matrix/client.js"; -export { probeMatrix } from "./matrix/probe.js"; -export { sendMessageMatrix } from "./matrix/send.js"; -export { resolveMatrixTargets } from "./resolve-targets.js"; -export { matrixOutbound } from "./outbound.js"; +import { + listMatrixDirectoryGroupsLive as listMatrixDirectoryGroupsLiveImpl, + listMatrixDirectoryPeersLive as listMatrixDirectoryPeersLiveImpl, +} from "./directory-live.js"; +import { resolveMatrixAuth as resolveMatrixAuthImpl } from "./matrix/client.js"; +import { probeMatrix as probeMatrixImpl } from "./matrix/probe.js"; +import { sendMessageMatrix as sendMessageMatrixImpl } from "./matrix/send.js"; +import { matrixOutbound as matrixOutboundImpl } from "./outbound.js"; +import { resolveMatrixTargets as resolveMatrixTargetsImpl } from "./resolve-targets.js"; + +type ListMatrixDirectoryGroupsLive = + typeof import("./directory-live.js").listMatrixDirectoryGroupsLive; +type ListMatrixDirectoryPeersLive = + typeof import("./directory-live.js").listMatrixDirectoryPeersLive; +type ResolveMatrixAuth = typeof import("./matrix/client.js").resolveMatrixAuth; +type ProbeMatrix = typeof import("./matrix/probe.js").probeMatrix; +type SendMessageMatrix = typeof import("./matrix/send.js").sendMessageMatrix; +type ResolveMatrixTargets = typeof import("./resolve-targets.js").resolveMatrixTargets; +type MatrixOutbound = typeof import("./outbound.js").matrixOutbound; + +export function listMatrixDirectoryGroupsLive( + ...args: Parameters +): ReturnType { + return listMatrixDirectoryGroupsLiveImpl(...args); +} + +export function listMatrixDirectoryPeersLive( + ...args: Parameters +): ReturnType { + return listMatrixDirectoryPeersLiveImpl(...args); +} + +export function resolveMatrixAuth( + ...args: Parameters +): ReturnType { + return resolveMatrixAuthImpl(...args); +} + +export function probeMatrix(...args: Parameters): ReturnType { + return probeMatrixImpl(...args); +} + +export function sendMessageMatrix( + ...args: Parameters +): ReturnType { + return sendMessageMatrixImpl(...args); +} + +export function resolveMatrixTargets( + ...args: Parameters +): ReturnType { + return resolveMatrixTargetsImpl(...args); +} + +export const matrixOutbound: MatrixOutbound = { ...matrixOutboundImpl }; diff --git a/extensions/msteams/src/channel.runtime.ts b/extensions/msteams/src/channel.runtime.ts index 45a0147f46b..c55d0fc626a 100644 --- a/extensions/msteams/src/channel.runtime.ts +++ b/extensions/msteams/src/channel.runtime.ts @@ -1,4 +1,49 @@ -export { listMSTeamsDirectoryGroupsLive, listMSTeamsDirectoryPeersLive } from "./directory-live.js"; -export { msteamsOutbound } from "./outbound.js"; -export { probeMSTeams } from "./probe.js"; -export { sendAdaptiveCardMSTeams, sendMessageMSTeams } from "./send.js"; +import { + listMSTeamsDirectoryGroupsLive as listMSTeamsDirectoryGroupsLiveImpl, + listMSTeamsDirectoryPeersLive as listMSTeamsDirectoryPeersLiveImpl, +} from "./directory-live.js"; +import { msteamsOutbound as msteamsOutboundImpl } from "./outbound.js"; +import { probeMSTeams as probeMSTeamsImpl } from "./probe.js"; +import { + sendAdaptiveCardMSTeams as sendAdaptiveCardMSTeamsImpl, + sendMessageMSTeams as sendMessageMSTeamsImpl, +} from "./send.js"; + +type ListMSTeamsDirectoryGroupsLive = + typeof import("./directory-live.js").listMSTeamsDirectoryGroupsLive; +type ListMSTeamsDirectoryPeersLive = + typeof import("./directory-live.js").listMSTeamsDirectoryPeersLive; +type MSTeamsOutbound = typeof import("./outbound.js").msteamsOutbound; +type ProbeMSTeams = typeof import("./probe.js").probeMSTeams; +type SendAdaptiveCardMSTeams = typeof import("./send.js").sendAdaptiveCardMSTeams; +type SendMessageMSTeams = typeof import("./send.js").sendMessageMSTeams; + +export function listMSTeamsDirectoryGroupsLive( + ...args: Parameters +): ReturnType { + return listMSTeamsDirectoryGroupsLiveImpl(...args); +} + +export function listMSTeamsDirectoryPeersLive( + ...args: Parameters +): ReturnType { + return listMSTeamsDirectoryPeersLiveImpl(...args); +} + +export const msteamsOutbound: MSTeamsOutbound = { ...msteamsOutboundImpl }; + +export function probeMSTeams(...args: Parameters): ReturnType { + return probeMSTeamsImpl(...args); +} + +export function sendAdaptiveCardMSTeams( + ...args: Parameters +): ReturnType { + return sendAdaptiveCardMSTeamsImpl(...args); +} + +export function sendMessageMSTeams( + ...args: Parameters +): ReturnType { + return sendMessageMSTeamsImpl(...args); +} diff --git a/extensions/signal/src/channel.runtime.ts b/extensions/signal/src/channel.runtime.ts index 0403246478f..de908d212b7 100644 --- a/extensions/signal/src/channel.runtime.ts +++ b/extensions/signal/src/channel.runtime.ts @@ -1 +1,5 @@ -export { signalSetupWizard } from "./setup-surface.js"; +import { signalSetupWizard as signalSetupWizardImpl } from "./setup-surface.js"; + +type SignalSetupWizard = typeof import("./setup-surface.js").signalSetupWizard; + +export const signalSetupWizard: SignalSetupWizard = { ...signalSetupWizardImpl }; diff --git a/extensions/slack/src/channel.runtime.ts b/extensions/slack/src/channel.runtime.ts index eefcc2c6215..6dfe5bed8fe 100644 --- a/extensions/slack/src/channel.runtime.ts +++ b/extensions/slack/src/channel.runtime.ts @@ -1 +1,5 @@ -export { slackSetupWizard } from "./setup-surface.js"; +import { slackSetupWizard as slackSetupWizardImpl } from "./setup-surface.js"; + +type SlackSetupWizard = typeof import("./setup-surface.js").slackSetupWizard; + +export const slackSetupWizard: SlackSetupWizard = { ...slackSetupWizardImpl }; diff --git a/extensions/slack/src/channel.ts b/extensions/slack/src/channel.ts index 2149f22ec60..bafc5fc8c91 100644 --- a/extensions/slack/src/channel.ts +++ b/extensions/slack/src/channel.ts @@ -25,6 +25,7 @@ import { type ChannelPlugin, type OpenClawConfig, } from "openclaw/plugin-sdk/slack"; +import type { SlackActionContext } from "../../../src/agents/tools/slack-actions.js"; import { createSlackActions } from "../../../src/channels/plugins/slack.actions.js"; import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js"; import { @@ -489,9 +490,7 @@ export const slackPlugin: ChannelPlugin = { await getSlackRuntime().channel.slack.handleSlackAction( action, cfg as OpenClawConfig, - toolContext as Parameters< - ReturnType["channel"]["slack"]["handleSlackAction"] - >[2], + toolContext as SlackActionContext | undefined, ), }), setup: slackSetupAdapter, diff --git a/extensions/whatsapp/src/channel.runtime.ts b/extensions/whatsapp/src/channel.runtime.ts index dbe5965a25d..de2203db2ad 100644 --- a/extensions/whatsapp/src/channel.runtime.ts +++ b/extensions/whatsapp/src/channel.runtime.ts @@ -1,18 +1,73 @@ -export { getActiveWebListener } from "./active-listener.js"; -export { - getWebAuthAgeMs, - logWebSelfId, - logoutWeb, - readWebSelfId, - webAuthExists, -} from "./auth-store.js"; -export { loginWeb } from "./login.js"; -export { startWebLoginWithQr, waitForWebLogin } from "./login-qr.js"; -export { whatsappSetupWizard } from "./setup-surface.js"; import { monitorWebChannel as monitorWebChannelImpl } from "openclaw/plugin-sdk/whatsapp"; +import { getActiveWebListener as getActiveWebListenerImpl } from "./active-listener.js"; +import { + getWebAuthAgeMs as getWebAuthAgeMsImpl, + logWebSelfId as logWebSelfIdImpl, + logoutWeb as logoutWebImpl, + readWebSelfId as readWebSelfIdImpl, + webAuthExists as webAuthExistsImpl, +} from "./auth-store.js"; +import { + startWebLoginWithQr as startWebLoginWithQrImpl, + waitForWebLogin as waitForWebLoginImpl, +} from "./login-qr.js"; +import { loginWeb as loginWebImpl } from "./login.js"; +import { whatsappSetupWizard as whatsappSetupWizardImpl } from "./setup-surface.js"; +type GetActiveWebListener = typeof import("./active-listener.js").getActiveWebListener; +type GetWebAuthAgeMs = typeof import("./auth-store.js").getWebAuthAgeMs; +type LogWebSelfId = typeof import("./auth-store.js").logWebSelfId; +type LogoutWeb = typeof import("./auth-store.js").logoutWeb; +type ReadWebSelfId = typeof import("./auth-store.js").readWebSelfId; +type WebAuthExists = typeof import("./auth-store.js").webAuthExists; +type LoginWeb = typeof import("./login.js").loginWeb; +type StartWebLoginWithQr = typeof import("./login-qr.js").startWebLoginWithQr; +type WaitForWebLogin = typeof import("./login-qr.js").waitForWebLogin; +type WhatsAppSetupWizard = typeof import("./setup-surface.js").whatsappSetupWizard; type MonitorWebChannel = typeof import("openclaw/plugin-sdk/whatsapp").monitorWebChannel; +export function getActiveWebListener( + ...args: Parameters +): ReturnType { + return getActiveWebListenerImpl(...args); +} + +export function getWebAuthAgeMs(...args: Parameters): ReturnType { + return getWebAuthAgeMsImpl(...args); +} + +export function logWebSelfId(...args: Parameters): ReturnType { + return logWebSelfIdImpl(...args); +} + +export function logoutWeb(...args: Parameters): ReturnType { + return logoutWebImpl(...args); +} + +export function readWebSelfId(...args: Parameters): ReturnType { + return readWebSelfIdImpl(...args); +} + +export function webAuthExists(...args: Parameters): ReturnType { + return webAuthExistsImpl(...args); +} + +export function loginWeb(...args: Parameters): ReturnType { + return loginWebImpl(...args); +} + +export function startWebLoginWithQr( + ...args: Parameters +): ReturnType { + return startWebLoginWithQrImpl(...args); +} + +export function waitForWebLogin(...args: Parameters): ReturnType { + return waitForWebLoginImpl(...args); +} + +export const whatsappSetupWizard: WhatsAppSetupWizard = { ...whatsappSetupWizardImpl }; + export async function monitorWebChannel( ...args: Parameters ): ReturnType { diff --git a/extensions/zalo/src/actions.runtime.ts b/extensions/zalo/src/actions.runtime.ts index a9616ce64a5..d463edc5b24 100644 --- a/extensions/zalo/src/actions.runtime.ts +++ b/extensions/zalo/src/actions.runtime.ts @@ -1 +1,7 @@ -export { sendMessageZalo } from "./send.js"; +import { sendMessageZalo as sendMessageZaloImpl } from "./send.js"; + +type SendMessageZalo = typeof import("./send.js").sendMessageZalo; + +export function sendMessageZalo(...args: Parameters): ReturnType { + return sendMessageZaloImpl(...args); +} diff --git a/src/cli/deps.test.ts b/src/cli/deps.test.ts index 8dbc8539cff..dff1a082296 100644 --- a/src/cli/deps.test.ts +++ b/src/cli/deps.test.ts @@ -21,32 +21,32 @@ const sendFns = vi.hoisted(() => ({ vi.mock("./send-runtime/whatsapp.js", () => { moduleLoads.whatsapp(); - return { sendMessageWhatsApp: sendFns.whatsapp }; + return { runtimeSend: { sendMessage: sendFns.whatsapp } }; }); vi.mock("./send-runtime/telegram.js", () => { moduleLoads.telegram(); - return { sendMessageTelegram: sendFns.telegram }; + return { runtimeSend: { sendMessage: sendFns.telegram } }; }); vi.mock("./send-runtime/discord.js", () => { moduleLoads.discord(); - return { sendMessageDiscord: sendFns.discord }; + return { runtimeSend: { sendMessage: sendFns.discord } }; }); vi.mock("./send-runtime/slack.js", () => { moduleLoads.slack(); - return { sendMessageSlack: sendFns.slack }; + return { runtimeSend: { sendMessage: sendFns.slack } }; }); vi.mock("./send-runtime/signal.js", () => { moduleLoads.signal(); - return { sendMessageSignal: sendFns.signal }; + return { runtimeSend: { sendMessage: sendFns.signal } }; }); vi.mock("./send-runtime/imessage.js", () => { moduleLoads.imessage(); - return { sendMessageIMessage: sendFns.imessage }; + return { runtimeSend: { sendMessage: sendFns.imessage } }; }); describe("createDefaultDeps", () => { diff --git a/src/cli/deps.ts b/src/cli/deps.ts index 908da8cd265..9996c155288 100644 --- a/src/cli/deps.ts +++ b/src/cli/deps.ts @@ -6,9 +6,15 @@ import { createOutboundSendDepsFromCliSource } from "./outbound-send-mapping.js" * Values are proxy functions that dynamically import the real module on first use. */ export type CliDeps = { [channelId: string]: unknown }; +type RuntimeSend = { + sendMessage: (...args: unknown[]) => Promise; +}; +type RuntimeSendModule = { + runtimeSend: RuntimeSend; +}; // Per-channel module caches for lazy loading. -const senderCache = new Map>>(); +const senderCache = new Map>(); /** * Create a lazy-loading send function proxy for a channel. @@ -16,18 +22,16 @@ const senderCache = new Map>>(); */ function createLazySender( channelId: string, - loader: () => Promise>, - exportName: string, + loader: () => Promise, ): (...args: unknown[]) => Promise { return async (...args: unknown[]) => { let cached = senderCache.get(channelId); if (!cached) { - cached = loader(); + cached = loader().then(({ runtimeSend }) => runtimeSend); senderCache.set(channelId, cached); } - const mod = await cached; - const fn = mod[exportName] as (...a: unknown[]) => Promise; - return await fn(...args); + const runtimeSend = await cached; + return await runtimeSend.sendMessage(...args); }; } @@ -35,33 +39,27 @@ export function createDefaultDeps(): CliDeps { return { whatsapp: createLazySender( "whatsapp", - () => import("./send-runtime/whatsapp.js") as Promise>, - "sendMessageWhatsApp", + () => import("./send-runtime/whatsapp.js") as Promise, ), telegram: createLazySender( "telegram", - () => import("./send-runtime/telegram.js") as Promise>, - "sendMessageTelegram", + () => import("./send-runtime/telegram.js") as Promise, ), discord: createLazySender( "discord", - () => import("./send-runtime/discord.js") as Promise>, - "sendMessageDiscord", + () => import("./send-runtime/discord.js") as Promise, ), slack: createLazySender( "slack", - () => import("./send-runtime/slack.js") as Promise>, - "sendMessageSlack", + () => import("./send-runtime/slack.js") as Promise, ), signal: createLazySender( "signal", - () => import("./send-runtime/signal.js") as Promise>, - "sendMessageSignal", + () => import("./send-runtime/signal.js") as Promise, ), imessage: createLazySender( "imessage", - () => import("./send-runtime/imessage.js") as Promise>, - "sendMessageIMessage", + () => import("./send-runtime/imessage.js") as Promise, ), }; } diff --git a/src/cli/send-runtime/discord.ts b/src/cli/send-runtime/discord.ts index 13e8293085b..768653752b6 100644 --- a/src/cli/send-runtime/discord.ts +++ b/src/cli/send-runtime/discord.ts @@ -1,9 +1,9 @@ import { sendMessageDiscord as sendMessageDiscordImpl } from "../../plugin-sdk/discord.js"; -type SendMessageDiscord = typeof import("../../plugin-sdk/discord.js").sendMessageDiscord; +type RuntimeSend = { + sendMessage: typeof import("../../plugin-sdk/discord.js").sendMessageDiscord; +}; -export async function sendMessageDiscord( - ...args: Parameters -): ReturnType { - return await sendMessageDiscordImpl(...args); -} +export const runtimeSend = { + sendMessage: sendMessageDiscordImpl, +} satisfies RuntimeSend; diff --git a/src/cli/send-runtime/imessage.ts b/src/cli/send-runtime/imessage.ts index eb5263a8b53..cdc91c0be74 100644 --- a/src/cli/send-runtime/imessage.ts +++ b/src/cli/send-runtime/imessage.ts @@ -1,9 +1,9 @@ import { sendMessageIMessage as sendMessageIMessageImpl } from "../../plugin-sdk/imessage.js"; -type SendMessageIMessage = typeof import("../../plugin-sdk/imessage.js").sendMessageIMessage; +type RuntimeSend = { + sendMessage: typeof import("../../plugin-sdk/imessage.js").sendMessageIMessage; +}; -export async function sendMessageIMessage( - ...args: Parameters -): ReturnType { - return await sendMessageIMessageImpl(...args); -} +export const runtimeSend = { + sendMessage: sendMessageIMessageImpl, +} satisfies RuntimeSend; diff --git a/src/cli/send-runtime/signal.ts b/src/cli/send-runtime/signal.ts index a1e72eb1200..151f13cc351 100644 --- a/src/cli/send-runtime/signal.ts +++ b/src/cli/send-runtime/signal.ts @@ -1,9 +1,9 @@ import { sendMessageSignal as sendMessageSignalImpl } from "../../plugin-sdk/signal.js"; -type SendMessageSignal = typeof import("../../plugin-sdk/signal.js").sendMessageSignal; +type RuntimeSend = { + sendMessage: typeof import("../../plugin-sdk/signal.js").sendMessageSignal; +}; -export async function sendMessageSignal( - ...args: Parameters -): ReturnType { - return await sendMessageSignalImpl(...args); -} +export const runtimeSend = { + sendMessage: sendMessageSignalImpl, +} satisfies RuntimeSend; diff --git a/src/cli/send-runtime/slack.ts b/src/cli/send-runtime/slack.ts index 3bef60a98c2..354186cd128 100644 --- a/src/cli/send-runtime/slack.ts +++ b/src/cli/send-runtime/slack.ts @@ -1,9 +1,9 @@ import { sendMessageSlack as sendMessageSlackImpl } from "../../plugin-sdk/slack.js"; -type SendMessageSlack = typeof import("../../plugin-sdk/slack.js").sendMessageSlack; +type RuntimeSend = { + sendMessage: typeof import("../../plugin-sdk/slack.js").sendMessageSlack; +}; -export async function sendMessageSlack( - ...args: Parameters -): ReturnType { - return await sendMessageSlackImpl(...args); -} +export const runtimeSend = { + sendMessage: sendMessageSlackImpl, +} satisfies RuntimeSend; diff --git a/src/cli/send-runtime/telegram.ts b/src/cli/send-runtime/telegram.ts index 3c384baa853..09d5e3e9b19 100644 --- a/src/cli/send-runtime/telegram.ts +++ b/src/cli/send-runtime/telegram.ts @@ -1,9 +1,9 @@ import { sendMessageTelegram as sendMessageTelegramImpl } from "../../plugin-sdk/telegram.js"; -type SendMessageTelegram = typeof import("../../plugin-sdk/telegram.js").sendMessageTelegram; +type RuntimeSend = { + sendMessage: typeof import("../../plugin-sdk/telegram.js").sendMessageTelegram; +}; -export async function sendMessageTelegram( - ...args: Parameters -): ReturnType { - return await sendMessageTelegramImpl(...args); -} +export const runtimeSend = { + sendMessage: sendMessageTelegramImpl, +} satisfies RuntimeSend; diff --git a/src/cli/send-runtime/whatsapp.ts b/src/cli/send-runtime/whatsapp.ts index f8b33db58c1..49f0e50baa6 100644 --- a/src/cli/send-runtime/whatsapp.ts +++ b/src/cli/send-runtime/whatsapp.ts @@ -1,9 +1,9 @@ import { sendMessageWhatsApp as sendMessageWhatsAppImpl } from "../../plugin-sdk/whatsapp.js"; -type SendMessageWhatsApp = typeof import("../../plugin-sdk/whatsapp.js").sendMessageWhatsApp; +type RuntimeSend = { + sendMessage: typeof import("../../plugin-sdk/whatsapp.js").sendMessageWhatsApp; +}; -export async function sendMessageWhatsApp( - ...args: Parameters -): ReturnType { - return await sendMessageWhatsAppImpl(...args); -} +export const runtimeSend = { + sendMessage: sendMessageWhatsAppImpl, +} satisfies RuntimeSend; diff --git a/src/commands/status.scan.deps.runtime.ts b/src/commands/status.scan.deps.runtime.ts index b9838d2176f..ce318085541 100644 --- a/src/commands/status.scan.deps.runtime.ts +++ b/src/commands/status.scan.deps.runtime.ts @@ -1,2 +1,34 @@ -export { getTailnetHostname } from "../infra/tailscale.js"; -export { getMemorySearchManager } from "../memory/index.js"; +import type { OpenClawConfig } from "../config/config.js"; +import { getTailnetHostname } from "../infra/tailscale.js"; +import { getMemorySearchManager as getMemorySearchManagerImpl } from "../memory/index.js"; +import type { MemoryProviderStatus } from "../memory/types.js"; + +export { getTailnetHostname }; + +type StatusMemoryManager = { + probeVectorAvailability(): Promise; + status(): MemoryProviderStatus; + close?(): Promise; +}; + +export async function getMemorySearchManager(params: { + cfg: OpenClawConfig; + agentId: string; + purpose: "status"; +}): Promise<{ manager: StatusMemoryManager | null }> { + const { manager } = await getMemorySearchManagerImpl(params); + if (!manager) { + return { manager: null }; + } + return { + manager: { + async probeVectorAvailability() { + await manager.probeVectorAvailability(); + }, + status() { + return manager.status(); + }, + close: manager.close ? async () => await manager.close?.() : undefined, + }, + }; +} diff --git a/src/config/schema.shared.test.ts b/src/config/schema.shared.test.ts index 48820fbf029..d566bfd55f5 100644 --- a/src/config/schema.shared.test.ts +++ b/src/config/schema.shared.test.ts @@ -21,7 +21,7 @@ describe("schema.shared", () => { it("treats branch schemas as having children", () => { expect( schemaHasChildren({ - oneOf: [{ type: "string" }, { properties: { token: { type: "string" } } }], + oneOf: [{}, { properties: { token: {} } }], }), ).toBe(true); }); diff --git a/src/plugins/contracts/auth-choice.contract.test.ts b/src/plugins/contracts/auth-choice.contract.test.ts index 33e9be99479..ac2069b0d75 100644 --- a/src/plugins/contracts/auth-choice.contract.test.ts +++ b/src/plugins/contracts/auth-choice.contract.test.ts @@ -1,5 +1,4 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { clearRuntimeAuthProfileStoreSnapshots } from "../../agents/auth-profiles/store.js"; import { createAuthTestLifecycle, createExitThrowingRuntime, @@ -7,7 +6,8 @@ import { readAuthProfilesForAgent, requireOpenClawAgentDir, setupAuthTestEnv, -} from "../../commands/test-wizard-helpers.js"; +} from "../../../test/helpers/auth-wizard.js"; +import { clearRuntimeAuthProfileStoreSnapshots } from "../../agents/auth-profiles/store.js"; import { applyAuthChoiceLoadedPluginProvider } from "../../plugins/provider-auth-choice.js"; import { buildProviderPluginMethodChoice } from "../provider-wizard.js"; import { requireProviderContractProvider, uniqueProviderContractProviders } from "./registry.js"; @@ -27,7 +27,6 @@ const resolveProviderPluginChoiceMock = vi.hoisted(() => vi.fn vi.fn(async () => {}), ); -const resolvePreferredProviderPluginProvidersMock = vi.hoisted(() => vi.fn()); vi.mock("../../../extensions/qwen-portal-auth/oauth.js", () => ({ loginQwenPortalOAuth: loginQwenPortalOAuthMock, @@ -43,15 +42,6 @@ vi.mock("../../plugins/provider-auth-choice.runtime.js", () => ({ runProviderModelSelectedHook: runProviderModelSelectedHookMock, })); -vi.mock("../../plugins/providers.js", async () => { - const actual = await vi.importActual("../../plugins/providers.js"); - return { - ...actual, - resolvePluginProviders: (...args: unknown[]) => - resolvePreferredProviderPluginProvidersMock(...args), - }; -}); - const { resolvePreferredProviderForAuthChoice } = await import("../../plugins/provider-auth-choice-preference.js"); @@ -84,8 +74,24 @@ describe("provider auth-choice contract", () => { } beforeEach(() => { - resolvePreferredProviderPluginProvidersMock.mockReset(); - resolvePreferredProviderPluginProvidersMock.mockReturnValue(uniqueProviderContractProviders); + resolvePluginProvidersMock.mockReset(); + resolvePluginProvidersMock.mockReturnValue(uniqueProviderContractProviders); + resolveProviderPluginChoiceMock.mockReset(); + resolveProviderPluginChoiceMock.mockImplementation(({ providers, choice }) => { + const provider = providers.find((entry) => + entry.auth.some( + (method) => buildProviderPluginMethodChoice(entry.id, method.id) === choice, + ), + ); + if (!provider) { + return null; + } + const method = + provider.auth.find( + (entry) => buildProviderPluginMethodChoice(provider.id, entry.id) === choice, + ) ?? null; + return method ? { provider, method } : null; + }); }); afterEach(async () => { @@ -117,18 +123,18 @@ describe("provider auth-choice contract", () => { }); for (const scenario of pluginFallbackScenarios) { - resolvePreferredProviderPluginProvidersMock.mockClear(); + resolvePluginProvidersMock.mockClear(); await expect( resolvePreferredProviderForAuthChoice({ choice: scenario.authChoice }), ).resolves.toBe(scenario.expectedProvider); - expect(resolvePreferredProviderPluginProvidersMock).toHaveBeenCalled(); + expect(resolvePluginProvidersMock).toHaveBeenCalled(); } - resolvePreferredProviderPluginProvidersMock.mockClear(); + resolvePluginProvidersMock.mockClear(); await expect(resolvePreferredProviderForAuthChoice({ choice: "unknown" })).resolves.toBe( undefined, ); - expect(resolvePreferredProviderPluginProvidersMock).toHaveBeenCalled(); + expect(resolvePluginProvidersMock).toHaveBeenCalled(); }); it("applies qwen portal auth choices through the shared plugin-provider path", async () => { diff --git a/src/plugins/contracts/runtime.contract.test.ts b/src/plugins/contracts/runtime.contract.test.ts index 87acf1f8a13..15adc59e130 100644 --- a/src/plugins/contracts/runtime.contract.test.ts +++ b/src/plugins/contracts/runtime.contract.test.ts @@ -518,7 +518,7 @@ describe("provider runtime contract", () => { }); it("falls back to legacy pi auth tokens for usage auth", async () => { - const provider = requireProvider("zai"); + const provider = requireProviderContractProvider("zai"); const home = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-zai-contract-")); await fs.mkdir(path.join(home, ".pi", "agent"), { recursive: true }); await fs.writeFile( diff --git a/src/plugins/runtime/runtime-discord-ops.runtime.ts b/src/plugins/runtime/runtime-discord-ops.runtime.ts index 6a9d9429713..182e9c75d41 100644 --- a/src/plugins/runtime/runtime-discord-ops.runtime.ts +++ b/src/plugins/runtime/runtime-discord-ops.runtime.ts @@ -19,132 +19,48 @@ import { sendTypingDiscord as sendTypingDiscordImpl, unpinMessageDiscord as unpinMessageDiscordImpl, } from "../../../extensions/discord/src/send.js"; +import type { PluginRuntimeChannel } from "./types-channel.js"; -type AuditDiscordChannelPermissions = - typeof import("../../../extensions/discord/src/audit.js").auditDiscordChannelPermissions; -type ListDiscordDirectoryGroupsLive = - typeof import("../../../extensions/discord/src/directory-live.js").listDiscordDirectoryGroupsLive; -type ListDiscordDirectoryPeersLive = - typeof import("../../../extensions/discord/src/directory-live.js").listDiscordDirectoryPeersLive; -type MonitorDiscordProvider = - typeof import("../../../extensions/discord/src/monitor.js").monitorDiscordProvider; -type ProbeDiscord = typeof import("../../../extensions/discord/src/probe.js").probeDiscord; -type ResolveDiscordChannelAllowlist = - typeof import("../../../extensions/discord/src/resolve-channels.js").resolveDiscordChannelAllowlist; -type ResolveDiscordUserAllowlist = - typeof import("../../../extensions/discord/src/resolve-users.js").resolveDiscordUserAllowlist; -type CreateThreadDiscord = - typeof import("../../../extensions/discord/src/send.js").createThreadDiscord; -type DeleteMessageDiscord = - typeof import("../../../extensions/discord/src/send.js").deleteMessageDiscord; -type EditChannelDiscord = - typeof import("../../../extensions/discord/src/send.js").editChannelDiscord; -type EditMessageDiscord = - typeof import("../../../extensions/discord/src/send.js").editMessageDiscord; -type PinMessageDiscord = typeof import("../../../extensions/discord/src/send.js").pinMessageDiscord; -type SendDiscordComponentMessage = - typeof import("../../../extensions/discord/src/send.js").sendDiscordComponentMessage; -type SendMessageDiscord = - typeof import("../../../extensions/discord/src/send.js").sendMessageDiscord; -type SendPollDiscord = typeof import("../../../extensions/discord/src/send.js").sendPollDiscord; -type SendTypingDiscord = typeof import("../../../extensions/discord/src/send.js").sendTypingDiscord; -type UnpinMessageDiscord = - typeof import("../../../extensions/discord/src/send.js").unpinMessageDiscord; +type RuntimeDiscordOps = Pick< + PluginRuntimeChannel["discord"], + | "auditChannelPermissions" + | "listDirectoryGroupsLive" + | "listDirectoryPeersLive" + | "probeDiscord" + | "resolveChannelAllowlist" + | "resolveUserAllowlist" + | "sendComponentMessage" + | "sendMessageDiscord" + | "sendPollDiscord" + | "monitorDiscordProvider" +> & { + typing: Pick; + conversationActions: Pick< + PluginRuntimeChannel["discord"]["conversationActions"], + "editMessage" | "deleteMessage" | "pinMessage" | "unpinMessage" | "createThread" | "editChannel" + >; +}; -export function auditDiscordChannelPermissions( - ...args: Parameters -): ReturnType { - return auditDiscordChannelPermissionsImpl(...args); -} - -export function listDiscordDirectoryGroupsLive( - ...args: Parameters -): ReturnType { - return listDiscordDirectoryGroupsLiveImpl(...args); -} - -export function listDiscordDirectoryPeersLive( - ...args: Parameters -): ReturnType { - return listDiscordDirectoryPeersLiveImpl(...args); -} - -export function monitorDiscordProvider( - ...args: Parameters -): ReturnType { - return monitorDiscordProviderImpl(...args); -} - -export function probeDiscord(...args: Parameters): ReturnType { - return probeDiscordImpl(...args); -} - -export function resolveDiscordChannelAllowlist( - ...args: Parameters -): ReturnType { - return resolveDiscordChannelAllowlistImpl(...args); -} - -export function resolveDiscordUserAllowlist( - ...args: Parameters -): ReturnType { - return resolveDiscordUserAllowlistImpl(...args); -} - -export function createThreadDiscord( - ...args: Parameters -): ReturnType { - return createThreadDiscordImpl(...args); -} - -export function deleteMessageDiscord( - ...args: Parameters -): ReturnType { - return deleteMessageDiscordImpl(...args); -} - -export function editChannelDiscord( - ...args: Parameters -): ReturnType { - return editChannelDiscordImpl(...args); -} - -export function editMessageDiscord( - ...args: Parameters -): ReturnType { - return editMessageDiscordImpl(...args); -} - -export function pinMessageDiscord( - ...args: Parameters -): ReturnType { - return pinMessageDiscordImpl(...args); -} - -export function sendDiscordComponentMessage( - ...args: Parameters -): ReturnType { - return sendDiscordComponentMessageImpl(...args); -} - -export function sendMessageDiscord( - ...args: Parameters -): ReturnType { - return sendMessageDiscordImpl(...args); -} - -export function sendPollDiscord(...args: Parameters): ReturnType { - return sendPollDiscordImpl(...args); -} - -export function sendTypingDiscord( - ...args: Parameters -): ReturnType { - return sendTypingDiscordImpl(...args); -} - -export function unpinMessageDiscord( - ...args: Parameters -): ReturnType { - return unpinMessageDiscordImpl(...args); -} +export const runtimeDiscordOps = { + auditChannelPermissions: auditDiscordChannelPermissionsImpl, + listDirectoryGroupsLive: listDiscordDirectoryGroupsLiveImpl, + listDirectoryPeersLive: listDiscordDirectoryPeersLiveImpl, + probeDiscord: probeDiscordImpl, + resolveChannelAllowlist: resolveDiscordChannelAllowlistImpl, + resolveUserAllowlist: resolveDiscordUserAllowlistImpl, + sendComponentMessage: sendDiscordComponentMessageImpl, + sendMessageDiscord: sendMessageDiscordImpl, + sendPollDiscord: sendPollDiscordImpl, + monitorDiscordProvider: monitorDiscordProviderImpl, + typing: { + pulse: sendTypingDiscordImpl, + }, + conversationActions: { + editMessage: editMessageDiscordImpl, + deleteMessage: deleteMessageDiscordImpl, + pinMessage: pinMessageDiscordImpl, + unpinMessage: unpinMessageDiscordImpl, + createThread: createThreadDiscordImpl, + editChannel: editChannelDiscordImpl, + }, +} satisfies RuntimeDiscordOps; diff --git a/src/plugins/runtime/runtime-discord.ts b/src/plugins/runtime/runtime-discord.ts index ae302ad0e5f..033c1631828 100644 --- a/src/plugins/runtime/runtime-discord.ts +++ b/src/plugins/runtime/runtime-discord.ts @@ -12,116 +12,119 @@ import { import { createDiscordTypingLease } from "./runtime-discord-typing.js"; import type { PluginRuntimeChannel } from "./types-channel.js"; -let runtimeDiscordOpsPromise: Promise | null = - null; +type RuntimeDiscordOps = typeof import("./runtime-discord-ops.runtime.js").runtimeDiscordOps; + +let runtimeDiscordOpsPromise: Promise | null = null; function loadRuntimeDiscordOps() { - runtimeDiscordOpsPromise ??= import("./runtime-discord-ops.runtime.js"); + runtimeDiscordOpsPromise ??= import("./runtime-discord-ops.runtime.js").then( + ({ runtimeDiscordOps }) => runtimeDiscordOps, + ); return runtimeDiscordOpsPromise; } const auditChannelPermissionsLazy: PluginRuntimeChannel["discord"]["auditChannelPermissions"] = async (...args) => { - const { auditDiscordChannelPermissions } = await loadRuntimeDiscordOps(); - return auditDiscordChannelPermissions(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.auditChannelPermissions(...args); }; const listDirectoryGroupsLiveLazy: PluginRuntimeChannel["discord"]["listDirectoryGroupsLive"] = async (...args) => { - const { listDiscordDirectoryGroupsLive } = await loadRuntimeDiscordOps(); - return listDiscordDirectoryGroupsLive(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.listDirectoryGroupsLive(...args); }; const listDirectoryPeersLiveLazy: PluginRuntimeChannel["discord"]["listDirectoryPeersLive"] = async (...args) => { - const { listDiscordDirectoryPeersLive } = await loadRuntimeDiscordOps(); - return listDiscordDirectoryPeersLive(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.listDirectoryPeersLive(...args); }; const probeDiscordLazy: PluginRuntimeChannel["discord"]["probeDiscord"] = async (...args) => { - const { probeDiscord } = await loadRuntimeDiscordOps(); - return probeDiscord(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.probeDiscord(...args); }; const resolveChannelAllowlistLazy: PluginRuntimeChannel["discord"]["resolveChannelAllowlist"] = async (...args) => { - const { resolveDiscordChannelAllowlist } = await loadRuntimeDiscordOps(); - return resolveDiscordChannelAllowlist(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.resolveChannelAllowlist(...args); }; const resolveUserAllowlistLazy: PluginRuntimeChannel["discord"]["resolveUserAllowlist"] = async ( ...args ) => { - const { resolveDiscordUserAllowlist } = await loadRuntimeDiscordOps(); - return resolveDiscordUserAllowlist(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.resolveUserAllowlist(...args); }; const sendComponentMessageLazy: PluginRuntimeChannel["discord"]["sendComponentMessage"] = async ( ...args ) => { - const { sendDiscordComponentMessage } = await loadRuntimeDiscordOps(); - return sendDiscordComponentMessage(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.sendComponentMessage(...args); }; const sendMessageDiscordLazy: PluginRuntimeChannel["discord"]["sendMessageDiscord"] = async ( ...args ) => { - const { sendMessageDiscord } = await loadRuntimeDiscordOps(); - return sendMessageDiscord(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.sendMessageDiscord(...args); }; const sendPollDiscordLazy: PluginRuntimeChannel["discord"]["sendPollDiscord"] = async (...args) => { - const { sendPollDiscord } = await loadRuntimeDiscordOps(); - return sendPollDiscord(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.sendPollDiscord(...args); }; const monitorDiscordProviderLazy: PluginRuntimeChannel["discord"]["monitorDiscordProvider"] = async (...args) => { - const { monitorDiscordProvider } = await loadRuntimeDiscordOps(); - return monitorDiscordProvider(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.monitorDiscordProvider(...args); }; const sendTypingDiscordLazy: PluginRuntimeChannel["discord"]["typing"]["pulse"] = async ( ...args ) => { - const { sendTypingDiscord } = await loadRuntimeDiscordOps(); - return sendTypingDiscord(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.typing.pulse(...args); }; const editMessageDiscordLazy: PluginRuntimeChannel["discord"]["conversationActions"]["editMessage"] = async (...args) => { - const { editMessageDiscord } = await loadRuntimeDiscordOps(); - return editMessageDiscord(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.conversationActions.editMessage(...args); }; const deleteMessageDiscordLazy: PluginRuntimeChannel["discord"]["conversationActions"]["deleteMessage"] = async (...args) => { - const { deleteMessageDiscord } = await loadRuntimeDiscordOps(); - return deleteMessageDiscord(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.conversationActions.deleteMessage(...args); }; const pinMessageDiscordLazy: PluginRuntimeChannel["discord"]["conversationActions"]["pinMessage"] = async (...args) => { - const { pinMessageDiscord } = await loadRuntimeDiscordOps(); - return pinMessageDiscord(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.conversationActions.pinMessage(...args); }; const unpinMessageDiscordLazy: PluginRuntimeChannel["discord"]["conversationActions"]["unpinMessage"] = async (...args) => { - const { unpinMessageDiscord } = await loadRuntimeDiscordOps(); - return unpinMessageDiscord(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.conversationActions.unpinMessage(...args); }; const createThreadDiscordLazy: PluginRuntimeChannel["discord"]["conversationActions"]["createThread"] = async (...args) => { - const { createThreadDiscord } = await loadRuntimeDiscordOps(); - return createThreadDiscord(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.conversationActions.createThread(...args); }; const editChannelDiscordLazy: PluginRuntimeChannel["discord"]["conversationActions"]["editChannel"] = async (...args) => { - const { editChannelDiscord } = await loadRuntimeDiscordOps(); - return editChannelDiscord(...args); + const runtimeDiscordOps = await loadRuntimeDiscordOps(); + return runtimeDiscordOps.conversationActions.editChannel(...args); }; export function createRuntimeDiscord(): PluginRuntimeChannel["discord"] { diff --git a/src/plugins/runtime/runtime-slack-ops.runtime.ts b/src/plugins/runtime/runtime-slack-ops.runtime.ts index b01568bc491..4f4dc1aeda7 100644 --- a/src/plugins/runtime/runtime-slack-ops.runtime.ts +++ b/src/plugins/runtime/runtime-slack-ops.runtime.ts @@ -8,63 +8,27 @@ import { resolveSlackChannelAllowlist as resolveSlackChannelAllowlistImpl } from import { resolveSlackUserAllowlist as resolveSlackUserAllowlistImpl } from "../../../extensions/slack/src/resolve-users.js"; import { sendMessageSlack as sendMessageSlackImpl } from "../../../extensions/slack/src/send.js"; import { handleSlackAction as handleSlackActionImpl } from "../../agents/tools/slack-actions.js"; +import type { PluginRuntimeChannel } from "./types-channel.js"; -type ListSlackDirectoryGroupsLive = - typeof import("../../../extensions/slack/src/directory-live.js").listSlackDirectoryGroupsLive; -type ListSlackDirectoryPeersLive = - typeof import("../../../extensions/slack/src/directory-live.js").listSlackDirectoryPeersLive; -type MonitorSlackProvider = - typeof import("../../../extensions/slack/src/index.js").monitorSlackProvider; -type ProbeSlack = typeof import("../../../extensions/slack/src/probe.js").probeSlack; -type ResolveSlackChannelAllowlist = - typeof import("../../../extensions/slack/src/resolve-channels.js").resolveSlackChannelAllowlist; -type ResolveSlackUserAllowlist = - typeof import("../../../extensions/slack/src/resolve-users.js").resolveSlackUserAllowlist; -type SendMessageSlack = typeof import("../../../extensions/slack/src/send.js").sendMessageSlack; -type HandleSlackAction = typeof import("../../agents/tools/slack-actions.js").handleSlackAction; +type RuntimeSlackOps = Pick< + PluginRuntimeChannel["slack"], + | "listDirectoryGroupsLive" + | "listDirectoryPeersLive" + | "probeSlack" + | "resolveChannelAllowlist" + | "resolveUserAllowlist" + | "sendMessageSlack" + | "monitorSlackProvider" + | "handleSlackAction" +>; -export function listSlackDirectoryGroupsLive( - ...args: Parameters -): ReturnType { - return listSlackDirectoryGroupsLiveImpl(...args); -} - -export function listSlackDirectoryPeersLive( - ...args: Parameters -): ReturnType { - return listSlackDirectoryPeersLiveImpl(...args); -} - -export function monitorSlackProvider( - ...args: Parameters -): ReturnType { - return monitorSlackProviderImpl(...args); -} - -export function probeSlack(...args: Parameters): ReturnType { - return probeSlackImpl(...args); -} - -export function resolveSlackChannelAllowlist( - ...args: Parameters -): ReturnType { - return resolveSlackChannelAllowlistImpl(...args); -} - -export function resolveSlackUserAllowlist( - ...args: Parameters -): ReturnType { - return resolveSlackUserAllowlistImpl(...args); -} - -export function sendMessageSlack( - ...args: Parameters -): ReturnType { - return sendMessageSlackImpl(...args); -} - -export function handleSlackAction( - ...args: Parameters -): ReturnType { - return handleSlackActionImpl(...args); -} +export const runtimeSlackOps = { + listDirectoryGroupsLive: listSlackDirectoryGroupsLiveImpl, + listDirectoryPeersLive: listSlackDirectoryPeersLiveImpl, + probeSlack: probeSlackImpl, + resolveChannelAllowlist: resolveSlackChannelAllowlistImpl, + resolveUserAllowlist: resolveSlackUserAllowlistImpl, + sendMessageSlack: sendMessageSlackImpl, + monitorSlackProvider: monitorSlackProviderImpl, + handleSlackAction: handleSlackActionImpl, +} satisfies RuntimeSlackOps; diff --git a/src/plugins/runtime/runtime-slack.ts b/src/plugins/runtime/runtime-slack.ts index 9579aed4c1b..23d34a7e5f4 100644 --- a/src/plugins/runtime/runtime-slack.ts +++ b/src/plugins/runtime/runtime-slack.ts @@ -1,60 +1,64 @@ import type { PluginRuntimeChannel } from "./types-channel.js"; -let runtimeSlackOpsPromise: Promise | null = null; +type RuntimeSlackOps = typeof import("./runtime-slack-ops.runtime.js").runtimeSlackOps; + +let runtimeSlackOpsPromise: Promise | null = null; function loadRuntimeSlackOps() { - runtimeSlackOpsPromise ??= import("./runtime-slack-ops.runtime.js"); + runtimeSlackOpsPromise ??= import("./runtime-slack-ops.runtime.js").then( + ({ runtimeSlackOps }) => runtimeSlackOps, + ); return runtimeSlackOpsPromise; } const listDirectoryGroupsLiveLazy: PluginRuntimeChannel["slack"]["listDirectoryGroupsLive"] = async (...args) => { - const { listSlackDirectoryGroupsLive } = await loadRuntimeSlackOps(); - return listSlackDirectoryGroupsLive(...args); + const runtimeSlackOps = await loadRuntimeSlackOps(); + return runtimeSlackOps.listDirectoryGroupsLive(...args); }; const listDirectoryPeersLiveLazy: PluginRuntimeChannel["slack"]["listDirectoryPeersLive"] = async ( ...args ) => { - const { listSlackDirectoryPeersLive } = await loadRuntimeSlackOps(); - return listSlackDirectoryPeersLive(...args); + const runtimeSlackOps = await loadRuntimeSlackOps(); + return runtimeSlackOps.listDirectoryPeersLive(...args); }; const probeSlackLazy: PluginRuntimeChannel["slack"]["probeSlack"] = async (...args) => { - const { probeSlack } = await loadRuntimeSlackOps(); - return probeSlack(...args); + const runtimeSlackOps = await loadRuntimeSlackOps(); + return runtimeSlackOps.probeSlack(...args); }; const resolveChannelAllowlistLazy: PluginRuntimeChannel["slack"]["resolveChannelAllowlist"] = async (...args) => { - const { resolveSlackChannelAllowlist } = await loadRuntimeSlackOps(); - return resolveSlackChannelAllowlist(...args); + const runtimeSlackOps = await loadRuntimeSlackOps(); + return runtimeSlackOps.resolveChannelAllowlist(...args); }; const resolveUserAllowlistLazy: PluginRuntimeChannel["slack"]["resolveUserAllowlist"] = async ( ...args ) => { - const { resolveSlackUserAllowlist } = await loadRuntimeSlackOps(); - return resolveSlackUserAllowlist(...args); + const runtimeSlackOps = await loadRuntimeSlackOps(); + return runtimeSlackOps.resolveUserAllowlist(...args); }; const sendMessageSlackLazy: PluginRuntimeChannel["slack"]["sendMessageSlack"] = async (...args) => { - const { sendMessageSlack } = await loadRuntimeSlackOps(); - return sendMessageSlack(...args); + const runtimeSlackOps = await loadRuntimeSlackOps(); + return runtimeSlackOps.sendMessageSlack(...args); }; const monitorSlackProviderLazy: PluginRuntimeChannel["slack"]["monitorSlackProvider"] = async ( ...args ) => { - const { monitorSlackProvider } = await loadRuntimeSlackOps(); - return monitorSlackProvider(...args); + const runtimeSlackOps = await loadRuntimeSlackOps(); + return runtimeSlackOps.monitorSlackProvider(...args); }; const handleSlackActionLazy: PluginRuntimeChannel["slack"]["handleSlackAction"] = async ( ...args ) => { - const { handleSlackAction } = await loadRuntimeSlackOps(); - return handleSlackAction(...args); + const runtimeSlackOps = await loadRuntimeSlackOps(); + return runtimeSlackOps.handleSlackAction(...args); }; export function createRuntimeSlack(): PluginRuntimeChannel["slack"] { diff --git a/src/plugins/runtime/runtime-telegram-ops.runtime.ts b/src/plugins/runtime/runtime-telegram-ops.runtime.ts index cc99abfb1c4..b8b915e6065 100644 --- a/src/plugins/runtime/runtime-telegram-ops.runtime.ts +++ b/src/plugins/runtime/runtime-telegram-ops.runtime.ts @@ -1,7 +1,4 @@ -import { - auditTelegramGroupMembership as auditTelegramGroupMembershipImpl, - collectTelegramUnmentionedGroupIds as collectTelegramUnmentionedGroupIdsImpl, -} from "../../../extensions/telegram/src/audit.js"; +import { auditTelegramGroupMembership as auditTelegramGroupMembershipImpl } from "../../../extensions/telegram/src/audit.js"; import { monitorTelegramProvider as monitorTelegramProviderImpl } from "../../../extensions/telegram/src/monitor.js"; import { probeTelegram as probeTelegramImpl } from "../../../extensions/telegram/src/probe.js"; import { @@ -15,113 +12,43 @@ import { sendTypingTelegram as sendTypingTelegramImpl, unpinMessageTelegram as unpinMessageTelegramImpl, } from "../../../extensions/telegram/src/send.js"; -import { resolveTelegramToken as resolveTelegramTokenImpl } from "../../../extensions/telegram/src/token.js"; +import type { PluginRuntimeChannel } from "./types-channel.js"; -type AuditTelegramGroupMembership = - typeof import("../../../extensions/telegram/src/audit.js").auditTelegramGroupMembership; -type CollectTelegramUnmentionedGroupIds = - typeof import("../../../extensions/telegram/src/audit.js").collectTelegramUnmentionedGroupIds; -type MonitorTelegramProvider = - typeof import("../../../extensions/telegram/src/monitor.js").monitorTelegramProvider; -type ProbeTelegram = typeof import("../../../extensions/telegram/src/probe.js").probeTelegram; -type DeleteMessageTelegram = - typeof import("../../../extensions/telegram/src/send.js").deleteMessageTelegram; -type EditMessageReplyMarkupTelegram = - typeof import("../../../extensions/telegram/src/send.js").editMessageReplyMarkupTelegram; -type EditMessageTelegram = - typeof import("../../../extensions/telegram/src/send.js").editMessageTelegram; -type PinMessageTelegram = - typeof import("../../../extensions/telegram/src/send.js").pinMessageTelegram; -type RenameForumTopicTelegram = - typeof import("../../../extensions/telegram/src/send.js").renameForumTopicTelegram; -type SendMessageTelegram = - typeof import("../../../extensions/telegram/src/send.js").sendMessageTelegram; -type SendPollTelegram = typeof import("../../../extensions/telegram/src/send.js").sendPollTelegram; -type SendTypingTelegram = - typeof import("../../../extensions/telegram/src/send.js").sendTypingTelegram; -type UnpinMessageTelegram = - typeof import("../../../extensions/telegram/src/send.js").unpinMessageTelegram; -type ResolveTelegramToken = - typeof import("../../../extensions/telegram/src/token.js").resolveTelegramToken; +type RuntimeTelegramOps = Pick< + PluginRuntimeChannel["telegram"], + | "auditGroupMembership" + | "probeTelegram" + | "sendMessageTelegram" + | "sendPollTelegram" + | "monitorTelegramProvider" +> & { + typing: Pick; + conversationActions: Pick< + PluginRuntimeChannel["telegram"]["conversationActions"], + | "editMessage" + | "editReplyMarkup" + | "deleteMessage" + | "renameTopic" + | "pinMessage" + | "unpinMessage" + >; +}; -export function auditTelegramGroupMembership( - ...args: Parameters -): ReturnType { - return auditTelegramGroupMembershipImpl(...args); -} - -export function collectTelegramUnmentionedGroupIds( - ...args: Parameters -): ReturnType { - return collectTelegramUnmentionedGroupIdsImpl(...args); -} - -export function monitorTelegramProvider( - ...args: Parameters -): ReturnType { - return monitorTelegramProviderImpl(...args); -} - -export function probeTelegram(...args: Parameters): ReturnType { - return probeTelegramImpl(...args); -} - -export function deleteMessageTelegram( - ...args: Parameters -): ReturnType { - return deleteMessageTelegramImpl(...args); -} - -export function editMessageReplyMarkupTelegram( - ...args: Parameters -): ReturnType { - return editMessageReplyMarkupTelegramImpl(...args); -} - -export function editMessageTelegram( - ...args: Parameters -): ReturnType { - return editMessageTelegramImpl(...args); -} - -export function pinMessageTelegram( - ...args: Parameters -): ReturnType { - return pinMessageTelegramImpl(...args); -} - -export function renameForumTopicTelegram( - ...args: Parameters -): ReturnType { - return renameForumTopicTelegramImpl(...args); -} - -export function sendMessageTelegram( - ...args: Parameters -): ReturnType { - return sendMessageTelegramImpl(...args); -} - -export function sendPollTelegram( - ...args: Parameters -): ReturnType { - return sendPollTelegramImpl(...args); -} - -export function sendTypingTelegram( - ...args: Parameters -): ReturnType { - return sendTypingTelegramImpl(...args); -} - -export function unpinMessageTelegram( - ...args: Parameters -): ReturnType { - return unpinMessageTelegramImpl(...args); -} - -export function resolveTelegramToken( - ...args: Parameters -): ReturnType { - return resolveTelegramTokenImpl(...args); -} +export const runtimeTelegramOps = { + auditGroupMembership: auditTelegramGroupMembershipImpl, + probeTelegram: probeTelegramImpl, + sendMessageTelegram: sendMessageTelegramImpl, + sendPollTelegram: sendPollTelegramImpl, + monitorTelegramProvider: monitorTelegramProviderImpl, + typing: { + pulse: sendTypingTelegramImpl, + }, + conversationActions: { + editMessage: editMessageTelegramImpl, + editReplyMarkup: editMessageReplyMarkupTelegramImpl, + deleteMessage: deleteMessageTelegramImpl, + renameTopic: renameForumTopicTelegramImpl, + pinMessage: pinMessageTelegramImpl, + unpinMessage: unpinMessageTelegramImpl, + }, +} satisfies RuntimeTelegramOps; diff --git a/src/plugins/runtime/runtime-telegram.ts b/src/plugins/runtime/runtime-telegram.ts index 22061a7e00d..d0d71d08c4e 100644 --- a/src/plugins/runtime/runtime-telegram.ts +++ b/src/plugins/runtime/runtime-telegram.ts @@ -8,87 +8,90 @@ import { resolveTelegramToken } from "../../../extensions/telegram/src/token.js" import { createTelegramTypingLease } from "./runtime-telegram-typing.js"; import type { PluginRuntimeChannel } from "./types-channel.js"; -let runtimeTelegramOpsPromise: Promise | null = - null; +type RuntimeTelegramOps = typeof import("./runtime-telegram-ops.runtime.js").runtimeTelegramOps; + +let runtimeTelegramOpsPromise: Promise | null = null; function loadRuntimeTelegramOps() { - runtimeTelegramOpsPromise ??= import("./runtime-telegram-ops.runtime.js"); + runtimeTelegramOpsPromise ??= import("./runtime-telegram-ops.runtime.js").then( + ({ runtimeTelegramOps }) => runtimeTelegramOps, + ); return runtimeTelegramOpsPromise; } const auditGroupMembershipLazy: PluginRuntimeChannel["telegram"]["auditGroupMembership"] = async ( ...args ) => { - const { auditTelegramGroupMembership } = await loadRuntimeTelegramOps(); - return auditTelegramGroupMembership(...args); + const runtimeTelegramOps = await loadRuntimeTelegramOps(); + return runtimeTelegramOps.auditGroupMembership(...args); }; const probeTelegramLazy: PluginRuntimeChannel["telegram"]["probeTelegram"] = async (...args) => { - const { probeTelegram } = await loadRuntimeTelegramOps(); - return probeTelegram(...args); + const runtimeTelegramOps = await loadRuntimeTelegramOps(); + return runtimeTelegramOps.probeTelegram(...args); }; const sendMessageTelegramLazy: PluginRuntimeChannel["telegram"]["sendMessageTelegram"] = async ( ...args ) => { - const { sendMessageTelegram } = await loadRuntimeTelegramOps(); - return sendMessageTelegram(...args); + const runtimeTelegramOps = await loadRuntimeTelegramOps(); + return runtimeTelegramOps.sendMessageTelegram(...args); }; const sendPollTelegramLazy: PluginRuntimeChannel["telegram"]["sendPollTelegram"] = async ( ...args ) => { - const { sendPollTelegram } = await loadRuntimeTelegramOps(); - return sendPollTelegram(...args); + const runtimeTelegramOps = await loadRuntimeTelegramOps(); + return runtimeTelegramOps.sendPollTelegram(...args); }; const monitorTelegramProviderLazy: PluginRuntimeChannel["telegram"]["monitorTelegramProvider"] = async (...args) => { - const { monitorTelegramProvider } = await loadRuntimeTelegramOps(); - return monitorTelegramProvider(...args); + const runtimeTelegramOps = await loadRuntimeTelegramOps(); + return runtimeTelegramOps.monitorTelegramProvider(...args); }; const sendTypingTelegramLazy: PluginRuntimeChannel["telegram"]["typing"]["pulse"] = async ( ...args ) => { - const { sendTypingTelegram } = await loadRuntimeTelegramOps(); - return sendTypingTelegram(...args); + const runtimeTelegramOps = await loadRuntimeTelegramOps(); + return runtimeTelegramOps.typing.pulse(...args); }; const editMessageTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["editMessage"] = async (...args) => { - const { editMessageTelegram } = await loadRuntimeTelegramOps(); - return editMessageTelegram(...args); + const runtimeTelegramOps = await loadRuntimeTelegramOps(); + return runtimeTelegramOps.conversationActions.editMessage(...args); }; const editMessageReplyMarkupTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["editReplyMarkup"] = async (...args) => { - const { editMessageReplyMarkupTelegram } = await loadRuntimeTelegramOps(); - return editMessageReplyMarkupTelegram(...args); + const runtimeTelegramOps = await loadRuntimeTelegramOps(); + return runtimeTelegramOps.conversationActions.editReplyMarkup(...args); }; const deleteMessageTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["deleteMessage"] = async (...args) => { - const { deleteMessageTelegram } = await loadRuntimeTelegramOps(); - return deleteMessageTelegram(...args); + const runtimeTelegramOps = await loadRuntimeTelegramOps(); + return runtimeTelegramOps.conversationActions.deleteMessage(...args); }; const renameForumTopicTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["renameTopic"] = async (...args) => { - const { renameForumTopicTelegram } = await loadRuntimeTelegramOps(); - return renameForumTopicTelegram(...args); + const runtimeTelegramOps = await loadRuntimeTelegramOps(); + return runtimeTelegramOps.conversationActions.renameTopic(...args); }; const pinMessageTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["pinMessage"] = async (...args) => { - const { pinMessageTelegram } = await loadRuntimeTelegramOps(); - return pinMessageTelegram(...args); + const runtimeTelegramOps = await loadRuntimeTelegramOps(); + return runtimeTelegramOps.conversationActions.pinMessage(...args); }; const unpinMessageTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["unpinMessage"] = async (...args) => { - const { unpinMessageTelegram } = await loadRuntimeTelegramOps(); - return unpinMessageTelegram(...args); + const runtimeTelegramOps = await loadRuntimeTelegramOps(); + return runtimeTelegramOps.conversationActions.unpinMessage(...args); }; export function createRuntimeTelegram(): PluginRuntimeChannel["telegram"] { diff --git a/src/plugins/runtime/runtime-whatsapp-login.runtime.ts b/src/plugins/runtime/runtime-whatsapp-login.runtime.ts index 4d44c7c87f6..2760db7311d 100644 --- a/src/plugins/runtime/runtime-whatsapp-login.runtime.ts +++ b/src/plugins/runtime/runtime-whatsapp-login.runtime.ts @@ -1,7 +1,8 @@ import { loginWeb as loginWebImpl } from "../../../extensions/whatsapp/src/login.js"; +import type { PluginRuntime } from "./types.js"; -type LoginWeb = typeof import("../../../extensions/whatsapp/src/login.js").loginWeb; +type RuntimeWhatsAppLogin = Pick; -export function loginWeb(...args: Parameters): ReturnType { - return loginWebImpl(...args); -} +export const runtimeWhatsAppLogin = { + loginWeb: loginWebImpl, +} satisfies RuntimeWhatsAppLogin; diff --git a/src/plugins/runtime/runtime-whatsapp-outbound.runtime.ts b/src/plugins/runtime/runtime-whatsapp-outbound.runtime.ts index 023e9e93e23..71aa83ce9ac 100644 --- a/src/plugins/runtime/runtime-whatsapp-outbound.runtime.ts +++ b/src/plugins/runtime/runtime-whatsapp-outbound.runtime.ts @@ -2,19 +2,14 @@ import { sendMessageWhatsApp as sendMessageWhatsAppImpl, sendPollWhatsApp as sendPollWhatsAppImpl, } from "../../../extensions/whatsapp/src/send.js"; +import type { PluginRuntime } from "./types.js"; -type SendMessageWhatsApp = - typeof import("../../../extensions/whatsapp/src/send.js").sendMessageWhatsApp; -type SendPollWhatsApp = typeof import("../../../extensions/whatsapp/src/send.js").sendPollWhatsApp; +type RuntimeWhatsAppOutbound = Pick< + PluginRuntime["channel"]["whatsapp"], + "sendMessageWhatsApp" | "sendPollWhatsApp" +>; -export function sendMessageWhatsApp( - ...args: Parameters -): ReturnType { - return sendMessageWhatsAppImpl(...args); -} - -export function sendPollWhatsApp( - ...args: Parameters -): ReturnType { - return sendPollWhatsAppImpl(...args); -} +export const runtimeWhatsAppOutbound = { + sendMessageWhatsApp: sendMessageWhatsAppImpl, + sendPollWhatsApp: sendPollWhatsAppImpl, +} satisfies RuntimeWhatsAppOutbound; diff --git a/src/plugins/runtime/runtime-whatsapp.ts b/src/plugins/runtime/runtime-whatsapp.ts index 21a92aefe09..10f8e9e6a94 100644 --- a/src/plugins/runtime/runtime-whatsapp.ts +++ b/src/plugins/runtime/runtime-whatsapp.ts @@ -9,23 +9,28 @@ import { import { createRuntimeWhatsAppLoginTool } from "./runtime-whatsapp-login-tool.js"; import type { PluginRuntime } from "./types.js"; +type RuntimeWhatsAppOutbound = + typeof import("./runtime-whatsapp-outbound.runtime.js").runtimeWhatsAppOutbound; +type RuntimeWhatsAppLogin = + typeof import("./runtime-whatsapp-login.runtime.js").runtimeWhatsAppLogin; + const sendMessageWhatsAppLazy: PluginRuntime["channel"]["whatsapp"]["sendMessageWhatsApp"] = async ( ...args ) => { - const { sendMessageWhatsApp } = await loadWebOutbound(); - return sendMessageWhatsApp(...args); + const runtimeWhatsAppOutbound = await loadWebOutbound(); + return runtimeWhatsAppOutbound.sendMessageWhatsApp(...args); }; const sendPollWhatsAppLazy: PluginRuntime["channel"]["whatsapp"]["sendPollWhatsApp"] = async ( ...args ) => { - const { sendPollWhatsApp } = await loadWebOutbound(); - return sendPollWhatsApp(...args); + const runtimeWhatsAppOutbound = await loadWebOutbound(); + return runtimeWhatsAppOutbound.sendPollWhatsApp(...args); }; const loginWebLazy: PluginRuntime["channel"]["whatsapp"]["loginWeb"] = async (...args) => { - const { loginWeb } = await loadWebLogin(); - return loginWeb(...args); + const runtimeWhatsAppLogin = await loadWebLogin(); + return runtimeWhatsAppLogin.loginWeb(...args); }; const startWebLoginWithQrLazy: PluginRuntime["channel"]["whatsapp"]["startWebLoginWithQr"] = async ( @@ -59,20 +64,23 @@ let webLoginQrPromise: Promise< typeof import("../../../extensions/whatsapp/src/login-qr.js") > | null = null; let webChannelPromise: Promise | null = null; -let webOutboundPromise: Promise | null = - null; -let webLoginPromise: Promise | null = null; +let webOutboundPromise: Promise | null = null; +let webLoginPromise: Promise | null = null; let whatsappActionsPromise: Promise< typeof import("../../agents/tools/whatsapp-actions.js") > | null = null; function loadWebOutbound() { - webOutboundPromise ??= import("./runtime-whatsapp-outbound.runtime.js"); + webOutboundPromise ??= import("./runtime-whatsapp-outbound.runtime.js").then( + ({ runtimeWhatsAppOutbound }) => runtimeWhatsAppOutbound, + ); return webOutboundPromise; } function loadWebLogin() { - webLoginPromise ??= import("./runtime-whatsapp-login.runtime.js"); + webLoginPromise ??= import("./runtime-whatsapp-login.runtime.js").then( + ({ runtimeWhatsAppLogin }) => runtimeWhatsAppLogin, + ); return webLoginPromise; } From 73ca53ee026db82b559204f6e8d667b7caac549c Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 17 Mar 2026 00:01:17 -0700 Subject: [PATCH 6/6] fix: remove discord setup rebase marker --- extensions/discord/src/setup-surface.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/discord/src/setup-surface.ts b/extensions/discord/src/setup-surface.ts index 78e1c1da804..66f7f8bbf4b 100644 --- a/extensions/discord/src/setup-surface.ts +++ b/extensions/discord/src/setup-surface.ts @@ -100,7 +100,6 @@ async function resolveDiscordGroupAllowlist(params: { }); } -<<<<<<< HEAD export const discordSetupWizard: ChannelSetupWizard = createDiscordSetupWizardBase({ promptAllowFrom: promptDiscordAllowFrom, resolveAllowFromEntries: async ({ cfg, accountId, credentialValues, entries }) =>