diff --git a/CHANGELOG.md b/CHANGELOG.md index bfda6d25c63..63f86409009 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -110,6 +110,7 @@ Docs: https://docs.openclaw.ai - Agents/model resolution: keep explicit-model runtime comparisons on the configured workspace plugin registry, so workspace-installed providers do not silently fall back to stale explicit metadata during runtime model lookup. - Providers/Z.AI: default onboarding and endpoint detection to GLM-5.1 instead of GLM-5. (#61998) Thanks @serg0x. - Telegram/setup: load setup and secret contracts through packaged top-level sidecars so installed 2026.4.7 npm builds no longer try to import missing `dist/extensions/telegram/src/*` files during gateway startup. +- Bundled channels/setup: load shared secret contracts through packaged top-level sidecars across BlueBubbles, Feishu, Google Chat, IRC, Matrix, Mattermost, Microsoft Teams, Nextcloud Talk, Slack, and Zalo so installed npm builds no longer rely on missing `dist/extensions/*/src/*` files during gateway startup. ## 2026.4.5 diff --git a/extensions/bluebubbles/index.ts b/extensions/bluebubbles/index.ts index f37faeae223..058f82c8daa 100644 --- a/extensions/bluebubbles/index.ts +++ b/extensions/bluebubbles/index.ts @@ -10,7 +10,7 @@ export default defineBundledChannelEntry({ exportName: "bluebubblesPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, runtime: { diff --git a/extensions/bluebubbles/secret-contract-api.ts b/extensions/bluebubbles/secret-contract-api.ts index bc8f64f050f..9f44ef28569 100644 --- a/extensions/bluebubbles/secret-contract-api.ts +++ b/extensions/bluebubbles/secret-contract-api.ts @@ -1,4 +1,5 @@ export { + channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries, } from "./src/secret-contract.js"; diff --git a/extensions/bluebubbles/setup-entry.ts b/extensions/bluebubbles/setup-entry.ts index 40ef304ef41..32bd2147be7 100644 --- a/extensions/bluebubbles/setup-entry.ts +++ b/extensions/bluebubbles/setup-entry.ts @@ -7,7 +7,7 @@ export default defineBundledChannelSetupEntry({ exportName: "bluebubblesSetupPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, }); diff --git a/extensions/feishu/channel-entry.ts b/extensions/feishu/channel-entry.ts index 99fd860c7fb..07b4a52c909 100644 --- a/extensions/feishu/channel-entry.ts +++ b/extensions/feishu/channel-entry.ts @@ -10,7 +10,7 @@ export default defineBundledChannelEntry({ exportName: "feishuPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, runtime: { diff --git a/extensions/feishu/index.ts b/extensions/feishu/index.ts index 0f39644c8bc..9d0f6b2540d 100644 --- a/extensions/feishu/index.ts +++ b/extensions/feishu/index.ts @@ -71,7 +71,7 @@ export default defineBundledChannelEntry({ exportName: "feishuPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, runtime: { diff --git a/extensions/feishu/secret-contract-api.ts b/extensions/feishu/secret-contract-api.ts index bc8f64f050f..9f44ef28569 100644 --- a/extensions/feishu/secret-contract-api.ts +++ b/extensions/feishu/secret-contract-api.ts @@ -1,4 +1,5 @@ export { + channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries, } from "./src/secret-contract.js"; diff --git a/extensions/feishu/setup-entry.ts b/extensions/feishu/setup-entry.ts index 44a9dbf62c3..ec75c552ab7 100644 --- a/extensions/feishu/setup-entry.ts +++ b/extensions/feishu/setup-entry.ts @@ -7,7 +7,7 @@ export default defineBundledChannelSetupEntry({ exportName: "feishuPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, }); diff --git a/extensions/googlechat/index.ts b/extensions/googlechat/index.ts index 2b1e541ccf9..11216e06f1a 100644 --- a/extensions/googlechat/index.ts +++ b/extensions/googlechat/index.ts @@ -10,7 +10,7 @@ export default defineBundledChannelEntry({ exportName: "googlechatPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, runtime: { diff --git a/extensions/googlechat/secret-contract-api.ts b/extensions/googlechat/secret-contract-api.ts index bc8f64f050f..9f44ef28569 100644 --- a/extensions/googlechat/secret-contract-api.ts +++ b/extensions/googlechat/secret-contract-api.ts @@ -1,4 +1,5 @@ export { + channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries, } from "./src/secret-contract.js"; diff --git a/extensions/googlechat/setup-entry.ts b/extensions/googlechat/setup-entry.ts index a8e2558dae3..f9e358e5359 100644 --- a/extensions/googlechat/setup-entry.ts +++ b/extensions/googlechat/setup-entry.ts @@ -7,7 +7,7 @@ export default defineBundledChannelSetupEntry({ exportName: "googlechatPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, }); diff --git a/extensions/irc/index.ts b/extensions/irc/index.ts index 2c1f7e48d75..965af1664ba 100644 --- a/extensions/irc/index.ts +++ b/extensions/irc/index.ts @@ -10,7 +10,7 @@ export default defineBundledChannelEntry({ exportName: "ircPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, runtime: { diff --git a/extensions/irc/secret-contract-api.ts b/extensions/irc/secret-contract-api.ts index bc8f64f050f..9f44ef28569 100644 --- a/extensions/irc/secret-contract-api.ts +++ b/extensions/irc/secret-contract-api.ts @@ -1,4 +1,5 @@ export { + channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries, } from "./src/secret-contract.js"; diff --git a/extensions/irc/setup-entry.ts b/extensions/irc/setup-entry.ts index 83687c75784..6f940a664e5 100644 --- a/extensions/irc/setup-entry.ts +++ b/extensions/irc/setup-entry.ts @@ -7,7 +7,7 @@ export default defineBundledChannelSetupEntry({ exportName: "ircPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, }); diff --git a/extensions/matrix/index.ts b/extensions/matrix/index.ts index 0bf159a8434..9dd62e49be0 100644 --- a/extensions/matrix/index.ts +++ b/extensions/matrix/index.ts @@ -12,7 +12,7 @@ export default defineBundledChannelEntry({ exportName: "matrixPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, runtime: { diff --git a/extensions/matrix/secret-contract-api.ts b/extensions/matrix/secret-contract-api.ts index bc8f64f050f..9f44ef28569 100644 --- a/extensions/matrix/secret-contract-api.ts +++ b/extensions/matrix/secret-contract-api.ts @@ -1,4 +1,5 @@ export { + channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries, } from "./src/secret-contract.js"; diff --git a/extensions/matrix/setup-entry.ts b/extensions/matrix/setup-entry.ts index 11922509d4e..3cd065e0856 100644 --- a/extensions/matrix/setup-entry.ts +++ b/extensions/matrix/setup-entry.ts @@ -7,7 +7,7 @@ export default defineBundledChannelSetupEntry({ exportName: "matrixPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, }); diff --git a/extensions/mattermost/index.ts b/extensions/mattermost/index.ts index 7293c22ea0d..987c2ffe6e6 100644 --- a/extensions/mattermost/index.ts +++ b/extensions/mattermost/index.ts @@ -22,7 +22,7 @@ export default defineBundledChannelEntry({ exportName: "mattermostPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, runtime: { diff --git a/extensions/mattermost/secret-contract-api.ts b/extensions/mattermost/secret-contract-api.ts index bc8f64f050f..9f44ef28569 100644 --- a/extensions/mattermost/secret-contract-api.ts +++ b/extensions/mattermost/secret-contract-api.ts @@ -1,4 +1,5 @@ export { + channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries, } from "./src/secret-contract.js"; diff --git a/extensions/mattermost/setup-entry.ts b/extensions/mattermost/setup-entry.ts index 73b914b1c40..57d905c653f 100644 --- a/extensions/mattermost/setup-entry.ts +++ b/extensions/mattermost/setup-entry.ts @@ -7,7 +7,7 @@ export default defineBundledChannelSetupEntry({ exportName: "mattermostSetupPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, }); diff --git a/extensions/msteams/index.ts b/extensions/msteams/index.ts index 20df747f3b6..56ce8932b19 100644 --- a/extensions/msteams/index.ts +++ b/extensions/msteams/index.ts @@ -10,7 +10,7 @@ export default defineBundledChannelEntry({ exportName: "msteamsPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, runtime: { diff --git a/extensions/msteams/secret-contract-api.ts b/extensions/msteams/secret-contract-api.ts index bc8f64f050f..9f44ef28569 100644 --- a/extensions/msteams/secret-contract-api.ts +++ b/extensions/msteams/secret-contract-api.ts @@ -1,4 +1,5 @@ export { + channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries, } from "./src/secret-contract.js"; diff --git a/extensions/msteams/setup-entry.ts b/extensions/msteams/setup-entry.ts index ca2041131f4..0d3a069e969 100644 --- a/extensions/msteams/setup-entry.ts +++ b/extensions/msteams/setup-entry.ts @@ -7,7 +7,7 @@ export default defineBundledChannelSetupEntry({ exportName: "msteamsPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, }); diff --git a/extensions/nextcloud-talk/index.ts b/extensions/nextcloud-talk/index.ts index 63c0091270e..4cf42af8913 100644 --- a/extensions/nextcloud-talk/index.ts +++ b/extensions/nextcloud-talk/index.ts @@ -10,7 +10,7 @@ export default defineBundledChannelEntry({ exportName: "nextcloudTalkPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, runtime: { diff --git a/extensions/nextcloud-talk/secret-contract-api.ts b/extensions/nextcloud-talk/secret-contract-api.ts index bc8f64f050f..9f44ef28569 100644 --- a/extensions/nextcloud-talk/secret-contract-api.ts +++ b/extensions/nextcloud-talk/secret-contract-api.ts @@ -1,4 +1,5 @@ export { + channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries, } from "./src/secret-contract.js"; diff --git a/extensions/nextcloud-talk/setup-entry.ts b/extensions/nextcloud-talk/setup-entry.ts index 94ccd110887..e5e9067dfc4 100644 --- a/extensions/nextcloud-talk/setup-entry.ts +++ b/extensions/nextcloud-talk/setup-entry.ts @@ -7,7 +7,7 @@ export default defineBundledChannelSetupEntry({ exportName: "nextcloudTalkPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, }); diff --git a/extensions/slack/channel-entry.ts b/extensions/slack/channel-entry.ts index 4eaba4b7866..95eed34ee86 100644 --- a/extensions/slack/channel-entry.ts +++ b/extensions/slack/channel-entry.ts @@ -10,7 +10,7 @@ export default defineBundledChannelEntry({ exportName: "slackPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, runtime: { diff --git a/extensions/slack/index.ts b/extensions/slack/index.ts index 91efdfdce49..9fab5680053 100644 --- a/extensions/slack/index.ts +++ b/extensions/slack/index.ts @@ -22,7 +22,7 @@ export default defineBundledChannelEntry({ exportName: "slackPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, runtime: { diff --git a/extensions/slack/secret-contract-api.ts b/extensions/slack/secret-contract-api.ts index bc8f64f050f..9f44ef28569 100644 --- a/extensions/slack/secret-contract-api.ts +++ b/extensions/slack/secret-contract-api.ts @@ -1,4 +1,5 @@ export { + channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries, } from "./src/secret-contract.js"; diff --git a/extensions/slack/setup-entry.ts b/extensions/slack/setup-entry.ts index ddc51ef3d07..5e035d44a82 100644 --- a/extensions/slack/setup-entry.ts +++ b/extensions/slack/setup-entry.ts @@ -7,7 +7,7 @@ export default defineBundledChannelSetupEntry({ exportName: "slackSetupPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, }); diff --git a/extensions/zalo/index.ts b/extensions/zalo/index.ts index 1c81a2f93fe..342666bc0bf 100644 --- a/extensions/zalo/index.ts +++ b/extensions/zalo/index.ts @@ -10,7 +10,7 @@ export default defineBundledChannelEntry({ exportName: "zaloPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, runtime: { diff --git a/extensions/zalo/secret-contract-api.ts b/extensions/zalo/secret-contract-api.ts index bc8f64f050f..9f44ef28569 100644 --- a/extensions/zalo/secret-contract-api.ts +++ b/extensions/zalo/secret-contract-api.ts @@ -1,4 +1,5 @@ export { + channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries, } from "./src/secret-contract.js"; diff --git a/extensions/zalo/setup-entry.ts b/extensions/zalo/setup-entry.ts index 21f28135814..e1742889588 100644 --- a/extensions/zalo/setup-entry.ts +++ b/extensions/zalo/setup-entry.ts @@ -7,7 +7,7 @@ export default defineBundledChannelSetupEntry({ exportName: "zaloPlugin", }, secrets: { - specifier: "./src/secret-contract.js", + specifier: "./secret-contract-api.js", exportName: "channelSecrets", }, }); diff --git a/test/scripts/bundled-plugin-build-entries.test.ts b/test/scripts/bundled-plugin-build-entries.test.ts index 66593fb9aad..f49b0fc77cf 100644 --- a/test/scripts/bundled-plugin-build-entries.test.ts +++ b/test/scripts/bundled-plugin-build-entries.test.ts @@ -1,4 +1,5 @@ import fs from "node:fs"; +import path from "node:path"; import { describe, expect, it } from "vitest"; import { listBundledPluginBuildEntries, @@ -49,15 +50,40 @@ describe("bundled plugin build entries", () => { expect(artifacts).toContain("dist/extensions/matrix/plugin-entry.handlers.runtime.js"); }); - it("keeps the Telegram setup entry on packed top-level sidecars", () => { - const setupEntry = fs.readFileSync("extensions/telegram/setup-entry.ts", "utf8"); + it("keeps bundled channel secret contracts on packed top-level sidecars", () => { const artifacts = listBundledPluginPackArtifacts(); + const sourceEntries = ["index.ts", "channel-entry.ts", "setup-entry.ts"]; + const offenders: string[] = []; + const secretBackedPluginIds = new Set(); - expect(setupEntry).toContain('specifier: "./setup-plugin-api.js"'); - expect(setupEntry).toContain('specifier: "./secret-contract-api.js"'); - expect(setupEntry).not.toContain("./src/channel.setup.js"); - expect(setupEntry).not.toContain("./src/secret-contract.js"); - expect(artifacts).toContain("dist/extensions/telegram/setup-plugin-api.js"); - expect(artifacts).toContain("dist/extensions/telegram/secret-contract-api.js"); + for (const dirent of fs.readdirSync("extensions", { withFileTypes: true })) { + if (!dirent.isDirectory()) { + continue; + } + + for (const sourceEntry of sourceEntries) { + const entryPath = path.join("extensions", dirent.name, sourceEntry); + if (!fs.existsSync(entryPath)) { + continue; + } + const entry = fs.readFileSync(entryPath, "utf8"); + if (!entry.includes('exportName: "channelSecrets"')) { + continue; + } + secretBackedPluginIds.add(dirent.name); + if (entry.includes("./src/secret-contract.js")) { + offenders.push(entryPath); + } + expect(entry).toContain('specifier: "./secret-contract-api.js"'); + } + } + + expect(offenders).toEqual([]); + + for (const pluginId of [...secretBackedPluginIds].toSorted()) { + const secretApiPath = path.join("extensions", pluginId, "secret-contract-api.ts"); + expect(fs.readFileSync(secretApiPath, "utf8")).toContain("channelSecrets"); + expect(artifacts).toContain(`dist/extensions/${pluginId}/secret-contract-api.js`); + } }); });