diff --git a/extensions/discord/src/setup-core.ts b/extensions/discord/src/setup-core.ts index 4425ed6adeb..7f4c1be29d3 100644 --- a/extensions/discord/src/setup-core.ts +++ b/extensions/discord/src/setup-core.ts @@ -13,13 +13,13 @@ import { setSetupChannelEnabled, type OpenClawConfig, } from "openclaw/plugin-sdk/setup"; -import { formatDocsLink } from "../../../src/terminal/links.js"; import { createAllowlistSetupWizardProxy, type ChannelSetupAdapter, type ChannelSetupDmPolicy, type ChannelSetupWizard, } from "openclaw/plugin-sdk/setup"; +import { formatDocsLink } from "../../../src/terminal/links.js"; import { inspectDiscordAccount } from "./account-inspect.js"; import { listDiscordAccountIds, resolveDiscordAccount } from "./accounts.js"; diff --git a/extensions/discord/src/setup-surface.ts b/extensions/discord/src/setup-surface.ts index 6373f89dbcf..be5a374d0fa 100644 --- a/extensions/discord/src/setup-surface.ts +++ b/extensions/discord/src/setup-surface.ts @@ -11,8 +11,8 @@ import { setSetupChannelEnabled, type WizardPrompter, } from "openclaw/plugin-sdk/setup"; -import { formatDocsLink } from "../../../src/terminal/links.js"; import { type ChannelSetupDmPolicy, type ChannelSetupWizard } from "openclaw/plugin-sdk/setup"; +import { formatDocsLink } from "../../../src/terminal/links.js"; import { inspectDiscordAccount } from "./account-inspect.js"; import { listDiscordAccountIds, diff --git a/extensions/imessage/src/setup-core.ts b/extensions/imessage/src/setup-core.ts index 7543df157e8..bc99f521510 100644 --- a/extensions/imessage/src/setup-core.ts +++ b/extensions/imessage/src/setup-core.ts @@ -10,13 +10,13 @@ import { type OpenClawConfig, type WizardPrompter, } from "openclaw/plugin-sdk/setup"; -import { formatDocsLink } from "../../../src/terminal/links.js"; import type { ChannelSetupAdapter, ChannelSetupDmPolicy, ChannelSetupWizard, ChannelSetupWizardTextInput, } from "openclaw/plugin-sdk/setup"; +import { formatDocsLink } from "../../../src/terminal/links.js"; import { listIMessageAccountIds, resolveDefaultIMessageAccountId, diff --git a/extensions/imessage/src/setup-surface.ts b/extensions/imessage/src/setup-surface.ts index f01b4c03f93..6581fabbacd 100644 --- a/extensions/imessage/src/setup-surface.ts +++ b/extensions/imessage/src/setup-surface.ts @@ -1,7 +1,4 @@ -import { - setSetupChannelEnabled, - type ChannelSetupWizard, -} from "openclaw/plugin-sdk/setup"; +import { setSetupChannelEnabled, type ChannelSetupWizard } from "openclaw/plugin-sdk/setup"; import { detectBinary } from "../../../src/plugins/setup-binary.js"; import { listIMessageAccountIds, resolveIMessageAccount } from "./accounts.js"; import { diff --git a/extensions/imessage/src/shared.ts b/extensions/imessage/src/shared.ts index 5ce509f757d..1ede2ad412d 100644 --- a/extensions/imessage/src/shared.ts +++ b/extensions/imessage/src/shared.ts @@ -2,19 +2,19 @@ import { buildAccountScopedDmSecurityPolicy, collectAllowlistProviderRestrictSendersWarnings, } from "openclaw/plugin-sdk/channel-policy"; +import { + deleteAccountFromConfigSection, + setAccountEnabledInConfigSection, +} from "../../../src/channels/plugins/config-helpers.js"; +import { buildChannelConfigSchema } from "../../../src/channels/plugins/config-schema.js"; +import type { ChannelPlugin } from "../../../src/channels/plugins/types.plugin.js"; +import { getChatChannelMeta } from "../../../src/channels/registry.js"; +import { IMessageConfigSchema } from "../../../src/config/zod-schema.providers-core.js"; import { formatTrimmedAllowFromEntries, resolveIMessageConfigAllowFrom, resolveIMessageConfigDefaultTo, } from "../../../src/plugin-sdk/channel-config-helpers.js"; -import { buildChannelConfigSchema } from "../../../src/channels/plugins/config-schema.js"; -import { - deleteAccountFromConfigSection, - setAccountEnabledInConfigSection, -} from "../../../src/channels/plugins/config-helpers.js"; -import type { ChannelPlugin } from "../../../src/channels/plugins/types.plugin.js"; -import { getChatChannelMeta } from "../../../src/channels/registry.js"; -import { IMessageConfigSchema } from "../../../src/config/zod-schema.providers-core.js"; import { DEFAULT_ACCOUNT_ID } from "../../../src/routing/session-key.js"; import { listIMessageAccountIds, diff --git a/extensions/signal/src/setup-surface.ts b/extensions/signal/src/setup-surface.ts index 2094e76da05..edcea39d6b1 100644 --- a/extensions/signal/src/setup-surface.ts +++ b/extensions/signal/src/setup-surface.ts @@ -1,7 +1,4 @@ -import { - setSetupChannelEnabled, - type ChannelSetupWizard, -} from "openclaw/plugin-sdk/setup"; +import { setSetupChannelEnabled, type ChannelSetupWizard } from "openclaw/plugin-sdk/setup"; import { detectBinary } from "../../../src/plugins/setup-binary.js"; import { installSignalCli } from "../../../src/plugins/signal-cli-install.js"; import { listSignalAccountIds, resolveSignalAccount } from "./accounts.js"; diff --git a/extensions/slack/src/setup-core.ts b/extensions/slack/src/setup-core.ts index 5cd7a71d22c..6cd9232b388 100644 --- a/extensions/slack/src/setup-core.ts +++ b/extensions/slack/src/setup-core.ts @@ -15,13 +15,13 @@ import { setLegacyChannelDmPolicyWithAllowFrom, setSetupChannelEnabled, } from "openclaw/plugin-sdk/setup"; -import { formatDocsLink } from "../../../src/terminal/links.js"; import { type ChannelSetupAdapter, type ChannelSetupDmPolicy, type ChannelSetupWizard, type ChannelSetupWizardAllowFromEntry, } from "openclaw/plugin-sdk/setup"; +import { formatDocsLink } from "../../../src/terminal/links.js"; import { inspectSlackAccount } from "./account-inspect.js"; import { listSlackAccountIds, resolveSlackAccount, type ResolvedSlackAccount } from "./accounts.js"; import { diff --git a/extensions/slack/src/setup-surface.ts b/extensions/slack/src/setup-surface.ts index 309dc669af8..4e3670ac843 100644 --- a/extensions/slack/src/setup-surface.ts +++ b/extensions/slack/src/setup-surface.ts @@ -14,12 +14,12 @@ import { setSetupChannelEnabled, type WizardPrompter, } from "openclaw/plugin-sdk/setup"; -import { formatDocsLink } from "../../../src/terminal/links.js"; import type { ChannelSetupDmPolicy, ChannelSetupWizard, ChannelSetupWizardAllowFromEntry, } from "openclaw/plugin-sdk/setup"; +import { formatDocsLink } from "../../../src/terminal/links.js"; import { inspectSlackAccount } from "./account-inspect.js"; import { listSlackAccountIds, diff --git a/extensions/telegram/src/bot-native-commands.menu-test-support.ts b/extensions/telegram/src/bot-native-commands.menu-test-support.ts index 8e67e625f93..e37634e7d55 100644 --- a/extensions/telegram/src/bot-native-commands.menu-test-support.ts +++ b/extensions/telegram/src/bot-native-commands.menu-test-support.ts @@ -1,4 +1,4 @@ -import type { RuntimeEnv } from "openclaw/plugin-sdk"; +import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env"; import type { OpenClawConfig } from "openclaw/plugin-sdk/telegram"; import { expect, vi } from "vitest"; import { diff --git a/extensions/telegram/src/setup-core.ts b/extensions/telegram/src/setup-core.ts index 10543aad1eb..f2b5fc04d77 100644 --- a/extensions/telegram/src/setup-core.ts +++ b/extensions/telegram/src/setup-core.ts @@ -9,9 +9,9 @@ import { type OpenClawConfig, type WizardPrompter, } from "openclaw/plugin-sdk/setup"; +import type { ChannelSetupAdapter, ChannelSetupDmPolicy } from "openclaw/plugin-sdk/setup"; import { formatCliCommand } from "../../../src/cli/command-format.js"; import { formatDocsLink } from "../../../src/terminal/links.js"; -import type { ChannelSetupAdapter, ChannelSetupDmPolicy } from "openclaw/plugin-sdk/setup"; import { resolveDefaultTelegramAccountId, resolveTelegramAccount } from "./accounts.js"; import { fetchTelegramChatId } from "./api-fetch.js"; diff --git a/extensions/whatsapp/src/setup-surface.ts b/extensions/whatsapp/src/setup-surface.ts index 0975c28d444..7bee33c2ef4 100644 --- a/extensions/whatsapp/src/setup-surface.ts +++ b/extensions/whatsapp/src/setup-surface.ts @@ -10,9 +10,9 @@ import { type DmPolicy, type OpenClawConfig, } from "openclaw/plugin-sdk/setup"; +import type { ChannelSetupWizard } from "openclaw/plugin-sdk/setup"; import { formatCliCommand } from "../../../src/cli/command-format.js"; import { formatDocsLink } from "../../../src/terminal/links.js"; -import type { ChannelSetupWizard } from "openclaw/plugin-sdk/setup"; import { listWhatsAppAccountIds, resolveWhatsAppAuthDir } from "./accounts.js"; import { loginWeb } from "./login.js"; import { whatsappSetupAdapter } from "./setup-core.js"; diff --git a/extensions/whatsapp/src/shared.ts b/extensions/whatsapp/src/shared.ts index 6469d1cf18e..575954a516c 100644 --- a/extensions/whatsapp/src/shared.ts +++ b/extensions/whatsapp/src/shared.ts @@ -3,20 +3,20 @@ import { collectAllowlistProviderGroupPolicyWarnings, collectOpenGroupPolicyRouteAllowlistWarnings, } from "openclaw/plugin-sdk/channel-policy"; +import { buildChannelConfigSchema } from "../../../src/channels/plugins/config-schema.js"; +import { + resolveWhatsAppGroupRequireMention, + resolveWhatsAppGroupToolPolicy, +} from "../../../src/channels/plugins/group-mentions.js"; +import type { ChannelPlugin } from "../../../src/channels/plugins/types.plugin.js"; +import { resolveWhatsAppGroupIntroHint } from "../../../src/channels/plugins/whatsapp-shared.js"; +import { getChatChannelMeta } from "../../../src/channels/registry.js"; +import { WhatsAppConfigSchema } from "../../../src/config/zod-schema.providers-whatsapp.js"; import { formatWhatsAppConfigAllowFromEntries, resolveWhatsAppConfigAllowFrom, resolveWhatsAppConfigDefaultTo, } from "../../../src/plugin-sdk/channel-config-helpers.js"; -import { buildChannelConfigSchema } from "../../../src/channels/plugins/config-schema.js"; -import type { ChannelPlugin } from "../../../src/channels/plugins/types.plugin.js"; -import { - resolveWhatsAppGroupRequireMention, - resolveWhatsAppGroupToolPolicy, -} from "../../../src/channels/plugins/group-mentions.js"; -import { resolveWhatsAppGroupIntroHint } from "../../../src/channels/plugins/whatsapp-shared.js"; -import { getChatChannelMeta } from "../../../src/channels/registry.js"; -import { WhatsAppConfigSchema } from "../../../src/config/zod-schema.providers-whatsapp.js"; import { DEFAULT_ACCOUNT_ID } from "../../../src/routing/session-key.js"; import { normalizeE164 } from "../../../src/utils.js"; import { diff --git a/scripts/check-no-monolithic-plugin-sdk-entry-imports.ts b/scripts/check-no-monolithic-plugin-sdk-entry-imports.ts index 32c4646009a..bc24087ace3 100644 --- a/scripts/check-no-monolithic-plugin-sdk-entry-imports.ts +++ b/scripts/check-no-monolithic-plugin-sdk-entry-imports.ts @@ -68,6 +68,27 @@ function collectSharedExtensionSourceFiles(): string[] { return collectPluginSourceFiles(path.join(process.cwd(), "extensions", "shared")); } +function collectBundledExtensionSourceFiles(): string[] { + const extensionsDir = path.join(process.cwd(), "extensions"); + let entries: fs.Dirent[] = []; + try { + entries = fs.readdirSync(extensionsDir, { withFileTypes: true }); + } catch { + return []; + } + + const files: string[] = []; + for (const entry of entries) { + if (!entry.isDirectory() || entry.name === "shared") { + continue; + } + for (const srcFile of collectPluginSourceFiles(path.join(extensionsDir, entry.name))) { + files.push(srcFile); + } + } + return files; +} + function main() { const discovery = discoverOpenClawPlugins({}); const bundledCandidates = discovery.candidates.filter((c) => c.origin === "bundled"); @@ -81,6 +102,9 @@ function main() { for (const sharedFile of collectSharedExtensionSourceFiles()) { filesToCheck.add(sharedFile); } + for (const extensionFile of collectBundledExtensionSourceFiles()) { + filesToCheck.add(extensionFile); + } const monolithicOffenders: string[] = []; const legacyCompatOffenders: string[] = []; diff --git a/src/plugin-sdk/channel-import-guardrails.test.ts b/src/plugin-sdk/channel-import-guardrails.test.ts index 51905b66b02..447489b1a0f 100644 --- a/src/plugin-sdk/channel-import-guardrails.test.ts +++ b/src/plugin-sdk/channel-import-guardrails.test.ts @@ -1,4 +1,4 @@ -import { readFileSync } from "node:fs"; +import { readdirSync, readFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; @@ -108,6 +108,47 @@ function readSetupBarrelImportBlock(path: string): string { return lines.slice(startLineIndex, targetLineIndex + 1).join("\n"); } +function collectExtensionSourceFiles(): string[] { + const extensionsDir = resolve(ROOT_DIR, "..", "extensions"); + const sharedExtensionsDir = resolve(extensionsDir, "shared"); + const files: string[] = []; + const stack = [extensionsDir]; + while (stack.length > 0) { + const current = stack.pop(); + if (!current) { + continue; + } + for (const entry of readdirSync(current, { withFileTypes: true })) { + const fullPath = resolve(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() || !/\.(?:[cm]?ts|[cm]?js|tsx|jsx)$/u.test(entry.name)) { + continue; + } + if (entry.name.endsWith(".d.ts") || fullPath.includes(sharedExtensionsDir)) { + continue; + } + if (fullPath.includes(`${resolve(ROOT_DIR, "..", "extensions")}/shared/`)) { + continue; + } + if ( + fullPath.includes(".test.") || + fullPath.includes(".fixture.") || + fullPath.includes(".snap") + ) { + continue; + } + files.push(fullPath); + } + } + return files; +} + describe("channel import guardrails", () => { it("keeps channel helper modules off their own SDK barrels", () => { for (const source of SAME_CHANNEL_SDK_GUARDS) { @@ -128,4 +169,16 @@ describe("channel import guardrails", () => { } } }); + + it("keeps bundled extension source files off root and compat plugin-sdk imports", () => { + for (const file of collectExtensionSourceFiles()) { + const text = readFileSync(file, "utf8"); + expect(text, `${file} should not import openclaw/plugin-sdk root`).not.toMatch( + /["']openclaw\/plugin-sdk["']/, + ); + expect(text, `${file} should not import openclaw/plugin-sdk/compat`).not.toMatch( + /["']openclaw\/plugin-sdk\/compat["']/, + ); + } + }); });