@ebowwa/channel-types 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,157 @@
1
+ /**
2
+ * @ebowwa/channel-types/config
3
+ *
4
+ * Composable channel configuration loading.
5
+ * Supports Doppler (env vars), JSON files, and programmatic config.
6
+ *
7
+ * Usage:
8
+ * const config = loadChannelConfig(); // From env
9
+ * const config = parseChannelConfig(jsonString); // From JSON
10
+ * const config = createChannelConfig({ ... }); // Programmatic
11
+ */
12
+ import type { ChannelPlatform, ChannelId } from "./index.js";
13
+ /** Base configuration for all channels */
14
+ export interface BaseChannelConfig {
15
+ /** Channel platform */
16
+ platform: ChannelPlatform;
17
+ /** Unique account/bot identifier */
18
+ accountId: string;
19
+ /** Enable/disable channel */
20
+ enabled: boolean;
21
+ /** Human-readable label */
22
+ label?: string;
23
+ /** Optional instance ID for multiple instances */
24
+ instanceId?: string;
25
+ }
26
+ /** Telegram channel configuration */
27
+ export interface TelegramChannelConfig extends BaseChannelConfig {
28
+ platform: "telegram";
29
+ /** Bot token from @BotFather */
30
+ botToken: string;
31
+ /** Polling interval in ms (default: 1000) */
32
+ pollingInterval?: number;
33
+ /** Allowed user IDs (empty = all) */
34
+ allowedUsers?: number[];
35
+ /** Allowed chat IDs (empty = all) */
36
+ allowedChats?: number[];
37
+ }
38
+ /** Discord channel configuration */
39
+ export interface DiscordChannelConfig extends BaseChannelConfig {
40
+ platform: "discord";
41
+ /** Bot token */
42
+ botToken: string;
43
+ /** Application ID */
44
+ applicationId?: string;
45
+ /** Guild ID to restrict to */
46
+ guildId?: string;
47
+ /** Channel IDs to listen to */
48
+ channelIds?: string[];
49
+ /** Enable slash commands */
50
+ enableSlashCommands?: boolean;
51
+ }
52
+ /** WhatsApp channel configuration */
53
+ export interface WhatsAppChannelConfig extends BaseChannelConfig {
54
+ platform: "whatsapp";
55
+ /** Webhook URL for business API */
56
+ webhookUrl?: string;
57
+ /** Phone number ID */
58
+ phoneNumberId?: string;
59
+ /** Access token */
60
+ accessToken?: string;
61
+ /** Or use QR code pairing (non-business) */
62
+ sessionPath?: string;
63
+ }
64
+ /** iMessage channel configuration */
65
+ export interface IMessageChannelConfig extends BaseChannelConfig {
66
+ platform: "imessage";
67
+ /** BlueBubbles server URL */
68
+ serverUrl?: string;
69
+ /** BlueBubbles password */
70
+ password?: string;
71
+ /** Or use local iMessage */
72
+ local?: boolean;
73
+ }
74
+ /** Slack channel configuration */
75
+ export interface SlackChannelConfig extends BaseChannelConfig {
76
+ platform: "slack";
77
+ /** Bot token (xoxb-...) */
78
+ botToken: string;
79
+ /** App token (xapp-...) for Socket Mode */
80
+ appToken?: string;
81
+ /** Signing secret */
82
+ signingSecret?: string;
83
+ /** Channel IDs to listen to */
84
+ channelIds?: string[];
85
+ }
86
+ /** Signal channel configuration */
87
+ export interface SignalChannelConfig extends BaseChannelConfig {
88
+ platform: "signal";
89
+ /** Phone number */
90
+ phoneNumber: string;
91
+ /** signal-cli path */
92
+ cliPath?: string;
93
+ /** Signal REST API URL */
94
+ apiUrl?: string;
95
+ }
96
+ /** CLI channel configuration (terminal-based) */
97
+ export interface CLIChannelConfig extends BaseChannelConfig {
98
+ platform: "cli";
99
+ /** Enable interactive mode */
100
+ interactive?: boolean;
101
+ /** Prompt string */
102
+ prompt?: string;
103
+ /** History file path */
104
+ historyPath?: string;
105
+ }
106
+ /** Web channel configuration */
107
+ export interface WebChannelConfig extends BaseChannelConfig {
108
+ platform: "web";
109
+ /** Port to listen on */
110
+ port?: number;
111
+ /** Host to bind to */
112
+ host?: string;
113
+ /** API key for authentication */
114
+ apiKey?: string;
115
+ /** Enable CORS */
116
+ cors?: boolean;
117
+ }
118
+ /** Union of all channel configs */
119
+ export type ChannelConfig = TelegramChannelConfig | DiscordChannelConfig | WhatsAppChannelConfig | IMessageChannelConfig | SlackChannelConfig | SignalChannelConfig | CLIChannelConfig | WebChannelConfig;
120
+ /** Map of channel name -> config */
121
+ export type ChannelConfigMap = Record<string, ChannelConfig>;
122
+ /**
123
+ * Environment variable naming convention:
124
+ *
125
+ * {PLATFORM}_ENABLED - Enable channel (true/false)
126
+ * {PLATFORM}_BOT_TOKEN - Bot token
127
+ * {PLATFORM}_ACCOUNT_ID - Account identifier
128
+ * {PLATFORM}_{OPTION} - Platform-specific options
129
+ *
130
+ * Examples:
131
+ * TELEGRAM_ENABLED=true
132
+ * TELEGRAM_BOT_TOKEN=123456:ABC
133
+ * DISCORD_ENABLED=true
134
+ * DISCORD_BOT_TOKEN=xyz.abc
135
+ * DISCORD_GUILD_ID=123456789
136
+ */
137
+ /** Load all channel configs from environment variables (Doppler) */
138
+ export declare function loadChannelConfigsFromEnv(): ChannelConfigMap;
139
+ /** Get ChannelId from config */
140
+ export declare function getChannelId(config: ChannelConfig): ChannelId;
141
+ /** Validate channel config */
142
+ export declare function validateChannelConfig(config: ChannelConfig): string[];
143
+ /** Filter configs by enabled status */
144
+ export declare function getEnabledChannels(configs: ChannelConfigMap): ChannelConfig[];
145
+ /** Filter configs by platform */
146
+ export declare function getChannelsByPlatform(configs: ChannelConfigMap, platform: ChannelPlatform): ChannelConfig[];
147
+ /** Create config programmatically */
148
+ export declare function createChannelConfig<T extends ChannelConfig>(config: T): T;
149
+ /** Parse config from JSON string */
150
+ export declare function parseChannelConfig(json: string): ChannelConfig;
151
+ /** Parse multiple configs from JSON string */
152
+ export declare function parseChannelConfigs(json: string): ChannelConfigMap;
153
+ /** Serialize config to JSON */
154
+ export declare function serializeChannelConfig(config: ChannelConfig): string;
155
+ /** Serialize multiple configs to JSON */
156
+ export declare function serializeChannelConfigs(configs: ChannelConfigMap): string;
157
+ //# sourceMappingURL=config.d.ts.map
package/dist/config.js ADDED
@@ -0,0 +1,257 @@
1
+ /**
2
+ * @ebowwa/channel-types/config
3
+ *
4
+ * Composable channel configuration loading.
5
+ * Supports Doppler (env vars), JSON files, and programmatic config.
6
+ *
7
+ * Usage:
8
+ * const config = loadChannelConfig(); // From env
9
+ * const config = parseChannelConfig(jsonString); // From JSON
10
+ * const config = createChannelConfig({ ... }); // Programmatic
11
+ */
12
+ import { createChannelId } from "./index.js";
13
+ // ============================================================
14
+ // CONFIG LOADING FROM DOPPLER (ENV VARS)
15
+ // ============================================================
16
+ /**
17
+ * Environment variable naming convention:
18
+ *
19
+ * {PLATFORM}_ENABLED - Enable channel (true/false)
20
+ * {PLATFORM}_BOT_TOKEN - Bot token
21
+ * {PLATFORM}_ACCOUNT_ID - Account identifier
22
+ * {PLATFORM}_{OPTION} - Platform-specific options
23
+ *
24
+ * Examples:
25
+ * TELEGRAM_ENABLED=true
26
+ * TELEGRAM_BOT_TOKEN=123456:ABC
27
+ * DISCORD_ENABLED=true
28
+ * DISCORD_BOT_TOKEN=xyz.abc
29
+ * DISCORD_GUILD_ID=123456789
30
+ */
31
+ /** Load all channel configs from environment variables (Doppler) */
32
+ export function loadChannelConfigsFromEnv() {
33
+ const configs = {};
34
+ // Telegram
35
+ if (isChannelEnabled("TELEGRAM")) {
36
+ configs.telegram = loadTelegramConfig();
37
+ }
38
+ // Discord
39
+ if (isChannelEnabled("DISCORD")) {
40
+ configs.discord = loadDiscordConfig();
41
+ }
42
+ // WhatsApp
43
+ if (isChannelEnabled("WHATSAPP")) {
44
+ configs.whatsapp = loadWhatsAppConfig();
45
+ }
46
+ // iMessage
47
+ if (isChannelEnabled("IMESSAGE")) {
48
+ configs.imessage = loadIMessageConfig();
49
+ }
50
+ // Slack
51
+ if (isChannelEnabled("SLACK")) {
52
+ configs.slack = loadSlackConfig();
53
+ }
54
+ // Signal
55
+ if (isChannelEnabled("SIGNAL")) {
56
+ configs.signal = loadSignalConfig();
57
+ }
58
+ // CLI (enabled by default)
59
+ if (isChannelEnabled("CLI", true)) {
60
+ configs.cli = loadCLIConfig();
61
+ }
62
+ // Web
63
+ if (isChannelEnabled("WEB")) {
64
+ configs.web = loadWebConfig();
65
+ }
66
+ return configs;
67
+ }
68
+ /** Check if a channel is enabled via env var */
69
+ function isChannelEnabled(prefix, defaultEnabled = false) {
70
+ const value = process.env[`${prefix}_ENABLED`];
71
+ if (value === undefined)
72
+ return defaultEnabled;
73
+ return value === "true" || value === "1";
74
+ }
75
+ /** Get env var with fallback */
76
+ function getEnv(key, fallback) {
77
+ return process.env[key] ?? fallback;
78
+ }
79
+ /** Get required env var (throws if missing) */
80
+ function requireEnv(key) {
81
+ const value = process.env[key];
82
+ if (!value) {
83
+ throw new Error(`Required environment variable ${key} is not set`);
84
+ }
85
+ return value;
86
+ }
87
+ /** Parse comma-separated list from env */
88
+ function parseList(value) {
89
+ if (!value)
90
+ return undefined;
91
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
92
+ }
93
+ /** Parse number list from env */
94
+ function parseNumberList(value) {
95
+ const list = parseList(value);
96
+ return list?.map((s) => parseInt(s, 10)).filter((n) => !isNaN(n));
97
+ }
98
+ // Individual channel loaders
99
+ function loadTelegramConfig() {
100
+ return {
101
+ platform: "telegram",
102
+ accountId: getEnv("TELEGRAM_ACCOUNT_ID", "default"),
103
+ enabled: true,
104
+ botToken: requireEnv("TELEGRAM_BOT_TOKEN"),
105
+ pollingInterval: parseInt(getEnv("TELEGRAM_POLLING_INTERVAL", "1000"), 10),
106
+ allowedUsers: parseNumberList(getEnv("TELEGRAM_ALLOWED_USERS")),
107
+ allowedChats: parseNumberList(getEnv("TELEGRAM_ALLOWED_CHATS")),
108
+ };
109
+ }
110
+ function loadDiscordConfig() {
111
+ return {
112
+ platform: "discord",
113
+ accountId: getEnv("DISCORD_ACCOUNT_ID", "default"),
114
+ enabled: true,
115
+ botToken: requireEnv("DISCORD_BOT_TOKEN"),
116
+ applicationId: getEnv("DISCORD_APPLICATION_ID"),
117
+ guildId: getEnv("DISCORD_GUILD_ID"),
118
+ channelIds: parseList(getEnv("DISCORD_CHANNEL_IDS")),
119
+ enableSlashCommands: getEnv("DISCORD_ENABLE_SLASH_COMMANDS", "true") === "true",
120
+ };
121
+ }
122
+ function loadWhatsAppConfig() {
123
+ return {
124
+ platform: "whatsapp",
125
+ accountId: getEnv("WHATSAPP_ACCOUNT_ID", "default"),
126
+ enabled: true,
127
+ webhookUrl: getEnv("WHATSAPP_WEBHOOK_URL"),
128
+ phoneNumberId: getEnv("WHATSAPP_PHONE_NUMBER_ID"),
129
+ accessToken: getEnv("WHATSAPP_ACCESS_TOKEN"),
130
+ sessionPath: getEnv("WHATSAPP_SESSION_PATH"),
131
+ };
132
+ }
133
+ function loadIMessageConfig() {
134
+ return {
135
+ platform: "imessage",
136
+ accountId: getEnv("IMESSAGE_ACCOUNT_ID", "default"),
137
+ enabled: true,
138
+ serverUrl: getEnv("IMESSAGE_SERVER_URL"),
139
+ password: getEnv("IMESSAGE_PASSWORD"),
140
+ local: getEnv("IMESSAGE_LOCAL", "false") === "true",
141
+ };
142
+ }
143
+ function loadSlackConfig() {
144
+ return {
145
+ platform: "slack",
146
+ accountId: getEnv("SLACK_ACCOUNT_ID", "default"),
147
+ enabled: true,
148
+ botToken: requireEnv("SLACK_BOT_TOKEN"),
149
+ appToken: getEnv("SLACK_APP_TOKEN"),
150
+ signingSecret: getEnv("SLACK_SIGNING_SECRET"),
151
+ channelIds: parseList(getEnv("SLACK_CHANNEL_IDS")),
152
+ };
153
+ }
154
+ function loadSignalConfig() {
155
+ return {
156
+ platform: "signal",
157
+ accountId: getEnv("SIGNAL_ACCOUNT_ID", "default"),
158
+ enabled: true,
159
+ phoneNumber: requireEnv("SIGNAL_PHONE_NUMBER"),
160
+ cliPath: getEnv("SIGNAL_CLI_PATH"),
161
+ apiUrl: getEnv("SIGNAL_API_URL"),
162
+ };
163
+ }
164
+ function loadCLIConfig() {
165
+ return {
166
+ platform: "cli",
167
+ accountId: getEnv("CLI_ACCOUNT_ID", "default"),
168
+ enabled: true,
169
+ interactive: getEnv("CLI_INTERACTIVE", "true") === "true",
170
+ prompt: getEnv("CLI_PROMPT", "> "),
171
+ historyPath: getEnv("CLI_HISTORY_PATH"),
172
+ };
173
+ }
174
+ function loadWebConfig() {
175
+ return {
176
+ platform: "web",
177
+ accountId: getEnv("WEB_ACCOUNT_ID", "default"),
178
+ enabled: true,
179
+ port: parseInt(getEnv("WEB_PORT", "3000"), 10),
180
+ host: getEnv("WEB_HOST", "0.0.0.0"),
181
+ apiKey: getEnv("WEB_API_KEY"),
182
+ cors: getEnv("WEB_CORS", "true") === "true",
183
+ };
184
+ }
185
+ // ============================================================
186
+ // CONFIG UTILITIES
187
+ // ============================================================
188
+ /** Get ChannelId from config */
189
+ export function getChannelId(config) {
190
+ return createChannelId(config.platform, config.accountId, config.instanceId);
191
+ }
192
+ /** Validate channel config */
193
+ export function validateChannelConfig(config) {
194
+ const errors = [];
195
+ switch (config.platform) {
196
+ case "telegram":
197
+ if (!config.botToken)
198
+ errors.push("Telegram: botToken is required");
199
+ break;
200
+ case "discord":
201
+ if (!config.botToken)
202
+ errors.push("Discord: botToken is required");
203
+ break;
204
+ case "whatsapp":
205
+ if (!config.webhookUrl && !config.sessionPath) {
206
+ errors.push("WhatsApp: webhookUrl or sessionPath is required");
207
+ }
208
+ break;
209
+ case "signal":
210
+ if (!config.phoneNumber)
211
+ errors.push("Signal: phoneNumber is required");
212
+ break;
213
+ case "slack":
214
+ if (!config.botToken)
215
+ errors.push("Slack: botToken is required");
216
+ break;
217
+ }
218
+ return errors;
219
+ }
220
+ /** Filter configs by enabled status */
221
+ export function getEnabledChannels(configs) {
222
+ return Object.values(configs).filter((c) => c.enabled);
223
+ }
224
+ /** Filter configs by platform */
225
+ export function getChannelsByPlatform(configs, platform) {
226
+ return Object.values(configs).filter((c) => c.platform === platform);
227
+ }
228
+ /** Create config programmatically */
229
+ export function createChannelConfig(config) {
230
+ const errors = validateChannelConfig(config);
231
+ if (errors.length > 0) {
232
+ throw new Error(`Invalid channel config: ${errors.join(", ")}`);
233
+ }
234
+ return config;
235
+ }
236
+ /** Parse config from JSON string */
237
+ export function parseChannelConfig(json) {
238
+ const config = JSON.parse(json);
239
+ const errors = validateChannelConfig(config);
240
+ if (errors.length > 0) {
241
+ throw new Error(`Invalid channel config: ${errors.join(", ")}`);
242
+ }
243
+ return config;
244
+ }
245
+ /** Parse multiple configs from JSON string */
246
+ export function parseChannelConfigs(json) {
247
+ return JSON.parse(json);
248
+ }
249
+ /** Serialize config to JSON */
250
+ export function serializeChannelConfig(config) {
251
+ return JSON.stringify(config, null, 2);
252
+ }
253
+ /** Serialize multiple configs to JSON */
254
+ export function serializeChannelConfigs(configs) {
255
+ return JSON.stringify(configs, null, 2);
256
+ }
257
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Example: Composable Channel-LLM Architecture
3
+ *
4
+ * This demonstrates how to:
5
+ * 1. Create a channel connector (Discord)
6
+ * 2. Create an LLM handler (GLM)
7
+ * 3. Connect them via a bridge
8
+ */
9
+ export {};
10
+ /**
11
+ * Output:
12
+ *
13
+ * === Composable Channel-LLM Example ===
14
+ *
15
+ * [Bridge] Registered handler: glm-4.7
16
+ * [Bridge] Registered channel: Discord
17
+ * [Bridge] Set default handler: discord:my-bot-123 → glm-4.7
18
+ * [Discord] Started
19
+ *
20
+ * --- Simulating messages ---
21
+ *
22
+ * [Discord] Sending: Hello! How can I help you today?
23
+ * [Discord] Sending: All systems operational. GLM 4.7 is ready to assist.
24
+ * [Discord] Sending: I received your message: "Can you help me with a task?"
25
+ *
26
+ * --- Stopping ---
27
+ *
28
+ * [Discord] Stopped
29
+ */
30
+ //# sourceMappingURL=example.d.ts.map
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Example: Composable Channel-LLM Architecture
3
+ *
4
+ * This demonstrates how to:
5
+ * 1. Create a channel connector (Discord)
6
+ * 2. Create an LLM handler (GLM)
7
+ * 3. Connect them via a bridge
8
+ */
9
+ import { createChannelId, createMessageRef, RICH_CAPABILITIES, } from "./index.js";
10
+ // ============================================================
11
+ // EXAMPLE: Discord Channel Connector
12
+ // ============================================================
13
+ class DiscordChannel {
14
+ id;
15
+ label = "Discord";
16
+ capabilities = RICH_CAPABILITIES;
17
+ messageHandler;
18
+ connected = false;
19
+ constructor(accountId) {
20
+ this.id = createChannelId("discord", accountId);
21
+ }
22
+ async start() {
23
+ // In real impl: connect to Discord gateway
24
+ this.connected = true;
25
+ console.log(`[${this.label}] Started`);
26
+ }
27
+ async stop() {
28
+ this.connected = false;
29
+ console.log(`[${this.label}] Stopped`);
30
+ }
31
+ onMessage(handler) {
32
+ this.messageHandler = handler;
33
+ }
34
+ async send(response) {
35
+ // In real impl: send to Discord API
36
+ console.log(`[${this.label}] Sending: ${response.content.text}`);
37
+ }
38
+ async stream(response, chunks) {
39
+ // In real impl: edit message with streaming content
40
+ let fullText = "";
41
+ for await (const chunk of chunks) {
42
+ fullText += chunk.text;
43
+ console.log(`[${this.label}] Stream: ${fullText}`);
44
+ }
45
+ }
46
+ isConnected() {
47
+ return this.connected;
48
+ }
49
+ // Simulate receiving a message (for testing)
50
+ async simulateMessage(text, senderId) {
51
+ if (!this.messageHandler)
52
+ return;
53
+ const message = {
54
+ messageId: `msg-${Date.now()}`,
55
+ channelId: this.id,
56
+ timestamp: new Date(),
57
+ sender: { id: senderId, username: `user_${senderId}` },
58
+ text,
59
+ context: { isDM: true },
60
+ };
61
+ const response = await this.messageHandler(message);
62
+ if (response) {
63
+ await this.send(response);
64
+ }
65
+ }
66
+ }
67
+ // ============================================================
68
+ // EXAMPLE: GLM LLM Handler
69
+ // ============================================================
70
+ class GLMHandler {
71
+ id = "glm-4.7";
72
+ model = "glm-4-flash";
73
+ ready = true;
74
+ async process(message) {
75
+ // In real impl: call GLM API
76
+ const startTime = Date.now();
77
+ // Simulate LLM response
78
+ const responseText = this.generateResponse(message.text);
79
+ return {
80
+ content: {
81
+ text: responseText,
82
+ replyToOriginal: true,
83
+ },
84
+ replyTo: createMessageRef(message.messageId, message.channelId),
85
+ isComplete: true,
86
+ metadata: {
87
+ model: this.model,
88
+ latency: Date.now() - startTime,
89
+ },
90
+ };
91
+ }
92
+ async *stream(message) {
93
+ // In real impl: stream from GLM API
94
+ const response = await this.process(message);
95
+ const words = response.content.text.split(" ");
96
+ for (let i = 0; i < words.length; i++) {
97
+ yield {
98
+ text: words[i] + " ",
99
+ done: i === words.length - 1,
100
+ seq: i,
101
+ };
102
+ await new Promise((r) => setTimeout(r, 50));
103
+ }
104
+ }
105
+ isReady() {
106
+ return this.ready;
107
+ }
108
+ generateResponse(input) {
109
+ // Simple echo + prefix for demo
110
+ if (input.toLowerCase().includes("hello")) {
111
+ return "Hello! How can I help you today?";
112
+ }
113
+ if (input.toLowerCase().includes("status")) {
114
+ return "All systems operational. GLM 4.7 is ready to assist.";
115
+ }
116
+ return `I received your message: "${input}"`;
117
+ }
118
+ }
119
+ // ============================================================
120
+ // EXAMPLE: Simple Bridge Implementation
121
+ // ============================================================
122
+ class SimpleBridge {
123
+ channels = new Map();
124
+ handlers = new Map();
125
+ defaultHandlers = new Map();
126
+ registerChannel(channel) {
127
+ const key = this.channelKey(channel.id);
128
+ this.channels.set(key, channel);
129
+ // Set up message routing
130
+ channel.onMessage(async (message) => {
131
+ const handlerId = this.defaultHandlers.get(key);
132
+ if (!handlerId) {
133
+ console.warn(`No handler for channel ${key}`);
134
+ return;
135
+ }
136
+ const handler = this.handlers.get(handlerId);
137
+ if (!handler) {
138
+ console.warn(`Handler ${handlerId} not found`);
139
+ return;
140
+ }
141
+ return handler.process(message);
142
+ });
143
+ console.log(`[Bridge] Registered channel: ${channel.label}`);
144
+ }
145
+ registerHandler(handler) {
146
+ this.handlers.set(handler.id, handler);
147
+ console.log(`[Bridge] Registered handler: ${handler.id}`);
148
+ }
149
+ setDefaultHandler(channelId, handlerId) {
150
+ this.defaultHandlers.set(this.channelKey(channelId), handlerId);
151
+ console.log(`[Bridge] Set default handler: ${this.channelKey(channelId)} → ${handlerId}`);
152
+ }
153
+ async start() {
154
+ for (const channel of this.channels.values()) {
155
+ await channel.start();
156
+ }
157
+ }
158
+ async stop() {
159
+ for (const channel of this.channels.values()) {
160
+ await channel.stop();
161
+ }
162
+ }
163
+ getChannels() {
164
+ return Array.from(this.channels.values());
165
+ }
166
+ getHandlers() {
167
+ return Array.from(this.handlers.values());
168
+ }
169
+ channelKey(id) {
170
+ return `${id.platform}:${id.accountId}`;
171
+ }
172
+ }
173
+ // ============================================================
174
+ // USAGE EXAMPLE
175
+ // ============================================================
176
+ async function main() {
177
+ console.log("=== Composable Channel-LLM Example ===\n");
178
+ // 1. Create bridge
179
+ const bridge = new SimpleBridge();
180
+ // 2. Create and register LLM handler
181
+ const glmHandler = new GLMHandler();
182
+ bridge.registerHandler(glmHandler);
183
+ // 3. Create and register channel
184
+ const discordChannel = new DiscordChannel("my-bot-123");
185
+ bridge.registerChannel(discordChannel);
186
+ // 4. Connect channel to handler
187
+ bridge.setDefaultHandler(discordChannel.id, glmHandler.id);
188
+ // 5. Start the bridge
189
+ await bridge.start();
190
+ // 6. Simulate messages
191
+ console.log("\n--- Simulating messages ---\n");
192
+ await discordChannel.simulateMessage("Hello!", "user-1");
193
+ await discordChannel.simulateMessage("What's the status?", "user-2");
194
+ await discordChannel.simulateMessage("Can you help me with a task?", "user-3");
195
+ // 7. Stop
196
+ console.log("\n--- Stopping ---\n");
197
+ await bridge.stop();
198
+ }
199
+ // Run example
200
+ main().catch(console.error);
201
+ /**
202
+ * Output:
203
+ *
204
+ * === Composable Channel-LLM Example ===
205
+ *
206
+ * [Bridge] Registered handler: glm-4.7
207
+ * [Bridge] Registered channel: Discord
208
+ * [Bridge] Set default handler: discord:my-bot-123 → glm-4.7
209
+ * [Discord] Started
210
+ *
211
+ * --- Simulating messages ---
212
+ *
213
+ * [Discord] Sending: Hello! How can I help you today?
214
+ * [Discord] Sending: All systems operational. GLM 4.7 is ready to assist.
215
+ * [Discord] Sending: I received your message: "Can you help me with a task?"
216
+ *
217
+ * --- Stopping ---
218
+ *
219
+ * [Discord] Stopped
220
+ */
221
+ //# sourceMappingURL=example.js.map