@ganglion/weacpx-channel-feishu 0.1.2 → 0.2.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/dist/channel.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ChannelStartInput, CoordinatorMessageInput, CreateChannelDeps, MessageChannelRuntime, OrchestrationDeliveryCallbacks } from "weacpx/plugin-api";
1
+ import type { ChannelStartInput, CoordinatorMessageInput, CreateChannelDeps, ScheduledChannelMessageInput, MessageChannelRuntime, OrchestrationDeliveryCallbacks } from "weacpx/plugin-api";
2
2
  import type { FeishuResolvedAccountConfig } from "./config.js";
3
3
  import { type FeishuLarkClient } from "./lark-client.js";
4
4
  type OrchestrationTaskRecord = Parameters<MessageChannelRuntime["notifyTaskCompletion"]>[0];
@@ -27,6 +27,7 @@ export declare class FeishuChannel implements MessageChannelRuntime {
27
27
  notifyTaskCompletion(task: OrchestrationTaskRecord): Promise<void>;
28
28
  notifyTaskProgress(task: OrchestrationTaskRecord, text: string): Promise<void>;
29
29
  sendCoordinatorMessage(input: CoordinatorMessageInput): Promise<void>;
30
+ sendScheduledMessage(input: ScheduledChannelMessageInput): Promise<void>;
30
31
  private sendRouteText;
31
32
  private handleMessageEvent;
32
33
  /**
package/dist/index.js CHANGED
@@ -80363,6 +80363,103 @@ class FeishuChannel {
80363
80363
  async sendCoordinatorMessage(input) {
80364
80364
  await this.sendRouteText(input.chatKey, input.replyContextToken, input.text);
80365
80365
  }
80366
+ async sendScheduledMessage(input) {
80367
+ if (!this.agent || !this.logger) {
80368
+ throw new Error("FeishuChannel.start() must be called before scheduled message delivery");
80369
+ }
80370
+ const route = parseFeishuConversationId(input.chatKey);
80371
+ if (!route)
80372
+ throw new Error(`cannot deliver Feishu scheduled message to non-Feishu chatKey: ${input.chatKey}`);
80373
+ if (input.accountId && input.accountId !== route.accountId) {
80374
+ throw new Error(`scheduled Feishu accountId "${input.accountId}" does not match chatKey account "${route.accountId}"`);
80375
+ }
80376
+ const runtime = this.accounts.get(route.accountId);
80377
+ if (!runtime) {
80378
+ throw new Error(`feishu account "${route.accountId}" is not started; check channel.options.accounts and enabled flags`);
80379
+ }
80380
+ const deliverText = async (text) => {
80381
+ if (input.abortSignal?.aborted)
80382
+ return;
80383
+ const trimmed = text?.trim() ?? "";
80384
+ if (trimmed.length === 0)
80385
+ return;
80386
+ await this.sendRouteText(input.chatKey, input.replyContextToken, trimmed);
80387
+ };
80388
+ await this.sendRouteText(input.chatKey, input.replyContextToken, input.noticeText);
80389
+ const effectiveReplyMode = resolveEffectiveReplyMode(runtime.account.replyMode, undefined);
80390
+ const cardController = effectiveReplyMode === "streaming" ? await this.trySeedStreamingCard({
80391
+ runtime,
80392
+ accountId: route.accountId,
80393
+ chatId: route.chatId,
80394
+ ...input.replyContextToken ? { replyToMessageId: input.replyContextToken } : {}
80395
+ }) : null;
80396
+ const deliverReply = async (text) => {
80397
+ if (input.abortSignal?.aborted)
80398
+ return;
80399
+ if (cardController) {
80400
+ cardController.appendStream(text);
80401
+ return;
80402
+ }
80403
+ await deliverText(text);
80404
+ };
80405
+ try {
80406
+ const response = await this.agent.chat({
80407
+ accountId: route.accountId,
80408
+ conversationId: input.chatKey,
80409
+ text: input.promptText,
80410
+ ...input.replyContextToken ? { replyContextToken: input.replyContextToken } : {},
80411
+ ...input.abortSignal ? { abortSignal: input.abortSignal } : {},
80412
+ metadata: { channel: "feishu", scheduledSessionAlias: input.sessionAlias },
80413
+ reply: deliverReply,
80414
+ ...cardController ? {
80415
+ onToolEvent: (event) => {
80416
+ if (input.abortSignal?.aborted)
80417
+ return;
80418
+ cardController.recordToolEvent(event);
80419
+ },
80420
+ onThought: (chunk) => {
80421
+ if (input.abortSignal?.aborted)
80422
+ return;
80423
+ cardController.appendReasoning(chunk);
80424
+ }
80425
+ } : {}
80426
+ });
80427
+ if (input.abortSignal?.aborted) {
80428
+ if (cardController && !cardController.isTerminated()) {
80429
+ await cardController.abort(abortAck()).catch(() => {});
80430
+ }
80431
+ return;
80432
+ }
80433
+ const media = normalizeMediaArray(response.media);
80434
+ if (media.length > 0) {
80435
+ await this.logger.error("feishu.scheduled.media_unsupported", "scheduled feishu media responses are not supported", {
80436
+ accountId: route.accountId,
80437
+ chatKey: input.chatKey,
80438
+ taskId: input.taskId,
80439
+ sessionAlias: input.sessionAlias,
80440
+ count: media.length
80441
+ });
80442
+ }
80443
+ if (cardController) {
80444
+ const responseText = response.text?.trim() ?? "";
80445
+ await cardController.complete(responseText.length > 0 ? response.text : undefined);
80446
+ if (cardController.isDegraded() && responseText.length > 0) {
80447
+ await deliverText(response.text);
80448
+ }
80449
+ } else {
80450
+ await deliverText(response.text);
80451
+ }
80452
+ } catch (error) {
80453
+ if (cardController && !cardController.isTerminated()) {
80454
+ await cardController.fail(error instanceof Error ? error.message : String(error)).catch(() => {});
80455
+ } else {
80456
+ try {
80457
+ await deliverText(formatScheduledFailureText(input, error));
80458
+ } catch {}
80459
+ }
80460
+ throw error;
80461
+ }
80462
+ }
80366
80463
  async sendRouteText(chatKey, replyContextToken, text) {
80367
80464
  const route = parseFeishuConversationId(chatKey);
80368
80465
  if (!route)
@@ -80541,7 +80638,7 @@ class FeishuChannel {
80541
80638
  return;
80542
80639
  const effectiveReplyMode = resolveEffectiveReplyMode(runtime.account.replyMode, chatType);
80543
80640
  if (effectiveReplyMode === "streaming") {
80544
- await this.trySeedStreamingCard({ runtime, accountId, chatId, messageId, active });
80641
+ active.cardController = await this.trySeedStreamingCard({ runtime, accountId, chatId, replyToMessageId: messageId });
80545
80642
  }
80546
80643
  if (active.suppressed) {
80547
80644
  if (active.cardController && !active.cardController.isTerminated()) {
@@ -80614,7 +80711,7 @@ class FeishuChannel {
80614
80711
  }
80615
80712
  }
80616
80713
  async trySeedStreamingCard(input) {
80617
- const { runtime, accountId, chatId, messageId, active } = input;
80714
+ const { runtime, accountId, chatId } = input;
80618
80715
  try {
80619
80716
  const controller = new StreamingCardController({
80620
80717
  client: runtime.client.sdk,
@@ -80629,8 +80726,8 @@ class FeishuChannel {
80629
80726
  this.logger?.error("feishu.card.degraded", "streaming card updates failing; will deliver answer via plain reply", { accountId, chatId, consecutiveFailures, bufferChars: buffer.length });
80630
80727
  }
80631
80728
  });
80632
- await controller.seed({ to: chatId, replyToMessageId: messageId });
80633
- active.cardController = controller;
80729
+ await controller.seed({ to: chatId, ...input.replyToMessageId ? { replyToMessageId: input.replyToMessageId } : {} });
80730
+ return controller;
80634
80731
  } catch (error) {
80635
80732
  const permErr = extractPermissionError(error);
80636
80733
  await this.logger.info("feishu.streaming.fallback", "streaming card seed failed; falling back to static", {
@@ -80641,7 +80738,7 @@ class FeishuChannel {
80641
80738
  });
80642
80739
  if (permErr)
80643
80740
  await this.maybeNotifyPermissionError({ runtime, chatId, error });
80644
- active.cardController = null;
80741
+ return null;
80645
80742
  }
80646
80743
  }
80647
80744
  async deliverResponse(input) {
@@ -80826,6 +80923,10 @@ class FeishuChannel {
80826
80923
  return true;
80827
80924
  }
80828
80925
  }
80926
+ function formatScheduledFailureText(input, error) {
80927
+ const message = error instanceof Error ? error.message : String(error);
80928
+ return input.taskId ? `⏰ 定时任务 #${input.taskId} 执行失败:${message}` : `⏰ 定时任务执行失败:${message}`;
80929
+ }
80829
80930
  function defaultMimeForKind(kind) {
80830
80931
  if (kind === "image")
80831
80932
  return "image/*";
@@ -81061,7 +81162,7 @@ var feishuCliProvider = {
81061
81162
  var plugin = {
81062
81163
  apiVersion: 1,
81063
81164
  name: "@ganglion/weacpx-channel-feishu",
81064
- minWeacpxVersion: "0.4.0",
81165
+ minWeacpxVersion: "0.5.0",
81065
81166
  channels: [
81066
81167
  {
81067
81168
  type: "feishu",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ganglion/weacpx-channel-feishu",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "Feishu channel plugin for weacpx.",
5
5
  "license": "MIT",
6
6
  "keywords": ["weacpx", "feishu", "channel", "plugin"],
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "files": ["dist", "README.md"],
22
22
  "peerDependencies": {
23
- "weacpx": ">=0.4.0-0"
23
+ "weacpx": ">=0.5.0-0"
24
24
  },
25
25
  "peerDependenciesMeta": {
26
26
  "weacpx": {