@vibearound/plugin-channel-sdk 0.3.0 → 0.4.1

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.
package/src/renderer.ts CHANGED
@@ -25,36 +25,30 @@
25
25
  *
26
26
  * ## Usage
27
27
  *
28
+ * Subclass and implement `sendText` + `sendBlock` (+ optionally `editBlock`):
29
+ *
28
30
  * ```ts
29
31
  * class MyRenderer extends BlockRenderer<string> {
30
- * protected async sendBlock(channelId, kind, content) {
31
- * const msg = await myApi.sendMessage(channelId, content);
32
+ * protected async sendText(chatId, text) {
33
+ * await myApi.sendMessage(chatId, text);
34
+ * }
35
+ * protected async sendBlock(chatId, kind, content) {
36
+ * const msg = await myApi.sendMessage(chatId, content);
32
37
  * return msg.id;
33
38
  * }
34
- * protected async editBlock(channelId, ref, kind, content, sealed) {
39
+ * protected async editBlock(chatId, ref, kind, content, sealed) {
35
40
  * await myApi.editMessage(ref, content);
36
41
  * }
37
42
  * }
38
- *
39
- * // In main.ts:
40
- * const renderer = new MyRenderer({ verbose: { showThinking: false } });
41
- *
42
- * // When user sends a message:
43
- * renderer.onPromptSent(channelId);
44
- * try {
45
- * await agent.prompt({ sessionId, content });
46
- * await renderer.onTurnEnd(channelId);
47
- * } catch (e) {
48
- * await renderer.onTurnError(channelId, String(e));
49
- * }
50
- *
51
- * // In the ACP client's sessionUpdate handler:
52
- * renderer.onSessionUpdate(notification);
53
43
  * ```
44
+ *
45
+ * The SDK's `runChannelPlugin` wires all ACP events to this renderer
46
+ * automatically — plugins don't call onSessionUpdate/onPromptSent/etc
47
+ * directly.
54
48
  */
55
49
 
56
50
  import type { SessionNotification } from "@agentclientprotocol/sdk";
57
- import type { BlockKind, BlockRendererOptions, VerboseConfig } from "./types.js";
51
+ import type { BlockKind, BlockRendererOptions, CommandEntry, VerboseConfig } from "./types.js";
58
52
 
59
53
  // ---------------------------------------------------------------------------
60
54
  // Local ACP session-update narrowing
@@ -100,7 +94,7 @@ type ConsumedSessionUpdate =
100
94
 
101
95
  interface ManagedBlock<TRef> {
102
96
  /** Channel this block belongs to. Captured at creation time. */
103
- channelId: string;
97
+ chatId: string;
104
98
  kind: BlockKind;
105
99
  content: string;
106
100
  /** Platform message reference set after the first successful send. */
@@ -139,13 +133,21 @@ const DEFAULT_MIN_EDIT_INTERVAL_MS = 1000;
139
133
  * return type of `sendBlock` and the first argument of `editBlock`.
140
134
  */
141
135
  export abstract class BlockRenderer<TRef = string> {
136
+ /** When true, blocks are sent and edited in real-time. When false, each
137
+ * block is held until complete, then sent once (send-only mode). */
138
+ protected readonly streaming: boolean;
142
139
  protected readonly flushIntervalMs: number;
143
140
  protected readonly minEditIntervalMs: number;
144
141
  protected readonly verbose: VerboseConfig;
145
142
 
146
143
  private states = new Map<string, ChannelState<TRef>>();
147
144
 
145
+ /** The chatId of the most recent prompt. Used as fallback target for
146
+ * notifications that arrive without an explicit chatId. */
147
+ private lastActiveChatId: string | null = null;
148
+
148
149
  constructor(options: BlockRendererOptions = {}) {
150
+ this.streaming = options.streaming ?? true;
149
151
  this.flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
150
152
  this.minEditIntervalMs = options.minEditIntervalMs ?? DEFAULT_MIN_EDIT_INTERVAL_MS;
151
153
  this.verbose = {
@@ -155,21 +157,31 @@ export abstract class BlockRenderer<TRef = string> {
155
157
  }
156
158
 
157
159
  // ---------------------------------------------------------------------------
158
- // Abstract / overridable subclass implements these
160
+ // Abstract plugin MUST implement these
159
161
  // ---------------------------------------------------------------------------
160
162
 
161
163
  /**
162
- * Send a new block message to the platform.
164
+ * Send a plain text message to the IM. Used for system text, agent ready
165
+ * notifications, session ready, and error messages.
166
+ */
167
+ protected abstract sendText(chatId: string, text: string): Promise<void>;
168
+
169
+ /**
170
+ * Send a new streaming block message to the platform.
163
171
  *
164
172
  * Return the platform message reference that will be passed to future
165
173
  * `editBlock` calls. Return `null` if editing is not supported.
166
174
  */
167
175
  protected abstract sendBlock(
168
- channelId: string,
176
+ chatId: string,
169
177
  kind: BlockKind,
170
178
  content: string,
171
179
  ): Promise<TRef | null>;
172
180
 
181
+ // ---------------------------------------------------------------------------
182
+ // Optional overrides — plugin MAY implement these
183
+ // ---------------------------------------------------------------------------
184
+
173
185
  /**
174
186
  * Edit an existing block message in-place.
175
187
  *
@@ -180,7 +192,7 @@ export abstract class BlockRenderer<TRef = string> {
180
192
  * Use to switch from a "streaming" card format to a finalized one.
181
193
  */
182
194
  protected editBlock?(
183
- channelId: string,
195
+ chatId: string,
184
196
  ref: TRef,
185
197
  kind: BlockKind,
186
198
  content: string,
@@ -207,31 +219,18 @@ export abstract class BlockRenderer<TRef = string> {
207
219
 
208
220
  /**
209
221
  * Called after the last block has been flushed and the turn is complete.
210
- * Override to perform cleanup (e.g. remove a "typing" indicator, a
211
- * processing reaction, etc.).
212
- */
213
- protected onAfterTurnEnd(_channelId: string): Promise<void> {
214
- return Promise.resolve();
215
- }
216
-
217
- /**
218
- * Called after a turn error, once state has been cleaned up.
219
- * Override to send an error message to the user.
222
+ * Override to perform cleanup (e.g. remove a "typing" indicator).
220
223
  */
221
- protected onAfterTurnError(_channelId: string, _error: string): Promise<void> {
224
+ protected onAfterTurnEnd(_chatId: string): Promise<void> {
222
225
  return Promise.resolve();
223
226
  }
224
227
 
225
228
  /**
226
- * Map an ACP `sessionId` to the channel ID used internally.
227
- *
228
- * Default: identity (sessionId === channelId).
229
- *
230
- * Override if your plugin namespaces channel IDs (e.g. Feishu uses
231
- * `"feishu:<sessionId>"`, WeChat uses `"weixin-openclaw-bridge:<sessionId>"`).
229
+ * Called after a turn error. Default sends an error message via sendText.
230
+ * Override for platform-specific error rendering (e.g. error card).
232
231
  */
233
- protected sessionIdToChannelId(sessionId: string): string {
234
- return sessionId;
232
+ protected async onAfterTurnError(chatId: string, error: string): Promise<void> {
233
+ await this.sendText(chatId, `❌ Error: ${error}`);
235
234
  }
236
235
 
237
236
  // ---------------------------------------------------------------------------
@@ -244,14 +243,12 @@ export abstract class BlockRenderer<TRef = string> {
244
243
  * Routes the event to the correct block based on its variant, appending
245
244
  * deltas to the current block or starting a new one when the kind changes.
246
245
  *
247
- * Call this from the ACP `Client.sessionUpdate` handler.
246
+ * Called automatically by `runChannelPlugin` — plugins don't call this directly.
248
247
  */
249
248
  onSessionUpdate(notification: SessionNotification): void {
250
- const sessionId = notification.sessionId;
251
- // Narrow through the local ConsumedSessionUpdate union see the type
252
- // declaration above this class for why we re-declare it locally instead
253
- // of importing the SDK's own union. Variants other than the four we
254
- // handle are treated as no-ops.
249
+ // sessionId from ACP = chatId (the host replaces the real agent session
250
+ // ID with the chat ID before forwarding to the plugin).
251
+ const chatId = notification.sessionId;
255
252
  const rawUpdate = notification.update as unknown as { sessionUpdate: string };
256
253
  const variant = rawUpdate.sessionUpdate;
257
254
  if (
@@ -263,47 +260,44 @@ export abstract class BlockRenderer<TRef = string> {
263
260
  return;
264
261
  }
265
262
  const update = rawUpdate as ConsumedSessionUpdate;
266
- const channelId = this.sessionIdToChannelId(sessionId);
267
263
 
268
264
  switch (update.sessionUpdate) {
269
265
  case "agent_message_chunk": {
270
266
  const delta = update.content?.text ?? "";
271
- if (delta) this.appendToBlock(channelId, "text", delta);
267
+ if (delta) this.appendToBlock(chatId, "text", delta);
272
268
  break;
273
269
  }
274
270
  case "agent_thought_chunk": {
275
- if (!this.verbose.showThinking) return; // skip — no block, no boundary
271
+ if (!this.verbose.showThinking) return;
276
272
  const delta = update.content?.text ?? "";
277
- if (delta) this.appendToBlock(channelId, "thinking", delta);
273
+ if (delta) this.appendToBlock(chatId, "thinking", delta);
278
274
  break;
279
275
  }
280
276
  case "tool_call": {
281
- if (!this.verbose.showToolUse) return; // skip
282
- if (update.title) this.appendToBlock(channelId, "tool", `🔧 ${update.title}\n`);
277
+ if (!this.verbose.showToolUse) return;
278
+ if (update.title) this.appendToBlock(chatId, "tool", `🔧 ${update.title}\n`);
283
279
  break;
284
280
  }
285
281
  case "tool_call_update": {
286
- if (!this.verbose.showToolUse) return; // skip
282
+ if (!this.verbose.showToolUse) return;
287
283
  const title = update.title ?? "tool";
288
284
  if (update.status === "completed" || update.status === "error") {
289
- this.appendToBlock(channelId, "tool", `✅ ${title}\n`);
285
+ this.appendToBlock(chatId, "tool", `✅ ${title}\n`);
290
286
  }
291
287
  break;
292
288
  }
293
- // Unknown / unconsumed variant — ignore.
294
289
  }
295
290
  }
296
291
 
297
292
  /**
298
- * Call this before sending a prompt to the agent.
299
- *
300
- * Clears any leftover state from a previous turn so the new turn starts
301
- * with a clean slate.
293
+ * Call before sending a prompt. Tracks the active chatId and clears
294
+ * leftover state from a previous turn.
302
295
  */
303
- onPromptSent(channelId: string): void {
304
- const old = this.states.get(channelId);
296
+ onPromptSent(chatId: string): void {
297
+ this.lastActiveChatId = chatId;
298
+ const old = this.states.get(chatId);
305
299
  if (old?.flushTimer) clearTimeout(old.flushTimer);
306
- this.states.set(channelId, {
300
+ this.states.set(chatId, {
307
301
  blocks: [],
308
302
  flushTimer: null,
309
303
  lastEditMs: 0,
@@ -311,14 +305,70 @@ export abstract class BlockRenderer<TRef = string> {
311
305
  });
312
306
  }
313
307
 
308
+ // ---------------------------------------------------------------------------
309
+ // Host notification handlers — built-in defaults, no per-plugin duplication
310
+ // ---------------------------------------------------------------------------
311
+
312
+ /** Handle `va/system_text` from host. */
313
+ onSystemText(chatId: string, text: string): void {
314
+ this.sendText(chatId, text).catch(() => {});
315
+ }
316
+
317
+ /** Handle `va/agent_ready` from host. */
318
+ onAgentReady(chatId: string, agent: string, version: string): void {
319
+ this.sendText(chatId, `🤖 Agent: ${agent} v${version}`).catch(() => {});
320
+ }
321
+
322
+ /** Handle `va/session_ready` from host. */
323
+ onSessionReady(chatId: string, sessionId: string): void {
324
+ this.sendText(chatId, `📋 Session: ${sessionId}`).catch(() => {});
325
+ }
326
+
327
+ /**
328
+ * Handle `va/command_menu` from host — display available commands.
329
+ *
330
+ * Default renders a plain-text list. Override for platform-specific
331
+ * rendering (e.g. Feishu interactive card, Slack Block Kit, Telegram
332
+ * inline keyboard).
333
+ */
334
+ onCommandMenu(
335
+ chatId: string,
336
+ systemCommands: CommandEntry[],
337
+ agentCommands: CommandEntry[],
338
+ ): void {
339
+ const lines: string[] = [];
340
+
341
+ lines.push("System commands:");
342
+ for (const cmd of systemCommands) {
343
+ const usage = cmd.args ? `/${cmd.name} ${cmd.args}` : `/${cmd.name}`;
344
+ lines.push(` ${usage} — ${cmd.description}`);
345
+ }
346
+
347
+ if (agentCommands.length > 0) {
348
+ lines.push("");
349
+ lines.push("Agent commands (use /agent <command>):");
350
+ for (const cmd of agentCommands) {
351
+ const desc = cmd.description.length > 80
352
+ ? `${cmd.description.slice(0, 77)}...`
353
+ : cmd.description;
354
+ lines.push(` /${cmd.name} — ${desc}`);
355
+ }
356
+ } else {
357
+ lines.push("");
358
+ lines.push("Agent commands will appear after sending your first message.");
359
+ }
360
+
361
+ this.sendText(chatId, lines.join("\n")).catch(() => {});
362
+ }
363
+
314
364
  /**
315
365
  * Call this after `agent.prompt()` resolves (turn complete).
316
366
  *
317
367
  * Seals and flushes the last block, then waits for all pending sends/edits
318
368
  * to complete before calling `onAfterTurnEnd`.
319
369
  */
320
- async onTurnEnd(channelId: string): Promise<void> {
321
- const state = this.states.get(channelId);
370
+ async onTurnEnd(chatId: string): Promise<void> {
371
+ const state = this.states.get(chatId);
322
372
  if (!state) return;
323
373
 
324
374
  if (state.flushTimer) {
@@ -332,35 +382,33 @@ export abstract class BlockRenderer<TRef = string> {
332
382
  this.enqueueFlush(state, last);
333
383
  }
334
384
 
335
- // Wait for the entire chain to drain before cleanup
336
385
  await state.sendChain;
337
- this.states.delete(channelId);
338
- await this.onAfterTurnEnd(channelId);
386
+ this.states.delete(chatId);
387
+ await this.onAfterTurnEnd(chatId);
339
388
  }
340
389
 
341
390
  /**
342
391
  * Call this when `agent.prompt()` throws (turn error).
343
392
  *
344
- * Discards pending state and calls `onAfterTurnError` so the subclass can
345
- * send an error message to the user.
393
+ * Discards pending state and calls `onAfterTurnError`.
346
394
  */
347
- async onTurnError(channelId: string, error: string): Promise<void> {
348
- const state = this.states.get(channelId);
395
+ async onTurnError(chatId: string, error: string): Promise<void> {
396
+ const state = this.states.get(chatId);
349
397
  if (state?.flushTimer) clearTimeout(state.flushTimer);
350
- this.states.delete(channelId);
351
- await this.onAfterTurnError(channelId, error);
398
+ this.states.delete(chatId);
399
+ await this.onAfterTurnError(chatId, error);
352
400
  }
353
401
 
354
402
  // ---------------------------------------------------------------------------
355
403
  // Internal — block management
356
404
  // ---------------------------------------------------------------------------
357
405
 
358
- private appendToBlock(channelId: string, kind: BlockKind, delta: string): void {
359
- let state = this.states.get(channelId);
406
+ private appendToBlock(chatId: string, kind: BlockKind, delta: string): void {
407
+ let state = this.states.get(chatId);
360
408
  if (!state) {
361
409
  // Auto-create state if onPromptSent wasn't called (e.g. host-initiated turns)
362
410
  state = { blocks: [], flushTimer: null, lastEditMs: 0, sendChain: Promise.resolve() };
363
- this.states.set(channelId, state);
411
+ this.states.set(chatId, state);
364
412
  }
365
413
 
366
414
  const last = state.blocks.at(-1);
@@ -379,33 +427,30 @@ export abstract class BlockRenderer<TRef = string> {
379
427
  }
380
428
  this.enqueueFlush(state, last);
381
429
  }
382
- state.blocks.push({ channelId, kind, content: delta, ref: null, creating: false, sealed: false });
430
+ state.blocks.push({ chatId, kind, content: delta, ref: null, creating: false, sealed: false });
383
431
  }
384
432
 
385
- this.scheduleFlush(channelId, state);
433
+ this.scheduleFlush(chatId, state);
386
434
  }
387
435
 
388
- private scheduleFlush(channelId: string, state: ChannelState<TRef>): void {
436
+ private scheduleFlush(chatId: string, state: ChannelState<TRef>): void {
389
437
  if (state.flushTimer) return; // already scheduled
390
438
 
391
439
  state.flushTimer = setTimeout(() => {
392
440
  state.flushTimer = null;
393
- this.flush(channelId, state);
441
+ this.flush(chatId, state);
394
442
  }, this.flushIntervalMs);
395
443
  }
396
444
 
397
- private flush(channelId: string, state: ChannelState<TRef>): void {
445
+ private flush(chatId: string, state: ChannelState<TRef>): void {
398
446
  const block = state.blocks.at(-1);
399
447
  if (!block || block.sealed || !block.content) return;
400
448
 
401
- // Send-only mode: subclasses that don't override `editBlock` (e.g.
402
- // QQ Bot, where the platform has no edit support) would otherwise
403
- // POST a new message for every debounced flush, so the user sees a
404
- // partial chunk followed by the full message as two separate
405
- // deliveries. Defer intermediate sends; only `onTurnEnd` and block
406
- // boundary transitions inside `appendToBlock` (which seal the block
407
- // first) will actually POST.
408
- if (!this.editBlock) {
449
+ // Send-only mode (streaming=false): defer intermediate sends.
450
+ // Only sealed blocks (from onTurnEnd or block boundary transitions)
451
+ // will actually POST. This prevents the user seeing a partial chunk
452
+ // followed by the full message as two separate messages.
453
+ if (!this.streaming) {
409
454
  return;
410
455
  }
411
456
 
@@ -416,7 +461,7 @@ export abstract class BlockRenderer<TRef = string> {
416
461
  if (!state.flushTimer) {
417
462
  state.flushTimer = setTimeout(() => {
418
463
  state.flushTimer = null;
419
- this.flush(channelId, state);
464
+ this.flush(chatId, state);
420
465
  }, delay);
421
466
  }
422
467
  return;
@@ -439,12 +484,12 @@ export abstract class BlockRenderer<TRef = string> {
439
484
  if (block.ref === null && !block.creating) {
440
485
  // First send — use sentinel to prevent concurrent creates
441
486
  block.creating = true;
442
- block.ref = await this.sendBlock(block.channelId, block.kind, content);
487
+ block.ref = await this.sendBlock(block.chatId, block.kind, content);
443
488
  block.creating = false;
444
489
  state.lastEditMs = Date.now();
445
- } else if (block.ref !== null && !block.creating && this.editBlock) {
446
- // Subsequent update — edit in-place
447
- await this.editBlock(block.channelId, block.ref, block.kind, content, block.sealed);
490
+ } else if (block.ref !== null && !block.creating && this.streaming && this.editBlock) {
491
+ // Subsequent update — edit in-place (streaming mode only)
492
+ await this.editBlock(block.chatId, block.ref, block.kind, content, block.sealed);
448
493
  state.lastEditMs = Date.now();
449
494
  }
450
495
  // else: create is in-flight (creating === true) — skip
package/src/types.ts CHANGED
@@ -43,6 +43,18 @@ export interface PluginManifest {
43
43
  capabilities?: PluginCapabilities;
44
44
  }
45
45
 
46
+ // ---------------------------------------------------------------------------
47
+ // Command menu
48
+ // ---------------------------------------------------------------------------
49
+
50
+ /** A command entry from the host's command list. */
51
+ export interface CommandEntry {
52
+ name: string;
53
+ description: string;
54
+ args?: string;
55
+ aliases?: string[];
56
+ }
57
+
46
58
  // ---------------------------------------------------------------------------
47
59
  // Block rendering
48
60
  // ---------------------------------------------------------------------------
@@ -58,6 +70,18 @@ export interface VerboseConfig {
58
70
  }
59
71
 
60
72
  export interface BlockRendererOptions {
73
+ /**
74
+ * Whether the IM platform supports message editing (streaming mode).
75
+ *
76
+ * - `true` (default): blocks stream in real-time — `sendBlock()` creates
77
+ * the message, `editBlock()` updates it as more content arrives.
78
+ * - `false`: each block is held until complete, then sent once via
79
+ * `sendBlock()`. `editBlock()` is never called.
80
+ *
81
+ * Set to `false` for platforms that don't support editing sent messages
82
+ * (e.g. QQ Bot, WhatsApp, WeChat, LINE).
83
+ */
84
+ streaming?: boolean;
61
85
  /**
62
86
  * Debounce interval before flushing an unsealed block (ms).
63
87
  * Controls how often in-progress blocks are sent to the platform.
@@ -1,95 +0,0 @@
1
- /**
2
- * runChannelPlugin — shared main.ts boilerplate for every channel plugin.
3
- *
4
- * Each channel plugin used to have a ~120-line `main.ts` that was 85%
5
- * identical across Slack, Telegram, Discord, DingTalk, WeCom, etc:
6
- * connect to host, validate config keys, wire the standard sessionUpdate
7
- * and extNotification handlers, create the stream handler, start the bot,
8
- * wait for disconnect, stop. This helper absorbs that boilerplate so each
9
- * plugin's `main.ts` reduces to ~20 lines — a factory for the bot and a
10
- * factory for the stream handler.
11
- *
12
- * ## Usage
13
- *
14
- * ```ts
15
- * import { runChannelPlugin } from "@vibearound/plugin-channel-sdk";
16
- * import { SlackBot } from "./bot.js";
17
- * import { AgentStreamHandler } from "./agent-stream.js";
18
- *
19
- * runChannelPlugin({
20
- * name: "vibearound-slack",
21
- * version: "0.1.0",
22
- * requiredConfig: ["bot_token", "app_token"],
23
- * createBot: ({ config, agent, log, cacheDir }) =>
24
- * new SlackBot(
25
- * { bot_token: config.bot_token as string, app_token: config.app_token as string },
26
- * agent,
27
- * log,
28
- * cacheDir,
29
- * ),
30
- * createStreamHandler: (bot, log, verbose) =>
31
- * new AgentStreamHandler(bot, log, verbose),
32
- * });
33
- * ```
34
- */
35
- import type { Agent } from "@agentclientprotocol/sdk";
36
- import type { SessionNotification } from "./types.js";
37
- export type ChannelPluginLogger = (level: string, msg: string) => void;
38
- export interface ChannelStreamHandler {
39
- onSessionUpdate(params: SessionNotification): void;
40
- onSystemText(text: string): void;
41
- onAgentReady(agent: string, version: string): void;
42
- onSessionReady(sessionId: string): void;
43
- }
44
- export interface ChannelBot<THandler extends ChannelStreamHandler = ChannelStreamHandler> {
45
- setStreamHandler(handler: THandler): void;
46
- start(): Promise<void> | void;
47
- stop(): Promise<void> | void;
48
- }
49
- export interface CreateBotContext {
50
- config: Record<string, unknown>;
51
- agent: Agent;
52
- log: ChannelPluginLogger;
53
- cacheDir: string;
54
- }
55
- export interface VerboseOptions {
56
- showThinking: boolean;
57
- showToolUse: boolean;
58
- }
59
- export interface RunChannelPluginSpec<TBot extends ChannelBot<THandler>, THandler extends ChannelStreamHandler> {
60
- /** Plugin name reported during ACP initialize (e.g. "vibearound-slack"). */
61
- name: string;
62
- /** Plugin version reported during ACP initialize. */
63
- version: string;
64
- /**
65
- * Config keys that MUST be present on `meta.config`. Plugin startup
66
- * fails with a clear error if any are missing. Keep to primitives
67
- * (strings/booleans); deeper validation belongs in the bot constructor.
68
- */
69
- requiredConfig?: string[];
70
- /** Factory: build the platform bot from host-supplied config + agent. */
71
- createBot: (ctx: CreateBotContext) => TBot | Promise<TBot>;
72
- /**
73
- * Factory: build the agent stream handler for this plugin. The handler
74
- * is wired to the bot via `bot.setStreamHandler(handler)` before the
75
- * bot is started.
76
- */
77
- createStreamHandler: (bot: TBot, log: ChannelPluginLogger, verbose: VerboseOptions) => THandler;
78
- /**
79
- * Optional hook invoked after the bot has been constructed but before
80
- * `start()` is called. Use this for one-off initialization that needs
81
- * to log diagnostic info (e.g. Telegram's `probe()`).
82
- */
83
- afterCreate?: (bot: TBot, log: ChannelPluginLogger) => Promise<void> | void;
84
- }
85
- /**
86
- * Run a channel plugin to completion.
87
- *
88
- * Performs the ACP initialize handshake, validates required config,
89
- * constructs the bot + stream handler, starts the bot, waits for the host
90
- * connection to close, then stops the bot and exits the process.
91
- *
92
- * Never returns under normal operation — the process exits at the end.
93
- */
94
- export declare function runChannelPlugin<TBot extends ChannelBot<THandler>, THandler extends ChannelStreamHandler>(spec: RunChannelPluginSpec<TBot, THandler>): Promise<void>;
95
- //# sourceMappingURL=run-plugin.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"run-plugin.d.ts","sourceRoot":"","sources":["../src/run-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAItD,OAAO,KAAK,EAGV,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAMpB,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;AAEvE,MAAM,WAAW,oBAAoB;IACnC,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACnD,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACnD,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,UAAU,CAAC,QAAQ,SAAS,oBAAoB,GAAG,oBAAoB;IACtF,gBAAgB,CAAC,OAAO,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,EAAE,KAAK,CAAC;IACb,GAAG,EAAE,mBAAmB,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB,CACnC,IAAI,SAAS,UAAU,CAAC,QAAQ,CAAC,EACjC,QAAQ,SAAS,oBAAoB;IAErC,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IAEb,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAE1B,yEAAyE;IACzE,SAAS,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3D;;;;OAIG;IACH,mBAAmB,EAAE,CACnB,GAAG,EAAE,IAAI,EACT,GAAG,EAAE,mBAAmB,EACxB,OAAO,EAAE,cAAc,KACpB,QAAQ,CAAC;IAEd;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC7E;AAMD;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,SAAS,UAAU,CAAC,QAAQ,CAAC,EACjC,QAAQ,SAAS,oBAAoB,EACrC,IAAI,EAAE,oBAAoB,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAY3D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"run-plugin.js","sourceRoot":"","sources":["../src/run-plugin.ts"],"names":[],"mappings":"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"}