refactor: clean bundled channel bootstrap boundaries

This commit is contained in:
Peter Steinberger
2026-04-05 18:18:47 +01:00
parent cb76e5c899
commit 3126809cb0
88 changed files with 1107 additions and 472 deletions

View File

@@ -1,3 +1,5 @@
export { bluebubblesPlugin } from "./src/channel.js";
export { bluebubblesSetupPlugin } from "./src/channel.setup.js";
export * from "./src/conversation-id.js";
export * from "./src/conversation-bindings.js";
export { collectBlueBubblesStatusIssues } from "./src/status-issues.js";

View File

@@ -1,14 +1,16 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { bluebubblesPlugin } from "./src/channel.js";
import { setBlueBubblesRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { bluebubblesPlugin } from "./src/channel.js";
export { setBlueBubblesRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "bluebubbles",
name: "BlueBubbles",
description: "BlueBubbles channel plugin (macOS app)",
plugin: bluebubblesPlugin,
setRuntime: setBlueBubblesRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "bluebubblesPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setBlueBubblesRuntime",
},
});

View File

@@ -2,3 +2,4 @@ export {
resolveBlueBubblesGroupRequireMention,
resolveBlueBubblesGroupToolPolicy,
} from "./src/group-policy.js";
export { setBlueBubblesRuntime } from "./src/runtime.js";

View File

@@ -1,6 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { bluebubblesSetupPlugin } from "./src/channel.setup.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { bluebubblesSetupPlugin } from "./src/channel.setup.js";
export default defineSetupPluginEntry(bluebubblesSetupPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "bluebubblesSetupPlugin",
},
});

View File

@@ -1,3 +1,10 @@
export { discordPlugin } from "./src/channel.js";
export { discordSetupPlugin } from "./src/channel.setup.js";
export {
handleDiscordSubagentDeliveryTarget,
handleDiscordSubagentEnded,
handleDiscordSubagentSpawning,
} from "./src/subagent-hooks.js";
export * from "./src/account-inspect.js";
export * from "./src/accounts.js";
export * from "./src/actions/handle-action.guild-admin.js";

View File

@@ -1,25 +1,27 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { discordPlugin } from "./src/channel.js";
import { setDiscordRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { discordPlugin } from "./src/channel.js";
export { setDiscordRuntime } from "./src/runtime.js";
type DiscordSubagentHooksModule = typeof import("./src/subagent-hooks.js");
type DiscordSubagentHooksModule = typeof import("./api.js");
let discordSubagentHooksPromise: Promise<DiscordSubagentHooksModule> | null = null;
function loadDiscordSubagentHooksModule() {
discordSubagentHooksPromise ??= import("./src/subagent-hooks.js");
discordSubagentHooksPromise ??= import("./api.js");
return discordSubagentHooksPromise;
}
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "discord",
name: "Discord",
description: "Discord channel plugin",
plugin: discordPlugin,
setRuntime: setDiscordRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "discordPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setDiscordRuntime",
},
registerFull(api) {
api.on("subagent_spawning", async (event) => {
const { handleDiscordSubagentSpawning } = await loadDiscordSubagentHooksModule();

View File

@@ -17,3 +17,4 @@ export * from "./src/resolve-users.js";
export * from "./src/outbound-session-route.js";
export * from "./src/send.js";
export * from "./src/send.components.js";
export { setDiscordRuntime } from "./src/runtime.js";

View File

@@ -1,6 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { discordSetupPlugin } from "./src/channel.setup.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { discordSetupPlugin } from "./src/channel.setup.js";
export default defineSetupPluginEntry(discordSetupPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "discordSetupPlugin",
},
});

View File

@@ -1,4 +1,15 @@
export { feishuPlugin } from "./src/channel.js";
export { registerFeishuDocTools } from "./src/docx.js";
export { registerFeishuChatTools } from "./src/chat.js";
export { registerFeishuWikiTools } from "./src/wiki.js";
export { registerFeishuDriveTools } from "./src/drive.js";
export { registerFeishuPermTools } from "./src/perm.js";
export { registerFeishuBitableTools } from "./src/bitable.js";
export {
handleFeishuSubagentDeliveryTarget,
handleFeishuSubagentEnded,
handleFeishuSubagentSpawning,
} from "./src/subagent-hooks.js";
export * from "./src/conversation-id.js";
export * from "./src/setup-core.js";
export * from "./src/setup-surface.js";

View File

@@ -1,14 +1,16 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { feishuPlugin } from "./src/channel.js";
import { setFeishuRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { feishuPlugin } from "./src/channel.js";
export { setFeishuRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "feishu",
name: "Feishu",
description: "Feishu/Lark channel plugin",
plugin: feishuPlugin,
setRuntime: setFeishuRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "feishuPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setFeishuRuntime",
},
});

View File

@@ -1,78 +1,79 @@
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";
import { registerFeishuDocTools } from "./src/docx.js";
import { registerFeishuDriveTools } from "./src/drive.js";
import { registerFeishuPermTools } from "./src/perm.js";
import { setFeishuRuntime } from "./src/runtime.js";
import { registerFeishuWikiTools } from "./src/wiki.js";
import {
defineBundledChannelEntry,
loadBundledEntryExportSync,
} from "openclaw/plugin-sdk/channel-entry-contract";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/channel-entry-contract";
export { feishuPlugin } from "./src/channel.js";
export { setFeishuRuntime } from "./src/runtime.js";
export {
sendMessageFeishu,
sendCardFeishu,
updateCardFeishu,
editMessageFeishu,
getMessageFeishu,
} from "./src/send.js";
export {
uploadImageFeishu,
uploadFileFeishu,
sendImageFeishu,
sendFileFeishu,
sendMediaFeishu,
} from "./src/media.js";
export { probeFeishu } from "./src/probe.js";
export {
addReactionFeishu,
removeReactionFeishu,
listReactionsFeishu,
FeishuEmoji,
} from "./src/reactions.js";
export {
extractMentionTargets,
extractMessageBody,
isMentionForwardRequest,
formatMentionForText,
formatMentionForCard,
formatMentionAllForText,
formatMentionAllForCard,
buildMentionedMessage,
buildMentionedCardContent,
type MentionTarget,
} from "./src/mention.js";
type FeishuSubagentHooksModule = typeof import("./api.js");
type MonitorFeishuProvider = typeof import("./src/monitor.js").monitorFeishuProvider;
type FeishuSubagentHooksModule = typeof import("./src/subagent-hooks.js");
let feishuMonitorPromise: Promise<typeof import("./src/monitor.js")> | null = null;
let feishuSubagentHooksPromise: Promise<FeishuSubagentHooksModule> | null = null;
function loadFeishuMonitorModule() {
feishuMonitorPromise ??= import("./src/monitor.js");
return feishuMonitorPromise;
}
function loadFeishuSubagentHooksModule() {
feishuSubagentHooksPromise ??= import("./src/subagent-hooks.js");
feishuSubagentHooksPromise ??= import("./api.js");
return feishuSubagentHooksPromise;
}
export async function monitorFeishuProvider(
...args: Parameters<MonitorFeishuProvider>
): ReturnType<MonitorFeishuProvider> {
const { monitorFeishuProvider } = await loadFeishuMonitorModule();
return await monitorFeishuProvider(...args);
function registerFeishuDocTools(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerFeishuDocTools",
});
register(api);
}
export default defineChannelPluginEntry({
function registerFeishuChatTools(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerFeishuChatTools",
});
register(api);
}
function registerFeishuWikiTools(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerFeishuWikiTools",
});
register(api);
}
function registerFeishuDriveTools(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerFeishuDriveTools",
});
register(api);
}
function registerFeishuPermTools(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerFeishuPermTools",
});
register(api);
}
function registerFeishuBitableTools(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerFeishuBitableTools",
});
register(api);
}
export default defineBundledChannelEntry({
id: "feishu",
name: "Feishu",
description: "Feishu/Lark channel plugin",
plugin: feishuPlugin,
setRuntime: setFeishuRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "feishuPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setFeishuRuntime",
},
registerFull(api) {
api.on("subagent_spawning", async (event, ctx) => {
const { handleFeishuSubagentSpawning } = await loadFeishuSubagentHooksModule();

View File

@@ -49,3 +49,4 @@ export {
readRequestBodyWithLimit,
requestBodyErrorToText,
} from "openclaw/plugin-sdk/webhook-ingress";
export { setFeishuRuntime } from "./src/runtime.js";

View File

@@ -1,4 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { feishuPlugin } from "./src/channel.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export default defineSetupPluginEntry(feishuPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "feishuPlugin",
},
});

View File

@@ -1,2 +1,3 @@
export { googlechatPlugin } from "./src/channel.js";
export * from "./src/setup-core.js";
export * from "./src/setup-surface.js";

View File

@@ -1,15 +1,16 @@
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";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { googlechatPlugin } from "./src/channel.js";
export { setGoogleChatRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "googlechat",
name: "Google Chat",
description: "OpenClaw Google Chat channel plugin",
plugin: googlechatPlugin as ChannelPlugin,
setRuntime: setGoogleChatRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "googlechatPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setGoogleChatRuntime",
},
});

View File

@@ -60,3 +60,4 @@ export {
readJsonWebhookBodyOrReject,
type WebhookInFlightLimiter,
} from "openclaw/plugin-sdk/webhook-request-guards";
export { setGoogleChatRuntime } from "./src/runtime.js";

View File

@@ -1,4 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { googlechatPlugin } from "./src/channel.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export default defineSetupPluginEntry(googlechatPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "googlechatPlugin",
},
});

View File

@@ -1,4 +1,5 @@
export { imessagePlugin } from "./src/channel.js";
export { imessageSetupPlugin } from "./src/channel.setup.js";
export * from "./src/accounts.js";
export * from "./src/conversation-bindings.js";
export * from "./src/conversation-id.js";

View File

@@ -1,14 +1,16 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { imessagePlugin } from "./src/channel.js";
import { setIMessageRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { imessagePlugin } from "./src/channel.js";
export { setIMessageRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "imessage",
name: "iMessage",
description: "iMessage channel plugin",
plugin: imessagePlugin,
setRuntime: setIMessageRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "imessagePlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setIMessageRuntime",
},
});

View File

@@ -29,6 +29,7 @@ export type { MonitorIMessageOpts } from "./src/monitor.js";
export { probeIMessage } from "./src/probe.js";
export type { IMessageProbe } from "./src/probe.js";
export { sendMessageIMessage } from "./src/send.js";
export { setIMessageRuntime } from "./src/runtime.js";
export { chunkTextForOutbound } from "./src/channel-api.js";
export type IMessageAccountConfig = Omit<

View File

@@ -1,6 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { imessageSetupPlugin } from "./src/channel.setup.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { imessageSetupPlugin } from "./src/channel.setup.js";
export default defineSetupPluginEntry(imessageSetupPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "imessageSetupPlugin",
},
});

View File

@@ -1,2 +1,4 @@
export { ircPlugin } from "./src/channel.js";
export { setIrcRuntime } from "./src/runtime.js";
export * from "./src/accounts.js";
export * from "./src/setup-surface.js";

View File

@@ -1,15 +1,16 @@
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";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { ircPlugin } from "./src/channel.js";
export { setIrcRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "irc",
name: "IRC",
description: "IRC channel plugin",
plugin: ircPlugin as ChannelPlugin,
setRuntime: setIrcRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "ircPlugin",
},
runtime: {
specifier: "./api.js",
exportName: "setIrcRuntime",
},
});

View File

@@ -1,4 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { ircPlugin } from "./src/channel.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export default defineSetupPluginEntry(ircPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "ircPlugin",
},
});

View File

@@ -40,5 +40,8 @@ export {
setSetupChannelEnabled,
splitSetupEntries,
} from "./runtime-api.js";
export { linePlugin } from "./src/channel.js";
export { lineSetupPlugin } from "./src/channel.setup.js";
export { registerLineCardCommand } from "./src/card-command.js";
export * from "./runtime-api.js";
export * from "./setup-api.js";

View File

@@ -1,9 +1,7 @@
import { defineChannelPluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/channel-core";
import { linePlugin } from "./src/channel.js";
import { setLineRuntime } from "./src/runtime.js";
export { linePlugin } from "./src/channel.js";
export { setLineRuntime } from "./src/runtime.js";
import {
defineBundledChannelEntry,
type OpenClawPluginApi,
} from "openclaw/plugin-sdk/channel-entry-contract";
type RegisteredLineCardCommand = Parameters<OpenClawPluginApi["registerCommand"]>[0];
@@ -12,7 +10,7 @@ let lineCardCommandPromise: Promise<RegisteredLineCardCommand> | null = null;
async function loadLineCardCommand(api: OpenClawPluginApi): Promise<RegisteredLineCardCommand> {
lineCardCommandPromise ??= (async () => {
let registered: RegisteredLineCardCommand | null = null;
const { registerLineCardCommand } = await import("./src/card-command.js");
const { registerLineCardCommand } = await import("./api.js");
registerLineCardCommand({
...api,
registerCommand(command: RegisteredLineCardCommand) {
@@ -27,12 +25,19 @@ async function loadLineCardCommand(api: OpenClawPluginApi): Promise<RegisteredLi
return await lineCardCommandPromise;
}
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "line",
name: "LINE",
description: "LINE Messaging API channel plugin",
plugin: linePlugin,
setRuntime: setLineRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "linePlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setLineRuntime",
},
registerFull(api) {
api.registerCommand({
name: "card",

View File

@@ -26,6 +26,7 @@ export {
setSetupChannelEnabled,
splitSetupEntries,
} from "openclaw/plugin-sdk/setup";
export { setLineRuntime } from "./src/runtime.js";
// Keep named exports explicit here so the runtime barrel stays self-contained
// and plugin-sdk can re-export this file directly without reaching into
// extension internals.

View File

@@ -1,6 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { lineSetupPlugin } from "./src/channel.setup.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { lineSetupPlugin } from "./src/channel.setup.js";
export default defineSetupPluginEntry(lineSetupPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "lineSetupPlugin",
},
});

View File

@@ -1,3 +1,4 @@
export { matrixPlugin } from "./src/channel.js";
export * from "./src/setup-core.js";
export * from "./src/setup-surface.js";
export * from "./src/account-selection.js";

View File

@@ -1,20 +1,33 @@
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";
import {
defineBundledChannelEntry,
loadBundledEntryExportSync,
} from "openclaw/plugin-sdk/channel-entry-contract";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/channel-entry-contract";
export { matrixPlugin } from "./src/channel.js";
export { setMatrixRuntime } from "./src/runtime.js";
function registerMatrixCliMetadata(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./cli-metadata.js",
exportName: "registerMatrixCliMetadata",
});
register(api);
}
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "matrix",
name: "Matrix",
description: "Matrix channel plugin (matrix-js-sdk)",
plugin: matrixPlugin,
setRuntime: setMatrixRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "matrixPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setMatrixRuntime",
},
registerCliMetadata: registerMatrixCliMetadata,
registerFull(api) {
void import("./src/plugin-entry.runtime.js")
void import("./plugin-entry.handlers.runtime.js")
.then(({ ensureMatrixCryptoRuntime }) =>
ensureMatrixCryptoRuntime({ log: api.logger.info }).catch((err: unknown) => {
const message = err instanceof Error ? err.message : String(err);
@@ -27,17 +40,17 @@ export default defineChannelPluginEntry({
});
api.registerGatewayMethod("matrix.verify.recoveryKey", async (ctx) => {
const { handleVerifyRecoveryKey } = await import("./src/plugin-entry.runtime.js");
const { handleVerifyRecoveryKey } = await import("./plugin-entry.handlers.runtime.js");
await handleVerifyRecoveryKey(ctx);
});
api.registerGatewayMethod("matrix.verify.bootstrap", async (ctx) => {
const { handleVerificationBootstrap } = await import("./src/plugin-entry.runtime.js");
const { handleVerificationBootstrap } = await import("./plugin-entry.handlers.runtime.js");
await handleVerificationBootstrap(ctx);
});
api.registerGatewayMethod("matrix.verify.status", async (ctx) => {
const { handleVerificationStatus } = await import("./src/plugin-entry.runtime.js");
const { handleVerificationStatus } = await import("./plugin-entry.handlers.runtime.js");
await handleVerificationStatus(ctx);
});
},

View File

@@ -1,4 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { matrixPlugin } from "./src/channel.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export default defineSetupPluginEntry(matrixPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "matrixPlugin",
},
});

View File

@@ -1,17 +1,30 @@
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";
import {
defineBundledChannelEntry,
loadBundledEntryExportSync,
} from "openclaw/plugin-sdk/channel-entry-contract";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/channel-entry-contract";
export { mattermostPlugin } from "./src/channel.js";
export { setMattermostRuntime } from "./src/runtime.js";
function registerSlashCommandRoute(api: OpenClawPluginApi): void {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./runtime-api.js",
exportName: "registerSlashCommandRoute",
});
register(api);
}
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "mattermost",
name: "Mattermost",
description: "Mattermost channel plugin",
plugin: mattermostPlugin,
setRuntime: setMattermostRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./runtime-api.js",
exportName: "mattermostPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setMattermostRuntime",
},
registerFull(api) {
// Actual slash-command registration happens after the monitor connects and
// knows the team id; the route itself can be wired here.

View File

@@ -57,6 +57,7 @@ export {
resolveDmGroupAccessWithLists,
resolveEffectiveAllowFromLists,
} from "openclaw/plugin-sdk/channel-policy";
export { mattermostPlugin } from "./src/channel.js";
export { evaluateSenderGroupAccessForPolicy } from "openclaw/plugin-sdk/group-access";
export { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";
export { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
@@ -86,3 +87,5 @@ export {
resolveChannelMediaMaxBytes,
} from "openclaw/plugin-sdk/media-runtime";
export { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
export { registerSlashCommandRoute } from "./src/mattermost/slash-state.js";
export { setMattermostRuntime } from "./src/runtime.js";

View File

@@ -1,4 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { mattermostPlugin } from "./src/channel.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export default defineSetupPluginEntry(mattermostPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./runtime-api.js",
exportName: "mattermostPlugin",
},
});

View File

@@ -1,2 +1,3 @@
export { msteamsPlugin } from "./src/channel.js";
export * from "./src/setup-core.js";
export * from "./src/setup-surface.js";

View File

@@ -1,14 +1,16 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { msteamsPlugin } from "./src/channel.js";
import { setMSTeamsRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { msteamsPlugin } from "./src/channel.js";
export { setMSTeamsRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "msteams",
name: "Microsoft Teams",
description: "Microsoft Teams channel plugin (Bot Framework)",
plugin: msteamsPlugin,
setRuntime: setMSTeamsRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "msteamsPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setMSTeamsRuntime",
},
});

View File

@@ -2,3 +2,4 @@
// Keep this barrel thin and aligned with the local extension surface.
export * from "openclaw/plugin-sdk/msteams";
export { setMSTeamsRuntime } from "./src/runtime.js";

View File

@@ -1,4 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { msteamsPlugin } from "./src/channel.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export default defineSetupPluginEntry(msteamsPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "msteamsPlugin",
},
});

View File

@@ -1,14 +1,16 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { nextcloudTalkPlugin } from "./src/channel.js";
import { setNextcloudTalkRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { nextcloudTalkPlugin } from "./src/channel.js";
export { setNextcloudTalkRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "nextcloud-talk",
name: "Nextcloud Talk",
description: "Nextcloud Talk channel plugin",
plugin: nextcloudTalkPlugin,
setRuntime: setNextcloudTalkRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "nextcloudTalkPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setNextcloudTalkRuntime",
},
});

View File

@@ -2,3 +2,4 @@
// Keep this barrel thin and aligned with the local extension surface.
export * from "openclaw/plugin-sdk/nextcloud-talk";
export { setNextcloudTalkRuntime } from "./src/runtime.js";

View File

@@ -1,4 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { nextcloudTalkPlugin } from "./src/channel.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export default defineSetupPluginEntry(nextcloudTalkPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "nextcloudTalkPlugin",
},
});

View File

@@ -1 +1,5 @@
export * from "./runtime-api.js";
export { nostrPlugin } from "./src/channel.js";
export { createNostrProfileHttpHandler } from "./src/nostr-profile-http.js";
export { getNostrRuntime, setNostrRuntime } from "./src/runtime.js";
export { resolveNostrAccount } from "./src/types.js";

View File

@@ -1,28 +1,56 @@
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";
import { getNostrRuntime, setNostrRuntime } from "./src/runtime.js";
import { resolveNostrAccount } from "./src/types.js";
import {
defineBundledChannelEntry,
loadBundledEntryExportSync,
} from "openclaw/plugin-sdk/channel-entry-contract";
export { nostrPlugin } from "./src/channel.js";
export { setNostrRuntime } from "./src/runtime.js";
function createNostrProfileHttpHandler() {
return loadBundledEntryExportSync<
(params: Record<string, unknown>) => (ctx: unknown) => Promise<void> | void
>(import.meta.url, {
specifier: "./api.js",
exportName: "createNostrProfileHttpHandler",
});
}
export default defineChannelPluginEntry({
function getNostrRuntime() {
return loadBundledEntryExportSync<() => any>(import.meta.url, {
specifier: "./api.js",
exportName: "getNostrRuntime",
})();
}
function resolveNostrAccount(params: { cfg: unknown; accountId: string }) {
return loadBundledEntryExportSync<(params: { cfg: unknown; accountId: string }) => any>(
import.meta.url,
{
specifier: "./api.js",
exportName: "resolveNostrAccount",
},
)(params);
}
export default defineBundledChannelEntry({
id: "nostr",
name: "Nostr",
description: "Nostr DM channel plugin via NIP-04",
plugin: nostrPlugin,
setRuntime: setNostrRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "nostrPlugin",
},
runtime: {
specifier: "./api.js",
exportName: "setNostrRuntime",
},
registerFull(api) {
const httpHandler = createNostrProfileHttpHandler({
const httpHandler = createNostrProfileHttpHandler()({
getConfigProfile: (accountId: string) => {
const runtime = getNostrRuntime();
const cfg = runtime.config.loadConfig();
const account = resolveNostrAccount({ cfg, accountId });
return account.profile;
},
updateConfigProfile: async (accountId: string, profile: NostrProfile) => {
updateConfigProfile: async (accountId: string, profile: unknown) => {
const runtime = getNostrRuntime();
const cfg = runtime.config.loadConfig();

View File

@@ -1,4 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { nostrPlugin } from "./src/channel.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export default defineSetupPluginEntry(nostrPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "nostrPlugin",
},
});

View File

@@ -1,3 +1,8 @@
export { qqbotPlugin } from "./src/channel.js";
export { qqbotSetupPlugin } from "./src/channel.setup.js";
export { getFrameworkCommands } from "./src/slash-commands.js";
export { registerChannelTool } from "./src/tools/channel.js";
export { registerRemindTool } from "./src/tools/remind.js";
export * from "./src/types.js";
export * from "./src/config.js";
export * from "./src/outbound.js";

View File

@@ -1,26 +1,96 @@
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";
import { setQQBotRuntime } from "./src/runtime.js";
import { getFrameworkCommands } from "./src/slash-commands.js";
import { registerChannelTool } from "./src/tools/channel.js";
import { registerRemindTool } from "./src/tools/remind.js";
import {
defineBundledChannelEntry,
loadBundledEntryExportSync,
type OpenClawPluginApi,
type PluginCommandContext,
} from "openclaw/plugin-sdk/channel-entry-contract";
export { qqbotPlugin } from "./src/channel.js";
export { setQQBotRuntime, getQQBotRuntime } from "./src/runtime.js";
type QQBotAccount = {
accountId: string;
appId: string;
config: unknown;
};
export default defineChannelPluginEntry({
type MediaTargetContext = {
targetType: "c2c" | "group" | "channel" | "dm";
targetId: string;
account: QQBotAccount;
logPrefix: string;
};
type QQBotFrameworkCommandResult =
| string
| {
text: string;
filePath?: string;
}
| null
| undefined;
type QQBotFrameworkCommand = {
name: string;
description: string;
handler: (ctx: Record<string, unknown>) => Promise<QQBotFrameworkCommandResult>;
};
function resolveQQBotAccount(config: unknown, accountId?: string): QQBotAccount {
const resolve = loadBundledEntryExportSync<(config: unknown, accountId?: string) => QQBotAccount>(
import.meta.url,
{
specifier: "./api.js",
exportName: "resolveQQBotAccount",
},
);
return resolve(config, accountId);
}
function sendDocument(context: MediaTargetContext, filePath: string) {
const send = loadBundledEntryExportSync<
(context: MediaTargetContext, filePath: string) => Promise<unknown>
>(import.meta.url, {
specifier: "./api.js",
exportName: "sendDocument",
});
return send(context, filePath);
}
function getFrameworkCommands(): QQBotFrameworkCommand[] {
const getCommands = loadBundledEntryExportSync<() => QQBotFrameworkCommand[]>(import.meta.url, {
specifier: "./api.js",
exportName: "getFrameworkCommands",
});
return getCommands();
}
function registerChannelTool(api: OpenClawPluginApi): void {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerChannelTool",
});
register(api);
}
function registerRemindTool(api: OpenClawPluginApi): void {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerRemindTool",
});
register(api);
}
export default defineBundledChannelEntry({
id: "qqbot",
name: "QQ Bot",
description: "QQ Bot channel plugin",
plugin: qqbotPlugin as ChannelPlugin,
setRuntime: setQQBotRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "qqbotPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setQQBotRuntime",
},
registerFull(api: OpenClawPluginApi) {
registerChannelTool(api);
registerRemindTool(api);
@@ -95,7 +165,7 @@ export default defineChannelPluginEntry({
}
// File result: send the file attachment via QQ API, return text summary.
if (result && "filePath" in result) {
if (result && typeof result === "object" && "filePath" in result) {
try {
const mediaCtx: MediaTargetContext = {
targetType,
@@ -103,14 +173,22 @@ export default defineChannelPluginEntry({
account,
logPrefix: `[qqbot:${account.accountId}]`,
};
await sendDocument(mediaCtx, result.filePath);
await sendDocument(mediaCtx, String(result.filePath));
} catch {
// File send failed; the text summary is still returned below.
}
return { text: result.text };
return { text: String(result.text) };
}
return { text: "⚠️ 命令返回了意外结果。" };
return {
text:
result &&
typeof result === "object" &&
"text" in result &&
typeof result.text === "string"
? result.text
: "⚠️ 命令返回了意外结果。",
};
},
});
}

View File

@@ -1,6 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { qqbotSetupPlugin } from "./src/channel.setup.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { qqbotSetupPlugin } from "./src/channel.setup.js";
export default defineSetupPluginEntry(qqbotSetupPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "qqbotSetupPlugin",
},
});

View File

@@ -1,3 +1,5 @@
export { signalPlugin } from "./src/channel.js";
export { signalSetupPlugin } from "./src/channel.setup.js";
export * from "./src/accounts.js";
export * from "./src/format.js";
export * from "./src/identity.js";

View File

@@ -1,14 +1,16 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { signalPlugin } from "./src/channel.js";
import { setSignalRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { signalPlugin } from "./src/channel.js";
export { setSignalRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "signal",
name: "Signal",
description: "Signal channel plugin",
plugin: signalPlugin,
setRuntime: setSignalRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "signalPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setSignalRuntime",
},
});

View File

@@ -1,14 +1,16 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { signalPlugin } from "./src/channel.js";
import { setSignalRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { signalPlugin } from "./src/channel.js";
export { setSignalRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "signal",
name: "Signal",
description: "Signal channel plugin",
plugin: signalPlugin,
setRuntime: setSignalRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "signalPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setSignalRuntime",
},
});

View File

@@ -1 +1,2 @@
export * from "./src/runtime-api.js";
export { setSignalRuntime } from "./src/runtime.js";

View File

@@ -1,6 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { signalSetupPlugin } from "./src/channel.setup.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { signalSetupPlugin } from "./src/channel.setup.js";
export default defineSetupPluginEntry(signalSetupPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "signalSetupPlugin",
},
});

View File

@@ -1,3 +1,5 @@
export { slackPlugin } from "./src/channel.js";
export { slackSetupPlugin } from "./src/channel.setup.js";
export * from "./src/account-inspect.js";
export * from "./src/accounts.js";
export * from "./src/action-threading.js";

View File

@@ -1,14 +1,16 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { slackPlugin } from "./src/channel.js";
import { setSlackRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { slackPlugin } from "./src/channel.js";
export { setSlackRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "slack",
name: "Slack",
description: "Slack channel plugin",
plugin: slackPlugin,
setRuntime: setSlackRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "slackPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setSlackRuntime",
},
});

View File

@@ -1,16 +1,29 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { slackPlugin } from "./src/channel.js";
import { registerSlackPluginHttpRoutes } from "./src/http/plugin-routes.js";
import { setSlackRuntime } from "./src/runtime.js";
import {
defineBundledChannelEntry,
loadBundledEntryExportSync,
} from "openclaw/plugin-sdk/channel-entry-contract";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/channel-entry-contract";
export { slackPlugin } from "./src/channel.js";
export { setSlackRuntime } from "./src/runtime.js";
function registerSlackPluginHttpRoutes(api: OpenClawPluginApi): void {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./runtime-api.js",
exportName: "registerSlackPluginHttpRoutes",
});
register(api);
}
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "slack",
name: "Slack",
description: "Slack channel plugin",
plugin: slackPlugin,
setRuntime: setSlackRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "slackPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setSlackRuntime",
},
registerFull: registerSlackPluginHttpRoutes,
});

View File

@@ -3,3 +3,5 @@ export * from "./src/directory-live.js";
export * from "./src/index.js";
export * from "./src/resolve-channels.js";
export * from "./src/resolve-users.js";
export { registerSlackPluginHttpRoutes } from "./src/http/plugin-routes.js";
export { setSlackRuntime } from "./src/runtime.js";

View File

@@ -1,6 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { slackSetupPlugin } from "./src/channel.setup.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { slackSetupPlugin } from "./src/channel.setup.js";
export default defineSetupPluginEntry(slackSetupPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "slackSetupPlugin",
},
});

View File

@@ -1 +1,3 @@
export { synologyChatPlugin } from "./src/channel.js";
export { setSynologyRuntime } from "./src/runtime.js";
export * from "./src/security-audit.js";

View File

@@ -1,14 +1,16 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { synologyChatPlugin } from "./src/channel.js";
import { setSynologyRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { synologyChatPlugin } from "./src/channel.js";
export { setSynologyRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "synology-chat",
name: "Synology Chat",
description: "Native Synology Chat channel plugin for OpenClaw",
plugin: synologyChatPlugin,
setRuntime: setSynologyRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "synologyChatPlugin",
},
runtime: {
specifier: "./api.js",
exportName: "setSynologyRuntime",
},
});

View File

@@ -1,4 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { synologyChatPlugin } from "./src/channel.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export default defineSetupPluginEntry(synologyChatPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "synologyChatPlugin",
},
});

View File

@@ -1,3 +1,5 @@
export { telegramPlugin } from "./src/channel.js";
export { telegramSetupPlugin } from "./src/channel.setup.js";
export * from "./src/account-inspect.js";
export * from "./src/accounts.js";
export * from "./src/action-threading.js";

View File

@@ -1,15 +1,16 @@
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";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { telegramPlugin } from "./src/channel.js";
export { setTelegramRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "telegram",
name: "Telegram",
description: "Telegram channel plugin",
plugin: telegramPlugin as ChannelPlugin,
setRuntime: setTelegramRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "telegramPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setTelegramRuntime",
},
});

View File

@@ -83,6 +83,7 @@ export {
setTelegramThreadBindingMaxAgeBySessionKey,
} from "./src/thread-bindings.js";
export { resolveTelegramToken } from "./src/token.js";
export { setTelegramRuntime } from "./src/runtime.js";
export type { ChannelPlugin } from "openclaw/plugin-sdk/core";
export type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
export type TelegramAccountConfig = NonNullable<

View File

@@ -1,6 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { telegramSetupPlugin } from "./src/channel.setup.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { telegramSetupPlugin } from "./src/channel.setup.js";
export default defineSetupPluginEntry(telegramSetupPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "telegramSetupPlugin",
},
});

View File

@@ -1 +1,3 @@
export * from "./runtime-api.js";
export { tlonPlugin } from "./src/channel.js";
export { setTlonRuntime } from "./src/runtime.js";

View File

@@ -2,12 +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/channel-core";
import { tlonPlugin } from "./src/channel.js";
import { setTlonRuntime } from "./src/runtime.js";
export { tlonPlugin } from "./src/channel.js";
export { setTlonRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -117,12 +112,19 @@ function runTlonCommand(binary: string, args: string[]): Promise<string> {
});
}
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "tlon",
name: "Tlon",
description: "Tlon/Urbit channel plugin",
plugin: tlonPlugin,
setRuntime: setTlonRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "tlonPlugin",
},
runtime: {
specifier: "./api.js",
exportName: "setTlonRuntime",
},
registerFull(api) {
api.logger.debug?.("[tlon] Registering tlon tool");
api.registerTool({
@@ -164,9 +166,14 @@ export default defineChannelPluginEntry({
content: [{ type: "text" as const, text: output }],
details: undefined,
};
} catch (error: any) {
} catch (error: unknown) {
return {
content: [{ type: "text" as const, text: `Error: ${error.message}` }],
content: [
{
type: "text" as const,
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
details: { error: true },
};
}

View File

@@ -1,4 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { tlonPlugin } from "./src/channel.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export default defineSetupPluginEntry(tlonPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "tlonPlugin",
},
});

View File

@@ -1 +1,3 @@
export * from "./runtime-api.js";
export { twitchPlugin } from "./src/plugin.js";
export { setTwitchRuntime } from "./src/runtime.js";

View File

@@ -1,13 +1,16 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { twitchPlugin } from "./src/plugin.js";
import { setTwitchRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { monitorTwitchProvider } from "./src/monitor.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "twitch",
name: "Twitch",
description: "Twitch chat channel plugin",
plugin: twitchPlugin,
setRuntime: setTwitchRuntime,
description: "Twitch IRC chat channel plugin",
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "twitchPlugin",
},
runtime: {
specifier: "./api.js",
exportName: "setTwitchRuntime",
},
});

View File

@@ -1,3 +1,5 @@
export { whatsappPlugin } from "./src/channel.js";
export { whatsappSetupPlugin } from "./src/channel.setup.js";
export * from "./src/accounts.js";
export * from "./src/auto-reply/constants.js";
export { whatsappCommandPolicy } from "./src/command-policy.js";

View File

@@ -1,14 +1,16 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { whatsappPlugin } from "./src/channel.js";
import { setWhatsAppRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { whatsappPlugin } from "./src/channel.js";
export { setWhatsAppRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "whatsapp",
name: "WhatsApp",
description: "WhatsApp channel plugin",
plugin: whatsappPlugin,
setRuntime: setWhatsAppRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "whatsappPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setWhatsAppRuntime",
},
});

View File

@@ -8,6 +8,7 @@ export * from "./src/login.js";
export * from "./src/media.js";
export * from "./src/send.js";
export * from "./src/session.js";
export { setWhatsAppRuntime } from "./src/runtime.js";
type StartWebLoginWithQr = typeof import("./src/login-qr.js").startWebLoginWithQr;
type WaitForWebLogin = typeof import("./src/login-qr.js").waitForWebLogin;

View File

@@ -1,6 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { whatsappSetupPlugin } from "./src/channel.setup.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { whatsappSetupPlugin } from "./src/channel.setup.js";
export default defineSetupPluginEntry(whatsappSetupPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "whatsappSetupPlugin",
},
});

View File

@@ -1,3 +1 @@
export * from "./src/setup-core.js";
export * from "./src/setup-surface.js";
export { evaluateZaloGroupAccess, resolveZaloRuntimeGroupPolicy } from "./src/group-access.js";
export * from "./setup-api.js";

View File

@@ -1,14 +1,16 @@
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { zaloPlugin } from "./src/channel.js";
import { setZaloRuntime } from "./src/runtime.js";
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { zaloPlugin } from "./src/channel.js";
export { setZaloRuntime } from "./src/runtime.js";
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "zalo",
name: "Zalo",
description: "Zalo channel plugin",
plugin: zaloPlugin,
setRuntime: setZaloRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./runtime-api.js",
exportName: "zaloPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setZaloRuntime",
},
});

View File

@@ -1,6 +1,7 @@
// Private runtime barrel for the bundled Zalo extension.
// Keep this barrel thin and aligned with the local extension surface.
export { zaloPlugin } from "./src/channel.js";
export * from "./api.js";
export type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
export type { OpenClawConfig, GroupPolicy } from "openclaw/plugin-sdk/config-runtime";
@@ -92,3 +93,4 @@ export type {
RegisterWebhookPluginRouteOptions,
RegisterWebhookTargetOptions,
} from "openclaw/plugin-sdk/webhook-ingress";
export { setZaloRuntime } from "./src/runtime.js";

View File

@@ -0,0 +1,34 @@
import { loadBundledEntryExportSync } from "openclaw/plugin-sdk/channel-entry-contract";
type SetupSurfaceModule = typeof import("./src/setup-surface.js");
function createLazyObjectValue<T extends object>(load: () => T): T {
return new Proxy({} as T, {
get(_target, property, receiver) {
return Reflect.get(load(), property, receiver);
},
has(_target, property) {
return property in load();
},
ownKeys() {
return Reflect.ownKeys(load());
},
getOwnPropertyDescriptor(_target, property) {
const descriptor = Object.getOwnPropertyDescriptor(load(), property);
return descriptor ? { ...descriptor, configurable: true } : undefined;
},
});
}
function loadSetupSurfaceModule(): SetupSurfaceModule {
return loadBundledEntryExportSync<SetupSurfaceModule>(import.meta.url, {
specifier: "./src/setup-surface.js",
});
}
export { zaloDmPolicy, zaloSetupAdapter, createZaloSetupWizardProxy } from "./src/setup-core.js";
export { evaluateZaloGroupAccess, resolveZaloRuntimeGroupPolicy } from "./src/group-access.js";
export const zaloSetupWizard: SetupSurfaceModule["zaloSetupWizard"] = createLazyObjectValue(
() => loadSetupSurfaceModule().zaloSetupWizard as object,
) as SetupSurfaceModule["zaloSetupWizard"];

View File

@@ -1,4 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { zaloPlugin } from "./src/channel.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export default defineSetupPluginEntry(zaloPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./runtime-api.js",
exportName: "zaloPlugin",
},
});

View File

@@ -1,3 +1,6 @@
export { zalouserPlugin } from "./src/channel.js";
export { zalouserSetupPlugin } from "./src/channel.setup.js";
export { createZalouserTool } from "./src/tool.js";
export * from "./src/setup-core.js";
export * from "./src/setup-surface.js";
export * from "./src/security-audit.js";

View File

@@ -1,17 +1,33 @@
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";
import {
type AnyAgentTool,
defineBundledChannelEntry,
loadBundledEntryExportSync,
} from "openclaw/plugin-sdk/channel-entry-contract";
export { zalouserPlugin } from "./src/channel.js";
export { setZalouserRuntime } from "./src/runtime.js";
function createZalouserTool(context?: unknown): AnyAgentTool {
const createTool = loadBundledEntryExportSync<(context?: unknown) => AnyAgentTool>(
import.meta.url,
{
specifier: "./api.js",
exportName: "createZalouserTool",
},
);
return createTool(context);
}
export default defineChannelPluginEntry({
export default defineBundledChannelEntry({
id: "zalouser",
name: "Zalo Personal",
description: "Zalo personal account messaging via native zca-js integration",
plugin: zalouserPlugin,
setRuntime: setZalouserRuntime,
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "zalouserPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setZalouserRuntime",
},
registerFull(api) {
api.registerTool((ctx) => createZalouserTool(ctx), { name: "zalouser" });
},

View File

@@ -2,6 +2,7 @@
// Keep this barrel thin and aligned with the local extension surface.
export * from "./api.js";
export { setZalouserRuntime } from "./src/runtime.js";
export type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
export type {
BaseProbeResult,

View File

@@ -1,6 +1,9 @@
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
import { zalouserSetupPlugin } from "./src/channel.setup.js";
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export { zalouserSetupPlugin } from "./src/channel.setup.js";
export default defineSetupPluginEntry(zalouserSetupPlugin);
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
exportName: "zalouserSetupPlugin",
},
});

View File

@@ -459,6 +459,10 @@
"types": "./dist/plugin-sdk/channel-core.d.ts",
"default": "./dist/plugin-sdk/channel-core.js"
},
"./plugin-sdk/channel-entry-contract": {
"types": "./dist/plugin-sdk/channel-entry-contract.d.ts",
"default": "./dist/plugin-sdk/channel-entry-contract.js"
},
"./plugin-sdk/channel-contract": {
"types": "./dist/plugin-sdk/channel-contract.d.ts",
"default": "./dist/plugin-sdk/channel-contract.js"

View File

@@ -104,6 +104,7 @@
"channel-actions",
"channel-plugin-common",
"channel-core",
"channel-entry-contract",
"channel-contract",
"channel-feedback",
"channel-inbound",

View File

@@ -34,7 +34,7 @@ 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", () => {
it("keeps channel entrypoints on the dedicated entry-contract SDK surface", () => {
const extensionRoot = path.resolve("extensions");
const offenders: string[] = [];
@@ -43,18 +43,53 @@ describe("bundled channel entry shape guards", () => {
if (!fs.statSync(extensionDir).isDirectory()) {
continue;
}
for (const relativePath of ["index.ts", "setup-entry.ts"]) {
for (const relativePath of ["index.ts", "channel-entry.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");
source.includes("defineBundledChannelEntry") ||
source.includes("defineBundledChannelSetupEntry");
if (!usesEntryHelpers) {
continue;
}
if (source.includes('from "openclaw/plugin-sdk/core"')) {
if (
!source.includes('from "openclaw/plugin-sdk/channel-entry-contract"') ||
source.includes('from "openclaw/plugin-sdk/core"') ||
source.includes('from "openclaw/plugin-sdk/channel-core"')
) {
offenders.push(path.relative(process.cwd(), filePath));
}
}
}
expect(offenders).toEqual([]);
});
it("keeps bundled channel entrypoints free of static src imports", () => {
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", "channel-entry.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("defineBundledChannelEntry") ||
source.includes("defineBundledChannelSetupEntry");
if (!usesEntryHelpers) {
continue;
}
if (/^(?:import|export)\s.+["']\.\/src\//mu.test(source)) {
offenders.push(path.relative(process.cwd(), filePath));
}
}
@@ -199,11 +234,19 @@ describe("bundled channel entry shape guards", () => {
}
return {
default: {
channelPlugin: {
id: "alpha",
meta: {},
capabilities: {},
config: {},
kind: "bundled-channel-entry",
id: "alpha",
name: "Alpha",
description: "Alpha",
configSchema: {},
register() {},
loadChannelPlugin() {
return {
id: "alpha",
meta: {},
capabilities: {},
config: {},
};
},
},
};

View File

@@ -4,6 +4,10 @@ import path from "node:path";
import { createJiti } from "jiti";
import { openBoundaryFileSync } from "../../infra/boundary-file-read.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import type {
BundledChannelEntryContract,
BundledChannelSetupEntryContract,
} from "../../plugin-sdk/channel-entry-contract.js";
import { discoverOpenClawPlugins } from "../../plugins/discovery.js";
import { loadPluginManifestRegistry } from "../../plugins/manifest-registry.js";
import type { PluginRuntime } from "../../plugins/runtime/types.js";
@@ -16,13 +20,8 @@ import type { ChannelId, ChannelPlugin } from "./types.js";
type GeneratedBundledChannelEntry = {
id: string;
entry: {
channelPlugin: ChannelPlugin;
setChannelRuntime?: (runtime: PluginRuntime) => void;
};
setupEntry?: {
plugin: ChannelPlugin;
};
entry: BundledChannelEntryContract;
setupEntry?: BundledChannelSetupEntryContract;
};
type BundledChannelDiscoveryCandidate = {
@@ -44,38 +43,7 @@ const nodeRequire = createRequire(import.meta.url);
function resolveChannelPluginModuleEntry(
moduleExport: unknown,
): GeneratedBundledChannelEntry["entry"] | null {
const resolveNamedFallback = (value: unknown): GeneratedBundledChannelEntry["entry"] | null => {
if (!value || typeof value !== "object") {
return null;
}
const entries = Object.entries(value as Record<string, unknown>).filter(
([key]) => key !== "default",
);
const pluginCandidates = entries.filter(
([key, candidate]) =>
key.endsWith("Plugin") &&
!!candidate &&
typeof candidate === "object" &&
"id" in (candidate as Record<string, unknown>),
);
if (pluginCandidates.length !== 1) {
return null;
}
const runtimeCandidates = entries.filter(
([key, candidate]) =>
key.startsWith("set") && key.endsWith("Runtime") && typeof candidate === "function",
);
return {
channelPlugin: pluginCandidates[0][1] as ChannelPlugin,
...(runtimeCandidates.length === 1
? {
setChannelRuntime: runtimeCandidates[0][1] as (runtime: PluginRuntime) => void,
}
: {}),
};
};
): BundledChannelEntryContract | null {
const resolved =
moduleExport &&
typeof moduleExport === "object" &&
@@ -85,24 +53,25 @@ function resolveChannelPluginModuleEntry(
if (!resolved || typeof resolved !== "object") {
return null;
}
const record = resolved as {
channelPlugin?: unknown;
setChannelRuntime?: unknown;
};
if (!record.channelPlugin || typeof record.channelPlugin !== "object") {
return resolveNamedFallback(resolved) ?? resolveNamedFallback(moduleExport);
const record = resolved as Partial<BundledChannelEntryContract>;
if (record.kind !== "bundled-channel-entry") {
return null;
}
return {
channelPlugin: record.channelPlugin as ChannelPlugin,
...(typeof record.setChannelRuntime === "function"
? { setChannelRuntime: record.setChannelRuntime as (runtime: PluginRuntime) => void }
: {}),
};
if (
typeof record.id !== "string" ||
typeof record.name !== "string" ||
typeof record.description !== "string" ||
typeof record.register !== "function" ||
typeof record.loadChannelPlugin !== "function"
) {
return null;
}
return record as BundledChannelEntryContract;
}
function resolveChannelSetupModuleEntry(
moduleExport: unknown,
): GeneratedBundledChannelEntry["setupEntry"] | null {
): BundledChannelSetupEntryContract | null {
const resolved =
moduleExport &&
typeof moduleExport === "object" &&
@@ -112,15 +81,14 @@ function resolveChannelSetupModuleEntry(
if (!resolved || typeof resolved !== "object") {
return null;
}
const record = resolved as {
plugin?: unknown;
};
if (!record.plugin || typeof record.plugin !== "object") {
const record = resolved as Partial<BundledChannelSetupEntryContract>;
if (record.kind !== "bundled-channel-setup-entry") {
return null;
}
return {
plugin: record.plugin as ChannelPlugin,
};
if (typeof record.loadSetupPlugin !== "function") {
return null;
}
return record as BundledChannelSetupEntryContract;
}
function createModuleLoader() {
@@ -237,7 +205,7 @@ function loadGeneratedBundledChannelEntries(): readonly GeneratedBundledChannelE
);
if (!entry) {
log.warn(
`[channels] bundled channel entry ${manifest.id} missing channelPlugin export from ${sourcePath}; skipping`,
`[channels] bundled channel entry ${manifest.id} missing bundled-channel-entry contract from ${sourcePath}; skipping`,
);
continue;
}
@@ -281,10 +249,7 @@ type BundledChannelState = {
plugins: readonly ChannelPlugin[];
setupPlugins: readonly ChannelPlugin[];
pluginsById: Map<ChannelId, ChannelPlugin>;
runtimeSettersById: Map<
ChannelId,
NonNullable<GeneratedBundledChannelEntry["entry"]["setChannelRuntime"]>
>;
runtimeSettersById: Map<ChannelId, NonNullable<BundledChannelEntryContract["setChannelRuntime"]>>;
};
const EMPTY_BUNDLED_CHANNEL_STATE: BundledChannelState = {
@@ -307,18 +272,18 @@ function getBundledChannelState(): BundledChannelState {
}
bundledChannelStateLoadInProgress = true;
const entries = loadGeneratedBundledChannelEntries();
const plugins = entries.map(({ entry }) => entry.channelPlugin);
const plugins = entries.map(({ entry }) => entry.loadChannelPlugin());
const setupPlugins = entries.flatMap(({ setupEntry }) => {
const plugin = setupEntry?.plugin;
const plugin = setupEntry?.loadSetupPlugin();
return plugin ? [plugin] : [];
});
const runtimeSettersById = new Map<
ChannelId,
NonNullable<GeneratedBundledChannelEntry["entry"]["setChannelRuntime"]>
NonNullable<BundledChannelEntryContract["setChannelRuntime"]>
>();
for (const { entry } of entries) {
if (entry.setChannelRuntime) {
runtimeSettersById.set(entry.channelPlugin.id, entry.setChannelRuntime);
runtimeSettersById.set(entry.id, entry.setChannelRuntime);
}
}

View File

@@ -0,0 +1,227 @@
import fs from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { createJiti } from "jiti";
import { emptyChannelConfigSchema } from "../channels/plugins/config-schema.js";
import type { ChannelConfigSchema, ChannelPlugin } from "../channels/plugins/types.plugin.js";
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
import type { PluginRuntime } from "../plugins/runtime/types.js";
import {
buildPluginLoaderAliasMap,
buildPluginLoaderJitiOptions,
shouldPreferNativeJiti,
} from "../plugins/sdk-alias.js";
import type { AnyAgentTool, OpenClawPluginApi, PluginCommandContext } from "../plugins/types.js";
export type { AnyAgentTool, OpenClawPluginApi, PluginCommandContext };
type ChannelEntryConfigSchema<TPlugin> =
TPlugin extends ChannelPlugin<unknown>
? NonNullable<TPlugin["configSchema"]>
: ChannelConfigSchema;
type BundledEntryModuleRef = {
specifier: string;
exportName?: string;
};
type DefineBundledChannelEntryOptions<TPlugin = ChannelPlugin> = {
id: string;
name: string;
description: string;
importMetaUrl: string;
plugin: BundledEntryModuleRef;
configSchema?: ChannelEntryConfigSchema<TPlugin> | (() => ChannelEntryConfigSchema<TPlugin>);
runtime?: BundledEntryModuleRef;
registerCliMetadata?: (api: OpenClawPluginApi) => void;
registerFull?: (api: OpenClawPluginApi) => void;
};
type DefineBundledChannelSetupEntryOptions = {
importMetaUrl: string;
plugin: BundledEntryModuleRef;
};
export type BundledChannelEntryContract<TPlugin = ChannelPlugin> = {
kind: "bundled-channel-entry";
id: string;
name: string;
description: string;
configSchema: ChannelEntryConfigSchema<TPlugin>;
register: (api: OpenClawPluginApi) => void;
loadChannelPlugin: () => TPlugin;
setChannelRuntime?: (runtime: PluginRuntime) => void;
};
export type BundledChannelSetupEntryContract<TPlugin = ChannelPlugin> = {
kind: "bundled-channel-setup-entry";
loadSetupPlugin: () => TPlugin;
};
const nodeRequire = createRequire(import.meta.url);
const jitiLoaders = new Map<string, ReturnType<typeof createJiti>>();
const loadedModuleExports = new Map<string, unknown>();
function resolveSpecifierCandidates(modulePath: string): string[] {
const ext = path.extname(modulePath).toLowerCase();
if (ext === ".js") {
return [modulePath, modulePath.slice(0, -3) + ".ts"];
}
if (ext === ".mjs") {
return [modulePath, modulePath.slice(0, -4) + ".mts"];
}
if (ext === ".cjs") {
return [modulePath, modulePath.slice(0, -4) + ".cts"];
}
return [modulePath];
}
function resolveEntryBoundaryRoot(importMetaUrl: string): string {
return path.dirname(fileURLToPath(importMetaUrl));
}
function resolveBundledEntryModulePath(importMetaUrl: string, specifier: string): string {
const importerPath = fileURLToPath(importMetaUrl);
const resolved = path.resolve(path.dirname(importerPath), specifier);
const boundaryRoot = resolveEntryBoundaryRoot(importMetaUrl);
const candidate =
resolveSpecifierCandidates(resolved).find((entry) => fs.existsSync(entry)) ?? resolved;
const opened = openBoundaryFileSync({
absolutePath: candidate,
rootPath: boundaryRoot,
boundaryLabel: "plugin root",
rejectHardlinks: false,
skipLexicalRootCheck: true,
});
if (!opened.ok) {
throw new Error(`plugin entry path escapes plugin root: ${specifier}`);
}
fs.closeSync(opened.fd);
return opened.path;
}
function getJiti(modulePath: string) {
const tryNative =
shouldPreferNativeJiti(modulePath) || modulePath.includes(`${path.sep}dist${path.sep}`);
const aliasMap = buildPluginLoaderAliasMap(modulePath, process.argv[1], import.meta.url);
const cacheKey = JSON.stringify({
tryNative,
aliasMap: Object.entries(aliasMap).toSorted(([left], [right]) => left.localeCompare(right)),
});
const cached = jitiLoaders.get(cacheKey);
if (cached) {
return cached;
}
const loader = createJiti(import.meta.url, {
...buildPluginLoaderJitiOptions(aliasMap),
tryNative,
});
jitiLoaders.set(cacheKey, loader);
return loader;
}
function loadBundledEntryModuleSync(importMetaUrl: string, specifier: string): unknown {
const modulePath = resolveBundledEntryModulePath(importMetaUrl, specifier);
const cached = loadedModuleExports.get(modulePath);
if (cached !== undefined) {
return cached;
}
let loaded: unknown;
if (
process.platform === "win32" &&
modulePath.includes(`${path.sep}dist${path.sep}`) &&
[".js", ".mjs", ".cjs"].includes(path.extname(modulePath).toLowerCase())
) {
try {
loaded = nodeRequire(modulePath);
} catch {
loaded = getJiti(modulePath)(modulePath);
}
} else {
loaded = getJiti(modulePath)(modulePath);
}
loadedModuleExports.set(modulePath, loaded);
return loaded;
}
export function loadBundledEntryExportSync<T>(
importMetaUrl: string,
reference: BundledEntryModuleRef,
): T {
const loaded = loadBundledEntryModuleSync(importMetaUrl, reference.specifier);
const resolved =
loaded && typeof loaded === "object" && "default" in (loaded as Record<string, unknown>)
? (loaded as { default: unknown }).default
: loaded;
if (!reference.exportName) {
return resolved as T;
}
const record = (resolved ?? loaded) as Record<string, unknown> | undefined;
if (!record || !(reference.exportName in record)) {
throw new Error(
`missing export "${reference.exportName}" from bundled entry module ${reference.specifier}`,
);
}
return record[reference.exportName] as T;
}
export function defineBundledChannelEntry<TPlugin = ChannelPlugin>({
id,
name,
description,
importMetaUrl,
plugin,
configSchema,
runtime,
registerCliMetadata,
registerFull,
}: DefineBundledChannelEntryOptions<TPlugin>): BundledChannelEntryContract<TPlugin> {
const resolvedConfigSchema: ChannelEntryConfigSchema<TPlugin> =
typeof configSchema === "function"
? configSchema()
: ((configSchema ?? emptyChannelConfigSchema()) as ChannelEntryConfigSchema<TPlugin>);
const loadChannelPlugin = () => loadBundledEntryExportSync<TPlugin>(importMetaUrl, plugin);
const setChannelRuntime = runtime
? (pluginRuntime: PluginRuntime) => {
const setter = loadBundledEntryExportSync<(runtime: PluginRuntime) => void>(
importMetaUrl,
runtime,
);
setter(pluginRuntime);
}
: undefined;
return {
kind: "bundled-channel-entry",
id,
name,
description,
configSchema: resolvedConfigSchema,
register(api: OpenClawPluginApi) {
if (api.registrationMode === "cli-metadata") {
registerCliMetadata?.(api);
return;
}
setChannelRuntime?.(api.runtime);
api.registerChannel({ plugin: loadChannelPlugin() as ChannelPlugin });
if (api.registrationMode !== "full") {
return;
}
registerCliMetadata?.(api);
registerFull?.(api);
},
loadChannelPlugin,
...(setChannelRuntime ? { setChannelRuntime } : {}),
};
}
export function defineBundledChannelSetupEntry<TPlugin = ChannelPlugin>({
importMetaUrl,
plugin,
}: DefineBundledChannelSetupEntryOptions): BundledChannelSetupEntryContract<TPlugin> {
return {
kind: "bundled-channel-setup-entry",
loadSetupPlugin: () => loadBundledEntryExportSync<TPlugin>(importMetaUrl, plugin),
};
}

View File

@@ -1,5 +1,5 @@
// Manual facade. Keep loader boundary explicit.
type FacadeModule = typeof import("@openclaw/zalo/api.js");
type FacadeModule = typeof import("@openclaw/zalo/setup-api.js");
import {
createLazyFacadeObjectValue,
loadBundledPluginPublicSurfaceModuleSync,
@@ -8,7 +8,7 @@ import {
function loadFacadeModule(): FacadeModule {
return loadBundledPluginPublicSurfaceModuleSync<FacadeModule>({
dirName: "zalo",
artifactBasename: "api.js",
artifactBasename: "setup-api.js",
});
}
export const evaluateZaloGroupAccess: FacadeModule["evaluateZaloGroupAccess"] = ((...args) =>