diff --git a/extensions/irc/src/setup-core.test.ts b/extensions/irc/src/setup-core.test.ts index 1c7c5554316..33d87143beb 100644 --- a/extensions/irc/src/setup-core.test.ts +++ b/extensions/irc/src/setup-core.test.ts @@ -126,26 +126,31 @@ describe("irc setup core", () => { }); it("validates required input and applies normalized account config", () => { + const validateInput = ircSetupAdapter.validateInput; + const applyAccountConfig = ircSetupAdapter.applyAccountConfig; + expect(validateInput).toBeTypeOf("function"); + expect(applyAccountConfig).toBeTypeOf("function"); + expect( - ircSetupAdapter.validateInput({ + validateInput!({ input: { host: "", nick: "openclaw" }, } as never), ).toBe("IRC requires host."); expect( - ircSetupAdapter.validateInput({ + validateInput!({ input: { host: "irc.libera.chat", nick: "" }, } as never), ).toBe("IRC requires nick."); expect( - ircSetupAdapter.validateInput({ + validateInput!({ input: { host: "irc.libera.chat", nick: "openclaw" }, } as never), ).toBeNull(); expect( - ircSetupAdapter.applyAccountConfig({ + applyAccountConfig!({ cfg: { channels: { irc: {} } }, accountId: "default", input: { diff --git a/extensions/mattermost/src/setup-core.test.ts b/extensions/mattermost/src/setup-core.test.ts index 4dac2947b4c..4d04a14a8cc 100644 --- a/extensions/mattermost/src/setup-core.test.ts +++ b/extensions/mattermost/src/setup-core.test.ts @@ -64,9 +64,11 @@ describe("mattermost setup core", () => { it("validates env and explicit credential requirements", async () => { const { mattermostSetupAdapter } = await import("./setup-core.js"); + const validateInput = mattermostSetupAdapter.validateInput; + expect(validateInput).toBeTypeOf("function"); expect( - mattermostSetupAdapter.validateInput({ + validateInput!({ accountId: "secondary", input: { useEnv: true }, } as never), @@ -74,7 +76,7 @@ describe("mattermost setup core", () => { normalizeMattermostBaseUrl.mockReturnValue(undefined); expect( - mattermostSetupAdapter.validateInput({ + validateInput!({ accountId: DEFAULT_ACCOUNT_ID, input: { useEnv: false, botToken: "tok", httpUrl: "not-a-url" }, } as never), @@ -82,7 +84,7 @@ describe("mattermost setup core", () => { normalizeMattermostBaseUrl.mockReturnValue("https://chat.example.com"); expect( - mattermostSetupAdapter.validateInput({ + validateInput!({ accountId: DEFAULT_ACCOUNT_ID, input: { useEnv: false, botToken: "tok", httpUrl: "https://chat.example.com" }, } as never), @@ -92,9 +94,11 @@ describe("mattermost setup core", () => { it("applies normalized config for default and named accounts", async () => { normalizeMattermostBaseUrl.mockReturnValue("https://chat.example.com"); const { mattermostSetupAdapter } = await import("./setup-core.js"); + const applyAccountConfig = mattermostSetupAdapter.applyAccountConfig; + expect(applyAccountConfig).toBeTypeOf("function"); expect( - mattermostSetupAdapter.applyAccountConfig({ + applyAccountConfig!({ cfg: { channels: { mattermost: {} } }, accountId: DEFAULT_ACCOUNT_ID, input: { @@ -115,7 +119,7 @@ describe("mattermost setup core", () => { }); expect( - mattermostSetupAdapter.applyAccountConfig({ + applyAccountConfig!({ cfg: { channels: { mattermost: { diff --git a/extensions/msteams/src/monitor-types.ts b/extensions/msteams/src/monitor-types.ts index 7035838a815..c8674c2dce5 100644 --- a/extensions/msteams/src/monitor-types.ts +++ b/extensions/msteams/src/monitor-types.ts @@ -1,5 +1,6 @@ export type MSTeamsMonitorLogger = { debug?: (message: string, meta?: Record) => void; info: (message: string, meta?: Record) => void; + warn?: (message: string, meta?: Record) => void; error: (message: string, meta?: Record) => void; }; diff --git a/extensions/msteams/src/reply-dispatcher.ts b/extensions/msteams/src/reply-dispatcher.ts index 8a00be2f81a..b54bf82da98 100644 --- a/extensions/msteams/src/reply-dispatcher.ts +++ b/extensions/msteams/src/reply-dispatcher.ts @@ -16,6 +16,7 @@ import { import { buildConversationReference, type MSTeamsAdapter, + type MSTeamsRenderedMessage, renderReplyPayloadsToMessages, sendMSTeamsMessages, } from "./messenger.js"; diff --git a/extensions/nextcloud-talk/src/setup-core.test.ts b/extensions/nextcloud-talk/src/setup-core.test.ts index becb95c50dd..dcc1b031d4a 100644 --- a/extensions/nextcloud-talk/src/setup-core.test.ts +++ b/extensions/nextcloud-talk/src/setup-core.test.ts @@ -103,29 +103,34 @@ describe("nextcloud talk setup core", () => { }); it("validates env/default-account constraints and applies config patches", () => { + const validateInput = nextcloudTalkSetupAdapter.validateInput; + const applyAccountConfig = nextcloudTalkSetupAdapter.applyAccountConfig; + expect(validateInput).toBeTypeOf("function"); + expect(applyAccountConfig).toBeTypeOf("function"); + expect( - nextcloudTalkSetupAdapter.validateInput({ + validateInput!({ accountId: "work", input: { useEnv: true }, } as never), ).toBe("NEXTCLOUD_TALK_BOT_SECRET can only be used for the default account."); expect( - nextcloudTalkSetupAdapter.validateInput({ + validateInput!({ accountId: DEFAULT_ACCOUNT_ID, input: { useEnv: false, baseUrl: "", secret: "" }, } as never), ).toBe("Nextcloud Talk requires bot secret or --secret-file (or --use-env)."); expect( - nextcloudTalkSetupAdapter.validateInput({ + validateInput!({ accountId: DEFAULT_ACCOUNT_ID, input: { useEnv: false, secret: "secret", baseUrl: "" }, } as never), ).toBe("Nextcloud Talk requires --base-url."); expect( - nextcloudTalkSetupAdapter.applyAccountConfig({ + applyAccountConfig!({ cfg: { channels: { "nextcloud-talk": {}, @@ -150,7 +155,7 @@ describe("nextcloud talk setup core", () => { }); expect( - nextcloudTalkSetupAdapter.applyAccountConfig({ + applyAccountConfig!({ cfg: { channels: { "nextcloud-talk": { diff --git a/src/plugins/bundled-plugin-naming.test.ts b/src/plugins/bundled-plugin-naming.test.ts index 69155047656..07d9b7d7e56 100644 --- a/src/plugins/bundled-plugin-naming.test.ts +++ b/src/plugins/bundled-plugin-naming.test.ts @@ -31,7 +31,14 @@ const DIR_ID_EXCEPTIONS = new Map([ // Historical directory name kept until a wider repo cleanup is worth the churn. ["kimi-coding", "kimi"], ]); -const ALLOWED_PACKAGE_SUFFIXES = ["", "-provider", "-plugin", "-speech", "-sandbox"] as const; +const ALLOWED_PACKAGE_SUFFIXES = [ + "", + "-provider", + "-plugin", + "-speech", + "-sandbox", + "-media-understanding", +] as const; function readJsonFile(filePath: string): T { return JSON.parse(fs.readFileSync(filePath, "utf8")) as T; diff --git a/src/tts/tts.test.ts b/src/tts/tts.test.ts index 0ac16566fe5..9ddc7b289e9 100644 --- a/src/tts/tts.test.ts +++ b/src/tts/tts.test.ts @@ -1,9 +1,14 @@ import { completeSimple, type AssistantMessage } from "@mariozechner/pi-ai"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { buildElevenLabsSpeechProvider } from "../../extensions/elevenlabs/speech-provider.ts"; +import { buildMicrosoftSpeechProvider } from "../../extensions/microsoft/speech-provider.ts"; +import { buildOpenAISpeechProvider } from "../../extensions/openai/speech-provider.ts"; import { ensureCustomApiRegistered } from "../agents/custom-api-registry.js"; import { getApiKeyForModel } from "../agents/model-auth.js"; import { resolveModelAsync } from "../agents/pi-embedded-runner/model.js"; import type { OpenClawConfig } from "../config/config.js"; +import { createEmptyPluginRegistry } from "../plugins/registry-empty.js"; +import { setActivePluginRegistry } from "../plugins/runtime.js"; import { withEnv } from "../test-utils/env.js"; import * as tts from "./tts.js"; @@ -124,6 +129,13 @@ function createOpenAiTelephonyCfg(model: "tts-1" | "gpt-4o-mini-tts"): OpenClawC describe("tts", () => { beforeEach(() => { + const registry = createEmptyPluginRegistry(); + registry.speechProviders = [ + { pluginId: "openai", provider: buildOpenAISpeechProvider(), source: "test" }, + { pluginId: "microsoft", provider: buildMicrosoftSpeechProvider(), source: "test" }, + { pluginId: "elevenlabs", provider: buildElevenLabsSpeechProvider(), source: "test" }, + ]; + setActivePluginRegistry(registry, "tts-test"); vi.clearAllMocks(); vi.mocked(completeSimple).mockResolvedValue( mockAssistantMessage([{ type: "text", text: "Summary" }]),