refactor: remove remaining extension src imports

This commit is contained in:
Peter Steinberger
2026-03-17 19:48:47 -07:00
parent 055632460d
commit 5b2c5ee2bc
20 changed files with 136 additions and 16 deletions

View File

@@ -1147,11 +1147,18 @@ authoring plugins:
- Domain subpaths such as `openclaw/plugin-sdk/channel-config-helpers`,
`openclaw/plugin-sdk/channel-config-schema`,
`openclaw/plugin-sdk/channel-policy`,
`openclaw/plugin-sdk/channel-runtime`,
`openclaw/plugin-sdk/config-runtime`,
`openclaw/plugin-sdk/agent-runtime`,
`openclaw/plugin-sdk/lazy-runtime`,
`openclaw/plugin-sdk/reply-history`,
`openclaw/plugin-sdk/routing`,
`openclaw/plugin-sdk/runtime-store`, and
`openclaw/plugin-sdk/directory-runtime` for shared runtime/config helpers.
- Narrow channel-core subpaths such as `openclaw/plugin-sdk/discord-core`,
`openclaw/plugin-sdk/telegram-core`, `openclaw/plugin-sdk/whatsapp-core`,
and `openclaw/plugin-sdk/line-core` for channel-specific primitives that
should stay smaller than the full channel helper barrels.
- `openclaw/plugin-sdk/compat` remains as a legacy migration surface for older
external plugins. Bundled plugins should not use it, and non-test imports emit
a one-time deprecation warning outside test environments.

View File

@@ -1,8 +1,8 @@
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
import { readBooleanParam } from "openclaw/plugin-sdk/boolean-param";
import {
type ActionGate,
assertMediaNotDataUrl,
type ActionGate,
jsonResult,
readNumberParam,
readReactionParams,

View File

@@ -1,6 +1,6 @@
import { Type } from "@sinclair/typebox";
import { optionalStringEnum } from "openclaw/plugin-sdk/agent-runtime";
import { jsonResult, readNumberParam, readStringParam } from "openclaw/plugin-sdk/agent-runtime";
import { optionalStringEnum } from "openclaw/plugin-sdk/core";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-runtime";
import { runFirecrawlScrape } from "./firecrawl-client.js";

View File

@@ -1,11 +1,11 @@
import {
DEFAULT_ACCOUNT_ID,
listLineAccountIds,
normalizeAccountId,
resolveLineAccount,
type ChannelSetupAdapter,
type LineConfig,
type OpenClawConfig,
} from "openclaw/plugin-sdk/line-core";
import type { ChannelSetupAdapter, OpenClawConfig } from "openclaw/plugin-sdk/setup";
const channel = "line" as const;
@@ -158,4 +158,4 @@ export const lineSetupAdapter: ChannelSetupAdapter = {
},
};
export { listLineAccountIds } from "openclaw/plugin-sdk/line-core";
export { listLineAccountIds };

View File

@@ -1,12 +1,13 @@
import crypto from "node:crypto";
import { configureClient } from "@tloncorp/api";
import { createReplyPrefixOptions } from "openclaw/plugin-sdk/channel-runtime";
import type {
ChannelAccountSnapshot,
ChannelOutboundAdapter,
ChannelPlugin,
OpenClawConfig,
} from "../api.js";
import { createLoggerBackedRuntime, createReplyPrefixOptions } from "../api.js";
} from "openclaw/plugin-sdk/channel-runtime";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
import { createLoggerBackedRuntime } from "openclaw/plugin-sdk/runtime";
import { monitorTlonProvider } from "./monitor/index.js";
import { tlonSetupWizard } from "./setup-surface.js";
import {

View File

@@ -1,5 +1,6 @@
import type { ChannelAccountSnapshot, ChannelPlugin } from "openclaw/plugin-sdk/channel-runtime";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
import type { ChannelAccountSnapshot, ChannelPlugin, OpenClawConfig } from "../api.js";
import { tlonChannelConfigSchema } from "./config-schema.js";
import {
applyTlonSetupConfig,

View File

@@ -1,5 +1,5 @@
import { buildChannelConfigSchema } from "openclaw/plugin-sdk/core";
import { z } from "zod";
import { buildChannelConfigSchema } from "../api.js";
const ShipSchema = z.string().min(1);
const ChannelNestSchema = z.string().min(1);

View File

@@ -1,5 +1,5 @@
import type { PluginRuntime } from "openclaw/plugin-sdk/plugin-runtime";
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
import type { PluginRuntime } from "../api.js";
const { setRuntime: setTlonRuntime, getRuntime: getTlonRuntime } =
createPluginRuntimeStore<PluginRuntime>("Tlon runtime not initialized");

View File

@@ -1,4 +1,4 @@
import type { OpenClawConfig } from "../api.js";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
export type TlonResolvedAccount = {
accountId: string;

View File

@@ -1,4 +1,4 @@
import { isBlockedHostnameOrIp } from "../../api.js";
import { isBlockedHostnameOrIp } from "openclaw/plugin-sdk/infra-runtime";
export type UrbitBaseUrlValidation =
| { ok: true; baseUrl: string; hostname: string }

View File

@@ -487,7 +487,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: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": "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-src-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",
@@ -539,6 +539,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-src-imports": "node --import tsx scripts/check-no-extension-src-imports.ts",
"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",

View File

@@ -0,0 +1,88 @@
import fs from "node:fs";
import path from "node:path";
const FORBIDDEN_REPO_SRC_IMPORT = /["'](?:\.\.\/)+(?:src\/)[^"']+["']/;
function isSourceFile(filePath: string): boolean {
if (filePath.endsWith(".d.ts")) {
return false;
}
return /\.(?:[cm]?ts|[cm]?js|tsx|jsx)$/u.test(filePath);
}
function isProductionExtensionFile(filePath: string): boolean {
return !(
filePath.includes(".test.") ||
filePath.includes(".spec.") ||
filePath.includes(".fixture.") ||
filePath.includes(".snap") ||
filePath.includes("test-harness") ||
filePath.includes("test-support") ||
filePath.includes("/__tests__/") ||
filePath.includes("/coverage/") ||
filePath.includes("/dist/") ||
filePath.includes("/node_modules/")
);
}
function collectExtensionSourceFiles(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() && isSourceFile(fullPath) && isProductionExtensionFile(fullPath)) {
files.push(fullPath);
}
}
}
return files;
}
function main() {
const extensionsDir = path.join(process.cwd(), "extensions");
const files = collectExtensionSourceFiles(extensionsDir);
const offenders: string[] = [];
for (const file of files) {
const content = fs.readFileSync(file, "utf8");
if (FORBIDDEN_REPO_SRC_IMPORT.test(content)) {
offenders.push(file);
}
}
if (offenders.length > 0) {
console.error("Production extension files must not import the repo src/ tree directly.");
for (const offender of offenders.toSorted()) {
const relative = path.relative(process.cwd(), offender) || offender;
console.error(`- ${relative}`);
}
console.error(
"Publish a focused openclaw/plugin-sdk/<subpath> seam or use the extension's own public barrel instead.",
);
process.exit(1);
}
console.log(
`OK: production extension files avoid direct repo src/ imports (${files.length} checked).`,
);
}
main();

View File

@@ -3,6 +3,7 @@
export * from "../agents/agent-scope.js";
export * from "../agents/auth-profiles.js";
export * from "../agents/current-time.js";
export * from "../agents/date-time.js";
export * from "../agents/defaults.js";
export * from "../agents/identity-avatar.js";
export * from "../agents/identity.js";
@@ -13,6 +14,7 @@ export * from "../agents/model-selection.js";
export * from "../agents/pi-embedded-block-chunker.js";
export * from "../agents/pi-embedded-utils.js";
export * from "../agents/provider-id.js";
export * from "../agents/sandbox-paths.js";
export * from "../agents/schema/typebox.js";
export * from "../agents/sglang-defaults.js";
export * from "../agents/tools/common.js";

View File

@@ -42,6 +42,7 @@ export * from "../channels/plugins/outbound/interactive.js";
export * from "../channels/plugins/status-issues/shared.js";
export * from "../channels/plugins/whatsapp-heartbeat.js";
export * from "../infra/outbound/send-deps.js";
export * from "../polls.js";
export * from "../utils/message-channel.js";
export * from "./channel-lifecycle.js";
export type {

View File

@@ -72,6 +72,12 @@ export {
export { formatPairingApproveHint } from "../channels/plugins/helpers.js";
export { getChatChannelMeta } from "../channels/registry.js";
export { buildOauthProviderAuthResult } from "./provider-auth-result.js";
export {
channelTargetSchema,
channelTargetsSchema,
optionalStringEnum,
stringEnum,
} from "../agents/schema/typebox.js";
export {
DEFAULT_SECRET_FILE_MAX_BYTES,
loadSecretFileSync,

View File

@@ -1,4 +1,5 @@
export type { ChannelPlugin } from "./channel-plugin-common.js";
export type { DiscordActionConfig } from "../config/types.js";
export { buildChannelConfigSchema, getChatChannelMeta } from "./channel-plugin-common.js";
export type { OpenClawConfig } from "../config/config.js";
export type { DiscordActionConfig } from "../config/types.js";

View File

@@ -11,6 +11,9 @@ export {
export type { ChannelSetupAdapter, ChannelSetupDmPolicy, ChannelSetupWizard } from "./setup.js";
export {
listLineAccountIds,
normalizeAccountId,
resolveDefaultLineAccountId,
resolveLineAccount,
} from "../line/accounts.js";
export type { ResolvedLineAccount } from "../line/types.js";
export { LineConfigSchema } from "../line/config-schema.js";

View File

@@ -9,6 +9,7 @@ import * as discordSdk from "openclaw/plugin-sdk/discord";
import * as imessageSdk from "openclaw/plugin-sdk/imessage";
import * as lazyRuntimeSdk from "openclaw/plugin-sdk/lazy-runtime";
import * as lineSdk from "openclaw/plugin-sdk/line";
import * as lineCoreSdk from "openclaw/plugin-sdk/line-core";
import * as msteamsSdk from "openclaw/plugin-sdk/msteams";
import * as nostrSdk from "openclaw/plugin-sdk/nostr";
import * as ollamaSetupSdk from "openclaw/plugin-sdk/ollama-setup";
@@ -67,6 +68,7 @@ describe("plugin-sdk subpath exports", () => {
expect(typeof coreSdk.definePluginEntry).toBe("function");
expect(typeof coreSdk.defineChannelPluginEntry).toBe("function");
expect(typeof coreSdk.defineSetupPluginEntry).toBe("function");
expect(typeof coreSdk.optionalStringEnum).toBe("function");
expect("runPassiveAccountLifecycle" in asExports(coreSdk)).toBe(false);
expect("createLoggerBackedRuntime" in asExports(coreSdk)).toBe(false);
expect("registerSandboxBackend" in asExports(coreSdk)).toBe(false);
@@ -207,6 +209,12 @@ describe("plugin-sdk subpath exports", () => {
expect(typeof lineSdk.lineSetupAdapter).toBe("object");
});
it("exports narrow LINE core helpers", () => {
expect(typeof lineCoreSdk.resolveLineAccount).toBe("function");
expect(typeof lineCoreSdk.listLineAccountIds).toBe("function");
expect(typeof lineCoreSdk.LineConfigSchema).toBe("object");
});
it("exports Microsoft Teams helpers", () => {
expect(typeof msteamsSdk.resolveControlCommandGate).toBe("function");
expect(typeof msteamsSdk.loadOutboundMediaFromUrl).toBe("function");

View File

@@ -1,6 +1,7 @@
export type { OpenClawConfig } from "../config/config.js";
export type { TelegramActionConfig } from "../config/types.js";
export type { ChannelPlugin } from "./channel-plugin-common.js";
export type { TelegramActionConfig } from "../config/types.js";
export { buildChannelConfigSchema, getChatChannelMeta } from "./channel-plugin-common.js";
export { normalizeAccountId } from "../routing/session-key.js";
export {

View File

@@ -23,5 +23,5 @@ export {
readStringParam,
} from "../agents/tools/common.js";
export { WhatsAppConfigSchema } from "../config/zod-schema.providers-whatsapp.js";
export { normalizeE164 } from "../utils.js";
export { resolveWhatsAppOutboundTarget } from "../whatsapp/resolve-outbound-target.js";
export { normalizeE164 } from "../utils.js";