Files
Fabric.OpenclawPlugin/src/channel.ts
hzhang 9221664428 feat: Fabric channel plugin for OpenClaw (Phase 1, B1)
OpenClaw channel plugin: defineChannelPluginEntry + createChatChannelPlugin.
Inbound via channel-turn kernel (wakeup -> admission: true=dispatch,
else drop+recordHistory). One Fabric socket per agent identity in the
plugin runtime (no sidecar). Center API-key agent auth. Tools:
fabric-register, create-{chat,work,report,discussion}-channel,
discussion-complete (post summary + close channel).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 17:13:26 +01:00

64 lines
2.1 KiB
TypeScript

import {
createChatChannelPlugin,
createChannelPluginBase,
} from 'openclaw/plugin-sdk/channel-core';
import type { OpenClawConfig } from 'openclaw/plugin-sdk/channel-core';
export type ResolvedFabricAccount = {
accountId: string | null;
centerApiBase: string;
allowFrom: string[];
dmPolicy: string | undefined;
};
export function resolveFabricAccount(
cfg: OpenClawConfig,
accountId?: string | null,
): ResolvedFabricAccount {
const section = (cfg.channels as Record<string, any>)?.['fabric'];
const centerApiBase: string | undefined = section?.centerApiBase;
if (!centerApiBase) throw new Error('fabric: channels.fabric.centerApiBase is required');
return {
accountId: accountId ?? null,
centerApiBase,
allowFrom: section?.allowFrom ?? [],
dmPolicy: section?.dmSecurity,
};
}
// Outbound is wired by the entry (it needs the identity registry + client to
// post as the right agent). Channel-turn visible replies go through the
// inbound adapter's delivery callback; this object owns config/security only.
export function buildFabricChannelPlugin(
sendText: (params: { accountId?: string | null; to: string; text: string }) => Promise<{ messageId?: string }>,
) {
return createChatChannelPlugin<ResolvedFabricAccount>({
base: createChannelPluginBase({
id: 'fabric',
setup: {
resolveAccount: resolveFabricAccount,
inspectAccount(cfg, accountId) {
const section = (cfg.channels as Record<string, any>)?.['fabric'];
const ok = Boolean(section?.centerApiBase);
return { enabled: ok, configured: ok, tokenStatus: ok ? 'available' : 'missing' };
},
},
}),
security: {
dm: {
channelKey: 'fabric',
resolvePolicy: (a) => a.dmPolicy,
resolveAllowFrom: (a) => a.allowFrom,
defaultPolicy: 'allowlist',
},
},
// Fabric replies thread by being posted into the same channel.
threading: { topLevelReplyToMode: 'channel' },
outbound: {
attachedResults: {
sendText: async (params) => sendText(params),
},
},
});
}