feat(channels): add ChannelPlugin interface and registry-based factory

Refactor channel system to use a formal plugin interface:

- Add ChannelPlugin interface to @qwen-code/channel-base (channelType,
  displayName, requiredConfigFields, createChannel factory)
- Each built-in adapter (Telegram, WeChat, DingTalk) exports a plugin object
- Replace hardcoded if/else factory with a Map-based channel registry
- Config validation now uses plugin's requiredConfigFields dynamically
- ChannelType changed from fixed union to string for extensibility
- Multi-channel mode now picks model from first channel config

This is the foundation for external plugin support — built-in channels
go through the exact same code path that third-party plugins will use.
This commit is contained in:
tanzhenxin 2026-03-26 12:34:31 +00:00
parent f3a03d0bdc
commit 8a6ed128ea
8 changed files with 134 additions and 39 deletions

View file

@ -15,6 +15,7 @@ export type { SenderCheckResult } from './SenderGate.js';
export { SessionRouter } from './SessionRouter.js';
export type {
ChannelConfig,
ChannelPlugin,
ChannelType,
Envelope,
GroupConfig,

View file

@ -1,11 +1,9 @@
import type { AcpBridge } from './AcpBridge.js';
import type { ChannelBase, ChannelBaseOptions } from './ChannelBase.js';
export type SenderPolicy = 'allowlist' | 'pairing' | 'open';
export type SessionScope = 'user' | 'thread' | 'single';
export type ChannelType =
| 'telegram'
| 'weixin'
| 'dingtalk'
| 'discord'
| 'webhook';
export type ChannelType = string;
export type GroupPolicy = 'disabled' | 'allowlist' | 'open';
export interface GroupConfig {
@ -52,3 +50,30 @@ export interface SessionTarget {
chatId: string;
threadId?: string;
}
/**
* A channel plugin registers a channel type and provides a factory
* to create adapter instances. Both built-in adapters and external
* plugins conform to this interface.
*/
export interface ChannelPlugin {
/** Unique channel type ID (e.g., "telegram", "tmcp-dingtalk"). */
channelType: string;
/** Human-readable name for CLI output. */
displayName: string;
/**
* Config fields required by this channel type, beyond the shared
* ChannelConfig fields. Validated at startup.
*/
requiredConfigFields?: string[];
/** Create a channel adapter instance. */
createChannel(
name: string,
config: ChannelConfig & Record<string, unknown>,
bridge: AcpBridge,
options?: ChannelBaseOptions,
): ChannelBase;
}