fix: break bundled channel bootstrap cycles

This commit is contained in:
Peter Steinberger 2026-04-05 14:12:48 +01:00
parent 4fedc5c105
commit 89e8c8672c
No known key found for this signature in database
87 changed files with 476 additions and 212 deletions

View File

@ -3,7 +3,7 @@ import {
type ChannelDoctorConfigMutation,
} from "openclaw/plugin-sdk/channel-contract";
import { type OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { collectProviderDangerousNameMatchingScopes } from "openclaw/plugin-sdk/runtime";
import { collectProviderDangerousNameMatchingScopes } from "openclaw/plugin-sdk/runtime-doctor";
import { DISCORD_LEGACY_CONFIG_RULES } from "./doctor-shared.js";
import { resolveDiscordPreviewStreamMode } from "./preview-streaming.js";

View File

@ -1,4 +1,4 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { registerFeishuBitableTools } from "./src/bitable.js";
import { feishuPlugin } from "./src/channel.js";
import { registerFeishuChatTools } from "./src/chat.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { feishuPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(feishuPlugin);

View File

@ -1,5 +1,5 @@
import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import type { ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { googlechatPlugin } from "./src/channel.js";
import { setGoogleChatRuntime } from "./src/runtime.js";

View File

@ -1,4 +1,62 @@
// Private runtime barrel for the bundled Google Chat extension.
// Keep this barrel thin and aligned with the local extension surface.
// Keep this barrel thin and avoid broad plugin-sdk surfaces during bootstrap.
export * from "openclaw/plugin-sdk/googlechat";
export { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
export {
createActionGate,
jsonResult,
readNumberParam,
readReactionParams,
readStringParam,
} from "openclaw/plugin-sdk/channel-actions";
export { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-primitives";
export type {
ChannelMessageActionAdapter,
ChannelMessageActionName,
ChannelStatusIssue,
} from "openclaw/plugin-sdk/channel-contract";
export { missingTargetError } from "openclaw/plugin-sdk/channel-feedback";
export {
createAccountStatusSink,
runPassiveAccountLifecycle,
} from "openclaw/plugin-sdk/channel-lifecycle";
export { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
export { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";
export {
evaluateGroupRouteAccessForPolicy,
resolveDmGroupAccessWithLists,
resolveSenderScopedGroupPolicy,
} from "openclaw/plugin-sdk/channel-policy";
export { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
export { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
export type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
export {
GROUP_POLICY_BLOCKED_LABEL,
isDangerousNameMatchingEnabled,
resolveAllowlistProviderRuntimeGroupPolicy,
resolveDefaultGroupPolicy,
warnMissingProviderGroupPolicyFallbackOnce,
} from "openclaw/plugin-sdk/config-runtime";
export { fetchRemoteMedia, resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/media-runtime";
export { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
export type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store";
export { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
export {
GoogleChatConfigSchema,
type GoogleChatAccountConfig,
type GoogleChatConfig,
} from "openclaw/plugin-sdk/googlechat-runtime-shared";
export { extractToolSend } from "openclaw/plugin-sdk/tool-send";
export { resolveMentionGatingWithBypass } from "openclaw/plugin-sdk/channel-inbound";
export { resolveInboundRouteEnvelopeBuilderWithRuntime } from "openclaw/plugin-sdk/inbound-envelope";
export { resolveWebhookPath } from "openclaw/plugin-sdk/webhook-path";
export {
registerWebhookTargetWithPluginRoute,
resolveWebhookTargetWithAuthOrReject,
withResolvedWebhookRequestPipeline,
} from "openclaw/plugin-sdk/webhook-targets";
export {
createWebhookInFlightLimiter,
readJsonWebhookBodyOrReject,
type WebhookInFlightLimiter,
} from "openclaw/plugin-sdk/webhook-request-guards";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { googlechatPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(googlechatPlugin);

View File

@ -2,11 +2,12 @@ import {
createAccountListHelpers,
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
type OpenClawConfig,
resolveAccountEntry,
resolveMergedAccountConfig,
} from "openclaw/plugin-sdk/account-resolution";
import { isSecretRef, type OpenClawConfig } from "openclaw/plugin-sdk/core";
import { safeParseJsonWithSchema, safeParseWithSchema } from "openclaw/plugin-sdk/extension-shared";
import { isSecretRef } from "openclaw/plugin-sdk/secret-input";
import { z } from "zod";
import type { GoogleChatAccountConfig } from "./types.config.js";

View File

@ -4,7 +4,6 @@ export {
createAccountStatusSink,
DEFAULT_ACCOUNT_ID,
fetchRemoteMedia,
getChatChannelMeta,
GoogleChatConfigSchema,
loadOutboundMediaFromUrl,
missingTargetError,

View File

@ -4,13 +4,13 @@ import {
adaptScopedAccountAccessor,
createScopedChannelConfigAdapter,
} from "openclaw/plugin-sdk/channel-config-helpers";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import {
composeAccountWarningCollectors,
composeWarningCollectors,
createAllowlistProviderGroupPolicyWarningCollector,
createAllowlistProviderOpenWarningCollector,
} from "openclaw/plugin-sdk/channel-policy";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
import {
createChannelDirectoryAdapter,
listResolvedDirectoryGroupEntriesFromMapKeys,
@ -31,7 +31,6 @@ import {
createAccountStatusSink,
DEFAULT_ACCOUNT_ID,
fetchRemoteMedia,
getChatChannelMeta,
GoogleChatConfigSchema,
listGoogleChatAccountIds,
loadOutboundMediaFromUrl,
@ -57,7 +56,19 @@ import { collectRuntimeConfigAssignments, secretTargetRegistryEntries } from "./
import { googlechatSetupAdapter } from "./setup-core.js";
import { googlechatSetupWizard } from "./setup-surface.js";
const meta = getChatChannelMeta("googlechat");
const meta = {
id: "googlechat",
label: "Google Chat",
selectionLabel: "Google Chat (Chat API)",
docsPath: "/channels/googlechat",
docsLabel: "googlechat",
blurb: "Google Workspace Chat app with HTTP webhook.",
aliases: ["gchat", "google-chat"],
order: 55,
detailLabel: "Google Chat",
systemImage: "message.badge",
markdownCapable: true,
};
const loadGoogleChatChannelRuntime = createLazyRuntimeNamedExport(
() => import("./channel.runtime.js"),

View File

@ -1,4 +1,4 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { imessagePlugin } from "./src/channel.js";
import { setIMessageRuntime } from "./src/runtime.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { imessageSetupPlugin } from "./src/channel.setup.js";
export { imessageSetupPlugin } from "./src/channel.setup.js";

View File

@ -1,5 +1,5 @@
import { buildDmGroupAccountAllowlistAdapter } from "openclaw/plugin-sdk/allowlist-config-edit";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
import { sanitizeForPlainText } from "openclaw/plugin-sdk/outbound-runtime";

View File

@ -1,5 +1,5 @@
import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import type { ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { ircPlugin } from "./src/channel.js";
import { setIrcRuntime } from "./src/runtime.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { ircPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(ircPlugin);

View File

@ -1,8 +1,10 @@
import { createAccountListHelpers } from "openclaw/plugin-sdk/account-helpers";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import { resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
import { parseOptionalDelimitedEntries } from "openclaw/plugin-sdk/core";
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/core";
import {
parseOptionalDelimitedEntries,
tryReadSecretFileSync,
} from "openclaw/plugin-sdk/channel-core";
import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
import type { CoreConfig, IrcAccountConfig, IrcNickServConfig } from "./types.js";

View File

@ -1,6 +1,6 @@
export { createAccountStatusSink } from "openclaw/plugin-sdk/channel-lifecycle";
export { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
export type { ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
export { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
export { buildBaseChannelStatusSummary } from "openclaw/plugin-sdk/status-helpers";
export type { ChannelPlugin } from "openclaw/plugin-sdk/core";
export { DEFAULT_ACCOUNT_ID, getChatChannelMeta } from "openclaw/plugin-sdk/core";
export { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";

View File

@ -5,12 +5,12 @@ import {
createScopedChannelConfigAdapter,
createScopedDmSecurityResolver,
} from "openclaw/plugin-sdk/channel-config-helpers";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import {
composeAccountWarningCollectors,
composeWarningCollectors,
createAllowlistProviderOpenWarningCollector,
} from "openclaw/plugin-sdk/channel-policy";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
import {
createChannelDirectoryAdapter,
createResolvedDirectoryEntriesLister,
@ -32,7 +32,6 @@ import {
createAccountStatusSink,
chunkTextForOutbound,
DEFAULT_ACCOUNT_ID,
getChatChannelMeta,
PAIRING_APPROVED_MESSAGE,
type ChannelPlugin,
} from "./channel-api.js";
@ -54,7 +53,18 @@ import { ircSetupAdapter } from "./setup-core.js";
import { ircSetupWizard } from "./setup-surface.js";
import type { CoreConfig, IrcProbe } from "./types.js";
const meta = getChatChannelMeta("irc");
const meta = {
id: "irc",
label: "IRC",
selectionLabel: "IRC (Server + Nick)",
docsPath: "/channels/irc",
docsLabel: "irc",
blurb: "classic IRC networks; host, nick, channels.",
order: 80,
detailLabel: "IRC",
systemImage: "number",
markdownCapable: true,
};
function normalizePairingTarget(raw: string): string {
const normalized = normalizeIrcAllowEntry(raw);

View File

@ -1,12 +1,10 @@
// Private runtime barrel for the bundled IRC extension.
// Keep this barrel thin and generic-only.
export type {
BaseProbeResult,
ChannelPlugin,
OpenClawConfig,
PluginRuntime,
} from "openclaw/plugin-sdk/core";
export type { BaseProbeResult } from "openclaw/plugin-sdk/channel-contract";
export type { ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
export type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
export type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store";
export type { RuntimeEnv } from "openclaw/plugin-sdk/runtime";
export type {
BlockStreamingCoalesceConfig,
@ -18,11 +16,8 @@ export type {
MarkdownConfig,
} from "openclaw/plugin-sdk/config-runtime";
export type { OutboundReplyPayload } from "openclaw/plugin-sdk/reply-payload";
export {
DEFAULT_ACCOUNT_ID,
buildChannelConfigSchema,
getChatChannelMeta,
} from "openclaw/plugin-sdk/core";
export { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
export { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-primitives";
export {
PAIRING_APPROVED_MESSAGE,
buildBaseChannelStatusSummary,
@ -43,9 +38,9 @@ export {
} from "openclaw/plugin-sdk/reply-payload";
export {
GROUP_POLICY_BLOCKED_LABEL,
isDangerousNameMatchingEnabled,
resolveAllowlistProviderRuntimeGroupPolicy,
resolveDefaultGroupPolicy,
warnMissingProviderGroupPolicyFallbackOnce,
isDangerousNameMatchingEnabled,
} from "openclaw/plugin-sdk/config-runtime";
export { logInboundDrop } from "openclaw/plugin-sdk/channel-inbound";

View File

@ -1,4 +1,4 @@
import { defineChannelPluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/channel-core";
import { linePlugin } from "./src/channel.js";
import { setLineRuntime } from "./src/runtime.js";
@ -15,7 +15,7 @@ async function loadLineCardCommand(api: OpenClawPluginApi): Promise<RegisteredLi
const { registerLineCardCommand } = await import("./src/card-command.js");
registerLineCardCommand({
...api,
registerCommand(command) {
registerCommand(command: RegisteredLineCardCommand) {
registered = command;
},
});

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { lineSetupPlugin } from "./src/channel.setup.js";
export { lineSetupPlugin } from "./src/channel.setup.js";

View File

@ -1,6 +1,6 @@
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
import { createRestrictSendersChannelSecurity } from "openclaw/plugin-sdk/channel-policy";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
import { createEmptyChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
import { resolveLineAccount } from "./accounts.js";

View File

@ -1,4 +1,4 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { matrixPlugin } from "./src/channel.js";
import { registerMatrixCliMetadata } from "./src/cli-metadata.js";
import { setMatrixRuntime } from "./src/runtime.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { matrixPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(matrixPlugin);

View File

@ -5,6 +5,8 @@ import {
createScopedChannelConfigAdapter,
createScopedDmSecurityResolver,
} from "openclaw/plugin-sdk/channel-config-helpers";
import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-primitives";
import { createChatChannelPlugin, type ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import {
createPairingPrefixStripper,
createTextPairingAdapter,
@ -15,11 +17,6 @@ import {
} from "openclaw/plugin-sdk/channel-policy";
import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
import { createScopedAccountReplyToModeResolver } from "openclaw/plugin-sdk/conversation-runtime";
import {
buildChannelConfigSchema,
createChatChannelPlugin,
type ChannelPlugin,
} from "openclaw/plugin-sdk/core";
import {
createChannelDirectoryAdapter,
createResolvedDirectoryEntriesLister,

View File

@ -8,7 +8,7 @@ import {
detectPluginInstallPathIssue,
formatPluginInstallPathIssue,
removePluginFromConfig,
} from "openclaw/plugin-sdk/runtime";
} from "openclaw/plugin-sdk/runtime-doctor";
import {
hasLegacyFlatAllowPrivateNetworkAlias,
isPrivateNetworkOptInEnabled,

View File

@ -4,7 +4,6 @@ import path from "node:path";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
import { resolveRequiredHomeDir } from "openclaw/plugin-sdk/provider-auth";
import { createBackupArchive } from "openclaw/plugin-sdk/runtime";
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
import { detectLegacyMatrixCrypto } from "./legacy-crypto.js";
import { detectLegacyMatrixState } from "./legacy-state.js";
@ -103,6 +102,7 @@ export async function maybeCreateMatrixMigrationSnapshot(params: {
outputDir?: string;
log?: { info?: (message: string) => void; warn?: (message: string) => void };
}): Promise<MatrixMigrationSnapshotResult> {
const { createBackupArchive } = await import("openclaw/plugin-sdk/runtime");
const env = params.env ?? process.env;
const markerPath = resolveMatrixMigrationSnapshotMarkerPath(env);
const existingMarker = loadSnapshotMarker(markerPath);

View File

@ -1,23 +1,18 @@
export {
DEFAULT_ACCOUNT_ID,
buildChannelConfigSchema,
createActionGate,
getChatChannelMeta,
jsonResult,
normalizeAccountId,
normalizeOptionalAccountId,
} from "openclaw/plugin-sdk/account-id";
export {
createActionGate,
jsonResult,
readNumberParam,
readReactionParams,
readStringArrayParam,
readStringParam,
type PollInput,
type ReplyPayload,
} from "openclaw/plugin-sdk/core";
export type {
ChannelPlugin,
NormalizedLocation,
PluginRuntime,
RuntimeLogger,
} from "openclaw/plugin-sdk/core";
} from "openclaw/plugin-sdk/channel-actions";
export { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-primitives";
export type { ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
export type {
BaseProbeResult,
ChannelDirectoryEntry,
@ -31,9 +26,14 @@ export type {
ChannelResolveResult,
ChannelToolSend,
} from "openclaw/plugin-sdk/channel-contract";
export { formatZonedTimestamp } from "openclaw/plugin-sdk/core";
export { normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-id";
export type { ChannelSetupInput } from "openclaw/plugin-sdk/core";
export {
formatLocationText,
logInboundDrop,
toLocationContext,
type NormalizedLocation,
} from "openclaw/plugin-sdk/channel-inbound";
export { resolveAckReaction, logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
export type { ChannelSetupInput } from "openclaw/plugin-sdk/setup";
export type {
OpenClawConfig,
ContextVisibilityMode,
@ -41,7 +41,7 @@ export type {
GroupPolicy,
} from "openclaw/plugin-sdk/config-runtime";
export type { GroupToolPolicyConfig } from "openclaw/plugin-sdk/config-runtime";
export type { WizardPrompter } from "openclaw/plugin-sdk/core";
export type { WizardPrompter } from "openclaw/plugin-sdk/matrix-runtime-shared";
export type { SecretInput } from "openclaw/plugin-sdk/secret-input";
export {
GROUP_POLICY_BLOCKED_LABEL,
@ -74,7 +74,7 @@ export { dispatchReplyFromConfigWithSettledDispatcher } from "openclaw/plugin-sd
export {
ensureConfiguredAcpBindingReady,
resolveConfiguredAcpBindingRecord,
} from "openclaw/plugin-sdk/core";
} from "openclaw/plugin-sdk/acp-binding-runtime";
export {
buildProbeChannelStatusSummary,
collectStatusIssuesFromLastError,
@ -90,8 +90,22 @@ export { resolveAgentIdFromSessionKey } from "openclaw/plugin-sdk/routing";
export { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
export { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";
export { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
export { normalizePollInput } from "openclaw/plugin-sdk/media-runtime";
export { normalizePollInput, type PollInput } from "openclaw/plugin-sdk/media-runtime";
export { writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
export {
buildChannelKeyCandidates,
resolveChannelEntryMatch,
} from "openclaw/plugin-sdk/channel-targets";
export {
evaluateGroupRouteAccessForPolicy,
resolveSenderScopedGroupPolicy,
} from "openclaw/plugin-sdk/channel-policy";
export {
formatZonedTimestamp,
type PluginRuntime,
type RuntimeLogger,
} from "openclaw/plugin-sdk/matrix-runtime-shared";
export type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
// resolveMatrixAccountStringValues already comes from plugin-sdk/matrix.
// Re-exporting auth-precedence here makes Jiti try to define the same export twice.

View File

@ -3,7 +3,7 @@ import {
stripChannelTargetPrefix,
stripTargetKindPrefix,
type ChannelOutboundSessionRouteParams,
} from "openclaw/plugin-sdk/core";
} from "openclaw/plugin-sdk/channel-core";
export function resolveMatrixOutboundSessionRoute(params: ChannelOutboundSessionRouteParams) {
const stripped = stripChannelTargetPrefix(params.target, "matrix");

View File

@ -1,4 +1,4 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { mattermostPlugin } from "./src/channel.js";
import { registerSlashCommandRoute } from "./src/mattermost/slash-state.js";
import { setMattermostRuntime } from "./src/runtime.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { mattermostPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(mattermostPlugin);

View File

@ -11,9 +11,9 @@ import type {
ChannelMessageActionName,
ChannelMessageToolDiscovery,
} from "openclaw/plugin-sdk/channel-contract";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { createLoggedPairingApprovalNotifier } from "openclaw/plugin-sdk/channel-pairing";
import { createRestrictSendersChannelSecurity } from "openclaw/plugin-sdk/channel-policy";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
import { createChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
import { isPrivateNetworkOptInEnabled } from "openclaw/plugin-sdk/ssrf-runtime";

View File

@ -1,4 +1,4 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { msteamsPlugin } from "./src/channel.js";
import { setMSTeamsRuntime } from "./src/runtime.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { msteamsPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(msteamsPlugin);

View File

@ -6,12 +6,12 @@ import type {
ChannelMessageActionAdapter,
ChannelMessageToolDiscovery,
} from "openclaw/plugin-sdk/channel-contract";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
import {
createAllowlistProviderGroupPolicyWarningCollector,
projectConfigWarningCollector,
} from "openclaw/plugin-sdk/channel-policy";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
import {
createChannelDirectoryAdapter,
createRuntimeDirectoryLiveAdapter,

View File

@ -1,4 +1,4 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { nextcloudTalkPlugin } from "./src/channel.js";
import { setNextcloudTalkRuntime } from "./src/runtime.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { nextcloudTalkPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(nextcloudTalkPlugin);

View File

@ -1,5 +1,5 @@
import { resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/core";
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/channel-core";
import {
createAccountListHelpers,
DEFAULT_ACCOUNT_ID,

View File

@ -1,3 +1,5 @@
export type { ChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk/core";
export { clearAccountEntryFields, DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/core";
export type { ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
export type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
export { clearAccountEntryFields } from "openclaw/plugin-sdk/channel-core";
export { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
export { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";

View File

@ -5,13 +5,13 @@ import {
createScopedChannelConfigAdapter,
createScopedDmSecurityResolver,
} from "openclaw/plugin-sdk/channel-config-helpers";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { createAccountStatusSink } from "openclaw/plugin-sdk/channel-lifecycle";
import {
createLoggedPairingApprovalNotifier,
createPairingPrefixStripper,
} from "openclaw/plugin-sdk/channel-pairing";
import { createAllowlistProviderRouteAllowlistWarningCollector } from "openclaw/plugin-sdk/channel-policy";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
import { runStoppablePassiveMonitor } from "openclaw/plugin-sdk/extension-shared";
import {
buildWebhookChannelStatusSummary,

View File

@ -1,7 +1,7 @@
import {
buildChannelOutboundSessionRoute,
type ChannelOutboundSessionRouteParams,
} from "openclaw/plugin-sdk/core";
} from "openclaw/plugin-sdk/channel-core";
import { stripNextcloudTalkTargetPrefix } from "./normalize.js";
export function resolveNextcloudTalkOutboundSessionRoute(

View File

@ -1,4 +1,4 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { nostrPlugin } from "./src/channel.js";
import type { NostrProfile } from "./src/config-schema.js";
import { createNostrProfileHttpHandler } from "./src/nostr-profile-http.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { nostrPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(nostrPlugin);

View File

@ -3,9 +3,9 @@ import {
createScopedDmSecurityResolver,
createTopLevelChannelConfigAdapter,
} from "openclaw/plugin-sdk/channel-config-helpers";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
import { attachChannelToResult } from "openclaw/plugin-sdk/channel-send-result";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
import {
buildPassiveChannelStatusSummary,
buildTrafficStatusSummary,

View File

@ -1,5 +1,9 @@
import type { ChannelPlugin, OpenClawPluginApi } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import type {
ChannelPlugin,
OpenClawPluginApi,
PluginCommandContext,
} from "openclaw/plugin-sdk/channel-core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { qqbotPlugin } from "./src/channel.js";
import { resolveQQBotAccount } from "./src/config.js";
import { sendDocument, type MediaTargetContext } from "./src/outbound.js";
@ -30,7 +34,7 @@ export default defineChannelPluginEntry({
description: cmd.description,
requireAuth: true,
acceptsArgs: true,
handler: async (ctx) => {
handler: async (ctx: PluginCommandContext) => {
// Derive the QQBot message type from ctx.from so that handlers that
// inspect SlashCommandContext.type get the correct value.
// ctx.from format: "qqbot:<type>:<id>" e.g. "qqbot:c2c:<senderId>"

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { qqbotSetupPlugin } from "./src/channel.setup.js";
export { qqbotSetupPlugin } from "./src/channel.setup.js";

View File

@ -1,5 +1,6 @@
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
import { buildDmGroupAccountAllowlistAdapter } from "openclaw/plugin-sdk/allowlist-config-edit";
import { createChatChannelPlugin, type ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
import {
attachChannelToResult,
@ -7,7 +8,6 @@ import {
} from "openclaw/plugin-sdk/channel-send-result";
import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/config-runtime";
import { createChatChannelPlugin, type ChannelPlugin } from "openclaw/plugin-sdk/core";
import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/media-runtime";
import { resolveOutboundSendDep } from "openclaw/plugin-sdk/outbound-runtime";
import { chunkText, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";

View File

@ -4,7 +4,7 @@ import {
type ChannelDoctorLegacyConfigRule,
} from "openclaw/plugin-sdk/channel-contract";
import { type OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { collectProviderDangerousNameMatchingScopes } from "openclaw/plugin-sdk/runtime";
import { collectProviderDangerousNameMatchingScopes } from "openclaw/plugin-sdk/runtime-doctor";
import { isSlackMutableAllowEntry } from "./security-doctor.js";
import {
formatSlackStreamingBooleanMigrationMessage,

View File

@ -1,4 +1,4 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { synologyChatPlugin } from "./src/channel.js";
import { setSynologyRuntime } from "./src/runtime.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { synologyChatPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(synologyChatPlugin);

View File

@ -10,6 +10,7 @@ import {
createHybridChannelConfigAdapter,
createScopedDmSecurityResolver,
} from "openclaw/plugin-sdk/channel-config-helpers";
import { createChatChannelPlugin, type ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { waitUntilAbort } from "openclaw/plugin-sdk/channel-lifecycle";
import {
composeWarningCollectors,
@ -18,7 +19,6 @@ import {
projectAccountWarningCollector,
} from "openclaw/plugin-sdk/channel-policy";
import { attachChannelToResult } from "openclaw/plugin-sdk/channel-send-result";
import { createChatChannelPlugin, type ChannelPlugin } from "openclaw/plugin-sdk/core";
import { createEmptyChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
import { listAccountIds, resolveAccount } from "./accounts.js";
import { synologyChatApprovalAuth } from "./approval-auth.js";

View File

@ -1,5 +1,5 @@
import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import type { ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { telegramPlugin } from "./src/channel.js";
import { setTelegramRuntime } from "./src/runtime.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { telegramSetupPlugin } from "./src/channel.setup.js";
export { telegramSetupPlugin } from "./src/channel.setup.js";

View File

@ -4,6 +4,7 @@ import {
createNestedAllowlistOverrideResolver,
} from "openclaw/plugin-sdk/allowlist-config-edit";
import type { ChannelMessageActionAdapter } from "openclaw/plugin-sdk/channel-contract";
import { clearAccountEntryFields, createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
import { createAllowlistProviderRouteAllowlistWarningCollector } from "openclaw/plugin-sdk/channel-policy";
import { attachChannelToResult } from "openclaw/plugin-sdk/channel-send-result";
@ -14,8 +15,6 @@ import {
resolveConfiguredFromCredentialStatuses,
} from "openclaw/plugin-sdk/channel-status";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
import { clearAccountEntryFields } from "openclaw/plugin-sdk/core";
import { createChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
import {
resolveOutboundSendDep,

View File

@ -5,10 +5,6 @@ import {
type ChannelDoctorLegacyConfigRule,
} from "openclaw/plugin-sdk/channel-contract";
import { type OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import {
getChannelsCommandSecretTargetIds,
resolveCommandSecretRefsViaGateway,
} from "openclaw/plugin-sdk/runtime";
import { inspectTelegramAccount } from "./account-inspect.js";
import { listTelegramAccountIds, resolveTelegramAccount } from "./accounts.js";
import { isNumericTelegramUserId, normalizeTelegramAllowFromEntry } from "./allow-from.js";
@ -252,6 +248,9 @@ export async function maybeRepairTelegramAllowFromUsernames(cfg: OpenClawConfig)
return { config: cfg, changes: [] };
}
const { getChannelsCommandSecretTargetIds, resolveCommandSecretRefsViaGateway } =
await import("openclaw/plugin-sdk/runtime-secret-resolution");
const { resolvedConfig } = await resolveCommandSecretRefsViaGateway({
config: cfg,
commandName: "doctor --fix",

View File

@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
import { existsSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { tlonPlugin } from "./src/channel.js";
import { setTlonRuntime } from "./src/runtime.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { tlonPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(tlonPlugin);

View File

@ -1,8 +1,8 @@
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
import { createHybridChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
import { createChatChannelPlugin, type ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { createChatChannelPlugin, type ChannelPlugin } from "openclaw/plugin-sdk/core";
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
import { createRuntimeOutboundDelegates } from "openclaw/plugin-sdk/outbound-runtime";
import {
@ -10,6 +10,7 @@ import {
createDefaultChannelRuntimeState,
} from "openclaw/plugin-sdk/status-helpers";
import { tlonChannelConfigSchema } from "./config-schema.js";
import { tlonDoctor } from "./doctor.js";
import { resolveTlonOutboundSessionRoute } from "./session-route.js";
import {
applyTlonSetupConfig,
@ -17,7 +18,6 @@ import {
resolveTlonSetupConfigured,
tlonSetupAdapter,
} from "./setup-core.js";
import { tlonDoctor } from "./doctor.js";
import {
formatTargetHint,
normalizeShip,

View File

@ -1,4 +1,4 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { twitchPlugin } from "./src/plugin.js";
import { setTwitchRuntime } from "./src/runtime.js";

View File

@ -6,11 +6,11 @@
*/
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import {
createLoggedPairingApprovalNotifier,
createPairingPrefixStripper,
} from "openclaw/plugin-sdk/channel-pairing";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
import {
createComputedAccountStatusAdapter,

View File

@ -1,4 +1,4 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { whatsappPlugin } from "./src/channel.js";
import { setWhatsAppRuntime } from "./src/runtime.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { whatsappSetupPlugin } from "./src/channel.setup.js";
export { whatsappSetupPlugin } from "./src/channel.setup.js";

View File

@ -1,6 +1,6 @@
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
import { buildDmGroupAccountAllowlistAdapter } from "openclaw/plugin-sdk/allowlist-config-edit";
import { createChatChannelPlugin, type ChannelPlugin } from "openclaw/plugin-sdk/core";
import { createChatChannelPlugin, type ChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
import {
createAsyncComputedAccountStatusAdapter,

View File

@ -1,4 +1,4 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { zaloPlugin } from "./src/channel.js";
import { setZaloRuntime } from "./src/runtime.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { zaloPlugin } from "./src/channel.js";
export default defineSetupPluginEntry(zaloPlugin);

View File

@ -8,6 +8,11 @@ import {
mapAllowFromEntries,
} from "openclaw/plugin-sdk/channel-config-helpers";
import type { ChannelAccountSnapshot } from "openclaw/plugin-sdk/channel-contract";
import {
buildChannelConfigSchema,
createChatChannelPlugin,
type ChannelPlugin,
} from "openclaw/plugin-sdk/channel-core";
import {
buildOpenGroupPolicyRestrictSendersWarning,
buildOpenGroupPolicyWarning,
@ -20,8 +25,6 @@ import {
import { buildTokenChannelStatusSummary } from "openclaw/plugin-sdk/channel-status";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { createStaticReplyToModeResolver } from "openclaw/plugin-sdk/conversation-runtime";
import { createChatChannelPlugin, buildChannelConfigSchema } from "openclaw/plugin-sdk/core";
import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
import { createChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
import { listResolvedDirectoryUserEntriesFromAllowFrom } from "openclaw/plugin-sdk/directory-runtime";
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";

View File

@ -1,4 +1,4 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { zalouserPlugin } from "./src/channel.js";
import { setZalouserRuntime } from "./src/runtime.js";
import { createZalouserTool } from "./src/tool.js";

View File

@ -1,4 +1,4 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { zalouserSetupPlugin } from "./src/channel.setup.js";
export { zalouserSetupPlugin } from "./src/channel.setup.js";

View File

@ -1,4 +1,5 @@
import { createScopedDmSecurityResolver } from "openclaw/plugin-sdk/channel-config-helpers";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
import { createAccountStatusSink } from "openclaw/plugin-sdk/channel-lifecycle";
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
import {
@ -6,7 +7,6 @@ import {
createRawChannelSendResultAdapter,
} from "openclaw/plugin-sdk/channel-send-result";
import { createStaticReplyToModeResolver } from "openclaw/plugin-sdk/conversation-runtime";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
import {

View File

@ -4,7 +4,7 @@ import type {
ChannelDoctorLegacyConfigRule,
} from "openclaw/plugin-sdk/channel-contract";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { collectProviderDangerousNameMatchingScopes } from "openclaw/plugin-sdk/runtime";
import { collectProviderDangerousNameMatchingScopes } from "openclaw/plugin-sdk/runtime-doctor";
import { isZalouserMutableGroupEntry } from "./security-audit.js";
type ZalouserChannelsConfig = NonNullable<OpenClawConfig["channels"]>;

View File

@ -6,7 +6,9 @@
"self-hosted-provider-setup",
"routing",
"runtime",
"runtime-doctor",
"runtime-env",
"runtime-secret-resolution",
"setup",
"setup-adapter-runtime",
"setup-runtime",

View File

@ -267,6 +267,15 @@ async function inferImplicitProviderTestPluginIds(params: {
providerIds.add(providerId.trim());
}
}
const legacyGrokApiKey =
params.config?.tools?.web?.search &&
typeof params.config.tools.web.search === "object" &&
"grok" in params.config.tools.web.search
? (params.config.tools.web.search.grok as { apiKey?: unknown } | undefined)?.apiKey
: undefined;
if (legacyGrokApiKey !== undefined && params.config?.plugins?.entries?.xai?.enabled !== false) {
providerIds.add("xai");
}
for (const [envVar, mappedProviderIds] of Object.entries(TEST_PROVIDER_ENV_TO_PROVIDER_IDS)) {
if (!params.env[envVar]?.trim()) {
continue;

View File

@ -1,3 +1,5 @@
import fs from "node:fs";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { importFreshModule } from "../../../test/helpers/import-fresh.ts";
@ -29,4 +31,120 @@ describe("bundled channel entry shape guards", () => {
expect(bundled.listBundledChannelPlugins()).toEqual([]);
expect(bundled.listBundledChannelSetupPlugins()).toEqual([]);
});
it("keeps channel entrypoints on the narrow channel-core SDK surface", () => {
const extensionRoot = path.resolve("extensions");
const offenders: string[] = [];
for (const extensionId of fs.readdirSync(extensionRoot)) {
const extensionDir = path.join(extensionRoot, extensionId);
if (!fs.statSync(extensionDir).isDirectory()) {
continue;
}
for (const relativePath of ["index.ts", "setup-entry.ts"]) {
const filePath = path.join(extensionDir, relativePath);
if (!fs.existsSync(filePath)) {
continue;
}
const source = fs.readFileSync(filePath, "utf8");
const usesEntryHelpers =
source.includes("defineChannelPluginEntry") || source.includes("defineSetupPluginEntry");
if (!usesEntryHelpers) {
continue;
}
if (source.includes('from "openclaw/plugin-sdk/core"')) {
offenders.push(path.relative(process.cwd(), filePath));
}
}
}
expect(offenders).toEqual([]);
});
it("keeps channel implementations off the broad core SDK surface", () => {
const extensionRoot = path.resolve("extensions");
const offenders: string[] = [];
for (const extensionId of fs.readdirSync(extensionRoot)) {
const extensionDir = path.join(extensionRoot, extensionId);
if (!fs.statSync(extensionDir).isDirectory()) {
continue;
}
for (const relativePath of ["src/channel.ts", "src/plugin.ts"]) {
const filePath = path.join(extensionDir, relativePath);
if (!fs.existsSync(filePath)) {
continue;
}
const source = fs.readFileSync(filePath, "utf8");
if (!source.includes("createChatChannelPlugin")) {
continue;
}
if (source.includes('from "openclaw/plugin-sdk/core"')) {
offenders.push(path.relative(process.cwd(), filePath));
}
}
}
expect(offenders).toEqual([]);
});
it("keeps plugin-sdk channel-core free of chat metadata bootstrap imports", () => {
const source = fs.readFileSync(path.resolve("src/plugin-sdk/channel-core.ts"), "utf8");
expect(source.includes("../channels/chat-meta.js")).toBe(false);
expect(source.includes("getChatChannelMeta")).toBe(false);
});
it("keeps bundled hot runtime barrels off the broad core SDK surface", () => {
const offenders = [
"extensions/googlechat/runtime-api.ts",
"extensions/irc/src/runtime-api.ts",
"extensions/matrix/src/runtime-api.ts",
].filter((filePath) =>
fs.readFileSync(path.resolve(filePath), "utf8").includes("openclaw/plugin-sdk/core"),
);
expect(offenders).toEqual([]);
});
it("keeps runtime helper surfaces off bootstrap-registry", () => {
const offenders = [
"src/config/markdown-tables.ts",
"src/config/sessions/group.ts",
"src/channels/plugins/setup-helpers.ts",
"src/plugin-sdk/extension-shared.ts",
].filter((filePath) =>
fs.readFileSync(path.resolve(filePath), "utf8").includes("bootstrap-registry.js"),
);
expect(offenders).toEqual([]);
});
it("keeps extension-shared off the broad runtime barrel", () => {
const source = fs.readFileSync(path.resolve("src/plugin-sdk/extension-shared.ts"), "utf8");
expect(source.includes('from "./runtime.js"')).toBe(false);
});
it("keeps nextcloud-talk's private SDK surface off the broad runtime barrel", () => {
const source = fs.readFileSync(path.resolve("src/plugin-sdk/nextcloud-talk.ts"), "utf8");
expect(source.includes('from "./runtime.js"')).toBe(false);
});
it("keeps bundled doctor surfaces off the broad runtime barrel", () => {
const offenders = [
"extensions/discord/src/doctor.ts",
"extensions/matrix/src/doctor.ts",
"extensions/slack/src/doctor.ts",
"extensions/telegram/src/doctor.ts",
"extensions/zalouser/src/doctor.ts",
].filter((filePath) =>
fs
.readFileSync(path.resolve(filePath), "utf8")
.includes('from "openclaw/plugin-sdk/runtime"'),
);
expect(offenders).toEqual([]);
});
});

View File

@ -242,8 +242,9 @@ function loadGeneratedBundledChannelEntries(): readonly GeneratedBundledChannelE
...(setupEntry ? { setupEntry } : {}),
});
} catch (error) {
const detail = error instanceof Error ? error.message : String(error);
log.warn(
`[channels] failed to load bundled channel ${manifest.id} from ${candidate.source}: ${String(error)}`,
`[channels] failed to load bundled channel ${manifest.id} from ${candidate.source}: ${detail}`,
);
}
}

View File

@ -1,7 +1,7 @@
import { z, type ZodType } from "zod";
import type { OpenClawConfig } from "../../config/config.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
import { getBootstrapChannelPlugin } from "./bootstrap-registry.js";
import { getChannelPlugin } from "./registry.js";
import type { ChannelSetupAdapter } from "./types.adapters.js";
import type { ChannelSetupInput } from "./types.core.js";
@ -417,7 +417,7 @@ type ChannelSetupPromotionSurface = {
};
function getChannelSetupPromotionSurface(channelKey: string): ChannelSetupPromotionSurface | null {
const setup = getBootstrapChannelPlugin(channelKey)?.setup;
const setup = getChannelPlugin(channelKey)?.setup;
if (!setup || typeof setup !== "object") {
return null;
}

View File

@ -1,5 +1,6 @@
import { listBootstrapChannelPlugins } from "../channels/plugins/bootstrap-registry.js";
import { normalizeChannelId } from "../channels/plugins/index.js";
import { listChannelPlugins } from "../channels/plugins/registry.js";
import { getActivePluginChannelRegistryVersion } from "../plugins/runtime.js";
import { resolveAccountEntry } from "../routing/account-lookup.js";
import { normalizeAccountId } from "../routing/session-key.js";
import type { OpenClawConfig } from "./config.js";
@ -17,7 +18,7 @@ type MarkdownConfigSection = MarkdownConfigEntry & {
function buildDefaultTableModes(): Map<string, MarkdownTableMode> {
return new Map(
listBootstrapChannelPlugins()
listChannelPlugins()
.flatMap((plugin) => {
const defaultMarkdownTableMode = plugin.messaging?.defaultMarkdownTableMode;
return defaultMarkdownTableMode ? [[plugin.id, defaultMarkdownTableMode] as const] : [];
@ -27,9 +28,14 @@ function buildDefaultTableModes(): Map<string, MarkdownTableMode> {
}
let cachedDefaultTableModes: Map<string, MarkdownTableMode> | null = null;
let cachedDefaultTableModesRegistryVersion: number | null = null;
function getDefaultTableModes(): Map<string, MarkdownTableMode> {
cachedDefaultTableModes ??= buildDefaultTableModes();
const registryVersion = getActivePluginChannelRegistryVersion();
if (!cachedDefaultTableModes || cachedDefaultTableModesRegistryVersion !== registryVersion) {
cachedDefaultTableModes = buildDefaultTableModes();
cachedDefaultTableModesRegistryVersion = registryVersion;
}
return cachedDefaultTableModes;
}

View File

@ -1,5 +1,5 @@
import type { MsgContext } from "../../auto-reply/templating.js";
import { listBootstrapChannelPlugins } from "../../channels/plugins/bootstrap-registry.js";
import { listChannelPlugins } from "../../channels/plugins/registry.js";
import { normalizeHyphenSlug } from "../../shared/string-normalization.js";
import { listDeliverableMessageChannels } from "../../utils/message-channel.js";
import type { GroupKeyResolution } from "./types.js";
@ -11,7 +11,7 @@ type LegacyGroupSessionSurface = {
};
function resolveLegacyGroupSessionKey(ctx: MsgContext): GroupKeyResolution | null {
for (const plugin of listBootstrapChannelPlugins()) {
for (const plugin of listChannelPlugins()) {
const resolved = (
plugin.messaging as LegacyGroupSessionSurface | undefined
)?.resolveLegacyGroupSessionKey?.(ctx);

View File

@ -0,0 +1,5 @@
// Narrow ACP binding helpers for plugins that need persistent ACP setup state
// without importing the broad core SDK surface.
export { ensureConfiguredAcpBindingReady } from "../acp/persistent-bindings.lifecycle.js";
export { resolveConfiguredAcpBindingRecord } from "../acp/persistent-bindings.resolve.js";

View File

@ -25,11 +25,16 @@ import type { ReplyToMode } from "../config/types.base.js";
import { buildOutboundBaseSessionKey } from "../infra/outbound/base-session-key.js";
import type { OutboundDeliveryResult } from "../infra/outbound/deliver.js";
import type { PluginRuntime } from "../plugins/runtime/types.js";
import type { OpenClawPluginApi } from "../plugins/types.js";
import type { OpenClawPluginApi, PluginCommandContext } from "../plugins/types.js";
export type { ChannelConfigUiHint, ChannelPlugin };
export type { OpenClawConfig };
export type { PluginRuntime };
export type { OpenClawPluginApi, PluginCommandContext };
export { buildChannelConfigSchema } from "../channels/plugins/config-schema.js";
export { clearAccountEntryFields } from "../channels/plugins/config-helpers.js";
export { parseOptionalDelimitedEntries } from "../channels/plugins/helpers.js";
export { tryReadSecretFileSync } from "../infra/secret-file.js";
export type ChannelOutboundSessionRouteParams = Parameters<
NonNullable<ChannelMessagingAdapter["resolveOutboundSessionRoute"]>

View File

@ -46,6 +46,10 @@ export {
resolveDmGroupAccessWithLists,
resolveEffectiveAllowFromLists,
} from "../security/dm-policy-shared.js";
export {
evaluateGroupRouteAccessForPolicy,
resolveSenderScopedGroupPolicy,
} from "./group-access.js";
export { createAllowlistProviderRestrictSendersWarningCollector };
export type ChannelMutableAllowlistCandidate = {

View File

@ -1,6 +1,6 @@
import type { z } from "zod";
import { runPassiveAccountLifecycle } from "./channel-lifecycle.core.js";
import { createLoggerBackedRuntime } from "./runtime.js";
import { createLoggerBackedRuntime } from "./runtime-logger.js";
export { safeParseJsonWithSchema, safeParseWithSchema } from "../utils/zod-parse.js";
type PassiveChannelStatusSnapshot = {

View File

@ -0,0 +1,4 @@
// Narrow Google Chat runtime exports used by the bundled Google Chat plugin.
export type { GoogleChatAccountConfig, GoogleChatConfig } from "../config/types.js";
export { GoogleChatConfigSchema } from "../config/zod-schema.providers-core.js";

View File

@ -103,7 +103,7 @@ export {
resolveOutboundMediaUrls,
} from "./reply-payload.js";
export { dispatchInboundReplyWithBase } from "./inbound-reply-dispatch.js";
export { createLoggerBackedRuntime } from "./runtime.js";
export { createLoggerBackedRuntime } from "./runtime-logger.js";
export {
buildBaseChannelStatusSummary,
buildRuntimeAccountStatusSnapshot,

View File

@ -0,0 +1,6 @@
export { collectProviderDangerousNameMatchingScopes } from "../config/dangerous-name-matching.js";
export {
detectPluginInstallPathIssue,
formatPluginInstallPathIssue,
} from "../infra/plugin-install-path-warnings.js";
export { removePluginFromConfig } from "../plugins/uninstall.js";

View File

@ -0,0 +1,80 @@
import { format } from "node:util";
import type { OutputRuntimeEnv, RuntimeEnv } from "../runtime.js";
/** Minimal logger contract accepted by runtime-adapter helpers. */
type LoggerLike = {
info: (message: string) => void;
error: (message: string) => void;
};
/** Adapt a simple logger into the RuntimeEnv contract used by shared plugin SDK helpers. */
export function createLoggerBackedRuntime(params: {
logger: LoggerLike;
exitError?: (code: number) => Error;
}): OutputRuntimeEnv {
return {
log: (...args) => {
params.logger.info(format(...args));
},
error: (...args) => {
params.logger.error(format(...args));
},
writeStdout: (value) => {
params.logger.info(value);
},
writeJson: (value, space = 2) => {
params.logger.info(JSON.stringify(value, null, space > 0 ? space : undefined));
},
exit: (code: number): never => {
throw params.exitError?.(code) ?? new Error(`exit ${code}`);
},
};
}
/** Reuse an existing runtime when present, otherwise synthesize one from the provided logger. */
export function resolveRuntimeEnv(params: {
runtime: RuntimeEnv;
logger: LoggerLike;
exitError?: (code: number) => Error;
}): RuntimeEnv;
export function resolveRuntimeEnv(params: {
runtime?: undefined;
logger: LoggerLike;
exitError?: (code: number) => Error;
}): OutputRuntimeEnv;
export function resolveRuntimeEnv(params: {
runtime?: RuntimeEnv;
logger: LoggerLike;
exitError?: (code: number) => Error;
}): RuntimeEnv | OutputRuntimeEnv {
return params.runtime ?? createLoggerBackedRuntime(params);
}
/** Resolve a runtime that treats exit requests as unsupported errors instead of process termination. */
export function resolveRuntimeEnvWithUnavailableExit(params: {
runtime: RuntimeEnv;
logger: LoggerLike;
unavailableMessage?: string;
}): RuntimeEnv;
export function resolveRuntimeEnvWithUnavailableExit(params: {
runtime?: undefined;
logger: LoggerLike;
unavailableMessage?: string;
}): OutputRuntimeEnv;
export function resolveRuntimeEnvWithUnavailableExit(params: {
runtime?: RuntimeEnv;
logger: LoggerLike;
unavailableMessage?: string;
}): RuntimeEnv | OutputRuntimeEnv {
if (params.runtime) {
return resolveRuntimeEnv({
runtime: params.runtime,
logger: params.logger,
exitError: () => new Error(params.unavailableMessage ?? "Runtime exit not available"),
});
}
return resolveRuntimeEnv({
logger: params.logger,
exitError: () => new Error(params.unavailableMessage ?? "Runtime exit not available"),
});
}

View File

@ -0,0 +1,2 @@
export { resolveCommandSecretRefsViaGateway } from "../cli/command-secret-gateway.js";
export { getChannelsCommandSecretTargetIds } from "../cli/command-secret-targets.js";

View File

@ -1,9 +1,13 @@
import { format } from "node:util";
import type { OutputRuntimeEnv, RuntimeEnv } from "../runtime.js";
export type { OutputRuntimeEnv, RuntimeEnv } from "../runtime.js";
export { createNonExitingRuntime, defaultRuntime } from "../runtime.js";
export { resolveCommandSecretRefsViaGateway } from "../cli/command-secret-gateway.js";
export { getChannelsCommandSecretTargetIds } from "../cli/command-secret-targets.js";
export {
createLoggerBackedRuntime,
resolveRuntimeEnv,
resolveRuntimeEnvWithUnavailableExit,
} from "./runtime-logger.js";
export {
danger,
info,
@ -27,81 +31,3 @@ export {
export { collectProviderDangerousNameMatchingScopes } from "../config/dangerous-name-matching.js";
export { registerUnhandledRejectionHandler } from "../infra/unhandled-rejections.js";
export { removePluginFromConfig } from "../plugins/uninstall.js";
/** Minimal logger contract accepted by runtime-adapter helpers. */
type LoggerLike = {
info: (message: string) => void;
error: (message: string) => void;
};
/** Adapt a simple logger into the RuntimeEnv contract used by shared plugin SDK helpers. */
export function createLoggerBackedRuntime(params: {
logger: LoggerLike;
exitError?: (code: number) => Error;
}): OutputRuntimeEnv {
return {
log: (...args) => {
params.logger.info(format(...args));
},
error: (...args) => {
params.logger.error(format(...args));
},
writeStdout: (value) => {
params.logger.info(value);
},
writeJson: (value, space = 2) => {
params.logger.info(JSON.stringify(value, null, space > 0 ? space : undefined));
},
exit: (code: number): never => {
throw params.exitError?.(code) ?? new Error(`exit ${code}`);
},
};
}
/** Reuse an existing runtime when present, otherwise synthesize one from the provided logger. */
export function resolveRuntimeEnv(params: {
runtime: RuntimeEnv;
logger: LoggerLike;
exitError?: (code: number) => Error;
}): RuntimeEnv;
export function resolveRuntimeEnv(params: {
runtime?: undefined;
logger: LoggerLike;
exitError?: (code: number) => Error;
}): OutputRuntimeEnv;
export function resolveRuntimeEnv(params: {
runtime?: RuntimeEnv;
logger: LoggerLike;
exitError?: (code: number) => Error;
}): RuntimeEnv | OutputRuntimeEnv {
return params.runtime ?? createLoggerBackedRuntime(params);
}
/** Resolve a runtime that treats exit requests as unsupported errors instead of process termination. */
export function resolveRuntimeEnvWithUnavailableExit(params: {
runtime: RuntimeEnv;
logger: LoggerLike;
unavailableMessage?: string;
}): RuntimeEnv;
export function resolveRuntimeEnvWithUnavailableExit(params: {
runtime?: undefined;
logger: LoggerLike;
unavailableMessage?: string;
}): OutputRuntimeEnv;
export function resolveRuntimeEnvWithUnavailableExit(params: {
runtime?: RuntimeEnv;
logger: LoggerLike;
unavailableMessage?: string;
}): RuntimeEnv | OutputRuntimeEnv {
if (params.runtime) {
return resolveRuntimeEnv({
runtime: params.runtime,
logger: params.logger,
exitError: () => new Error(params.unavailableMessage ?? "Runtime exit not available"),
});
}
return resolveRuntimeEnv({
logger: params.logger,
exitError: () => new Error(params.unavailableMessage ?? "Runtime exit not available"),
});
}

View File

@ -1,6 +1,7 @@
import { z } from "zod";
import {
hasConfiguredSecretInput,
isSecretRef,
normalizeResolvedSecretInputString,
normalizeSecretInputString,
} from "../config/types.secrets.js";
@ -11,6 +12,7 @@ export type { SecretInput } from "../config/types.secrets.js";
export {
buildSecretInputSchema,
hasConfiguredSecretInput,
isSecretRef,
normalizeResolvedSecretInputString,
normalizeSecretInput,
normalizeSecretInputString,