refactor: enforce scoped plugin sdk imports

This commit is contained in:
Peter Steinberger
2026-03-16 23:15:24 -07:00
parent 14d6b762fb
commit c1ef5748eb
14 changed files with 105 additions and 34 deletions

View File

@@ -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";

View File

@@ -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,

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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";

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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";

View File

@@ -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";

View File

@@ -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 {

View File

@@ -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[] = [];

View File

@@ -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["']/,
);
}
});
});