@soimy/dingtalk 3.5.1 → 3.5.2

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/channel.ts CHANGED
@@ -33,7 +33,7 @@ import {
33
33
  recordExplicitFeedbackLearning,
34
34
  } from "./feedback-learning-service";
35
35
  import { handleDingTalkMessage } from "./inbound-handler";
36
- import { getLogger } from "./logger-context";
36
+ import { getLogger, setCurrentLogger } from "./logger-context";
37
37
  import { prepareMediaInput, resolveOutboundMediaType } from "./media-utils";
38
38
  import { dingtalkSetupAdapter, dingtalkSetupWizard } from "./onboarding.js";
39
39
  import { resolveOriginalPeerId, preloadPeerIdsFromSessions } from "./peer-id-registry";
@@ -62,11 +62,13 @@ import type {
62
62
  } from "./types";
63
63
  import { ConnectionState } from "./types";
64
64
  import {
65
+ closePluginDebugLog,
65
66
  cleanupOrphanedTempFiles,
66
67
  createResolve4FallbackLookup,
67
68
  formatDingTalkConnectionErrorLog,
68
69
  formatDingTalkErrorPayloadLog,
69
70
  getCurrentTimestamp,
71
+ resolvePluginDebugLog,
70
72
  } from "./utils";
71
73
 
72
74
  type InstrumentedDWClient = {
@@ -460,14 +462,15 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
460
462
  const storePath = rt.channel.session.resolveStorePath(cfg.session?.store, {
461
463
  agentId: accountId,
462
464
  });
465
+ const effectiveLog = getLogger(accountId) || log;
463
466
  try {
464
467
  const result = await sendMessage(config, to, text, {
465
- log,
468
+ log: effectiveLog,
466
469
  accountId,
467
470
  storePath,
468
471
  conversationId: to,
469
472
  });
470
- getLogger()?.debug?.(`[DingTalk] sendText: "${text}" result: ${JSON.stringify(result)}`);
473
+ effectiveLog?.debug?.(`[DingTalk] sendText: "${text}" result: ${JSON.stringify(result)}`);
471
474
  if (!result.ok) {
472
475
  throw new Error(result.error || "sendText failed");
473
476
  }
@@ -487,7 +490,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
487
490
  };
488
491
  } catch (err: any) {
489
492
  if (err?.response?.data !== undefined) {
490
- log?.error?.(formatDingTalkErrorPayloadLog("outbound.sendText", err.response.data));
493
+ effectiveLog?.error?.(formatDingTalkErrorPayloadLog("outbound.sendText", err.response.data));
491
494
  }
492
495
  throw new Error(
493
496
  typeof err?.response?.data === "string"
@@ -514,6 +517,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
514
517
  const storePath = rt.channel.session.resolveStorePath(cfg.session?.store, {
515
518
  agentId: accountId,
516
519
  });
520
+ const effectiveLog = getLogger(accountId) || log;
517
521
  if (!config.clientId) {
518
522
  throw new Error("DingTalk not configured");
519
523
  }
@@ -521,7 +525,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
521
525
  // Support mediaPath/filePath/mediaUrl aliases for better CLI compatibility.
522
526
  const rawMediaPath = mediaPath || filePath || mediaUrl;
523
527
 
524
- getLogger()?.debug?.(
528
+ effectiveLog?.debug?.(
525
529
  `[DingTalk] sendMedia called: to=${to}, mediaPath=${mediaPath}, filePath=${filePath}, mediaUrl=${mediaUrl}, rawMediaPath=${rawMediaPath}`,
526
530
  );
527
531
 
@@ -539,10 +543,10 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
539
543
  let preparedMedia;
540
544
  try {
541
545
  try {
542
- preparedMedia = await prepareMediaInput(rawMediaPath, log, config.mediaUrlAllowlist);
546
+ preparedMedia = await prepareMediaInput(rawMediaPath, effectiveLog, config.mediaUrlAllowlist);
543
547
  } catch (err: any) {
544
548
  if (err?.response?.data !== undefined) {
545
- log?.error?.(
549
+ effectiveLog?.error?.(
546
550
  formatDingTalkErrorPayloadLog("outbound.sendMedia.prepare", err.response.data),
547
551
  );
548
552
  }
@@ -559,7 +563,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
559
563
  ? preparedMedia.path
560
564
  : resolveRelativePath(preparedMedia.path);
561
565
 
562
- getLogger()?.debug?.(
566
+ effectiveLog?.debug?.(
563
567
  `[DingTalk] sendMedia resolved path: rawMediaPath=${rawMediaPath}, actualMediaPath=${actualMediaPath}`,
564
568
  );
565
569
 
@@ -571,7 +575,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
571
575
  let result;
572
576
  try {
573
577
  result = await sendProactiveMedia(config, to, actualMediaPath, mediaType, {
574
- log,
578
+ log: effectiveLog,
575
579
  accountId,
576
580
  storePath,
577
581
  conversationId: to,
@@ -579,7 +583,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
579
583
  });
580
584
  } catch (err: any) {
581
585
  if (err?.response?.data !== undefined) {
582
- log?.error?.(
586
+ effectiveLog?.error?.(
583
587
  formatDingTalkErrorPayloadLog("outbound.sendMedia.send", err.response.data),
584
588
  );
585
589
  }
@@ -587,7 +591,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
587
591
  cause: err,
588
592
  });
589
593
  }
590
- getLogger()?.debug?.(
594
+ effectiveLog?.debug?.(
591
595
  `[DingTalk] sendMedia: ${mediaType} file=${actualMediaPath} result: ${JSON.stringify(result)}`,
592
596
  );
593
597
 
@@ -609,7 +613,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
609
613
  );
610
614
  } catch (err: any) {
611
615
  if (err?.response?.data !== undefined) {
612
- log?.error?.(formatDingTalkErrorPayloadLog("outbound.sendMedia", err.response.data));
616
+ effectiveLog?.error?.(formatDingTalkErrorPayloadLog("outbound.sendMedia", err.response.data));
613
617
  }
614
618
  throw new Error(
615
619
  typeof err?.response?.data === "string"
@@ -639,29 +643,37 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
639
643
  accountStorePath = undefined;
640
644
  }
641
645
 
642
- ctx.log?.info?.(`[${account.accountId}] Initializing DingTalk Stream client...`);
646
+ const pluginLog = resolvePluginDebugLog({
647
+ accountId: account.accountId,
648
+ storePath: accountStorePath,
649
+ debug: config.debug,
650
+ baseLog: ctx.log,
651
+ });
652
+ setCurrentLogger(pluginLog, account.accountId);
653
+
654
+ pluginLog?.info?.(`[${account.accountId}] Initializing DingTalk Stream client...`);
643
655
 
644
656
  // Preload known peer IDs from sessions so outbound delivery (e.g. cron
645
657
  // jobs that fire immediately after startup) can resolve the original
646
658
  // case-sensitive conversationId before any inbound message has arrived.
647
659
  preloadPeerIdsFromSessions();
648
- ctx.log?.debug?.(`[${account.accountId}] Peer ID registry preloaded from sessions`);
660
+ pluginLog?.debug?.(`[${account.accountId}] Peer ID registry preloaded from sessions`);
649
661
 
650
- cleanupOrphanedTempFiles(ctx.log);
662
+ cleanupOrphanedTempFiles(pluginLog);
651
663
  try {
652
664
  const recovered = await recoverPendingCardsForAccount(
653
665
  config,
654
666
  account.accountId,
655
667
  accountStorePath,
656
- ctx.log,
668
+ pluginLog,
657
669
  );
658
670
  if (recovered > 0) {
659
- ctx.log?.info?.(
671
+ pluginLog?.info?.(
660
672
  `[${account.accountId}] Recovered and finalized ${recovered} unfinished card(s) from previous runtime`,
661
673
  );
662
674
  }
663
675
  } catch (err: any) {
664
- ctx.log?.warn?.(
676
+ pluginLog?.warn?.(
665
677
  `[${account.accountId}] Failed to recover unfinished cards: ${err.message}`,
666
678
  );
667
679
  }
@@ -683,7 +695,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
683
695
  });
684
696
  (c as any).sslopts = {
685
697
  ...(c as any).sslopts,
686
- lookup: createResolve4FallbackLookup(ctx.log, account.accountId),
698
+ lookup: createResolve4FallbackLookup(pluginLog, account.accountId),
687
699
  };
688
700
 
689
701
  instrumentConnectionStages(c);
@@ -702,7 +714,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
702
714
  c.socketCallBackResponse(messageId, { success: true });
703
715
  stats.acked += 1;
704
716
  } catch (ackError: any) {
705
- ctx.log?.warn?.(
717
+ pluginLog?.warn?.(
706
718
  `[${account.accountId}] Failed to acknowledge callback ${messageId}: ${ackError.message}`,
707
719
  );
708
720
  }
@@ -723,38 +735,38 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
723
735
  accountId: account.accountId,
724
736
  data,
725
737
  sessionWebhook: data.sessionWebhook,
726
- log: ctx.log,
738
+ log: pluginLog,
727
739
  dingtalkConfig: config,
728
740
  });
729
741
  stats.processed += 1;
730
742
  if (stats.received % INBOUND_COUNTER_LOG_EVERY === 0) {
731
- logInboundCounters(ctx.log, account.accountId, "periodic");
743
+ logInboundCounters(pluginLog, account.accountId, "periodic");
732
744
  }
733
745
  return;
734
746
  }
735
747
 
736
748
  if (isMessageProcessed(dedupKey)) {
737
- ctx.log?.debug?.(`[${account.accountId}] Skipping duplicate message: ${dedupKey}`);
749
+ pluginLog?.debug?.(`[${account.accountId}] Skipping duplicate message: ${dedupKey}`);
738
750
  stats.dedupSkipped += 1;
739
751
  acknowledge();
740
- logInboundCounters(ctx.log, account.accountId, "dedup-skipped");
752
+ logInboundCounters(pluginLog, account.accountId, "dedup-skipped");
741
753
  return;
742
754
  }
743
755
 
744
756
  const inflightSince = processingDedupKeys.get(dedupKey);
745
757
  if (inflightSince !== undefined) {
746
758
  if (Date.now() - inflightSince > INFLIGHT_TTL_MS) {
747
- ctx.log?.warn?.(
759
+ pluginLog?.warn?.(
748
760
  `[${account.accountId}] Releasing stale in-flight lock for ${dedupKey} (held ${Date.now() - inflightSince}ms > TTL ${INFLIGHT_TTL_MS}ms)`,
749
761
  );
750
762
  processingDedupKeys.delete(dedupKey);
751
763
  } else {
752
- ctx.log?.debug?.(
764
+ pluginLog?.debug?.(
753
765
  `[${account.accountId}] Skipping in-flight duplicate message: ${dedupKey}`,
754
766
  );
755
767
  stats.inflightSkipped += 1;
756
768
  acknowledge();
757
- logInboundCounters(ctx.log, account.accountId, "inflight-skipped");
769
+ logInboundCounters(pluginLog, account.accountId, "inflight-skipped");
758
770
  return;
759
771
  }
760
772
  }
@@ -767,21 +779,21 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
767
779
  accountId: account.accountId,
768
780
  data,
769
781
  sessionWebhook: data.sessionWebhook,
770
- log: ctx.log,
782
+ log: pluginLog,
771
783
  dingtalkConfig: config,
772
784
  });
773
785
  stats.processed += 1;
774
786
  markMessageProcessed(dedupKey);
775
787
  if (stats.received % INBOUND_COUNTER_LOG_EVERY === 0) {
776
- logInboundCounters(ctx.log, account.accountId, "periodic");
788
+ logInboundCounters(pluginLog, account.accountId, "periodic");
777
789
  }
778
790
  } finally {
779
791
  processingDedupKeys.delete(dedupKey);
780
792
  }
781
793
  } catch (error: any) {
782
794
  stats.failed += 1;
783
- logInboundCounters(ctx.log, account.accountId, "failed");
784
- ctx.log?.error?.(`[${account.accountId}] Error processing message: ${error.message}`);
795
+ logInboundCounters(pluginLog, account.accountId, "failed");
796
+ pluginLog?.error?.(`[${account.accountId}] Error processing message: ${error.message}`);
785
797
  }
786
798
  });
787
799
 
@@ -794,7 +806,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
794
806
  try {
795
807
  c.socketCallBackResponse(messageId, { success: true });
796
808
  } catch (ackError: any) {
797
- ctx.log?.warn?.(
809
+ pluginLog?.warn?.(
798
810
  `[${account.accountId}] Failed to acknowledge card callback ${messageId}: ${ackError.message}`,
799
811
  );
800
812
  }
@@ -803,7 +815,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
803
815
  try {
804
816
  const payload = JSON.parse(res.data);
805
817
  const analysis = analyzeCardCallback(payload);
806
- ctx.log?.info?.(
818
+ pluginLog?.info?.(
807
819
  `[${account.accountId}] [DingTalk][CardCallback] action=${analysis.summary} raw=${JSON.stringify(payload)}`,
808
820
  );
809
821
 
@@ -826,14 +838,14 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
826
838
  analysis.feedbackAckText,
827
839
  {
828
840
  accountId: account.accountId,
829
- log: ctx.log,
841
+ log: pluginLog,
830
842
  },
831
843
  );
832
- ctx.log?.info?.(
844
+ pluginLog?.info?.(
833
845
  `[${account.accountId}] [DingTalk][CardCallback] feedback ack sent to ${analysis.feedbackTarget}`,
834
846
  );
835
847
  } catch (sendErr: any) {
836
- ctx.log?.warn?.(
848
+ pluginLog?.warn?.(
837
849
  `[${account.accountId}] [DingTalk][CardCallback] Failed to send feedback ack: ${sendErr?.message || String(sendErr)}`,
838
850
  );
839
851
  }
@@ -843,15 +855,15 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
843
855
  cfg,
844
856
  accountId: account.accountId,
845
857
  config,
846
- log: ctx.log,
858
+ log: pluginLog,
847
859
  });
848
860
  if (!actionResult.handled && analysis.actionId && analysis.actionId !== "feedback_up" && analysis.actionId !== "feedback_down") {
849
- ctx.log?.debug?.(
861
+ pluginLog?.debug?.(
850
862
  `[${account.accountId}] [DingTalk][CardCallback] Unhandled actionId=${analysis.actionId}`,
851
863
  );
852
864
  }
853
865
  } catch (error: any) {
854
- ctx.log?.error?.(
866
+ pluginLog?.error?.(
855
867
  `[${account.accountId}] [DingTalk][CardCallback] Failed to parse callback: ${error.message}`,
856
868
  );
857
869
  } finally {
@@ -877,15 +889,15 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
877
889
  return;
878
890
  }
879
891
  stopped = true;
880
- ctx.log?.info?.(`[${account.accountId}] Stopping DingTalk Stream client...`);
892
+ pluginLog?.info?.(`[${account.accountId}] Stopping DingTalk Stream client...`);
881
893
  void finalizeActiveCardsForAccount(
882
894
  config,
883
895
  account.accountId,
884
896
  "⚠️ 服务正在重启,当前回复已中断。请重新发送你的问题。",
885
897
  accountStorePath,
886
- ctx.log,
898
+ pluginLog,
887
899
  ).catch((err: any) => {
888
- ctx.log?.debug?.(
900
+ pluginLog?.debug?.(
889
901
  `[${account.accountId}] Failed to finalize active cards during stop: ${err.message}`,
890
902
  );
891
903
  });
@@ -895,7 +907,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
895
907
  try {
896
908
  client.disconnect();
897
909
  } catch (err: any) {
898
- ctx.log?.warn?.(`[${account.accountId}] Error during disconnect: ${err.message}`);
910
+ pluginLog?.warn?.(`[${account.accountId}] Error during disconnect: ${err.message}`);
899
911
  }
900
912
  nativeStopResolve?.();
901
913
  }
@@ -906,12 +918,16 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
906
918
  lastStopAt: getCurrentTimestamp(),
907
919
  });
908
920
 
909
- ctx.log?.info?.(`[${account.accountId}] DingTalk Stream client stopped`);
921
+ pluginLog?.info?.(`[${account.accountId}] DingTalk Stream client stopped`);
922
+ closePluginDebugLog({
923
+ accountId: account.accountId,
924
+ storePath: accountStorePath,
925
+ });
910
926
  };
911
927
 
912
928
  if (abortSignal) {
913
929
  if (abortSignal.aborted) {
914
- ctx.log?.warn?.(
930
+ pluginLog?.warn?.(
915
931
  `[${account.accountId}] Abort signal already active, skipping connection`,
916
932
  );
917
933
 
@@ -929,7 +945,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
929
945
  if (stopped) {
930
946
  return;
931
947
  }
932
- ctx.log?.info?.(
948
+ pluginLog?.info?.(
933
949
  `[${account.accountId}] Abort signal received, stopping DingTalk Stream client...`,
934
950
  );
935
951
  stopClient();
@@ -946,11 +962,11 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
946
962
  lastStartAt: getCurrentTimestamp(),
947
963
  lastError: null,
948
964
  });
949
- ctx.log?.info?.(`[${account.accountId}] DingTalk Stream client connected successfully`);
965
+ pluginLog?.info?.(`[${account.accountId}] DingTalk Stream client connected successfully`);
950
966
  await nativeStopPromise;
951
967
  }
952
968
  } catch (err: any) {
953
- ctx.log?.error?.(
969
+ pluginLog?.error?.(
954
970
  formatDingTalkConnectionErrorLog(
955
971
  // Use connect.open as base scope; instrumentation can override to connect.websocket
956
972
  "connect.open",
@@ -984,7 +1000,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
984
1000
  if (stopped) {
985
1001
  return;
986
1002
  }
987
- ctx.log?.debug?.(
1003
+ pluginLog?.debug?.(
988
1004
  `[${account.accountId}] Connection state changed to: ${state}${error ? ` (${error})` : ""}`,
989
1005
  );
990
1006
  if (state === ConnectionState.CONNECTED) {
@@ -1007,7 +1023,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
1007
1023
  }
1008
1024
  }
1009
1025
  if (cleared > 0) {
1010
- ctx.log?.info?.(
1026
+ pluginLog?.info?.(
1011
1027
  `[${account.accountId}] Cleared ${cleared} stale in-flight lock(s) on disconnect`,
1012
1028
  );
1013
1029
  }
@@ -1020,7 +1036,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
1020
1036
  },
1021
1037
  };
1022
1038
 
1023
- ctx.log?.debug?.(
1039
+ pluginLog?.debug?.(
1024
1040
  `[${account.accountId}] Connection config: maxAttempts=${connectionConfig.maxAttempts}, ` +
1025
1041
  `initialDelay=${connectionConfig.initialDelay}ms, maxDelay=${connectionConfig.maxDelay}ms, ` +
1026
1042
  `jitter=${connectionConfig.jitter}`,
@@ -1030,7 +1046,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
1030
1046
  client,
1031
1047
  account.accountId,
1032
1048
  connectionConfig,
1033
- ctx.log,
1049
+ pluginLog,
1034
1050
  createStreamClient,
1035
1051
  );
1036
1052
 
@@ -1044,17 +1060,17 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
1044
1060
  lastStartAt: getCurrentTimestamp(),
1045
1061
  lastError: null,
1046
1062
  });
1047
- ctx.log?.info?.(`[${account.accountId}] DingTalk Stream client connected successfully`);
1063
+ pluginLog?.info?.(`[${account.accountId}] DingTalk Stream client connected successfully`);
1048
1064
 
1049
1065
  await connectionManager.waitForStop();
1050
1066
  } else {
1051
- ctx.log?.info?.(
1067
+ pluginLog?.info?.(
1052
1068
  `[${account.accountId}] DingTalk Stream client connect() completed but channel is ` +
1053
1069
  `not running (stopped=${stopped}, connected=${connectionManager.isConnected()})`,
1054
1070
  );
1055
1071
  }
1056
1072
  } catch (err: any) {
1057
- ctx.log?.error?.(
1073
+ pluginLog?.error?.(
1058
1074
  formatDingTalkConnectionErrorLog(
1059
1075
  // Use connect.open as base scope; instrumentation can override to connect.websocket
1060
1076
  "connect.open",
@@ -1,26 +1,8 @@
1
- import type { OpenClawConfig } from "openclaw/plugin-sdk";
1
+ import { resolveNativeCommandSessionTargets } from "openclaw/plugin-sdk/command-auth";
2
+ import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
2
3
  import { getDingTalkRuntime } from "../runtime";
3
4
  import type { Logger } from "../types";
4
5
 
5
- /**
6
- * Local implementation of the same logic as
7
- * `resolveNativeCommandSessionTargets` from `openclaw/plugin-sdk/command-auth`.
8
- *
9
- * Inlined because the CI openclaw package does not yet export that sub-path.
10
- * Replace with a direct import once the upstream package is updated.
11
- */
12
- function resolveNativeCommandSessionTargets(params: {
13
- agentId: string;
14
- sessionPrefix: string;
15
- userId: string;
16
- targetSessionKey: string;
17
- }): { sessionKey: string; commandTargetSessionKey: string } {
18
- return {
19
- sessionKey: `agent:${params.agentId}:${params.sessionPrefix}:${params.userId}`,
20
- commandTargetSessionKey: params.targetSessionKey,
21
- };
22
- }
23
-
24
6
  /**
25
7
  * Dispatch a native targeted `/stop` command through the OpenClaw SDK,
26
8
  * replacing the previous self-built Gateway WebSocket `chat.abort` approach.
@@ -35,8 +17,8 @@ function resolveNativeCommandSessionTargets(params: {
35
17
  * and executes `abortEmbeddedPiRun` + `clearSessionQueues`.
36
18
  *
37
19
  * Accesses SDK functions via `getDingTalkRuntime().channel.reply` — the same
38
- * pattern used by `inbound-handler.ts` — to avoid direct sub-path imports
39
- * that may not be available in the CI openclaw package version.
20
+ * pattern used by `inbound-handler.ts` — while reusing the upstream native
21
+ * command session-target helper exported by the plugin SDK.
40
22
  */
41
23
  export async function dispatchDingTalkCardStopCommand(params: {
42
24
  cfg: OpenClawConfig;